Index: include/lld/Core/Error.h =================================================================== --- include/lld/Core/Error.h +++ include/lld/Core/Error.h @@ -36,7 +36,8 @@ success = 0, parse_error, unknown_symbol_in_expr, - unrecognized_function_in_expr + unrecognized_function_in_expr, + unknown_phdr_ids, }; inline std::error_code make_error_code(LinkerScriptReaderError e) { Index: include/lld/ReaderWriter/LinkerScript.h =================================================================== --- include/lld/ReaderWriter/LinkerScript.h +++ include/lld/ReaderWriter/LinkerScript.h @@ -20,6 +20,7 @@ #include "lld/Core/range.h" #include "llvm/ADT/DenseSet.h" #include "llvm/ADT/DenseMap.h" +#include "llvm/ADT/SmallVector.h" #include "llvm/ADT/StringMap.h" #include "llvm/ADT/StringSet.h" #include "llvm/ADT/StringSwitch.h" @@ -777,6 +778,8 @@ const_iterator begin() const { return _outputSectionCommands.begin(); } const_iterator end() const { return _outputSectionCommands.end(); } StringRef name() const { return _sectionName; } + bool isDiscarded() const { return _discard; } + ArrayRef PHDRs() const { return _phdrs; } private: StringRef _sectionName; @@ -814,9 +817,13 @@ _includePHDRs(includePHDRs), _at(at), _flags(flags) {} ~PHDR() = delete; + StringRef name() const { return _name; } void dump(raw_ostream &os) const; + /// Special header that discards output sections assigned to it. + static const PHDR *NONE; + private: StringRef _name; uint64_t _type; @@ -1320,6 +1327,13 @@ /// has been performed (by calling evalExpr() for all expressions). uint64_t getLinkerScriptExprValue(StringRef name) 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; + void dump() const; private: @@ -1361,6 +1375,9 @@ bool localCompare(int order, const SectionKey &lhs, const SectionKey &rhs) const; + /// Build map that matches output section names to segments they should be + /// put into. + std::error_code buildSectionToPHDR(); /// Our goal with all linearizeAST overloaded functions is to /// traverse the linker script AST while putting nodes in a vector and @@ -1428,6 +1445,9 @@ llvm::DenseSet _deliveredExprs; mutable llvm::StringSet<> _definedSymbols; + bool _parsedPHDRS; + llvm::StringMap> _sectionToPHDR; + Expression::SymbolTableTy _symbolTable; }; Index: lib/Core/Error.cpp =================================================================== --- lib/Core/Error.cpp +++ lib/Core/Error.cpp @@ -56,6 +56,8 @@ case LinkerScriptReaderError::unrecognized_function_in_expr: return "Unrecognized function call when evaluating linker script " "expression"; + case LinkerScriptReaderError::unknown_phdr_ids: + return "Unknown header identifiers (missing in PHDRS command) are used"; } llvm_unreachable("An enumerator of LinkerScriptReaderError does not have a " "message defined."); Index: lib/ReaderWriter/ELF/TargetLayout.h =================================================================== --- lib/ReaderWriter/ELF/TargetLayout.h +++ lib/ReaderWriter/ELF/TargetLayout.h @@ -209,6 +209,9 @@ // Output sections with the same name into a OutputSection void createOutputSections(); + // Check that output section has proper segment set + void checkOutputSectionSegment(const OutputSection *sec); + /// \brief Sort the sections by their order as defined by the layout, /// preparing all sections to be assigned to a segment. virtual void sortInputSections(); Index: lib/ReaderWriter/ELF/TargetLayout.cpp =================================================================== --- lib/ReaderWriter/ELF/TargetLayout.cpp +++ lib/ReaderWriter/ELF/TargetLayout.cpp @@ -320,6 +320,7 @@ } else { outputSection = new (_allocator.Allocate>()) OutputSection(section->outputSectionName()); + checkOutputSectionSegment(outputSection); _outputSections.push_back(outputSection); outputSectionInsert.first->second = outputSection; } @@ -327,6 +328,17 @@ } } +// Check that output section has proper segment set +template +void TargetLayout::checkOutputSectionSegment( + const OutputSection *sec) { + std::vector phdrs; + if (_linkerScriptSema.getPHDRsForOutputSection(sec->name(), phdrs)) { + llvm::report_fatal_error( + "Linker script has wrong segments set for output sections"); + } +} + template uint64_t TargetLayout::getLookupSectionFlags(const OutputSection *os) const { Index: lib/ReaderWriter/LinkerScript.cpp =================================================================== --- lib/ReaderWriter/LinkerScript.cpp +++ lib/ReaderWriter/LinkerScript.cpp @@ -952,6 +952,9 @@ os << ";\n"; } +static PHDR none("NONE", 0, false, false, NULL, 0); +const PHDR *PHDR::NONE = &none; + void PHDRS::dump(raw_ostream &os) const { os << "PHDRS\n{\n"; for (auto &&phdr : _phdrs) { @@ -2352,10 +2355,7 @@ } // Sema member functions -Sema::Sema() - : _scripts(), _layoutCommands(), _memberToLayoutOrder(), - _memberNameWildcards(), _cacheSectionOrder(), _cacheExpressionOrder(), - _deliveredExprs(), _symbolTable() {} +Sema::Sema() : _parsedPHDRS(false) {} void Sema::perform() { for (auto &parser : _scripts) @@ -2464,6 +2464,18 @@ 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; + + auto vec = _sectionToPHDR.lookup(name); + std::copy(std::begin(vec), std::end(vec), std::back_inserter(phdrs)); + return std::error_code(); +} + void Sema::dump() const { raw_ostream &os = llvm::outs(); os << "Linker script semantics dump\n"; @@ -2700,6 +2712,64 @@ 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; + } + } + } + const bool noPhdrs = phdrs.empty(); + + // Add NONE header to the map provided there's no user-defined + // header with the same name. + if (!_sectionToPHDR.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 == '\\') Index: test/elf/linkerscript/phdrs-default.test =================================================================== --- /dev/null +++ test/elf/linkerscript/phdrs-default.test @@ -0,0 +1,82 @@ +/* +This group of tests checks usage of default headers during linking, +when PHDRS command is not defined or defined empty in linker scripts. + +This test uses a single X86-64 input object, simple.o, created with the +following X86-64 assembly code: + +*** simple.S: + +(command line clang -c simple.S -o simple.o) + + .text + main: + mov $1, %eax + movq $1, %rdi + movq $msg, %rsi + movq $14, %rdx + syscall + ret + + .globl _start + _start: + call main + mov $60, %eax + syscall + ret + + .data + msg: .asciz "Hello, World!\n" +*/ + +/* +Prepare the object file to test on. + +RUN: yaml2obj -format=elf %p/Inputs/simple.o.yaml -o=%t.o +*/ + +/* +Test when no linker script passed. + +RUN: lld -flavor gnu -target x86_64 %t.o -static -o %t1 +RUN: llvm-objdump -section-headers %t1 | FileCheck -check-prefix SECTIONS %s +RUN: llvm-readobj -program-headers %t1 | FileCheck -check-prefix HEADERS %s + +SECTIONS: .text {{[0-9a-f]+}} 00000000004000b0 +SECTIONS: .data {{[0-9a-f]+}} 0000000000401000 + +HEADERS: ProgramHeader { +HEADERS: Type: PT_LOAD (0x1) +HEADERS: VirtualAddress: 0x400000 +HEADERS: } +HEADERS: ProgramHeader { +HEADERS: Type: PT_LOAD (0x1) +HEADERS: VirtualAddress: 0x401000 +HEADERS: } +*/ + +/* +Test when linker script doesn't contain PHDRS and sections are not assigned to any segments. + +RUN: lld -flavor gnu -target x86_64 -T %p/phdrs/sections-no-phdrs.script %t.o -static -o %t2 +RUN: llvm-objdump -section-headers %t2 | FileCheck -check-prefix SECTIONS %s +RUN: llvm-readobj -program-headers %t2 | FileCheck -check-prefix HEADERS %s +*/ + +/* +Test when linker script contains empty PHDRS and sections are not assigned to any segments. + +RUN: lld -flavor gnu -target x86_64 -T %p/phdrs/sections-empty-phdrs.script %t.o -static -o %t3 +RUN: llvm-objdump -section-headers %t3 | FileCheck -check-prefix SECTIONS %s +RUN: llvm-readobj -program-headers %t3 | FileCheck -check-prefix HEADERS %s +*/ + +/* +Test when linker script contains empty PHDRS and sections are only assigned to NONE segments +or not assigned at all. +NOTE: Segments with the name NONE are ignored in such a case. + +RUN: lld -flavor gnu -target x86_64 -T %p/phdrs/sections-none-phdrs.script %t.o -static -o %t4 +RUN: llvm-objdump -section-headers %t4 | FileCheck -check-prefix SECTIONS %s +RUN: llvm-readobj -program-headers %t4 | FileCheck -check-prefix HEADERS %s +*/ Index: test/elf/linkerscript/phdrs-invalid.test =================================================================== --- /dev/null +++ test/elf/linkerscript/phdrs-invalid.test @@ -0,0 +1,63 @@ +/* +This group of tests checks invalid cases of defining and using PHDRS +command in linker scripts. + +This test uses a single X86-64 input object, simple.o, created with the +following X86-64 assembly code: + +*** simple.S: + +(command line clang -c simple.S -o simple.o) + + .text + main: + mov $1, %eax + movq $1, %rdi + movq $msg, %rsi + movq $14, %rdx + syscall + ret + + .globl _start + _start: + call main + mov $60, %eax + syscall + ret + + .data + msg: .asciz "Hello, World!\n" +*/ + +/* +Prepare the object file to test on. + +RUN: yaml2obj -format=elf %p/Inputs/simple.o.yaml -o=%t.o +*/ + +/* +Test undefined header used when no PHDRS defined. + +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 +*/ + +/* +Test undefined header used when PHDRS is empty. + +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 +*/ + +/* +Test undefined header used when PHDRS contains definitions. + +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 +*/ Index: test/elf/linkerscript/phdrs/sections-empty-phdrs.script =================================================================== --- /dev/null +++ test/elf/linkerscript/phdrs/sections-empty-phdrs.script @@ -0,0 +1,11 @@ +ENTRY(_start) + +PHDRS +{ +} + +SECTIONS +{ + .text : { *(.text) } + .data : { *(.data) } +} Index: test/elf/linkerscript/phdrs/sections-no-phdrs.script =================================================================== --- /dev/null +++ test/elf/linkerscript/phdrs/sections-no-phdrs.script @@ -0,0 +1,7 @@ +ENTRY(_start) + +SECTIONS +{ + .text : { *(.text) } + .data : { *(.data) } +} Index: test/elf/linkerscript/phdrs/sections-none-phdrs.script =================================================================== --- /dev/null +++ test/elf/linkerscript/phdrs/sections-none-phdrs.script @@ -0,0 +1,11 @@ +ENTRY(_start) + +PHDRS +{ +} + +SECTIONS +{ + .text : { *(.text) } :NONE + .data : { *(.data) } +} Index: test/elf/linkerscript/phdrs/undef-empty-phdrs.script =================================================================== --- /dev/null +++ test/elf/linkerscript/phdrs/undef-empty-phdrs.script @@ -0,0 +1,11 @@ +ENTRY(_start) + +PHDRS +{ +} + +SECTIONS +{ + .text : { *(.text) } + .data : { *(.data) } :phdr +} Index: test/elf/linkerscript/phdrs/undef-id-phdrs.script =================================================================== --- /dev/null +++ test/elf/linkerscript/phdrs/undef-id-phdrs.script @@ -0,0 +1,12 @@ +ENTRY(_start) + +PHDRS +{ + phdr PT_LOAD; +} + +SECTIONS +{ + .text : { *(.text) } :phdr_wrong + .data : { *(.data) } +} Index: test/elf/linkerscript/phdrs/undef-no-phdrs.script =================================================================== --- /dev/null +++ test/elf/linkerscript/phdrs/undef-no-phdrs.script @@ -0,0 +1,7 @@ +ENTRY(_start) + +SECTIONS +{ + .text : { *(.text) } :phdr + .data : { *(.data) } +}