1 //===- LinkerScript.cpp ---------------------------------------------------===//
2 //
3 //                             The LLVM Linker
4 //
5 // This file is distributed under the University of Illinois Open Source
6 // License. See LICENSE.TXT for details.
7 //
8 //===----------------------------------------------------------------------===//
9 //
10 // This file contains the parser/evaluator of the linker script.
11 // It does not construct an AST but consume linker script directives directly.
12 // Results are written to Symtab or Config object.
13 //
14 //===----------------------------------------------------------------------===//
15 
16 #include "Config.h"
17 #include "Driver.h"
18 #include "SymbolTable.h"
19 #include "llvm/Support/FileSystem.h"
20 #include "llvm/Support/MemoryBuffer.h"
21 
22 using namespace llvm;
23 using namespace lld;
24 using namespace lld::elf2;
25 
26 namespace {
27 class LinkerScript {
28 public:
29   LinkerScript(StringRef S) : Tokens(tokenize(S)) {}
30   void run();
31 
32 private:
33   static std::vector<StringRef> tokenize(StringRef S);
34   static StringRef skipSpace(StringRef S);
35   StringRef next();
36   bool atEOF() { return Tokens.size() == Pos; }
37   void expect(StringRef Expect);
38 
39   void readAsNeeded();
40   void readGroup();
41   void readOutputFormat();
42 
43   std::vector<StringRef> Tokens;
44   size_t Pos = 0;
45 };
46 }
47 
48 void LinkerScript::run() {
49   while (!atEOF()) {
50     StringRef Tok = next();
51     if (Tok == "GROUP") {
52       readGroup();
53     } else if (Tok == "OUTPUT_FORMAT") {
54       readOutputFormat();
55     } else {
56       error("unknown directive: " + Tok);
57     }
58   }
59 }
60 
61 // Split S into linker script tokens.
62 std::vector<StringRef> LinkerScript::tokenize(StringRef S) {
63   std::vector<StringRef> Ret;
64   for (;;) {
65     S = skipSpace(S);
66     if (S.empty())
67       return Ret;
68 
69     // Quoted token
70     if (S.startswith("\"")) {
71       size_t E = S.find("\"", 1);
72       if (E == StringRef::npos)
73         error("unclosed quote");
74       Ret.push_back(S.substr(1, E));
75       S = S.substr(E + 1);
76       continue;
77     }
78 
79     // Unquoted token
80     size_t Pos = S.find_first_not_of(
81         "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"
82         "0123456789_.$/\\~=+[]*?-:");
83     // A character that cannot start a word (which is usually a
84     // punctuation) forms a single character token.
85     if (Pos == 0)
86       Pos = 1;
87     Ret.push_back(S.substr(0, Pos));
88     S = S.substr(Pos);
89   }
90 }
91 
92 // Skip leading whitespace characters or /**/-style comments.
93 StringRef LinkerScript::skipSpace(StringRef S) {
94   for (;;) {
95     if (S.startswith("/*")) {
96       size_t E = S.find("*/", 2);
97       if (E == StringRef::npos)
98         error("unclosed comment in a linker script");
99       S = S.substr(E + 2);
100       continue;
101     }
102     size_t Size = S.size();
103     S = S.ltrim();
104     if (S.size() == Size)
105       return S;
106   }
107 }
108 
109 StringRef LinkerScript::next() {
110   if (Pos == Tokens.size())
111     error("unexpected EOF");
112   return Tokens[Pos++];
113 }
114 
115 void LinkerScript::expect(StringRef Expect) {
116   StringRef Tok = next();
117   if (Tok != Expect)
118     error(Expect + " expected, but got " + Tok);
119 }
120 
121 void LinkerScript::readAsNeeded() {
122   expect("(");
123   for (;;) {
124     StringRef Tok = next();
125     if (Tok == ")")
126       return;
127     Driver->addFile(Tok);
128   }
129 }
130 
131 void LinkerScript::readGroup() {
132   expect("(");
133   for (;;) {
134     StringRef Tok = next();
135     if (Tok == ")")
136       return;
137     if (Tok == "AS_NEEDED") {
138       readAsNeeded();
139       continue;
140     }
141     Driver->addFile(Tok);
142   }
143 }
144 
145 void LinkerScript::readOutputFormat() {
146   // Error checking only for now.
147   expect("(");
148   next();
149   expect(")");
150 }
151 
152 // Entry point. The other functions or classes are private to this file.
153 void lld::elf2::readLinkerScript(MemoryBufferRef MB) {
154   LinkerScript(MB.getBuffer()).run();
155 }
156