Index: ELF/LinkerScript.h =================================================================== --- ELF/LinkerScript.h +++ ELF/LinkerScript.h @@ -220,7 +220,8 @@ std::vector getPhdrIndices(OutputSection *Sec); - MemoryRegion *findMemoryRegion(OutputSection *Sec); + MemoryRegion *findMemoryRegion(llvm::StringRef Name); + MemoryRegion *pickMemoryRegion(OutputSection *Sec); void switchTo(OutputSection *Sec); uint64_t advance(uint64_t Size, unsigned Align); Index: ELF/LinkerScript.cpp =================================================================== --- ELF/LinkerScript.cpp +++ ELF/LinkerScript.cpp @@ -617,19 +617,26 @@ Ctx->OutSec->LMAOffset = Ctx->LMAOffset(); } -// This function searches for a memory region to place the given output -// section in. If found, a pointer to the appropriate memory region is -// returned. Otherwise, a nullptr is returned. -MemoryRegion *LinkerScript::findMemoryRegion(OutputSection *Sec) { +// Function finds memory region by given name. +MemoryRegion *LinkerScript::findMemoryRegion(StringRef Name) { + if (Name.empty()) + return nullptr; + auto It = MemoryRegions.find(Name); + if (It != MemoryRegions.end()) + return It->second; + error("memory region '" + Name + "' not declared"); + return nullptr; +} + +// This function tries to pick up a memory region to place the given +// output section in. At first it searches region by name, then by section +// flags. Returns pointer to the appropriate memory region or nullptr if no +// region was found. +MemoryRegion *LinkerScript::pickMemoryRegion(OutputSection *Sec) { // If a memory region name was specified in the output section command, // then try to find that region first. - if (!Sec->MemoryRegionName.empty()) { - auto It = MemoryRegions.find(Sec->MemoryRegionName); - if (It != MemoryRegions.end()) - return It->second; - error("memory region '" + Sec->MemoryRegionName + "' not declared"); - return nullptr; - } + if (MemoryRegion *MR = findMemoryRegion(Sec->MemoryRegionName)) + return MR; // If at least one memory region is defined, all sections must // belong to some memory region. Otherwise, we don't need to do @@ -666,6 +673,10 @@ uint64_t D = Dot; Ctx->LMAOffset = [=] { return Sec->LMAExpr().getValue() - D; }; } + if (Sec->LMARegion) { + uint64_t D = Dot; + Ctx->LMAOffset = [=] { return Sec->LMARegion->Origin - D; }; + } switchTo(Sec); @@ -793,7 +804,10 @@ if (auto *Sec = dyn_cast(Base)) { if (!Sec->Live) continue; - Sec->MemRegion = findMemoryRegion(Sec); + + Sec->MemRegion = pickMemoryRegion(Sec); + Sec->LMARegion = findMemoryRegion(Sec->LMARegionName); + // Handle align (e.g. ".foo : ALIGN(16) { ... }"). if (Sec->AlignExpr) Sec->Alignment = Index: ELF/OutputSections.h =================================================================== --- ELF/OutputSections.h +++ ELF/OutputSections.h @@ -89,6 +89,7 @@ // The following members are normally only used in linker scripts. MemoryRegion *MemRegion = nullptr; + MemoryRegion *LMARegion = nullptr; Expr AddrExpr; Expr AlignExpr; Expr LMAExpr; @@ -99,6 +100,7 @@ ConstraintKind Constraint = ConstraintKind::NoConstraint; std::string Location; std::string MemoryRegionName; + std::string LMARegionName; bool Noload = false; template void finalize(); Index: ELF/ScriptParser.cpp =================================================================== --- ELF/ScriptParser.cpp +++ ELF/ScriptParser.cpp @@ -708,6 +708,14 @@ if (consume(">")) Cmd->MemoryRegionName = next(); + if (consume("AT")) { + expect(">"); + Cmd->LMARegionName = next(); + } + + if (Cmd->LMAExpr && !Cmd->LMARegionName.empty()) + error("section can't have both LMA and a load region"); + Cmd->Phdrs = readOutputSectionPhdrs(); if (consume("=")) Index: test/ELF/linkerscript/at2.s =================================================================== --- test/ELF/linkerscript/at2.s +++ test/ELF/linkerscript/at2.s @@ -0,0 +1,68 @@ +# REQUIRES: x86 +# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %s -o %t +# RUN: echo "MEMORY { \ +# RUN: FLASH (ax) : ORIGIN = 0x2000, LENGTH = 0x100 \ +# RUN: RAM (aw) : ORIGIN = 0x5000, LENGTH = 0x100 } \ +# RUN: SECTIONS { \ +# RUN: .foo1 : { *(.foo1) } > FLASH AT>FLASH \ +# RUN: .foo2 : { *(.foo2) } > FLASH \ +# RUN: .bar1 : { *(.bar1) } > RAM AT> RAM \ +# RUN: .bar2 : { *(.bar2) } > RAM AT > RAM \ +# RUN: .bar3 : { *(.bar3) } > RAM AT >RAM \ +# RUN: }" > %t.script +# RUN: ld.lld %t --script %t.script -o %t2 +# RUN: llvm-readobj -program-headers %t2 | FileCheck %s + +# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %s -o %t +# RUN: echo "MEMORY { \ +# RUN: FLASH (ax) : ORIGIN = 0x2000, LENGTH = 0x100 \ +# RUN: RAM (aw) : ORIGIN = 0x5000, LENGTH = 0x100 } \ +# RUN: SECTIONS { \ +# RUN: .foo1 : AT(0x500) { *(.foo1) } > FLASH AT>FLASH \ +# RUN: }" > %t2.script +# RUN: not ld.lld %t --script %t2.script -o %t2 2>&1 | \ +# RUN: FileCheck %s --check-prefix=ERR +# ERR: error: section can't have both LMA and a load region + +# CHECK: ProgramHeaders [ +# CHECK-NEXT: ProgramHeader { +# CHECK-NEXT: Type: PT_LOAD +# CHECK-NEXT: Offset: 0x1000 +# CHECK-NEXT: VirtualAddress: 0x2000 +# CHECK-NEXT: PhysicalAddress: 0x2000 +# CHECK-NEXT: FileSize: +# CHECK-NEXT: MemSize: +# CHECK-NEXT: Flags [ +# CHECK-NEXT: PF_R +# CHECK-NEXT: PF_X +# CHECK-NEXT: ] +# CHECK-NEXT: Alignment: +# CHECK-NEXT: } +# CHECK-NEXT: ProgramHeader { +# CHECK-NEXT: Type: PT_LOAD +# CHECK-NEXT: Offset: 0x2000 +# CHECK-NEXT: VirtualAddress: 0x5000 +# CHECK-NEXT: PhysicalAddress: 0x5000 +# CHECK-NEXT: FileSize: +# CHECK-NEXT: MemSize: +# CHECK-NEXT: Flags [ +# CHECK-NEXT: PF_R +# CHECK-NEXT: PF_W +# CHECK-NEXT: ] +# CHECK-NEXT: Alignment: 4096 +# CHECK-NEXT: } + +.section .foo1, "ax" +.quad 0 + +.section .foo2, "ax" +.quad 0 + +.section .bar1, "aw" +.quad 0 + +.section .bar2, "aw" +.quad 0 + +.section .bar3, "aw" +.quad 0