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 Driver 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 #include "llvm/Support/Path.h"
22 #include "llvm/Support/StringSaver.h"
23 
24 using namespace llvm;
25 using namespace lld;
26 using namespace lld::elf2;
27 
28 namespace {
29 class LinkerScript {
30 public:
31   LinkerScript(BumpPtrAllocator *A, StringRef S)
32       : Saver(*A), Tokens(tokenize(S)) {}
33   void run();
34 
35 private:
36   static std::vector<StringRef> tokenize(StringRef S);
37   static StringRef skipSpace(StringRef S);
38   StringRef next();
39   bool atEOF() { return Tokens.size() == Pos; }
40   void expect(StringRef Expect);
41 
42   void addFile(StringRef Path);
43 
44   void readAsNeeded();
45   void readEntry();
46   void readExtern();
47   void readGroup();
48   void readInclude();
49   void readOutput();
50   void readOutputArch();
51   void readOutputFormat();
52   void readSearchDir();
53 
54   StringSaver Saver;
55   std::vector<StringRef> Tokens;
56   size_t Pos = 0;
57 };
58 }
59 
60 void LinkerScript::run() {
61   while (!atEOF()) {
62     StringRef Tok = next();
63     if (Tok == ";")
64       continue;
65     if (Tok == "ENTRY") {
66       readEntry();
67     } else if (Tok == "EXTERN") {
68       readExtern();
69     } else if (Tok == "GROUP" || Tok == "INPUT") {
70       readGroup();
71     } else if (Tok == "INCLUDE") {
72       readInclude();
73     } else if (Tok == "OUTPUT") {
74       readOutput();
75     } else if (Tok == "OUTPUT_ARCH") {
76       readOutputArch();
77     } else if (Tok == "OUTPUT_FORMAT") {
78       readOutputFormat();
79     } else if (Tok == "SEARCH_DIR") {
80       readSearchDir();
81     } else {
82       error("unknown directive: " + Tok);
83     }
84   }
85 }
86 
87 // Split S into linker script tokens.
88 std::vector<StringRef> LinkerScript::tokenize(StringRef S) {
89   std::vector<StringRef> Ret;
90   for (;;) {
91     S = skipSpace(S);
92     if (S.empty())
93       return Ret;
94 
95     // Quoted token
96     if (S.startswith("\"")) {
97       size_t E = S.find("\"", 1);
98       if (E == StringRef::npos)
99         error("unclosed quote");
100       Ret.push_back(S.substr(1, E));
101       S = S.substr(E + 1);
102       continue;
103     }
104 
105     // Unquoted token
106     size_t Pos = S.find_first_not_of(
107         "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"
108         "0123456789_.$/\\~=+[]*?-:");
109     // A character that cannot start a word (which is usually a
110     // punctuation) forms a single character token.
111     if (Pos == 0)
112       Pos = 1;
113     Ret.push_back(S.substr(0, Pos));
114     S = S.substr(Pos);
115   }
116 }
117 
118 // Skip leading whitespace characters or /**/-style comments.
119 StringRef LinkerScript::skipSpace(StringRef S) {
120   for (;;) {
121     if (S.startswith("/*")) {
122       size_t E = S.find("*/", 2);
123       if (E == StringRef::npos)
124         error("unclosed comment in a linker script");
125       S = S.substr(E + 2);
126       continue;
127     }
128     size_t Size = S.size();
129     S = S.ltrim();
130     if (S.size() == Size)
131       return S;
132   }
133 }
134 
135 StringRef LinkerScript::next() {
136   if (Pos == Tokens.size())
137     error("unexpected EOF");
138   return Tokens[Pos++];
139 }
140 
141 void LinkerScript::expect(StringRef Expect) {
142   StringRef Tok = next();
143   if (Tok != Expect)
144     error(Expect + " expected, but got " + Tok);
145 }
146 
147 void LinkerScript::addFile(StringRef S) {
148   if (sys::path::is_absolute(S)) {
149     Driver->addFile(S);
150   } else if (S.startswith("=")) {
151     if (Config->Sysroot.empty())
152       Driver->addFile(S.substr(1));
153     else
154       Driver->addFile(Saver.save(Config->Sysroot + "/" + S.substr(1)));
155   } else if (S.startswith("-l")) {
156     Driver->addFile(searchLibrary(S.substr(2)));
157   } else {
158     std::string Path = findFromSearchPaths(S);
159     if (Path.empty())
160       error("Unable to find " + S);
161     Driver->addFile(Saver.save(Path));
162   }
163 }
164 
165 void LinkerScript::readAsNeeded() {
166   expect("(");
167   bool Orig = Config->AsNeeded;
168   Config->AsNeeded = true;
169   for (;;) {
170     StringRef Tok = next();
171     if (Tok == ")")
172       break;
173     addFile(Tok);
174   }
175   Config->AsNeeded = Orig;
176 }
177 
178 void LinkerScript::readEntry() {
179   // -e <symbol> takes predecence over ENTRY(<symbol>).
180   expect("(");
181   StringRef Tok = next();
182   if (Config->Entry.empty())
183     Config->Entry = Tok;
184   expect(")");
185 }
186 
187 void LinkerScript::readExtern() {
188   expect("(");
189   for (;;) {
190     StringRef Tok = next();
191     if (Tok == ")")
192       return;
193     Config->Undefined.push_back(Tok);
194   }
195 }
196 
197 void LinkerScript::readGroup() {
198   expect("(");
199   for (;;) {
200     StringRef Tok = next();
201     if (Tok == ")")
202       return;
203     if (Tok == "AS_NEEDED") {
204       readAsNeeded();
205       continue;
206     }
207     addFile(Tok);
208   }
209 }
210 
211 void LinkerScript::readInclude() {
212   StringRef Tok = next();
213   auto MBOrErr = MemoryBuffer::getFile(Tok);
214   error(MBOrErr, "cannot open " + Tok);
215   std::unique_ptr<MemoryBuffer> &MB = *MBOrErr;
216   StringRef S = Saver.save(MB->getMemBufferRef().getBuffer());
217   std::vector<StringRef> V = tokenize(S);
218   Tokens.insert(Tokens.begin() + Pos, V.begin(), V.end());
219 }
220 
221 void LinkerScript::readOutput() {
222   // -o <file> takes predecence over OUTPUT(<file>).
223   expect("(");
224   StringRef Tok = next();
225   if (Config->OutputFile.empty())
226     Config->OutputFile = Tok;
227   expect(")");
228 }
229 
230 void LinkerScript::readOutputArch() {
231   // Error checking only for now.
232   expect("(");
233   next();
234   expect(")");
235 }
236 
237 void LinkerScript::readOutputFormat() {
238   // Error checking only for now.
239   expect("(");
240   next();
241   StringRef Tok = next();
242   if (Tok == ")")
243    return;
244   if (Tok != ",")
245     error("unexpected token: " + Tok);
246   next();
247   expect(",");
248   next();
249   expect(")");
250 }
251 
252 void LinkerScript::readSearchDir() {
253   expect("(");
254   Config->SearchPaths.push_back(next());
255   expect(")");
256 }
257 
258 // Entry point. The other functions or classes are private to this file.
259 void lld::elf2::readLinkerScript(BumpPtrAllocator *A, MemoryBufferRef MB) {
260   LinkerScript(A, MB.getBuffer()).run();
261 }
262