Index: lld/trunk/include/lld/Core/Error.h =================================================================== --- lld/trunk/include/lld/Core/Error.h +++ lld/trunk/include/lld/Core/Error.h @@ -38,6 +38,9 @@ unknown_symbol_in_expr, unrecognized_function_in_expr, unknown_phdr_ids, + extra_program_phdr, + misplaced_program_phdr, + program_phdr_wrong_phdrs, }; inline std::error_code make_error_code(LinkerScriptReaderError e) { Index: lld/trunk/include/lld/ReaderWriter/LinkerScript.h =================================================================== --- lld/trunk/include/lld/ReaderWriter/LinkerScript.h +++ lld/trunk/include/lld/ReaderWriter/LinkerScript.h @@ -79,7 +79,9 @@ kw_entry, kw_exclude_file, kw_extern, + kw_filehdr, kw_fill, + kw_flags, kw_group, kw_hidden, kw_input, @@ -818,6 +820,8 @@ StringRef name() const { return _name; } uint64_t type() const { return _type; } + bool hasFileHdr() const { return _includeFileHdr; } + bool hasPHDRs() const { return _includePHDRs; } uint64_t flags() const { return _flags; } bool isNone() const; @@ -1284,7 +1288,7 @@ /// Prepare our data structures according to the linker scripts currently in /// our control (control given via addLinkerScript()). Called once all linker /// scripts have been parsed. - void perform(); + std::error_code perform(); /// Answer if we have layout commands (section mapping rules). If we don't, /// the output file writer can assume there is no linker script special rule @@ -1326,12 +1330,14 @@ /// has been performed (by calling evalExpr() for all expressions). uint64_t getLinkerScriptExprValue(StringRef name) const; + /// Check if there are custom headers available. + bool hasPHDRs() const; + /// Retrieve all the headers the given output section is assigned to. - /// Error is returned if the output section is assigned to headers with - /// missing declarations. - std::error_code - getPHDRsForOutputSection(StringRef name, - std::vector &phdrs) const; + std::vector getPHDRsForOutputSection(StringRef name) const; + + /// Retrieve program header if available. + const PHDR *getProgramPHDR() const; void dump() const; @@ -1374,9 +1380,14 @@ bool localCompare(int order, const SectionKey &lhs, const SectionKey &rhs) const; + /// Convert the PHDRS command into map of names to headers. + /// Determine program header during processing. + std::error_code collectPHDRs(const PHDRS *ph, + llvm::StringMap &phdrs); + /// Build map that matches output section names to segments they should be /// put into. - std::error_code buildSectionToPHDR(); + std::error_code buildSectionToPHDR(llvm::StringMap &phdrs); /// Our goal with all linearizeAST overloaded functions is to /// traverse the linker script AST while putting nodes in a vector and @@ -1433,8 +1444,6 @@ void linearizeAST(const InputSectionsCmd *inputSections); void linearizeAST(const InputSection *inputSection); - void perform(const LinkerScript *ls); - std::vector> _scripts; std::vector _layoutCommands; std::unordered_multimap _memberToLayoutOrder; @@ -1444,8 +1453,8 @@ llvm::DenseSet _deliveredExprs; mutable llvm::StringSet<> _definedSymbols; - bool _parsedPHDRS; llvm::StringMap> _sectionToPHDR; + const PHDR *_programPHDR; Expression::SymbolTableTy _symbolTable; }; Index: lld/trunk/lib/Core/Error.cpp =================================================================== --- lld/trunk/lib/Core/Error.cpp +++ lld/trunk/lib/Core/Error.cpp @@ -58,6 +58,12 @@ "expression"; case LinkerScriptReaderError::unknown_phdr_ids: return "Unknown header identifiers (missing in PHDRS command) are used"; + case LinkerScriptReaderError::extra_program_phdr: + return "Extra program header is found"; + case LinkerScriptReaderError::misplaced_program_phdr: + return "Program header must precede load segments"; + case LinkerScriptReaderError::program_phdr_wrong_phdrs: + return "Program header has invalid PHDRS attribute"; } llvm_unreachable("An enumerator of LinkerScriptReaderError does not have a " "message defined."); Index: lld/trunk/lib/Driver/GnuLdDriver.cpp =================================================================== --- lld/trunk/lib/Driver/GnuLdDriver.cpp +++ lld/trunk/lib/Driver/GnuLdDriver.cpp @@ -748,7 +748,10 @@ return false; // Perform linker script semantic actions - ctx->linkerScriptSema().perform(); + if (auto ec = ctx->linkerScriptSema().perform()) { + diag << "Error in the linker script's semantics: " << ec.message() << "\n"; + return false; + } context.swap(ctx); return true; Index: lld/trunk/lib/ReaderWriter/ELF/SegmentChunks.cpp =================================================================== --- lld/trunk/lib/ReaderWriter/ELF/SegmentChunks.cpp +++ lld/trunk/lib/ReaderWriter/ELF/SegmentChunks.cpp @@ -423,7 +423,7 @@ } template void Segment::setSegmentFlags(uint64_t flags) { - assert(!_segmentFlags && !_flags && "Flags has already been set"); + assert(!_segmentFlags && "Segment flags have already been set"); _segmentFlags = true; _flags = flags; _atomflags = toAtomPermsSegment(flags); Index: lld/trunk/lib/ReaderWriter/ELF/TargetLayout.cpp =================================================================== --- lld/trunk/lib/ReaderWriter/ELF/TargetLayout.cpp +++ lld/trunk/lib/ReaderWriter/ELF/TargetLayout.cpp @@ -330,12 +330,7 @@ template std::vector TargetLayout::getCustomSegments(const OutputSection *sec) const { - std::vector phdrs; - if (_linkerScriptSema.getPHDRsForOutputSection(sec->name(), phdrs)) { - llvm::report_fatal_error( - "Linker script has wrong segments set for output sections"); - } - return phdrs; + return _linkerScriptSema.getPHDRsForOutputSection(sec->name()); } template @@ -466,11 +461,36 @@ } } } - if (_ctx.isDynamic() && !_ctx.isDynamicLibrary()) { + + // Default values if no linker script is available + bool hasProgramSegment = _ctx.isDynamic() && !_ctx.isDynamicLibrary(); + bool hasElfHeader = true; + bool hasProgramHeader = true; + uint64_t segmentFlags = 0; + + // Check if linker script has PHDRS and program segment defined + if (_linkerScriptSema.hasPHDRs()) { + if (auto p = _linkerScriptSema.getProgramPHDR()) { + hasProgramSegment = true; + hasElfHeader = p->hasFileHdr(); + hasProgramHeader = p->hasPHDRs(); + segmentFlags = p->flags(); + } else { + hasProgramSegment = false; + hasElfHeader = false; + hasProgramHeader = false; + } + } + + if (hasProgramSegment) { Segment *segment = new (_allocator) ProgramHeaderSegment(_ctx); _segments.push_back(segment); - segment->append(_elfHeader); - segment->append(_programHeader); + if (segmentFlags) + segment->setSegmentFlags(segmentFlags); + if (hasElfHeader) + segment->append(_elfHeader); + if (hasProgramHeader) + segment->append(_programHeader); } } Index: lld/trunk/lib/ReaderWriter/LinkerScript.cpp =================================================================== --- lld/trunk/lib/ReaderWriter/LinkerScript.cpp +++ lld/trunk/lib/ReaderWriter/LinkerScript.cpp @@ -68,7 +68,9 @@ 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) @@ -476,7 +478,9 @@ .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) @@ -2126,26 +2130,57 @@ } uint64_t flags = 0; + const Expression *flagsExpr = nullptr; + bool includeFileHdr = false; + bool includePHDRs = false; - if (_tok._kind == Token::identifier && _tok._range == "FLAGS") { - consumeToken(); - if (!expectAndConsume(Token::l_paren, "Expected (")) - return nullptr; - const Expression *flagsExpr = parseExpression(); - if (!flagsExpr) - return nullptr; - auto f = flagsExpr->evalExpr(); - if (!f) - return nullptr; - flags = *f; - if (!expectAndConsume(Token::r_paren, "Expected )")) + 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, false, false, nullptr, flags); + return new (getAllocator()) + PHDR(name, type, includeFileHdr, includePHDRs, nullptr, flags); } PHDRS *Parser::parsePHDRS() { @@ -2360,11 +2395,22 @@ } // Sema member functions -Sema::Sema() : _parsedPHDRS(false) {} +Sema::Sema() : _programPHDR(nullptr) {} -void Sema::perform() { - for (auto &parser : _scripts) - perform(parser->get()); +std::error_code Sema::perform() { + llvm::StringMap phdrs; + + 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 { @@ -2469,18 +2515,15 @@ return it->second; } -std::error_code -Sema::getPHDRsForOutputSection(StringRef name, - std::vector &phdrs) const { - // Cache results if not done yet. - if (auto ec = const_cast(this)->buildSectionToPHDR()) - return ec; +bool Sema::hasPHDRs() const { return !_sectionToPHDR.empty(); } +std::vector Sema::getPHDRsForOutputSection(StringRef name) const { auto vec = _sectionToPHDR.lookup(name); - std::copy(std::begin(vec), std::end(vec), std::back_inserter(phdrs)); - return std::error_code(); + 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"; @@ -2717,25 +2760,35 @@ return false; } -std::error_code Sema::buildSectionToPHDR() { - if (_parsedPHDRS) - return std::error_code(); - _parsedPHDRS = true; - - // No scripts - nothing to do. - if (_scripts.empty() || _layoutCommands.empty()) - return std::error_code(); - - // Collect all header declarations. - llvm::StringMap phdrs; - for (auto &parser : _scripts) { - for (auto *cmd : parser->get()->_commands) { - if (auto *ph = dyn_cast(cmd)) { - for (auto *p : *ph) - phdrs[p->name()] = p; - } +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 @@ -2838,12 +2891,5 @@ } } -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 lld Index: lld/trunk/test/elf/linkerscript/phdrs-extra-program.test =================================================================== --- lld/trunk/test/elf/linkerscript/phdrs-extra-program.test +++ lld/trunk/test/elf/linkerscript/phdrs-extra-program.test @@ -0,0 +1,27 @@ +/* +Test extra program header generates error. + +RUN: yaml2obj -format=elf %p/Inputs/simple.o.yaml -o=%t.o + +RUN: not lld -flavor gnu -target x86_64 -T %s %t.o -static -o %t1 &> %t1-error +RUN: FileCheck -check-prefix EXTRA-PROGRAM-PHDR %s < %t1-error +*/ + +ENTRY(_start) + +PHDRS +{ + header PT_PHDR PHDRS; + header2 PT_PHDR PHDRS; + text PT_LOAD; +} + +SECTIONS +{ + .text : { *(.text) } :NONE + .data : { *(.data) } +} + +/* +EXTRA-PROGRAM-PHDR: Extra program header is found +*/ Index: lld/trunk/test/elf/linkerscript/phdrs-has-program.test =================================================================== --- lld/trunk/test/elf/linkerscript/phdrs-has-program.test +++ lld/trunk/test/elf/linkerscript/phdrs-has-program.test @@ -0,0 +1,33 @@ +/* +Test when program segment is set it's generated. + +RUN: yaml2obj -format=elf %p/Inputs/simple.o.yaml -o=%t.o + +RUN: lld -flavor gnu -target x86_64 -T %s %t.o -static -o %t1 +RUN: llvm-readobj -program-headers %t1 | FileCheck -check-prefix PROGRAM-PHDR %s +*/ + +ENTRY(_start) + +PHDRS +{ + header PT_PHDR FILEHDR PHDRS; + text PT_LOAD PHDRS; +} + +SECTIONS +{ + .text : { *(.text) } :text + .data : { *(.data) } +} + +/* +PROGRAM-PHDR: ProgramHeader { +PROGRAM-PHDR: Type: PT_PHDR (0x6) +PROGRAM-PHDR: VirtualAddress: 0x400040 +PROGRAM-PHDR: Flags [ (0x5) +PROGRAM-PHDR: PF_R (0x4) +PROGRAM-PHDR: PF_X (0x1) +PROGRAM-PHDR: ] +PROGRAM-PHDR: } +*/ Index: lld/trunk/test/elf/linkerscript/phdrs-invalid.test =================================================================== --- lld/trunk/test/elf/linkerscript/phdrs-invalid.test +++ lld/trunk/test/elf/linkerscript/phdrs-invalid.test @@ -41,7 +41,7 @@ RUN: not lld -flavor gnu -target x86_64 -T %p/phdrs/undef-no-phdrs.script %t.o -static -o %t1 &> %t1-error RUN: FileCheck -check-prefix UNDEF-NO-PHDRS %s < %t1-error -UNDEF-NO-PHDRS: Linker script has wrong segments set for output sections +UNDEF-NO-PHDRS: Unknown header identifiers (missing in PHDRS command) are used */ /* @@ -50,7 +50,7 @@ RUN: not lld -flavor gnu -target x86_64 -T %p/phdrs/undef-empty-phdrs.script %t.o -static -o %t2 &> %t2-error RUN: FileCheck -check-prefix UNDEF-EMPTY-PHDRS %s < %t2-error -UNDEF-EMPTY-PHDRS: Linker script has wrong segments set for output sections +UNDEF-EMPTY-PHDRS: Unknown header identifiers (missing in PHDRS command) are used */ /* @@ -59,5 +59,5 @@ RUN: not lld -flavor gnu -target x86_64 -T %p/phdrs/undef-id-phdrs.script %t.o -static -o %t3 &> %t3-error RUN: FileCheck -check-prefix UNDEF-ID-PHDRS %s < %t3-error -UNDEF-ID-PHDRS: Linker script has wrong segments set for output sections +UNDEF-ID-PHDRS: Unknown header identifiers (missing in PHDRS command) are used */ Index: lld/trunk/test/elf/linkerscript/phdrs-misplaced-program.test =================================================================== --- lld/trunk/test/elf/linkerscript/phdrs-misplaced-program.test +++ lld/trunk/test/elf/linkerscript/phdrs-misplaced-program.test @@ -0,0 +1,26 @@ +/* +Test misplaced program header generates error. + +RUN: yaml2obj -format=elf %p/Inputs/simple.o.yaml -o=%t.o + +RUN: not lld -flavor gnu -target x86_64 -T %s %t.o -static -o %t1 &> %t1-error +RUN: FileCheck -check-prefix MISPLACED-PROGRAM-PHDR %s < %t1-error +*/ + +ENTRY(_start) + +PHDRS +{ + text PT_LOAD; + header PT_PHDR PHDRS; +} + +SECTIONS +{ + .text : { *(.text) } :NONE + .data : { *(.data) } +} + +/* +MISPLACED-PROGRAM-PHDR: Program header must precede load segments +*/ Index: lld/trunk/test/elf/linkerscript/phdrs-no-program.test =================================================================== --- lld/trunk/test/elf/linkerscript/phdrs-no-program.test +++ lld/trunk/test/elf/linkerscript/phdrs-no-program.test @@ -0,0 +1,25 @@ +/* +Test when no program segment set it's not generated. + +RUN: yaml2obj -format=elf %p/Inputs/simple.o.yaml -o=%t.o + +RUN: lld -flavor gnu -target x86_64 -T %s %t.o -static -o %t1 +RUN: llvm-readobj -program-headers %t1 | FileCheck -check-prefix PROGRAM-PHDR %s +*/ + +ENTRY(_start) + +PHDRS +{ + text PT_LOAD; +} + +SECTIONS +{ + .text : { *(.text) } :text + .data : { *(.data) } +} + +/* +PROGRAM-PHDR-NOT: Type: PT_PHDR (0x6) +*/ Index: lld/trunk/test/elf/linkerscript/phdrs-program-flags.test =================================================================== --- lld/trunk/test/elf/linkerscript/phdrs-program-flags.test +++ lld/trunk/test/elf/linkerscript/phdrs-program-flags.test @@ -0,0 +1,33 @@ +/* +Test when program segment contains only FLAGS attribute. + +RUN: yaml2obj -format=elf %p/Inputs/simple.o.yaml -o=%t.o + +RUN: lld -flavor gnu -target x86_64 -T %s %t.o -static -o %t1 +RUN: llvm-readobj -program-headers %t1 | FileCheck -check-prefix PROGRAM-FLAGS-PHDR %s +*/ + +ENTRY(_start) + +PHDRS +{ + header PT_PHDR PHDRS FLAGS(0x7); + text PT_LOAD FILEHDR PHDRS; +} + +SECTIONS +{ + .text : { *(.text) } :text + .data : { *(.data) } +} + +/* +PROGRAM-FLAGS-PHDR: ProgramHeader { +PROGRAM-FLAGS-PHDR: Type: PT_PHDR (0x6) +PROGRAM-FLAGS-PHDR: Flags [ (0x7) +PROGRAM-FLAGS-PHDR: PF_R (0x4) +PROGRAM-FLAGS-PHDR: PF_W (0x2) +PROGRAM-FLAGS-PHDR: PF_X (0x1) +PROGRAM-FLAGS-PHDR: ] +PROGRAM-FLAGS-PHDR: } +*/ Index: lld/trunk/test/elf/linkerscript/phdrs-program-good-phdrs.test =================================================================== --- lld/trunk/test/elf/linkerscript/phdrs-program-good-phdrs.test +++ lld/trunk/test/elf/linkerscript/phdrs-program-good-phdrs.test @@ -0,0 +1,34 @@ +/* +Test when program segment contains only PHDRS attribute. + +RUN: yaml2obj -format=elf %p/Inputs/simple.o.yaml -o=%t.o + +RUN: lld -flavor gnu -target x86_64 -T %s %t.o -static -o %t1 +RUN: llvm-readobj -program-headers %t1 | FileCheck -check-prefix PROGRAM-PHDRS-PHDR %s +*/ + +ENTRY(_start) + +PHDRS +{ + header PT_PHDR PHDRS; + text PT_LOAD PHDRS; +} + +SECTIONS +{ + .text : { *(.text) } :text + .data : { *(.data) } +} + +/* +PROGRAM-PHDRS-PHDR: ProgramHeader { +PROGRAM-PHDRS-PHDR: Type: PT_PHDR (0x6) +PROGRAM-PHDRS-PHDR: VirtualAddress: 0x400040 +PROGRAM-PHDRS-PHDR: MemSize: 168 +PROGRAM-PHDRS-PHDR: Flags [ (0x5) +PROGRAM-PHDRS-PHDR: PF_R (0x4) +PROGRAM-PHDRS-PHDR: PF_X (0x1) +PROGRAM-PHDRS-PHDR: ] +PROGRAM-PHDRS-PHDR: } +*/ Index: lld/trunk/test/elf/linkerscript/phdrs-program-no-phdrs.test =================================================================== --- lld/trunk/test/elf/linkerscript/phdrs-program-no-phdrs.test +++ lld/trunk/test/elf/linkerscript/phdrs-program-no-phdrs.test @@ -0,0 +1,26 @@ +/* +Test when program segment doesn't contain PHDRS attribute. + +RUN: yaml2obj -format=elf %p/Inputs/simple.o.yaml -o=%t.o + +RUN: not lld -flavor gnu -target x86_64 -T %s %t.o -static -o %t1 &> %t1-error +RUN: FileCheck -check-prefix PROGRAM-PHDR-NO-PHDRS %s < %t1-error +*/ + +ENTRY(_start) + +PHDRS +{ + header PT_PHDR; + text PT_LOAD; +} + +SECTIONS +{ + .text : { *(.text) } :text + .data : { *(.data) } +} + +/* +PROGRAM-PHDR-NO-PHDRS: Program header has invalid PHDRS attribute +*/ Index: lld/trunk/test/elf/linkerscript/phdrs-program-wrong-phdrs.test =================================================================== --- lld/trunk/test/elf/linkerscript/phdrs-program-wrong-phdrs.test +++ lld/trunk/test/elf/linkerscript/phdrs-program-wrong-phdrs.test @@ -0,0 +1,26 @@ +/* +Test when program segment contains PHDRS attribute not mapped to load segment. + +RUN: yaml2obj -format=elf %p/Inputs/simple.o.yaml -o=%t.o + +RUN: not lld -flavor gnu -target x86_64 -T %s %t.o -static -o %t1 &> %t1-error +RUN: FileCheck -check-prefix PROGRAM-PHDR-WRONG-PHDRS %s < %t1-error +*/ + +ENTRY(_start) + +PHDRS +{ + header PT_PHDR PHDRS; + text PT_LOAD; +} + +SECTIONS +{ + .text : { *(.text) } :text + .data : { *(.data) } +} + +/* +PROGRAM-PHDR-WRONG-PHDRS: Program header has invalid PHDRS attribute +*/