public inbox for binutils@sourceware.org
 help / color / mirror / Atom feed
From: Nick Clifton <nickc@cygnus.com>
To: ian@zembu.com
Cc: binutils@sourceware.cygnus.com
Subject: Re: Patch to improve linker output format selection
Date: Sat, 17 Jul 1999 09:20:00 -0000	[thread overview]
Message-ID: <199907171619.RAA16066@pathia.cygnus.co.uk> (raw)

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

             reply	other threads:[~1999-07-17  9:20 UTC|newest]

Thread overview: 3+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
1999-07-17  9:20 Nick Clifton [this message]
  -- strict thread matches above, loose matches on Subject: below --
1999-07-16  7:36 Nick Clifton
1999-07-16  8:40 ` Ian Lance Taylor

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=199907171619.RAA16066@pathia.cygnus.co.uk \
    --to=nickc@cygnus.com \
    --cc=binutils@sourceware.cygnus.com \
    --cc=ian@zembu.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
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).