[ELF] Validate merge section offsets in getSymVA and match GNU ld (#188677)

Move the "offset is outside the section" error for merge sections from
getSectionPiece to getSymVA, where we know the offset comes from a
section symbol + addend. Include the offset value in the diagnostic.

Accept offset == section_size (one-past-end) to match GNU ld behavior,
while rejecting offset > section_size. Skip out-of-bounds offsets in
MarkLive to avoid assertion failures in getSectionPiece.
This commit is contained in:
Fangrui Song
2026-03-26 10:29:36 -07:00
committed by GitHub
parent 593f82ab9d
commit bb443359a8
4 changed files with 31 additions and 13 deletions

View File

@@ -1548,10 +1548,7 @@ void MergeInputSection::splitIntoPieces() {
}
SectionPiece &MergeInputSection::getSectionPiece(uint64_t offset) {
if (content().size() <= offset) {
Err(getCtx()) << this << ": offset is outside the section";
return pieces[0];
}
assert(offset < content().size());
return partition_point(
pieces, [=](SectionPiece p) { return p.inputOff <= offset; })[-1];
}

View File

@@ -145,6 +145,11 @@ void MarkLive<ELFT, TrackWhyLive>::resolveReloc(InputSectionBase &sec,
offset += rel.addend;
else
offset += getAddend<ELFT>(ctx, sec, rel);
// Skip out-of-bounds offsets to avoid an assertion failure in
// getSectionPiece.
if (auto *ms = dyn_cast<MergeInputSection>(relSec);
ms && offset >= ms->content().size())
return;
}
// fromFDE being true means this is referenced by a FDE in a .eh_frame

View File

@@ -88,8 +88,16 @@ static uint64_t getSymVA(Ctx &ctx, const Symbol &sym, int64_t addend) {
// To make this work, we incorporate the addend into the section
// offset (and zero out the addend for later processing) so that
// we find the right object in the section.
if (d.isSection())
if (d.isSection()) {
offset += addend;
if (auto *ms = dyn_cast<MergeInputSection>(isec);
ms && offset >= ms->content().size()) {
if (offset > ms->content().size())
Err(ctx) << ms << ": offset 0x" << Twine::utohexstr(offset)
<< " is outside the section";
return 0;
}
}
// In the typical case, this is actually very simple and boils
// down to adding together 3 numbers:

View File

@@ -1,18 +1,25 @@
# REQUIRES: x86
## Test out-of-bounds section symbol offsets in SHF_MERGE sections.
## Non-section symbols and offset <= section_size are accepted, matching GNU ld.
# RUN: llvm-mc %s -o %t.o -filetype=obj -triple=x86_64
# RUN: not ld.lld %t.o -o /dev/null -shared 2>&1 | FileCheck %s -DPREFIX=error --implicit-check-not=error:
# RUN: ld.lld %t.o -o /dev/null -shared --noinhibit-exec 2>&1 | FileCheck %s -DPREFIX=warning --implicit-check-not=warning:
# CHECK: [[PREFIX]]: {{.*}}:(.foo): offset is outside the section
# CHECK-NEXT: [[PREFIX]]: {{.*}}:(.foo): offset is outside the section
# CHECK-NEXT: [[PREFIX]]: {{.*}}:(.foo): offset is outside the section
# CHECK-NEXT: [[PREFIX]]: {{.*}}:(.foo): offset is outside the section
## .rodata.str1.1 is "abc\0" (4 bytes).
# CHECK-NEXT: [[PREFIX]]: {{.*}}:(.rodata.str1.1): offset is outside the section
## .foo is 8 bytes with entsize=8 (1 piece). .foo+8 (offset==size) is accepted.
# CHECK: [[PREFIX]]: {{.*}}:(.foo): offset 0x9 is outside the section
# CHECK-NEXT: [[PREFIX]]: {{.*}}:(.foo): offset 0x100000000 is outside the section
# CHECK-NEXT: [[PREFIX]]: {{.*}}:(.foo): offset 0xffffffffffffffff is outside the section
## .rodata.str1.1 is "abc\0" (4 bytes). offset<=size is accepted.
# CHECK-NEXT: [[PREFIX]]: {{.*}}:(.rodata.str1.1): offset 0x5 is outside the section
## .data.retain references .foo-1 as well.
# CHECK-NEXT: [[PREFIX]]: {{.*}}:(.foo): offset is outside the section
# CHECK-NEXT: [[PREFIX]]: {{.*}}:(.foo): offset 0xfffffffffffffffe is outside the section
## Test that --gc-sections with an out-of-bounds offset doesn't crash.
## .data is discarded but .data.retain (SHF_GNU_RETAIN) is kept.
## The bad offset prevents the piece from being marked live, so .foo is discarded.
# RUN: not ld.lld %t.o -o /dev/null --gc-sections 2>&1 | FileCheck %s --check-prefix=GC
# GC: error: relocation refers to a discarded section: .foo
.globl _start
_start:
@@ -24,12 +31,13 @@ _start:
.quad .foo - 1
.quad .rodata.str1.1 + 3
.quad .rodata.str1.1 + 4
.quad .rodata.str1.1 + 5
.quad a0 - 1
.quad a0 + 9
.section .data.retain,"awR"
.quad .foo - 1
.quad .foo - 2
.section .foo,"aM",@progbits,8
a0: