This is the mail archive of the
binutils@sourceware.cygnus.com
mailing list for the binutils project.
Patch to improve linker output format selection
- To: binutils@sourceware.cygnus.com
- Subject: Patch to improve linker output format selection
- From: Nick Clifton <nickc@cygnus.com>
- Date: Fri, 16 Jul 1999 15:36:15 +0100
Hi Guys,
Well here is my first pass at a patch to improve linker output
format selection. It is not perfect, in that it does not implement
Ian's suggestion of extending the bfd_target structure to include a
reverse_endian link, but with this patch applied my patch to the
linker testsuite infrastructure no longers generates an spurious
failures.
What this patch does is to implement a new BFD function called
bfd_search_for_target, which can be used to locate a bfd_target
structure according to arbitrary search criteria. The linker then
uses this function in order to locate a suitable output format. The
modified linker heuristic is:
1. If an output format has been specified on the command line, then
use that.
2. Otherwise if an endianness has been specified on the command
line then:
a. If the default output format has the required endianness
then use that.
b. Otherwise attempt to locate the target format which is the
closest match to the default output format, but which has
the required endian characteristic.
3. Otherwise attempt to discover the format of the first of the
linker's input files and set the output format to the same.
4. Otherwise if that search fails use the default output format.
It is step 2b. which is not yet complete. At the moment the code
will look for any target format which has the required endianness
and the same flavour as the default target. If more than one such
target exists (and for the ARM is does) then it will just issue a
warning message and use the first target found.
To improve the code we could implement Ian's suggestion of an extra
field in the bfd_target structure. Examining this field could then
come in between steps 2a and 2b, with 2b only being executed if the
field was empty. Also the heuristic in step 2b could be improved by
creating a target name comparision function which could compare the
name strings of two targets, ignoring any occurance of "big" or
"little" and seeing if they were the same.
Anyway, any comments anyone ?
Cheers
Nick
1999-07-16 Nick Clifton <nickc@cygnus.com>
* targets.c (bfd_search_for_target): New function. Search
through the list of target formats known to BFD for one that
satisfies a given search function.
* bfd-in2.h: Regenerate.
Index: bfd/targets.c
===================================================================
RCS file: /cvs/binutils/binutils/bfd/targets.c,v
retrieving revision 1.3
diff -p -r1.3 targets.c
*** targets.c 1999/05/27 21:44:39 1.3
- --- targets.c 1999/07/16 13:25:01
*************** bfd_target_list ()
*** 1079,1081 ****
- --- 1079,1110 ----
return name_list;
}
+
+ /*
+ FUNCTION
+ bfd_seach_for_target
+
+ SYNOPSIS
+ const bfd_target * bfd_search_for_target (int (* search_func)(const bfd_target *, void *), void *);
+
+ DESCRIPTION
+ Return a pointer to the first transfer vector in the list of
+ transfer vectors maintained by BFD that produces a non-zero
+ result when passed to the function @var{search_func}. The
+ parameter @var{data} is passed, unexamined, to the search
+ function.
+ */
+
+ const bfd_target *
+ bfd_search_for_target (search_func, data)
+ int (* search_func) PARAMS ((const bfd_target * target, void * data));
+ void * data;
+ {
+ const bfd_target * const * target;
+
+ for (target = bfd_target_vector; * target != NULL; target ++)
+ if (search_func (* target, data))
+ return * target;
+
+ return NULL;
+ }
Index: bfd/bfd-in2.h
===================================================================
RCS file: /cvs/binutils/binutils/bfd/bfd-in2.h,v
retrieving revision 1.13
diff -p -r1.13 bfd-in2.h
*** bfd-in2.h 1999/07/12 11:06:03 1.13
- --- bfd-in2.h 1999/07/16 13:25:02
*************** bfd_find_target PARAMS ((CONST char *tar
*** 3014,3019 ****
- --- 3014,3022 ----
const char **
bfd_target_list PARAMS ((void));
+ const bfd_target *
+ bfd_search_for_target PARAMS ((int (* search_func)(const bfd_target *, void *), void *));
+
boolean
bfd_check_format PARAMS ((bfd *abfd, bfd_format format));
1999-07-16 Nick Clifton <nickc@cygnus.com>
* lang.c (get_default): New function: Return true if the
given BFD target is the default target.
(winner): New static variable: Best match of BFD targets to
default target.
(closest_target_match): New function: Set 'winner' to the BFD
target which most closely matches the default target and which
meets the endian requirements.
(get_first_input_target): New function: Return the name of the
BFD target used by the first of the linker's input files.
(open_output): If output_target is not set, then attempt to
deduce the best target to used based on either the endianness
set by a command line switch to the linker or the target of
the first of the linker's input files.
(lang_add_output_format): If the format is being set from a
linker script and neither the -EL nor the -EB command line
switches have been used, then assume that the script is
setting the default output target format, not the desired
output target format.
Index: ldlang.c
===================================================================
RCS file: /cvs/binutils/binutils/ld/ldlang.c,v
retrieving revision 1.7
diff -p -r1.7 ldlang.c
*** ldlang.c 1999/07/14 16:45:13 1.7
- --- ldlang.c 1999/07/16 13:44:10
*************** wild (s, section, file, target, output)
*** 1449,1469 ****
}
}
/* Open the output file. */
static bfd *
open_output (name)
! const char *name;
{
! bfd *output;
if (output_target == (char *) NULL)
{
! if (current_target != (char *) NULL)
output_target = current_target;
else
! output_target = default_target;
}
output = bfd_openw (name, output_target);
if (output == (bfd *) NULL)
- --- 1449,1610 ----
}
}
+ /* Return true iff target is the default target. */
+ static int
+ get_default (target, data)
+ const bfd_target * target;
+ void * data;
+ {
+ if (strcmp (target->name, default_target))
+ return 0;
+
+ return 1;
+ }
+
+ /* Set by closest_target_match() below. */
+ static const bfd_target * winner;
+
+ /* Scan all the valid bfd targets looking for one that has the endianness
+ requirement that was specified on the command line, and is the nearest
+ match to the default target. */
+ static int
+ closest_target_match (target, data)
+ const bfd_target * target;
+ void * data;
+ {
+ const bfd_target * def = (const bfd_target *) data;
+
+ if (command_line.endian == ENDIAN_BIG && target->byteorder != BFD_ENDIAN_BIG)
+ return 0;
+
+ if (command_line.endian == ENDIAN_LITTLE && target->byteorder != BFD_ENDIAN_LITTLE)
+ return 0;
+
+ /* Must be the same flavour. */
+ if (target->flavour != def->flavour)
+ return 0;
+
+ /* If we have not found a potential winner yet, then record this one. */
+ if (winner == NULL)
+ {
+ winner = target;
+ return 0;
+ }
+
+ /* Oh dear, we now have two potential candidates for a successful match.
+ What to do ?
+ For now just issue a warning message and stick with the first. */
+ einfo (_("%P: multiple possible output formats satisfy endianness requirement\n"));
+ einfo (_("%P: using: %s\n"), winner->name);
+
+ /* Stop the search since we have already found two candidates. */
+ return 1;
+ }
+
+
+ static char *
+ get_first_input_target (s)
+ lang_statement_union_type * s;
+ {
+ char * target = NULL;
+
+ for (; s != (lang_statement_union_type *) NULL; s = s->next)
+ {
+ switch (s->header.type)
+ {
+ case lang_constructors_statement_enum:
+ target = get_first_input_target (constructor_list.head);
+ break;
+ case lang_output_section_statement_enum:
+ target = get_first_input_target (s->output_section_statement.children.head);
+ break;
+ case lang_wild_statement_enum:
+ target = get_first_input_target (s->wild_statement.children.head);
+ break;
+ case lang_group_statement_enum:
+ target = get_first_input_target (s->group_statement.children.head);
+ break;
+ case lang_target_statement_enum:
+ current_target = s->target_statement.target;
+ break;
+ case lang_input_statement_enum:
+ if (s->input_statement.real == true)
+ {
+ ldfile_open_file (& s->input_statement);
+ if (s->input_statement.the_bfd != NULL)
+ if (bfd_check_format (s->input_statement.the_bfd, bfd_object))
+ target = bfd_get_target (s->input_statement.the_bfd);
+ }
+ break;
+ default:
+ break;
+ }
+ if (target != NULL)
+ return target;
+ }
+
+ return target;
+ }
+
/* Open the output file. */
static bfd *
open_output (name)
! const char * name;
{
! bfd * output;
+ /* Has the user told us which output format to use ? */
if (output_target == (char *) NULL)
{
! /* No - has the current target been set to something other than the default ? */
! if (current_target != default_target)
output_target = current_target;
+
+ /* No - has the user requested a particular endianness on the command line ? */
+ else if (command_line.endian != ENDIAN_UNSET)
+ {
+ /* Yes - try to find an output format that matches this request. */
+ const bfd_target * target;
+ const bfd_target * def;
+
+ /* Get the default target. */
+ def = bfd_search_for_target (get_default, NULL);
+
+ /* See if the default has the required endianness. */
+ if (command_line.endian == ENDIAN_BIG && def->byteorder == BFD_ENDIAN_BIG)
+ target = def;
+ else if (command_line.endian == ENDIAN_BIG && def->byteorder == BFD_ENDIAN_LITTLE)
+ target = def;
+ else
+ {
+ /* Try to find a target as similar as possible to the default
+ target, but which has the desired endian characteristic. */
+ (void) bfd_search_for_target (closest_target_match, (void *) def);
+ target = winner;
+
+ /* Oh dear - we could not find any targets that satisfy our requirements. */
+ if (target == NULL)
+ {
+ einfo (_("%F%P: could not find any targets that match endianness requirement\n"));
+ target = def;
+ }
+ }
+
+ output_target = target->name;
+ }
+
+ /* No - can we determine the format of the first input file ? */
else
! {
! output_target = get_first_input_target (statement_list.head);
!
! /* Failed - use the default output target. */
! if (output_target == NULL)
! output_target = default_target;
! }
}
+
output = bfd_openw (name, output_target);
if (output == (bfd *) NULL)
*************** lang_add_output_format (format, big, lit
*** 4030,4036 ****
else if (command_line.endian == ENDIAN_LITTLE
&& little != NULL)
format = little;
!
output_target = format;
}
}
- --- 4171,4181 ----
else if (command_line.endian == ENDIAN_LITTLE
&& little != NULL)
format = little;
! else if (from_script)
! {
! default_target = (char *) format;
! format = NULL;
! }
output_target = format;
}
}