[lld] r287547 - [ELF] Better error reporting for linker scripts

Eugene Leviant via llvm-commits llvm-commits at lists.llvm.org
Mon Nov 21 07:49:56 PST 2016


Author: evgeny777
Date: Mon Nov 21 09:49:56 2016
New Revision: 287547

URL: http://llvm.org/viewvc/llvm-project?rev=287547&view=rev
Log:
[ELF] Better error reporting for linker scripts

Differential revision: https://reviews.llvm.org/D26795

Modified:
    lld/trunk/ELF/DriverUtils.cpp
    lld/trunk/ELF/LinkerScript.cpp
    lld/trunk/ELF/ScriptParser.cpp
    lld/trunk/ELF/ScriptParser.h
    lld/trunk/test/ELF/invalid-dynamic-list.test
    lld/trunk/test/ELF/linkerscript/diagnostic.s
    lld/trunk/test/ELF/version-script-err.s

Modified: lld/trunk/ELF/DriverUtils.cpp
URL: http://llvm.org/viewvc/llvm-project/lld/trunk/ELF/DriverUtils.cpp?rev=287547&r1=287546&r2=287547&view=diff
==============================================================================
--- lld/trunk/ELF/DriverUtils.cpp (original)
+++ lld/trunk/ELF/DriverUtils.cpp Mon Nov 21 09:49:56 2016
@@ -99,7 +99,7 @@ opt::InputArgList ELFOptTable::parse(Arr
 void elf::parseDynamicList(MemoryBufferRef MB) {
   class Parser : public ScriptParserBase {
   public:
-    Parser(StringRef S) : ScriptParserBase(S) {}
+    Parser(MemoryBufferRef MB) : ScriptParserBase(MB) {}
 
     void run() {
       while (!atEOF()) {
@@ -113,7 +113,7 @@ void elf::parseDynamicList(MemoryBufferR
     }
   };
 
-  Parser(MB.getBuffer()).run();
+  Parser(MB).run();
 }
 
 void elf::printHelp(const char *Argv0) {

Modified: lld/trunk/ELF/LinkerScript.cpp
URL: http://llvm.org/viewvc/llvm-project/lld/trunk/ELF/LinkerScript.cpp?rev=287547&r1=287546&r2=287547&view=diff
==============================================================================
--- lld/trunk/ELF/LinkerScript.cpp (original)
+++ lld/trunk/ELF/LinkerScript.cpp Mon Nov 21 09:49:56 2016
@@ -33,7 +33,6 @@
 #include "llvm/Support/ErrorHandling.h"
 #include "llvm/Support/FileSystem.h"
 #include "llvm/Support/MathExtras.h"
-#include "llvm/Support/MemoryBuffer.h"
 #include "llvm/Support/Path.h"
 #include <algorithm>
 #include <cassert>
@@ -948,11 +947,12 @@ size_t LinkerScript<ELFT>::getPhdrIndex(
   return 0;
 }
 
-class elf::ScriptParser : public ScriptParserBase {
+class elf::ScriptParser final : public ScriptParserBase {
   typedef void (ScriptParser::*Handler)();
 
 public:
-  ScriptParser(StringRef S, bool B) : ScriptParserBase(S), IsUnderSysroot(B) {}
+  ScriptParser(MemoryBufferRef MB, bool B)
+      : ScriptParserBase(MB), IsUnderSysroot(B) {}
 
   void readLinkerScript();
   void readVersionScript();
@@ -1006,6 +1006,7 @@ private:
 
   ScriptConfiguration &Opt = *ScriptConfig;
   bool IsUnderSysroot;
+  std::vector<std::unique_ptr<MemoryBuffer>> OwningMBs;
 };
 
 void ScriptParser::readVersionScript() {
@@ -1148,9 +1149,8 @@ void ScriptParser::readInclude() {
     return;
   }
   std::unique_ptr<MemoryBuffer> &MB = *MBOrErr;
-  StringRef S = Saver.save(MB->getMemBufferRef().getBuffer());
-  std::vector<StringRef> V = tokenize(S);
-  Tokens.insert(Tokens.begin() + Pos, V.begin(), V.end());
+  tokenize(MB->getMemBufferRef());
+  OwningMBs.push_back(std::move(MB));
 }
 
 void ScriptParser::readOutput() {
@@ -1912,11 +1912,11 @@ static bool isUnderSysroot(StringRef Pat
 
 void elf::readLinkerScript(MemoryBufferRef MB) {
   StringRef Path = MB.getBufferIdentifier();
-  ScriptParser(MB.getBuffer(), isUnderSysroot(Path)).readLinkerScript();
+  ScriptParser(MB, isUnderSysroot(Path)).readLinkerScript();
 }
 
 void elf::readVersionScript(MemoryBufferRef MB) {
-  ScriptParser(MB.getBuffer(), false).readVersionScript();
+  ScriptParser(MB, false).readVersionScript();
 }
 
 template class elf::LinkerScript<ELF32LE>;

Modified: lld/trunk/ELF/ScriptParser.cpp
URL: http://llvm.org/viewvc/llvm-project/lld/trunk/ELF/ScriptParser.cpp?rev=287547&r1=287546&r2=287547&view=diff
==============================================================================
--- lld/trunk/ELF/ScriptParser.cpp (original)
+++ lld/trunk/ELF/ScriptParser.cpp Mon Nov 21 09:49:56 2016
@@ -20,45 +20,59 @@ using namespace llvm;
 using namespace lld;
 using namespace lld::elf;
 
-// Returns the line that the character S[Pos] is in.
-static StringRef getLine(StringRef S, size_t Pos) {
-  size_t Begin = S.rfind('\n', Pos);
-  size_t End = S.find('\n', Pos);
+// Returns the line that the token Tok is in.
+static StringRef getLine(StringRef Data, StringRef Tok) {
+  size_t Pos = Tok.data() - Data.data();
+  size_t Begin = Data.rfind('\n', Pos);
+  size_t End = Data.find('\n', Pos);
   Begin = (Begin == StringRef::npos) ? 0 : Begin + 1;
   if (End == StringRef::npos)
-    End = S.size();
+    End = Data.size();
   // rtrim for DOS-style newlines.
-  return S.substr(Begin, End - Begin).rtrim();
+  return Data.substr(Begin, End - Begin).rtrim();
 }
 
-void ScriptParserBase::printErrorPos() {
-  StringRef Tok = Tokens[Pos == 0 ? 0 : Pos - 1];
-  StringRef Line = getLine(Input, Tok.data() - Input.data());
-  size_t Col = Tok.data() - Line.data();
-  error(Line);
-  error(std::string(Col, ' ') + "^");
+static std::pair<size_t, size_t> getPos(StringRef Data, StringRef Tok) {
+  StringRef Line = getLine(Data, Tok);
+  size_t LineNo =
+      StringRef(Data.data(), Tok.data() - Data.data()).count('\n') + 1;
+  return {LineNo, Tok.data() - Line.data()};
 }
 
+ScriptParserBase::ScriptParserBase(MemoryBufferRef MB) { tokenize(MB); }
+
 // We don't want to record cascading errors. Keep only the first one.
 void ScriptParserBase::setError(const Twine &Msg) {
   if (Error)
     return;
-  if (Input.empty() || Tokens.empty()) {
-    error(Msg);
-  } else {
-    error("line " + Twine(getPos()) + ": " + Msg);
-    printErrorPos();
+
+  std::pair<size_t, size_t> ErrPos;
+  MemoryBufferRef MB = currentBuffer();
+  std::string Location = MB.getBufferIdentifier();
+  if (Pos) {
+    ErrPos = getPos(MB.getBuffer(), Tokens[Pos - 1]);
+    Location += ":";
+    Location += std::to_string(ErrPos.first);
+  }
+  error(Location + ": " + Msg);
+  if (Pos) {
+    error(Location + ": " + getLine(MB.getBuffer(), Tokens[Pos - 1]));
+    error(Location + ": " + std::string(ErrPos.second, ' ') + "^");
   }
+
   Error = true;
 }
 
 // Split S into linker script tokens.
-std::vector<StringRef> ScriptParserBase::tokenize(StringRef S) {
+void ScriptParserBase::tokenize(MemoryBufferRef MB) {
   std::vector<StringRef> Ret;
+  MBs.push_back(MB);
+  StringRef S = MB.getBuffer();
+  StringRef Begin = S;
   for (;;) {
     S = skipSpace(S);
     if (S.empty())
-      return Ret;
+      break;
 
     // Quoted token. Note that double-quote characters are parts of a token
     // because, in a glob match context, only unquoted tokens are interpreted
@@ -67,8 +81,10 @@ std::vector<StringRef> ScriptParserBase:
     if (S.startswith("\"")) {
       size_t E = S.find("\"", 1);
       if (E == StringRef::npos) {
-        error("unclosed quote");
-        return {};
+        auto ErrPos = getPos(Begin, S);
+        error(MB.getBufferIdentifier() + ":" + Twine(ErrPos.first) +
+              ": unclosed quote");
+        return;
       }
       Ret.push_back(S.take_front(E + 1));
       S = S.substr(E + 1);
@@ -88,6 +104,7 @@ std::vector<StringRef> ScriptParserBase:
     Ret.push_back(S.substr(0, Pos));
     S = S.substr(Pos);
   }
+  Tokens.insert(Tokens.begin() + Pos, Ret.begin(), Ret.end());
 }
 
 // Skip leading whitespace characters or comments.
@@ -155,11 +172,21 @@ void ScriptParserBase::expect(StringRef
     setError(Expect + " expected, but got " + Tok);
 }
 
-// Returns the current line number.
-size_t ScriptParserBase::getPos() {
-  if (Pos == 0)
-    return 1;
-  const char *Begin = Input.data();
-  const char *Tok = Tokens[Pos - 1].data();
-  return StringRef(Begin, Tok - Begin).count('\n') + 1;
+// Returns true if string 'Bigger' contains string 'Shorter'.
+static bool containsString(StringRef Bigger, StringRef Shorter) {
+  const char *BiggerEnd = Bigger.data() + Bigger.size();
+  const char *ShorterEnd = Shorter.data() + Shorter.size();
+
+  return Bigger.data() <= Shorter.data() && BiggerEnd >= ShorterEnd;
+}
+
+MemoryBufferRef ScriptParserBase::currentBuffer() {
+  // Find input buffer containing the current token.
+  assert(!MBs.empty());
+  if (Pos)
+    for (MemoryBufferRef MB : MBs)
+      if (containsString(MB.getBuffer(), Tokens[Pos - 1]))
+        return MB;
+
+  return MBs.front();
 }

Modified: lld/trunk/ELF/ScriptParser.h
URL: http://llvm.org/viewvc/llvm-project/lld/trunk/ELF/ScriptParser.h?rev=287547&r1=287546&r2=287547&view=diff
==============================================================================
--- lld/trunk/ELF/ScriptParser.h (original)
+++ lld/trunk/ELF/ScriptParser.h Mon Nov 21 09:49:56 2016
@@ -12,6 +12,7 @@
 
 #include "lld/Core/LLVM.h"
 #include "llvm/ADT/StringRef.h"
+#include "llvm/Support/MemoryBuffer.h"
 #include <utility>
 #include <vector>
 
@@ -20,11 +21,10 @@ namespace elf {
 
 class ScriptParserBase {
 public:
-  explicit ScriptParserBase(StringRef S) : Input(S), Tokens(tokenize(S)) {}
+  explicit ScriptParserBase(MemoryBufferRef MB);
 
-protected:
   void setError(const Twine &Msg);
-  static std::vector<StringRef> tokenize(StringRef S);
+  void tokenize(MemoryBufferRef MB);
   static StringRef skipSpace(StringRef S);
   bool atEOF();
   StringRef next();
@@ -33,13 +33,13 @@ protected:
   bool consume(StringRef Tok);
   void expect(StringRef Expect);
 
-  size_t getPos();
-  void printErrorPos();
-
-  StringRef Input;
+  std::vector<MemoryBufferRef> MBs;
   std::vector<StringRef> Tokens;
   size_t Pos = 0;
   bool Error = false;
+
+private:
+  MemoryBufferRef currentBuffer();
 };
 
 } // namespace elf

Modified: lld/trunk/test/ELF/invalid-dynamic-list.test
URL: http://llvm.org/viewvc/llvm-project/lld/trunk/test/ELF/invalid-dynamic-list.test?rev=287547&r1=287546&r2=287547&view=diff
==============================================================================
--- lld/trunk/test/ELF/invalid-dynamic-list.test (original)
+++ lld/trunk/test/ELF/invalid-dynamic-list.test Mon Nov 21 09:49:56 2016
@@ -11,27 +11,27 @@
 
 # RUN: echo foobar > %t1
 # RUN: not ld.lld --dynamic-list %t1 2>&1 | FileCheck -check-prefix=ERR1 %s
-# ERR1: line 1: { expected, but got foobar
+# ERR1: {{.*}}:1: { expected, but got foobar
 
 # RUN: echo "{ foobar;" > %t1
 # RUN: not ld.lld --dynamic-list %t1 2>&1 | FileCheck -check-prefix=ERR2 %s
-# ERR2: line 1: unexpected EOF
+# ERR2: {{.*}}:1: unexpected EOF
 
 ## Missing ';' before '}'
 # RUN: echo "{ foobar }" > %t1
 # RUN: not ld.lld --dynamic-list %t1 2>&1 | FileCheck -check-prefix=ERR3 %s
-# ERR3: line 1: ; expected, but got }
+# ERR3: {{.*}}:1: ; expected, but got }
 
 ## Missing final ';'
 # RUN: echo "{ foobar; }" > %t1
 # RUN: not ld.lld --dynamic-list %t1 2>&1 | FileCheck -check-prefix=ERR4 %s
-# ERR4: line 1: unexpected EOF
+# ERR4: {{.*}}:1: unexpected EOF
 
 ## Missing \" in foobar definition
 # RUN echo "{ \"foobar; };" > %t1
 # RUN: not ld.lld --dynamic-list %t1 2>&1 | FileCheck -check-prefix=ERR5 %s
-# ERR5: line 1: unexpected EOF
+# ERR5: {{.*}}:1: unexpected EOF
 
 # RUN: echo "{ extern \"BOGUS\" { test }; };" > %t1
 # RUN: not ld.lld --dynamic-list %t1 2>&1 | FileCheck -check-prefix=ERR6 %s
-# ERR6: line 1: ; expected, but got "BOGUS"
+# ERR6: {{.*}}:1: ; expected, but got "BOGUS"

Modified: lld/trunk/test/ELF/linkerscript/diagnostic.s
URL: http://llvm.org/viewvc/llvm-project/lld/trunk/test/ELF/linkerscript/diagnostic.s?rev=287547&r1=287546&r2=287547&view=diff
==============================================================================
--- lld/trunk/test/ELF/linkerscript/diagnostic.s (original)
+++ lld/trunk/test/ELF/linkerscript/diagnostic.s Mon Nov 21 09:49:56 2016
@@ -20,7 +20,7 @@
 # RUN: echo "comment line 2 */" >> %t.script
 # RUN: echo ".temp : { *(.temp) } }" >> %t.script
 # RUN: not ld.lld -shared %t -o %t1 --script %t.script 2>&1 | FileCheck -check-prefix=ERR1 %s
-# ERR1: line 2:
+# ERR1: {{.*}}.script:2:
 
 ## Change ":" to "+" at line 3 now, check correct error line number:
 # RUN: echo "SECTIONS {" > %t.script
@@ -30,7 +30,7 @@
 # RUN: echo "comment line 2 */" >> %t.script
 # RUN: echo ".temp : { *(.temp) } }" >> %t.script
 # RUN: not ld.lld -shared %t -o %t1 --script %t.script 2>&1 | FileCheck -check-prefix=ERR2 %s
-# ERR2: line 3:
+# ERR2: {{.*}}.script:3:
 
 ## Change ":" to "+" at line 6, after multiline comment,
 ## check correct error line number:
@@ -41,7 +41,7 @@
 # RUN: echo "comment line 2 */" >> %t.script
 # RUN: echo ".temp + { *(.temp) } }" >> %t.script
 # RUN: not ld.lld -shared %t -o %t1 --script %t.script 2>&1 | FileCheck -check-prefix=ERR5 %s
-# ERR5: line 6:
+# ERR5: {{.*}}.script:6:
 
 ## Check that text of lines and pointer to 'bad' token are working ok.
 # RUN: echo "UNKNOWN_TAG {" > %t.script
@@ -50,9 +50,9 @@
 # RUN: echo ".temp : { *(.temp) } }" >> %t.script
 # RUN: not ld.lld -shared %t -o %t1 --script %t.script 2>&1 | \
 # RUN:   FileCheck -check-prefix=ERR6 -strict-whitespace %s
-# ERR6:      error: line 1:
-# ERR6-NEXT: error: UNKNOWN_TAG {
-# ERR6-NEXT: error: ^
+# ERR6:      error: {{.*}}.script:1:
+# ERR6-NEXT: error: {{.*}}.script:1: UNKNOWN_TAG {
+# ERR6-NEXT: error: {{.*}}.script:1: ^
 
 ## One more check that text of lines and pointer to 'bad' token are working ok.
 # RUN: echo "SECTIONS {" > %t.script
@@ -61,6 +61,46 @@
 # RUN: echo "boom .temp : { *(.temp) } }" >> %t.script
 # RUN: not ld.lld -shared %t -o %t1 --script %t.script 2>&1 | \
 # RUN:   FileCheck -check-prefix=ERR7 -strict-whitespace %s
-# ERR7:      error: line 4: malformed number: .temp
-# ERR7-NEXT: error: boom .temp : { *(.temp) } }
-# ERR7-NEXT: error:      ^
+# ERR7:      error: {{.*}}.script:4: malformed number: .temp
+# ERR7-NEXT: error: {{.*}}.script:4: boom .temp : { *(.temp) } }
+# ERR7-NEXT: error: {{.*}}.script:4:      ^
+
+## Check tokenize() error
+# RUN: echo "SECTIONS {}" > %t.script
+# RUN: echo "\"" >> %t.script
+# RUN: not ld.lld -shared %t -o %t1 --script %t.script 2>&1 | \
+# RUN:   FileCheck -check-prefix=ERR8 -strict-whitespace %s
+# ERR8: {{.*}}.script:2: unclosed quote
+
+## Check tokenize() error in included script file
+# RUN: echo "SECTIONS {}" > %t.script.inc
+# RUN: echo "\"" >> %t.script.inc
+# RUN: echo "INCLUDE \"%t.script.inc\"" > %t.script
+# RUN: not ld.lld -shared %t -o %t1 --script %t.script 2>&1 | \
+# RUN:   FileCheck -check-prefix=ERR9 -strict-whitespace %s
+# ERR9: {{.*}}.script.inc:2: unclosed quote
+
+## Check error reporting correctness for included files.
+# RUN: echo "SECTIONS {" > %t.script.inc
+# RUN: echo ".text : { *(.text) }" >> %t.script.inc
+# RUN: echo ".keep : { *(.keep) }" >> %t.script.inc
+# RUN: echo "boom .temp : { *(.temp) } }" >> %t.script.inc
+# RUN: echo "INCLUDE \"%t.script.inc\"" > %t.script
+# RUN: not ld.lld -shared %t -o %t1 --script %t.script 2>&1 | \
+# RUN:   FileCheck -check-prefix=ERR10 -strict-whitespace %s
+# ERR10:      error: {{.*}}.script.inc:4: malformed number: .temp
+# ERR10-NEXT: error: {{.*}}.script.inc:4: boom .temp : { *(.temp) } }
+# ERR10-NEXT: error: {{.*}}.script.inc:4:      ^
+
+## Check error reporting in script with INCLUDE directive.
+# RUN: echo "SECTIONS {" > %t.script.inc
+# RUN: echo ".text : { *(.text) }" >> %t.script.inc
+# RUN: echo ".keep : { *(.keep) }" >> %t.script.inc
+# RUN: echo ".temp : { *(.temp) } }" >> %t.script.inc
+# RUN: echo "/* One line before INCLUDE */" > %t.script
+# RUN: echo "INCLUDE \"%t.script.inc\"" >> %t.script
+# RUN: echo "/* One line ater INCLUDE */" >> %t.script
+# RUN: echo "Error" >> %t.script
+# RUN: not ld.lld -shared %t -o %t1 --script %t.script 2>&1 | \
+# RUN:   FileCheck -check-prefix=ERR11 -strict-whitespace %s
+# ERR11: error: {{.*}}.script:4: unexpected EOF

Modified: lld/trunk/test/ELF/version-script-err.s
URL: http://llvm.org/viewvc/llvm-project/lld/trunk/test/ELF/version-script-err.s?rev=287547&r1=287546&r2=287547&view=diff
==============================================================================
--- lld/trunk/test/ELF/version-script-err.s (original)
+++ lld/trunk/test/ELF/version-script-err.s Mon Nov 21 09:49:56 2016
@@ -7,4 +7,5 @@
 // RUN: echo    "\"" > %terr1.script
 // RUN: not ld.lld --version-script %terr1.script -shared %t.o -o %t.so 2>&1 | \
 // RUN:   FileCheck -check-prefix=ERR1 %s
-// ERR1: unclosed quote
+// ERR1: {{.*}}:1: unclosed quote
+// ERR1-NEXT: {{.*}}: unexpected EOF




More information about the llvm-commits mailing list