aboutsummaryrefslogtreecommitdiff
path: root/ELF/LinkerScript.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'ELF/LinkerScript.cpp')
-rw-r--r--ELF/LinkerScript.cpp318
1 files changed, 318 insertions, 0 deletions
diff --git a/ELF/LinkerScript.cpp b/ELF/LinkerScript.cpp
new file mode 100644
index 000000000000..883b623f9e2c
--- /dev/null
+++ b/ELF/LinkerScript.cpp
@@ -0,0 +1,318 @@
+//===- LinkerScript.cpp ---------------------------------------------------===//
+//
+// The LLVM Linker
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file contains the parser/evaluator of the linker script.
+// It does not construct an AST but consume linker script directives directly.
+// Results are written to Driver or Config object.
+//
+//===----------------------------------------------------------------------===//
+
+#include "Config.h"
+#include "Driver.h"
+#include "SymbolTable.h"
+#include "llvm/Support/FileSystem.h"
+#include "llvm/Support/MemoryBuffer.h"
+#include "llvm/Support/Path.h"
+#include "llvm/Support/StringSaver.h"
+
+using namespace llvm;
+using namespace lld;
+using namespace lld::elf2;
+
+namespace {
+class LinkerScript {
+public:
+ LinkerScript(BumpPtrAllocator *A, StringRef S, bool B)
+ : Saver(*A), Tokens(tokenize(S)), IsUnderSysroot(B) {}
+ void run();
+
+private:
+ static std::vector<StringRef> tokenize(StringRef S);
+ static StringRef skipSpace(StringRef S);
+ StringRef next();
+ bool skip(StringRef Tok);
+ bool atEOF() { return Tokens.size() == Pos; }
+ void expect(StringRef Expect);
+
+ void addFile(StringRef Path);
+
+ void readAsNeeded();
+ void readEntry();
+ void readExtern();
+ void readGroup();
+ void readInclude();
+ void readOutput();
+ void readOutputArch();
+ void readOutputFormat();
+ void readSearchDir();
+ void readSections();
+
+ void readOutputSectionDescription();
+
+ StringSaver Saver;
+ std::vector<StringRef> Tokens;
+ size_t Pos = 0;
+ bool IsUnderSysroot;
+};
+}
+
+void LinkerScript::run() {
+ while (!atEOF()) {
+ StringRef Tok = next();
+ if (Tok == ";")
+ continue;
+ if (Tok == "ENTRY") {
+ readEntry();
+ } else if (Tok == "EXTERN") {
+ readExtern();
+ } else if (Tok == "GROUP" || Tok == "INPUT") {
+ readGroup();
+ } else if (Tok == "INCLUDE") {
+ readInclude();
+ } else if (Tok == "OUTPUT") {
+ readOutput();
+ } else if (Tok == "OUTPUT_ARCH") {
+ readOutputArch();
+ } else if (Tok == "OUTPUT_FORMAT") {
+ readOutputFormat();
+ } else if (Tok == "SEARCH_DIR") {
+ readSearchDir();
+ } else if (Tok == "SECTIONS") {
+ readSections();
+ } else {
+ error("unknown directive: " + Tok);
+ }
+ }
+}
+
+// Split S into linker script tokens.
+std::vector<StringRef> LinkerScript::tokenize(StringRef S) {
+ std::vector<StringRef> Ret;
+ for (;;) {
+ S = skipSpace(S);
+ if (S.empty())
+ return Ret;
+
+ // Quoted token
+ if (S.startswith("\"")) {
+ size_t E = S.find("\"", 1);
+ if (E == StringRef::npos)
+ error("unclosed quote");
+ Ret.push_back(S.substr(1, E));
+ S = S.substr(E + 1);
+ continue;
+ }
+
+ // Unquoted token
+ size_t Pos = S.find_first_not_of(
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"
+ "0123456789_.$/\\~=+[]*?-:");
+ // A character that cannot start a word (which is usually a
+ // punctuation) forms a single character token.
+ if (Pos == 0)
+ Pos = 1;
+ Ret.push_back(S.substr(0, Pos));
+ S = S.substr(Pos);
+ }
+}
+
+// Skip leading whitespace characters or /**/-style comments.
+StringRef LinkerScript::skipSpace(StringRef S) {
+ for (;;) {
+ if (S.startswith("/*")) {
+ size_t E = S.find("*/", 2);
+ if (E == StringRef::npos)
+ error("unclosed comment in a linker script");
+ S = S.substr(E + 2);
+ continue;
+ }
+ size_t Size = S.size();
+ S = S.ltrim();
+ if (S.size() == Size)
+ return S;
+ }
+}
+
+StringRef LinkerScript::next() {
+ if (atEOF())
+ error("unexpected EOF");
+ return Tokens[Pos++];
+}
+
+bool LinkerScript::skip(StringRef Tok) {
+ if (atEOF())
+ error("unexpected EOF");
+ if (Tok != Tokens[Pos])
+ return false;
+ ++Pos;
+ return true;
+}
+
+void LinkerScript::expect(StringRef Expect) {
+ StringRef Tok = next();
+ if (Tok != Expect)
+ error(Expect + " expected, but got " + Tok);
+}
+
+void LinkerScript::addFile(StringRef S) {
+ if (IsUnderSysroot && S.startswith("/")) {
+ SmallString<128> Path;
+ (Config->Sysroot + S).toStringRef(Path);
+ if (sys::fs::exists(Path)) {
+ Driver->addFile(Saver.save(Path.str()));
+ return;
+ }
+ }
+
+ if (sys::path::is_absolute(S)) {
+ Driver->addFile(S);
+ } else if (S.startswith("=")) {
+ if (Config->Sysroot.empty())
+ Driver->addFile(S.substr(1));
+ else
+ Driver->addFile(Saver.save(Config->Sysroot + "/" + S.substr(1)));
+ } else if (S.startswith("-l")) {
+ Driver->addFile(searchLibrary(S.substr(2)));
+ } else if (sys::fs::exists(S)) {
+ Driver->addFile(S);
+ } else {
+ std::string Path = findFromSearchPaths(S);
+ if (Path.empty())
+ error("Unable to find " + S);
+ Driver->addFile(Saver.save(Path));
+ }
+}
+
+void LinkerScript::readAsNeeded() {
+ expect("(");
+ bool Orig = Config->AsNeeded;
+ Config->AsNeeded = true;
+ for (;;) {
+ StringRef Tok = next();
+ if (Tok == ")")
+ break;
+ addFile(Tok);
+ }
+ Config->AsNeeded = Orig;
+}
+
+void LinkerScript::readEntry() {
+ // -e <symbol> takes predecence over ENTRY(<symbol>).
+ expect("(");
+ StringRef Tok = next();
+ if (Config->Entry.empty())
+ Config->Entry = Tok;
+ expect(")");
+}
+
+void LinkerScript::readExtern() {
+ expect("(");
+ for (;;) {
+ StringRef Tok = next();
+ if (Tok == ")")
+ return;
+ Config->Undefined.push_back(Tok);
+ }
+}
+
+void LinkerScript::readGroup() {
+ expect("(");
+ for (;;) {
+ StringRef Tok = next();
+ if (Tok == ")")
+ return;
+ if (Tok == "AS_NEEDED") {
+ readAsNeeded();
+ continue;
+ }
+ addFile(Tok);
+ }
+}
+
+void LinkerScript::readInclude() {
+ StringRef Tok = next();
+ auto MBOrErr = MemoryBuffer::getFile(Tok);
+ error(MBOrErr, "cannot open " + Tok);
+ 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());
+}
+
+void LinkerScript::readOutput() {
+ // -o <file> takes predecence over OUTPUT(<file>).
+ expect("(");
+ StringRef Tok = next();
+ if (Config->OutputFile.empty())
+ Config->OutputFile = Tok;
+ expect(")");
+}
+
+void LinkerScript::readOutputArch() {
+ // Error checking only for now.
+ expect("(");
+ next();
+ expect(")");
+}
+
+void LinkerScript::readOutputFormat() {
+ // Error checking only for now.
+ expect("(");
+ next();
+ StringRef Tok = next();
+ if (Tok == ")")
+ return;
+ if (Tok != ",")
+ error("unexpected token: " + Tok);
+ next();
+ expect(",");
+ next();
+ expect(")");
+}
+
+void LinkerScript::readSearchDir() {
+ expect("(");
+ Config->SearchPaths.push_back(next());
+ expect(")");
+}
+
+void LinkerScript::readSections() {
+ expect("{");
+ while (!skip("}"))
+ readOutputSectionDescription();
+}
+
+void LinkerScript::readOutputSectionDescription() {
+ StringRef Name = next();
+ std::vector<StringRef> &InputSections = Config->OutputSections[Name];
+
+ expect(":");
+ expect("{");
+ while (!skip("}")) {
+ next(); // Skip input file name.
+ expect("(");
+ while (!skip(")"))
+ InputSections.push_back(next());
+ }
+}
+
+static bool isUnderSysroot(StringRef Path) {
+ if (Config->Sysroot == "")
+ return false;
+ for (; !Path.empty(); Path = sys::path::parent_path(Path))
+ if (sys::fs::equivalent(Config->Sysroot, Path))
+ return true;
+ return false;
+}
+
+// Entry point. The other functions or classes are private to this file.
+void lld::elf2::readLinkerScript(BumpPtrAllocator *A, MemoryBufferRef MB) {
+ StringRef Path = MB.getBufferIdentifier();
+ LinkerScript(A, MB.getBuffer(), isUnderSysroot(Path)).run();
+}