summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--lld/include/lld/Core/Error.h3
-rw-r--r--lld/include/lld/ReaderWriter/LinkerScript.h20
-rw-r--r--lld/lib/Core/Error.cpp2
-rw-r--r--lld/lib/ReaderWriter/ELF/TargetLayout.cpp12
-rw-r--r--lld/lib/ReaderWriter/ELF/TargetLayout.h3
-rw-r--r--lld/lib/ReaderWriter/LinkerScript.cpp78
-rw-r--r--lld/test/elf/linkerscript/phdrs-default.test82
-rw-r--r--lld/test/elf/linkerscript/phdrs-invalid.test63
-rw-r--r--lld/test/elf/linkerscript/phdrs/sections-empty-phdrs.script11
-rw-r--r--lld/test/elf/linkerscript/phdrs/sections-no-phdrs.script7
-rw-r--r--lld/test/elf/linkerscript/phdrs/sections-none-phdrs.script11
-rw-r--r--lld/test/elf/linkerscript/phdrs/undef-empty-phdrs.script11
-rw-r--r--lld/test/elf/linkerscript/phdrs/undef-id-phdrs.script12
-rw-r--r--lld/test/elf/linkerscript/phdrs/undef-no-phdrs.script7
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) }
+}
OpenPOWER on IntegriCloud