From: Nick Clifton Date: Wed, 14 Jun 2023 12:39:03 +0000 (+0100) Subject: Add --remap-inputs option to the BFD linker. X-Git-Url: https://git.libre-soc.org/?a=commitdiff_plain;h=fb221fba1a5eb05355f248d6aa1e3ab4316899fd;p=binutils-gdb.git Add --remap-inputs option to the BFD linker. PR 30374 * ldfile.c (struct input_remap): New structure. (ldfile_add_remap): New function. (ldfile_remap_input_free): New function. (ldfile_add_remap_file): New function. (ldfile_possibly_remap_input): New function. (ldfile_print_input_remaps): New function. * ldfile.h: Add prototypes for new functions. * ldlang.c (new_afile): Call ldfile_possibly_remap_input. (lang_finish): Call ldfile_remap_input_free. (lang_map): Call ldfile_print_input_remaps. * ldlex.h (OPTION_REMAP_INPUTS, OPTION_REMAP_INPUTS_FILE): Define. * lexsup.c (ld_options): Add --remap-inputs-file and --remap-inputs. (parse_args): Handle new options. * NEWS: Mention the new feature. * ld.texi: Document the new options. * testsuite/ld-misc/input-remap.exp: New test driver. * testsuite/ld-misc/remaps.r: New file: Expected linker output. * testsuite/ld-misc/remaps.txt: New file. Input remaps file. --- diff --git a/ld/ChangeLog b/ld/ChangeLog index d722d0ba482..8fd6504fa6e 100644 --- a/ld/ChangeLog +++ b/ld/ChangeLog @@ -1,3 +1,25 @@ +2023-06-14 Nick Clifton + + PR 30374 + * ldfile.c (struct input_remap): New structure. + (ldfile_add_remap): New function. + (ldfile_remap_input_free): New function. + (ldfile_add_remap_file): New function. + (ldfile_possibly_remap_input): New function. + (ldfile_print_input_remaps): New function. + * ldfile.h: Add prototypes for new functions. + * ldlang.c (new_afile): Call ldfile_possibly_remap_input. + (lang_finish): Call ldfile_remap_input_free. + (lang_map): Call ldfile_print_input_remaps. + * ldlex.h (OPTION_REMAP_INPUTS, OPTION_REMAP_INPUTS_FILE): Define. + * lexsup.c (ld_options): Add --remap-inputs-file and --remap-inputs. + (parse_args): Handle new options. + * NEWS: Mention the new feature. + * ld.texi: Document the new options. + * testsuite/ld-misc/input-remap.exp: New test driver. + * testsuite/ld-misc/remaps.r: New file: Expected linker output. + * testsuite/ld-misc/remaps.txt: New file. Input remaps file. + 2023-06-07 Nick Clifton PR 30499 diff --git a/ld/NEWS b/ld/NEWS index 9920d0209b8..4dee2301158 100644 --- a/ld/NEWS +++ b/ld/NEWS @@ -1,5 +1,10 @@ -*- text -*- +* The linker now accepts a command line option of --remap-inputs + = to relace any input file that matches with + . In addition the option --remap-inputs-file= can be used to + specify a file containing any number of these remapping directives. + * The linker command line option --print-map-locals can be used to include local symbols in a linker map. (ELF targets only). diff --git a/ld/ld.texi b/ld/ld.texi index 5639de797de..a007ae7fade 100644 --- a/ld/ld.texi +++ b/ld/ld.texi @@ -793,6 +793,66 @@ If the @samp{-m} option is not used, the emulation is taken from the Otherwise, the default emulation depends upon how the linker was configured. +@cindex remapping inputs +@kindex --remap-inputs=@file{pattern}=@file{filename} +@kindex --remap-inputs-file=@file{file} +@item --remap-inputs=@file{pattern}=@file{filename} +@itemx --remap-inputs-file=@file{file} +These options allow the names of input files to be changed before the +linker attempts to open them. The option +@option{--remap-inputs=foo.o=bar.o} will cause any attempt to load a +file called @file{foo.o} to instead try to load a file called +@file{bar.o}. Wildcard patterns are permitted in the first filename, +so @option{--remap-inputs=foo*.o=bar.o} will rename any input file that +matches @file{foo*.o} to @file{bar.o}. + +An alternative form of the option +@option{--remap-inputs-file=filename} allows the remappings to be read +from a file. Each line in the file can contain a single remapping. +Blank lines are ignored. Anything from a hash character (@samp{#}) to +the end of a line is considered to be a comment and is also ignored. +The mapping pattern can be separated from the filename by whitespace +or an equals (@samp{=}) character. + +The options can be specified multiple times. Their contents +accumulate. The remappings will be processed in the order in which +they occur on the command line, and if they come from a file, in the +order in which they occur in the file. If a match is made, no further +checking for that filename will be performed. + +If the replacement filename is @file{/dev/null} or just @file{NUL} +then the remapping will actually cause the input file to be ignored. +This can be a convenient way to experiment with removing input files +from a complicated build environment. + +Note that this option is position dependent and only affects filenames +that come after it on the command line. Thus: + +@smallexample + ld foo.o --remap-inputs=foo.o=bar.o +@end smallexample + +Will have no effect, whereas: + +@smallexample + ld --remap-inputs=foo.o=bar.o foo.o +@end smallexample + +Will rename the input file @file{foo.o} to @file{bar.o}. + +Note - these options also affect files referenced by @emph{INPUT} +statements in linker scripts. But since linker scripts are processed +after the entire command line is read, the position of the remap +options on the command line is not significant. + +If the @option{verbose} option is enabled then any mappings that match +will be reported, although again the @option{verbose} option needs to +be enabled on the command line @emph{before} the remaped filenames +appear. + +If the @option{-Map} or @option{--print-map} options are enabled then +the remapping list will be included in the map output. + @cindex link map @kindex -M @kindex --print-map diff --git a/ld/ldfile.c b/ld/ldfile.c index b8fd4e5d8e0..4976367bbf0 100644 --- a/ld/ldfile.c +++ b/ld/ldfile.c @@ -34,6 +34,7 @@ #include "ldemul.h" #include "libiberty.h" #include "filenames.h" +#include #if BFD_SUPPORTS_PLUGINS #include "plugin-api.h" #include "plugin.h" @@ -65,6 +66,215 @@ static search_dirs_type **search_tail_ptr = &search_head; static search_arch_type *search_arch_head; static search_arch_type **search_arch_tail_ptr = &search_arch_head; +typedef struct input_remap +{ + const char * pattern; /* Pattern to match input files. */ + const char * renamed; /* Filename to use if the pattern matches. */ + struct input_remap * next; /* Link in a chain of these structures. */ +} input_remap; + +static struct input_remap * input_remaps = NULL; + +void +ldfile_add_remap (const char * pattern, const char * renamed) +{ + struct input_remap * new_entry; + + new_entry = xmalloc (sizeof * new_entry); + new_entry->pattern = xstrdup (pattern); + new_entry->next = NULL; + + /* Look for special filenames that mean that the input file should be ignored. */ + if (strcmp (renamed, "/dev/null") == 0 + || strcmp (renamed, "NUL") == 0) + new_entry->renamed = NULL; + else + /* FIXME: Should we add sanity checking of the 'renamed' string ? */ + new_entry->renamed = xstrdup (renamed); + + /* It would be easier to add this new node at the start of the chain, + but users expect that remapping will occur in the order in which + they occur on the command line, and in the remapping files. */ + if (input_remaps == NULL) + { + input_remaps = new_entry; + } + else + { + struct input_remap * i; + + for (i = input_remaps; i->next != NULL; i = i->next) + ; + i->next = new_entry; + } +} + +void +ldfile_remap_input_free (void) +{ + while (input_remaps != NULL) + { + struct input_remap * i = input_remaps; + + input_remaps = i->next; + free ((void *) i->pattern); + free ((void *) i->renamed); + free (i); + } +} + +bool +ldfile_add_remap_file (const char * file) +{ + FILE * f; + + f = fopen (file, FOPEN_RT); + if (f == NULL) + return false; + + size_t linelen = 256; + char * line = xmalloc (linelen); + + do + { + char * p = line; + char * q; + + /* Normally this would use getline(3), but we need to be portable. */ + while ((q = fgets (p, linelen - (p - line), f)) != NULL + && strlen (q) == linelen - (p - line) - 1 + && line[linelen - 2] != '\n') + { + line = xrealloc (line, 2 * linelen); + p = line + linelen - 1; + linelen += linelen; + } + + if (q == NULL && p == line) + break; + + p = strchr (line, '\n'); + if (p) + *p = '\0'; + + /* Because the file format does not know any form of quoting we + can search forward for the next '#' character and if found + make it terminating the line. */ + p = strchr (line, '#'); + if (p) + *p = '\0'; + + /* Remove leading whitespace. NUL is no whitespace character. */ + p = line; + while (*p == ' ' || *p == '\f' || *p == '\r' || *p == '\t' || *p == '\v') + ++p; + + /* If the line is blank it is ignored. */ + if (*p == '\0') + continue; + + char * pattern = p; + + /* Advance past the pattern. We accept whitespace or '=' as an + end-of-pattern marker. */ + while (*p && *p != '=' && *p != ' ' && *p != '\t' && *p != '\f' + && *p != '\r' && *p != '\v') + ++p; + + if (*p == '\0') + { + einfo ("%F%P: malformed remap file entry: %s\n", line); + continue; + } + + * p++ = '\0'; + + /* Skip whitespace again. */ + while (*p == ' ' || *p == '\f' || *p == '\r' || *p == '\t' || *p == '\v') + ++p; + + if (*p == '\0') + { + einfo ("%F%P: malformed remap file entry: %s\n", line); + continue; + } + + char * rename = p; + + /* Advance past the rename entry. */ + while (*p && *p != '=' && *p != ' ' && *p != '\t' && *p != '\f' + && *p != '\r' && *p != '\v') + ++p; + /* And terminate it. */ + *p = '\0'; + + ldfile_add_remap (pattern, rename); + } + while (! feof (f)); + + free (line); + fclose (f); + + return true; +} + +const char * +ldfile_possibly_remap_input (const char * filename) +{ + struct input_remap * i; + + if (filename == NULL) + return NULL; + + for (i = input_remaps; i != NULL; i = i->next) + { + if (fnmatch (i->pattern, filename, 0) == 0) + { + if (verbose) + { + if (strpbrk ((i->pattern), "?*[") != NULL) + { + if (i->renamed) + info_msg (_("remap input file '%s' to '%s' based upon pattern '%s'\n"), + filename, i->renamed, i->pattern); + else + info_msg (_("remove input file '%s' based upon pattern '%s'\n"), + filename, i->pattern); + } + else + { + if (i->renamed) + info_msg (_("remap input file '%s' to '%s'\n"), + filename, i->renamed); + else + info_msg (_("remove input file '%s'\n"), + filename); + } + } + + return i->renamed; + } + } + + return filename; +} + +void +ldfile_print_input_remaps (void) +{ + if (input_remaps == NULL) + return; + + minfo (_("\nInput File Remapping\n\n")); + + struct input_remap * i; + + for (i = input_remaps; i != NULL; i = i->next) + minfo (_(" Pattern: %s\tMaps To: %s\n"), i->pattern, + i->renamed ? i->renamed : _("")); +} + + /* Test whether a pathname, after canonicalization, is the same or a sub-directory of the sysroot directory. */ diff --git a/ld/ldfile.h b/ld/ldfile.h index defb550f76b..153fd0449bc 100644 --- a/ld/ldfile.h +++ b/ld/ldfile.h @@ -60,4 +60,15 @@ extern bool ldfile_open_file_search (const char *arch, struct lang_input_statement_struct *, const char *lib, const char *suffix); +extern void ldfile_add_remap + (const char *, const char *); +extern bool ldfile_add_remap_file + (const char *); +extern void ldfile_remap_input_free + (void); +extern const char * ldfile_possibly_remap_input + (const char *); +extern void ldfile_print_input_remaps + (void); + #endif diff --git a/ld/ldlang.c b/ld/ldlang.c index a9c4a4d5bd7..78716f17729 100644 --- a/ld/ldlang.c +++ b/ld/ldlang.c @@ -1125,6 +1125,10 @@ new_afile (const char *name, lang_has_input_file = true; + name = ldfile_possibly_remap_input (name); + if (name == NULL) + return NULL; + p = new_stat (lang_input_statement, stat_ptr); memset (&p->the_bfd, 0, sizeof (*p) - offsetof (lang_input_statement_type, the_bfd)); @@ -1328,6 +1332,7 @@ void lang_finish (void) { output_section_statement_table_free (); + ldfile_remap_input_free (); } /*---------------------------------------------------------------------- @@ -2280,6 +2285,8 @@ lang_map (void) lang_memory_region_type *m; bool dis_header_printed = false; + ldfile_print_input_remaps (); + LANG_FOR_EACH_INPUT_STATEMENT (file) { asection *s; diff --git a/ld/ldlex.h b/ld/ldlex.h index be942ec4327..87cac02141d 100644 --- a/ld/ldlex.h +++ b/ld/ldlex.h @@ -174,6 +174,8 @@ enum option_values OPTION_NO_WARN_RWX_SEGMENTS, OPTION_ENABLE_LINKER_VERSION, OPTION_DISABLE_LINKER_VERSION, + OPTION_REMAP_INPUTS, + OPTION_REMAP_INPUTS_FILE, }; /* The initial parser states. */ diff --git a/ld/lexsup.c b/ld/lexsup.c index 6090921bda4..4e20e938a49 100644 --- a/ld/lexsup.c +++ b/ld/lexsup.c @@ -219,6 +219,12 @@ static const struct ld_option ld_options[] = { {"just-symbols", required_argument, NULL, 'R'}, 'R', N_("FILE"), N_("Just link symbols (if directory, same as --rpath)"), TWO_DASHES }, + + { {"remap-inputs-file", required_argument, NULL, OPTION_REMAP_INPUTS_FILE}, + '\0', N_("FILE"), "Provide a FILE containing input remapings", TWO_DASHES }, + { {"remap-inputs", required_argument, NULL, OPTION_REMAP_INPUTS}, + '\0', N_("PATTERN=FILE"), "Remap input files matching PATTERN to FILE", TWO_DASHES }, + { {"strip-all", no_argument, NULL, 's'}, 's', NULL, N_("Strip all symbols"), TWO_DASHES }, { {"strip-debug", no_argument, NULL, 'S'}, @@ -1682,6 +1688,27 @@ parse_args (unsigned argc, char **argv) link_info.fini_function = optarg; break; + case OPTION_REMAP_INPUTS_FILE: + if (! ldfile_add_remap_file (optarg)) + einfo (_("%F%P: failed to add remap file %s\n"), optarg); + break; + + case OPTION_REMAP_INPUTS: + { + char *optarg2 = strchr (optarg, '='); + if (optarg2 == NULL) + /* FIXME: Should we allow --remap-inputs=@myfile as a synonym + for --remap-inputs-file=myfile ? */ + einfo (_("%F%P: invalid argument to option --remap-inputs\n")); + size_t len = optarg2 - optarg; + char * pattern = xmalloc (len + 1); + memcpy (pattern, optarg, len); + pattern[len] = 0; + ldfile_add_remap (pattern, optarg2 + 1); + free (pattern); + } + break; + case OPTION_REDUCE_MEMORY_OVERHEADS: link_info.reduce_memory_overheads = true; if (config.hash_table_size == 0) diff --git a/ld/testsuite/ld-misc/input-remap.exp b/ld/testsuite/ld-misc/input-remap.exp new file mode 100644 index 00000000000..950b7e88d2f --- /dev/null +++ b/ld/testsuite/ld-misc/input-remap.exp @@ -0,0 +1,75 @@ +# Test handling of --input-remap +# Copyright (C) 2023-2023 Free Software Foundation, Inc. +# +# This file is part of the GNU Binutils. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, +# MA 02110-1301, USA. +# + + +# First make sure that linking without remapping does not work: +file delete tmpdir/foo.o +setup_xfail *-*-* +run_ld_link_tests { + { "--remap-inputs (expected fail)" + "" + "-e 0" + "-o tmpdir/barzzz.o" + {foo.s} + {} + "foo" } +} + +global srcdir +global subdir + +# Now use the remap options to allow the linking to succeed. +# Note: we have to use the [list... format when we need to +# have $-substitutions. +run_ld_link_tests [list \ + { "--remap-inputs (simple)" + "--remap-inputs=tmpdir/foo.o=tmpdir/barzzz.o" + "-e 0" + "-o tmpdir/barzzz.o" + {foo.s} + {} + "foo" + } \ + { "--remap-inputs (wildcard)" + "--remap-inputs=*/foo.o=tmpdir/barzzz.o" + "-e 0" + "-o tmpdir/barzzz.o" + {foo.s} + {} + "foo" + } \ + [ list "--remap-inputs-file" \ + "--remap-inputs-file $srcdir/$subdir/remaps.txt" \ + "-e 0 bazzz.o" \ + "-o tmpdir/barzzz.o" \ + {foo.s} \ + {} \ + "foo" \ + ] \ + [ list "--remap-inputs-file (with map output)" \ + "--remap-inputs-file $srcdir/$subdir/remaps.txt" \ + "-e 0 --print-map" \ + "-o tmpdir/barzzz.o" \ + {foo.s} \ + { { ld remaps.r } } \ + "foo" \ + ] \ +] diff --git a/ld/testsuite/ld-misc/remaps.r b/ld/testsuite/ld-misc/remaps.r new file mode 100644 index 00000000000..472021974e5 --- /dev/null +++ b/ld/testsuite/ld-misc/remaps.r @@ -0,0 +1,6 @@ +#... +Input File Remapping + + Pattern: \*/foo\.o Maps To: tmpdir/barzzz\.o + Pattern: bazzz\.o Maps To: \ +#pass diff --git a/ld/testsuite/ld-misc/remaps.txt b/ld/testsuite/ld-misc/remaps.txt new file mode 100644 index 00000000000..4ca2a136c73 --- /dev/null +++ b/ld/testsuite/ld-misc/remaps.txt @@ -0,0 +1,4 @@ +# A comment +*/foo.o tmpdir/barzzz.o # A remapping +bazzz.o=/dev/null # A deletion +