Index: lld/trunk/ELF/DriverUtils.cpp =================================================================== --- lld/trunk/ELF/DriverUtils.cpp +++ lld/trunk/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) { Index: lld/trunk/ELF/LinkerScript.cpp =================================================================== --- lld/trunk/ELF/LinkerScript.cpp +++ lld/trunk/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 #include @@ -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> OwningMBs; }; void ScriptParser::readVersionScript() { @@ -1148,9 +1149,8 @@ return; } std::unique_ptr &MB = *MBOrErr; - StringRef S = Saver.save(MB->getMemBufferRef().getBuffer()); - std::vector 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; Index: lld/trunk/ELF/ScriptParser.h =================================================================== --- lld/trunk/ELF/ScriptParser.h +++ lld/trunk/ELF/ScriptParser.h @@ -12,6 +12,7 @@ #include "lld/Core/LLVM.h" #include "llvm/ADT/StringRef.h" +#include "llvm/Support/MemoryBuffer.h" #include #include @@ -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 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 MBs; std::vector Tokens; size_t Pos = 0; bool Error = false; + +private: + MemoryBufferRef currentBuffer(); }; } // namespace elf Index: lld/trunk/ELF/ScriptParser.cpp =================================================================== --- lld/trunk/ELF/ScriptParser.cpp +++ lld/trunk/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 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 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 ScriptParserBase::tokenize(StringRef S) { +void ScriptParserBase::tokenize(MemoryBufferRef MB) { std::vector 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(); } Index: lld/trunk/test/ELF/invalid-dynamic-list.test =================================================================== --- lld/trunk/test/ELF/invalid-dynamic-list.test +++ lld/trunk/test/ELF/invalid-dynamic-list.test @@ -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" Index: lld/trunk/test/ELF/linkerscript/diagnostic.s =================================================================== --- lld/trunk/test/ELF/linkerscript/diagnostic.s +++ lld/trunk/test/ELF/linkerscript/diagnostic.s @@ -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 Index: lld/trunk/test/ELF/version-script-err.s =================================================================== --- lld/trunk/test/ELF/version-script-err.s +++ lld/trunk/test/ELF/version-script-err.s @@ -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