[lld][Hexagon] Fix out-of-range PLT branch thunks (#186545)
Linking large Hexagon binaries (e.g. ASan runtime with >8 MiB of text) fails with R_HEX_B22_PCREL / R_HEX_PLT_B22_PCREL relocation overflow on calls to PLT entries, even though the thunk infrastructure exists and needsThunks is set. needsThunk() always used s.getVA() to compute the branch destination, even for PLT calls where the actual destination is the PLT entry. This meant the distance check used the wrong address and failed to create thunks when the PLT entry was out of B22_PCREL range. Fix by using s.getPltVA() when expr == R_PLT_PC. Also override getThunkSectionSpacing() so ThunkSections are pre-created at appropriate intervals for large binaries.
This commit is contained in:
parent
04785adec3
commit
ba228181c2
@ -48,6 +48,7 @@ public:
|
||||
bool needsThunk(RelExpr expr, RelType type, const InputFile *file,
|
||||
uint64_t branchAddr, const Symbol &s,
|
||||
int64_t a) const override;
|
||||
uint32_t getThunkSectionSpacing() const override;
|
||||
bool inBranchRange(RelType type, uint64_t src, uint64_t dst) const override;
|
||||
void relocate(uint8_t *loc, const Relocation &rel,
|
||||
uint64_t val) const override;
|
||||
@ -387,13 +388,22 @@ bool Hexagon::needsThunk(RelExpr expr, RelType type, const InputFile *file,
|
||||
case R_HEX_LD_PLT_B22_PCREL:
|
||||
case R_HEX_B15_PCREL:
|
||||
case R_HEX_B13_PCREL:
|
||||
case R_HEX_B9_PCREL:
|
||||
return !ctx.target->inBranchRange(type, branchAddr, s.getVA(ctx, a));
|
||||
case R_HEX_B9_PCREL: {
|
||||
uint64_t dst = expr == R_PLT_PC ? s.getPltVA(ctx) : s.getVA(ctx, a);
|
||||
return !ctx.target->inBranchRange(type, branchAddr, dst);
|
||||
}
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t Hexagon::getThunkSectionSpacing() const {
|
||||
// B22_PCREL has a range of +/- 8 MiB (22-bit signed offset * 4).
|
||||
// Pre-create ThunkSections at intervals below this to leave room for
|
||||
// thunk growth.
|
||||
return 0x800000 - 0x30000;
|
||||
}
|
||||
|
||||
void Hexagon::relocate(uint8_t *loc, const Relocation &rel,
|
||||
uint64_t val) const {
|
||||
switch (rel.type) {
|
||||
|
||||
@ -2,74 +2,61 @@
|
||||
# RUN: rm -rf %t && split-file %s %t && cd %t
|
||||
# RUN: llvm-mc -filetype=obj -triple=hexagon-unknown-elf external.s -o external.o
|
||||
# RUN: ld.lld -shared external.o -soname external.so -o external.so
|
||||
|
||||
## PLT calls within range (2 MiB padding) — no thunks needed.
|
||||
# RUN: llvm-mc -filetype=obj -triple=hexagon-unknown-elf main.s -o main.o
|
||||
# RUN: ld.lld main.o external.so -o test
|
||||
# RUN: llvm-objdump -d --no-show-raw-insn test | FileCheck %s
|
||||
# RUN: llvm-objdump -d --no-show-raw-insn test | \
|
||||
# RUN: FileCheck --check-prefix=INRANGE %s
|
||||
|
||||
## PLT calls out of range (>8 MiB padding) — thunks required.
|
||||
# RUN: llvm-mc -filetype=obj \
|
||||
# RUN: -triple=hexagon-unknown-elf main-large.s -o main-large.o
|
||||
# RUN: ld.lld main-large.o external.so -o test-large
|
||||
# RUN: llvm-objdump -d --no-show-raw-insn test-large | \
|
||||
# RUN: FileCheck --check-prefix=OUTRANGE %s
|
||||
|
||||
## Test thunk range scenarios for Hexagon R_HEX_PLT_B22_PCREL relocations.
|
||||
## PLT calls use the same ±8MB range as regular calls but go through PLT entries.
|
||||
## This test verifies thunk generation for PLT calls at range boundaries.
|
||||
## PLT calls use the same +/- 8 MiB range as regular B22_PCREL calls.
|
||||
## When the PLT entry is beyond this range, a thunk must be created.
|
||||
|
||||
#--- external.s
|
||||
.globl extern_within_range, extern_beyond_range, extern_close
|
||||
.type extern_within_range, @function
|
||||
.type extern_beyond_range, @function
|
||||
.type extern_close, @function
|
||||
|
||||
extern_within_range:
|
||||
jumpr r31
|
||||
|
||||
extern_beyond_range:
|
||||
jumpr r31
|
||||
|
||||
extern_close:
|
||||
.globl extern_func
|
||||
.type extern_func, @function
|
||||
extern_func:
|
||||
jumpr r31
|
||||
|
||||
#--- main.s
|
||||
## Within-range case: 2 MiB padding keeps PLT entries reachable.
|
||||
.globl _start
|
||||
.type _start, @function
|
||||
_start:
|
||||
## Test PLT calls to external functions at various ranges
|
||||
call extern_within_range@PLT
|
||||
call extern_beyond_range@PLT
|
||||
call extern_close@PLT
|
||||
call extern_func@PLT
|
||||
jumpr r31
|
||||
|
||||
.skip 0x200000
|
||||
|
||||
# CHECK: Disassembly of section .text:
|
||||
# CHECK: <_start>:
|
||||
# CHECK-NEXT: 2021c: { call 0x220250 <extern_within_range@plt> }
|
||||
# CHECK-NEXT: { call 0x220260 <extern_beyond_range@plt> }
|
||||
# CHECK-NEXT: { call 0x220270 <extern_close@plt> }
|
||||
# CHECK-NEXT: { jumpr r31 }
|
||||
#--- main-large.s
|
||||
## Out-of-range case: >8 MiB padding pushes PLT entries beyond B22_PCREL range.
|
||||
.globl _start
|
||||
.type _start, @function
|
||||
_start:
|
||||
call extern_func@PLT
|
||||
jumpr r31
|
||||
|
||||
## Verify PLT header and entries are created with exact addresses
|
||||
# CHECK: Disassembly of section .plt:
|
||||
# CHECK: <.plt>:
|
||||
# CHECK-NEXT: 220230: { immext(#0x20080)
|
||||
# CHECK-NEXT: r28 = add(pc,##0x200b8) }
|
||||
# CHECK-NEXT: { r14 -= add(r28,#0x10)
|
||||
# CHECK-NEXT: r15 = memw(r28+#0x8)
|
||||
# CHECK-NEXT: r28 = memw(r28+#0x4) }
|
||||
# CHECK-NEXT: { r14 = asr(r14,#0x2)
|
||||
# CHECK-NEXT: jumpr r28 }
|
||||
# CHECK-NEXT: { trap0(#0xdb) }
|
||||
.skip 0x900000
|
||||
|
||||
# CHECK: <extern_within_range@plt>:
|
||||
# CHECK-NEXT: 220250: { immext(#0x20080)
|
||||
# CHECK-NEXT: r14 = add(pc,##0x200a8) }
|
||||
# CHECK-NEXT: { r28 = memw(r14+#0x0) }
|
||||
# CHECK-NEXT: { jumpr r28 }
|
||||
## Within-range: _start calls the PLT entry directly (no thunk).
|
||||
# INRANGE: <_start>:
|
||||
# INRANGE-NEXT: 201ac: { call 0x2201e0 <extern_func@plt> }
|
||||
# INRANGE-NEXT: { jumpr r31 }
|
||||
|
||||
# CHECK: <extern_beyond_range@plt>:
|
||||
# CHECK-NEXT: 220260: { immext(#0x20080)
|
||||
# CHECK-NEXT: r14 = add(pc,##0x2009c) }
|
||||
# CHECK-NEXT: { r28 = memw(r14+#0x0) }
|
||||
# CHECK-NEXT: { jumpr r28 }
|
||||
## Out-of-range: thunk uses immext+jump to reach the PLT entry.
|
||||
# OUTRANGE: <__hexagon_thunk_extern_func_from_.text.thunk>:
|
||||
# OUTRANGE-NEXT: 201ac: { immext(#0x900000)
|
||||
# OUTRANGE-NEXT: jump 0x9201e0 <extern_func@plt> }
|
||||
|
||||
# CHECK: <extern_close@plt>:
|
||||
# CHECK-NEXT: 220270: { immext(#0x20080)
|
||||
# CHECK-NEXT: r14 = add(pc,##0x20090) }
|
||||
# CHECK-NEXT: { r28 = memw(r14+#0x0) }
|
||||
# CHECK-NEXT: { jumpr r28 }
|
||||
## _start calls the thunk instead of the PLT entry directly.
|
||||
# OUTRANGE: <_start>:
|
||||
# OUTRANGE-NEXT: 201b4: { call 0x201ac <__hexagon_thunk_extern_func_from_.text.thunk> }
|
||||
# OUTRANGE-NEXT: { jumpr r31 }
|
||||
|
||||
@ -15,18 +15,9 @@
|
||||
|
||||
# CHECK: Disassembly of section .text:
|
||||
|
||||
# CHECK-NONPIC: 000200b4 <__hexagon_thunk_myfn_a_from_.text.thunk>:
|
||||
# CHECK-NONPIC: { immext(#0x1000040)
|
||||
# CHECK-NONPIC: jump 0x1020110 <myfn_a> }
|
||||
|
||||
# CHECK-PIC: 00010150 <__hexagon_thunk_myfn_a_from_.text.thunk>:
|
||||
# CHECK-PIC-NEXT: { immext(#0x1000040)
|
||||
# CHECK-PIC-NEXT: r14 = add(pc,##0x1000060) }
|
||||
# CHECK-PIC-NEXT: { jumpr r14 }
|
||||
|
||||
# CHECK-NONPIC: 000200bc <myfn_b>:
|
||||
# CHECK-NONPIC: 000200b4 <myfn_b>:
|
||||
# CHECK-NONPIC: { jumpr r31 }
|
||||
# CHECK-PIC: 0001015c <myfn_b>:
|
||||
# CHECK-PIC: 00010150 <myfn_b>:
|
||||
# CHECK-PIC: { jumpr r31 }
|
||||
.globl myfn_b
|
||||
.type myfn_b, @function
|
||||
@ -34,47 +25,47 @@ myfn_b:
|
||||
jumpr r31
|
||||
.size myfn_b, .-myfn_b
|
||||
|
||||
# CHECK-PIC: 00010160 <main>:
|
||||
# CHECK-PIC: 00010154 <main>:
|
||||
.globl main
|
||||
.type main, @function
|
||||
main:
|
||||
{ r0 = #0
|
||||
call myfn_a }
|
||||
# CHECK-PIC: { call 0x10150 <__hexagon_thunk_myfn_a_from_.text.thunk>
|
||||
# CHECK-NONPIC: { call 0x200b4 <__hexagon_thunk_myfn_a_from_.text.thunk>
|
||||
# CHECK-PIC: { call 0x101a4 <__hexagon_thunk_myfn_a_from_.text.thunk>
|
||||
# CHECK-NONPIC: { call 0x20108 <__hexagon_thunk_myfn_a_from_.text.thunk>
|
||||
# CHECK-NEXT: r0 = #0x0 }
|
||||
call myfn_a
|
||||
# CHECK-PIC: call 0x10150 <__hexagon_thunk_myfn_a_from_.text.thunk>
|
||||
# CHECK-NONPIC: call 0x200b4 <__hexagon_thunk_myfn_a_from_.text.thunk>
|
||||
# CHECK-PIC: call 0x101a4 <__hexagon_thunk_myfn_a_from_.text.thunk>
|
||||
# CHECK-NONPIC: call 0x20108 <__hexagon_thunk_myfn_a_from_.text.thunk>
|
||||
call myfn_b
|
||||
# CHECK-PIC-NEXT: call 0x1015c <myfn_b>
|
||||
# CHECK-NONPIC-NEXT: call 0x200bc <myfn_b>
|
||||
# CHECK-PIC-NEXT: call 0x10150 <myfn_b>
|
||||
# CHECK-NONPIC-NEXT: call 0x200b4 <myfn_b>
|
||||
|
||||
{ r2 = add(r0, r1)
|
||||
if (p0) call #myfn_b
|
||||
if (!p0) call #myfn_a }
|
||||
# CHECK-PIC-NEXT: { if (p0) call 0x1015c <myfn_b>
|
||||
# CHECK-PIC-NEXT: if (!p0) call 0x10150 <__hexagon_thunk_myfn_a_from_.text.thunk>
|
||||
# CHECK-NONPIC-NEXT: { if (p0) call 0x200bc <myfn_b>
|
||||
# CHECK-NONPIC-NEXT: if (!p0) call 0x200b4 <__hexagon_thunk_myfn_a_from_.text.thunk>
|
||||
# CHECK-PIC-NEXT: { if (p0) call 0x10150 <myfn_b>
|
||||
# CHECK-PIC-NEXT: if (!p0) call 0x101a4 <__hexagon_thunk_myfn_a_from_.text.thunk>
|
||||
# CHECK-NONPIC-NEXT: { if (p0) call 0x200b4 <myfn_b>
|
||||
# CHECK-NONPIC-NEXT: if (!p0) call 0x20108 <__hexagon_thunk_myfn_a_from_.text.thunk>
|
||||
|
||||
# CHECK-NEXT: r2 = add(r0,r1) }
|
||||
|
||||
{ r2 = add(r0, r1)
|
||||
if (p0) call #myfn_a
|
||||
if (!p0) call #myfn_a }
|
||||
# CHECK-PIC-NEXT: { if (p0) call 0x10150 <__hexagon_thunk_myfn_a_from_.text.thunk>
|
||||
# CHECK-PIC-NEXT: if (!p0) call 0x10150 <__hexagon_thunk_myfn_a_from_.text.thunk>
|
||||
# CHECK-NONPIC-NEXT: { if (p0) call 0x200b4 <__hexagon_thunk_myfn_a_from_.text.thunk>
|
||||
# CHECK-NONPIC-NEXT: if (!p0) call 0x200b4 <__hexagon_thunk_myfn_a_from_.text.thunk>
|
||||
# CHECK-PIC-NEXT: { if (p0) call 0x101a4 <__hexagon_thunk_myfn_a_from_.text.thunk>
|
||||
# CHECK-PIC-NEXT: if (!p0) call 0x101a4 <__hexagon_thunk_myfn_a_from_.text.thunk>
|
||||
# CHECK-NONPIC-NEXT: { if (p0) call 0x20108 <__hexagon_thunk_myfn_a_from_.text.thunk>
|
||||
# CHECK-NONPIC-NEXT: if (!p0) call 0x20108 <__hexagon_thunk_myfn_a_from_.text.thunk>
|
||||
# CHECK-NEXT: r2 = add(r0,r1) }
|
||||
|
||||
{ r2 = add(r0, r1)
|
||||
r1 = r4
|
||||
r4 = r5
|
||||
if (r0 == #0) jump:t #myfn_a }
|
||||
# CHECK-PIC-NEXT: { if (r0==#0) jump:t 0x10150
|
||||
# CHECK-NONPIC-NEXT: { if (r0==#0) jump:t 0x200b4
|
||||
# CHECK-PIC-NEXT: { if (r0==#0) jump:t 0x101a4
|
||||
# CHECK-NONPIC-NEXT: { if (r0==#0) jump:t 0x20108
|
||||
# CHECK-NEXT: r2 = add(r0,r1)
|
||||
# CHECK-NEXT: r1 = r4; r4 = r5 }
|
||||
|
||||
@ -82,19 +73,19 @@ main:
|
||||
r4 = r5
|
||||
if (r0 <= #0) jump:t #myfn_a
|
||||
p1 = cmp.eq(r0, #0); if (p1.new) jump:nt #myfn_a }
|
||||
# CHECK-NONPIC-NEXT: { if (r0<=#0) jump:t 0x200b4
|
||||
# CHECK-NONPIC-NEXT: p1 = cmp.eq(r0,#0x0); if (p1.new) jump:nt 0x200b4 <__hexagon_thunk_myfn_a_from_.text.thunk>
|
||||
# CHECK-PIC-NEXT: { if (r0<=#0) jump:t 0x10150
|
||||
# CHECK-PIC-NEXT: p1 = cmp.eq(r0,#0x0); if (p1.new) jump:nt 0x10150 <__hexagon_thunk_myfn_a_from_.text.thunk>
|
||||
# CHECK-NONPIC-NEXT: { if (r0<=#0) jump:t 0x20108
|
||||
# CHECK-NONPIC-NEXT: p1 = cmp.eq(r0,#0x0); if (p1.new) jump:nt 0x20108 <__hexagon_thunk_myfn_a_from_.text.thunk>
|
||||
# CHECK-PIC-NEXT: { if (r0<=#0) jump:t 0x101a4
|
||||
# CHECK-PIC-NEXT: p1 = cmp.eq(r0,#0x0); if (p1.new) jump:nt 0x101a4 <__hexagon_thunk_myfn_a_from_.text.thunk>
|
||||
# CHECK-NEXT: r2 = add(r0,r1)
|
||||
# CHECK-NEXT: r4 = r5 }
|
||||
|
||||
{r0 = #0; jump #myfn_a}
|
||||
# CHECK-PIC-NEXT: { r0 = #0x0 ; jump 0x10150 <__hexagon_thunk_myfn_a_from_.text.thunk> }
|
||||
# CHECK-NONPIC-NEXT: { r0 = #0x0 ; jump 0x200b4 <__hexagon_thunk_myfn_a_from_.text.thunk> }
|
||||
# CHECK-PIC-NEXT: { r0 = #0x0 ; jump 0x101a4 <__hexagon_thunk_myfn_a_from_.text.thunk> }
|
||||
# CHECK-NONPIC-NEXT: { r0 = #0x0 ; jump 0x20108 <__hexagon_thunk_myfn_a_from_.text.thunk> }
|
||||
{r0 = #0; jump #myfn_b}
|
||||
# CHECK-PIC-NEXT: { r0 = #0x0 ; jump 0x1015c <myfn_b> }
|
||||
# CHECK-NONPIC-NEXT: { r0 = #0x0 ; jump 0x200bc <myfn_b> }
|
||||
# CHECK-PIC-NEXT: { r0 = #0x0 ; jump 0x10150 <myfn_b> }
|
||||
# CHECK-NONPIC-NEXT: { r0 = #0x0 ; jump 0x200b4 <myfn_b> }
|
||||
jumpr r31
|
||||
.size main, .-main
|
||||
|
||||
@ -108,15 +99,28 @@ myfn_a:
|
||||
jumpr r31
|
||||
.size myfn_a, .-myfn_a
|
||||
|
||||
# CHECK-NONPIC: 00020108 <__hexagon_thunk_myfn_a_from_.text.thunk>:
|
||||
# CHECK-NONPIC-NEXT: { immext(#0x1000000)
|
||||
# CHECK-NONPIC-NEXT: jump 0x1020110 <myfn_a> }
|
||||
|
||||
# CHECK-PIC: 000101a4 <__hexagon_thunk_myfn_a_from_.text.thunk>:
|
||||
# CHECK-PIC-NEXT: { immext(#0x1000000)
|
||||
# CHECK-PIC-NEXT: r14 = add(pc,##0x100000c) }
|
||||
# CHECK-PIC-NEXT: { jumpr r14 }
|
||||
|
||||
# CHECK-NONPIC: 01020110 <myfn_a>:
|
||||
# CHECK-NONPIC-NEXT: { r0 = #0x0 ; jump 0x1020118 <__hexagon_thunk_myfn_b_from_.text.thunk> }
|
||||
# CHECK-NONPIC-NEXT: { jumpr r31 }
|
||||
|
||||
# CHECK-NONPIC: 01020118 <__hexagon_thunk_myfn_b_from_.text.thunk>:
|
||||
# CHECK-NONPIC-NEXT: { immext(#0xfeffff80)
|
||||
# CHECK-NONPIC-NEXT: jump 0x200bc <myfn_b> }
|
||||
# CHECK-NONPIC-NEXT: jump 0x200b4 <myfn_b> }
|
||||
|
||||
# CHECK-PIC: 010101b0 <myfn_a>:
|
||||
# CHECK-PIC-NEXT: { r0 = #0x0 ; jump 0x10101b8 <__hexagon_thunk_myfn_b_from_.text.thunk> }
|
||||
# CHECK-PIC-NEXT: { jumpr r31 }
|
||||
|
||||
# CHECK-PIC: 010101b8 <__hexagon_thunk_myfn_b_from_.text.thunk>:
|
||||
# CHECK-PIC-NEXT: { immext(#0xfeffff80)
|
||||
# CHECK-PIC-NEXT: r14 = add(pc,##0xfeffffa4) }
|
||||
# CHECK-PIC-NEXT: r14 = add(pc,##0xfeffff98) }
|
||||
# CHECK-PIC-NEXT: { jumpr r14 }
|
||||
|
||||
@ -29,19 +29,17 @@ myfn:
|
||||
|
||||
# CHECK: Disassembly of section .text_low:
|
||||
|
||||
# CHECK: <__hexagon_thunk_myfn_from_.text.thunk>:
|
||||
# CHECK-NONPIC-NEXT: 200b4: { immext(#0x1000000)
|
||||
# CHECK-NONPIC-NEXT: jump 0x10200bc <myfn> }
|
||||
# CHECK-PIC-NEXT: 200b4: { immext(#0x1000000)
|
||||
# CHECK-PIC-NEXT: r14 = add(pc,##0x1000008) }
|
||||
# CHECK-PIC-NEXT: { jumpr r14 }
|
||||
|
||||
# CHECK-NONPIC: <main>:
|
||||
# CHECK-NONPIC-NEXT: 200bc: { call 0x200b4 <__hexagon_thunk_myfn_from_.text.thunk> }
|
||||
# CHECK-PIC: <main>:
|
||||
# CHECK-PIC-NEXT: 200c0: { call 0x200b4 <__hexagon_thunk_myfn_from_.text.thunk> }
|
||||
# CHECK: <main>:
|
||||
# CHECK-NEXT: 200b4: { call 0x200bc <__hexagon_thunk_myfn_from_.text.thunk> }
|
||||
# CHECK-NEXT: { jumpr r31 }
|
||||
|
||||
# CHECK: <__hexagon_thunk_myfn_from_.text.thunk>:
|
||||
# CHECK-NONPIC-NEXT: 200bc: { immext(#0x1000000)
|
||||
# CHECK-NONPIC-NEXT: jump 0x10200bc <myfn> }
|
||||
# CHECK-PIC-NEXT: 200bc: { immext(#0x1000000)
|
||||
# CHECK-PIC-NEXT: r14 = add(pc,##0x1000000) }
|
||||
# CHECK-PIC-NEXT: { jumpr r14 }
|
||||
|
||||
# CHECK: Disassembly of section .text_high:
|
||||
# CHECK: <myfn>:
|
||||
# CHECK-NEXT: 10200bc: { jumpr r31 }
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user