public inbox for binutils@sourceware.org
 help / color / mirror / Atom feed
* Re: Patch to improve linker output format selection
@ 1999-07-17  9:20 Nick Clifton
  0 siblings, 0 replies; 3+ messages in thread
From: Nick Clifton @ 1999-07-17  9:20 UTC (permalink / raw)
  To: ian; +Cc: binutils

Hi Ian,

  Below is a revised patch which addresses most of the points you
raised.  I have changed the target finding heuristic to pay more
attention to what the user or linker script has specified as the
output target format, and I have also implemented the new field for
struct bfd_target which can point to an alternative other-endian
target.  The patch also includes an example of how this might work for
elf based targets, although more patches would be needed to implement
the field for all targets known to bfd.  Not implementing the field
will not be a problem however, as the code copes if it is NULL.

  One change I did not make is to use LANG_FOR_EACH_INPUT_STATEMENT in
the function get_first_input_target.  The reason is that this function
is basically a straight copy of the function open_input_bfds() and
like that function, it is recursive.

  The new heuristic to decide the output target format looks like
  this:

   1. If the linker script or user has specified an output target
      format then use that.

   2. Otherwise if current_target has been specifically set then use
      that.

   3. Otherwise use the target format of the first input file, if it
      can be found.

   4. Otherwise use the default target format.

  Then:

   1. If an endianness has been specified on the command line and the
      selected output target format does not match this then:

   2. If the output target has an alternative target specified and the
      alternative is the correct endianness then use that.

   3. Otherwise search through all of the target formats known to bfd
      to find one which is the closest match to the current output
      target format, but which has the correct endianness.

   4. If none can be found then just use the current output target
      format and assume that an error message will be generated by
      later code.

What do you think ?

Cheers
	Nick

1999-07-17  Nick Clifton  <nickc@cygnus.com>

	* targets.c (alternative_target): New field in bfd_target
	structure.
	(bfd_search_for_target): New function:  Find a target that
	satisifies a search function.
	* bfd-in2.h: Regenerate.

	* elfxx-target.h: Initialise the alternative_target field of
	the bfd_target structures to point to the other target (if
	defined). 

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/17 15:58:09
*************** Data for use by back-end routines, which
*** 464,469 ****
--- 464,480 ----
  in this structure.
  
  . PTR backend_data;
+ 
+ A pointer to an alternative bfd_target in case the current one is not
+ satisfactory.  This can happen when the target cpu supports both big
+ and little endian code, and target chosen by the linker has the wrong
+ endianness.  The function open_output() in ld/ldlang.c uses this field
+ to find an alternative output format that is suitable.
+ 
+ . 
+ . {* Opposite endian version of this target.  *}  
+ . const struct bfd_target * alternative_target;
+ . 
  .} bfd_target;
  
  */
*************** bfd_target_list ()
*** 1078,1081 ****
--- 1089,1121 ----
      *(name_ptr++) = (*target)->name;
  
    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/17 15:58:10
*************** CAT(NAME,_canonicalize_dynamic_reloc)
*** 3004,3009 ****
--- 3004,3013 ----
      PARAMS ((bfd *, arelent **, struct symbol_cache_entry **));
  
   PTR backend_data;
+     
+  /* Opposite endian version of this target.  */
+  const struct bfd_target * alternative_target;
+  
  } bfd_target;
  boolean 
  bfd_set_default_target  PARAMS ((const char *name));
*************** bfd_find_target PARAMS ((CONST char *tar
*** 3013,3018 ****
--- 3017,3025 ----
  
  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));

Index: bfd/elfxx-target.h
===================================================================
RCS file: /cvs/binutils/binutils/bfd/elfxx-target.h,v
retrieving revision 1.7
diff -p -r1.7 elfxx-target.h
*** elfxx-target.h	1999/07/14 16:14:49	1.7
--- elfxx-target.h	1999/07/17 15:58:10
*************** static CONST struct elf_backend_data elf
*** 388,393 ****
--- 388,398 ----
    elf_backend_want_dynbss
  };
  
+ /* Forward declaration for use when initialising alternative_target field.  */
+ #ifdef TARGET_LITTLE_SYM
+ extern const bfd_target TARGET_LITTLE_SYM;
+ #endif
+ 
  #ifdef TARGET_BIG_SYM
  const bfd_target TARGET_BIG_SYM =
  {
*************** const bfd_target TARGET_BIG_SYM =
*** 473,478 ****
--- 478,490 ----
  
    /* backend_data: */
    (PTR) &elfNN_bed,
+ 
+   /* alternative endian target.  */
+ #ifdef TARGET_LITTLE_SYM
+   & TARGET_LITTLE_SYM,
+ #else
+   NULL,
+ #endif
  };
  #endif
  
