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, bool B)
32       : Saver(*A), Tokens(tokenize(S)), IsUnderSysroot(B) {}
33   void run();
34 
35 private:
36   void setError(const Twine &Msg);
37   static std::vector<StringRef> tokenize(StringRef S);
38   static StringRef skipSpace(StringRef S);
39   bool atEOF();
40   StringRef next();
41   bool skip(StringRef Tok);
42   void expect(StringRef Expect);
43 
44   void addFile(StringRef Path);
45 
46   void readAsNeeded();
47   void readEntry();
48   void readExtern();
49   void readGroup();
50   void readInclude();
51   void readOutput();
52   void readOutputArch();
53   void readOutputFormat();
54   void readSearchDir();
55   void readSections();
56 
57   void readOutputSectionDescription();
58 
59   StringSaver Saver;
60   std::vector<StringRef> Tokens;
61   bool Error = false;
62   size_t Pos = 0;
63   bool IsUnderSysroot;
64 };
65 }
66 
67 void LinkerScript::run() {
68   while (!atEOF()) {
69     StringRef Tok = next();
70     if (Tok == ";")
71       continue;
72     if (Tok == "ENTRY") {
73       readEntry();
74     } else if (Tok == "EXTERN") {
75       readExtern();
76     } else if (Tok == "GROUP" || Tok == "INPUT") {
77       readGroup();
78     } else if (Tok == "INCLUDE") {
79       readInclude();
80     } else if (Tok == "OUTPUT") {
81       readOutput();
82     } else if (Tok == "OUTPUT_ARCH") {
83       readOutputArch();
84     } else if (Tok == "OUTPUT_FORMAT") {
85       readOutputFormat();
86     } else if (Tok == "SEARCH_DIR") {
87       readSearchDir();
88     } else if (Tok == "SECTIONS") {
89       readSections();
90     } else {
91       setError("unknown directive: " + Tok);
92       return;
93     }
94   }
95 }
96 
97 // We don't want to record cascading errors. Keep only the first one.
98 void LinkerScript::setError(const Twine &Msg) {
99   if (Error)
100     return;
101   error(Msg);
102   Error = true;
103 }
104 
105 // Split S into linker script tokens.
106 std::vector<StringRef> LinkerScript::tokenize(StringRef S) {
107   std::vector<StringRef> Ret;
108   for (;;) {
109     S = skipSpace(S);
110     if (S.empty())
111       return Ret;
112 
113     // Quoted token
114     if (S.startswith("\"")) {
115       size_t E = S.find("\"", 1);
116       if (E == StringRef::npos) {
117         error("unclosed quote");
118         return {};
119       }
120       Ret.push_back(S.substr(1, E - 1));
121       S = S.substr(E + 1);
122       continue;
123     }
124 
125     // Unquoted token
126     size_t Pos = S.find_first_not_of(
127         "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"
128         "0123456789_.$/\\~=+[]*?-:");
129     // A character that cannot start a word (which is usually a
130     // punctuation) forms a single character token.
131     if (Pos == 0)
132       Pos = 1;
133     Ret.push_back(S.substr(0, Pos));
134     S = S.substr(Pos);
135   }
136 }
137 
138 // Skip leading whitespace characters or /**/-style comments.
139 StringRef LinkerScript::skipSpace(StringRef S) {
140   for (;;) {
141     if (S.startswith("/*")) {
142       size_t E = S.find("*/", 2);
143       if (E == StringRef::npos) {
144         error("unclosed comment in a linker script");
145         return "";
146       }
147       S = S.substr(E + 2);
148       continue;
149     }
150     size_t Size = S.size();
151     S = S.ltrim();
152     if (S.size() == Size)
153       return S;
154   }
155 }
156 
157 // An errneous token is handled as if it were the last token before EOF.
158 bool LinkerScript::atEOF() { return Error || Tokens.size() == Pos; }
159 
160 StringRef LinkerScript::next() {
161   if (Error)
162     return "";
163   if (atEOF()) {
164     setError("unexpected EOF");
165     return "";
166   }
167   return Tokens[Pos++];
168 }
169 
170 bool LinkerScript::skip(StringRef Tok) {
171   if (Error)
172     return false;
173   if (atEOF()) {
174     setError("unexpected EOF");
175     return false;
176   }
177   if (Tok != Tokens[Pos])
178     return false;
179   ++Pos;
180   return true;
181 }
182 
183 void LinkerScript::expect(StringRef Expect) {
184   if (Error)
185     return;
186   StringRef Tok = next();
187   if (Tok != Expect)
188     setError(Expect + " expected, but got " + Tok);
189 }
190 
191 void LinkerScript::addFile(StringRef S) {
192   if (IsUnderSysroot && S.startswith("/")) {
193     SmallString<128> Path;
194     (Config->Sysroot + S).toStringRef(Path);
195     if (sys::fs::exists(Path)) {
196       Driver->addFile(Saver.save(Path.str()));
197       return;
198     }
199   }
200 
201   if (sys::path::is_absolute(S)) {
202     Driver->addFile(S);
203   } else if (S.startswith("=")) {
204     if (Config->Sysroot.empty())
205       Driver->addFile(S.substr(1));
206     else
207       Driver->addFile(Saver.save(Config->Sysroot + "/" + S.substr(1)));
208   } else if (S.startswith("-l")) {
209     Driver->addLibrary(S.substr(2));
210   } else if (sys::fs::exists(S)) {
211     Driver->addFile(S);
212   } else {
213     std::string Path = findFromSearchPaths(S);
214     if (Path.empty())
215       setError("Unable to find " + S);
216     else
217       Driver->addFile(Saver.save(Path));
218   }
219 }
220 
221 void LinkerScript::readAsNeeded() {
222   expect("(");
223   bool Orig = Config->AsNeeded;
224   Config->AsNeeded = true;
225   while (!Error) {
226     StringRef Tok = next();
227     if (Tok == ")")
228       break;
229     addFile(Tok);
230   }
231   Config->AsNeeded = Orig;
232 }
233 
234 void LinkerScript::readEntry() {
235   // -e <symbol> takes predecence over ENTRY(<symbol>).
236   expect("(");
237   StringRef Tok = next();
238   if (Config->Entry.empty())
239     Config->Entry = Tok;
240   expect(")");
241 }
242 
243 void LinkerScript::readExtern() {
244   expect("(");
245   while (!Error) {
246     StringRef Tok = next();
247     if (Tok == ")")
248       return;
249     Config->Undefined.push_back(Tok);
250   }
251 }
252 
253 void LinkerScript::readGroup() {
254   expect("(");
255   while (!Error) {
256     StringRef Tok = next();
257     if (Tok == ")")
258       return;
259     if (Tok == "AS_NEEDED") {
260       readAsNeeded();
261       continue;
262     }
263     addFile(Tok);
264   }
265 }
266 
267 void LinkerScript::readInclude() {
268   StringRef Tok = next();
269   auto MBOrErr = MemoryBuffer::getFile(Tok);
270   if (!MBOrErr) {
271     setError("cannot open " + Tok);
272     return;
273   }
274   std::unique_ptr<MemoryBuffer> &MB = *MBOrErr;
275   StringRef S = Saver.save(MB->getMemBufferRef().getBuffer());
276   std::vector<StringRef> V = tokenize(S);
277   Tokens.insert(Tokens.begin() + Pos, V.begin(), V.end());
278 }
279 
280 void LinkerScript::readOutput() {
281   // -o <file> takes predecence over OUTPUT(<file>).
282   expect("(");
283   StringRef Tok = next();
284   if (Config->OutputFile.empty())
285     Config->OutputFile = Tok;
286   expect(")");
287 }
288 
289 void LinkerScript::readOutputArch() {
290   // Error checking only for now.
291   expect("(");
292   next();
293   expect(")");
294 }
295 
296 void LinkerScript::readOutputFormat() {
297   // Error checking only for now.
298   expect("(");
299   next();
300   StringRef Tok = next();
301   if (Tok == ")")
302    return;
303   if (Tok != ",") {
304     setError("unexpected token: " + Tok);
305     return;
306   }
307   next();
308   expect(",");
309   next();
310   expect(")");
311 }
312 
313 void LinkerScript::readSearchDir() {
314   expect("(");
315   Config->SearchPaths.push_back(next());
316   expect(")");
317 }
318 
319 void LinkerScript::readSections() {
320   expect("{");
321   while (!Error && !skip("}"))
322     readOutputSectionDescription();
323 }
324 
325 void LinkerScript::readOutputSectionDescription() {
326   StringRef Name = next();
327   std::vector<StringRef> &InputSections = Config->OutputSections[Name];
328 
329   expect(":");
330   expect("{");
331   while (!Error && !skip("}")) {
332     next(); // Skip input file name.
333     expect("(");
334     while (!Error && !skip(")"))
335       InputSections.push_back(next());
336   }
337 }
338 
339 static bool isUnderSysroot(StringRef Path) {
340   if (Config->Sysroot == "")
341     return false;
342   for (; !Path.empty(); Path = sys::path::parent_path(Path))
343     if (sys::fs::equivalent(Config->Sysroot, Path))
344       return true;
345   return false;
346 }
347 
348 // Entry point. The other functions or classes are private to this file.
349 void elf2::readLinkerScript(BumpPtrAllocator *A, MemoryBufferRef MB) {
350   StringRef Path = MB.getBufferIdentifier();
351   LinkerScript(A, MB.getBuffer(), isUnderSysroot(Path)).run();
352 }
353