From 8ceadb38a88fa5a71c8c325f420ba3dfd073ce3c Mon Sep 17 00:00:00 2001 From: George Rimar Date: Wed, 17 Aug 2016 07:44:19 +0000 Subject: [PATCH] [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 --- lld/ELF/LinkerScript.cpp | 18 ++++ lld/ELF/LinkerScript.h | 2 + lld/ELF/Writer.cpp | 12 ++- lld/test/ELF/linkerscript/linkerscript-at.s | 128 ++++++++++++++++++++++++++++ 4 files changed, 158 insertions(+), 2 deletions(-) create mode 100644 lld/test/ELF/linkerscript/linkerscript-at.s 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 @@ ArrayRef LinkerScript::getFiller(StringRef Name) { return {}; } +template typename Expr LinkerScript::getLma(StringRef Name) { + for (const std::unique_ptr &Base : Opt.Commands) + if (auto *Cmd = dyn_cast(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 @@ private: 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 @@ Expr ScriptParser::readAssert() { }; } +void ScriptParser::readAt(OutputSectionCommand *Cmd) { + expect("("); + Cmd->LmaExpr = readExpr(); + expect(")"); +} + OutputSectionCommand * ScriptParser::readOutputSectionDescription(StringRef OutSec) { OutputSectionCommand *Cmd = new OutputSectionCommand(OutSec); @@ -929,6 +944,9 @@ ScriptParser::readOutputSectionDescription(StringRef OutSec) { 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 @@ struct OutputSectionCommand : BaseCommand { StringRef Name; Expr AddrExpr; Expr AlignExpr; + Expr LmaExpr; std::vector> Commands; std::vector Phdrs; std::vector Filler; @@ -147,6 +148,7 @@ public: bool ignoreInterpSection(); ArrayRef getFiller(StringRef Name); + Expr getLma(StringRef Name); bool shouldKeep(InputSectionBase *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 @@ std::vector> Writer::createPhdrs() { 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::X->getLma(Sec->getName()) || Flags != NewFlags) { Load = AddHdr(PT_LOAD, NewFlags); Flags = NewFlags; } @@ -1128,7 +1132,11 @@ template void Writer::setPhdrs() { 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::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 -- 2.7.4