Add --remap-inputs option to the BFD linker.
authorNick Clifton <nickc@redhat.com>
Wed, 14 Jun 2023 12:39:03 +0000 (13:39 +0100)
committerNick Clifton <nickc@redhat.com>
Wed, 14 Jun 2023 12:39:03 +0000 (13:39 +0100)
  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.

ld/ChangeLog
ld/NEWS
ld/ld.texi
ld/ldfile.c
ld/ldfile.h
ld/ldlang.c
ld/ldlex.h
ld/lexsup.c
ld/testsuite/ld-misc/input-remap.exp [new file with mode: 0644]
ld/testsuite/ld-misc/remaps.r [new file with mode: 0644]
ld/testsuite/ld-misc/remaps.txt [new file with mode: 0644]

index d722d0ba4827714543f75682605ee3e9e05e3edb..8fd6504fa6ef9e2015b976b948c52f5d3f1f8ced 100644 (file)
@@ -1,3 +1,25 @@
+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
diff --git a/ld/NEWS b/ld/NEWS
index 9920d0209b828b0e2303b229b86451020b476436..4dee2301158a2f06ac76169409c2823413e0b77d 100644 (file)
--- a/ld/NEWS
+++ b/ld/NEWS
@@ -1,5 +1,10 @@
 -*- 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).
 
index 5639de797de39cd66010622b49af256e4471a02d..a007ae7fadea4a0c8be4d36217bf2a28df970500 100644 (file)
@@ -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
index b8fd4e5d8e0af3e2c3da4aa8eb207bc8d2db5e9c..4976367bbf03b09df6e5625a676acb309e1f30ea 100644 (file)
@@ -34,6 +34,7 @@
 #include "ldemul.h"
 #include "libiberty.h"
 #include "filenames.h"
+#include <fnmatch.h>
 #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 : _("<discard>"));
+}
+
+
 /* Test whether a pathname, after canonicalization, is the same or a
    sub-directory of the sysroot directory.  */
 
index defb550f76bdc4d8b44416ea57b8cbea5e3fdd08..153fd0449bc306e8e1c5e68c5b5148fd8d3c8923 100644 (file)
@@ -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
index a9c4a4d5bd7010e5ee5d6afa900a7acd87a15cef..78716f17729103a4310207a2db6c236926ce6af4 100644 (file)
@@ -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;
index be942ec432733f9f7a86b260fe87ee370f429ad8..87cac02141d8c8cf090001ec877dd4e458d19c1b 100644 (file)
@@ -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.  */
index 6090921bda434575fa037b37afd5d3f83a9339a1..4e20e938a49dbfd367e1dd7c5752135f248673d9 100644 (file)
@@ -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 (file)
index 0000000..950b7e8
--- /dev/null
@@ -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 (file)
index 0000000..4720219
--- /dev/null
@@ -0,0 +1,6 @@
+#...
+Input File Remapping
+
+  Pattern: \*/foo\.o   Maps To: tmpdir/barzzz\.o
+  Pattern: bazzz\.o    Maps To: \<discard\>
+#pass
diff --git a/ld/testsuite/ld-misc/remaps.txt b/ld/testsuite/ld-misc/remaps.txt
new file mode 100644 (file)
index 0000000..4ca2a13
--- /dev/null
@@ -0,0 +1,4 @@
+# A comment
+*/foo.o tmpdir/barzzz.o # A remapping
+bazzz.o=/dev/null # A deletion
+