From c42fe24754f4a2173d16b799085cec88cad6f24c Mon Sep 17 00:00:00 2001 From: Thomas Preud'homme Date: Fri, 10 Jan 2020 16:56:07 +0000 Subject: [PATCH] [lld/ELF] PR44498: Support input filename in double quote Summary: Linker scripts allow filenames to be put in double quotes to prevent characters in filenames that are part of the linker script syntax from having their special meaning. Case in point the * wildcard character. Availability of double quoting filenames also allows to fix a failure in ELF/linkerscript/filename-spec.s when the path contain a @ which the lexer consider as a special characters and thus break up a filename containing it. This may happens under Jenkins which createspath such as pipeline@2. To avoid the need for escaping GlobPattern metacharacters in filename in double quotes, GlobPattern::create is augmented with a new parameter to request literal matching instead of relying on the presence of a wildcard character in the pattern. Reviewers: jhenderson, MaskRay, evgeny777, espindola, alexshap Reviewed By: MaskRay Subscribers: peter.smith, grimar, ruiu, emaste, arichardson, hiraditya, llvm-commits Tags: #llvm Differential Revision: https://reviews.llvm.org/D72517 --- lld/Common/Strings.cpp | 26 +++++++++---- lld/ELF/LinkerScript.h | 2 +- lld/ELF/ScriptParser.cpp | 17 +++++---- lld/ELF/SymbolTable.cpp | 2 +- lld/include/lld/Common/Strings.h | 46 ++++++++++++++++++++--- lld/test/ELF/linkerscript/filename-spec.s | 4 +- 6 files changed, 72 insertions(+), 25 deletions(-) diff --git a/lld/Common/Strings.cpp b/lld/Common/Strings.cpp index 627435f141d..c5ddcc2d056 100644 --- a/lld/Common/Strings.cpp +++ b/lld/Common/Strings.cpp @@ -31,18 +31,28 @@ std::string lld::demangleItanium(StringRef name) { return demangle(name); } -StringMatcher::StringMatcher(ArrayRef pat) { - for (StringRef s : pat) { - Expected pat = GlobPattern::create(s); - if (!pat) - error(toString(pat.takeError())); - else - patterns.push_back(*pat); +SingleStringMatcher::SingleStringMatcher(StringRef Pattern) { + if (Pattern.size() > 2 && Pattern.startswith("\"") && + Pattern.endswith("\"")) { + ExactMatch = true; + ExactPattern = Pattern.substr(1, Pattern.size() - 2); + } else { + Expected Glob = GlobPattern::create(Pattern); + if (!Glob) { + error(toString(Glob.takeError())); + return; + } + ExactMatch = false; + GlobPatternMatcher = *Glob; } } +bool SingleStringMatcher::match(StringRef s) const { + return ExactMatch ? (ExactPattern == s) : GlobPatternMatcher.match(s); +} + bool StringMatcher::match(StringRef s) const { - for (const GlobPattern &pat : patterns) + for (const SingleStringMatcher &pat : patterns) if (pat.match(s)) return true; return false; diff --git a/lld/ELF/LinkerScript.h b/lld/ELF/LinkerScript.h index d57301cf352..f43a66080e0 100644 --- a/lld/ELF/LinkerScript.h +++ b/lld/ELF/LinkerScript.h @@ -164,7 +164,7 @@ struct InputSectionDescription : BaseCommand { return c->kind == InputSectionKind; } - StringMatcher filePat; + SingleStringMatcher filePat; // Input sections that matches at least one of SectionPatterns // will be associated with this InputSectionDescription. diff --git a/lld/ELF/ScriptParser.cpp b/lld/ELF/ScriptParser.cpp index f62a0d133af..a0d763eca32 100644 --- a/lld/ELF/ScriptParser.cpp +++ b/lld/ELF/ScriptParser.cpp @@ -597,10 +597,11 @@ static int precedence(StringRef op) { } StringMatcher ScriptParser::readFilePatterns() { - std::vector v; + StringMatcher Matcher; + while (!errorCount() && !consume(")")) - v.push_back(next()); - return StringMatcher(v); + Matcher.addPattern(SingleStringMatcher(next())); + return Matcher; } SortSectionPolicy ScriptParser::readSortKind() { @@ -637,12 +638,12 @@ std::vector ScriptParser::readInputSectionsList() { excludeFilePat = readFilePatterns(); } - std::vector v; + StringMatcher SectionMatcher; while (!errorCount() && peek() != ")" && peek() != "EXCLUDE_FILE") - v.push_back(unquote(next())); + SectionMatcher.addPattern(unquote(next())); - if (!v.empty()) - ret.push_back({std::move(excludeFilePat), StringMatcher(v)}); + if (!SectionMatcher.empty()) + ret.push_back({std::move(excludeFilePat), std::move(SectionMatcher)}); else setError("section pattern is expected"); } @@ -864,7 +865,7 @@ OutputSection *ScriptParser::readOutputSectionDescription(StringRef outSec) { // handle this case here as it will already have been matched by the // case above. auto *isd = make(tok); - isd->sectionPatterns.push_back({{}, StringMatcher({"*"})}); + isd->sectionPatterns.push_back({{}, StringMatcher("*")}); cmd->sectionCommands.push_back(isd); } } diff --git a/lld/ELF/SymbolTable.cpp b/lld/ELF/SymbolTable.cpp index f7a8a99cf8f..1f5d5981928 100644 --- a/lld/ELF/SymbolTable.cpp +++ b/lld/ELF/SymbolTable.cpp @@ -139,7 +139,7 @@ std::vector SymbolTable::findByVersion(SymbolVersion ver) { std::vector SymbolTable::findAllByVersion(SymbolVersion ver) { std::vector res; - StringMatcher m(ver.name); + SingleStringMatcher m(ver.name); if (ver.isExternCpp) { for (auto &p : getDemangledSyms()) diff --git a/lld/include/lld/Common/Strings.h b/lld/include/lld/Common/Strings.h index 9d002bf336d..3940d2443cd 100644 --- a/lld/include/lld/Common/Strings.h +++ b/lld/include/lld/Common/Strings.h @@ -27,16 +27,52 @@ bool isValidCIdentifier(llvm::StringRef s); // Write the contents of the a buffer to a file void saveBuffer(llvm::StringRef buffer, const llvm::Twine &path); -// This class represents multiple glob patterns. -class StringMatcher { +// A single pattern to match against. A pattern can either be double-quoted +// text that should be matched exactly after removing the quoting marks or a +// glob pattern in the sense of GlobPattern. +class SingleStringMatcher { public: - StringMatcher() = default; - explicit StringMatcher(llvm::ArrayRef pat); + // Create a StringPattern from Pattern to be matched exactly irregardless + // of globbing characters if ExactMatch is true. + SingleStringMatcher(llvm::StringRef Pattern); + // Match s against this pattern, exactly if ExactMatch is true. bool match(llvm::StringRef s) const; private: - std::vector patterns; + // Whether to do an exact match irregardless of the presence of wildcard + // character. + bool ExactMatch; + + // GlobPattern object if not doing an exact match. + llvm::GlobPattern GlobPatternMatcher; + + // StringRef to match exactly if doing an exact match. + llvm::StringRef ExactPattern; +}; + +// This class represents multiple patterns to match against. A pattern can +// either be a double-quoted text that should be matched exactly after removing +// the quoted marks or a glob pattern. +class StringMatcher { +private: + // Patterns to match against. + std::vector patterns; + +public: + StringMatcher() = default; + + // Matcher for a single pattern. + StringMatcher(llvm::StringRef Pattern) + : patterns({SingleStringMatcher(Pattern)}) {} + + // Add a new pattern to the existing ones to match against. + void addPattern(SingleStringMatcher Matcher) { patterns.push_back(Matcher); } + + bool empty() { return patterns.empty(); } + + // Match s against the patterns. + bool match(llvm::StringRef s) const; }; } // namespace lld diff --git a/lld/test/ELF/linkerscript/filename-spec.s b/lld/test/ELF/linkerscript/filename-spec.s index de9c7b6f835..82fc4f4c746 100644 --- a/lld/test/ELF/linkerscript/filename-spec.s +++ b/lld/test/ELF/linkerscript/filename-spec.s @@ -30,12 +30,12 @@ # RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux \ # RUN: %p/Inputs/filename-spec.s -o %t.dir/filename-spec2.o -# RUN: echo "SECTIONS{.foo :{ %/t.dir/filename-spec2.o(.foo) %/t.dir/filename-spec1.o(.foo) }}" > %t5.script +# RUN: echo "SECTIONS{.foo :{ \"%/t.dir/filename-spec2.o\"(.foo) \"%/t.dir/filename-spec1.o\"(.foo) }}" > %t5.script # RUN: ld.lld -o %t5 --script %t5.script \ # RUN: %/t.dir/filename-spec1.o %/t.dir/filename-spec2.o # RUN: llvm-objdump -s %t5 | FileCheck --check-prefix=SECONDFIRST %s -# RUN: echo "SECTIONS{.foo :{ %/t.dir/filename-spec1.o(.foo) %/t.dir/filename-spec2.o(.foo) }}" > %t6.script +# RUN: echo "SECTIONS{.foo :{ \"%/t.dir/filename-spec1.o\"(.foo) \"%/t.dir/filename-spec2.o\"(.foo) }}" > %t6.script # RUN: ld.lld -o %t6 --script %t6.script \ # RUN: %/t.dir/filename-spec1.o %/t.dir/filename-spec2.o # RUN: llvm-objdump -s %t6 | FileCheck --check-prefix=FIRSTY %s