[ELF] Better error reporting for linker scripts
Differential revision: https://reviews.llvm.org/D26795
llvm-svn: 287547
diff --git a/lld/ELF/DriverUtils.cpp b/lld/ELF/DriverUtils.cpp
index 546256b..d56dfba 100644
--- a/lld/ELF/DriverUtils.cpp
+++ b/lld/ELF/DriverUtils.cpp
@@ -99,7 +99,7 @@
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 @@
}
};
- Parser(MB.getBuffer()).run();
+ Parser(MB).run();
}
void elf::printHelp(const char *Argv0) {
diff --git a/lld/ELF/LinkerScript.cpp b/lld/ELF/LinkerScript.cpp
index ff58e44..38477c8 100644
--- a/lld/ELF/LinkerScript.cpp
+++ b/lld/ELF/LinkerScript.cpp
@@ -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 @@
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 @@
ScriptConfiguration &Opt = *ScriptConfig;
bool IsUnderSysroot;
+ std::vector<std::unique_ptr<MemoryBuffer>> OwningMBs;
};
void ScriptParser::readVersionScript() {
@@ -1148,9 +1149,8 @@
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 @@
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>;
diff --git a/lld/ELF/ScriptParser.cpp b/lld/ELF/ScriptParser.cpp
index 3c66af5..33c2f90 100644
--- a/lld/ELF/ScriptParser.cpp
+++ b/lld/ELF/ScriptParser.cpp
@@ -20,45 +20,59 @@
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 @@
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 @@
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 @@
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();
}
diff --git a/lld/ELF/ScriptParser.h b/lld/ELF/ScriptParser.h
index e2bdeef..6c314a4 100644
--- a/lld/ELF/ScriptParser.h
+++ b/lld/ELF/ScriptParser.h
@@ -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 @@
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 @@
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