From 5a5c549fe9a3fef595297bd21d36bed8409dc37d Mon Sep 17 00:00:00 2001 From: Dimitry Andric Date: Wed, 30 Dec 2015 11:57:38 +0000 Subject: Vendor import of lld trunk r256633: https://llvm.org/svn/llvm-project/lld/trunk@256633 --- lib/ReaderWriter/LinkerScript.cpp | 411 ++++++++++++++++++++++++++++++++++---- 1 file changed, 371 insertions(+), 40 deletions(-) (limited to 'lib/ReaderWriter/LinkerScript.cpp') diff --git a/lib/ReaderWriter/LinkerScript.cpp b/lib/ReaderWriter/LinkerScript.cpp index 56194cae5e724..67822dc48fe60 100644 --- a/lib/ReaderWriter/LinkerScript.cpp +++ b/lib/ReaderWriter/LinkerScript.cpp @@ -1,4 +1,4 @@ -//===- ReaderWriter/LinkerScript.cpp --------------------------------------===// +//===- ReaderWriter/LinkerScript.cpp ----------------------------*- C++ -*-===// // // The LLVM Linker // @@ -14,6 +14,11 @@ #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 { @@ -63,6 +68,9 @@ void Token::dump(raw_ostream &os) const { 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) @@ -70,6 +78,7 @@ void Token::dump(raw_ostream &os) const { CASE(kw_length) CASE(kw_memory) CASE(kw_origin) + CASE(kw_phdrs) CASE(kw_provide) CASE(kw_provide_hidden) CASE(kw_only_if_ro) @@ -100,7 +109,7 @@ static llvm::ErrorOr parseDecimal(StringRef str) { for (auto &c : str) { res *= 10; if (c < '0' || c > '9') - return llvm::ErrorOr(std::make_error_code(std::errc::io_error)); + return llvm::ErrorOr(make_error_code(llvm::errc::io_error)); res += c - '0'; } return res; @@ -111,7 +120,7 @@ static llvm::ErrorOr parseOctal(StringRef str) { for (auto &c : str) { res <<= 3; if (c < '0' || c > '7') - return llvm::ErrorOr(std::make_error_code(std::errc::io_error)); + return llvm::ErrorOr(make_error_code(llvm::errc::io_error)); res += c - '0'; } return res; @@ -122,7 +131,7 @@ static llvm::ErrorOr parseBinary(StringRef str) { for (auto &c : str) { res <<= 1; if (c != '0' && c != '1') - return llvm::ErrorOr(std::make_error_code(std::errc::io_error)); + return llvm::ErrorOr(make_error_code(llvm::errc::io_error)); res += c - '0'; } return res; @@ -139,7 +148,7 @@ static llvm::ErrorOr parseHex(StringRef str) { else if (c >= 'A' && c <= 'F') res += c - 'A' + 10; else - return llvm::ErrorOr(std::make_error_code(std::errc::io_error)); + return llvm::ErrorOr(make_error_code(llvm::errc::io_error)); } return res; } @@ -469,6 +478,9 @@ void Lexer::lex(Token &tok) { .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) @@ -486,6 +498,7 @@ void Lexer::lex(Token &tok) { .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) @@ -544,14 +557,14 @@ void Lexer::skipWhitespace() { // Constant functions void Constant::dump(raw_ostream &os) const { os << _num; } -ErrorOr Constant::evalExpr(SymbolTableTy &symbolTable) const { +ErrorOr Constant::evalExpr(const SymbolTableTy &symbolTable) const { return _num; } // Symbol functions void Symbol::dump(raw_ostream &os) const { os << _name; } -ErrorOr Symbol::evalExpr(SymbolTableTy &symbolTable) const { +ErrorOr Symbol::evalExpr(const SymbolTableTy &symbolTable) const { auto it = symbolTable.find(_name); if (it == symbolTable.end()) return LinkerScriptReaderError::unknown_symbol_in_expr; @@ -569,7 +582,8 @@ void FunctionCall::dump(raw_ostream &os) const { os << ")"; } -ErrorOr FunctionCall::evalExpr(SymbolTableTy &symbolTable) const { +ErrorOr +FunctionCall::evalExpr(const SymbolTableTy &symbolTable) const { return LinkerScriptReaderError::unrecognized_function_in_expr; } @@ -584,7 +598,7 @@ void Unary::dump(raw_ostream &os) const { os << ")"; } -ErrorOr Unary::evalExpr(SymbolTableTy &symbolTable) const { +ErrorOr Unary::evalExpr(const SymbolTableTy &symbolTable) const { auto child = _child->evalExpr(symbolTable); if (child.getError()) return child.getError(); @@ -654,7 +668,7 @@ void BinOp::dump(raw_ostream &os) const { os << ")"; } -ErrorOr BinOp::evalExpr(SymbolTableTy &symbolTable) const { +ErrorOr BinOp::evalExpr(const SymbolTableTy &symbolTable) const { auto lhs = _lhs->evalExpr(symbolTable); if (lhs.getError()) return lhs.getError(); @@ -695,7 +709,7 @@ void TernaryConditional::dump(raw_ostream &os) const { } ErrorOr -TernaryConditional::evalExpr(SymbolTableTy &symbolTable) const { +TernaryConditional::evalExpr(const SymbolTableTy &symbolTable) const { auto conditional = _conditional->evalExpr(symbolTable); if (conditional.getError()) return conditional.getError(); @@ -857,6 +871,12 @@ void InputSectionsCmd::dump(raw_ostream &os) const { 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) @@ -909,6 +929,9 @@ void OutputSectionDescription::dump(raw_ostream &os) const { } os << " }"; + for (auto && phdr : _phdrs) + os << " : " << phdr; + if (_fillStream.size() > 0) { os << " ="; dumpByteStream(os, _fillStream); @@ -918,6 +941,37 @@ void OutputSectionDescription::dump(raw_ostream &os) const { } } +// 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"; @@ -965,7 +1019,6 @@ void Extern::dump(raw_ostream &os) const { os << ")\n"; } - // Parser functions std::error_code Parser::parse() { // Get the first token. @@ -1024,6 +1077,13 @@ std::error_code Parser::parse() { _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) @@ -1122,7 +1182,7 @@ const Expression *Parser::parseExprOperand() { case Token::identifier: { if (peek()._kind== Token::l_paren) return parseFunctionCall(); - Symbol *sym = new (_alloc) Symbol(*this, _tok._range); + auto *sym = new (_alloc) Symbol(*this, _tok._range); consumeToken(); return sym; } @@ -1140,7 +1200,7 @@ const Expression *Parser::parseExprOperand() { error(_tok, "Unrecognized number constant"); return nullptr; } - Constant *c = new (_alloc) Constant(*this, *val); + auto *c = new (_alloc) Constant(*this, *val); consumeToken(); return c; } @@ -1588,7 +1648,7 @@ llvm::ErrorOr Parser::parseExcludeFile() { if (!expectAndConsume(Token::l_paren, "expected (")) return llvm::ErrorOr( - std::make_error_code(std::errc::io_error)); + make_error_code(llvm::errc::io_error)); while (_tok._kind == Token::identifier) { res.push_back(new (_alloc) InputSectionName(*this, _tok._range, true)); @@ -1597,7 +1657,7 @@ llvm::ErrorOr Parser::parseExcludeFile() { if (!expectAndConsume(Token::r_paren, "expected )")) return llvm::ErrorOr( - std::make_error_code(std::errc::io_error)); + make_error_code(llvm::errc::io_error)); return llvm::ErrorOr(std::move(res)); } @@ -1793,6 +1853,46 @@ const InputSectionsCmd *Parser::parseInputSectionsCmd() { archiveSortMode, inputSections); } +const FillCmd *Parser::parseFillCmd() { + assert(_tok._kind == Token::kw_fill && "Expected FILL!"); + consumeToken(); + if (!expectAndConsume(Token::l_paren, "expected (")) + return nullptr; + + SmallVector 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!"); @@ -1892,6 +1992,12 @@ const OutputSectionDescription *Parser::parseOutputSectionDescription() { 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: @@ -1921,6 +2027,17 @@ const OutputSectionDescription *Parser::parseOutputSectionDescription() { if (!expectAndConsume(Token::r_brace, "expected }")) return nullptr; + SmallVector 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")) { @@ -1945,7 +2062,7 @@ const OutputSectionDescription *Parser::parseOutputSectionDescription() { return new (_alloc) OutputSectionDescription( *this, sectionName, address, align, subAlign, at, fillExpr, fillStream, - alignWithInput, discard, constraint, outputSectionCommands); + alignWithInput, discard, constraint, outputSectionCommands, phdrs); } const Overlay *Parser::parseOverlay() { @@ -1954,6 +2071,141 @@ const Overlay *Parser::parseOverlay() { 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 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(); @@ -2101,8 +2353,7 @@ Memory *Parser::parseMemory() { if (!length) return nullptr; - MemoryBlock *block = - new (_alloc) MemoryBlock(name, attrs, origin, length); + auto *block = new (_alloc) MemoryBlock(name, attrs, origin, length); blocks.push_back(block); } else { unrecognizedToken = true; @@ -2142,14 +2393,22 @@ Extern *Parser::parseExtern() { } // Sema member functions -Sema::Sema() - : _scripts(), _layoutCommands(), _memberToLayoutOrder(), - _memberNameWildcards(), _cacheSectionOrder(), _cacheExpressionOrder(), - _deliveredExprs(), _symbolTable() {} +Sema::Sema() : _programPHDR(nullptr) {} + +std::error_code Sema::perform() { + llvm::StringMap phdrs; -void Sema::perform() { - for (auto &parser : _scripts) - perform(parser->get()); + for (auto &parser : _scripts) { + for (const Command *c : parser->get()->_commands) { + if (const auto *sec = dyn_cast(c)) { + linearizeAST(sec); + } else if (const auto *ph = dyn_cast(c)) { + if (auto ec = collectPHDRs(ph, phdrs)) + return ec; + } + } + } + return buildSectionToPHDR(phdrs); } bool Sema::less(const SectionKey &lhs, const SectionKey &rhs) const { @@ -2254,6 +2513,15 @@ uint64_t Sema::getLinkerScriptExprValue(StringRef name) const { return it->second; } +bool Sema::hasPHDRs() const { return !_sectionToPHDR.empty(); } + +std::vector Sema::getPHDRsForOutputSection(StringRef name) const { + auto vec = _sectionToPHDR.lookup(name); + return std::vector(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"; @@ -2287,7 +2555,7 @@ static bool wildcardMatch(StringRef pattern, StringRef name) { switch (*j) { case '*': while (!wildcardMatch(pattern.drop_front(j - pattern.begin() + 1), - name.drop_front(i - name.begin() + 1))) { + name.drop_front(i - name.begin()))) { if (i == name.end()) return false; ++i; @@ -2295,6 +2563,7 @@ static bool wildcardMatch(StringRef pattern, StringRef name) { break; case '?': // Matches any character + ++i; break; case '[': { // Matches a range of characters specified between brackets @@ -2307,20 +2576,22 @@ static bool wildcardMatch(StringRef pattern, StringRef name) { 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; } - ++i; } // If our pattern has't consumed the entire string, it is not a match @@ -2376,11 +2647,10 @@ int Sema::getLayoutOrder(const SectionKey &key, bool coarse) const { // 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)) + for (const auto &I : _memberNameWildcards) { + if (!wildcardMatch(I.first, key.memberPath)) continue; - int order = I->second; + int order = I.second; int exprOrder = -1; if ((exprOrder = matchSectionName(order, key)) >= 0) { @@ -2490,6 +2760,74 @@ bool Sema::localCompare(int order, const SectionKey &lhs, return false; } +std::error_code Sema::collectPHDRs(const PHDRS *ph, + llvm::StringMap &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 &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; + + // Match output sections to available headers. + llvm::SmallVector phdrsCur, phdrsLast { &PHDR_NONE }; + for (const Command *cmd : _layoutCommands) { + auto osd = dyn_cast(cmd); + if (!osd || osd->isDiscarded()) + continue; + + phdrsCur.clear(); + for (StringRef name : osd->PHDRs()) { + auto it = phdrs.find(name); + if (it == phdrs.end()) { + return LinkerScriptReaderError::unknown_phdr_ids; + } + phdrsCur.push_back(it->second); + } + + // If no headers and no errors - insert empty headers set. + // If the current set of headers is empty, then use the last non-empty + // set. Otherwise mark the current set to be the last non-empty set for + // successors. + if (noPhdrs) + phdrsCur.clear(); + else if (phdrsCur.empty()) + phdrsCur = phdrsLast; + else + phdrsLast = phdrsCur; + + _sectionToPHDR[osd->name()] = phdrsCur; + } + return std::error_code(); +} + static bool hasWildcard(StringRef name) { for (auto ch : name) if (ch == '*' || ch == '?' || ch == '[' || ch == '\\') @@ -2553,12 +2891,5 @@ void Sema::linearizeAST(const Sections *sections) { } } -void Sema::perform(const LinkerScript *ls) { - for (const Command *c : ls->_commands) { - if (const Sections *sec = dyn_cast(c)) - linearizeAST(sec); - } -} - -} // End namespace script +} // end namespace script } // end namespace lld -- cgit v1.2.3