Index: ELF/LinkerScript.h =================================================================== --- ELF/LinkerScript.h +++ ELF/LinkerScript.h @@ -68,6 +68,8 @@ template void assignAddresses(std::vector *> &S); int compareSections(StringRef A, StringRef B); + bool hasLma(StringRef Name) { return LmaExpr.count(Name) > 0; } + uint64_t getLma(uint64_t VA, StringRef SectionName); bool DoLayout = false; @@ -83,6 +85,10 @@ // Section fill attribute for each section. llvm::StringMap> Filler; + // Expressions for each section to calculate LMA, + // used in AT command implementation. + llvm::StringMap> LmaExpr; + // Used to assign addresses to sections. std::vector Commands; Index: ELF/LinkerScript.cpp =================================================================== --- ELF/LinkerScript.cpp +++ ELF/LinkerScript.cpp @@ -247,6 +247,13 @@ return I < J ? -1 : 1; } +uint64_t LinkerScript::getLma(uint64_t VA, StringRef SectionName) { + auto I = LmaExpr.find(SectionName); + if (I == LmaExpr.end()) + return VA; + return evaluate(I->second, 0); +} + // Returns true if S matches T. S can contain glob meta-characters. // The asterisk ('*') matches zero or more characacters, and the question // mark ('?') matches one character. @@ -302,6 +309,7 @@ void readLocationCounterValue(); void readOutputSectionDescription(); void readSectionPatterns(StringRef OutSec, bool Keep); + void readAt(StringRef OutSec); StringSaver Saver; const static StringMap Cmd; @@ -492,11 +500,28 @@ error("error in location counter expression"); } +void ScriptParser::readAt(StringRef OutSec) { + std::vector &Expr = Script->LmaExpr[OutSec]; + expect("AT"); + expect("("); + while (!Error) { + StringRef Tok = next(); + if (Tok == ")") + break; + Expr.push_back(Tok); + } + if (Expr.empty()) + setError("empty AT command expression"); +} + void ScriptParser::readOutputSectionDescription() { StringRef OutSec = next(); Script->SectionOrder.push_back(OutSec); Script->Commands.push_back({SectionKind, {}, OutSec}); expect(":"); + if (peek() == "AT") + readAt(OutSec); + expect("{"); while (!Error && !skip("}")) { StringRef Tok = next(); Index: ELF/Writer.cpp =================================================================== --- ELF/Writer.cpp +++ ELF/Writer.cpp @@ -1464,9 +1464,11 @@ if (!needsPtLoad(Sec)) continue; - // If flags changed then we want new load segment. + bool HasLma = Script->hasLma(Sec->getName()); + // If AT linker script command was used for section or if flags were + // changed, then we want new load segment. uintX_t NewFlags = toPhdrFlags(Sec->getFlags()); - if (Flags != NewFlags) { + if (HasLma || Flags != NewFlags) { Load = AddHdr(PT_LOAD, NewFlags); Flags = NewFlags; } @@ -1611,8 +1613,13 @@ H.p_align = Target->PageSize; else if (H.p_type == PT_GNU_RELRO) H.p_align = 1; - H.p_paddr = H.p_vaddr; + // Now set the segment's physical address. + if (H.p_type == PT_LOAD && First) + H.p_paddr = Script->getLma(H.p_vaddr, First->getName()); + else + H.p_paddr = 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. if (H.p_type == PT_TLS) { Index: test/ELF/linkerscript-at.s =================================================================== --- test/ELF/linkerscript-at.s +++ test/ELF/linkerscript-at.s @@ -0,0 +1,138 @@ +# 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: *(.ccc) \ +# 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: 0x1C8 +# 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: 0x1D8 +# 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: 0x1E0 +# 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: 0x1E8 +# 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: ] + +# RUN: echo "SECTIONS { \ +# RUN: .aaa : AT() \ +# RUN: { \ +# RUN: *(.aaa) \ +# RUN: } \ +# RUN: }" > %t.script +# RUN: not ld.lld %t --script %t.script -o %t2 2>&1 | \ +# RUN: FileCheck --check-prefix=ERREXPR %s +# ERREXPR: empty AT command expression + +.global _start +_start: + nop + +.section .aaa, "a" +.quad 0 + +.section .bbb, "a" +.quad 0 + +.section .ccc, "a" +.quad 0 + +.section .ddd, "a" +.quad 0