[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
This commit is contained in:
Thomas Preud'homme 2020-01-10 16:56:07 +00:00
parent cdcc4f2a44
commit c42fe24754
6 changed files with 72 additions and 25 deletions

View File

@ -31,18 +31,28 @@ std::string lld::demangleItanium(StringRef name) {
return demangle(name);
}
StringMatcher::StringMatcher(ArrayRef<StringRef> pat) {
for (StringRef s : pat) {
Expected<GlobPattern> 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<GlobPattern> 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;

View File

@ -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.

View File

@ -597,10 +597,11 @@ static int precedence(StringRef op) {
}
StringMatcher ScriptParser::readFilePatterns() {
std::vector<StringRef> 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<SectionPattern> ScriptParser::readInputSectionsList() {
excludeFilePat = readFilePatterns();
}
std::vector<StringRef> 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<InputSectionDescription>(tok);
isd->sectionPatterns.push_back({{}, StringMatcher({"*"})});
isd->sectionPatterns.push_back({{}, StringMatcher("*")});
cmd->sectionCommands.push_back(isd);
}
}

View File

@ -139,7 +139,7 @@ std::vector<Symbol *> SymbolTable::findByVersion(SymbolVersion ver) {
std::vector<Symbol *> SymbolTable::findAllByVersion(SymbolVersion ver) {
std::vector<Symbol *> res;
StringMatcher m(ver.name);
SingleStringMatcher m(ver.name);
if (ver.isExternCpp) {
for (auto &p : getDemangledSyms())

View File

@ -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<llvm::StringRef> 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<llvm::GlobPattern> 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<SingleStringMatcher> 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

View File

@ -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