void run();
private:
+ void setError(const Twine &Msg);
static std::vector<StringRef> tokenize(StringRef S);
static StringRef skipSpace(StringRef S);
+ bool atEOF();
StringRef next();
bool skip(StringRef Tok);
- bool atEOF() { return Tokens.size() == Pos; }
void expect(StringRef Expect);
void addFile(StringRef Path);
StringSaver Saver;
std::vector<StringRef> Tokens;
+ bool Error = false;
size_t Pos = 0;
bool IsUnderSysroot;
};
} else if (Tok == "SECTIONS") {
readSections();
} else {
- fatal("unknown directive: " + Tok);
+ setError("unknown directive: " + Tok);
+ return;
}
}
}
+// We don't want to record cascading errors. Keep only the first one.
+void LinkerScript::setError(const Twine &Msg) {
+ if (Error)
+ return;
+ error(Msg);
+ Error = true;
+}
+
// Split S into linker script tokens.
std::vector<StringRef> LinkerScript::tokenize(StringRef S) {
std::vector<StringRef> Ret;
// Quoted token
if (S.startswith("\"")) {
size_t E = S.find("\"", 1);
- if (E == StringRef::npos)
- fatal("unclosed quote");
+ if (E == StringRef::npos) {
+ error("unclosed quote");
+ return {};
+ }
Ret.push_back(S.substr(1, E - 1));
S = S.substr(E + 1);
continue;
for (;;) {
if (S.startswith("/*")) {
size_t E = S.find("*/", 2);
- if (E == StringRef::npos)
- fatal("unclosed comment in a linker script");
+ if (E == StringRef::npos) {
+ error("unclosed comment in a linker script");
+ return "";
+ }
S = S.substr(E + 2);
continue;
}
}
}
+// An errneous token is handled as if it were the last token before EOF.
+bool LinkerScript::atEOF() { return Error || Tokens.size() == Pos; }
+
StringRef LinkerScript::next() {
- if (atEOF())
- fatal("unexpected EOF");
+ if (Error)
+ return "";
+ if (atEOF()) {
+ setError("unexpected EOF");
+ return "";
+ }
return Tokens[Pos++];
}
bool LinkerScript::skip(StringRef Tok) {
- if (atEOF())
- fatal("unexpected EOF");
+ if (Error)
+ return false;
+ if (atEOF()) {
+ setError("unexpected EOF");
+ return false;
+ }
if (Tok != Tokens[Pos])
return false;
++Pos;
}
void LinkerScript::expect(StringRef Expect) {
+ if (Error)
+ return;
StringRef Tok = next();
if (Tok != Expect)
- fatal(Expect + " expected, but got " + Tok);
+ setError(Expect + " expected, but got " + Tok);
}
void LinkerScript::addFile(StringRef S) {
} else {
std::string Path = findFromSearchPaths(S);
if (Path.empty())
- fatal("Unable to find " + S);
- Driver->addFile(Saver.save(Path));
+ setError("Unable to find " + S);
+ else
+ Driver->addFile(Saver.save(Path));
}
}
expect("(");
bool Orig = Config->AsNeeded;
Config->AsNeeded = true;
- for (;;) {
+ while (!Error) {
StringRef Tok = next();
if (Tok == ")")
break;
void LinkerScript::readExtern() {
expect("(");
- for (;;) {
+ while (!Error) {
StringRef Tok = next();
if (Tok == ")")
return;
void LinkerScript::readGroup() {
expect("(");
- for (;;) {
+ while (!Error) {
StringRef Tok = next();
if (Tok == ")")
return;
void LinkerScript::readInclude() {
StringRef Tok = next();
auto MBOrErr = MemoryBuffer::getFile(Tok);
- fatal(MBOrErr, "cannot open " + Tok);
+ if (!MBOrErr) {
+ setError("cannot open " + Tok);
+ return;
+ }
std::unique_ptr<MemoryBuffer> &MB = *MBOrErr;
StringRef S = Saver.save(MB->getMemBufferRef().getBuffer());
std::vector<StringRef> V = tokenize(S);
StringRef Tok = next();
if (Tok == ")")
return;
- if (Tok != ",")
- fatal("unexpected token: " + Tok);
+ if (Tok != ",") {
+ setError("unexpected token: " + Tok);
+ return;
+ }
next();
expect(",");
next();
void LinkerScript::readSections() {
expect("{");
- while (!skip("}"))
+ while (!Error && !skip("}"))
readOutputSectionDescription();
}
expect(":");
expect("{");
- while (!skip("}")) {
+ while (!Error && !skip("}")) {
next(); // Skip input file name.
expect("(");
- while (!skip(")"))
+ while (!Error && !skip(")"))
InputSections.push_back(next());
}
}
--- /dev/null
+# RUN: mkdir -p %t.dir
+
+## Note that we will see "no input files" error message after
+## error messages from the linker script parser, because the
+## linker keeps going when an error is found.
+
+# RUN: echo foobar > %t1
+# RUN: not ld.lld %t1 2>&1 | FileCheck -check-prefix=ERR1 %s
+# ERR1: unknown directive: foobar
+# ERR1: no input files
+
+# RUN: echo "foo \"bar" > %t2
+# RUN: not ld.lld %t2 2>&1 | FileCheck -check-prefix=ERR2 %s
+# ERR2: unclosed quote
+# ERR2: no input files
+
+# RUN: echo "/*" > %t3
+# RUN: not ld.lld %t3 2>&1 | FileCheck -check-prefix=ERR3 %s
+# ERR3: unclosed comment
+# ERR3: no input files
+
+# RUN: echo "EXTERN (" > %t4
+# RUN: not ld.lld %t4 2>&1 | FileCheck -check-prefix=ERR4 %s
+# ERR4: unexpected EOF
+# ERR4: no input files
+
+# RUN: echo "EXTERN (" > %t5
+# RUN: not ld.lld %t5 2>&1 | FileCheck -check-prefix=ERR5 %s
+# ERR5: unexpected EOF
+# ERR5: no input files
+
+# RUN: echo "EXTERN xyz" > %t6
+# RUN: not ld.lld %t6 2>&1 | FileCheck -check-prefix=ERR6 %s
+# ERR6: ( expected, but got xyz
+# ERR6: no input files
+
+# RUN: echo "INCLUDE /no/such/file" > %t7
+# RUN: not ld.lld %t7 2>&1 | FileCheck -check-prefix=ERR7 %s
+# ERR7: cannot open /no/such/file
+# ERR7: no input files
+
+# RUN: echo "OUTPUT_FORMAT(x y z)" > %t8
+# RUN: not ld.lld %t8 2>&1 | FileCheck -check-prefix=ERR8 %s
+# ERR8: unexpected token: y
+# ERR8: no input files