1 //===- ScriptParser.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 base parser class for linker script and dynamic 11 // list. 12 // 13 //===----------------------------------------------------------------------===// 14 15 #include "ScriptParser.h" 16 #include "Error.h" 17 #include "llvm/ADT/Twine.h" 18 19 using namespace llvm; 20 using namespace lld; 21 using namespace lld::elf; 22 23 // Returns the line that the character S[Pos] is in. 24 static StringRef getLine(StringRef S, size_t Pos) { 25 size_t Begin = S.rfind('\n', Pos); 26 size_t End = S.find('\n', Pos); 27 Begin = (Begin == StringRef::npos) ? 0 : Begin + 1; 28 if (End == StringRef::npos) 29 End = S.size(); 30 // rtrim for DOS-style newlines. 31 return S.substr(Begin, End - Begin).rtrim(); 32 } 33 34 void ScriptParserBase::printErrorPos() { 35 StringRef Tok = Tokens[Pos == 0 ? 0 : Pos - 1]; 36 StringRef Line = getLine(Input, Tok.data() - Input.data()); 37 size_t Col = Tok.data() - Line.data(); 38 error(Line); 39 error(std::string(Col, ' ') + "^"); 40 } 41 42 // We don't want to record cascading errors. Keep only the first one. 43 void ScriptParserBase::setError(const Twine &Msg) { 44 if (Error) 45 return; 46 if (Input.empty() || Tokens.empty()) { 47 error(Msg); 48 } else { 49 error("line " + Twine(getPos()) + ": " + Msg); 50 printErrorPos(); 51 } 52 Error = true; 53 } 54 55 // Split S into linker script tokens. 56 std::vector<StringRef> ScriptParserBase::tokenize(StringRef S) { 57 std::vector<StringRef> Ret; 58 for (;;) { 59 S = skipSpace(S); 60 if (S.empty()) 61 return Ret; 62 63 // Quoted token 64 if (S.startswith("\"")) { 65 size_t E = S.find("\"", 1); 66 if (E == StringRef::npos) { 67 error("unclosed quote"); 68 return {}; 69 } 70 Ret.push_back(S.substr(1, E - 1)); 71 S = S.substr(E + 1); 72 continue; 73 } 74 75 // Unquoted token 76 size_t Pos = S.find_first_not_of( 77 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz" 78 "0123456789_.$/\\~=+[]*?-:!<>"); 79 // A character that cannot start a word (which is usually a 80 // punctuation) forms a single character token. 81 if (Pos == 0) 82 Pos = 1; 83 Ret.push_back(S.substr(0, Pos)); 84 S = S.substr(Pos); 85 } 86 } 87 88 // Skip leading whitespace characters or comments. 89 StringRef ScriptParserBase::skipSpace(StringRef S) { 90 for (;;) { 91 if (S.startswith("/*")) { 92 size_t E = S.find("*/", 2); 93 if (E == StringRef::npos) { 94 error("unclosed comment in a linker script"); 95 return ""; 96 } 97 S = S.substr(E + 2); 98 continue; 99 } 100 if (S.startswith("#")) { 101 size_t E = S.find('\n', 1); 102 if (E == StringRef::npos) 103 E = S.size() - 1; 104 S = S.substr(E + 1); 105 continue; 106 } 107 size_t Size = S.size(); 108 S = S.ltrim(); 109 if (S.size() == Size) 110 return S; 111 } 112 } 113 114 // An erroneous token is handled as if it were the last token before EOF. 115 bool ScriptParserBase::atEOF() { return Error || Tokens.size() == Pos; } 116 117 StringRef ScriptParserBase::next() { 118 if (Error) 119 return ""; 120 if (atEOF()) { 121 setError("unexpected EOF"); 122 return ""; 123 } 124 return Tokens[Pos++]; 125 } 126 127 StringRef ScriptParserBase::peek() { 128 StringRef Tok = next(); 129 if (Error) 130 return ""; 131 --Pos; 132 return Tok; 133 } 134 135 bool ScriptParserBase::skip(StringRef Tok) { 136 if (Error) 137 return false; 138 if (atEOF()) { 139 setError("unexpected EOF"); 140 return false; 141 } 142 if (Tokens[Pos] != Tok) 143 return false; 144 ++Pos; 145 return true; 146 } 147 148 void ScriptParserBase::expect(StringRef Expect) { 149 if (Error) 150 return; 151 StringRef Tok = next(); 152 if (Tok != Expect) 153 setError(Expect + " expected, but got " + Tok); 154 } 155 156 // Returns the current line number. 157 size_t ScriptParserBase::getPos() { 158 if (Pos == 0) 159 return 1; 160 const char *Begin = Input.data(); 161 const char *Tok = Tokens[Pos - 1].data(); 162 return StringRef(Begin, Tok - Begin).count('\n') + 1; 163 } 164