aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorHans Wennborg <hans@hanshq.net>2018-01-30 09:20:27 +0000
committerHans Wennborg <hans@hanshq.net>2018-01-30 09:20:27 +0000
commita285adba1da4fc1af0c87401b763999ac64b21ee (patch)
tree19d48121ca7f0b321f497e4668f668b9ce262f28
parenta4e0f76208b2d56ee4b92735bf28275f8d5ff819 (diff)
Merging r323395, r323396, r323399, r323440, r323449, r323456, and r323625:
------------------------------------------------------------------------ r323395 | rafael | 2018-01-25 02:29:15 +0100 (Thu, 25 Jan 2018) | 1 line Use lookup instead of find. NFC, just simpler. ------------------------------------------------------------------------ ------------------------------------------------------------------------ r323396 | rafael | 2018-01-25 02:36:36 +0100 (Thu, 25 Jan 2018) | 3 lines Only lookup LMARegion once. NFC. This is similar to how we handle MemRegion. ------------------------------------------------------------------------ ------------------------------------------------------------------------ r323399 | rafael | 2018-01-25 03:18:00 +0100 (Thu, 25 Jan 2018) | 3 lines Remove MemRegionOffset. NFC. We can just use a member variable in MemoryRegion. ------------------------------------------------------------------------ ------------------------------------------------------------------------ r323440 | rafael | 2018-01-25 17:43:49 +0100 (Thu, 25 Jan 2018) | 1 line Simplify. NFC. ------------------------------------------------------------------------ ------------------------------------------------------------------------ r323449 | rafael | 2018-01-25 18:42:03 +0100 (Thu, 25 Jan 2018) | 9 lines Improve LMARegion handling. This fixes the crash reported at PR36083. The issue is that we were trying to put all the sections in the same PT_LOAD and crashing trying to write past the end of the file. This also adds accounting for used space in LMARegion, without it all 3 PT_LOADs would have the same physical address. ------------------------------------------------------------------------ ------------------------------------------------------------------------ r323456 | rafael | 2018-01-25 20:02:08 +0100 (Thu, 25 Jan 2018) | 19 lines Move LMAOffset from the OutputSection to the PhdrEntry. NFC. If two sections are in the same PT_LOAD, their relatives offsets, virtual address and physical addresses are all the same. I initially wanted to have a single global LMAOffset, on the assumption that every ELF file was in practiced loaded contiguously in both physical and virtual memory. Unfortunately that is not the case. The linux kernel has: LOAD 0x200000 0xffffffff81000000 0x0000000001000000 0xced000 0xced000 R E 0x200000 LOAD 0x1000000 0xffffffff81e00000 0x0000000001e00000 0x15f000 0x15f000 RW 0x200000 LOAD 0x1200000 0x0000000000000000 0x0000000001f5f000 0x01b198 0x01b198 RW 0x200000 LOAD 0x137b000 0xffffffff81f7b000 0x0000000001f7b000 0x116000 0x1ec000 RWE 0x200000 The delta for all but the third PT_LOAD is the same: 0xffffffff80000000. I think the 3rd one is a hack for implementing per cpu data, but we can't break that. ------------------------------------------------------------------------ ------------------------------------------------------------------------ r323625 | rafael | 2018-01-29 04:44:44 +0100 (Mon, 29 Jan 2018) | 10 lines Put the header in the first PT_LOAD even if that PT_LOAD has a LMAExpr. This should fix PR36017. The root problem is that we were creating a PT_LOAD just for the header. That was technically valid, but inconvenient: we should not be making the ELF discontinuous. The solution is to allow a section with LMAExpr to be added to a PT_LOAD if that PT_LOAD doesn't already have a LMAExpr. ------------------------------------------------------------------------ git-svn-id: https://llvm.org/svn/llvm-project/lld/branches/release_60@323733 91177308-0d34-0410-b5e6-96231b3b80d8
-rw-r--r--ELF/LinkerScript.cpp48
-rw-r--r--ELF/LinkerScript.h10
-rw-r--r--ELF/OutputSections.h4
-rw-r--r--ELF/ScriptParser.cpp4
-rw-r--r--ELF/Writer.cpp6
-rw-r--r--ELF/Writer.h7
-rw-r--r--test/ELF/linkerscript/at3.s38
-rw-r--r--test/ELF/linkerscript/merge-header-load.s21
8 files changed, 109 insertions, 29 deletions
diff --git a/ELF/LinkerScript.cpp b/ELF/LinkerScript.cpp
index 33a618952..23c3a11a9 100644
--- a/ELF/LinkerScript.cpp
+++ b/ELF/LinkerScript.cpp
@@ -589,8 +589,12 @@ void LinkerScript::output(InputSection *S) {
// If there is a memory region associated with this input section, then
// place the section in that region and update the region index.
+ if (Ctx->LMARegion)
+ Ctx->LMARegion->CurPos += Pos - Before;
+ // FIXME: should we also produce overflow errors for LMARegion?
+
if (Ctx->MemRegion) {
- uint64_t &CurOffset = Ctx->MemRegionOffset[Ctx->MemRegion];
+ uint64_t &CurOffset = Ctx->MemRegion->CurPos;
CurOffset += Pos - Before;
uint64_t CurSize = CurOffset - Ctx->MemRegion->Origin;
if (CurSize > Ctx->MemRegion->Length) {
@@ -617,9 +621,8 @@ MemoryRegion *LinkerScript::findMemoryRegion(OutputSection *Sec) {
// If a memory region name was specified in the output section command,
// then try to find that region first.
if (!Sec->MemoryRegionName.empty()) {
- auto It = MemoryRegions.find(Sec->MemoryRegionName);
- if (It != MemoryRegions.end())
- return It->second;
+ if (MemoryRegion *M = MemoryRegions.lookup(Sec->MemoryRegionName))
+ return M;
error("memory region '" + Sec->MemoryRegionName + "' not declared");
return nullptr;
}
@@ -652,31 +655,24 @@ void LinkerScript::assignOffsets(OutputSection *Sec) {
setDot(Sec->AddrExpr, Sec->Location, false);
Ctx->MemRegion = Sec->MemRegion;
+ Ctx->LMARegion = Sec->LMARegion;
if (Ctx->MemRegion)
- Dot = Ctx->MemRegionOffset[Ctx->MemRegion];
+ Dot = Ctx->MemRegion->CurPos;
switchTo(Sec);
- if (Sec->LMAExpr) {
- uint64_t D = Dot;
- Ctx->LMAOffset = [=] { return Sec->LMAExpr().getValue() - D; };
- }
+ if (Sec->LMAExpr)
+ Ctx->LMAOffset = Sec->LMAExpr().getValue() - Dot;
- if (!Sec->LMARegionName.empty()) {
- if (MemoryRegion *MR = MemoryRegions.lookup(Sec->LMARegionName)) {
- uint64_t Offset = MR->Origin - Dot;
- Ctx->LMAOffset = [=] { return Offset; };
- } else {
- error("memory region '" + Sec->LMARegionName + "' not declared");
- }
- }
+ if (MemoryRegion *MR = Sec->LMARegion)
+ Ctx->LMAOffset = MR->CurPos - Dot;
// If neither AT nor AT> is specified for an allocatable section, the linker
// will set the LMA such that the difference between VMA and LMA for the
// section is the same as the preceding output section in the same region
// https://sourceware.org/binutils/docs-2.20/ld/Output-Section-LMA.html
- if (Ctx->LMAOffset)
- Ctx->OutSec->LMAOffset = Ctx->LMAOffset();
+ if (PhdrEntry *L = Ctx->OutSec->PtLoad)
+ L->LMAOffset = Ctx->LMAOffset;
// The Size previously denoted how many InputSections had been added to this
// section, and was used for sorting SHF_LINK_ORDER sections. Reset it to
@@ -698,7 +694,9 @@ void LinkerScript::assignOffsets(OutputSection *Sec) {
Cmd->Offset = Dot - Ctx->OutSec->Addr;
Dot += Cmd->Size;
if (Ctx->MemRegion)
- Ctx->MemRegionOffset[Ctx->MemRegion] += Cmd->Size;
+ Ctx->MemRegion->CurPos += Cmd->Size;
+ if (Ctx->LMARegion)
+ Ctx->LMARegion->CurPos += Cmd->Size;
Ctx->OutSec->Size = Dot - Ctx->OutSec->Addr;
continue;
}
@@ -797,6 +795,12 @@ void LinkerScript::adjustSectionsAfterSorting() {
if (auto *Sec = dyn_cast<OutputSection>(Base)) {
if (!Sec->Live)
continue;
+ if (!Sec->LMARegionName.empty()) {
+ if (MemoryRegion *M = MemoryRegions.lookup(Sec->LMARegionName))
+ Sec->LMARegion = M;
+ else
+ error("memory region '" + Sec->LMARegionName + "' not declared");
+ }
Sec->MemRegion = findMemoryRegion(Sec);
// Handle align (e.g. ".foo : ALIGN(16) { ... }").
if (Sec->AlignExpr)
@@ -887,8 +891,8 @@ void LinkerScript::allocateHeaders(std::vector<PhdrEntry *> &Phdrs) {
LinkerScript::AddressState::AddressState() {
for (auto &MRI : Script->MemoryRegions) {
- const MemoryRegion *MR = MRI.second;
- MemRegionOffset[MR] = MR->Origin;
+ MemoryRegion *MR = MRI.second;
+ MR->CurPos = MR->Origin;
}
}
diff --git a/ELF/LinkerScript.h b/ELF/LinkerScript.h
index 11131dda8..0d7578cd2 100644
--- a/ELF/LinkerScript.h
+++ b/ELF/LinkerScript.h
@@ -118,11 +118,17 @@ enum class ConstraintKind { NoConstraint, ReadOnly, ReadWrite };
// target memory. Instances of the struct are created by parsing the
// MEMORY command.
struct MemoryRegion {
+ MemoryRegion(StringRef Name, uint64_t Origin, uint64_t Length, uint32_t Flags,
+ uint32_t NegFlags)
+ : Name(Name), Origin(Origin), Length(Length), Flags(Flags),
+ NegFlags(NegFlags) {}
+
std::string Name;
uint64_t Origin;
uint64_t Length;
uint32_t Flags;
uint32_t NegFlags;
+ uint64_t CurPos = 0;
};
// This struct represents one section match pattern in SECTIONS() command.
@@ -200,8 +206,8 @@ class LinkerScript final {
uint64_t ThreadBssOffset = 0;
OutputSection *OutSec = nullptr;
MemoryRegion *MemRegion = nullptr;
- llvm::DenseMap<const MemoryRegion *, uint64_t> MemRegionOffset;
- std::function<uint64_t()> LMAOffset;
+ MemoryRegion *LMARegion = nullptr;
+ uint64_t LMAOffset = 0;
};
llvm::DenseMap<StringRef, OutputSection *> NameToOutputSection;
diff --git a/ELF/OutputSections.h b/ELF/OutputSections.h
index 009f45c03..c006eae0c 100644
--- a/ELF/OutputSections.h
+++ b/ELF/OutputSections.h
@@ -49,7 +49,7 @@ public:
static bool classof(const BaseCommand *C);
- uint64_t getLMA() const { return Addr + LMAOffset; }
+ uint64_t getLMA() const { return PtLoad ? Addr + PtLoad->LMAOffset : Addr; }
template <typename ELFT> void writeHeaderTo(typename ELFT::Shdr *SHdr);
unsigned SectionIndex;
@@ -78,7 +78,6 @@ public:
// The following fields correspond to Elf_Shdr members.
uint64_t Offset = 0;
- uint64_t LMAOffset = 0;
uint64_t Addr = 0;
uint32_t ShName = 0;
@@ -89,6 +88,7 @@ public:
// The following members are normally only used in linker scripts.
MemoryRegion *MemRegion = nullptr;
+ MemoryRegion *LMARegion = nullptr;
Expr AddrExpr;
Expr AlignExpr;
Expr LMAExpr;
diff --git a/ELF/ScriptParser.cpp b/ELF/ScriptParser.cpp
index e068beeee..3d2606db8 100644
--- a/ELF/ScriptParser.cpp
+++ b/ELF/ScriptParser.cpp
@@ -1293,8 +1293,8 @@ void ScriptParser::readMemory() {
// Add the memory region to the region map.
if (Script->MemoryRegions.count(Name))
setError("region '" + Name + "' already defined");
- MemoryRegion *MR = make<MemoryRegion>();
- *MR = {Name, Origin, Length, Flags, NegFlags};
+ MemoryRegion *MR =
+ make<MemoryRegion>(Name, Origin, Length, Flags, NegFlags);
Script->MemoryRegions[Name] = MR;
}
}
diff --git a/ELF/Writer.cpp b/ELF/Writer.cpp
index 5feff456f..1c3b6495a 100644
--- a/ELF/Writer.cpp
+++ b/ELF/Writer.cpp
@@ -822,6 +822,8 @@ void PhdrEntry::add(OutputSection *Sec) {
p_align = std::max(p_align, Sec->Alignment);
if (p_type == PT_LOAD)
Sec->PtLoad = this;
+ if (Sec->LMAExpr)
+ ASectionHasLMA = true;
}
// The beginning and the ending of .rel[a].plt section are marked
@@ -1626,7 +1628,9 @@ template <class ELFT> std::vector<PhdrEntry *> Writer<ELFT>::createPhdrs() {
// different flags or is loaded at a discontiguous address using AT linker
// script command.
uint64_t NewFlags = computeFlags(Sec->getPhdrFlags());
- if (Sec->LMAExpr || Flags != NewFlags) {
+ if ((Sec->LMAExpr && Load->ASectionHasLMA) ||
+ Sec->MemRegion != Load->FirstSec->MemRegion || Flags != NewFlags) {
+
Load = AddHdr(PT_LOAD, NewFlags);
Flags = NewFlags;
}
diff --git a/ELF/Writer.h b/ELF/Writer.h
index d247068ba..f48f9d1e0 100644
--- a/ELF/Writer.h
+++ b/ELF/Writer.h
@@ -44,6 +44,13 @@ struct PhdrEntry {
OutputSection *FirstSec = nullptr;
OutputSection *LastSec = nullptr;
bool HasLMA = false;
+
+ // True if one of the sections in this program header has a LMA specified via
+ // linker script: AT(addr). We never allow 2 or more sections with LMA in the
+ // same program header.
+ bool ASectionHasLMA = false;
+
+ uint64_t LMAOffset = 0;
};
void addReservedSymbols();
diff --git a/test/ELF/linkerscript/at3.s b/test/ELF/linkerscript/at3.s
new file mode 100644
index 000000000..10bbfc6e9
--- /dev/null
+++ b/test/ELF/linkerscript/at3.s
@@ -0,0 +1,38 @@
+# REQUIRES: x86
+# RUN: llvm-mc -filetype=obj -triple=x86_64-pc-linux %s -o %t.o
+# RUN: echo "MEMORY { \
+# RUN: FOO (ax) : ORIGIN = 0x1000, LENGTH = 0x100 \
+# RUN: BAR (ax) : ORIGIN = 0x2000, LENGTH = 0x100 \
+# RUN: ZED (ax) : ORIGIN = 0x3000, LENGTH = 0x100 \
+# RUN: FLASH (ax) : ORIGIN = 0x6000, LENGTH = 0x200 \
+# RUN: } \
+# RUN: SECTIONS { \
+# RUN: .foo1 : { *(.foo1) } > FOO AT>FLASH \
+# RUN: .foo2 : { *(.foo2) BYTE(0x42) } > BAR AT>FLASH \
+# RUN: .foo3 : { *(.foo3) } > ZED AT>FLASH \
+# RUN: }" > %t.script
+# RUN: ld.lld %t.o --script %t.script -o %t
+# RUN: llvm-readelf -sections -program-headers %t | FileCheck %s
+
+# CHECK: .foo1 PROGBITS 0000000000001000 001000
+# CHECK: .foo2 PROGBITS 0000000000002000 002000
+# CHECK: .foo3 PROGBITS 0000000000003000 003000
+
+# CHECK: Program Headers:
+# CHECK-NOT: LOAD
+
+# CHECK: Type Offset VirtAddr PhysAddr
+# CHECK-NEXT: LOAD 0x001000 0x0000000000001000 0x0000000000006000
+# CHECK-NEXT: LOAD 0x002000 0x0000000000002000 0x0000000000006008
+# CHECK-NEXT: LOAD 0x003000 0x0000000000003000 0x0000000000006011
+
+# CHECK-NOT: LOAD
+
+.section .foo1, "a"
+.quad 0
+
+.section .foo2, "ax"
+.quad 0
+
+.section .foo3, "ax"
+.quad 0
diff --git a/test/ELF/linkerscript/merge-header-load.s b/test/ELF/linkerscript/merge-header-load.s
new file mode 100644
index 000000000..5fb866abe
--- /dev/null
+++ b/test/ELF/linkerscript/merge-header-load.s
@@ -0,0 +1,21 @@
+# REQUIRES: x86
+# RUN: llvm-mc -filetype=obj -triple=x86_64-pc-linux %s -o %t.o
+# RUN: echo "SECTIONS { \
+# RUN: . = 0xffffffff80000200; \
+# RUN: .text : AT (0x4200) { *(.text) } \
+# RUN: }" > %t.script
+# RUN: ld.lld %t.o --script %t.script -o %t
+# RUN: llvm-readelf -program-headers %t | FileCheck %s
+
+# Test that we put the header in the first PT_LOAD. We used to create a PT_LOAD
+# just for it and it would have a different virtual to physical address delta.
+
+# CHECK: Program Headers:
+# CHECK: Type Offset VirtAddr PhysAddr
+# CHECK-NEXT: PHDR 0x000040 0xffffffff80000040 0x0000000000004040
+# CHECK-NEXT: LOAD 0x000000 0xffffffff80000000 0x0000000000004000
+# CHECK-NOT: LOAD
+
+.global _start
+_start:
+nop