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