aboutsummaryrefslogtreecommitdiff
path: root/ld/ldlang.c
diff options
context:
space:
mode:
Diffstat (limited to 'ld/ldlang.c')
-rw-r--r--ld/ldlang.c263
1 files changed, 228 insertions, 35 deletions
diff --git a/ld/ldlang.c b/ld/ldlang.c
index 0c0cf3d..f2c1379 100644
--- a/ld/ldlang.c
+++ b/ld/ldlang.c
@@ -151,6 +151,13 @@ static void walk_wild_section
static void walk_wild_file
PARAMS ((lang_wild_statement_type *, const char *,
lang_input_statement_type *, callback_t, void *));
+
+static int get_target PARAMS ((const bfd_target *, void *));
+static void stricpy PARAMS ((char *, char *));
+static void strcut PARAMS ((char *, char *));
+static int name_compare PARAMS ((char *, char *));
+static int closest_target_match PARAMS ((const bfd_target *, void *));
+static char * get_first_input_target PARAMS ((void));
/* EXPORTS */
lang_output_section_statement_type *abs_output_section;
@@ -294,23 +301,17 @@ walk_wild (s, section, file, callback, data)
callback_t callback;
void *data;
{
- lang_input_statement_type *f;
-
if (file == (char *) NULL)
{
/* Perform the iteration over all files in the list. */
- for (f = (lang_input_statement_type *) file_chain.head;
- f != (lang_input_statement_type *) NULL;
- f = (lang_input_statement_type *) f->next)
+ LANG_FOR_EACH_INPUT_STATEMENT (f)
{
walk_wild_file (s, section, f, callback, data);
}
}
else if (wildcardp (file))
{
- for (f = (lang_input_statement_type *) file_chain.head;
- f != (lang_input_statement_type *) NULL;
- f = (lang_input_statement_type *) f->next)
+ LANG_FOR_EACH_INPUT_STATEMENT (f)
{
if (fnmatch (file, f->filename, FNM_FILE_NAME) == 0)
walk_wild_file (s, section, f, callback, data);
@@ -318,6 +319,8 @@ walk_wild (s, section, file, callback, data)
}
else
{
+ lang_input_statement_type *f;
+
/* Perform the iteration over a single file. */
f = lookup_name (file);
walk_wild_file (s, section, f, callback, data);
@@ -1449,29 +1452,234 @@ wild (s, section, file, target, output)
}
}
+/* 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 ++) != 0)
+ {
+ if (isupper ((unsigned char) 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;
+}
+
+/* Return the BFD target format of the first input file. */
+static char *
+get_first_input_target ()
+{
+ char * target = NULL;
+
+ LANG_FOR_EACH_INPUT_STATEMENT (s)
+ {
+ if (s->header.type == lang_input_statement_enum
+ && s->real)
+ {
+ ldfile_open_file (s);
+
+ if (s->the_bfd != NULL
+ && bfd_check_format (s->the_bfd, bfd_object))
+ {
+ target = bfd_get_target (s->the_bfd);
+
+ if (target != NULL)
+ break;
+ }
+ }
+ }
+
+ return target;
+}
+
/* Open the output file. */
static bfd *
open_output (name)
- const char *name;
+ const char * name;
{
- bfd *output;
+ bfd * output;
+ /* Has the user told us which output format to use ? */
if (output_target == (char *) NULL)
{
- if (current_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 ();
+
+ /* 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
- output_target = default_target;
+ 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 (_("%P: warning: 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: target %s not found\n"), output_target);
+
einfo (_("%P%F: cannot open output file %s: %E\n"), name);
}
@@ -1494,9 +1702,6 @@ open_output (name)
return output;
}
-
-
-
static void
ldlang_open_output (statement)
lang_statement_union_type * statement;
@@ -1573,7 +1778,7 @@ open_input_bfds (s, force)
current_target = s->target_statement.target;
break;
case lang_input_statement_enum:
- if (s->input_statement.real == true)
+ if (s->input_statement.real)
{
lang_statement_list_type add;
@@ -3211,11 +3416,7 @@ into the statement tree.
static void
lang_place_orphans ()
{
- lang_input_statement_type *file;
-
- for (file = (lang_input_statement_type *) file_chain.head;
- file != (lang_input_statement_type *) NULL;
- file = (lang_input_statement_type *) file->next)
+ LANG_FOR_EACH_INPUT_STATEMENT (file)
{
asection *s;
@@ -3340,11 +3541,7 @@ void
lang_for_each_file (func)
void (*func) PARAMS ((lang_input_statement_type *));
{
- lang_input_statement_type *f;
-
- for (f = (lang_input_statement_type *) file_chain.head;
- f != (lang_input_statement_type *) NULL;
- f = (lang_input_statement_type *) f->next)
+ LANG_FOR_EACH_INPUT_STATEMENT (f)
{
func (f);
}
@@ -3358,13 +3555,9 @@ void
lang_for_each_input_section (func)
void (*func) PARAMS ((bfd * ab, asection * as));
{
- lang_input_statement_type *f;
-
- for (f = (lang_input_statement_type *) file_chain.head;
- f != (lang_input_statement_type *) NULL;
- f = (lang_input_statement_type *) f->next)
+ LANG_FOR_EACH_INPUT_STATEMENT (f)
{
- asection *s;
+ asection * s;
for (s = f->the_bfd->sections;
s != (asection *) NULL;