[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:
@@ -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];
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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:
|
||||
|
||||
Reference in New Issue
Block a user