[lld/ELF] PR44498: Support input filename in double quote
authorThomas Preud'homme <thomasp@graphcore.ai>
Fri, 10 Jan 2020 16:56:07 +0000 (16:56 +0000)
committerThomas Preud'homme <thomasp@graphcore.ai>
Wed, 22 Jan 2020 12:03:10 +0000 (12:03 +0000)
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
lld/ELF/LinkerScript.h
lld/ELF/ScriptParser.cpp
lld/ELF/SymbolTable.cpp
lld/include/lld/Common/Strings.h
lld/test/ELF/linkerscript/filename-spec.s

index 627435f..c5ddcc2 100644 (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;
index d57301c..f43a660 100644 (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.
index f62a0d1..a0d763e 100644 (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);
     }
   }
index f7a8a99..1f5d598 100644 (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())
index 9d002bf..3940d24 100644 (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
index de9c7b6..82fc4f4 100644 (file)
 # 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