*************** const bfd_target TARGET_LITTLE_SYM =
*** 561,565 ****
--- 573,584 ----
  
    /* backend_data: */
    (PTR) &elfNN_bed,
+ 
+   /* alternative endian target.  */
+ #ifdef TARGET_BIG_SYM
+   & TARGET_BIG_SYM,
+ #else
+   NULL,
+ #endif
  };
  #endif


1999-07-17  Nick Clifton  <nickc@cygnus.com>

	* targets.c (get_target): New function: Return true iff the
	given target is the target being sought.
	(stricpy): New function:  Like strcpy but convert to lower
	case as well.
	(strcut): New function:  Like strstr but remove the located
	substring as well.
	(name_compare): New function: Compute a compatability rating
	for two target names.
	(winner): New variable: Best target found by
	closest_target_match() so far.
	(closest_target_match): New function: Find the target which is
	the closest match to the original target.
	(get_first_input_target): New function: Find the target format
	of the first of the linker's input file.
	(open_output): Be more clever about deciding the output target
	format. 


Index: ld/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/17 15:58:12
*************** wild (s, section, file, target, output)
*** 1449,1477 ****
      }
  }
  
  /* 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)
      {
        if (bfd_get_error () == bfd_error_invalid_target)
! 	{
! 	  einfo (_("%P%F: target %s not found\n"), output_target);
! 	}
        einfo (_("%P%F: cannot open output file %s: %E\n"), name);
      }
  
--- 1445,1696 ----
      }
  }
  
+ /* Return true iff target is the sought target.  */
+ static int
+ get_target (target, data)
+      const bfd_target * target;
+      void * data;
+ {
+   const char * sought = (const char *) data;
+   
+   return strcmp (target->name, sought) == 0;
+ }
+ 
+ /* Like strcpy() but convert to lower case as well.  */
+ static void
+ stricpy (dest, src)
+      char * dest;
+      char * src;
+ {
+   char c;
+   
+   while (c = * src ++)
+     {
+       if (isascii (c) && isupper (c))
+ 	c = tolower (c);
+ 
+       * dest ++ = c;
+     }
+ 
+   * dest = 0;
+ }
+ 
+ /* Remove the first occurance of needle (if any) in haystack
+    from haystack.  */
+ static void
+ strcut (haystack, needle)
+      char * haystack;
+      char * needle;
+ {
+   haystack = strstr (haystack, needle);
+   
+   if (haystack)
+     {
+       char * src;
+ 
+       for (src = haystack + strlen (needle); * src;)
+ 	* haystack ++ = * src ++;
+       
+       * haystack = 0;
+     }
+ }
+ 
+ /* Compare two target format name strings.
+    Return a value indicating how "similar" they are.  */
+ static int
+ name_compare (first, second)
+      char * first;
+      char * second;
+ {
+   char * copy1;
+   char * copy2;
+   int    result;
+   
+   copy1 = xmalloc (strlen (first) + 1);
+   copy2 = xmalloc (strlen (second) + 1);
+ 
+   /* Convert the names to lower case.  */
+   stricpy (copy1, first);
+   stricpy (copy2, second);
+ 
+   /* Remove and endian strings from the name.  */
+   strcut (copy1, "big");
+   strcut (copy1, "little");
+   strcut (copy2, "big");
+   strcut (copy2, "little");
+ 
+   /* Return a value based on how many characters match,
+      starting from the beginning.   If both strings are
+      the same then return 10 * their length.  */
+   for (result = 0; copy1 [result] == copy2 [result]; result ++)
+     if (copy1 [result] == 0)
+       {
+ 	result *= 10;
+ 	break;
+       }
+   
+   free (copy1);
+   free (copy2);
+ 
+   return result;
+ }
+ 
+ /* 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 original output target.  */
+ static int
+ closest_target_match (target, data)
+      const bfd_target * target;
+      void * data;
+ {
+   const bfd_target * original = (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 != original->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.
+      Compare their names and choose the better one. */
+   if (name_compare (target->name, original->name) > name_compare (winner->name, original->name))
+     winner = target;
+ 
+   /* Keep on searching until wqe have checked them all.  */
+   return 0;
+ }
+ 
+ 
+ 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)
+ 	    {
+ 	      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 - 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;
! 	}
!     }
!   
!   /* Has the user requested a particular endianness on the command line ?  */
!   if (command_line.endian != ENDIAN_UNSET)
!     {
!       const bfd_target * target;
!       int desired_endian;
! 
!       /* Get the chosen target.  */
!       target = bfd_search_for_target (get_target, (void *) output_target);
! 
!       if (command_line.endian == ENDIAN_BIG)
! 	desired_endian = BFD_ENDIAN_BIG;
!       else
! 	desired_endian = BFD_ENDIAN_LITTLE;
!       
!       /* See if the target has the wrong endianness.  This should not happen
! 	 if the linker script has provided big and little endian alternatives,
! 	 but some scrips don't do this.  */
!       if (target->byteorder != desired_endian)
! 	{
! 	  /* If it does, then see if the target provides
! 	     an alternative with the correct endianness.  */
! 	  if (target->alternative_target != NULL
! 	      && (target->alternative_target->byteorder == desired_endian))
! 	    output_target = target->alternative_target->name;
! 	  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 *) target);
! 	      
! 	      /* Oh dear - we could not find any targets that satisfy our requirements.  */
! 	      if (winner == NULL)
! 		einfo (_("%F%P: could not find any targets that match endianness requirement\n"));
! 	      else
! 		output_target = winner->name;
! 	    }
! 	}
      }
