diff options
Diffstat (limited to 'lib/ReaderWriter/LinkerScript.cpp')
-rw-r--r-- | lib/ReaderWriter/LinkerScript.cpp | 2564 |
1 files changed, 2564 insertions, 0 deletions
diff --git a/lib/ReaderWriter/LinkerScript.cpp b/lib/ReaderWriter/LinkerScript.cpp new file mode 100644 index 000000000000..56194cae5e72 --- /dev/null +++ b/lib/ReaderWriter/LinkerScript.cpp @@ -0,0 +1,2564 @@ +//===- 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_extern) + CASE(kw_group) + CASE(kw_hidden) + CASE(kw_input) + CASE(kw_keep) + CASE(kw_length) + CASE(kw_memory) + CASE(kw_origin) + 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>(std::make_error_code(std::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>(std::make_error_code(std::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>(std::make_error_code(std::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>(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<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("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("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(SymbolTableTy &symbolTable) const { + return _num; +} + +// Symbol functions +void Symbol::dump(raw_ostream &os) const { os << _name; } + +ErrorOr<int64_t> Symbol::evalExpr(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(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(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(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(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 << ")"; +} + +// 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"; +} + +// 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_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(); + Symbol *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; + } + Constant *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>( + std::make_error_code(std::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>( + std::make_error_code(std::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 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_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( + *this, 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; + 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; + + MemoryBlock *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() + : _scripts(), _layoutCommands(), _memberToLayoutOrder(), + _memberNameWildcards(), _cacheSectionOrder(), _cacheExpressionOrder(), + _deliveredExprs(), _symbolTable() {} + +void Sema::perform() { + for (auto &parser : _scripts) + perform(parser->get()); +} + +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; +} + +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() + 1))) { + if (i == name.end()) + return false; + ++i; + } + break; + case '?': + // Matches any character + 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; + break; + } + case '\\': + ++j; + if (*j != *i) + return false; + break; + default: + // No wildcard character means we must match exactly the same char + if (*j != *i) + return false; + break; + } + ++i; + } + + // 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 (auto I = _memberNameWildcards.begin(), E = _memberNameWildcards.end(); + I != E; ++I) { + 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; +} + +static bool hasWildcard(StringRef name) { + for (auto ch : name) + if (ch == '*' || ch == '?' || ch == '[' || ch == '\\') + return true; + return false; +} + +void Sema::linearizeAST(const InputSection *inputSection) { + if (isa<InputSectionName>(inputSection)) { + _layoutCommands.push_back(inputSection); + return; + } + + auto *sortedGroup = dyn_cast<InputSectionSortedGroup>(inputSection); + assert(sortedGroup && "Expected InputSectionSortedGroup object"); + + for (const InputSection *child : *sortedGroup) { + linearizeAST(child); + } +} + +void Sema::linearizeAST(const InputSectionsCmd *inputSections) { + StringRef memberName = inputSections->memberName(); + // Populate our maps for fast lookup of InputSectionsCmd + if (hasWildcard(memberName)) + _memberNameWildcards.push_back( + std::make_pair(memberName, (int)_layoutCommands.size())); + else if (!memberName.empty()) + _memberToLayoutOrder.insert( + std::make_pair(memberName.str(), (int)_layoutCommands.size())); + + _layoutCommands.push_back(inputSections); + for (const InputSection *inputSection : *inputSections) + linearizeAST(inputSection); +} + +void Sema::linearizeAST(const Sections *sections) { + for (const Command *sectionCommand : *sections) { + if (isa<SymbolAssignment>(sectionCommand)) { + _layoutCommands.push_back(sectionCommand); + continue; + } + + if (!isa<OutputSectionDescription>(sectionCommand)) + continue; + + _layoutCommands.push_back(sectionCommand); + auto *outSection = dyn_cast<OutputSectionDescription>(sectionCommand); + + for (const Command *outSecCommand : *outSection) { + if (isa<SymbolAssignment>(outSecCommand)) { + _layoutCommands.push_back(outSecCommand); + continue; + } + + if (!isa<InputSectionsCmd>(outSecCommand)) + continue; + + linearizeAST(dyn_cast<InputSectionsCmd>(outSecCommand)); + } + } +} + +void Sema::perform(const LinkerScript *ls) { + for (const Command *c : ls->_commands) { + if (const Sections *sec = dyn_cast<Sections>(c)) + linearizeAST(sec); + } +} + +} // End namespace script +} // end namespace lld |