| //===- ReaderWriter/LinkerScript.cpp ----------------------------*- C++ -*-===// |
| // |
| // 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" |
| |
| #include "llvm/ADT/APInt.h" |
| #include "llvm/ADT/StringExtras.h" |
| #include "llvm/Support/Errc.h" |
| #include "llvm/Support/ELF.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_extern) |
| CASE(kw_filehdr) |
| CASE(kw_fill) |
| CASE(kw_flags) |
| CASE(kw_group) |
| CASE(kw_hidden) |
| CASE(kw_input) |
| CASE(kw_keep) |
| CASE(kw_length) |
| CASE(kw_memory) |
| CASE(kw_origin) |
| CASE(kw_phdrs) |
| CASE(kw_provide) |
| CASE(kw_provide_hidden) |
| CASE(kw_only_if_ro) |
| CASE(kw_only_if_rw) |
| CASE(kw_output) |
| 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<uint64_t> parseDecimal(StringRef str) { |
| uint64_t res = 0; |
| for (auto &c : str) { |
| res *= 10; |
| if (c < '0' || c > '9') |
| return llvm::ErrorOr<uint64_t>(make_error_code(llvm::errc::io_error)); |
| res += c - '0'; |
| } |
| return res; |
| } |
| |
| static llvm::ErrorOr<uint64_t> parseOctal(StringRef str) { |
| uint64_t res = 0; |
| for (auto &c : str) { |
| res <<= 3; |
| if (c < '0' || c > '7') |
| return llvm::ErrorOr<uint64_t>(make_error_code(llvm::errc::io_error)); |
| res += c - '0'; |
| } |
| return res; |
| } |
| |
| static llvm::ErrorOr<uint64_t> parseBinary(StringRef str) { |
| uint64_t res = 0; |
| for (auto &c : str) { |
| res <<= 1; |
| if (c != '0' && c != '1') |
| return llvm::ErrorOr<uint64_t>(make_error_code(llvm::errc::io_error)); |
| res += c - '0'; |
| } |
| return res; |
| } |
| |
| static llvm::ErrorOr<uint64_t> 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<uint64_t>(make_error_code(llvm::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<uint64_t> parseNum(StringRef str) { |
| unsigned multiplier = 1; |
| enum NumKind { decimal, hex, octal, binary }; |
| NumKind kind = llvm::StringSwitch<NumKind>(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<uint64_t> 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 { |
| // [xX] = hex marker, [hHoO] = type suffix, [MK] = scale suffix. |
| return strchr("0123456789ABCDEFabcdefxXhHoOMK", c); |
| } |
| |
| bool Lexer::canStartName(char c) const { |
| return strchr( |
| "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz_.$/\\*", c); |
| } |
| |
| bool Lexer::canContinueName(char c) const { |
| return strchr("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz" |
| "0123456789_.$/\\~=+[]*?-:", c); |
| } |
| |
| /// 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<lib name> |
| _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<Token::Kind>(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("EXTERN", Token::kw_extern) |
| .Case("FILEHDR", Token::kw_filehdr) |
| .Case("FILL", Token::kw_fill) |
| .Case("FLAGS", Token::kw_flags) |
| .Case("GROUP", Token::kw_group) |
| .Case("HIDDEN", Token::kw_hidden) |
| .Case("INPUT", Token::kw_input) |
| .Case("KEEP", Token::kw_keep) |
| .Case("LENGTH", Token::kw_length) |
| .Case("l", Token::kw_length) |
| .Case("len", Token::kw_length) |
| .Case("MEMORY", Token::kw_memory) |
| .Case("ONLY_IF_RO", Token::kw_only_if_ro) |
| .Case("ONLY_IF_RW", Token::kw_only_if_rw) |
| .Case("ORIGIN", Token::kw_origin) |
| .Case("o", Token::kw_origin) |
| .Case("org", Token::kw_origin) |
| .Case("OUTPUT", Token::kw_output) |
| .Case("OUTPUT_ARCH", Token::kw_output_arch) |
| .Case("OUTPUT_FORMAT", Token::kw_output_format) |
| .Case("OVERLAY", Token::kw_overlay) |
| .Case("PHDRS", Token::kw_phdrs) |
| .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() <= 1 || _buffer[1] != '*') |
| return; |
| // 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; |
| } |
| break; |
| default: |
| return; |
| } |
| } |
| } |
| |
| // Constant functions |
| void Constant::dump(raw_ostream &os) const { os << _num; } |
| |
| ErrorOr<int64_t> Constant::evalExpr(const SymbolTableTy &symbolTable) const { |
| return _num; |
| } |
| |
| // Symbol functions |
| void Symbol::dump(raw_ostream &os) const { os << _name; } |
| |
| ErrorOr<int64_t> Symbol::evalExpr(const SymbolTableTy &symbolTable) const { |
| auto it = symbolTable.find(_name); |
| if (it == symbolTable.end()) |
| return LinkerScriptReaderError::unknown_symbol_in_expr; |
| return it->second; |
| } |
| |
| // 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 << ")"; |
| } |
| |
| ErrorOr<int64_t> |
| FunctionCall::evalExpr(const SymbolTableTy &symbolTable) const { |
| return LinkerScriptReaderError::unrecognized_function_in_expr; |
| } |
| |
| // Unary functions |
| void Unary::dump(raw_ostream &os) const { |
| os << "("; |
| if (_op == Unary::Minus) |
| os << "-"; |
| else |
| os << "~"; |
| _child->dump(os); |
| os << ")"; |
| } |
| |
| ErrorOr<int64_t> Unary::evalExpr(const SymbolTableTy &symbolTable) const { |
| auto child = _child->evalExpr(symbolTable); |
| if (child.getError()) |
| return child.getError(); |
| |
| int64_t childRes = *child; |
| switch (_op) { |
| case Unary::Minus: |
| return -childRes; |
| case Unary::Not: |
| return ~childRes; |
| } |
| |
| llvm_unreachable(""); |
| } |
| |
| // 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 << ")"; |
| } |
| |
| ErrorOr<int64_t> BinOp::evalExpr(const SymbolTableTy &symbolTable) const { |
| auto lhs = _lhs->evalExpr(symbolTable); |
| if (lhs.getError()) |
| return lhs.getError(); |
| auto rhs = _rhs->evalExpr(symbolTable); |
| if (rhs.getError()) |
| return rhs.getError(); |
| |
| int64_t lhsRes = *lhs; |
| int64_t rhsRes = *rhs; |
| |
| switch(_op) { |
| case And: return lhsRes & rhsRes; |
| case CompareDifferent: return lhsRes != rhsRes; |
| case CompareEqual: return lhsRes == rhsRes; |
| case CompareGreater: return lhsRes > rhsRes; |
| case CompareGreaterEqual: return lhsRes >= rhsRes; |
| case CompareLess: return lhsRes < rhsRes; |
| case CompareLessEqual: return lhsRes <= rhsRes; |
| case Div: return lhsRes / rhsRes; |
| case Mul: return lhsRes * rhsRes; |
| case Or: return lhsRes | rhsRes; |
| case Shl: return lhsRes << rhsRes; |
| case Shr: return lhsRes >> rhsRes; |
| case Sub: return lhsRes - rhsRes; |
| case Sum: return lhsRes + rhsRes; |
| } |
| |
| llvm_unreachable(""); |
| } |
| |
| // TernaryConditional functions |
| void TernaryConditional::dump(raw_ostream &os) const { |
| _conditional->dump(os); |
| os << " ? "; |
| _trueExpr->dump(os); |
| os << " : "; |
| _falseExpr->dump(os); |
| } |
| |
| ErrorOr<int64_t> |
| TernaryConditional::evalExpr(const SymbolTableTy &symbolTable) const { |
| auto conditional = _conditional->evalExpr(symbolTable); |
| if (conditional.getError()) |
| return conditional.getError(); |
| if (*conditional) |
| return _trueExpr->evalExpr(symbolTable); |
| return _falseExpr->evalExpr(symbolTable); |
| } |
| |
| // SymbolAssignment functions |
| void SymbolAssignment::dump(raw_ostream &os) const { |
| int numParen = 0; |
| |
| if (_assignmentVisibility != Default) { |
| 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, |
| llvm::ArrayRef<const InputSection *> 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<InputSectionName>(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 << _memberName; |
| 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 << ")"; |
| } |
| |
| void FillCmd::dump(raw_ostream &os) const { |
| os << "FILL("; |
| dumpByteStream(os, StringRef((const char *)_bytes.begin(), _bytes.size())); |
| 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 << " }"; |
| |
| for (auto && phdr : _phdrs) |
| os << " : " << phdr; |
| |
| if (_fillStream.size() > 0) { |
| os << " ="; |
| dumpByteStream(os, _fillStream); |
| } else if (_fillExpr) { |
| os << " ="; |
| _fillExpr->dump(os); |
| } |
| } |
| |
| // Special header that discards output sections assigned to it. |
| static const PHDR PHDR_NONE("NONE", 0, false, false, nullptr, 0); |
| |
| bool PHDR::isNone() const { |
| return this == &PHDR_NONE; |
| } |
| |
| void PHDR::dump(raw_ostream &os) const { |
| os << _name << " " << _type; |
| if (_includeFileHdr) |
| os << " FILEHDR"; |
| if (_includePHDRs) |
| os << " PHDRS"; |
| if (_at) { |
| os << " AT ("; |
| _at->dump(os); |
| os << ")"; |
| } |
| if (_flags) |
| os << " FLAGS (" << _flags << ")"; |
| os << ";\n"; |
| } |
| |
| void PHDRS::dump(raw_ostream &os) const { |
| os << "PHDRS\n{\n"; |
| for (auto &&phdr : _phdrs) { |
| phdr->dump(os); |
| } |
| os << "}\n"; |
| } |
| |
| // Sections functions |
| void Sections::dump(raw_ostream &os) const { |
| os << "SECTIONS\n{\n"; |
| for (auto &command : _sectionsCommands) { |
| command->dump(os); |
| os << "\n"; |
| } |
| os << "}\n"; |
| } |
| |
| // Memory functions |
| void MemoryBlock::dump(raw_ostream &os) const { |
| os << _name; |
| |
| if (!_attr.empty()) |
| os << " (" << _attr << ")"; |
| |
| os << " : "; |
| |
| os << "ORIGIN = "; |
| _origin->dump(os); |
| os << ", "; |
| |
| os << "LENGTH = "; |
| _length->dump(os); |
| } |
| |
| void Memory::dump(raw_ostream &os) const { |
| os << "MEMORY\n{\n"; |
| for (auto &block : _blocks) { |
| block->dump(os); |
| os << "\n"; |
| } |
| os << "}\n"; |
| } |
| |
| // Extern functions |
| void Extern::dump(raw_ostream &os) const { |
| os << "EXTERN("; |
| for (unsigned i = 0, e = _symbols.size(); i != e; ++i) { |
| if (i) |
| os << " "; |
| os << _symbols[i]; |
| } |
| os << ")\n"; |
| } |
| |
| // Parser functions |
| std::error_code Parser::parse() { |
| // Get the first token. |
| _lex.lex(_tok); |
| // Parse top level commands. |
| while (true) { |
| switch (_tok._kind) { |
| case Token::eof: |
| return std::error_code(); |
| case Token::semicolon: |
| consumeToken(); |
| break; |
| case Token::kw_output: { |
| auto output = parseOutput(); |
| if (!output) |
| return LinkerScriptReaderError::parse_error; |
| _script._commands.push_back(output); |
| break; |
| } |
| case Token::kw_output_format: { |
| auto outputFormat = parseOutputFormat(); |
| if (!outputFormat) |
| return LinkerScriptReaderError::parse_error; |
| _script._commands.push_back(outputFormat); |
| break; |
| } |
| case Token::kw_output_arch: { |
| auto outputArch = parseOutputArch(); |
| if (!outputArch) |
| return LinkerScriptReaderError::parse_error; |
| _script._commands.push_back(outputArch); |
| break; |
| } |
| case Token::kw_input: { |
| Input *input = parsePathList<Input>(); |
| if (!input) |
| return LinkerScriptReaderError::parse_error; |
| _script._commands.push_back(input); |
| break; |
| } |
| case Token::kw_group: { |
| Group *group = parsePathList<Group>(); |
| if (!group) |
| return LinkerScriptReaderError::parse_error; |
| _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 LinkerScriptReaderError::parse_error; |
| case Token::kw_entry: { |
| Entry *entry = parseEntry(); |
| if (!entry) |
| return LinkerScriptReaderError::parse_error; |
| _script._commands.push_back(entry); |
| break; |
| } |
| case Token::kw_phdrs: { |
| PHDRS *phdrs = parsePHDRS(); |
| if (!phdrs) |
| return LinkerScriptReaderError::parse_error; |
| _script._commands.push_back(phdrs); |
| break; |
| } |
| case Token::kw_search_dir: { |
| SearchDir *searchDir = parseSearchDir(); |
| if (!searchDir) |
| return LinkerScriptReaderError::parse_error; |
| _script._commands.push_back(searchDir); |
| break; |
| } |
| case Token::kw_sections: { |
| Sections *sections = parseSections(); |
| if (!sections) |
| return LinkerScriptReaderError::parse_error; |
| _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 LinkerScriptReaderError::parse_error; |
| _script._commands.push_back(cmd); |
| break; |
| } |
| case Token::kw_memory: { |
| const Command *cmd = parseMemory(); |
| if (!cmd) |
| return LinkerScriptReaderError::parse_error; |
| _script._commands.push_back(cmd); |
| break; |
| } |
| case Token::kw_extern: { |
| const Command *cmd = parseExtern(); |
| if (!cmd) |
| return LinkerScriptReaderError::parse_error; |
| _script._commands.push_back(cmd); |
| break; |
| } |
| default: |
| // Unexpected. |
| error(_tok, "expected linker script command"); |
| return LinkerScriptReaderError::parse_error; |
| } |
| } |
| return LinkerScriptReaderError::parse_error; |
| } |
| |
| const Expression *Parser::parseFunctionCall() { |
| assert((_tok._kind == Token::identifier || _tok._kind == Token::kw_align) && |
| "expected function call first tokens"); |
| SmallVector<const Expression *, 8> 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(*this, _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(*this, 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(); |
| auto *sym = new (_alloc) Symbol(*this, _tok._range); |
| consumeToken(); |
| return sym; |
| } |
| case Token::kw_align: |
| return parseFunctionCall(); |
| case Token::minus: |
| consumeToken(); |
| return new (_alloc) Unary(*this, Unary::Minus, parseExprOperand()); |
| case Token::tilde: |
| consumeToken(); |
| return new (_alloc) Unary(*this, Unary::Not, parseExprOperand()); |
| case Token::number: { |
| auto val = parseNum(_tok._range); |
| if (val.getError()) { |
| error(_tok, "Unrecognized number constant"); |
| return nullptr; |
| } |
| auto *c = new (_alloc) Constant(*this, *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(*this, 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(*this, lhs, trueExpr, falseExpr); |
| } |
| |
| // Parse OUTPUT(ident) |
| Output *Parser::parseOutput() { |
| assert(_tok._kind == Token::kw_output && "Expected OUTPUT"); |
| consumeToken(); |
| if (!expectAndConsume(Token::l_paren, "expected (")) |
| return nullptr; |
| |
| if (_tok._kind != Token::identifier) { |
| error(_tok, "Expected identifier in OUTPUT."); |
| return nullptr; |
| } |
| |
| auto ret = new (_alloc) Output(*this, _tok._range); |
| consumeToken(); |
| |
| if (!expectAndConsume(Token::r_paren, "expected )")) |
| return nullptr; |
| |
| return ret; |
| } |
| |
| // 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; |
| } |
| |
| SmallVector<StringRef, 8> formats; |
| formats.push_back(_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; |
| } |
| formats.push_back(_tok._range); |
| consumeToken(); |
| } while (isNextToken(Token::comma)); |
| |
| if (!expectAndConsume(Token::r_paren, "expected )")) |
| return nullptr; |
| |
| return new (_alloc) OutputFormat(*this, formats); |
| } |
| |
| // 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(*this, _tok._range); |
| consumeToken(); |
| |
| if (!expectAndConsume(Token::r_paren, "expected )")) |
| return nullptr; |
| |
| return ret; |
| } |
| |
| // Parse file list for INPUT or GROUP |
| template<class T> T *Parser::parsePathList() { |
| consumeToken(); |
| if (!expectAndConsume(Token::l_paren, "expected (")) |
| return nullptr; |
| |
| SmallVector<Path, 8> 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."); |
| } |
| } |
| if (!expectAndConsume(Token::r_paren, "expected )")) |
| return nullptr; |
| return new (_alloc) T(*this, paths); |
| } |
| |
| // Parse AS_NEEDED(file ...) |
| bool Parser::parseAsNeeded(SmallVectorImpl<Path> &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(*this, 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(*this, 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::Default; |
| 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(*this, name, expr, kind, visibility); |
| } |
| |
| llvm::ErrorOr<InputSectionsCmd::VectorTy> 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<InputSectionsCmd::VectorTy>( |
| make_error_code(llvm::errc::io_error)); |
| |
| while (_tok._kind == Token::identifier) { |
| res.push_back(new (_alloc) InputSectionName(*this, _tok._range, true)); |
| consumeToken(); |
| } |
| |
| if (!expectAndConsume(Token::r_paren, "expected )")) |
| return llvm::ErrorOr<InputSectionsCmd::VectorTy>( |
| make_error_code(llvm::errc::io_error)); |
| return llvm::ErrorOr<InputSectionsCmd::VectorTy>(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; |
| |
| SmallVector<const InputSection *, 8> inputSections; |
| |
| while (_tok._kind == Token::identifier) { |
| inputSections.push_back(new (_alloc) |
| InputSectionName(*this, _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(*this, 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 memberName; |
| 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; |
| memberName = _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; |
| } |
| } |
| |
| SmallVector<const InputSection *, 8> inputSections; |
| |
| if (_tok._kind != Token::l_paren) |
| return new (_alloc) |
| InputSectionsCmd(*this, memberName, 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(*this, _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(*this, memberName, archiveName, keep, fileSortMode, |
| archiveSortMode, inputSections); |
| } |
| |
| const FillCmd *Parser::parseFillCmd() { |
| assert(_tok._kind == Token::kw_fill && "Expected FILL!"); |
| consumeToken(); |
| if (!expectAndConsume(Token::l_paren, "expected (")) |
| return nullptr; |
| |
| SmallVector<uint8_t, 8> storage; |
| |
| // If the expression is just a number, it's arbitrary length. |
| if (_tok._kind == Token::number && peek()._kind == Token::r_paren) { |
| if (_tok._range.size() > 2 && _tok._range.startswith("0x")) { |
| StringRef num = _tok._range.substr(2); |
| for (char c : num) { |
| unsigned nibble = llvm::hexDigitValue(c); |
| if (nibble == -1u) |
| goto not_simple_hex; |
| storage.push_back(nibble); |
| } |
| |
| if (storage.size() % 2 != 0) |
| storage.insert(storage.begin(), 0); |
| |
| // Collapse nibbles. |
| for (std::size_t i = 0, e = storage.size() / 2; i != e; ++i) |
| storage[i] = (storage[i * 2] << 4) + storage[(i * 2) + 1]; |
| |
| storage.resize(storage.size() / 2); |
| } |
| } |
| not_simple_hex: |
| |
| const Expression *expr = parseExpression(); |
| if (!expr) |
| return nullptr; |
| if (!expectAndConsume(Token::r_paren, "expected )")) |
| return nullptr; |
| |
| return new(getAllocator()) FillCmd(*this, storage); |
| } |
| |
| 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; |
| SmallVector<const Command *, 8> 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_fill: |
| if (const Command *cmd = parseFillCmd()) |
| outputSectionCommands.push_back(cmd); |
| else |
| return nullptr; |
| 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; |
| |
| SmallVector<StringRef, 2> phdrs; |
| while (_tok._kind == Token::colon) { |
| consumeToken(); |
| if (_tok._kind != Token::identifier) { |
| error(_tok, "expected program header name"); |
| return nullptr; |
| } |
| phdrs.push_back(_tok._range); |
| consumeToken(); |
| } |
| |
| 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( |
| *this, sectionName, address, align, subAlign, at, fillExpr, fillStream, |
| alignWithInput, discard, constraint, outputSectionCommands, phdrs); |
| } |
| |
| const Overlay *Parser::parseOverlay() { |
| assert(_tok._kind == Token::kw_overlay && "Expected OVERLAY!"); |
| error(_tok, "Overlay description is not yet supported."); |
| return nullptr; |
| } |
| |
| const PHDR *Parser::parsePHDR() { |
| assert(_tok._kind == Token::identifier && "Expected identifier!"); |
| |
| StringRef name = _tok._range; |
| consumeToken(); |
| |
| uint64_t type; |
| |
| switch (_tok._kind) { |
| case Token::identifier: |
| case Token::number: |
| case Token::l_paren: { |
| const Expression *expr = parseExpression(); |
| if (!expr) |
| return nullptr; |
| Expression::SymbolTableTy PHDRTypes; |
| #define PHDR_INSERT(x) PHDRTypes.insert(std::make_pair(#x, llvm::ELF::x)) |
| PHDR_INSERT(PT_NULL); |
| PHDR_INSERT(PT_LOAD); |
| PHDR_INSERT(PT_DYNAMIC); |
| PHDR_INSERT(PT_INTERP); |
| PHDR_INSERT(PT_NOTE); |
| PHDR_INSERT(PT_SHLIB); |
| PHDR_INSERT(PT_PHDR); |
| PHDR_INSERT(PT_TLS); |
| PHDR_INSERT(PT_LOOS); |
| PHDR_INSERT(PT_GNU_EH_FRAME); |
| PHDR_INSERT(PT_GNU_STACK); |
| PHDR_INSERT(PT_GNU_RELRO); |
| PHDR_INSERT(PT_SUNW_EH_FRAME); |
| PHDR_INSERT(PT_SUNW_UNWIND); |
| PHDR_INSERT(PT_HIOS); |
| PHDR_INSERT(PT_LOPROC); |
| PHDR_INSERT(PT_ARM_ARCHEXT); |
| PHDR_INSERT(PT_ARM_EXIDX); |
| PHDR_INSERT(PT_ARM_UNWIND); |
| PHDR_INSERT(PT_MIPS_REGINFO); |
| PHDR_INSERT(PT_MIPS_RTPROC); |
| PHDR_INSERT(PT_MIPS_OPTIONS); |
| PHDR_INSERT(PT_MIPS_ABIFLAGS); |
| PHDR_INSERT(PT_HIPROC); |
| #undef PHDR_INSERT |
| auto t = expr->evalExpr(PHDRTypes); |
| if (t == LinkerScriptReaderError::unknown_symbol_in_expr) { |
| error(_tok, "Unknown type"); |
| return nullptr; |
| } |
| if (!t) |
| return nullptr; |
| type = *t; |
| break; |
| } |
| default: |
| error(_tok, "expected identifier or expression"); |
| return nullptr; |
| } |
| |
| uint64_t flags = 0; |
| const Expression *flagsExpr = nullptr; |
| bool includeFileHdr = false; |
| bool includePHDRs = false; |
| |
| while (_tok._kind != Token::semicolon) { |
| switch (_tok._kind) { |
| case Token::kw_filehdr: |
| if (includeFileHdr) { |
| error(_tok, "Duplicate FILEHDR attribute"); |
| return nullptr; |
| } |
| includeFileHdr = true; |
| consumeToken(); |
| break; |
| case Token::kw_phdrs: |
| if (includePHDRs) { |
| error(_tok, "Duplicate PHDRS attribute"); |
| return nullptr; |
| } |
| includePHDRs = true; |
| consumeToken(); |
| break; |
| case Token::kw_flags: { |
| if (flagsExpr) { |
| error(_tok, "Duplicate FLAGS attribute"); |
| return nullptr; |
| } |
| consumeToken(); |
| if (!expectAndConsume(Token::l_paren, "Expected (")) |
| return nullptr; |
| flagsExpr = parseExpression(); |
| if (!flagsExpr) |
| return nullptr; |
| auto f = flagsExpr->evalExpr(); |
| if (!f) |
| return nullptr; |
| flags = *f; |
| if (!expectAndConsume(Token::r_paren, "Expected )")) |
| return nullptr; |
| } break; |
| default: |
| error(_tok, "Unexpected token"); |
| return nullptr; |
| } |
| } |
| |
| if (!expectAndConsume(Token::semicolon, "Expected ;")) |
| return nullptr; |
| |
| return new (getAllocator()) |
| PHDR(name, type, includeFileHdr, includePHDRs, nullptr, flags); |
| } |
| |
| PHDRS *Parser::parsePHDRS() { |
| assert(_tok._kind == Token::kw_phdrs && "Expected PHDRS!"); |
| consumeToken(); |
| if (!expectAndConsume(Token::l_brace, "expected {")) |
| return nullptr; |
| |
| SmallVector<const PHDR *, 8> phdrs; |
| |
| while (true) { |
| if (_tok._kind == Token::identifier) { |
| const PHDR *phdr = parsePHDR(); |
| if (!phdr) |
| return nullptr; |
| phdrs.push_back(phdr); |
| } else |
| break; |
| } |
| |
| if (!expectAndConsume(Token::r_brace, "expected }")) |
| return nullptr; |
| |
| return new (getAllocator()) PHDRS(*this, phdrs); |
| } |
| |
| Sections *Parser::parseSections() { |
| assert(_tok._kind == Token::kw_sections && "Expected SECTIONS!"); |
| consumeToken(); |
| if (!expectAndConsume(Token::l_brace, "expected {")) |
| return nullptr; |
| SmallVector<const Command *, 8> 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(*this, sectionsCommands); |
| } |
| |
| Memory *Parser::parseMemory() { |
| assert(_tok._kind == Token::kw_memory && "Expected MEMORY!"); |
| consumeToken(); |
| if (!expectAndConsume(Token::l_brace, "expected {")) |
| return nullptr; |
| SmallVector<const MemoryBlock *, 8> blocks; |
| |
| bool unrecognizedToken = false; |
| // Parse zero or more memory block descriptors. |
| while (!unrecognizedToken) { |
| if (_tok._kind == Token::identifier) { |
| StringRef name; |
| StringRef attrs; |
| const Expression *origin = nullptr; |
| const Expression *length = nullptr; |
| |
| name = _tok._range; |
| consumeToken(); |
| |
| // Parse optional memory region attributes. |
| if (_tok._kind == Token::l_paren) { |
| consumeToken(); |
| |
| if (_tok._kind != Token::identifier) { |
| error(_tok, "Expected memory attribute string."); |
| return nullptr; |
| } |
| attrs = _tok._range; |
| consumeToken(); |
| |
| if (!expectAndConsume(Token::r_paren, "expected )")) |
| return nullptr; |
| } |
| |
| if (!expectAndConsume(Token::colon, "expected :")) |
| return nullptr; |
| |
| // Parse the ORIGIN (base address of memory block). |
| if (!expectAndConsume(Token::kw_origin, "expected ORIGIN")) |
| return nullptr; |
| |
| if (!expectAndConsume(Token::equal, "expected =")) |
| return nullptr; |
| |
| origin = parseExpression(); |
| if (!origin) |
| return nullptr; |
| |
| if (!expectAndConsume(Token::comma, "expected ,")) |
| return nullptr; |
| |
| // Parse the LENGTH (length of memory block). |
| if (!expectAndConsume(Token::kw_length, "expected LENGTH")) |
| return nullptr; |
| |
| if (!expectAndConsume(Token::equal, "expected =")) |
| return nullptr; |
| |
| length = parseExpression(); |
| if (!length) |
| return nullptr; |
| |
| auto *block = new (_alloc) MemoryBlock(name, attrs, origin, length); |
| blocks.push_back(block); |
| } else { |
| unrecognizedToken = true; |
| } |
| } |
| if (!expectAndConsume( |
| Token::r_brace, |
| "expected memory block definition.")) |
| return nullptr; |
| |
| return new (_alloc) Memory(*this, blocks); |
| } |
| |
| Extern *Parser::parseExtern() { |
| assert(_tok._kind == Token::kw_extern && "Expected EXTERN!"); |
| consumeToken(); |
| if (!expectAndConsume(Token::l_paren, "expected (")) |
| return nullptr; |
| |
| // Parse one or more symbols. |
| SmallVector<StringRef, 8> symbols; |
| if (_tok._kind != Token::identifier) { |
| error(_tok, "expected one or more symbols in EXTERN."); |
| return nullptr; |
| } |
| symbols.push_back(_tok._range); |
| consumeToken(); |
| while (_tok._kind == Token::identifier) { |
| symbols.push_back(_tok._range); |
| consumeToken(); |
| } |
| |
| if (!expectAndConsume(Token::r_paren, "expected symbol in EXTERN.")) |
| return nullptr; |
| |
| return new (_alloc) Extern(*this, symbols); |
| } |
| |
| // Sema member functions |
| Sema::Sema() : _programPHDR(nullptr) {} |
| |
| std::error_code Sema::perform() { |
| llvm::StringMap<const PHDR *> phdrs; |
| |
| for (auto &parser : _scripts) { |
| for (const Command *c : parser->get()->_commands) { |
| if (const auto *sec = dyn_cast<Sections>(c)) { |
| linearizeAST(sec); |
| } else if (const auto *ph = dyn_cast<PHDRS>(c)) { |
| if (auto ec = collectPHDRs(ph, phdrs)) |
| return ec; |
| } |
| } |
| } |
| return buildSectionToPHDR(phdrs); |
| } |
| |
| bool Sema::less(const SectionKey &lhs, const SectionKey &rhs) const { |
| int a = getLayoutOrder(lhs, true); |
| int b = getLayoutOrder(rhs, true); |
| |
| if (a != b) { |
| if (a < 0) |
| return false; |
| if (b < 0) |
| return true; |
| return a < b; |
| } |
| |
| // If both sections are not mapped anywhere, they have the same order |
| if (a < 0) |
| return false; |
| |
| // If both sections fall into the same layout order, we need to find their |
| // relative position as written in the (InputSectionsCmd). |
| return localCompare(a, lhs, rhs); |
| } |
| |
| StringRef Sema::getOutputSection(const SectionKey &key) const { |
| int layoutOrder = getLayoutOrder(key, true); |
| if (layoutOrder < 0) |
| return StringRef(); |
| |
| for (int i = layoutOrder - 1; i >= 0; --i) { |
| if (!isa<OutputSectionDescription>(_layoutCommands[i])) |
| continue; |
| |
| const OutputSectionDescription *out = |
| dyn_cast<OutputSectionDescription>(_layoutCommands[i]); |
| return out->name(); |
| } |
| |
| return StringRef(); |
| } |
| |
| std::vector<const SymbolAssignment *> |
| Sema::getExprs(const SectionKey &key) { |
| int layoutOrder = getLayoutOrder(key, false); |
| auto ans = std::vector<const SymbolAssignment *>(); |
| |
| if (layoutOrder < 0 || _deliveredExprs.count(layoutOrder) > 0) |
| return ans; |
| |
| for (int i = layoutOrder - 1; i >= 0; --i) { |
| if (isa<InputSection>(_layoutCommands[i])) |
| break; |
| if (auto assgn = dyn_cast<SymbolAssignment>(_layoutCommands[i])) |
| ans.push_back(assgn); |
| } |
| |
| // Reverse this order so we evaluate the expressions in the original order |
| // of the linker script |
| std::reverse(ans.begin(), ans.end()); |
| |
| // Mark this layout number as delivered |
| _deliveredExprs.insert(layoutOrder); |
| return ans; |
| } |
| |
| std::error_code Sema::evalExpr(const SymbolAssignment *assgn, |
| uint64_t &curPos) { |
| _symbolTable[StringRef(".")] = curPos; |
| |
| auto ans = assgn->expr()->evalExpr(_symbolTable); |
| if (ans.getError()) |
| return ans.getError(); |
| uint64_t result = *ans; |
| |
| if (assgn->symbol() == ".") { |
| curPos = result; |
| return std::error_code(); |
| } |
| |
| _symbolTable[assgn->symbol()] = result; |
| return std::error_code(); |
| } |
| |
| const llvm::StringSet<> &Sema::getScriptDefinedSymbols() const { |
| // Do we have cached results? |
| if (!_definedSymbols.empty()) |
| return _definedSymbols; |
| |
| // Populate our defined set and return it |
| for (auto cmd : _layoutCommands) |
| if (auto sa = dyn_cast<SymbolAssignment>(cmd)) { |
| StringRef symbol = sa->symbol(); |
| if (!symbol.empty() && symbol != ".") |
| _definedSymbols.insert(symbol); |
| } |
| |
| return _definedSymbols; |
| } |
| |
| uint64_t Sema::getLinkerScriptExprValue(StringRef name) const { |
| auto it = _symbolTable.find(name); |
| assert (it != _symbolTable.end() && "Invalid symbol name!"); |
| return it->second; |
| } |
| |
| bool Sema::hasPHDRs() const { return !_sectionToPHDR.empty(); } |
| |
| std::vector<const PHDR *> Sema::getPHDRsForOutputSection(StringRef name) const { |
| auto vec = _sectionToPHDR.lookup(name); |
| return std::vector<const PHDR *>(std::begin(vec), std::end(vec)); |
| } |
| |
| const PHDR *Sema::getProgramPHDR() const { return _programPHDR; } |
| |
| void Sema::dump() const { |
| raw_ostream &os = llvm::outs(); |
| os << "Linker script semantics dump\n"; |
| int num = 0; |
| for (auto &parser : _scripts) { |
| os << "Dumping script #" << ++num << ":\n"; |
| parser->get()->dump(os); |
| os << "\n"; |
| } |
| os << "Dumping rule ids:\n"; |
| for (unsigned i = 0; i < _layoutCommands.size(); ++i) { |
| os << "LayoutOrder " << i << ":\n"; |
| _layoutCommands[i]->dump(os); |
| os << "\n\n"; |
| } |
| } |
| |
| /// Given a string "pattern" with wildcard characters, return true if it |
| /// matches "name". This function is useful when checking if a given name |
| /// pattern written in the linker script, i.e. ".text*", should match |
| /// ".text.anytext". |
| static bool wildcardMatch(StringRef pattern, StringRef name) { |
| auto i = name.begin(); |
| |
| // Check if each char in pattern also appears in our input name, handling |
| // special wildcard characters. |
| for (auto j = pattern.begin(), e = pattern.end(); j != e; ++j) { |
| if (i == name.end()) |
| return false; |
| |
| switch (*j) { |
| case '*': |
| while (!wildcardMatch(pattern.drop_front(j - pattern.begin() + 1), |
| name.drop_front(i - name.begin()))) { |
| if (i == name.end()) |
| return false; |
| ++i; |
| } |
| break; |
| case '?': |
| // Matches any character |
| ++i; |
| break; |
| case '[': { |
| // Matches a range of characters specified between brackets |
| size_t end = pattern.find(']', j - pattern.begin()); |
| if (end == pattern.size()) |
| return false; |
| |
| StringRef chars = pattern.slice(j - pattern.begin(), end); |
| if (chars.find(i) == StringRef::npos) |
| return false; |
| |
| j = pattern.begin() + end; |
| ++i; |
| break; |
| } |
| case '\\': |
| ++j; |
| if (*j != *i) |
| return false; |
| ++i; |
| break; |
| default: |
| // No wildcard character means we must match exactly the same char |
| if (*j != *i) |
| return false; |
| ++i; |
| break; |
| } |
| } |
| |
| // If our pattern has't consumed the entire string, it is not a match |
| return i == name.end(); |
| } |
| |
| int Sema::matchSectionName(int id, const SectionKey &key) const { |
| const InputSectionsCmd *cmd = dyn_cast<InputSectionsCmd>(_layoutCommands[id]); |
| |
| if (!cmd || !wildcardMatch(cmd->archiveName(), key.archivePath)) |
| return -1; |
| |
| while ((size_t)++id < _layoutCommands.size() && |
| (isa<InputSection>(_layoutCommands[id]))) { |
| if (isa<InputSectionSortedGroup>(_layoutCommands[id])) |
| continue; |
| |
| const InputSectionName *in = |
| dyn_cast<InputSectionName>(_layoutCommands[id]); |
| if (wildcardMatch(in->name(), key.sectionName)) |
| return id; |
| } |
| return -1; |
| } |
| |
| int Sema::getLayoutOrder(const SectionKey &key, bool coarse) const { |
| // First check if we already answered this layout question |
| if (coarse) { |
| auto entry = _cacheSectionOrder.find(key); |
| if (entry != _cacheSectionOrder.end()) |
| return entry->second; |
| } else { |
| auto entry = _cacheExpressionOrder.find(key); |
| if (entry != _cacheExpressionOrder.end()) |
| return entry->second; |
| } |
| |
| // Try to match exact file name |
| auto range = _memberToLayoutOrder.equal_range(key.memberPath); |
| for (auto I = range.first, E = range.second; I != E; ++I) { |
| int order = I->second; |
| int exprOrder = -1; |
| |
| if ((exprOrder = matchSectionName(order, key)) >= 0) { |
| if (coarse) { |
| _cacheSectionOrder.insert(std::make_pair(key, order)); |
| return order; |
| } |
| _cacheExpressionOrder.insert(std::make_pair(key, exprOrder)); |
| return exprOrder; |
| } |
| } |
| |
| // If we still couldn't find a rule for this input section, try to match |
| // wildcards |
| for (const auto &I : _memberNameWildcards) { |
| if (!wildcardMatch(I.first, key.memberPath)) |
| continue; |
| int order = I.second; |
| int exprOrder = -1; |
| |
| if ((exprOrder = matchSectionName(order, key)) >= 0) { |
| if (coarse) { |
| _cacheSectionOrder.insert(std::make_pair(key, order)); |
| return order; |
| } |
| _cacheExpressionOrder.insert(std::make_pair(key, exprOrder)); |
| return exprOrder; |
| } |
| } |
| |
| _cacheSectionOrder.insert(std::make_pair(key, -1)); |
| _cacheExpressionOrder.insert(std::make_pair(key, -1)); |
| return -1; |
| } |
| |
| static bool compareSortedNames(WildcardSortMode sortMode, StringRef lhs, |
| StringRef rhs) { |
| switch (sortMode) { |
| case WildcardSortMode::None: |
| case WildcardSortMode::NA: |
| return false; |
| case WildcardSortMode::ByAlignment: |
| case WildcardSortMode::ByInitPriority: |
| case WildcardSortMode::ByAlignmentAndName: |
| assert(false && "Unimplemented sort order"); |
| break; |
| case WildcardSortMode::ByName: |
| return lhs.compare(rhs) < 0; |
| case WildcardSortMode::ByNameAndAlignment: |
| int compare = lhs.compare(rhs); |
| if (compare != 0) |
| return compare < 0; |
| return compareSortedNames(WildcardSortMode::ByAlignment, lhs, rhs); |
| } |
| return false; |
| } |
| |
| static bool sortedGroupContains(const InputSectionSortedGroup *cmd, |
| const Sema::SectionKey &key) { |
| for (const InputSection *child : *cmd) { |
| if (auto i = dyn_cast<InputSectionName>(child)) { |
| if (wildcardMatch(i->name(), key.sectionName)) |
| return true; |
| continue; |
| } |
| |
| auto *sortedGroup = dyn_cast<InputSectionSortedGroup>(child); |
| assert(sortedGroup && "Expected InputSectionSortedGroup object"); |
| |
| if (sortedGroupContains(sortedGroup, key)) |
| return true; |
| } |
| |
| return false; |
| } |
| |
| bool Sema::localCompare(int order, const SectionKey &lhs, |
| const SectionKey &rhs) const { |
| const InputSectionsCmd *cmd = |
| dyn_cast<InputSectionsCmd>(_layoutCommands[order]); |
| |
| assert(cmd && "Invalid InputSectionsCmd index"); |
| |
| if (lhs.archivePath != rhs.archivePath) |
| return compareSortedNames(cmd->archiveSortMode(), lhs.archivePath, |
| rhs.archivePath); |
| |
| if (lhs.memberPath != rhs.memberPath) |
| return compareSortedNames(cmd->fileSortMode(), lhs.memberPath, |
| rhs.memberPath); |
| |
| // Both sections come from the same exact same file and rule. Start walking |
| // through input section names as written in the linker script and the |
| // first one to match will have higher priority. |
| for (const InputSection *inputSection : *cmd) { |
| if (auto i = dyn_cast<InputSectionName>(inputSection)) { |
| // If both match, return false (both have equal priority) |
| // If rhs match, return false (rhs has higher priority) |
| if (wildcardMatch(i->name(), rhs.sectionName)) |
| return false; |
| // If lhs matches first, it has priority over rhs |
| if (wildcardMatch(i->name(), lhs.sectionName)) |
| return true; |
| continue; |
| } |
| |
| // Handle sorted subgroups specially |
| auto *sortedGroup = dyn_cast<InputSectionSortedGroup>(inputSection); |
| assert(sortedGroup && "Expected InputSectionSortedGroup object"); |
| |
| bool a = sortedGroupContains(sortedGroup, lhs); |
| bool b = sortedGroupContains(sortedGroup, rhs); |
| if (a && !b) |
| return false; |
| if (b && !a) |
| return true; |
| if (!a && !a) |
| continue; |
| |
| return compareSortedNames(sortedGroup->sortMode(), lhs.sectionName, |
| rhs.sectionName); |
| } |
| |
| llvm_unreachable(""); |
| return false; |
| } |
| |
| std::error_code Sema::collectPHDRs(const PHDRS *ph, |
| llvm::StringMap<const PHDR *> &phdrs) { |
| bool loadFound = false; |
| for (auto *p : *ph) { |
| phdrs[p->name()] = p; |
| |
| switch (p->type()) { |
| case llvm::ELF::PT_PHDR: |
| if (_programPHDR != nullptr) |
| return LinkerScriptReaderError::extra_program_phdr; |
| if (loadFound) |
| return LinkerScriptReaderError::misplaced_program_phdr; |
| if (!p->hasPHDRs()) |
| return LinkerScriptReaderError::program_phdr_wrong_phdrs; |
| _programPHDR = p; |
| break; |
| case llvm::ELF::PT_LOAD: |
| // Program header, if available, should have program header table |
| // mapped in the first loadable segment. |
| if (!loadFound && _programPHDR && !p->hasPHDRs()) |
| return LinkerScriptReaderError::program_phdr_wrong_phdrs; |
| loadFound = true; |
| break; |
| } |
| } |
| return std::error_code(); |
| } |
| |
| std::error_code Sema::buildSectionToPHDR(llvm::StringMap<const PHDR *> &phdrs) { |
| const bool noPhdrs = phdrs.empty(); |
| |
| // Add NONE header to the map provided there's no user-defined |
| // header with the same name. |
| if (!phdrs.count(PHDR_NONE.name())) |
| phdrs[PHDR_NONE.name()] = &PHDR_NONE; |
| |
|