+2023-06-14 Nick Clifton <nickc@redhat.com>
+
+ 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 <nickc@redhat.com>
PR 30499
-*- text -*-
+* The linker now accepts a command line option of --remap-inputs
+ <PATTERN>=<FILE> to relace any input file that matches <PATTERN> with
+ <FILE>. In addition the option --remap-inputs-file=<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).
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
#include "ldemul.h"
#include "libiberty.h"
#include "filenames.h"
+#include <fnmatch.h>
#if BFD_SUPPORTS_PLUGINS
#include "plugin-api.h"
#include "plugin.h"
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 : _("<discard>"));
+}
+
+
/* Test whether a pathname, after canonicalization, is the same or a
sub-directory of the sysroot directory. */
(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
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));
lang_finish (void)
{
output_section_statement_table_free ();
+ ldfile_remap_input_free ();
}
/*----------------------------------------------------------------------
lang_memory_region_type *m;
bool dis_header_printed = false;
+ ldfile_print_input_remaps ();
+
LANG_FOR_EACH_INPUT_STATEMENT (file)
{
asection *s;
OPTION_NO_WARN_RWX_SEGMENTS,
OPTION_ENABLE_LINKER_VERSION,
OPTION_DISABLE_LINKER_VERSION,
+ OPTION_REMAP_INPUTS,
+ OPTION_REMAP_INPUTS_FILE,
};
/* The initial parser states. */
{ {"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'},
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)
--- /dev/null
+# 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" \
+ ] \
+]
--- /dev/null
+#...
+Input File Remapping
+
+ Pattern: \*/foo\.o Maps To: tmpdir/barzzz\.o
+ Pattern: bazzz\.o Maps To: \<discard\>
+#pass
--- /dev/null
+# A comment
+*/foo.o tmpdir/barzzz.o # A remapping
+bazzz.o=/dev/null # A deletion
+