[ELF] - linkerscript AT keyword (in output section description) implemented.
The linker will normally set the LMA equal to the VMA.
You can change that by using the AT keyword.
The expression lma that follows the AT keyword specifies
the load address of the section.
Patch implements this keyword.
Differential revision: https://reviews.llvm.org/D19272
llvm-svn: 278911
diff --git a/lld/ELF/LinkerScript.cpp b/lld/ELF/LinkerScript.cpp
index df400a4..1b6ff23 100644
--- a/lld/ELF/LinkerScript.cpp
+++ b/lld/ELF/LinkerScript.cpp
@@ -504,6 +504,14 @@
return {};
}
+template <class ELFT> typename Expr LinkerScript<ELFT>::getLma(StringRef Name) {
+ for (const std::unique_ptr<BaseCommand> &Base : Opt.Commands)
+ if (auto *Cmd = dyn_cast<OutputSectionCommand>(Base.get()))
+ if (Cmd->LmaExpr && Cmd->Name == Name)
+ return Cmd->LmaExpr;
+ return {};
+}
+
// Returns the index of the given section name in linker script
// SECTIONS commands. Sections are laid out as the same order as they
// were in the script. If a given name did not appear in the script,
@@ -613,6 +621,7 @@
SortKind readSortKind();
SymbolAssignment *readProvideHidden(bool Provide, bool Hidden);
SymbolAssignment *readProvideOrAssignment(StringRef Tok);
+ void readAt(OutputSectionCommand *Cmd);
Expr readAlign();
void readSort();
Expr readAssert();
@@ -918,6 +927,12 @@
};
}
+void ScriptParser::readAt(OutputSectionCommand *Cmd) {
+ expect("(");
+ Cmd->LmaExpr = readExpr();
+ expect(")");
+}
+
OutputSectionCommand *
ScriptParser::readOutputSectionDescription(StringRef OutSec) {
OutputSectionCommand *Cmd = new OutputSectionCommand(OutSec);
@@ -929,6 +944,9 @@
expect(":");
+ if (skip("AT"))
+ readAt(Cmd);
+
if (skip("ALIGN"))
Cmd->AlignExpr = readAlign();
diff --git a/lld/ELF/LinkerScript.h b/lld/ELF/LinkerScript.h
index d33c12e..4cb9e99 100644
--- a/lld/ELF/LinkerScript.h
+++ b/lld/ELF/LinkerScript.h
@@ -83,6 +83,7 @@
StringRef Name;
Expr AddrExpr;
Expr AlignExpr;
+ Expr LmaExpr;
std::vector<std::unique_ptr<BaseCommand>> Commands;
std::vector<StringRef> Phdrs;
std::vector<uint8_t> Filler;
@@ -147,6 +148,7 @@
bool ignoreInterpSection();
ArrayRef<uint8_t> getFiller(StringRef Name);
+ Expr getLma(StringRef Name);
bool shouldKeep(InputSectionBase<ELFT> *S);
void assignAddresses();
int compareSections(StringRef A, StringRef B);
diff --git a/lld/ELF/Writer.cpp b/lld/ELF/Writer.cpp
index 5a39559..e369981 100644
--- a/lld/ELF/Writer.cpp
+++ b/lld/ELF/Writer.cpp
@@ -963,9 +963,13 @@
if (!needsPtLoad(Sec))
continue;
- // If flags changed then we want new load segment.
+ // Segments are contiguous memory regions that has the same attributes
+ // (e.g. executable or writable). There is one phdr for each segment.
+ // Therefore, we need to create a new phdr when the next section has
+ // different flags or is loaded at a discontiguous address using AT linker
+ // script command.
uintX_t NewFlags = Sec->getPhdrFlags();
- if (Flags != NewFlags) {
+ if (Script<ELFT>::X->getLma(Sec->getName()) || Flags != NewFlags) {
Load = AddHdr(PT_LOAD, NewFlags);
Flags = NewFlags;
}
@@ -1128,7 +1132,11 @@
H.p_align = Target->PageSize;
else if (H.p_type == PT_GNU_RELRO)
H.p_align = 1;
+
H.p_paddr = H.p_vaddr;
+ if (H.p_type == PT_LOAD && First)
+ if (Expr LmaExpr = Script<ELFT>::X->getLma(First->getName()))
+ H.p_paddr = LmaExpr(H.p_vaddr);
// The TLS pointer goes after PT_TLS. At least glibc will align it,
// so round up the size to make sure the offsets are correct.
diff --git a/lld/test/ELF/linkerscript/linkerscript-at.s b/lld/test/ELF/linkerscript/linkerscript-at.s
new file mode 100644
index 0000000..c26461c
--- /dev/null
+++ b/lld/test/ELF/linkerscript/linkerscript-at.s
@@ -0,0 +1,128 @@
+# REQUIRES: x86
+# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %s -o %t
+# RUN: echo "SECTIONS { \
+# RUN: . = 0x1000; \
+# RUN: .aaa : AT(0x2000) \
+# RUN: { \
+# RUN: *(.aaa) \
+# RUN: } \
+# RUN: .bbb : \
+# RUN: { \
+# RUN: *(.bbb) \
+# RUN: } \
+# RUN: .ccc : AT(0x3000) \
+# RUN: { \
+# RUN: *(.ccc) \
+# RUN: } \
+# RUN: .ddd : AT(0x4000) \
+# RUN: { \
+# RUN: *(.ddd) \
+# RUN: } \
+# RUN: }" > %t.script
+# RUN: ld.lld %t --script %t.script -o %t2
+# RUN: llvm-readobj -program-headers %t2 | FileCheck %s
+
+# CHECK: ProgramHeaders [
+# CHECK-NEXT: ProgramHeader {
+# CHECK-NEXT: Type: PT_PHDR
+# CHECK-NEXT: Offset: 0x40
+# CHECK-NEXT: VirtualAddress: 0x40
+# CHECK-NEXT: PhysicalAddress: 0x40
+# CHECK-NEXT: FileSize:
+# CHECK-NEXT: MemSize:
+# CHECK-NEXT: Flags [
+# CHECK-NEXT: PF_R
+# CHECK-NEXT: ]
+# CHECK-NEXT: Alignment: 8
+# CHECK-NEXT: }
+# CHECK-NEXT: ProgramHeader {
+# CHECK-NEXT: Type: PT_LOAD
+# CHECK-NEXT: Offset: 0x0
+# CHECK-NEXT: VirtualAddress: 0x0
+# CHECK-NEXT: PhysicalAddress: 0x0
+# CHECK-NEXT: FileSize:
+# CHECK-NEXT: MemSize:
+# CHECK-NEXT: Flags [
+# CHECK-NEXT: PF_R
+# CHECK-NEXT: ]
+# CHECK-NEXT: Alignment:
+# CHECK-NEXT: }
+# CHECK-NEXT: ProgramHeader {
+# CHECK-NEXT: Type: PT_LOAD
+# CHECK-NEXT: Offset: 0x1000
+# CHECK-NEXT: VirtualAddress: 0x1000
+# CHECK-NEXT: PhysicalAddress: 0x2000
+# CHECK-NEXT: FileSize: 16
+# CHECK-NEXT: MemSize: 16
+# CHECK-NEXT: Flags [
+# CHECK-NEXT: PF_R
+# CHECK-NEXT: ]
+# CHECK-NEXT: Alignment:
+# CHECK-NEXT: }
+# CHECK-NEXT: ProgramHeader {
+# CHECK-NEXT: Type: PT_LOAD
+# CHECK-NEXT: Offset: 0x1010
+# CHECK-NEXT: VirtualAddress: 0x1010
+# CHECK-NEXT: PhysicalAddress: 0x3000
+# CHECK-NEXT: FileSize: 8
+# CHECK-NEXT: MemSize: 8
+# CHECK-NEXT: Flags [
+# CHECK-NEXT: PF_R
+# CHECK-NEXT: ]
+# CHECK-NEXT: Alignment: 4096
+# CHECK-NEXT: }
+# CHECK-NEXT: ProgramHeader {
+# CHECK-NEXT: Type: PT_LOAD
+# CHECK-NEXT: Offset: 0x1018
+# CHECK-NEXT: VirtualAddress: 0x1018
+# CHECK-NEXT: PhysicalAddress: 0x4000
+# CHECK-NEXT: FileSize: 8
+# CHECK-NEXT: MemSize: 8
+# CHECK-NEXT: Flags [
+# CHECK-NEXT: PF_R
+# CHECK-NEXT: ]
+# CHECK-NEXT: Alignment: 4096
+# CHECK-NEXT: }
+# CHECK-NEXT: ProgramHeader {
+# CHECK-NEXT: Type: PT_LOAD
+# CHECK-NEXT: Offset: 0x1020
+# CHECK-NEXT: VirtualAddress: 0x1020
+# CHECK-NEXT: PhysicalAddress: 0x1020
+# CHECK-NEXT: FileSize: 1
+# CHECK-NEXT: MemSize: 1
+# CHECK-NEXT: Flags [
+# CHECK-NEXT: PF_R
+# CHECK-NEXT: PF_X
+# CHECK-NEXT: ]
+# CHECK-NEXT: Alignment: 4096
+# CHECK-NEXT: }
+# CHECK-NEXT: ProgramHeader {
+# CHECK-NEXT: Type: PT_GNU_STACK
+# CHECK-NEXT: Offset:
+# CHECK-NEXT: VirtualAddress: 0x0
+# CHECK-NEXT: PhysicalAddress: 0x0
+# CHECK-NEXT: FileSize:
+# CHECK-NEXT: MemSize:
+# CHECK-NEXT: Flags [
+# CHECK-NEXT: PF_R
+# CHECK-NEXT: PF_W
+# CHECK-NEXT: ]
+# CHECK-NEXT: Alignment: 0
+# CHECK-NEXT: }
+# CHECK-NEXT: ]
+
+.global _start
+_start:
+ nop
+
+.section .aaa, "a"
+.quad 0
+
+.section .bbb, "a"
+.quad 0
+
+.section .ccc, "a"
+.quad 0
+
+.section .ddd, "a"
+.quad 0