forked from OSchip/llvm-project
[ELF] Better error reporting for linker scripts
Differential revision: https://reviews.llvm.org/D26795 llvm-svn: 287547
This commit is contained in:
parent
d752c44612
commit
03ff016666
|
@ -99,7 +99,7 @@ opt::InputArgList ELFOptTable::parse(ArrayRef<const char *> Argv) {
|
|||
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(MemoryBufferRef MB) {
|
|||
}
|
||||
};
|
||||
|
||||
Parser(MB.getBuffer()).run();
|
||||
Parser(MB).run();
|
||||
}
|
||||
|
||||
void elf::printHelp(const char *Argv0) {
|
||||
|
|
|
@ -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(StringRef PhdrName) {
|
|||
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 Path) {
|
|||
|
||||
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>;
|
||||
|
|
|
@ -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::tokenize(StringRef S) {
|
|||
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::tokenize(StringRef S) {
|
|||
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 Expect) {
|
|||
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();
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue