diff options
author | Jordan Rupprecht <rupprecht@google.com> | 2019-10-17 20:51:00 +0000 |
---|---|---|
committer | Jordan Rupprecht <rupprecht@google.com> | 2019-10-17 20:51:00 +0000 |
commit | edeebad7715774b8481103733dc5d52dac43bdf3 (patch) | |
tree | c84c0c304f7142e3ecef79ccd5120179a6dc1e60 /llvm/tools | |
parent | 9c5d76ff4d15e2cabf976911bd150302ae5fdeea (diff) | |
download | llvm-edeebad7715774b8481103733dc5d52dac43bdf3.zip llvm-edeebad7715774b8481103733dc5d52dac43bdf3.tar.gz llvm-edeebad7715774b8481103733dc5d52dac43bdf3.tar.bz2 |
[llvm-objcopy] Add support for shell wildcards
Summary: GNU objcopy accepts the --wildcard flag to allow wildcard matching on symbol-related flags. (Note: it's implicitly true for section flags).
The basic syntax is to allow *, ?, \, and [] which work similarly to how they work in a shell. Additionally, starting a wildcard with ! causes that wildcard to prevent it from matching a flag.
Use an updated GlobPattern in libSupport to handle these patterns. It does not fully match the `fnmatch` used by GNU objcopy since named character classes (e.g. `[[:digit:]]`) are not supported, but this should support most existing use cases (mostly just `*` is what's used anyway).
Reviewers: jhenderson, MaskRay, evgeny777, espindola, alexshap
Reviewed By: MaskRay
Subscribers: nickdesaulniers, emaste, arichardson, hiraditya, jakehehrlich, abrachet, seiya, llvm-commits
Tags: #llvm
Differential Revision: https://reviews.llvm.org/D66613
llvm-svn: 375169
Diffstat (limited to 'llvm/tools')
-rw-r--r-- | llvm/tools/llvm-objcopy/CommonOpts.td | 10 | ||||
-rw-r--r-- | llvm/tools/llvm-objcopy/CopyConfig.cpp | 160 | ||||
-rw-r--r-- | llvm/tools/llvm-objcopy/CopyConfig.h | 56 | ||||
-rw-r--r-- | llvm/tools/llvm-objcopy/llvm-objcopy.cpp | 2 |
4 files changed, 179 insertions, 49 deletions
diff --git a/llvm/tools/llvm-objcopy/CommonOpts.td b/llvm/tools/llvm-objcopy/CommonOpts.td index 597163e..e8c092b4 100644 --- a/llvm/tools/llvm-objcopy/CommonOpts.td +++ b/llvm/tools/llvm-objcopy/CommonOpts.td @@ -111,3 +111,13 @@ def version : Flag<["--"], "version">, def V : Flag<["-"], "V">, Alias<version>, HelpText<"Alias for --version">; + +def wildcard + : Flag<["--"], "wildcard">, + HelpText<"Allow wildcard syntax for symbol-related flags. Incompatible " + "with --regex. Allows using '*' to match any number of " + "characters, '?' to match any single character, '\' to escape " + "special characters, and '[]' to define character classes. " + "Wildcards beginning with '!' will prevent a match, for example " + "\"-N '*' -N '!x'\" will strip all symbols except for \"x\".">; +def w : Flag<["-"], "w">, Alias<wildcard>, HelpText<"Alias for --wildcard">; diff --git a/llvm/tools/llvm-objcopy/CopyConfig.cpp b/llvm/tools/llvm-objcopy/CopyConfig.cpp index 952b3a7..c805631 100644 --- a/llvm/tools/llvm-objcopy/CopyConfig.cpp +++ b/llvm/tools/llvm-objcopy/CopyConfig.cpp @@ -260,8 +260,10 @@ getOutputTargetInfoByTargetName(StringRef TargetName) { return {TargetInfo{Format, MI}}; } -static Error addSymbolsFromFile(NameMatcher &Symbols, BumpPtrAllocator &Alloc, - StringRef Filename, bool UseRegex) { +static Error +addSymbolsFromFile(NameMatcher &Symbols, BumpPtrAllocator &Alloc, + StringRef Filename, MatchStyle MS, + llvm::function_ref<Error(Error)> ErrorCallback) { StringSaver Saver(Alloc); SmallVector<StringRef, 16> Lines; auto BufOrErr = MemoryBuffer::getFile(Filename); @@ -274,21 +276,46 @@ static Error addSymbolsFromFile(NameMatcher &Symbols, BumpPtrAllocator &Alloc, // it's not empty. auto TrimmedLine = Line.split('#').first.trim(); if (!TrimmedLine.empty()) - Symbols.addMatcher({Saver.save(TrimmedLine), UseRegex}); + if (Error E = Symbols.addMatcher(NameOrPattern::create( + Saver.save(TrimmedLine), MS, ErrorCallback))) + return E; } return Error::success(); } -NameOrRegex::NameOrRegex(StringRef Pattern, bool IsRegex) { - if (!IsRegex) { - Name = Pattern; - return; - } +Expected<NameOrPattern> +NameOrPattern::create(StringRef Pattern, MatchStyle MS, + llvm::function_ref<Error(Error)> ErrorCallback) { + switch (MS) { + case MatchStyle::Literal: + return NameOrPattern(Pattern); + case MatchStyle::Wildcard: { + SmallVector<char, 32> Data; + bool IsPositiveMatch = true; + if (Pattern[0] == '!') { + IsPositiveMatch = false; + Pattern = Pattern.drop_front(); + } + Expected<GlobPattern> GlobOrErr = GlobPattern::create(Pattern); + + // If we couldn't create it as a glob, report the error, but try again with + // a literal if the error reporting is non-fatal. + if (!GlobOrErr) { + if (Error E = ErrorCallback(GlobOrErr.takeError())) + return std::move(E); + return create(Pattern, MatchStyle::Literal, ErrorCallback); + } - SmallVector<char, 32> Data; - R = std::make_shared<Regex>( - ("^" + Pattern.ltrim('^').rtrim('$') + "$").toStringRef(Data)); + return NameOrPattern(std::make_shared<GlobPattern>(*GlobOrErr), + IsPositiveMatch); + } + case MatchStyle::Regex: { + SmallVector<char, 32> Data; + return NameOrPattern(std::make_shared<Regex>( + ("^" + Pattern.ltrim('^').rtrim('$') + "$").toStringRef(Data))); + } + } } static Error addSymbolsToRenameFromFile(StringMap<StringRef> &SymbolsToRename, @@ -338,7 +365,9 @@ static void printHelp(const opt::OptTable &OptTable, raw_ostream &OS, // ParseObjcopyOptions returns the config and sets the input arguments. If a // help flag is set then ParseObjcopyOptions will print the help messege and // exit. -Expected<DriverConfig> parseObjcopyOptions(ArrayRef<const char *> ArgsArr) { +Expected<DriverConfig> +parseObjcopyOptions(ArrayRef<const char *> ArgsArr, + llvm::function_ref<Error(Error)> ErrorCallback) { DriverConfig DC; ObjcopyOptTable T; unsigned MissingArgumentIndex, MissingArgumentCount; @@ -387,7 +416,18 @@ Expected<DriverConfig> parseObjcopyOptions(ArrayRef<const char *> ArgsArr) { errc::invalid_argument, "--target cannot be used with --input-target or --output-target"); - bool UseRegex = InputArgs.hasArg(OBJCOPY_regex); + if (InputArgs.hasArg(OBJCOPY_regex) && InputArgs.hasArg(OBJCOPY_wildcard)) + return createStringError(errc::invalid_argument, + "--regex and --wildcard are incompatible"); + + MatchStyle SectionMatchStyle = InputArgs.hasArg(OBJCOPY_regex) + ? MatchStyle::Regex + : MatchStyle::Wildcard; + MatchStyle SymbolMatchStyle = InputArgs.hasArg(OBJCOPY_regex) + ? MatchStyle::Regex + : InputArgs.hasArg(OBJCOPY_wildcard) + ? MatchStyle::Wildcard + : MatchStyle::Literal; StringRef InputFormat, OutputFormat; if (InputArgs.hasArg(OBJCOPY_target)) { InputFormat = InputArgs.getLastArgValue(OBJCOPY_target); @@ -541,11 +581,17 @@ Expected<DriverConfig> parseObjcopyOptions(ArrayRef<const char *> ArgsArr) { } for (auto Arg : InputArgs.filtered(OBJCOPY_remove_section)) - Config.ToRemove.addMatcher({Arg->getValue(), UseRegex}); + if (Error E = Config.ToRemove.addMatcher(NameOrPattern::create( + Arg->getValue(), SectionMatchStyle, ErrorCallback))) + return std::move(E); for (auto Arg : InputArgs.filtered(OBJCOPY_keep_section)) - Config.KeepSection.addMatcher({Arg->getValue(), UseRegex}); + if (Error E = Config.KeepSection.addMatcher(NameOrPattern::create( + Arg->getValue(), SectionMatchStyle, ErrorCallback))) + return std::move(E); for (auto Arg : InputArgs.filtered(OBJCOPY_only_section)) - Config.OnlySection.addMatcher({Arg->getValue(), UseRegex}); + if (Error E = Config.OnlySection.addMatcher(NameOrPattern::create( + Arg->getValue(), SectionMatchStyle, ErrorCallback))) + return std::move(E); for (auto Arg : InputArgs.filtered(OBJCOPY_add_section)) { StringRef ArgValue(Arg->getValue()); if (!ArgValue.contains('=')) @@ -583,46 +629,68 @@ Expected<DriverConfig> parseObjcopyOptions(ArrayRef<const char *> ArgsArr) { if (Config.DiscardMode == DiscardType::All) Config.StripDebug = true; for (auto Arg : InputArgs.filtered(OBJCOPY_localize_symbol)) - Config.SymbolsToLocalize.addMatcher({Arg->getValue(), UseRegex}); + if (Error E = Config.SymbolsToLocalize.addMatcher(NameOrPattern::create( + Arg->getValue(), SymbolMatchStyle, ErrorCallback))) + return std::move(E); for (auto Arg : InputArgs.filtered(OBJCOPY_localize_symbols)) if (Error E = addSymbolsFromFile(Config.SymbolsToLocalize, DC.Alloc, - Arg->getValue(), UseRegex)) + Arg->getValue(), SymbolMatchStyle, + ErrorCallback)) return std::move(E); for (auto Arg : InputArgs.filtered(OBJCOPY_keep_global_symbol)) - Config.SymbolsToKeepGlobal.addMatcher({Arg->getValue(), UseRegex}); + if (Error E = Config.SymbolsToKeepGlobal.addMatcher(NameOrPattern::create( + Arg->getValue(), SymbolMatchStyle, ErrorCallback))) + return std::move(E); for (auto Arg : InputArgs.filtered(OBJCOPY_keep_global_symbols)) if (Error E = addSymbolsFromFile(Config.SymbolsToKeepGlobal, DC.Alloc, - Arg->getValue(), UseRegex)) + Arg->getValue(), SymbolMatchStyle, + ErrorCallback)) return std::move(E); for (auto Arg : InputArgs.filtered(OBJCOPY_globalize_symbol)) - Config.SymbolsToGlobalize.addMatcher({Arg->getValue(), UseRegex}); + if (Error E = Config.SymbolsToGlobalize.addMatcher(NameOrPattern::create( + Arg->getValue(), SymbolMatchStyle, ErrorCallback))) + return std::move(E); for (auto Arg : InputArgs.filtered(OBJCOPY_globalize_symbols)) if (Error E = addSymbolsFromFile(Config.SymbolsToGlobalize, DC.Alloc, - Arg->getValue(), UseRegex)) + Arg->getValue(), SymbolMatchStyle, + ErrorCallback)) return std::move(E); for (auto Arg : InputArgs.filtered(OBJCOPY_weaken_symbol)) - Config.SymbolsToWeaken.addMatcher({Arg->getValue(), UseRegex}); + if (Error E = Config.SymbolsToWeaken.addMatcher(NameOrPattern::create( + Arg->getValue(), SymbolMatchStyle, ErrorCallback))) + return std::move(E); for (auto Arg : InputArgs.filtered(OBJCOPY_weaken_symbols)) if (Error E = addSymbolsFromFile(Config.SymbolsToWeaken, DC.Alloc, - Arg->getValue(), UseRegex)) + Arg->getValue(), SymbolMatchStyle, + ErrorCallback)) return std::move(E); for (auto Arg : InputArgs.filtered(OBJCOPY_strip_symbol)) - Config.SymbolsToRemove.addMatcher({Arg->getValue(), UseRegex}); + if (Error E = Config.SymbolsToRemove.addMatcher(NameOrPattern::create( + Arg->getValue(), SymbolMatchStyle, ErrorCallback))) + return std::move(E); for (auto Arg : InputArgs.filtered(OBJCOPY_strip_symbols)) if (Error E = addSymbolsFromFile(Config.SymbolsToRemove, DC.Alloc, - Arg->getValue(), UseRegex)) + Arg->getValue(), SymbolMatchStyle, + ErrorCallback)) return std::move(E); for (auto Arg : InputArgs.filtered(OBJCOPY_strip_unneeded_symbol)) - Config.UnneededSymbolsToRemove.addMatcher({Arg->getValue(), UseRegex}); + if (Error E = + Config.UnneededSymbolsToRemove.addMatcher(NameOrPattern::create( + Arg->getValue(), SymbolMatchStyle, ErrorCallback))) + return std::move(E); for (auto Arg : InputArgs.filtered(OBJCOPY_strip_unneeded_symbols)) if (Error E = addSymbolsFromFile(Config.UnneededSymbolsToRemove, DC.Alloc, - Arg->getValue(), UseRegex)) + Arg->getValue(), SymbolMatchStyle, + ErrorCallback)) return std::move(E); for (auto Arg : InputArgs.filtered(OBJCOPY_keep_symbol)) - Config.SymbolsToKeep.addMatcher({Arg->getValue(), UseRegex}); + if (Error E = Config.SymbolsToKeep.addMatcher(NameOrPattern::create( + Arg->getValue(), SymbolMatchStyle, ErrorCallback))) + return std::move(E); for (auto Arg : InputArgs.filtered(OBJCOPY_keep_symbols)) - if (Error E = addSymbolsFromFile(Config.SymbolsToKeep, DC.Alloc, - Arg->getValue(), UseRegex)) + if (Error E = + addSymbolsFromFile(Config.SymbolsToKeep, DC.Alloc, Arg->getValue(), + SymbolMatchStyle, ErrorCallback)) return std::move(E); for (auto Arg : InputArgs.filtered(OBJCOPY_add_symbol)) Config.SymbolsToAdd.push_back(Arg->getValue()); @@ -688,7 +756,7 @@ Expected<DriverConfig> parseObjcopyOptions(ArrayRef<const char *> ArgsArr) { // exit. Expected<DriverConfig> parseStripOptions(ArrayRef<const char *> ArgsArr, - std::function<Error(Error)> ErrorCallback) { + llvm::function_ref<Error(Error)> ErrorCallback) { StripOptTable T; unsigned MissingArgumentIndex, MissingArgumentCount; llvm::opt::InputArgList InputArgs = @@ -726,7 +794,17 @@ parseStripOptions(ArrayRef<const char *> ArgsArr, "multiple input files cannot be used in combination with -o"); CopyConfig Config; - bool UseRegexp = InputArgs.hasArg(STRIP_regex); + + if (InputArgs.hasArg(STRIP_regex) && InputArgs.hasArg(STRIP_wildcard)) + return createStringError(errc::invalid_argument, + "--regex and --wildcard are incompatible"); + MatchStyle SectionMatchStyle = + InputArgs.hasArg(STRIP_regex) ? MatchStyle::Regex : MatchStyle::Wildcard; + MatchStyle SymbolMatchStyle = InputArgs.hasArg(STRIP_regex) + ? MatchStyle::Regex + : InputArgs.hasArg(STRIP_wildcard) + ? MatchStyle::Wildcard + : MatchStyle::Literal; Config.AllowBrokenLinks = InputArgs.hasArg(STRIP_allow_broken_links); Config.StripDebug = InputArgs.hasArg(STRIP_strip_debug); @@ -744,16 +822,24 @@ parseStripOptions(ArrayRef<const char *> ArgsArr, Config.KeepFileSymbols = InputArgs.hasArg(STRIP_keep_file_symbols); for (auto Arg : InputArgs.filtered(STRIP_keep_section)) - Config.KeepSection.addMatcher({Arg->getValue(), UseRegexp}); + if (Error E = Config.KeepSection.addMatcher(NameOrPattern::create( + Arg->getValue(), SectionMatchStyle, ErrorCallback))) + return std::move(E); for (auto Arg : InputArgs.filtered(STRIP_remove_section)) - Config.ToRemove.addMatcher({Arg->getValue(), UseRegexp}); + if (Error E = Config.ToRemove.addMatcher(NameOrPattern::create( + Arg->getValue(), SectionMatchStyle, ErrorCallback))) + return std::move(E); for (auto Arg : InputArgs.filtered(STRIP_strip_symbol)) - Config.SymbolsToRemove.addMatcher({Arg->getValue(), UseRegexp}); + if (Error E = Config.SymbolsToRemove.addMatcher(NameOrPattern::create( + Arg->getValue(), SymbolMatchStyle, ErrorCallback))) + return std::move(E); for (auto Arg : InputArgs.filtered(STRIP_keep_symbol)) - Config.SymbolsToKeep.addMatcher({Arg->getValue(), UseRegexp}); + if (Error E = Config.SymbolsToKeep.addMatcher(NameOrPattern::create( + Arg->getValue(), SymbolMatchStyle, ErrorCallback))) + return std::move(E); if (!InputArgs.hasArg(STRIP_no_strip_all) && !Config.StripDebug && !Config.StripUnneeded && Config.DiscardMode == DiscardType::None && diff --git a/llvm/tools/llvm-objcopy/CopyConfig.h b/llvm/tools/llvm-objcopy/CopyConfig.h index 745af0ce..55a55d3 100644 --- a/llvm/tools/llvm-objcopy/CopyConfig.h +++ b/llvm/tools/llvm-objcopy/CopyConfig.h @@ -19,6 +19,7 @@ #include "llvm/Object/ELFTypes.h" #include "llvm/Support/Allocator.h" #include "llvm/Support/Error.h" +#include "llvm/Support/GlobPattern.h" #include "llvm/Support/Regex.h" // Necessary for llvm::DebugCompressionType::None #include "llvm/Target/TargetOptions.h" @@ -88,28 +89,58 @@ enum class DiscardType { Locals, // --discard-locals (-X) }; -class NameOrRegex { +enum class MatchStyle { + Literal, // Default for symbols. + Wildcard, // Default for sections, or enabled with --wildcard (-w). + Regex, // Enabled with --regex. +}; + +class NameOrPattern { StringRef Name; // Regex is shared between multiple CopyConfig instances. std::shared_ptr<Regex> R; + std::shared_ptr<GlobPattern> G; + bool IsPositiveMatch = true; + + NameOrPattern(StringRef N) : Name(N) {} + NameOrPattern(std::shared_ptr<Regex> R) : R(R) {} + NameOrPattern(std::shared_ptr<GlobPattern> G, bool IsPositiveMatch) + : G(G), IsPositiveMatch(IsPositiveMatch) {} public: - NameOrRegex(StringRef Pattern, bool IsRegex); - bool operator==(StringRef S) const { return R ? R->match(S) : Name == S; } + // ErrorCallback is used to handle recoverable errors. An Error returned + // by the callback aborts the parsing and is then returned by this function. + static Expected<NameOrPattern> + create(StringRef Pattern, MatchStyle MS, + llvm::function_ref<Error(Error)> ErrorCallback); + + bool isPositiveMatch() const { return IsPositiveMatch; } + bool operator==(StringRef S) const { + return R ? R->match(S) : G ? G->match(S) : Name == S; + } bool operator!=(StringRef S) const { return !operator==(S); } }; // Matcher that checks symbol or section names against the command line flags // provided for that option. class NameMatcher { - std::vector<NameOrRegex> Matchers; + std::vector<NameOrPattern> PosMatchers; + std::vector<NameOrPattern> NegMatchers; public: - void addMatcher(NameOrRegex Matcher) { - Matchers.push_back(std::move(Matcher)); + Error addMatcher(Expected<NameOrPattern> Matcher) { + if (!Matcher) + return Matcher.takeError(); + if (Matcher->isPositiveMatch()) + PosMatchers.push_back(std::move(*Matcher)); + else + NegMatchers.push_back(std::move(*Matcher)); + return Error::success(); + } + bool matches(StringRef S) const { + return is_contained(PosMatchers, S) && !is_contained(NegMatchers, S); } - bool matches(StringRef S) const { return is_contained(Matchers, S); } - bool empty() const { return Matchers.empty(); } + bool empty() const { return PosMatchers.empty() && NegMatchers.empty(); } }; // Configuration for copying/stripping a single file. @@ -214,8 +245,11 @@ struct DriverConfig { // ParseObjcopyOptions returns the config and sets the input arguments. If a // help flag is set then ParseObjcopyOptions will print the help messege and -// exit. -Expected<DriverConfig> parseObjcopyOptions(ArrayRef<const char *> ArgsArr); +// exit. ErrorCallback is used to handle recoverable errors. An Error returned +// by the callback aborts the parsing and is then returned by this function. +Expected<DriverConfig> +parseObjcopyOptions(ArrayRef<const char *> ArgsArr, + llvm::function_ref<Error(Error)> ErrorCallback); // ParseStripOptions returns the config and sets the input arguments. If a // help flag is set then ParseStripOptions will print the help messege and @@ -223,7 +257,7 @@ Expected<DriverConfig> parseObjcopyOptions(ArrayRef<const char *> ArgsArr); // by the callback aborts the parsing and is then returned by this function. Expected<DriverConfig> parseStripOptions(ArrayRef<const char *> ArgsArr, - std::function<Error(Error)> ErrorCallback); + llvm::function_ref<Error(Error)> ErrorCallback); } // namespace objcopy } // namespace llvm diff --git a/llvm/tools/llvm-objcopy/llvm-objcopy.cpp b/llvm/tools/llvm-objcopy/llvm-objcopy.cpp index db6d751..a68210f 100644 --- a/llvm/tools/llvm-objcopy/llvm-objcopy.cpp +++ b/llvm/tools/llvm-objcopy/llvm-objcopy.cpp @@ -335,7 +335,7 @@ int main(int argc, char **argv) { Expected<DriverConfig> DriverConfig = IsStrip ? parseStripOptions(Args, reportWarning) - : parseObjcopyOptions(Args); + : parseObjcopyOptions(Args, reportWarning); if (!DriverConfig) { logAllUnhandledErrors(DriverConfig.takeError(), WithColor::error(errs(), ToolName)); |