Index: ELF/LinkerScript.h =================================================================== --- ELF/LinkerScript.h +++ ELF/LinkerScript.h @@ -239,6 +239,8 @@ uint64_t advance(uint64_t Size, unsigned Align); void output(InputSection *Sec); + MemoryRegion *findLMARegion(OutputSection *Sec); + void moveTo(uint64_t Addr); 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,90 @@ 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; +} + +void LinkerScript::moveTo(uint64_t Addr) { + if (Ctx->MemRegion) + Ctx->MemRegion->CurPos = Addr; + Dot = Addr; +} + // 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); + if (Ctx->LMARegion && !areFlagsCompatible(Ctx->LMARegion, Sec)) + warn("requested to locate " + Sec->Name + " into LMA region with" + " incompatible flags (" + Ctx->LMARegion->Name + ")"); + Ctx->MemRegion = Sec->MemRegion; + if (!(Sec->Flags & SHF_ALLOC)) Dot = 0; else if (Sec->AddrExpr) - setDot(Sec->AddrExpr, Sec->Location, false); + moveTo(Sec->AddrExpr().getValue()); - 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,18 @@ +# 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 +} + +# CHECK: warning: requested to locate .sec1 into LMA region with incompatible flags (FLASH) +# 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,18 @@ +# 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 +} + +# CHECK: warning: requested to locate .sec1 into LMA region with incompatible flags (FLASH) +# 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 Index: test/ELF/linkerscript/vma.test =================================================================== --- /dev/null +++ test/ELF/linkerscript/vma.test @@ -0,0 +1,33 @@ +# REQUIRES: x86 +# RUN: echo '.global _start;.text; _start: nop; .rodata; .quad 1; .data; .quad 2;' \ +# RUN: | llvm-mc -filetype=obj -triple=x86_64-unknown-linux - -o %t.o +# RUN: ld.lld %t.o --script %s -o %t +# RUN: llvm-readelf -sections -program-headers %t | FileCheck %s + +MEMORY +{ + FLASH : ORIGIN = 0x20400000, LENGTH = 512M + RAM : ORIGIN = 0x80000000, LENGTH = 16K +} +SECTIONS +{ + .text : { *(.text); } > FLASH + .rodata : { *(.rodata); } > FLASH + PROVIDE(_sbss = ORIGIN(RAM)); + .bss _sbss : { *(.bss); } > RAM + .data : AT(LOADADDR(.rodata) + SIZEOF(.rodata)) + { + *(.data); + } > RAM +} + +# CHECK: Name Type Address Off +# CHECK: .text PROGBITS 0000000020400000 001000 +# CHECK: .rodata PROGBITS 0000000020400001 001001 +# CHECK: .bss NOBITS 0000000080000000 002000 +# CHECK: .data PROGBITS 0000000080000000 002000 + +# CHECK: Program Headers: +# CHECK: Type Offset VirtAddr PhysAddr +# CHECK-NEXT: LOAD 0x001000 0x0000000020400000 0x0000000020400000 +# CHECK-NEXT: LOAD 0x002000 0x0000000080000000 0x0000000020400009