[lld] 39c20a6 - [ELF] Add --remap-inputs= and --remap-inputs-file=

Fangrui Song via llvm-commits llvm-commits at lists.llvm.org
Wed Apr 26 13:19:00 PDT 2023


Author: Fangrui Song
Date: 2023-04-26T13:18:55-07:00
New Revision: 39c20a63b150053fcee5928be7ea1b3b0a51c9d4

URL: https://github.com/llvm/llvm-project/commit/39c20a63b150053fcee5928be7ea1b3b0a51c9d4
DIFF: https://github.com/llvm/llvm-project/commit/39c20a63b150053fcee5928be7ea1b3b0a51c9d4.diff

LOG: [ELF] Add --remap-inputs= and --remap-inputs-file=

--remap-inputs-file= can be specified multiple times, each naming a
remap file that contains `from-glob=to-file` lines or `#`-led comments.
('=' is used a separator a la -fdebug-prefix-map=)
--remap-inputs-file= can be used to:

* replace an input file. E.g. `"*/libz.so=exp/libz.so"` can replace a resolved
  `-lz` without updating the input file list or (if used) a response file.
  When debugging an application where a bug is isolated to one single
  input file, this option gives a convenient way to test fixes.
* remove an input file with `/dev/null` (changed to `NUL` on Windows), e.g.
  `"a.o=/dev/null"`. A build system may add unneeded dependencies.
  This option gives a convenient way to test the result removing some inputs.

`--remap-inputs=a.o=aa.o` can be specified to provide one pattern without using
an extra file.
(bash/zsh process substitution is handy for specifying a pattern without using
a remap file, e.g. `--remap-inputs-file=<(printf 'a.o=aa.o')`, but it may be
unavailable in some systems. An extra file can be inconvenient for a build
system.)

Exact patterns are tested before wildcard patterns. In case of a tie, the first
patterns wins. This is an implementation detail that users should not rely on.

Co-authored-by: Marco Elver <elver at google.com>
Link: https://discourse.llvm.org/t/rfc-support-exclude-inputs/70070

Reviewed By: melver, peter.smith

Differential Revision: https://reviews.llvm.org/D148859

Added: 
    lld/test/ELF/remap-inputs.test

Modified: 
    lld/ELF/Config.h
    lld/ELF/Driver.cpp
    lld/ELF/DriverUtils.cpp
    lld/ELF/InputFiles.cpp
    lld/ELF/Options.td
    lld/docs/ReleaseNotes.rst
    lld/docs/ld.lld.1

Removed: 
    


