diff options
author | Fangrui Song <i@maskray.me> | 2023-04-26 13:18:55 -0700 |
---|---|---|
committer | Fangrui Song <i@maskray.me> | 2023-04-26 13:18:55 -0700 |
commit | 39c20a63b150053fcee5928be7ea1b3b0a51c9d4 (patch) | |
tree | a7ab7c4b0fad5f3798e97d6b51318d21316962bf /lld/ELF | |
parent | 65443f6f0a70f3ced43e688053cd6c1642d81ff6 (diff) | |
download | llvm-39c20a63b150053fcee5928be7ea1b3b0a51c9d4.tar.gz |
[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@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
Diffstat (limited to 'lld/ELF')
-rw-r--r-- | lld/ELF/Config.h | 6 | ||||
-rw-r--r-- | lld/ELF/Driver.cpp | 34 | ||||
-rw-r--r-- | lld/ELF/DriverUtils.cpp | 1 | ||||
-rw-r--r-- | lld/ELF/InputFiles.cpp | 23 | ||||
-rw-r--r-- | lld/ELF/Options.td | 8 |
5 files changed, 72 insertions, 0 deletions
diff --git a/lld/ELF/Config.h b/lld/ELF/Config.h index c446508dfa2f..9c9a49d9f20e 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 79f16a281df9..f235ffcbb955 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 254f4b722462..50e9f509bcb4 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 b7686e8e3ba9..b8291fb4307b 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 5b3d9f6c40de..8f2a440fd67a 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">; |