//===- ReaderWriter/LinkerScript.cpp --------------------------------------===// // // The LLVM Linker // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// /// /// \file /// \brief Linker script parser. /// //===----------------------------------------------------------------------===// #include "lld/ReaderWriter/LinkerScript.h" namespace lld { namespace script { void Token::dump(raw_ostream &os) const { switch (_kind) { #define CASE(name) \ case Token::name: \ os << #name ": "; \ break; CASE(unknown) CASE(eof) CASE(exclaim) CASE(exclaimequal) CASE(amp) CASE(ampequal) CASE(l_paren) CASE(r_paren) CASE(star) CASE(starequal) CASE(plus) CASE(plusequal) CASE(comma) CASE(minus) CASE(minusequal) CASE(slash) CASE(slashequal) CASE(number) CASE(colon) CASE(semicolon) CASE(less) CASE(lessequal) CASE(lessless) CASE(lesslessequal) CASE(equal) CASE(equalequal) CASE(greater) CASE(greaterequal) CASE(greatergreater) CASE(greatergreaterequal) CASE(question) CASE(identifier) CASE(libname) CASE(kw_align) CASE(kw_align_with_input) CASE(kw_as_needed) CASE(kw_at) CASE(kw_discard) CASE(kw_entry) CASE(kw_exclude_file) CASE(kw_group) CASE(kw_hidden) CASE(kw_keep) CASE(kw_provide) CASE(kw_provide_hidden) CASE(kw_only_if_ro) CASE(kw_only_if_rw) CASE(kw_output_arch) CASE(kw_output_format) CASE(kw_overlay) CASE(kw_search_dir) CASE(kw_sections) CASE(kw_sort_by_alignment) CASE(kw_sort_by_init_priority) CASE(kw_sort_by_name) CASE(kw_sort_none) CASE(kw_subalign) CASE(l_brace) CASE(pipe) CASE(pipeequal) CASE(r_brace) CASE(tilde) #undef CASE } os << _range << "\n"; } static llvm::ErrorOr parseDecimal(StringRef str) { uint64_t res = 0; for (auto &c : str) { res *= 10; if (c < '0' || c > '9') return llvm::ErrorOr(std::make_error_code(std::errc::io_error)); res += c - '0'; } return res; } static llvm::ErrorOr parseOctal(StringRef str) { uint64_t res = 0; for (auto &c : str) { res <<= 3; if (c < '0' || c > '7') return llvm::ErrorOr(std::make_error_code(std::errc::io_error)); res += c - '0'; } return res; } static llvm::ErrorOr parseBinary(StringRef str) { uint64_t res = 0; for (auto &c : str) { res <<= 1; if (c != '0' && c != '1') return llvm::ErrorOr(std::make_error_code(std::errc::io_error)); res += c - '0'; } return res; } static llvm::ErrorOr parseHex(StringRef str) { uint64_t res = 0; for (auto &c : str) { res <<= 4; if (c >= '0' && c <= '9') res += c - '0'; else if (c >= 'a' && c <= 'f') res += c - 'a' + 10; else if (c >= 'A' && c <= 'F') res += c - 'A' + 10; else return llvm::ErrorOr(std::make_error_code(std::errc::io_error)); } return res; } static bool parseHexToByteStream(StringRef str, std::string &buf) { unsigned char byte = 0; bool dumpByte = str.size() % 2; for (auto &c : str) { byte <<= 4; if (c >= '0' && c <= '9') byte += c - '0'; else if (c >= 'a' && c <= 'f') byte += c - 'a' + 10; else if (c >= 'A' && c <= 'F') byte += c - 'A' + 10; else return false; if (!dumpByte) { dumpByte = true; continue; } buf.push_back(byte); byte = 0; dumpByte = false; } return !dumpByte; } static void dumpByteStream(raw_ostream &os, StringRef stream) { os << "0x"; for (auto &c : stream) { unsigned char firstNibble = c >> 4 & 0xF; if (firstNibble > 9) os << (char) ('A' + firstNibble - 10); else os << (char) ('0' + firstNibble); unsigned char secondNibble = c & 0xF; if (secondNibble > 9) os << (char) ('A' + secondNibble - 10); else os << (char) ('0' + secondNibble); } } static llvm::ErrorOr parseNum(StringRef str) { unsigned multiplier = 1; enum NumKind { decimal, hex, octal, binary }; NumKind kind = llvm::StringSwitch(str) .StartsWith("0x", hex) .StartsWith("0X", hex) .StartsWith("0", octal) .Default(decimal); // Parse scale if (str.endswith("K")) { multiplier = 1 << 10; str = str.drop_back(); } else if (str.endswith("M")) { multiplier = 1 << 20; str = str.drop_back(); } // Parse type if (str.endswith_lower("o")) { kind = octal; str = str.drop_back(); } else if (str.endswith_lower("h")) { kind = hex; str = str.drop_back(); } else if (str.endswith_lower("d")) { kind = decimal; str = str.drop_back(); } else if (str.endswith_lower("b")) { kind = binary; str = str.drop_back(); } llvm::ErrorOr res(0); switch (kind) { case hex: if (str.startswith_lower("0x")) str = str.drop_front(2); res = parseHex(str); break; case octal: res = parseOctal(str); break; case decimal: res = parseDecimal(str); break; case binary: res = parseBinary(str); break; } if (res.getError()) return res; *res = *res * multiplier; return res; } bool Lexer::canStartNumber(char c) const { return '0' <= c && c <= '9'; } bool Lexer::canContinueNumber(char c) const { switch (c) { // Digits case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': case 'A': case 'B': case 'C': case 'D': case 'E': case 'F': case 'a': case 'b': case 'c': case 'd': case 'e': case 'f': // Hex marker case 'x': case 'X': // Type suffix case 'h': case 'H': case 'o': case 'O': // Scale suffix case 'M': case 'K': return true; default: return false; } } bool Lexer::canStartName(char c) const { switch (c) { case 'A': case 'B': case 'C': case 'D': case 'E': case 'F': case 'G': case 'H': case 'I': case 'J': case 'K': case 'L': case 'M': case 'N': case 'O': case 'P': case 'Q': case 'R': case 'S': case 'T': case 'U': case 'V': case 'W': case 'X': case 'Y': case 'Z': case 'a': case 'b': case 'c': case 'd': case 'e': case 'f': case 'g': case 'h': case 'i': case 'j': case 'k': case 'l': case 'm': case 'n': case 'o': case 'p': case 'q': case 'r': case 's': case 't': case 'u': case 'v': case 'w': case 'x': case 'y': case 'z': case '_': case '.': case '$': case '/': case '\\': case '*': return true; default: return false; } } bool Lexer::canContinueName(char c) const { switch (c) { case 'A': case 'B': case 'C': case 'D': case 'E': case 'F': case 'G': case 'H': case 'I': case 'J': case 'K': case 'L': case 'M': case 'N': case 'O': case 'P': case 'Q': case 'R': case 'S': case 'T': case 'U': case 'V': case 'W': case 'X': case 'Y': case 'Z': case 'a': case 'b': case 'c': case 'd': case 'e': case 'f': case 'g': case 'h': case 'i': case 'j': case 'k': case 'l': case 'm': case 'n': case 'o': case 'p': case 'q': case 'r': case 's': case 't': case 'u': case 'v': case 'w': case 'x': case 'y': case 'z': case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': case '_': case '.': case '$': case '/': case '\\': case '~': case '=': case '+': case '[': case ']': case '*': case '?': case '-': case ':': return true; default: return false; } } /// Helper function to split a StringRef in two at the nth character. /// The StringRef s is updated, while the function returns the n first /// characters. static StringRef drop(StringRef &s, int n) { StringRef res = s.substr(0, n); s = s.drop_front(n); return res; } void Lexer::lex(Token &tok) { skipWhitespace(); if (_buffer.empty()) { tok = Token(_buffer, Token::eof); return; } switch (_buffer[0]) { case 0: tok = Token(drop(_buffer, 1), Token::eof); return; case '(': tok = Token(drop(_buffer, 1), Token::l_paren); return; case ')': tok = Token(drop(_buffer, 1), Token::r_paren); return; case '{': tok = Token(drop(_buffer, 1), Token::l_brace); return; case '}': tok = Token(drop(_buffer, 1), Token::r_brace); return; case '=': if (_buffer.startswith("==")) { tok = Token(drop(_buffer, 2), Token::equalequal); return; } tok = Token(drop(_buffer, 1), Token::equal); return; case '!': if (_buffer.startswith("!=")) { tok = Token(drop(_buffer, 2), Token::exclaimequal); return; } tok = Token(drop(_buffer, 1), Token::exclaim); return; case ',': tok = Token(drop(_buffer, 1), Token::comma); return; case ';': tok = Token(drop(_buffer, 1), Token::semicolon); return; case ':': tok = Token(drop(_buffer, 1), Token::colon); return; case '&': if (_buffer.startswith("&=")) { tok = Token(drop(_buffer, 2), Token::ampequal); return; } tok = Token(drop(_buffer, 1), Token::amp); return; case '|': if (_buffer.startswith("|=")) { tok = Token(drop(_buffer, 2), Token::pipeequal); return; } tok = Token(drop(_buffer, 1), Token::pipe); return; case '+': if (_buffer.startswith("+=")) { tok = Token(drop(_buffer, 2), Token::plusequal); return; } tok = Token(drop(_buffer, 1), Token::plus); return; case '-': { if (_buffer.startswith("-=")) { tok = Token(drop(_buffer, 2), Token::minusequal); return; } if (!_buffer.startswith("-l")) { tok = Token(drop(_buffer, 1), Token::minus); return; } // -l _buffer = _buffer.drop_front(2); StringRef::size_type start = 0; if (_buffer[start] == ':') ++start; if (!canStartName(_buffer[start])) // Create 'unknown' token. break; auto libNameEnd = std::find_if(_buffer.begin() + start + 1, _buffer.end(), [=](char c) { return !canContinueName(c); }); StringRef::size_type libNameLen = std::distance(_buffer.begin(), libNameEnd); tok = Token(_buffer.substr(0, libNameLen), Token::libname); _buffer = _buffer.drop_front(libNameLen); return; } case '<': if (_buffer.startswith("<<=")) { tok = Token(drop(_buffer, 3), Token::lesslessequal); return; } if (_buffer.startswith("<<")) { tok = Token(drop(_buffer, 2), Token::lessless); return; } if (_buffer.startswith("<=")) { tok = Token(drop(_buffer, 2), Token::lessequal); return; } tok = Token(drop(_buffer, 1), Token::less); return; case '>': if (_buffer.startswith(">>=")) { tok = Token(drop(_buffer, 3), Token::greatergreaterequal); return; } if (_buffer.startswith(">>")) { tok = Token(drop(_buffer, 2), Token::greatergreater); return; } if (_buffer.startswith(">=")) { tok = Token(drop(_buffer, 2), Token::greaterequal); return; } tok = Token(drop(_buffer, 1), Token::greater); return; case '~': tok = Token(drop(_buffer, 1), Token::tilde); return; case '\"': case '\'': { // Handle quoted strings. They are treated as identifiers for // simplicity. char c = _buffer[0]; _buffer = _buffer.drop_front(); auto quotedStringEnd = _buffer.find(c); if (quotedStringEnd == StringRef::npos || quotedStringEnd == 0) break; StringRef word = _buffer.substr(0, quotedStringEnd); tok = Token(word, Token::identifier); _buffer = _buffer.drop_front(quotedStringEnd + 1); return; } default: // Handle literal numbers if (canStartNumber(_buffer[0])) { auto endIter = std::find_if(_buffer.begin(), _buffer.end(), [=](char c) { return !canContinueNumber(c); }); StringRef::size_type end = endIter == _buffer.end() ? StringRef::npos : std::distance(_buffer.begin(), endIter); if (end == StringRef::npos || end == 0) break; StringRef word = _buffer.substr(0, end); tok = Token(word, Token::number); _buffer = _buffer.drop_front(end); return; } // Handle slashes '/', which can be either an operator inside an expression // or the beginning of an identifier if (_buffer.startswith("/=")) { tok = Token(drop(_buffer, 2), Token::slashequal); return; } if (_buffer[0] == '/' && _buffer.size() > 1 && !canContinueName(_buffer[1])) { tok = Token(drop(_buffer, 1), Token::slash); return; } // Handle stars '*' if (_buffer.startswith("*=")) { tok = Token(drop(_buffer, 2), Token::starequal); return; } if (_buffer[0] == '*' && _buffer.size() > 1 && !canContinueName(_buffer[1])) { tok = Token(drop(_buffer, 1), Token::star); return; } // Handle questions '?' if (_buffer[0] == '?' && _buffer.size() > 1 && !canContinueName(_buffer[1])) { tok = Token(drop(_buffer, 1), Token::question); return; } // keyword or identifier. if (!canStartName(_buffer[0])) break; auto endIter = std::find_if(_buffer.begin() + 1, _buffer.end(), [=](char c) { return !canContinueName(c); }); StringRef::size_type end = endIter == _buffer.end() ? StringRef::npos : std::distance(_buffer.begin(), endIter); if (end == StringRef::npos || end == 0) break; StringRef word = _buffer.substr(0, end); Token::Kind kind = llvm::StringSwitch(word) .Case("ALIGN", Token::kw_align) .Case("ALIGN_WITH_INPUT", Token::kw_align_with_input) .Case("AS_NEEDED", Token::kw_as_needed) .Case("AT", Token::kw_at) .Case("ENTRY", Token::kw_entry) .Case("EXCLUDE_FILE", Token::kw_exclude_file) .Case("GROUP", Token::kw_group) .Case("HIDDEN", Token::kw_hidden) .Case("KEEP", Token::kw_keep) .Case("ONLY_IF_RO", Token::kw_only_if_ro) .Case("ONLY_IF_RW", Token::kw_only_if_rw) .Case("OUTPUT_ARCH", Token::kw_output_arch) .Case("OUTPUT_FORMAT", Token::kw_output_format) .Case("OVERLAY", Token::kw_overlay) .Case("PROVIDE", Token::kw_provide) .Case("PROVIDE_HIDDEN", Token::kw_provide_hidden) .Case("SEARCH_DIR", Token::kw_search_dir) .Case("SECTIONS", Token::kw_sections) .Case("SORT", Token::kw_sort_by_name) .Case("SORT_BY_ALIGNMENT", Token::kw_sort_by_alignment) .Case("SORT_BY_INIT_PRIORITY", Token::kw_sort_by_init_priority) .Case("SORT_BY_NAME", Token::kw_sort_by_name) .Case("SORT_NONE", Token::kw_sort_none) .Case("SUBALIGN", Token::kw_subalign) .Case("/DISCARD/", Token::kw_discard) .Default(Token::identifier); tok = Token(word, kind); _buffer = _buffer.drop_front(end); return; } tok = Token(drop(_buffer, 1), Token::unknown); } void Lexer::skipWhitespace() { while (true) { if (_buffer.empty()) return; switch (_buffer[0]) { case ' ': case '\r': case '\n': case '\t': _buffer = _buffer.drop_front(); break; // Potential comment. case '/': if (_buffer.size() >= 2 && _buffer[1] == '*') { // Skip starting /* _buffer = _buffer.drop_front(2); // If the next char is also a /, it's not the end. if (!_buffer.empty() && _buffer[0] == '/') _buffer = _buffer.drop_front(); // Scan for /'s. We're done if it is preceded by a *. while (true) { if (_buffer.empty()) break; _buffer = _buffer.drop_front(); if (_buffer.data()[-1] == '/' && _buffer.data()[-2] == '*') break; } } else return; break; default: return; } } } // Constant functions void Constant::dump(raw_ostream &os) const { os << _num; } // Symbol functions void Symbol::dump(raw_ostream &os) const { os << _name; } // FunctionCall functions void FunctionCall::dump(raw_ostream &os) const { os << _name << "("; for (unsigned i = 0, e = _args.size(); i != e; ++i) { if (i) os << ", "; _args[i]->dump(os); } os << ")"; } // Unary functions void Unary::dump(raw_ostream &os) const { os << "("; if (_op == Unary::Minus) os << "-"; else os << "~"; _child->dump(os); os << ")"; } // BinOp functions void BinOp::dump(raw_ostream &os) const { os << "("; _lhs->dump(os); os << " "; switch (_op) { case Sum: os << "+"; break; case Sub: os << "-"; break; case Mul: os << "*"; break; case Div: os << "/"; break; case Shl: os << "<<"; break; case Shr: os << ">>"; break; case And: os << "&"; break; case Or: os << "|"; break; case CompareEqual: os << "=="; break; case CompareDifferent: os << "!="; break; case CompareLess: os << "<"; break; case CompareGreater: os << ">"; break; case CompareLessEqual: os << "<="; break; case CompareGreaterEqual: os << ">="; break; } os << " "; _rhs->dump(os); os << ")"; } // TernaryConditional functions void TernaryConditional::dump(raw_ostream &os) const { _conditional->dump(os); os << " ? "; _trueExpr->dump(os); os << " : "; _falseExpr->dump(os); } // SymbolAssignment functions void SymbolAssignment::dump(raw_ostream &os) const { int numParen = 0; if (_assignmentVisibility != Normal) { switch (_assignmentVisibility) { case Hidden: os << "HIDDEN("; break; case Provide: os << "PROVIDE("; break; case ProvideHidden: os << "PROVIDE_HIDDEN("; break; default: llvm_unreachable("Unknown visibility"); } ++numParen; } os << _symbol << " "; switch (_assignmentKind) { case Simple: os << "="; break; case Sum: os << "+="; break; case Sub: os << "-="; break; case Mul: os << "*="; break; case Div: os << "/="; break; case Shl: os << "<<="; break; case Shr: os << ">>="; break; case And: os << "&="; break; case Or: os << "|="; break; } os << " "; _expression->dump(os); if (numParen) os << ")"; os << ";"; } static int dumpSortDirectives(raw_ostream &os, WildcardSortMode sortMode) { switch (sortMode) { case WildcardSortMode::NA: return 0; case WildcardSortMode::ByName: os << "SORT_BY_NAME("; return 1; case WildcardSortMode::ByAlignment: os << "SORT_BY_ALIGNMENT("; return 1; case WildcardSortMode::ByInitPriority: os << "SORT_BY_INIT_PRIORITY("; return 1; case WildcardSortMode::ByNameAndAlignment: os << "SORT_BY_NAME(SORT_BY_ALIGNMENT("; return 2; case WildcardSortMode::ByAlignmentAndName: os << "SORT_BY_ALIGNMENT(SORT_BY_NAME("; return 2; case WildcardSortMode::None: os << "SORT_NONE("; return 1; } return 0; } // InputSectionName functions void InputSectionName::dump(raw_ostream &os) const { os << _name; } // InputSectionSortedGroup functions static void dumpInputSections(raw_ostream &os, const std::vector &secs) { bool excludeFile = false; bool first = true; for (auto &secName : secs) { if (!first) os << " "; first = false; // Coalesce multiple input sections marked with EXCLUDE_FILE in the same // EXCLUDE_FILE() group if (auto inputSec = dyn_cast(secName)) { if (!excludeFile && inputSec->hasExcludeFile()) { excludeFile = true; os << "EXCLUDE_FILE("; } else if (excludeFile && !inputSec->hasExcludeFile()) { excludeFile = false; os << ") "; } } secName->dump(os); } if (excludeFile) os << ")"; } void InputSectionSortedGroup::dump(raw_ostream &os) const { int numParen = dumpSortDirectives(os, _sortMode); dumpInputSections(os, _sections); for (int i = 0; i < numParen; ++i) os << ")"; } // InputSectionsCmd functions void InputSectionsCmd::dump(raw_ostream &os) const { if (_keep) os << "KEEP("; int numParen = dumpSortDirectives(os, _fileSortMode); os << _fileName; for (int i = 0; i < numParen; ++i) os << ")"; if (_archiveName.size() > 0) { os << ":"; numParen = dumpSortDirectives(os, _archiveSortMode); os << _archiveName; for (int i = 0; i < numParen; ++i) os << ")"; } if (_sections.size() > 0) { os << "("; dumpInputSections(os, _sections); os << ")"; } if (_keep) os << ")"; } // OutputSectionDescription functions void OutputSectionDescription::dump(raw_ostream &os) const { if (_discard) os << "/DISCARD/"; else os << _sectionName; if (_address) { os << " "; _address->dump(os); } os << " :\n"; if (_at) { os << " AT("; _at->dump(os); os << ")\n"; } if (_align) { os << " ALIGN("; _align->dump(os); os << ")\n"; } else if (_alignWithInput) { os << " ALIGN_WITH_INPUT\n"; } if (_subAlign) { os << " SUBALIGN("; _subAlign->dump(os); os << ")\n"; } switch (_constraint) { case C_None: break; case C_OnlyIfRO: os << "ONLY_IF_RO"; break; case C_OnlyIfRW: os << "ONLY_IF_RW"; break; } os << " {\n"; for (auto &command : _outputSectionCommands) { os << " "; command->dump(os); os << "\n"; } os << " }"; if (_fillStream.size() > 0) { os << " ="; dumpByteStream(os, _fillStream); } else if (_fillExpr) { os << " ="; _fillExpr->dump(os); } } // Sections functions void Sections::dump(raw_ostream &os) const { os << "SECTIONS\n{\n"; for (auto &command : _sectionsCommands) { command->dump(os); os << "\n"; } os << "}\n"; } // Parser functions LinkerScript *Parser::parse() { // Get the first token. _lex.lex(_tok); // Parse top level commands. while (true) { switch (_tok._kind) { case Token::eof: return &_script; case Token::semicolon: consumeToken(); break; case Token::kw_output_format: { auto outputFormat = parseOutputFormat(); if (!outputFormat) return nullptr; _script._commands.push_back(outputFormat); break; } case Token::kw_output_arch: { auto outputArch = parseOutputArch(); if (!outputArch) return nullptr; _script._commands.push_back(outputArch); break; } case Token::kw_group: { auto group = parseGroup(); if (!group) return nullptr; _script._commands.push_back(group); break; } case Token::kw_as_needed: // Not allowed at top level. error(_tok, "AS_NEEDED not allowed at top level."); return nullptr; case Token::kw_entry: { Entry *entry = parseEntry(); if (!entry) return nullptr; _script._commands.push_back(entry); break; } case Token::kw_search_dir: { SearchDir *searchDir = parseSearchDir(); if (!searchDir) return nullptr; _script._commands.push_back(searchDir); break; } case Token::kw_sections: { Sections *sections = parseSections(); if (!sections) return nullptr; _script._commands.push_back(sections); break; } case Token::identifier: case Token::kw_hidden: case Token::kw_provide: case Token::kw_provide_hidden: { const Command *cmd = parseSymbolAssignment(); if (!cmd) return nullptr; _script._commands.push_back(cmd); break; } default: // Unexpected. error(_tok, "expected linker script command"); return nullptr; } } return nullptr; } const Expression *Parser::parseFunctionCall() { assert((_tok._kind == Token::identifier || _tok._kind == Token::kw_align) && "expected function call first tokens"); std::vector params; StringRef name = _tok._range; consumeToken(); if (!expectAndConsume(Token::l_paren, "expected (")) return nullptr; if (_tok._kind == Token::r_paren) { consumeToken(); return new (_alloc) FunctionCall(_tok._range, params); } if (const Expression *firstParam = parseExpression()) params.push_back(firstParam); else return nullptr; while (_tok._kind == Token::comma) { consumeToken(); if (const Expression *param = parseExpression()) params.push_back(param); else return nullptr; } if (!expectAndConsume(Token::r_paren, "expected )")) return nullptr; return new (_alloc) FunctionCall(name, params); } bool Parser::expectExprOperand() { if (!(_tok._kind == Token::identifier || _tok._kind == Token::number || _tok._kind == Token::kw_align || _tok._kind == Token::l_paren || _tok._kind == Token::minus || _tok._kind == Token::tilde)) { error(_tok, "expected symbol, number, minus, tilde or left parenthesis."); return false; } return true; } const Expression *Parser::parseExprOperand() { if (!expectExprOperand()) return nullptr; switch (_tok._kind) { case Token::identifier: { if (peek()._kind== Token::l_paren) return parseFunctionCall(); Symbol *sym = new (_alloc) Symbol(_tok._range); consumeToken(); return sym; } case Token::kw_align: return parseFunctionCall(); case Token::minus: consumeToken(); return new (_alloc) Unary(Unary::Minus, parseExprOperand()); case Token::tilde: consumeToken(); return new (_alloc) Unary(Unary::Not, parseExprOperand()); case Token::number: { auto val = parseNum(_tok._range); if (val.getError()) { error(_tok, "Unrecognized number constant"); return nullptr; } Constant *c = new (_alloc) Constant(*val); consumeToken(); return c; } case Token::l_paren: { consumeToken(); const Expression *expr = parseExpression(); if (!expectAndConsume(Token::r_paren, "expected )")) return nullptr; return expr; } default: llvm_unreachable("Unknown token"); } } static bool TokenToBinOp(const Token &tok, BinOp::Operation &op, unsigned &precedence) { switch (tok._kind) { case Token::star: op = BinOp::Mul; precedence = 3; return true; case Token::slash: op = BinOp::Div; precedence = 3; return true; case Token::plus: op = BinOp::Sum; precedence = 4; return true; case Token::minus: op = BinOp::Sub; precedence = 4; return true; case Token::lessless: op = BinOp::Shl; precedence = 5; return true; case Token::greatergreater: op = BinOp::Shr; precedence = 5; return true; case Token::less: op = BinOp::CompareLess; precedence = 6; return true; case Token::greater: op = BinOp::CompareGreater; precedence = 6; return true; case Token::lessequal: op = BinOp::CompareLessEqual; precedence = 6; return true; case Token::greaterequal: op = BinOp::CompareGreaterEqual; precedence = 6; return true; case Token::equalequal: op = BinOp::CompareEqual; precedence = 7; return true; case Token::exclaimequal: op = BinOp::CompareDifferent; precedence = 7; return true; case Token::amp: op = BinOp::And; precedence = 8; return true; case Token::pipe: op = BinOp::Or; precedence = 10; return true; default: break; } return false; } static bool isExpressionOperator(Token tok) { switch (tok._kind) { case Token::star: case Token::slash: case Token::plus: case Token::minus: case Token::lessless: case Token::greatergreater: case Token::less: case Token::greater: case Token::lessequal: case Token::greaterequal: case Token::equalequal: case Token::exclaimequal: case Token::amp: case Token::pipe: case Token::question: return true; default: return false; } } const Expression *Parser::parseExpression(unsigned precedence) { assert(precedence <= 13 && "Invalid precedence value"); if (!expectExprOperand()) return nullptr; const Expression *expr = parseExprOperand(); if (!expr) return nullptr; BinOp::Operation op; unsigned binOpPrecedence = 0; if (TokenToBinOp(_tok, op, binOpPrecedence)) { if (precedence >= binOpPrecedence) return parseOperatorOperandLoop(expr, precedence); return expr; } // Non-binary operators if (_tok._kind == Token::question && precedence >= 13) return parseOperatorOperandLoop(expr, precedence); return expr; } const Expression *Parser::parseOperatorOperandLoop(const Expression *lhs, unsigned highestPrecedence) { assert(highestPrecedence <= 13 && "Invalid precedence value"); unsigned precedence = 0; const Expression *binOp = nullptr; while (1) { BinOp::Operation op; if (!TokenToBinOp(_tok, op, precedence)) { if (_tok._kind == Token::question && highestPrecedence >= 13) return parseTernaryCondOp(lhs); return binOp; } if (precedence > highestPrecedence) return binOp; consumeToken(); const Expression *rhs = parseExpression(precedence - 1); if (!rhs) return nullptr; binOp = new (_alloc) BinOp(lhs, op, rhs); lhs = binOp; } } const Expression *Parser::parseTernaryCondOp(const Expression *lhs) { assert(_tok._kind == Token::question && "Expected question mark"); consumeToken(); // The ternary conditional operator has right-to-left associativity. // To implement this, we allow our children to contain ternary conditional // operators themselves (precedence 13). const Expression *trueExpr = parseExpression(13); if (!trueExpr) return nullptr; if (!expectAndConsume(Token::colon, "expected :")) return nullptr; const Expression *falseExpr = parseExpression(13); if (!falseExpr) return nullptr; return new (_alloc) TernaryConditional(lhs, trueExpr, falseExpr); } // Parse OUTPUT_FORMAT(ident) OutputFormat *Parser::parseOutputFormat() { assert(_tok._kind == Token::kw_output_format && "Expected OUTPUT_FORMAT!"); consumeToken(); if (!expectAndConsume(Token::l_paren, "expected (")) return nullptr; if (_tok._kind != Token::identifier) { error(_tok, "Expected identifier in OUTPUT_FORMAT."); return nullptr; } auto ret = new (_alloc) OutputFormat(_tok._range); consumeToken(); do { if (isNextToken(Token::comma)) consumeToken(); else break; if (_tok._kind != Token::identifier) { error(_tok, "Expected identifier in OUTPUT_FORMAT."); return nullptr; } ret->addOutputFormat(_tok._range); consumeToken(); } while (isNextToken(Token::comma)); if (!expectAndConsume(Token::r_paren, "expected )")) return nullptr; return ret; } // Parse OUTPUT_ARCH(ident) OutputArch *Parser::parseOutputArch() { assert(_tok._kind == Token::kw_output_arch && "Expected OUTPUT_ARCH!"); consumeToken(); if (!expectAndConsume(Token::l_paren, "expected (")) return nullptr; if (_tok._kind != Token::identifier) { error(_tok, "Expected identifier in OUTPUT_ARCH."); return nullptr; } auto ret = new (_alloc) OutputArch(_tok._range); consumeToken(); if (!expectAndConsume(Token::r_paren, "expected )")) return nullptr; return ret; } // Parse GROUP(file ...) Group *Parser::parseGroup() { assert(_tok._kind == Token::kw_group && "Expected GROUP!"); consumeToken(); if (!expectAndConsume(Token::l_paren, "expected (")) return nullptr; std::vector paths; while (_tok._kind == Token::identifier || _tok._kind == Token::libname || _tok._kind == Token::kw_as_needed) { switch (_tok._kind) { case Token::identifier: paths.push_back(Path(_tok._range)); consumeToken(); break; case Token::libname: paths.push_back(Path(_tok._range, false, true)); consumeToken(); break; case Token::kw_as_needed: if (!parseAsNeeded(paths)) return nullptr; break; default: llvm_unreachable("Invalid token."); } } auto ret = new (_alloc) Group(paths); if (!expectAndConsume(Token::r_paren, "expected )")) return nullptr; return ret; } // Parse AS_NEEDED(file ...) bool Parser::parseAsNeeded(std::vector &paths) { assert(_tok._kind == Token::kw_as_needed && "Expected AS_NEEDED!"); consumeToken(); if (!expectAndConsume(Token::l_paren, "expected (")) return false; while (_tok._kind == Token::identifier || _tok._kind == Token::libname) { switch (_tok._kind) { case Token::identifier: paths.push_back(Path(_tok._range, true, false)); consumeToken(); break; case Token::libname: paths.push_back(Path(_tok._range, true, true)); consumeToken(); break; default: llvm_unreachable("Invalid token."); } } if (!expectAndConsume(Token::r_paren, "expected )")) return false; return true; } // Parse ENTRY(ident) Entry *Parser::parseEntry() { assert(_tok._kind == Token::kw_entry && "Expected ENTRY!"); consumeToken(); if (!expectAndConsume(Token::l_paren, "expected (")) return nullptr; if (_tok._kind != Token::identifier) { error(_tok, "expected identifier in ENTRY"); return nullptr; } StringRef entryName(_tok._range); consumeToken(); if (!expectAndConsume(Token::r_paren, "expected )")) return nullptr; return new (_alloc) Entry(entryName); } // Parse SEARCH_DIR(ident) SearchDir *Parser::parseSearchDir() { assert(_tok._kind == Token::kw_search_dir && "Expected SEARCH_DIR!"); consumeToken(); if (!expectAndConsume(Token::l_paren, "expected (")) return nullptr; if (_tok._kind != Token::identifier) { error(_tok, "expected identifier in SEARCH_DIR"); return nullptr; } StringRef searchPath(_tok._range); consumeToken(); if (!expectAndConsume(Token::r_paren, "expected )")) return nullptr; return new (_alloc) SearchDir(searchPath); } const SymbolAssignment *Parser::parseSymbolAssignment() { assert((_tok._kind == Token::identifier || _tok._kind == Token::kw_hidden || _tok._kind == Token::kw_provide || _tok._kind == Token::kw_provide_hidden) && "Expected identifier!"); SymbolAssignment::AssignmentVisibility visibility = SymbolAssignment::Normal; SymbolAssignment::AssignmentKind kind; int numParen = 0; switch (_tok._kind) { case Token::kw_hidden: visibility = SymbolAssignment::Hidden; ++numParen; consumeToken(); if (!expectAndConsume(Token::l_paren, "expected (")) return nullptr; break; case Token::kw_provide: visibility = SymbolAssignment::Provide; ++numParen; consumeToken(); if (!expectAndConsume(Token::l_paren, "expected (")) return nullptr; break; case Token::kw_provide_hidden: visibility = SymbolAssignment::ProvideHidden; ++numParen; consumeToken(); if (!expectAndConsume(Token::l_paren, "expected (")) return nullptr; break; default: break; } StringRef name = _tok._range; consumeToken(); // Parse assignment operator (=, +=, -= etc.) switch (_tok._kind) { case Token::equal: kind = SymbolAssignment::Simple; break; case Token::plusequal: kind = SymbolAssignment::Sum; break; case Token::minusequal: kind = SymbolAssignment::Sub; break; case Token::starequal: kind = SymbolAssignment::Mul; break; case Token::slashequal: kind = SymbolAssignment::Div; break; case Token::ampequal: kind = SymbolAssignment::And; break; case Token::pipeequal: kind = SymbolAssignment::Or; break; case Token::lesslessequal: kind = SymbolAssignment::Shl; break; case Token::greatergreaterequal: kind = SymbolAssignment::Shr; break; default: error(_tok, "unexpected token"); return nullptr; } consumeToken(); const Expression *expr = nullptr; switch (_tok._kind) { case Token::number: case Token::kw_align: case Token::identifier: case Token::l_paren: expr = parseExpression(); if (!expr) return nullptr; break; default: error(_tok, "unexpected token while parsing assignment value."); return nullptr; } for (int i = 0; i < numParen; ++i) if (!expectAndConsume(Token::r_paren, "expected )")) return nullptr; return new (_alloc) SymbolAssignment(name, expr, kind, visibility); } llvm::ErrorOr Parser::parseExcludeFile() { assert(_tok._kind == Token::kw_exclude_file && "Expected EXCLUDE_FILE!"); InputSectionsCmd::VectorTy res; consumeToken(); if (!expectAndConsume(Token::l_paren, "expected (")) return llvm::ErrorOr( std::make_error_code(std::errc::io_error)); while (_tok._kind == Token::identifier) { res.push_back(new (_alloc) InputSectionName(_tok._range, true)); consumeToken(); } if (!expectAndConsume(Token::r_paren, "expected )")) return llvm::ErrorOr( std::make_error_code(std::errc::io_error)); return llvm::ErrorOr(std::move(res)); } int Parser::parseSortDirectives(WildcardSortMode &sortMode) { int numParsedDirectives = 0; sortMode = WildcardSortMode::NA; if (_tok._kind == Token::kw_sort_by_name) { consumeToken(); if (!expectAndConsume(Token::l_paren, "expected (")) return -1; ++numParsedDirectives; sortMode = WildcardSortMode::ByName; } if (_tok._kind == Token::kw_sort_by_init_priority) { consumeToken(); if (!expectAndConsume(Token::l_paren, "expected (")) return -1; ++numParsedDirectives; sortMode = WildcardSortMode::ByInitPriority; } if (_tok._kind == Token::kw_sort_by_alignment) { consumeToken(); if (!expectAndConsume(Token::l_paren, "expected (")) return -1; ++numParsedDirectives; if (sortMode != WildcardSortMode::ByName) sortMode = WildcardSortMode::ByAlignment; else sortMode = WildcardSortMode::ByNameAndAlignment; } if (numParsedDirectives < 2 && _tok._kind == Token::kw_sort_by_name) { consumeToken(); if (!expectAndConsume(Token::l_paren, "expected (")) return -1; ++numParsedDirectives; if (sortMode == WildcardSortMode::ByAlignment) sortMode = WildcardSortMode::ByAlignmentAndName; } if (numParsedDirectives < 2 && _tok._kind == Token::kw_sort_by_alignment) { consumeToken(); if (!expectAndConsume(Token::l_paren, "expected (")) return -1; ++numParsedDirectives; } if (numParsedDirectives == 0 && _tok._kind == Token::kw_sort_none) { consumeToken(); if (!expectAndConsume(Token::l_paren, "expected (")) return -1; ++numParsedDirectives; sortMode = WildcardSortMode::None; } return numParsedDirectives; } const InputSection *Parser::parseSortedInputSections() { assert((_tok._kind == Token::kw_sort_by_name || _tok._kind == Token::kw_sort_by_alignment || _tok._kind == Token::kw_sort_by_init_priority || _tok._kind == Token::kw_sort_none) && "Expected SORT directives!"); WildcardSortMode sortMode = WildcardSortMode::NA; int numParen = parseSortDirectives(sortMode); if (numParen == -1) return nullptr; std::vector inputSections; while (_tok._kind == Token::identifier) { inputSections.push_back(new (_alloc) InputSectionName(_tok._range, false)); consumeToken(); } // Eat "numParen" rparens for (int i = 0, e = numParen; i != e; ++i) if (!expectAndConsume(Token::r_paren, "expected )")) return nullptr; return new (_alloc) InputSectionSortedGroup(sortMode, inputSections); } const InputSectionsCmd *Parser::parseInputSectionsCmd() { assert((_tok._kind == Token::identifier || _tok._kind == Token::colon || _tok._kind == Token::star || _tok._kind == Token::kw_keep || _tok._kind == Token::kw_sort_by_name || _tok._kind == Token::kw_sort_by_alignment || _tok._kind == Token::kw_sort_by_init_priority || _tok._kind == Token::kw_sort_none) && "Expected input section first tokens!"); int numParen = 1; bool keep = false; WildcardSortMode fileSortMode = WildcardSortMode::NA; WildcardSortMode archiveSortMode = WildcardSortMode::NA; StringRef fileName; StringRef archiveName; if (_tok._kind == Token::kw_keep) { consumeToken(); if (!expectAndConsume(Token::l_paren, "expected (")) return nullptr; ++numParen; keep = true; } // Input name if (_tok._kind != Token::colon) { int numParen = parseSortDirectives(fileSortMode); if (numParen == -1) return nullptr; fileName = _tok._range; consumeToken(); if (numParen) { while (numParen--) if (!expectAndConsume(Token::r_paren, "expected )")) return nullptr; } } if (_tok._kind == Token::colon) { consumeToken(); if (_tok._kind == Token::identifier || _tok._kind == Token::kw_sort_by_name || _tok._kind == Token::kw_sort_by_alignment || _tok._kind == Token::kw_sort_by_init_priority || _tok._kind == Token::kw_sort_none) { int numParen = parseSortDirectives(archiveSortMode); if (numParen == -1) return nullptr; archiveName = _tok._range; consumeToken(); for (int i = 0; i != numParen; ++i) if (!expectAndConsume(Token::r_paren, "expected )")) return nullptr; } } std::vector inputSections; if (_tok._kind != Token::l_paren) return new (_alloc) InputSectionsCmd(fileName, archiveName, keep, fileSortMode, archiveSortMode, inputSections); consumeToken(); while (_tok._kind == Token::identifier || _tok._kind == Token::kw_exclude_file || _tok._kind == Token::kw_sort_by_name || _tok._kind == Token::kw_sort_by_alignment || _tok._kind == Token::kw_sort_by_init_priority || _tok._kind == Token::kw_sort_none) { switch (_tok._kind) { case Token::kw_exclude_file: { auto vec = parseExcludeFile(); if (vec.getError()) return nullptr; inputSections.insert(inputSections.end(), vec->begin(), vec->end()); break; } case Token::star: case Token::identifier: { inputSections.push_back(new (_alloc) InputSectionName(_tok._range, false)); consumeToken(); break; } case Token::kw_sort_by_name: case Token::kw_sort_by_alignment: case Token::kw_sort_by_init_priority: case Token::kw_sort_none: { const InputSection *group = parseSortedInputSections(); if (!group) return nullptr; inputSections.push_back(group); break; } default: llvm_unreachable("Unknown token"); } } for (int i = 0; i < numParen; ++i) if (!expectAndConsume(Token::r_paren, "expected )")) return nullptr; return new (_alloc) InputSectionsCmd(fileName, archiveName, keep, fileSortMode, archiveSortMode, inputSections); } const OutputSectionDescription *Parser::parseOutputSectionDescription() { assert((_tok._kind == Token::kw_discard || _tok._kind == Token::identifier) && "Expected /DISCARD/ or identifier!"); StringRef sectionName; const Expression *address = nullptr; const Expression *align = nullptr; const Expression *subAlign = nullptr; const Expression *at = nullptr; const Expression *fillExpr = nullptr; StringRef fillStream; bool alignWithInput = false; bool discard = false; OutputSectionDescription::Constraint constraint = OutputSectionDescription::C_None; std::vector outputSectionCommands; if (_tok._kind == Token::kw_discard) discard = true; else sectionName = _tok._range; consumeToken(); if (_tok._kind == Token::number || _tok._kind == Token::identifier || _tok._kind == Token::kw_align || _tok._kind == Token::l_paren) { address = parseExpression(); if (!address) return nullptr; } if (!expectAndConsume(Token::colon, "expected :")) return nullptr; if (_tok._kind == Token::kw_at) { consumeToken(); at = parseExpression(); if (!at) return nullptr; } if (_tok._kind == Token::kw_align) { consumeToken(); align = parseExpression(); if (!align) return nullptr; } if (_tok._kind == Token::kw_align_with_input) { consumeToken(); alignWithInput = true; } if (_tok._kind == Token::kw_subalign) { consumeToken(); subAlign = parseExpression(); if (!subAlign) return nullptr; } if (_tok._kind == Token::kw_only_if_ro) { consumeToken(); constraint = OutputSectionDescription::C_OnlyIfRO; } else if (_tok._kind == Token::kw_only_if_rw) { consumeToken(); constraint = OutputSectionDescription::C_OnlyIfRW; } if (!expectAndConsume(Token::l_brace, "expected {")) return nullptr; // Parse zero or more output-section-commands while (_tok._kind != Token::r_brace) { switch (_tok._kind) { case Token::semicolon: consumeToken(); break; case Token::identifier: switch (peek()._kind) { case Token::equal: case Token::plusequal: case Token::minusequal: case Token::starequal: case Token::slashequal: case Token::ampequal: case Token::pipeequal: case Token::lesslessequal: case Token::greatergreaterequal: if (const Command *cmd = parseSymbolAssignment()) outputSectionCommands.push_back(cmd); else return nullptr; break; default: if (const Command *cmd = parseInputSectionsCmd()) outputSectionCommands.push_back(cmd); else return nullptr; break; } break; case Token::kw_keep: case Token::star: case Token::colon: case Token::kw_sort_by_name: case Token::kw_sort_by_alignment: case Token::kw_sort_by_init_priority: case Token::kw_sort_none: if (const Command *cmd = parseInputSectionsCmd()) outputSectionCommands.push_back(cmd); else return nullptr; break; case Token::kw_hidden: case Token::kw_provide: case Token::kw_provide_hidden: if (const Command *cmd = parseSymbolAssignment()) outputSectionCommands.push_back(cmd); else return nullptr; break; default: error(_tok, "expected symbol assignment or input file name."); return nullptr; } } if (!expectAndConsume(Token::r_brace, "expected }")) return nullptr; if (_tok._kind == Token::equal) { consumeToken(); if (_tok._kind != Token::number || !_tok._range.startswith_lower("0x")) { fillExpr = parseExpression(); if (!fillExpr) return nullptr; } else { std::string strBuf; if (isExpressionOperator(peek()) || !parseHexToByteStream(_tok._range.drop_front(2), strBuf)) { fillExpr = parseExpression(); if(!fillExpr) return nullptr; } else { char *rawBuf = (char *) _alloc.Allocate(strBuf.size(), 1); memcpy(rawBuf, strBuf.c_str(), strBuf.size()); fillStream = StringRef(rawBuf, strBuf.size()); consumeToken(); } } } return new (_alloc) OutputSectionDescription( sectionName, address, align, subAlign, at, fillExpr, fillStream, alignWithInput, discard, constraint, outputSectionCommands); } const Overlay *Parser::parseOverlay() { assert(_tok._kind == Token::kw_overlay && "Expected OVERLAY!"); error(_tok, "Overlay description is not yet supported."); return nullptr; } Sections *Parser::parseSections() { assert(_tok._kind == Token::kw_sections && "Expected SECTIONS!"); consumeToken(); if (!expectAndConsume(Token::l_brace, "expected {")) return nullptr; std::vector sectionsCommands; bool unrecognizedToken = false; // Parse zero or more sections-commands while (!unrecognizedToken) { switch (_tok._kind) { case Token::semicolon: consumeToken(); break; case Token::identifier: switch (peek()._kind) { case Token::equal: case Token::plusequal: case Token::minusequal: case Token::starequal: case Token::slashequal: case Token::ampequal: case Token::pipeequal: case Token::lesslessequal: case Token::greatergreaterequal: if (const Command *cmd = parseSymbolAssignment()) sectionsCommands.push_back(cmd); else return nullptr; break; default: if (const Command *cmd = parseOutputSectionDescription()) sectionsCommands.push_back(cmd); else return nullptr; break; } break; case Token::kw_discard: case Token::star: if (const Command *cmd = parseOutputSectionDescription()) sectionsCommands.push_back(cmd); else return nullptr; break; case Token::kw_entry: if (const Command *cmd = parseEntry()) sectionsCommands.push_back(cmd); else return nullptr; break; case Token::kw_hidden: case Token::kw_provide: case Token::kw_provide_hidden: if (const Command *cmd = parseSymbolAssignment()) sectionsCommands.push_back(cmd); else return nullptr; break; case Token::kw_overlay: if (const Command *cmd = parseOverlay()) sectionsCommands.push_back(cmd); else return nullptr; break; default: unrecognizedToken = true; break; } } if (!expectAndConsume( Token::r_brace, "expected symbol assignment, entry, overlay or output section name.")) return nullptr; return new (_alloc) Sections(sectionsCommands); } } // end namespace script } // end namespace lld