diff options
author | Denis Protivensky <dprotivensky@accesssoftek.com> | 2015-06-15 08:00:51 +0000 |
---|---|---|
committer | Denis Protivensky <dprotivensky@accesssoftek.com> | 2015-06-15 08:00:51 +0000 |
commit | 1aaf736d897170af4140643b49a5074b18f82fed (patch) | |
tree | 5bc7af7754b0740ec2e3318523a1f2261b1df644 | |
parent | 12b0c2835ea651a96af0061137cf4811a966ec35 (diff) | |
download | bcm5719-llvm-1aaf736d897170af4140643b49a5074b18f82fed.tar.gz bcm5719-llvm-1aaf736d897170af4140643b49a5074b18f82fed.zip |
[LinkerScript] Add matching of output sections to segments
Add method to query segments for specified output section name.
Return error if the section is assigned to unknown segment.
Check matching of sections to segments during layout on the subject of correctness.
NOTE: no actual functionality of using custom segments is implemented.
Differential Revision: http://reviews.llvm.org/D10359
llvm-svn: 239719
-rw-r--r-- | lld/include/lld/Core/Error.h | 3 | ||||
-rw-r--r-- | lld/include/lld/ReaderWriter/LinkerScript.h | 20 | ||||
-rw-r--r-- | lld/lib/Core/Error.cpp | 2 | ||||
-rw-r--r-- | lld/lib/ReaderWriter/ELF/TargetLayout.cpp | 12 | ||||
-rw-r--r-- | lld/lib/ReaderWriter/ELF/TargetLayout.h | 3 | ||||
-rw-r--r-- | lld/lib/ReaderWriter/LinkerScript.cpp | 78 | ||||
-rw-r--r-- | lld/test/elf/linkerscript/phdrs-default.test | 82 | ||||
-rw-r--r-- | lld/test/elf/linkerscript/phdrs-invalid.test | 63 | ||||
-rw-r--r-- | lld/test/elf/linkerscript/phdrs/sections-empty-phdrs.script | 11 | ||||
-rw-r--r-- | lld/test/elf/linkerscript/phdrs/sections-no-phdrs.script | 7 | ||||
-rw-r--r-- | lld/test/elf/linkerscript/phdrs/sections-none-phdrs.script | 11 | ||||
-rw-r--r-- | lld/test/elf/linkerscript/phdrs/undef-empty-phdrs.script | 11 | ||||
-rw-r--r-- | lld/test/elf/linkerscript/phdrs/undef-id-phdrs.script | 12 | ||||
-rw-r--r-- | lld/test/elf/linkerscript/phdrs/undef-no-phdrs.script | 7 |
14 files changed, 317 insertions, 5 deletions
diff --git a/lld/include/lld/Core/Error.h b/lld/include/lld/Core/Error.h index 320c0598864..5a712fa74f8 100644 --- a/lld/include/lld/Core/Error.h +++ b/lld/include/lld/Core/Error.h @@ -36,7 +36,8 @@ enum class LinkerScriptReaderError { 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) { diff --git a/lld/include/lld/ReaderWriter/LinkerScript.h b/lld/include/lld/ReaderWriter/LinkerScript.h index 7b2f21aa339..33b6374d3a0 100644 --- a/lld/include/lld/ReaderWriter/LinkerScript.h +++ b/lld/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 @@ public: 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<StringRef> PHDRs() const { return _phdrs; } private: StringRef _sectionName; @@ -814,9 +817,13 @@ public: _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 @@ public: /// 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<const PHDR *> &phdrs) const; + void dump() const; private: @@ -1361,6 +1375,9 @@ private: 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 @@ private: llvm::DenseSet<int> _deliveredExprs; mutable llvm::StringSet<> _definedSymbols; + bool _parsedPHDRS; + llvm::StringMap<llvm::SmallVector<const PHDR *, 2>> _sectionToPHDR; + Expression::SymbolTableTy _symbolTable; }; diff --git a/lld/lib/Core/Error.cpp b/lld/lib/Core/Error.cpp index 8b7a7e2b528..5d612f66401 100644 --- a/lld/lib/Core/Error.cpp +++ b/lld/lib/Core/Error.cpp @@ -56,6 +56,8 @@ public: 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."); diff --git a/lld/lib/ReaderWriter/ELF/TargetLayout.cpp b/lld/lib/ReaderWriter/ELF/TargetLayout.cpp index 87cbcd5cab5..c0dae207bd6 100644 --- a/lld/lib/ReaderWriter/ELF/TargetLayout.cpp +++ b/lld/lib/ReaderWriter/ELF/TargetLayout.cpp @@ -320,6 +320,7 @@ template <class ELFT> void TargetLayout<ELFT>::createOutputSections() { } else { outputSection = new (_allocator.Allocate<OutputSection<ELFT>>()) OutputSection<ELFT>(section->outputSectionName()); + checkOutputSectionSegment(outputSection); _outputSections.push_back(outputSection); outputSectionInsert.first->second = outputSection; } @@ -327,6 +328,17 @@ template <class ELFT> void TargetLayout<ELFT>::createOutputSections() { } } +// Check that output section has proper segment set +template <class ELFT> +void TargetLayout<ELFT>::checkOutputSectionSegment( + const OutputSection<ELFT> *sec) { + std::vector<const script::PHDR *> phdrs; + if (_linkerScriptSema.getPHDRsForOutputSection(sec->name(), phdrs)) { + llvm::report_fatal_error( + "Linker script has wrong segments set for output sections"); + } +} + template <class ELFT> uint64_t TargetLayout<ELFT>::getLookupSectionFlags(const OutputSection<ELFT> *os) const { diff --git a/lld/lib/ReaderWriter/ELF/TargetLayout.h b/lld/lib/ReaderWriter/ELF/TargetLayout.h index 7162cd5fe08..f35bf031146 100644 --- a/lld/lib/ReaderWriter/ELF/TargetLayout.h +++ b/lld/lib/ReaderWriter/ELF/TargetLayout.h @@ -209,6 +209,9 @@ public: // Output sections with the same name into a OutputSection void createOutputSections(); + // Check that output section has proper segment set + void checkOutputSectionSegment(const OutputSection<ELFT> *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(); diff --git a/lld/lib/ReaderWriter/LinkerScript.cpp b/lld/lib/ReaderWriter/LinkerScript.cpp index 6d82e29d95a..79a367d15bf 100644 --- a/lld/lib/ReaderWriter/LinkerScript.cpp +++ b/lld/lib/ReaderWriter/LinkerScript.cpp @@ -953,6 +953,9 @@ void PHDR::dump(raw_ostream &os) const { 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) { @@ -2353,10 +2356,7 @@ Extern *Parser::parseExtern() { } // Sema member functions -Sema::Sema() - : _scripts(), _layoutCommands(), _memberToLayoutOrder(), - _memberNameWildcards(), _cacheSectionOrder(), _cacheExpressionOrder(), - _deliveredExprs(), _symbolTable() {} +Sema::Sema() : _parsedPHDRS(false) {} void Sema::perform() { for (auto &parser : _scripts) @@ -2465,6 +2465,18 @@ uint64_t Sema::getLinkerScriptExprValue(StringRef name) const { return it->second; } +std::error_code +Sema::getPHDRsForOutputSection(StringRef name, + std::vector<const PHDR *> &phdrs) const { + // Cache results if not done yet. + if (auto ec = const_cast<Sema *>(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"; @@ -2701,6 +2713,64 @@ bool Sema::localCompare(int order, const SectionKey &lhs, 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<const PHDR *> phdrs; + for (auto &parser : _scripts) { + for (auto *cmd : parser->get()->_commands) { + if (auto *ph = dyn_cast<PHDRS>(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<const PHDR *, 2> phdrsCur, phdrsLast { PHDR::NONE }; + for (const Command *cmd : _layoutCommands) { + auto osd = dyn_cast<OutputSectionDescription>(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 == '\\') diff --git a/lld/test/elf/linkerscript/phdrs-default.test b/lld/test/elf/linkerscript/phdrs-default.test new file mode 100644 index 00000000000..77a40df5081 --- /dev/null +++ b/lld/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 +*/ diff --git a/lld/test/elf/linkerscript/phdrs-invalid.test b/lld/test/elf/linkerscript/phdrs-invalid.test new file mode 100644 index 00000000000..43ff988370b --- /dev/null +++ b/lld/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 +*/ diff --git a/lld/test/elf/linkerscript/phdrs/sections-empty-phdrs.script b/lld/test/elf/linkerscript/phdrs/sections-empty-phdrs.script new file mode 100644 index 00000000000..ac30df78d5e --- /dev/null +++ b/lld/test/elf/linkerscript/phdrs/sections-empty-phdrs.script @@ -0,0 +1,11 @@ +ENTRY(_start) + +PHDRS +{ +} + +SECTIONS +{ + .text : { *(.text) } + .data : { *(.data) } +} diff --git a/lld/test/elf/linkerscript/phdrs/sections-no-phdrs.script b/lld/test/elf/linkerscript/phdrs/sections-no-phdrs.script new file mode 100644 index 00000000000..b8848cd3ab0 --- /dev/null +++ b/lld/test/elf/linkerscript/phdrs/sections-no-phdrs.script @@ -0,0 +1,7 @@ +ENTRY(_start) + +SECTIONS +{ + .text : { *(.text) } + .data : { *(.data) } +} diff --git a/lld/test/elf/linkerscript/phdrs/sections-none-phdrs.script b/lld/test/elf/linkerscript/phdrs/sections-none-phdrs.script new file mode 100644 index 00000000000..4b240245d2b --- /dev/null +++ b/lld/test/elf/linkerscript/phdrs/sections-none-phdrs.script @@ -0,0 +1,11 @@ +ENTRY(_start) + +PHDRS +{ +} + +SECTIONS +{ + .text : { *(.text) } :NONE + .data : { *(.data) } +} diff --git a/lld/test/elf/linkerscript/phdrs/undef-empty-phdrs.script b/lld/test/elf/linkerscript/phdrs/undef-empty-phdrs.script new file mode 100644 index 00000000000..f92b452d234 --- /dev/null +++ b/lld/test/elf/linkerscript/phdrs/undef-empty-phdrs.script @@ -0,0 +1,11 @@ +ENTRY(_start) + +PHDRS +{ +} + +SECTIONS +{ + .text : { *(.text) } + .data : { *(.data) } :phdr +} diff --git a/lld/test/elf/linkerscript/phdrs/undef-id-phdrs.script b/lld/test/elf/linkerscript/phdrs/undef-id-phdrs.script new file mode 100644 index 00000000000..8ac56695f00 --- /dev/null +++ b/lld/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) } +} diff --git a/lld/test/elf/linkerscript/phdrs/undef-no-phdrs.script b/lld/test/elf/linkerscript/phdrs/undef-no-phdrs.script new file mode 100644 index 00000000000..d3ee5bdebe4 --- /dev/null +++ b/lld/test/elf/linkerscript/phdrs/undef-no-phdrs.script @@ -0,0 +1,7 @@ +ENTRY(_start) + +SECTIONS +{ + .text : { *(.text) } :phdr + .data : { *(.data) } +} |