Skip to content

Commit 7b6a89f

Browse files
authored
[ELF] Detect convergence of output section addresses
Some linker scripts don't converge. https://reviews.llvm.org/D66279 ("[ELF] Make LinkerScript::assignAddresses iterative") detected convergence of symbol assignments. This patch detects convergence of output section addresses. While input sections might also have convergence issues, they are less common as expressions that could cause convergence issues typically involve output sections and symbol assignments. GNU ld has an error `non constant or forward reference address expression for section` that correctly rejects ``` SECTIONS { .text ADDR(.data)+0x1000 : { *(.text) } .data : { *(.data) } } ``` but not the following variant: ``` SECTIONS { .text foo : { *(.text) } .data : { *(.data) } foo = ADDR(.data)+0x1000; } ``` Our approach consistently rejects both cases. Link: https://discourse.llvm.org/t/lld-and-layout-convergence/79232 Pull Request: #93888
1 parent 98d5d34 commit 7b6a89f

File tree

5 files changed

+65
-16
lines changed

5 files changed

+65
-16
lines changed

lld/ELF/LinkerScript.cpp

Lines changed: 14 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1025,13 +1025,14 @@ static OutputSection *findFirstSection(PhdrEntry *load) {
10251025
return nullptr;
10261026
}
10271027

