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 hasPA(StringRef Name) { return PhysAddrExpr.count(Name) > 0; } + uint64_t getPA(uint64_t VA, StringRef Name); bool DoLayout = false; @@ -84,6 +86,10 @@ // Section fill attribute for each section. llvm::StringMap> Filler; + // Expressions for each section to calculate physical address, + // used in AT command implementation. + llvm::StringMap> PhysAddrExpr; + // Used to assign addresses to sections. std::vector Commands; Index: ELF/LinkerScript.cpp =================================================================== --- ELF/LinkerScript.cpp +++ ELF/LinkerScript.cpp @@ -167,6 +167,13 @@ return I < J ? -1 : 1; } +uint64_t LinkerScript::getPA(uint64_t VA, StringRef Name) { + auto I = PhysAddrExpr.find(Name); + if (I == PhysAddrExpr.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. @@ -222,6 +229,7 @@ void readLocationCounterValue(); void readOutputSectionDescription(); void readSectionPatterns(StringRef OutSec, bool Keep); + void readCommandAt(StringRef OutSec); StringSaver Saver; const static StringMap Cmd; @@ -412,11 +420,29 @@ error("error in location counter expression"); } +void ScriptParser::readCommandAt(StringRef OutSec) { + std::vector &Expr = Script->PhysAddrExpr[OutSec]; + expect("AT"); + expect("("); + while (!Error) { + StringRef Tok = next(); + if (Tok == ")") + break; + Expr.push_back(Tok); + } + if (Expr.empty()) + error("empty AT command expression"); +} + void ScriptParser::readOutputSectionDescription() { StringRef OutSec = next(); Script->SectionOrder.push_back(OutSec); Script->Commands.push_back({SectionKind, {}, OutSec}); expect(":"); + StringRef Tok = peek(); + if (Tok == "AT") + readCommandAt(OutSec); + expect("{"); while (!Error && !skip("}")) { StringRef Tok = next(); @@ -431,7 +457,7 @@ setError("unknown command " + Tok); } } - StringRef Tok = peek(); + Tok = peek(); if (Tok.startswith("=")) { if (!Tok.startswith("=0x")) { setError("filler should be a hexadecimal value"); Index: ELF/Writer.cpp =================================================================== --- ELF/Writer.cpp +++ ELF/Writer.cpp @@ -1461,9 +1461,11 @@ if (!needsPtLoad(Sec)) continue; - // If flags changed then we want new load segment. + bool HasPA = Script->hasPA(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 (HasPA || Flags != NewFlags) { Load = AddHdr(PT_LOAD, NewFlags); Flags = NewFlags; } @@ -1608,7 +1610,12 @@ 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->getPA(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. 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