+       
    output = bfd_openw (name, output_target);
  
    if (output == (bfd *) NULL)
      {
        if (bfd_get_error () == bfd_error_invalid_target)
! 	einfo (_("%P%F: target %s not found\n"), output_target);
! 
        einfo (_("%P%F: cannot open output file %s: %E\n"), name);
      }
  

^ permalink raw reply	[flat|nested] 3+ messages in thread

* Re: Patch to improve linker output format selection
  1999-07-16  7:36 Nick Clifton
@ 1999-07-16  8:40 ` Ian Lance Taylor
  0 siblings, 0 replies; 3+ messages in thread
From: Ian Lance Taylor @ 1999-07-16  8:40 UTC (permalink / raw)
  To: nickc; +Cc: binutils

   Date: Fri, 16 Jul 1999 15:36:15 +0100
   From: Nick Clifton <nickc@cygnus.com>

       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.

I always worry about code like this where we try to outsmart the user.
I can see why we want to do it.  But consider the case of a
user-written linker script: the user has explicitly used the
OUTPUT_FORMAT command in the linker script.  With your patch, the
linker will now be clever and instead use the format of the first
input file.  We are effectively forcing the user to use the -oformat
option in this case.

This is a weird case, so that is not so bad.  But it's not a backward
compatible change, which means we need documentation changes and NEWS
changes.

Actually, the more I look at your patch, the weirder it seems.  In
case 2 you prefer the default output format.  In case 3 you prefer the
first input file.  What is the essential difference between these
cases?  Why shouldn't you always prefer the first input file?  In
fact, doesn't this patch make OUTPUT_FORMAT essentially useless?  It
sets a default, but that default will only be used in highly
exceptional cases.


   + static char *
   + get_first_input_target (s)
   +      lang_statement_union_type * s;

It would be better to write this using FOR_EACH_INPUT_STATEMENT.

   + 	  if (s->input_statement.real == true)

Please don't compare boolean values to true or false.  I know there is
existing code which does it, but I don't like it.  Just write
    if (s->input_statement.real)


   *************** 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;
	 }
     }

This looks wrong.  We are changing the meaning of OUTPUT_FORMAT in a
linker script, which I guess is OK.  But why should we only change it
when only one format is specified?  Basically I don't understand why
it is correct to have the ``else'' in the lines you added.

Ian

^ permalink raw reply	[flat|nested] 3+ messages in thread

* Patch to improve linker output format selection
@ 1999-07-16  7:36 Nick Clifton
  1999-07-16  8:40 ` Ian Lance Taylor
  0 siblings, 1 reply; 3+ messages in thread
From: Nick Clifton @ 1999-07-16  7:36 UTC (permalink / raw)
  To: binutils

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;
      }
  }

^ permalink raw reply	[flat|nested] 3+ messages in thread

end of thread, other threads:[~1999-07-17  9:20 UTC | newest]

Thread overview: 3+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
1999-07-17  9:20 Patch to improve linker output format selection Nick Clifton
  -- strict thread matches above, loose matches on Subject: below --
1999-07-16  7:36 Nick Clifton
1999-07-16  8:40 ` Ian Lance Taylor

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).