From mboxrd@z Thu Jan 1 00:00:00 1970 From: Nick Clifton 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 Message-id: <199907171619.RAA16066@pathia.cygnus.co.uk> X-SW-Source: 1999-q3/msg00236.html 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 * 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 * 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); }