From 2d7add6e2e56baf46504a8a22dec42b61f63360f Mon Sep 17 00:00:00 2001 From: Daniel Thornburgh Date: Mon, 31 Mar 2025 10:44:40 -0700 Subject: [PATCH] [LLD][ELF] Allow memory region in OVERLAY (#133540) This allows the contents of OVERLAYs to be attributed to memory regions. This is the only clean way to overlap VMAs in linker scripts that choose to primarily use memory regions to lay out addresses. This also simplifies OVERLAY expansion to better match GNU LD. Expressions for the first section's LMA and VMA are not generated if the user did not provide them. This allows the LMA/VMA offset to be preserved across multiple overlays in the same region, as with regular sections. Closes #129816 --- lld/ELF/LinkerScript.cpp | 15 +++++++++++- lld/ELF/LinkerScript.h | 1 + lld/ELF/OutputSections.h | 1 + lld/ELF/ScriptParser.cpp | 23 ++++++++++-------- lld/test/ELF/linkerscript/overlay.test | 33 ++++++++++++++++++++++++++ 5 files changed, 62 insertions(+), 11 deletions(-) diff --git a/lld/ELF/LinkerScript.cpp b/lld/ELF/LinkerScript.cpp index e19823f2ea75..8149513d821e 100644 --- a/lld/ELF/LinkerScript.cpp +++ b/lld/ELF/LinkerScript.cpp @@ -182,7 +182,18 @@ void LinkerScript::expandMemoryRegions(uint64_t size) { void LinkerScript::expandOutputSection(uint64_t size) { state->outSec->size += size; - expandMemoryRegions(size); + size_t regionSize = size; + if (state->outSec->inOverlay) { + // Expand the overlay if necessary, and expand the region by the + // corresponding amount. + if (state->outSec->size > state->overlaySize) { + regionSize = state->outSec->size - state->overlaySize; + state->overlaySize = state->outSec->size; + } else { + regionSize = 0; + } + } + expandMemoryRegions(regionSize); } void LinkerScript::setDot(Expr e, const Twine &loc, bool inSec) { @@ -1218,6 +1229,8 @@ bool LinkerScript::assignOffsets(OutputSection *sec) { // We can call this method multiple times during the creation of // thunks and want to start over calculation each time. sec->size = 0; + if (sec->firstInOverlay) + state->overlaySize = 0; // We visited SectionsCommands from processSectionCommands to // layout sections. Now, we visit SectionsCommands again to fix diff --git a/lld/ELF/LinkerScript.h b/lld/ELF/LinkerScript.h index 0a2dda13f4ef..80c4f564afab 100644 --- a/lld/ELF/LinkerScript.h +++ b/lld/ELF/LinkerScript.h @@ -311,6 +311,7 @@ class LinkerScript final { MemoryRegion *lmaRegion = nullptr; uint64_t lmaOffset = 0; uint64_t tbssAddr = 0; + uint64_t overlaySize; }; Ctx &ctx; diff --git a/lld/ELF/OutputSections.h b/lld/ELF/OutputSections.h index 3ab36a21ce48..77654f85ca78 100644 --- a/lld/ELF/OutputSections.h +++ b/lld/ELF/OutputSections.h @@ -102,6 +102,7 @@ public: bool expressionsUseSymbols = false; bool usedInExpression = false; bool inOverlay = false; + bool firstInOverlay = false; // Tracks whether the section has ever had an input section added to it, even // if the section was later removed (e.g. because it is a synthetic section diff --git a/lld/ELF/ScriptParser.cpp b/lld/ELF/ScriptParser.cpp index 4c52bfda7a70..4345b7bac117 100644 --- a/lld/ELF/ScriptParser.cpp +++ b/lld/ELF/ScriptParser.cpp @@ -561,37 +561,40 @@ void ScriptParser::readSearchDir() { // https://sourceware.org/binutils/docs/ld/Overlay-Description.html#Overlay-Description SmallVector ScriptParser::readOverlay() { Expr addrExpr; - if (consume(":")) { - addrExpr = [s = ctx.script] { return s->getDot(); }; - } else { + if (!consume(":")) { addrExpr = readExpr(); expect(":"); } - // When AT is omitted, LMA should equal VMA. script->getDot() when evaluating - // lmaExpr will ensure this, even if the start address is specified. - Expr lmaExpr = consume("AT") ? readParenExpr() - : [s = ctx.script] { return s->getDot(); }; + Expr lmaExpr = consume("AT") ? readParenExpr() : Expr{}; expect("{"); SmallVector v; OutputSection *prev = nullptr; while (!errCount(ctx) && !consume("}")) { // VA is the same for all sections. The LMAs are consecutive in memory - // starting from the base load address specified. + // starting from the base load address. OutputDesc *osd = readOverlaySectionDescription(); osd->osec.addrExpr = addrExpr; if (prev) { osd->osec.lmaExpr = [=] { return prev->getLMA() + prev->size; }; } else { osd->osec.lmaExpr = lmaExpr; - // Use first section address for subsequent sections as initial addrExpr - // can be DOT. Ensure the first section, even if empty, is not discarded. + // Use first section address for subsequent sections. Ensure the first + // section, even if empty, is not discarded. osd->osec.usedInExpression = true; addrExpr = [=]() -> ExprValue { return {&osd->osec, false, 0, ""}; }; } v.push_back(osd); prev = &osd->osec; } + if (!v.empty()) + static_cast(v.front())->osec.firstInOverlay = true; + if (consume(">")) { + StringRef regionName = readName(); + for (SectionCommand *od : v) + static_cast(od)->osec.memoryRegionName = + std::string(regionName); + } // According to the specification, at the end of the overlay, the location // counter should be equal to the overlay base address plus size of the diff --git a/lld/test/ELF/linkerscript/overlay.test b/lld/test/ELF/linkerscript/overlay.test index 7c64303b4565..e230134ad554 100644 --- a/lld/test/ELF/linkerscript/overlay.test +++ b/lld/test/ELF/linkerscript/overlay.test @@ -41,6 +41,23 @@ # ERR2-NEXT:>>> .out.aaa { *(.aaa) } > AX AT>FLASH # ERR2-NEXT:>>> ^ +# RUN: ld.lld a.o -T region.t -o region +# RUN: llvm-readelf --sections -l region | FileCheck --check-prefix=REGION %s + +# REGION: Name Type Address Off Size +# REGION: .big1 PROGBITS 0000000000001000 001000 000008 +# REGION-NEXT: .small1 PROGBITS 0000000000001000 002000 000004 +# REGION: .big2 PROGBITS 0000000000001008 002008 000008 +# REGION-NEXT: .small2 PROGBITS 0000000000001008 003008 000004 +# REGION-NEXT: .text PROGBITS 0000000000001010 003010 000001 + +# REGION: Program Headers: +# REGION: Type Offset VirtAddr PhysAddr FileSiz MemSiz Flg Align +# REGION-NEXT: LOAD 0x001000 0x0000000000001000 0x0000000000001000 0x000008 0x000008 R 0x1000 +# REGION-NEXT: LOAD 0x002000 0x0000000000001000 0x0000000000001008 0x000010 0x000010 R 0x1000 +# REGION-NEXT: LOAD 0x003008 0x0000000000001008 0x0000000000001018 0x000004 0x000004 R 0x1000 +# REGION-NEXT: LOAD 0x003010 0x0000000000001010 0x0000000000001020 0x000001 0x000001 R E 0x1000 + #--- a.s .globl _start _start: @@ -76,6 +93,22 @@ SECTIONS { .text : { *(.text) } } +#--- region.t +MEMORY { region : ORIGIN = 0x1000, LENGTH = 0x1000 } +SECTIONS { +## Memory region instead of explicit address. + OVERLAY : { + .big1 { *(.big1) } + .small1 { *(.small1) } + } >region + OVERLAY : { + .big2 { *(.big2) } + .small2 { *(.small2) } + } >region + .text : { *(.text) } >region + /DISCARD/ : { *(.big* .small*) } +} + #--- err1.t SECTIONS { OVERLAY 0x1000 : AT ( 0x2000 ) {