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 @@ -719,6 +719,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. @@ -739,11 +744,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) @@ -758,33 +761,80 @@ 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) { + return (Sec->Type & SHT_NOBITS) || areFlagsCompatible(M, Sec); +} + +MemoryRegion *LinkerScript::findLMARegion(OutputSection *Sec) { + // We implement a similar heuristic as the bfd linker + // (https://sourceware.org/binutils/docs-2.30/ld/Output-Section-LMA.html) + + // If this section has an LMARegion set, overwrite the previous LMARegion + if (Sec->LMARegion) + return Sec->LMARegion; + // If this section has an LMAExpr set, find the memory region the address + // falls into + if (Sec->LMAExpr) { + uint64_t Addr = Sec->LMAExpr().getValue(); + MemoryRegion *M = findRegion(Addr); + if (M) + M->CurPos = Addr; + return M; + } + // If a memory region can be found that is compatible with the current + // section, and this region contains at least one section, then the LMA is + // set so the difference between the VMA and LMA is the same as the + // difference between the VMA and LMA of the last section in the located + // region. + // This is only possible if the section has a memory region set and it is + // identical to the memory region of the previous section + if (Sec->MemRegion && Sec->MemRegion == Ctx->MemRegion && + isLMARegionCompatible(Ctx->LMARegion, Sec)) + return Ctx->LMARegion; + return nullptr; +} + // 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) { + // First set LMA and Memory regions before modifying any location counter + Ctx->LMARegion = findLMARegion(Sec); + Ctx->MemRegion = Sec->MemRegion; + if (!(Sec->Flags & SHF_ALLOC)) Dot = 0; else if (Sec->AddrExpr) setDot(Sec->AddrExpr, Sec->Location, false); - 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/at10.test =================================================================== --- /dev/null +++ test/ELF/linkerscript/at10.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 : AT(0x08000000) { *(.sec1) } > RAM + .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 Index: test/ELF/linkerscript/at11.test =================================================================== --- /dev/null +++ test/ELF/linkerscript/at11.test @@ -0,0 +1,23 @@ +# REQUIRES: x86 +# RUN: llvm-mc -filetype=obj -triple=x86_64-pc-linux %p/Inputs/at8.s -o %t.o +# RUN: not ld.lld %t.o --script %s -o %t 2>&1 | FileCheck %s + +MEMORY { + FLASH(rx) : ORIGIN = 0x08000000, LENGTH = 0x100 + RAM(rwx) : ORIGIN = 0x20000000, LENGTH = 0x200 +} + +SECTIONS { + .text : { *(.text) } > FLASH + .sec1 : AT(0x08000000) { *(.sec1) } > RAM + .sec2 : { *(.sec2) } > RAM + .sec3 : { *(.sec3) } > FLASH +} + +# .sec1's load address is explicitly set to the start of FLASH. +# Since .sec1 and .sec2 share the same memory region, they are put +# into the same PT_LOAD header. However, because .sec2's section flags are +# not compatible with FLASH, it is not implicitly put into that LMA +# region, leading to a load address overlap with .sec3. + +# CHECK: error: section .sec2 load address range overlaps with .sec3 Index: test/ELF/linkerscript/at12.test =================================================================== --- /dev/null +++ test/ELF/linkerscript/at12.test @@ -0,0 +1,23 @@ +# REQUIRES: x86 +# RUN: llvm-mc -filetype=obj -triple=x86_64-pc-linux %p/Inputs/at8.s -o %t.o +# RUN: not ld.lld %t.o --script %s -o %t 2>&1 | FileCheck %s + +MEMORY { + FLASH(rx) : ORIGIN = 0x08000000, LENGTH = 0x100 + RAM(rwx) : ORIGIN = 0x20000000, LENGTH = 0x200 +} + +SECTIONS { + .text : { *(.text) } > FLASH + .sec1 : { *(.sec1) } > RAM AT> FLASH + .sec2 : { *(.sec2) } > RAM + .sec3 : { *(.sec3) } > FLASH +} + +# .sec1's LMA region is explicitly set to FLASH. +# Since .sec1 and .sec2 share the same memory region, they are put +# into the same PT_LOAD header. However, because .sec2's section flags are +# not compatible with FLASH, it is not implicitly put into that LMA +# region, leading to a load address overlap with .sec3. + +# CHECK: error: section .sec2 load address range overlaps with .sec3 Index: test/ELF/linkerscript/at9.test =================================================================== --- /dev/null +++ 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