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