Index: ELF/LinkerScript.h =================================================================== --- ELF/LinkerScript.h +++ ELF/LinkerScript.h @@ -239,6 +239,7 @@ uint64_t advance(uint64_t Size, unsigned Align); void output(InputSection *Sec); + MemoryRegion *findLMARegion(OutputSection *Sec); void assignOffsets(OutputSection *Sec); // Ctx captures the local AddressState and makes it accessible Index: ELF/LinkerScript.cpp =================================================================== --- ELF/LinkerScript.cpp +++ ELF/LinkerScript.cpp @@ -720,6 +720,11 @@ expandMemoryRegions(Ctx->OutSec->Addr - Before); } +static bool areFlagsCompatible(MemoryRegion *M, OutputSection *Sec) { + return (M && (!M->Flags || (M->Flags & Sec->Flags)) && + ((M->NegFlags & Sec->Flags) == 0)); +} + // 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. @@ -740,11 +745,9 @@ return nullptr; // See if a region can be found by matching section flags. - for (auto &Pair : MemoryRegions) { - MemoryRegion *M = Pair.second; - if ((M->Flags & Sec->Flags) && (M->NegFlags & Sec->Flags) == 0) - return M; - } + for (auto &Pair : MemoryRegions) + if (areFlagsCompatible(Pair.second, Sec)) + return Pair.second; // Otherwise, no suitable region was found. if (Sec->Flags & SHF_ALLOC) @@ -759,6 +762,37 @@ return nullptr; } +static MemoryRegion *findRegion(uint64_t Addr) { + for (auto &Pair : Script->MemoryRegions) { + MemoryRegion *M = Pair.second; + if (M->Origin <= Addr && Addr < M->Origin + M->Length) + return M; + } + return nullptr; +} + +static bool isLMARegionCompatible(MemoryRegion *M, OutputSection *Sec) { + if (!(Sec->Flags & SHF_ALLOC)) + return false; + return (Sec->Type & SHT_NOBITS) || areFlagsCompatible(M, Sec); +} + +MemoryRegion *LinkerScript::findLMARegion(OutputSection *Sec) { + if (Sec->LMAExpr) { + uint64_t Addr = Sec->LMAExpr().getValue(); + MemoryRegion *MR = findRegion(Addr); + if (MR) + MR->CurPos = Addr; + return MR; + } + + if (Sec->AddrExpr || !Sec->MemRegion || Sec->MemRegion != Ctx->MemRegion || + !isLMARegionCompatible(Ctx->LMARegion, Sec)) + return nullptr; + + return Ctx->LMARegion; +} + // This function assigns offsets to input sections and an output section // for a single sections command (e.g. ".text { *(.text); }"). void LinkerScript::assignOffsets(OutputSection *Sec) { @@ -767,25 +801,33 @@ else if (Sec->AddrExpr) setDot(Sec->AddrExpr, Sec->Location, false); + if (Sec->LMARegion) + Ctx->LMARegion = Sec->LMARegion; + else + Ctx->LMARegion = findLMARegion(Sec); + Ctx->MemRegion = Sec->MemRegion; - Ctx->LMARegion = Sec->LMARegion; + if (Ctx->MemRegion) Dot = Ctx->MemRegion->CurPos; switchTo(Sec); - if (Sec->LMAExpr) - Ctx->LMAOffset = Sec->LMAExpr().getValue() - Dot; - - if (MemoryRegion *MR = Sec->LMARegion) + // Compute the LMAOffset + // If we have an LMARegion set, use it + if (MemoryRegion *MR = Ctx->LMARegion) Ctx->LMAOffset = MR->CurPos - Dot; + // If the LMAExpr is set but we could not find an LMARegion previously, + // this means we do not have any MEMORY commands and need to evaluate + // the expression again. + else if (Sec->LMAExpr) + Ctx->LMAOffset = Sec->LMAExpr().getValue() - Dot; + // In all other cases, VMA = LMA + else + Ctx->LMAOffset = 0; - // If neither AT nor AT> is specified for an allocatable section, the linker - // will set the LMA such that the difference between VMA and LMA for the - // section is the same as the preceding output section in the same region - // https://sourceware.org/binutils/docs-2.20/ld/Output-Section-LMA.html - // This, however, should only be done by the first "non-header" section - // in the segment. + // Only the first "non-header" section of each program header should set the + // LMAOffset if (PhdrEntry *L = Ctx->OutSec->PtLoad) if (Sec == findFirstSection(L)) L->LMAOffset = Ctx->LMAOffset; Index: test/ELF/linkerscript/at9.test =================================================================== --- test/ELF/linkerscript/at9.test +++ test/ELF/linkerscript/at9.test @@ -0,0 +1,33 @@ +# REQUIRES: x86 +# RUN: llvm-mc -filetype=obj -triple=x86_64-pc-linux %p/Inputs/at8.s -o %t.o +# RUN: ld.lld %t.o --script %s -o %t +# RUN: llvm-readelf -sections -program-headers %t | FileCheck %s + +MEMORY { + FLASH : ORIGIN = 0x08000000, LENGTH = 0x100 + RAM : ORIGIN = 0x20000000, LENGTH = 0x200 +} + +SECTIONS { + .text : { *(.text) } > FLASH + .sec1 : { *(.sec1) } > RAM AT > FLASH + .sec2 : { *(.sec2) } > RAM + .sec3 : { *(.sec3) } > FLASH +} + +# Make sure we increase the implicit LMA region of .sec2. +# Otherwise, .sec3 will receive a huge load address because of +# unsigned underflow in the calculation of the offset to its +# virtual address. + +# CHECK: Name Type Address Off +# CHECK: .text PROGBITS 0000000008000000 001000 +# CHECK: .sec1 PROGBITS 0000000020000000 001000 +# CHECK: .sec2 PROGBITS 0000000020000008 001008 +# CHECK: .sec3 PROGBITS 0000000008000010 001010 + +# CHECK: Program Headers: +# CHECK: Type Offset VirtAddr PhysAddr +# CHECK-NEXT: LOAD 0x001000 0x0000000020000000 0x0000000008000000 +# CHECK-NEXT: LOAD 0x001010 0x0000000008000010 0x0000000008000010 +# CHECK-NOT: LOAD