Index: ELF/LinkerScript.h =================================================================== --- ELF/LinkerScript.h +++ ELF/LinkerScript.h @@ -70,6 +70,7 @@ static bool classof(const BaseCommand *C); StringRef Name; std::vector> Commands; + std::vector LmaExpr; std::vector Phdrs; std::vector Filler; ConstraintKind Constraint = NoConstraint; @@ -119,6 +120,9 @@ createSections(OutputSectionFactory &Factory); ArrayRef getFiller(StringRef Name); + uintX_t getLma(StringRef Name, uintX_t VA); + bool hasLma(StringRef Name); + bool isDiscarded(InputSectionBase *S); bool shouldKeep(InputSectionBase *S); void assignAddresses(ArrayRef *> S); @@ -134,6 +138,7 @@ int getSectionIndex(StringRef Name); std::vector getPhdrIndicesForSection(StringRef Name); void dispatchAssignment(SymbolAssignment *Cmd); + OutputSectionCommand *getOutputSection(StringRef Name); uintX_t Dot; }; Index: ELF/LinkerScript.cpp =================================================================== --- ELF/LinkerScript.cpp +++ ELF/LinkerScript.cpp @@ -499,6 +499,31 @@ return {}; } +template +bool LinkerScript::hasLma(StringRef Name) { + if (OutputSectionCommand *Cmd = getOutputSection(Name)) + return !Cmd->LmaExpr.empty(); + return nullptr; +} + +template +typename ELFT::uint LinkerScript::getLma(StringRef Name, uintX_t VA) { + if (OutputSectionCommand *Cmd = getOutputSection(Name)) + if (!Cmd->LmaExpr.empty()) + return evalExpr(Cmd->LmaExpr, VA); + assert(false && "no LMA for section"); + return (uintX_t)-1; +} + +template +OutputSectionCommand *LinkerScript::getOutputSection(StringRef Name) { + for (const std::unique_ptr &Base : Opt.Commands) + if (auto *Cmd = dyn_cast(Base.get())) + if (Cmd->Name == Name) + return Cmd; + return nullptr; +} + // 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, @@ -599,6 +624,7 @@ void readLocationCounterValue(); void readOutputSectionDescription(StringRef OutSec); + std::vector readAt(); std::vector readOutputSectionPhdrs(); unsigned readPhdrType(); void readProvide(bool Hidden); @@ -820,11 +846,29 @@ Opt.Commands.push_back(llvm::make_unique(".", Expr)); } +std::vector ScriptParser::readAt() { + std::vector Expr; + expect("AT"); + expect("("); + while (!Error) { + StringRef Tok = next(); + if (Tok == ")") + break; + Expr.push_back(Tok); + } + if (Expr.empty()) + setError("empty AT command expression"); + return Expr; +} + void ScriptParser::readOutputSectionDescription(StringRef OutSec) { OutputSectionCommand *Cmd = new OutputSectionCommand(OutSec); Opt.Commands.emplace_back(Cmd); expect(":"); + if (peek() == "AT") + Cmd->LmaExpr = readAt(); + // Parse constraints. if (skip("ONLY_IF_RO")) Cmd->Constraint = ReadOnly; Index: ELF/Writer.cpp =================================================================== --- ELF/Writer.cpp +++ ELF/Writer.cpp @@ -987,9 +987,12 @@ 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 = toPhdrFlags(Sec->getFlags()); - if (Flags != NewFlags) { + if (Script::X->hasLma(Sec->getName()) || Flags != NewFlags) { Load = AddHdr(PT_LOAD, NewFlags); Flags = NewFlags; } @@ -1153,7 +1156,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 && + Script::X->hasLma(First->getName())) + H.p_paddr = Script::X->getLma(First->getName(), H.p_vaddr); + 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: *(.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: ] + +# 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