################################################################################
diff  --git a/lld/ELF/Config.h b/lld/ELF/Config.h
index c446508dfa2f0..9c9a49d9f20ed 100644
--- a/lld/ELF/Config.h
+++ b/lld/ELF/Config.h
@@ -407,6 +407,12 @@ struct Config {
   bool androidMemtagStack;
 
   unsigned threadCount;
+
+  // If an input file equals a key, remap it to the value.
+  llvm::DenseMap<llvm::StringRef, llvm::StringRef> remapInputs;
+  // If an input file matches a wildcard pattern, remap it to the value.
+  llvm::SmallVector<std::pair<llvm::GlobPattern, llvm::StringRef>, 0>
+      remapInputsWildcards;
 };
 struct ConfigWrapper {
   Config c;

diff  --git a/lld/ELF/Driver.cpp b/lld/ELF/Driver.cpp
index 79f16a281df9b..f235ffcbb9559 100644
--- a/lld/ELF/Driver.cpp
+++ b/lld/ELF/Driver.cpp
@@ -1077,6 +1077,25 @@ static bool isValidReportString(StringRef arg) {
   return arg == "none" || arg == "warning" || arg == "error";
 }
 
+// Process a remap pattern 'from-glob=to-file'.
+static bool remapInputs(StringRef line, const Twine &location) {
+  SmallVector<StringRef, 0> fields;
+  line.split(fields, '=');
+  if (fields.size() != 2 || fields[1].empty()) {
+    error(location + ": parse error, not 'from-glob=to-file'");
+    return true;
+  }
+  if (!hasWildcard(fields[0]))
+    config->remapInputs[fields[0]] = fields[1];
+  else if (Expected<GlobPattern> pat = GlobPattern::create(fields[0]))
+    config->remapInputsWildcards.emplace_back(std::move(*pat), fields[1]);
+  else {
+    error(location + ": " + toString(pat.takeError()));
+    return true;
+  }
+  return false;
+}
+
 // Initializes Config members by the command line options.
 static void readConfigs(opt::InputArgList &args) {
   errorHandler().verbose = args.hasArg(OPT_verbose);
@@ -1335,6 +1354,21 @@ static void readConfigs(opt::InputArgList &args) {
       config->optEL = true;
   }
 
+  for (opt::Arg *arg : args.filtered(OPT_remap_inputs)) {
+    StringRef value(arg->getValue());
+    remapInputs(value, arg->getSpelling());
+  }
+  for (opt::Arg *arg : args.filtered(OPT_remap_inputs_file)) {
+    StringRef filename(arg->getValue());
+    std::optional<MemoryBufferRef> buffer = readFile(filename);
+    if (!buffer)
+      continue;
+    // Parse 'from-glob=to-file' lines, ignoring #-led comments.
+    for (auto [lineno, line] : llvm::enumerate(args::getLines(*buffer)))
+      if (remapInputs(line, filename + ":" + Twine(lineno + 1)))
+        break;
+  }
+
   for (opt::Arg *arg : args.filtered(OPT_shuffle_sections)) {
     constexpr StringRef errPrefix = "--shuffle-sections=: ";
     std::pair<StringRef, StringRef> kv = StringRef(arg->getValue()).split('=');

diff  --git a/lld/ELF/DriverUtils.cpp b/lld/ELF/DriverUtils.cpp
index 254f4b7224628..50e9f509bcb47 100644
--- a/lld/ELF/DriverUtils.cpp
+++ b/lld/ELF/DriverUtils.cpp
@@ -192,6 +192,7 @@ std::string elf::createResponseFile(const opt::InputArgList &args) {
     case OPT_export_dynamic_symbol_list:
     case OPT_just_symbols:
     case OPT_library_path:
+    case OPT_remap_inputs_file:
     case OPT_retain_symbols_file:
     case OPT_rpath:
     case OPT_script:

diff  --git a/lld/ELF/InputFiles.cpp b/lld/ELF/InputFiles.cpp
index b7686e8e3ba99..b8291fb4307bc 100644
--- a/lld/ELF/InputFiles.cpp
+++ b/lld/ELF/InputFiles.cpp
@@ -195,6 +195,29 @@ std::optional<MemoryBufferRef> elf::readFile(StringRef path) {
   if (!config->chroot.empty() && path.startswith("/"))
     path = saver().save(config->chroot + path);
 
+  bool remapped = false;
+  auto it = config->remapInputs.find(path);
+  if (it != config->remapInputs.end()) {
+    path = it->second;
+    remapped = true;
+  } else {
+    for (const auto &[pat, toFile] : config->remapInputsWildcards) {
+      if (pat.match(path)) {
+        path = toFile;
+        remapped = true;
+        break;
+      }
+    }
+  }
+  if (remapped) {
+    // Use /dev/null to indicate an input file that should be ignored. Change
+    // the path to NUL on Windows.
+#ifdef _WIN32
+    if (path == "/dev/null")
+      path = "NUL";
+#endif
+  }
+
   log(path);
   config->dependencyFiles.insert(llvm::CachedHashString(path));
 

diff  --git a/lld/ELF/Options.td b/lld/ELF/Options.td
index 5b3d9f6c40dee..8f2a440fd67a2 100644
--- a/lld/ELF/Options.td
+++ b/lld/ELF/Options.td
@@ -359,6 +359,14 @@ defm relax_gp: BB<"relax-gp",
   "Enable global pointer relaxation",
   "Disable global pointer relaxation (default)">;
 
+defm remap_inputs: EEq<"remap-inputs",
+  "Remap input files matching <from-glob> to <to-file>">,
+  MetaVarName<"<from-glob>=<to-file>">;
+
+def remap_inputs_file: JJ<"remap-inputs-file=">,
+  HelpText<"Each line contains 'from-glob=to-file'. An input file matching <from-glob> is remapped to <to-file>">,
+  MetaVarName<"<file>">;
+
 defm reproduce:
   EEq<"reproduce",
      "Write tar file containing inputs and command to reproduce link">;

diff  --git a/lld/docs/ReleaseNotes.rst b/lld/docs/ReleaseNotes.rst
index 50c556bb8b311..393b3f7d848de 100644
--- a/lld/docs/ReleaseNotes.rst
+++ b/lld/docs/ReleaseNotes.rst
@@ -26,6 +26,9 @@ Non-comprehensive list of changes in this release
 ELF Improvements
 ----------------
 
+* ``--remap-inputs=`` and ``--remap-inputs-file=`` are added to remap input files.
+  (`D148859 <https://reviews.llvm.org/D148859>`_)
+
 Breaking changes
 ----------------
 

diff  --git a/lld/docs/ld.lld.1 b/lld/docs/ld.lld.1
index d97cd26a6378b..e3db3a20678d9 100644
--- a/lld/docs/ld.lld.1
+++ b/lld/docs/ld.lld.1
@@ -497,6 +497,21 @@ Restore the states saved by
 Enable global pointer relaxation for RISC-V.
 .It Fl -relocatable , Fl r
 Create relocatable object file.
+.It Fl -remap-inputs Ns = Ns Ar from-glob=to-file
+Input files matching
+.Cm from-glob
+are mapped to
+.Cm to-file.
+Use
+.Cm /dev/null
+to ignore an input file.
+.It Fl -remap-inputs-file Ns = Ns Ar file
+Remap input files based on patterns in
+.Ar file .
+Each line in the remap file is of the format
+.Cm from-glob=to-file
+or a comment starting with
+.Cm # .
 .It Fl -reproduce Ns = Ns Ar path
 Write a tar file to
 .Ar path,

diff  --git a/lld/test/ELF/remap-inputs.test b/lld/test/ELF/remap-inputs.test
new file mode 100644
index 0000000000000..8a2d2e9891064
--- /dev/null
+++ b/lld/test/ELF/remap-inputs.test
@@ -0,0 +1,85 @@
+# REQUIRES: x86
+## --remap-inputs and --remap-inputs-file= remap input files.
+
+# RUN: rm -rf %t && split-file %s %t && cd %t
+# RUN: llvm-mc -filetype=obj -triple=x86_64 a.s -o a.o
+# RUN: llvm-as b.ll -o b.o
+# RUN: llvm-mc -filetype=obj -triple=x86_64 c.s -o c.o && llvm-ar rc c.a c.o
+# RUN: llvm-mc -filetype=obj -triple=x86_64 d.s -o d.o && ld.lld -shared -soname=d d.o -o d.so
+# RUN: ld.lld --remap-inputs-file=1.map --remap-inputs-file=2.map --remap-inputs='d*.so=d.so' --reproduce=repro.tar aa.o bb.bc cc.a dd.so empty -o out
+# RUN: tar tf repro.tar | FileCheck %s --check-prefix=REPRO
+
+# REPRO:      1.map
+# REPRO-NEXT: 2.map
+# REPRO-NEXT: a.o
+# REPRO-NEXT: b.o
+# REPRO-NEXT: c.a
+# REPRO-NEXT: d.so
+
+## --remap-inputs can also be specified multiple times.
+# RUN: ld.lld --remap-inputs 'aa.o=a.o' --remap-inputs='d[d].so=d.so' aa.o b.o c.a d.so -o /dev/null
+
+## A multiple-to-one pattern may easily cause issues. Users should be careful.
+# RUN: not ld.lld --remap-inputs-file=3.map aa.o bb.bc -o /dev/null 2>&1 | \
+# RUN:   FileCheck %s --check-prefix=DUPLICATE --implicit-check-not=error:
+# DUPLICATE: error: duplicate symbol: _start
+
+# RUN: not ld.lld --remap-inputs-file=err1.map aa.o bb.bc -o /dev/null 2>&1 | \
+# RUN:   FileCheck %s --check-prefix=ERR1 --implicit-check-not=error:
+# ERR1:      error: err1.map:2: parse error, not 'from-glob=to-file'
+# ERR1-NEXT: error: cannot open bb.bc: {{.*}}
+
+# RUN: not ld.lld --remap-inputs-file=err2.map aa.o -o /dev/null 2>&1 | \
+# RUN:   FileCheck %s --check-prefix=ERR2 --implicit-check-not=error:
+# ERR2:      error: err2.map:1: invalid glob pattern: aa.[o
+# ERR2-NEXT: error: cannot open aa.o: {{.*}}
+
+# RUN: not ld.lld --remap-inputs=aa.o aa.o -o /dev/null 2>&1 | \
+# RUN:   FileCheck %s --check-prefix=ERR3 --implicit-check-not=error:
+# RUN: not ld.lld --remap-inputs=aa.o= aa.o -o /dev/null 2>&1 | \
+# RUN:   FileCheck %s --check-prefix=ERR3 --implicit-check-not=error:
+# ERR3:      error: --remap-inputs: parse error, not 'from-glob=to-file'
+# ERR3-NEXT: error: cannot open aa.o: {{.*}}
+
+#--- a.s
+.globl _start
+_start:
+  call b
+  call c
+  call d
+
+#--- b.ll
+target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128"
+target triple = "x86_64-unknown-linux-gnu"
+
+define void @b() {
+  ret void
+}
+
+#--- c.s
+.globl c
+c:
+
+#--- d.s
+.globl d
+d:
+
+#--- 1.map
+aa.o=a.o
+b?.[b]c=b.o
+
+#--- 2.map
+cc.a=c.a
+## Use /dev/null to indicate an input file which should be ignored.
+empty=/dev/null
+
+#--- 3.map
+*=a.o
+
+#--- err1.map
+aa.o=a.o
+bb.bc
+cc.a
+
+#--- err2.map
+aa.[o=a.o


        


More information about the llvm-commits mailing list