From: Fangrui Song Date: Wed, 26 Apr 2023 20:18:55 +0000 (-0700) Subject: [ELF] Add --remap-inputs= and --remap-inputs-file= X-Git-Tag: upstream/17.0.6~10298 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=39c20a63b150053fcee5928be7ea1b3b0a51c9d4;p=platform%2Fupstream%2Fllvm.git [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 Link: https://discourse.llvm.org/t/rfc-support-exclude-inputs/70070 Reviewed By: melver, peter.smith Differential Revision: https://reviews.llvm.org/D148859 --- diff --git a/lld/ELF/Config.h b/lld/ELF/Config.h index c446508..9c9a49d 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 remapInputs; + // If an input file matches a wildcard pattern, remap it to the value. + llvm::SmallVector, 0> + remapInputsWildcards; }; struct ConfigWrapper { Config c; diff --git a/lld/ELF/Driver.cpp b/lld/ELF/Driver.cpp index 79f16a2..f235ffc 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 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 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 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 kv = StringRef(arg->getValue()).split('='); diff --git a/lld/ELF/DriverUtils.cpp b/lld/ELF/DriverUtils.cpp index 254f4b7..50e9f50 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 b7686e8..b8291fb 100644 --- a/lld/ELF/InputFiles.cpp +++ b/lld/ELF/InputFiles.cpp @@ -195,6 +195,29 @@ std::optional 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 5b3d9f6..8f2a440 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 to ">, + MetaVarName<"=">; + +def remap_inputs_file: JJ<"remap-inputs-file=">, + HelpText<"Each line contains 'from-glob=to-file'. An input file matching is remapped to ">, + MetaVarName<"">; + 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 50c556b..393b3f7 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 `_) + Breaking changes ---------------- diff --git a/lld/docs/ld.lld.1 b/lld/docs/ld.lld.1 index d97cd26a..e3db3a2 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 0000000..8a2d2e98 --- /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