This is the mail archive of the binutils@sourceware.cygnus.com mailing list for the binutils project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]

Patch to improve linker output format selection


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

Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]