1028-
// This function assigns offsets to input sections and an output section
1029-
// for a single sections command (e.g. ".text { *(.text); }").
1030-
void LinkerScript::assignOffsets(OutputSection *sec) {
1028+
// Assign addresses to an output section and offsets to its input sections and
1029+
// symbol assignments. Return true if the output section's address has changed.
1030+
bool LinkerScript::assignOffsets(OutputSection *sec) {
10311031
const bool isTbss = (sec->flags & SHF_TLS) && sec->type == SHT_NOBITS;
10321032
const bool sameMemRegion = state->memRegion == sec->memRegion;
10331033
const bool prevLMARegionIsDefault = state->lmaRegion == nullptr;
10341034
const uint64_t savedDot = dot;
1035+
bool addressChanged = false;
10351036
state->memRegion = sec->memRegion;
10361037
state->lmaRegion = sec->lmaRegion;
10371038

@@ -1068,6 +1069,7 @@ void LinkerScript::assignOffsets(OutputSection *sec) {
10681069
dot = alignToPowerOf2(dot, sec->addralign);
10691070
expandMemoryRegions(dot - pos);
10701071
}
1072+
addressChanged = sec->addr != dot;
10711073
sec->addr = dot;
10721074

10731075
// state->lmaOffset is LMA minus VMA. If LMA is explicitly specified via AT()
@@ -1151,6 +1153,7 @@ void LinkerScript::assignOffsets(OutputSection *sec) {
11511153
state->tbssAddr = dot;
11521154
dot = savedDot;
11531155
}
1156+
return addressChanged;
11541157
}
11551158

11561159
static bool isDiscardable(const OutputSection &sec) {
@@ -1387,9 +1390,10 @@ LinkerScript::AddressState::AddressState() {
13871390
// Here we assign addresses as instructed by linker script SECTIONS
13881391
// sub-commands. Doing that allows us to use final VA values, so here
13891392
// we also handle rest commands like symbol assignments and ASSERTs.
1390-
// Returns a symbol that has changed its section or value, or nullptr if no
1391-
// symbol has changed.
1392-
const Defined *LinkerScript::assignAddresses() {
1393+
// Return an output section that has changed its address or null, and a symbol
1394+
// that has changed its section or value (or nullptr if no symbol has changed).
1395+
std::pair<const OutputSection *, const Defined *>
1396+
LinkerScript::assignAddresses() {
13931397
if (script->hasSectionsCommand) {
13941398
// With a linker script, assignment of addresses to headers is covered by
13951399
// allocateHeaders().
@@ -1402,6 +1406,7 @@ const Defined *LinkerScript::assignAddresses() {
14021406
dot += getHeaderSize();
14031407
}
14041408

1409+
OutputSection *changedOsec = nullptr;
14051410
AddressState st;
14061411
state = &st;
14071412
errorOnMissingSection = true;
@@ -1416,11 +1421,12 @@ const Defined *LinkerScript::assignAddresses() {
14161421
assign->size = dot - assign->addr;
14171422
continue;
14181423
}
1419-
assignOffsets(&cast<OutputDesc>(cmd)->osec);
1424+
if (assignOffsets(&cast<OutputDesc>(cmd)->osec) && !changedOsec)
1425+
changedOsec = &cast<OutputDesc>(cmd)->osec;
14201426
}
14211427

14221428
state = nullptr;
1423-
return getChangedSymbolAssignment(oldValues);
1429+
return {changedOsec, getChangedSymbolAssignment(oldValues)};
14241430
}
14251431

14261432
static bool hasRegionOverflowed(MemoryRegion *mr) {

lld/ELF/LinkerScript.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -300,7 +300,7 @@ class LinkerScript final {
300300
std::pair<MemoryRegion *, MemoryRegion *>
301301
findMemoryRegion(OutputSection *sec, MemoryRegion *hint);
302302

303-
void assignOffsets(OutputSection *sec);
303+
bool assignOffsets(OutputSection *sec);
304304

305305
// This captures the local AddressState and makes it accessible
306306
// deliberately. This is needed as there are some cases where we cannot just
@@ -334,7 +334,7 @@ class LinkerScript final {
334334
bool needsInterpSection();
335335

336336
bool shouldKeep(InputSectionBase *s);
337-
const Defined *assignAddresses();
337+
std::pair<const OutputSection *, const Defined *> assignAddresses();
338338
bool spillSections();
339339
void erasePotentialSpillSections();
340340
void allocateHeaders(SmallVector<PhdrEntry *, 0> &phdrs);

lld/ELF/Writer.cpp

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1479,16 +1479,22 @@ template <class ELFT> void Writer<ELFT>::finalizeAddressDependentContent() {
14791479
changed |= part.memtagGlobalDescriptors->updateAllocSize();
14801480
}
14811481

1482-
const Defined *changedSym = script->assignAddresses();
1482+
std::pair<const OutputSection *, const Defined *> changes =
1483+
script->assignAddresses();
14831484
if (!changed) {
14841485
// Some symbols may be dependent on section addresses. When we break the
14851486
// loop, the symbol values are finalized because a previous
14861487
// assignAddresses() finalized section addresses.
1487-
if (!changedSym)
1488+
if (!changes.first && !changes.second)
14881489
break;
14891490
if (++assignPasses == 5) {
1490-
errorOrWarn("assignment to symbol " + toString(*changedSym) +
1491-
" does not converge");
1491+
if (changes.first)
1492+
errorOrWarn("address (0x" + Twine::utohexstr(changes.first->addr) +
1493+
") of section '" + changes.first->name +
1494+
"' does not converge");
1495+
if (changes.second)
1496+
errorOrWarn("assignment to symbol " + toString(*changes.second) +
1497+
" does not converge");
14921498
break;
14931499
}
14941500
} else if (spilled) {

lld/test/ELF/linkerscript/memory-err.s

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -68,8 +68,8 @@
6868
# RUN: symbol = .; \
6969
# RUN: .data : { *(.data) } > ram \
7070
# RUN: }' > %t.script
71-
# RUN: not ld.lld -T %t.script %t.o -o /dev/null 2>&1 | FileCheck --check-prefix=ERR_OVERFLOW %s
72-
# ERR_OVERFLOW: error: section '.data' will not fit in region 'ram': overflowed by 2 bytes
71+
# RUN: not ld.lld -T %t.script %t.o -o /dev/null 2>&1 | FileCheck --check-prefix=NOT_CONVERGE %s
72+
# NOT_CONVERGE: error: address (0x14) of section '.text' does not converge
7373

7474
nop
7575

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
# REQUIRES: x86
2+
# RUN: rm -rf %t && split-file %s %t && cd %t
3+
# RUN: llvm-mc -filetype=obj -triple=x86_64 a.s -o a.o
4+
5+
# RUN: not ld.lld a.o -T a.lds 2>&1 | FileCheck %s --implicit-check-not=error:
6+
# CHECK: error: address (0x6014) of section '.text' does not converge
7+
8+
# RUN: ld.lld a.o -T b.lds --noinhibit-exec 2>&1 | FileCheck %s --check-prefix=CHECK2 --implicit-check-not=warning:
9+
# CHECK2: warning: address (0x5014) of section '.text' does not converge
10+
# CHECK2: warning: assignment to symbol a does not converge
11+
12+
#--- a.s
13+
.globl _start
14+
_start: .space 4
15+
.data; .byte 0
16+
17+
#--- a.lds
18+
SECTIONS {
19+
. = 0x1000;
20+
.text ADDR(.data) + 0x1000 : { *(.text) }
21+
.data : { *(.data) }
22+
}
23+
24+
#--- b.lds
25+
SECTIONS {
26+
. = 0x1000;
27+
.text text : { *(.text) }
28+
.data : {
29+
*(.data)
30+
x = ADDR(.text);
31+
a = b;
32+
b = c;
33+
## Absolute symbol; not converging
34+
c = ABSOLUTE(ADDR(.text));
35+
}
36+
text = ADDR(.data) + 0x1000;
37+
}

0 commit comments

Comments
 (0)