-
Notifications
You must be signed in to change notification settings - Fork 14.5k
release/21.x: [lld] Add thunks for hexagon (#111217) #149723
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: release/21.x
Are you sure you want to change the base?
Conversation
Without thunks, programs will encounter link errors complaining that the branch target is out of range. Thunks will extend the range of branch targets, which is a critical need for large programs. Thunks provide this flexibility at a cost of some modest code size increase. When configured with the maximal feature set, the hexagon port of the linux kernel would often encounter these limitations when linking with `lld`. The relocations which will be extended by thunks are: * R_HEX_B22_PCREL, R_HEX_{G,L}D_PLT_B22_PCREL, R_HEX_PLT_B22_PCREL relocations have a range of ± 8MiB on the baseline * R_HEX_B15_PCREL: ±65,532 bytes * R_HEX_B13_PCREL: ±16,380 bytes * R_HEX_B9_PCREL: ±1,020 bytes Fixes llvm#149689 Co-authored-by: Alexey Karyakin <[email protected]> --------- Co-authored-by: Alexey Karyakin <[email protected]> (cherry picked from commit b42f96b)
@MaskRay What do you think about merging this PR to the release branch? |
@llvm/pr-subscribers-lld @llvm/pr-subscribers-lld-elf Author: None (llvmbot) ChangesBackport b42f96b Requested by: @androm3da Patch is 27.54 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/149723.diff 9 Files Affected:
diff --git a/lld/ELF/Arch/Hexagon.cpp b/lld/ELF/Arch/Hexagon.cpp
index 479131a24dcfc..9b33e78731c97 100644
--- a/lld/ELF/Arch/Hexagon.cpp
+++ b/lld/ELF/Arch/Hexagon.cpp
@@ -11,6 +11,7 @@
#include "Symbols.h"
#include "SyntheticSections.h"
#include "Target.h"
+#include "Thunks.h"
#include "lld/Common/ErrorHandler.h"
#include "llvm/ADT/SmallVector.h"
#include "llvm/BinaryFormat/ELF.h"
@@ -36,6 +37,10 @@ class Hexagon final : public TargetInfo {
const uint8_t *loc) const override;
RelType getDynRel(RelType type) const override;
int64_t getImplicitAddend(const uint8_t *buf, RelType type) const override;
+ bool needsThunk(RelExpr expr, RelType type, const InputFile *file,
+ uint64_t branchAddr, const Symbol &s,
+ int64_t a) 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;
void writePltHeader(uint8_t *buf) const override;
@@ -63,6 +68,8 @@ Hexagon::Hexagon(Ctx &ctx) : TargetInfo(ctx) {
tlsGotRel = R_HEX_TPREL_32;
tlsModuleIndexRel = R_HEX_DTPMOD_32;
tlsOffsetRel = R_HEX_DTPREL_32;
+
+ needsThunks = true;
}
uint32_t Hexagon::calcEFlags() const {
@@ -258,6 +265,46 @@ static uint32_t findMaskR16(Ctx &ctx, uint32_t insn) {
static void or32le(uint8_t *p, int32_t v) { write32le(p, read32le(p) | v); }
+bool Hexagon::inBranchRange(RelType type, uint64_t src, uint64_t dst) const {
+ int64_t offset = dst - src;
+ switch (type) {
+ case llvm::ELF::R_HEX_B22_PCREL:
+ case llvm::ELF::R_HEX_PLT_B22_PCREL:
+ case llvm::ELF::R_HEX_GD_PLT_B22_PCREL:
+ case llvm::ELF::R_HEX_LD_PLT_B22_PCREL:
+ return llvm::isInt<22>(offset >> 2);
+ case llvm::ELF::R_HEX_B15_PCREL:
+ return llvm::isInt<15>(offset >> 2);
+ break;
+ case llvm::ELF::R_HEX_B13_PCREL:
+ return llvm::isInt<13>(offset >> 2);
+ break;
+ case llvm::ELF::R_HEX_B9_PCREL:
+ return llvm::isInt<9>(offset >> 2);
+ default:
+ return true;
+ }
+ llvm_unreachable("unsupported relocation");
+}
+
+bool Hexagon::needsThunk(RelExpr expr, RelType type, const InputFile *file,
+ uint64_t branchAddr, const Symbol &s,
+ int64_t a) const {
+ // Only check branch range for supported branch relocation types
+ switch (type) {
+ case R_HEX_B22_PCREL:
+ case R_HEX_PLT_B22_PCREL:
+ case R_HEX_GD_PLT_B22_PCREL:
+ 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));
+ default:
+ return false;
+ }
+}
+
void Hexagon::relocate(uint8_t *loc, const Relocation &rel,
uint64_t val) const {
switch (rel.type) {
diff --git a/lld/ELF/Relocations.cpp b/lld/ELF/Relocations.cpp
index cebd564036b2c..41f153e97de77 100644
--- a/lld/ELF/Relocations.cpp
+++ b/lld/ELF/Relocations.cpp
@@ -2139,17 +2139,44 @@ void ThunkCreator::mergeThunks(ArrayRef<OutputSection *> outputSections) {
});
}
-static int64_t getPCBias(Ctx &ctx, RelType type) {
- if (ctx.arg.emachine != EM_ARM)
- return 0;
- switch (type) {
- case R_ARM_THM_JUMP19:
- case R_ARM_THM_JUMP24:
- case R_ARM_THM_CALL:
- return 4;
- default:
- return 8;
+constexpr uint32_t HEXAGON_MASK_END_PACKET = 3 << 14;
+constexpr uint32_t HEXAGON_END_OF_PACKET = 3 << 14;
+constexpr uint32_t HEXAGON_END_OF_DUPLEX = 0 << 14;
+
+// Return the distance between the packet start and the instruction in the
+// relocation.
+static int getHexagonPacketOffset(const InputSection &isec,
+ const Relocation &rel) {
+ const ArrayRef<uint8_t> data = isec.content();
+
+ // Search back as many as 3 instructions.
+ for (unsigned i = 0;; i++) {
+ if (i == 3 || rel.offset < (i + 1) * 4)
+ return i * 4;
+ uint32_t instWord = 0;
+ const ArrayRef<uint8_t> instWordContents =
+ data.drop_front(rel.offset - (i + 1) * 4);
+ memcpy(&instWord, instWordContents.data(), sizeof(instWord));
+ if (((instWord & HEXAGON_MASK_END_PACKET) == HEXAGON_END_OF_PACKET) ||
+ ((instWord & HEXAGON_MASK_END_PACKET) == HEXAGON_END_OF_DUPLEX))
+ return i * 4;
+ }
+}
+static int64_t getPCBias(Ctx &ctx, const InputSection &isec,
+ const Relocation &rel) {
+ if (ctx.arg.emachine == EM_ARM) {
+ switch (rel.type) {
+ case R_ARM_THM_JUMP19:
+ case R_ARM_THM_JUMP24:
+ case R_ARM_THM_CALL:
+ return 4;
+ default:
+ return 8;
+ }
}
+ if (ctx.arg.emachine == EM_HEXAGON)
+ return -getHexagonPacketOffset(isec, rel);
+ return 0;
}
// Find or create a ThunkSection within the InputSectionDescription (ISD) that
@@ -2161,7 +2188,7 @@ ThunkSection *ThunkCreator::getISDThunkSec(OutputSection *os,
const Relocation &rel,
uint64_t src) {
// See the comment in getThunk for -pcBias below.
- const int64_t pcBias = getPCBias(ctx, rel.type);
+ const int64_t pcBias = getPCBias(ctx, *isec, rel);
for (std::pair<ThunkSection *, uint32_t> tp : isd->thunkSections) {
ThunkSection *ts = tp.first;
uint64_t tsBase = os->addr + ts->outSecOff - pcBias;
@@ -2322,7 +2349,7 @@ std::pair<Thunk *, bool> ThunkCreator::getThunk(InputSection *isec,
// out in the relocation addend. We compensate for the PC bias so that
// an Arm and Thumb relocation to the same destination get the same keyAddend,
// which is usually 0.
- const int64_t pcBias = getPCBias(ctx, rel.type);
+ const int64_t pcBias = getPCBias(ctx, *isec, rel);
const int64_t keyAddend = rel.addend + pcBias;
// We use a ((section, offset), addend) pair to find the thunk position if
@@ -2481,7 +2508,7 @@ bool ThunkCreator::createThunks(uint32_t pass,
// STT_SECTION + non-zero addend, clear the addend after
// redirection.
if (ctx.arg.emachine != EM_MIPS)
- rel.addend = -getPCBias(ctx, rel.type);
+ rel.addend = -getPCBias(ctx, *isec, rel);
}
for (auto &p : isd->thunkSections)
diff --git a/lld/ELF/Thunks.cpp b/lld/ELF/Thunks.cpp
index c26ba76bccb7e..65d0f094c43c3 100644
--- a/lld/ELF/Thunks.cpp
+++ b/lld/ELF/Thunks.cpp
@@ -415,6 +415,22 @@ class AVRThunk : public Thunk {
void addSymbols(ThunkSection &isec) override;
};
+// Hexagon CPUs need thunks for R_HEX_B{9,1{3,5},22}_PCREL,
+// R_HEX_{,GD_}PLT_B22_PCREL when their destination is out of
+// range.
+class HexagonThunk : public Thunk {
+public:
+ HexagonThunk(Ctx &ctx, const InputSection &isec, Relocation &rel,
+ Symbol &dest)
+ : Thunk(ctx, dest, 0), relOffset(rel.offset) {
+ alignment = 4;
+ }
+ uint32_t relOffset;
+ uint32_t size() override { return ctx.arg.isPic ? 12 : 8; }
+ void writeTo(uint8_t *buf) override;
+ void addSymbols(ThunkSection &isec) override;
+};
+
// MIPS LA25 thunk
class MipsThunk final : public Thunk {
public:
@@ -1519,6 +1535,39 @@ bool PPC64LongBranchThunk::isCompatibleWith(const InputSection &isec,
return rel.type == R_PPC64_REL24 || rel.type == R_PPC64_REL14;
}
+// Hexagon Target Thunks
+static uint64_t getHexagonThunkDestVA(Ctx &ctx, const Symbol &s, int64_t a) {
+ uint64_t v = s.isInPlt(ctx) ? s.getPltVA(ctx) : s.getVA(ctx, a);
+ return SignExtend64<32>(v);
+}
+
+void HexagonThunk::writeTo(uint8_t *buf) {
+ uint64_t s = getHexagonThunkDestVA(ctx, destination, addend);
+ uint64_t p = getThunkTargetSym()->getVA(ctx);
+
+ if (ctx.arg.isPic) {
+ write32(ctx, buf + 0, 0x00004000); // { immext(#0)
+ ctx.target->relocateNoSym(buf, R_HEX_B32_PCREL_X, s - p);
+ write32(ctx, buf + 4, 0x6a49c00e); // r14 = add(pc,##0) }
+ ctx.target->relocateNoSym(buf + 4, R_HEX_6_PCREL_X, s - p);
+
+ write32(ctx, buf + 8, 0x528ec000); // { jumpr r14 }
+ } else {
+ write32(ctx, buf + 0, 0x00004000); // { immext
+ ctx.target->relocateNoSym(buf, R_HEX_B32_PCREL_X, s - p);
+ write32(ctx, buf + 4, 0x5800c000); // jump <> }
+ ctx.target->relocateNoSym(buf + 4, R_HEX_B22_PCREL_X, s - p);
+ }
+}
+void HexagonThunk::addSymbols(ThunkSection &isec) {
+ Symbol *enclosing = isec.getEnclosingSymbol(relOffset);
+ StringRef src = enclosing ? enclosing->getName() : isec.name;
+
+ addSymbol(
+ saver().save("__hexagon_thunk_" + destination.getName() + "_from_" + src),
+ STT_FUNC, 0, isec);
+}
+
Thunk::Thunk(Ctx &ctx, Symbol &d, int64_t a)
: ctx(ctx), destination(d), addend(a), offset(0) {
destination.thunkAccessed = true;
@@ -1692,6 +1741,24 @@ static std::unique_ptr<Thunk> addThunkAVR(Ctx &ctx, RelType type, Symbol &s,
}
}
+static std::unique_ptr<Thunk> addThunkHexagon(Ctx &ctx,
+ const InputSection &isec,
+ Relocation &rel, Symbol &s) {
+ switch (rel.type) {
+ case R_HEX_B9_PCREL:
+ case R_HEX_B13_PCREL:
+ case R_HEX_B15_PCREL:
+ case R_HEX_B22_PCREL:
+ case R_HEX_PLT_B22_PCREL:
+ case R_HEX_GD_PLT_B22_PCREL:
+ return std::make_unique<HexagonThunk>(ctx, isec, rel, s);
+ default:
+ Fatal(ctx) << "unrecognized relocation " << rel.type << " to " << &s
+ << " for hexagon target";
+ llvm_unreachable("");
+ }
+}
+
static std::unique_ptr<Thunk> addThunkMips(Ctx &ctx, RelType type, Symbol &s) {
if ((s.stOther & STO_MIPS_MICROMIPS) && isMipsR6(ctx))
return std::make_unique<MicroMipsR6Thunk>(ctx, s);
@@ -1761,8 +1828,11 @@ std::unique_ptr<Thunk> elf::addThunk(Ctx &ctx, const InputSection &isec,
return addThunkPPC32(ctx, isec, rel, s);
case EM_PPC64:
return addThunkPPC64(ctx, rel.type, s, a);
+ case EM_HEXAGON:
+ return addThunkHexagon(ctx, isec, rel, s);
default:
- llvm_unreachable("add Thunk only supported for ARM, AVR, Mips and PowerPC");
+ llvm_unreachable(
+ "add Thunk only supported for ARM, AVR, Hexagon, Mips and PowerPC");
}
}
diff --git a/lld/test/ELF/hexagon-jump-error.s b/lld/test/ELF/hexagon-jump-error.s
deleted file mode 100644
index 53860b5daf2b1..0000000000000
--- a/lld/test/ELF/hexagon-jump-error.s
+++ /dev/null
@@ -1,32 +0,0 @@
-# REQUIRES: hexagon
-# RUN: llvm-mc -filetype=obj -triple=hexagon-unknown-elf %s -o %t.o
-## Use --threads=1 to keep emitted warnings across sections sequential.
-# RUN: not ld.lld %t.o -o /dev/null --threads=1 2>&1 | FileCheck --implicit-check-not "out of range" %s
-
- .globl _start
- .type _start, @function
-_start:
-
-# CHECK: relocation R_HEX_B9_PCREL out of range: 1028 is not in [-1024, 1023]
-{r0 = #0; jump #1f}
-.space (1<<10)
-.section b9, "ax"
-1:
-
-# CHECK: relocation R_HEX_B13_PCREL out of range: 16388 is not in [-16384, 16383]
-if (r0==#0) jump:t #1f
-.space (1<<14)
-.section b13, "ax"
-1:
-
-# CHECK: relocation R_HEX_B15_PCREL out of range: 65540 is not in [-65536, 65535]
-if (p0) jump #1f
-.space (1<<16)
-.section b15, "ax"
-1:
-
-# CHECK: relocation R_HEX_B22_PCREL out of range: 8388612 is not in [-8388608, 8388607]
-jump #1f
-.space (1<<23)
-.section b22, "ax"
-1:
diff --git a/lld/test/ELF/hexagon-thunk-range-b22rel.s b/lld/test/ELF/hexagon-thunk-range-b22rel.s
new file mode 100644
index 0000000000000..08e37bf0a5552
--- /dev/null
+++ b/lld/test/ELF/hexagon-thunk-range-b22rel.s
@@ -0,0 +1,115 @@
+# REQUIRES: hexagon
+# RUN: rm -rf %t && split-file %s %t && cd %t
+# RUN: llvm-mc -filetype=obj -triple=hexagon-unknown-elf main.s -o main.o
+# RUN: ld.lld main.o -o test
+# RUN: llvm-objdump -d --no-show-raw-insn test | FileCheck %s
+
+## Test thunk range scenarios for Hexagon R_HEX_B22_PCREL relocations.
+## R_HEX_B22_PCREL has a range of +/- 8MB (0x800000 bytes).
+
+#--- main.s
+.globl _start
+.type _start, %function
+_start:
+ call target_within_range_max
+ call target_beyond_range
+ call target_within_range_min
+ call target_beyond_range_min
+ call target_multiple_calls
+ call target_multiple_calls
+ call target_close
+ jumpr r31
+
+target_close:
+ jumpr r31
+
+## Target at maximum positive range (8MB - 4 bytes from _start)
+## We need to account for the instructions above: 7 calls + 1 jumpr = 8 * 4 = 32 bytes
+.skip 0X7fffbc
+.globl target_within_range_max
+.type target_within_range_max, %function
+target_within_range_max:
+ jumpr r31
+
+## Target just beyond maximum positive range (needs thunk)
+.skip 8
+.globl target_beyond_range
+.type target_beyond_range, %function
+target_beyond_range:
+ call target_within_range_max
+ jumpr r31
+
+## Target for multiple calls test
+.skip 0x100000
+.globl target_multiple_calls
+.type target_multiple_calls, %function
+target_multiple_calls:
+ jumpr r31
+
+## Now place targets at maximum negative range
+## We'll put these before _start in memory layout
+.section .text_negative, "ax", %progbits
+
+## Target at maximum negative range (-8MB + 4 bytes from _start)
+.globl target_within_range_min
+.type target_within_range_min, %function
+target_within_range_min:
+ call target_close
+ jumpr r31
+
+.skip 0X7ffff4
+
+## Target beyond maximum negative range (needs thunk)
+.globl target_beyond_range_min
+.type target_beyond_range_min, %function
+target_beyond_range_min:
+ jumpr r31
+
+## Verify thunk generation for targets beyond B22_PCREL range
+# CHECK: <__hexagon_thunk_target_within_range_min_from_.text.thunk>:
+# CHECK-NEXT: 200b4: { immext(#0x900000)
+# CHECK-NEXT: jump 0x9200cc <target_within_range_min> }
+
+# CHECK: <__hexagon_thunk_target_beyond_range_min_from_.text.thunk>:
+# CHECK-NEXT: 200bc: { immext(#0x1100000)
+# CHECK-NEXT: jump 0x11200c8 <target_beyond_range_min> }
+
+# CHECK: <__hexagon_thunk_target_multiple_calls_from_.text.thunk>:
+# CHECK-NEXT: 200c4: { immext(#0x8fffc0)
+# CHECK-NEXT: jump 0x9200c0 <target_multiple_calls> }
+
+## Verify _start calls - some direct, some via thunks
+# CHECK: <_start>:
+# CHECK-NEXT: 200cc: { call 0x8200ac <target_within_range_max> }
+# CHECK-NEXT: { call 0x8200b8 <target_beyond_range> }
+# CHECK-NEXT: { call 0x200b4 <__hexagon_thunk_target_within_range_min_from_.text.thunk> }
+# CHECK-NEXT: { call 0x200bc <__hexagon_thunk_target_beyond_range_min_from_.text.thunk> }
+# CHECK-NEXT: { call 0x200c4 <__hexagon_thunk_target_multiple_calls_from_.text.thunk> }
+# CHECK-NEXT: { call 0x200c4 <__hexagon_thunk_target_multiple_calls_from_.text.thunk> }
+# CHECK-NEXT: { call 0x200ec <target_close> }
+
+# CHECK: <target_close>:
+# CHECK-NEXT: 200ec: { jumpr r31 }
+
+## Verify targets at maximum positive range (direct calls, no thunks needed)
+# CHECK: <target_within_range_max>:
+# CHECK-NEXT: 8200ac: { jumpr r31 }
+
+# CHECK: <target_beyond_range>:
+# CHECK-NEXT: 8200b8: { call 0x8200ac <target_within_range_max> }
+# CHECK-NEXT: { jumpr r31 }
+
+# CHECK: <target_multiple_calls>:
+# CHECK-NEXT: 9200c0: { jumpr r31 }
+
+## Verify targets in negative section and thunk for calling back to main section
+# CHECK: <__hexagon_thunk__from_.text.thunk>:
+# CHECK-NEXT: 9200c4: { immext(#0xff700000)
+# CHECK-NEXT: jump 0x200cc <_start> }
+
+# CHECK: <target_within_range_min>:
+# CHECK-NEXT: 9200cc: { call 0x9200c4 <__hexagon_thunk__from_.text.thunk> }
+# CHECK-NEXT: { jumpr r31 }
+
+# CHECK: <target_beyond_range_min>:
+# CHECK-NEXT: 11200c8: { jumpr r31 }
diff --git a/lld/test/ELF/hexagon-thunk-range-gdplt.s b/lld/test/ELF/hexagon-thunk-range-gdplt.s
new file mode 100644
index 0000000000000..77fd0e5754568
--- /dev/null
+++ b/lld/test/ELF/hexagon-thunk-range-gdplt.s
@@ -0,0 +1,95 @@
+# REQUIRES: hexagon
+# RUN: rm -rf %t && split-file %s %t && cd %t
+# RUN: llvm-mc -filetype=obj -triple=hexagon-unknown-elf main.s -o main.o
+# RUN: ld.lld -shared main.o -o test.so
+# RUN: llvm-objdump -d --no-show-raw-insn test.so | FileCheck %s
+
+## Test thunk range scenarios for Hexagon R_HEX_GD_PLT_B22_PCREL relocations.
+## Same ±8MB range as regular calls.
+
+#--- main.s
+.globl _start
+.type _start, @function
+_start:
+ ## Setup for TLS Global Dynamic calls
+ r2 = add(pc,##_GLOBAL_OFFSET_TABLE_@PCREL)
+
+ ## Test TLS GD PLT calls
+ r0 = add(r2,##tls_var_close@GDGOT)
+ call tls_var_close@GDPLT
+
+ r0 = add(r2,##tls_var_far@GDGOT)
+ call tls_var_far@GDPLT
+
+ jumpr r31
+
+.skip 0x400000
+
+more_code:
+ r0 = add(r2,##tls_var_distant@GDGOT)
+ call tls_var_distant@GDPLT
+ jumpr r31
+
+## TLS variables in .tdata section
+.section .tdata,"awT",@progbits
+.globl tls_var_close, tls_var_far, tls_var_distant
+.type tls_var_close, @object
+.type tls_var_far, @object
+.type tls_var_distant, @object
+
+tls_var_close:
+ .word 0x1234
+
+tls_var_far:
+ .word 0x5678
+
+tls_var_distant:
+ .word 0x9abc
+
+# CHECK: Disassembly of section .text:
+# CHECK: <_start>:
+# CHECK-NEXT: 102d4: { immext(#0x420100)
+# CHECK-NEXT: r2 = add(pc,##0x420130) }
+# CHECK-NEXT: { immext(#0xfffeffc0)
+# CHECK-NEXT: r0 = add(r2,##-0x10018) }
+# CHECK-NEXT: { call 0x410360 <__tls_get_addr@plt> }
+# CHECK-NEXT: { immext(#0xfffeffc0)
+# CHECK-NEXT: r0 = add(r2,##-0x10010) }
+# CHECK-NEXT: { call 0x410360 <__tls_get_addr@plt> }
+# CHECK-NEXT: { jumpr r31 }
+
+# CHECK: <more_code>:
+# CHECK-NEXT: 4102f8: { immext(#0xfffeffc0)
+# CHECK-NEXT: r0 = add(r2,##-0x10008) }
+# CHECK-NEXT: { call 0x410360 <__tls_get_addr@plt> }
+# CHECK-NEXT: { jumpr r31 }
+
+## Verify PLT entries are created for TLS
+# CHECK: Disassembly of section .plt:
+# CHECK: <.plt>:
+# CHECK-NEXT: 410310: { immext(#0x200c0)
+# CHECK-NEXT: r28 = add(pc,##0x200f4) }
+# 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) }
+
+# CHECK: <tls_var_far@plt>:
+# CHECK-NEXT: 410340: { immext(#0x200c0)
+# CHECK-NEXT: r14 = add(pc,##0x200d8) }
+# CHECK-NEXT: { r28 = memw(r14+#0x0) }
+# CHECK-NEXT: { jumpr r28 }
+
+# CHECK: <tls_var_distant@plt>:
+# CHECK-NEXT: 410350: { immext(#0x200c0)
+# CHECK-NEXT: r14 = add(pc,##0x200cc) }
+# CHECK-NEXT: { r28 = memw(r14+#0x0) }
+# CHECK-NEXT: { jumpr r28 }
+
+# CHECK: <__tls_get_addr@plt>:
+# CHECK-NEXT: 410360: { immext(#0x200c0)
+# CHECK-NEXT: r14 = add(pc,##0x200c0) }
+# CHECK-NEXT: { r28 = memw(r14+#0x0) }
+# CHECK-NEXT: { jumpr r28 }
diff --git a/lld/test/ELF/hexagon-thunk-range-plt.s b/lld/test/ELF/hexagon-thunk-range-plt.s
new file mode 100644
index 0000000000000..3a8f50b681d81
--- /dev/null
+++ b/lld/test/ELF/hexagon-thunk-range-plt.s
@@ -0,0 +1,75 @@
+# REQUIRES: hexagon
+# 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
+# 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
+
+## 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.
+
+#--- 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:
+ jumpr r31
+
+#--- main.s
+.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
+ 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 }
+
+## Verify PLT header and entries are created with exact addresses
+# CHECK: Disassembly of section .plt:
+# CHECK: <.plt>:
+# CHECK-NEXT: 220230: { immext(#0x20080)
+# CHECK-NEXT: ...
[truncated]
|
@llvm/pr-subscribers-backend-hexagon Author: None (llvmbot) ChangesBackport b42f96b Requested by: @androm3da Patch is 27.54 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/149723.diff 9 Files Affected:
diff --git a/lld/ELF/Arch/Hexagon.cpp b/lld/ELF/Arch/Hexagon.cpp
index 479131a24dcfc..9b33e78731c97 100644
--- a/lld/ELF/Arch/Hexagon.cpp
+++ b/lld/ELF/Arch/Hexagon.cpp
@@ -11,6 +11,7 @@
#include "Symbols.h"
#include "SyntheticSections.h"
#include "Target.h"
+#include "Thunks.h"
#include "lld/Common/ErrorHandler.h"
#include "llvm/ADT/SmallVector.h"
#include "llvm/BinaryFormat/ELF.h"
@@ -36,6 +37,10 @@ class Hexagon final : public TargetInfo {
const uint8_t *loc) const override;
RelType getDynRel(RelType type) const override;
int64_t getImplicitAddend(const uint8_t *buf, RelType type) const override;
+ bool needsThunk(RelExpr expr, RelType type, const InputFile *file,
+ uint64_t branchAddr, const Symbol &s,
+ int64_t a) 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;
void writePltHeader(uint8_t *buf) const override;
@@ -63,6 +68,8 @@ Hexagon::Hexagon(Ctx &ctx) : TargetInfo(ctx) {
tlsGotRel = R_HEX_TPREL_32;
tlsModuleIndexRel = R_HEX_DTPMOD_32;
tlsOffsetRel = R_HEX_DTPREL_32;
+
+ needsThunks = true;
}
uint32_t Hexagon::calcEFlags() const {
@@ -258,6 +265,46 @@ static uint32_t findMaskR16(Ctx &ctx, uint32_t insn) {
static void or32le(uint8_t *p, int32_t v) { write32le(p, read32le(p) | v); }
+bool Hexagon::inBranchRange(RelType type, uint64_t src, uint64_t dst) const {
+ int64_t offset = dst - src;
+ switch (type) {
+ case llvm::ELF::R_HEX_B22_PCREL:
+ case llvm::ELF::R_HEX_PLT_B22_PCREL:
+ case llvm::ELF::R_HEX_GD_PLT_B22_PCREL:
+ case llvm::ELF::R_HEX_LD_PLT_B22_PCREL:
+ return llvm::isInt<22>(offset >> 2);
+ case llvm::ELF::R_HEX_B15_PCREL:
+ return llvm::isInt<15>(offset >> 2);
+ break;
+ case llvm::ELF::R_HEX_B13_PCREL:
+ return llvm::isInt<13>(offset >> 2);
+ break;
+ case llvm::ELF::R_HEX_B9_PCREL:
+ return llvm::isInt<9>(offset >> 2);
+ default:
+ return true;
+ }
+ llvm_unreachable("unsupported relocation");
+}
+
+bool Hexagon::needsThunk(RelExpr expr, RelType type, const InputFile *file,
+ uint64_t branchAddr, const Symbol &s,
+ int64_t a) const {
+ // Only check branch range for supported branch relocation types
+ switch (type) {
+ case R_HEX_B22_PCREL:
+ case R_HEX_PLT_B22_PCREL:
+ case R_HEX_GD_PLT_B22_PCREL:
+ 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));
+ default:
+ return false;
+ }
+}
+
void Hexagon::relocate(uint8_t *loc, const Relocation &rel,
uint64_t val) const {
switch (rel.type) {
diff --git a/lld/ELF/Relocations.cpp b/lld/ELF/Relocations.cpp
index cebd564036b2c..41f153e97de77 100644
--- a/lld/ELF/Relocations.cpp
+++ b/lld/ELF/Relocations.cpp
@@ -2139,17 +2139,44 @@ void ThunkCreator::mergeThunks(ArrayRef<OutputSection *> outputSections) {
});
}
-static int64_t getPCBias(Ctx &ctx, RelType type) {
- if (ctx.arg.emachine != EM_ARM)
- return 0;
- switch (type) {
- case R_ARM_THM_JUMP19:
- case R_ARM_THM_JUMP24:
- case R_ARM_THM_CALL:
- return 4;
- default:
- return 8;
+constexpr uint32_t HEXAGON_MASK_END_PACKET = 3 << 14;
+constexpr uint32_t HEXAGON_END_OF_PACKET = 3 << 14;
+constexpr uint32_t HEXAGON_END_OF_DUPLEX = 0 << 14;
+
+// Return the distance between the packet start and the instruction in the
+// relocation.
+static int getHexagonPacketOffset(const InputSection &isec,
+ const Relocation &rel) {
+ const ArrayRef<uint8_t> data = isec.content();
+
+ // Search back as many as 3 instructions.
+ for (unsigned i = 0;; i++) {
+ if (i == 3 || rel.offset < (i + 1) * 4)
+ return i * 4;
+ uint32_t instWord = 0;
+ const ArrayRef<uint8_t> instWordContents =
+ data.drop_front(rel.offset - (i + 1) * 4);
+ memcpy(&instWord, instWordContents.data(), sizeof(instWord));
+ if (((instWord & HEXAGON_MASK_END_PACKET) == HEXAGON_END_OF_PACKET) ||
+ ((instWord & HEXAGON_MASK_END_PACKET) == HEXAGON_END_OF_DUPLEX))
+ return i * 4;
+ }
+}
+static int64_t getPCBias(Ctx &ctx, const InputSection &isec,
+ const Relocation &rel) {
+ if (ctx.arg.emachine == EM_ARM) {
+ switch (rel.type) {
+ case R_ARM_THM_JUMP19:
+ case R_ARM_THM_JUMP24:
+ case R_ARM_THM_CALL:
+ return 4;
+ default:
+ return 8;
+ }
}
+ if (ctx.arg.emachine == EM_HEXAGON)
+ return -getHexagonPacketOffset(isec, rel);
+ return 0;
}
// Find or create a ThunkSection within the InputSectionDescription (ISD) that
@@ -2161,7 +2188,7 @@ ThunkSection *ThunkCreator::getISDThunkSec(OutputSection *os,
const Relocation &rel,
uint64_t src) {
// See the comment in getThunk for -pcBias below.
- const int64_t pcBias = getPCBias(ctx, rel.type);
+ const int64_t pcBias = getPCBias(ctx, *isec, rel);
for (std::pair<ThunkSection *, uint32_t> tp : isd->thunkSections) {
ThunkSection *ts = tp.first;
uint64_t tsBase = os->addr + ts->outSecOff - pcBias;
@@ -2322,7 +2349,7 @@ std::pair<Thunk *, bool> ThunkCreator::getThunk(InputSection *isec,
// out in the relocation addend. We compensate for the PC bias so that
// an Arm and Thumb relocation to the same destination get the same keyAddend,
// which is usually 0.
- const int64_t pcBias = getPCBias(ctx, rel.type);
+ const int64_t pcBias = getPCBias(ctx, *isec, rel);
const int64_t keyAddend = rel.addend + pcBias;
// We use a ((section, offset), addend) pair to find the thunk position if
@@ -2481,7 +2508,7 @@ bool ThunkCreator::createThunks(uint32_t pass,
// STT_SECTION + non-zero addend, clear the addend after
// redirection.
if (ctx.arg.emachine != EM_MIPS)
- rel.addend = -getPCBias(ctx, rel.type);
+ rel.addend = -getPCBias(ctx, *isec, rel);
}
for (auto &p : isd->thunkSections)
diff --git a/lld/ELF/Thunks.cpp b/lld/ELF/Thunks.cpp
index c26ba76bccb7e..65d0f094c43c3 100644
--- a/lld/ELF/Thunks.cpp
+++ b/lld/ELF/Thunks.cpp
@@ -415,6 +415,22 @@ class AVRThunk : public Thunk {
void addSymbols(ThunkSection &isec) override;
};
+// Hexagon CPUs need thunks for R_HEX_B{9,1{3,5},22}_PCREL,
+// R_HEX_{,GD_}PLT_B22_PCREL when their destination is out of
+// range.
+class HexagonThunk : public Thunk {
+public:
+ HexagonThunk(Ctx &ctx, const InputSection &isec, Relocation &rel,
+ Symbol &dest)
+ : Thunk(ctx, dest, 0), relOffset(rel.offset) {
+ alignment = 4;
+ }
+ uint32_t relOffset;
+ uint32_t size() override { return ctx.arg.isPic ? 12 : 8; }
+ void writeTo(uint8_t *buf) override;
+ void addSymbols(ThunkSection &isec) override;
+};
+
// MIPS LA25 thunk
class MipsThunk final : public Thunk {
public:
@@ -1519,6 +1535,39 @@ bool PPC64LongBranchThunk::isCompatibleWith(const InputSection &isec,
return rel.type == R_PPC64_REL24 || rel.type == R_PPC64_REL14;
}
+// Hexagon Target Thunks
+static uint64_t getHexagonThunkDestVA(Ctx &ctx, const Symbol &s, int64_t a) {
+ uint64_t v = s.isInPlt(ctx) ? s.getPltVA(ctx) : s.getVA(ctx, a);
+ return SignExtend64<32>(v);
+}
+
+void HexagonThunk::writeTo(uint8_t *buf) {
+ uint64_t s = getHexagonThunkDestVA(ctx, destination, addend);
+ uint64_t p = getThunkTargetSym()->getVA(ctx);
+
+ if (ctx.arg.isPic) {
+ write32(ctx, buf + 0, 0x00004000); // { immext(#0)
+ ctx.target->relocateNoSym(buf, R_HEX_B32_PCREL_X, s - p);
+ write32(ctx, buf + 4, 0x6a49c00e); // r14 = add(pc,##0) }
+ ctx.target->relocateNoSym(buf + 4, R_HEX_6_PCREL_X, s - p);
+
+ write32(ctx, buf + 8, 0x528ec000); // { jumpr r14 }
+ } else {
+ write32(ctx, buf + 0, 0x00004000); // { immext
+ ctx.target->relocateNoSym(buf, R_HEX_B32_PCREL_X, s - p);
+ write32(ctx, buf + 4, 0x5800c000); // jump <> }
+ ctx.target->relocateNoSym(buf + 4, R_HEX_B22_PCREL_X, s - p);
+ }
+}
+void HexagonThunk::addSymbols(ThunkSection &isec) {
+ Symbol *enclosing = isec.getEnclosingSymbol(relOffset);
+ StringRef src = enclosing ? enclosing->getName() : isec.name;
+
+ addSymbol(
+ saver().save("__hexagon_thunk_" + destination.getName() + "_from_" + src),
+ STT_FUNC, 0, isec);
+}
+
Thunk::Thunk(Ctx &ctx, Symbol &d, int64_t a)
: ctx(ctx), destination(d), addend(a), offset(0) {
destination.thunkAccessed = true;
@@ -1692,6 +1741,24 @@ static std::unique_ptr<Thunk> addThunkAVR(Ctx &ctx, RelType type, Symbol &s,
}
}
+static std::unique_ptr<Thunk> addThunkHexagon(Ctx &ctx,
+ const InputSection &isec,
+ Relocation &rel, Symbol &s) {
+ switch (rel.type) {
+ case R_HEX_B9_PCREL:
+ case R_HEX_B13_PCREL:
+ case R_HEX_B15_PCREL:
+ case R_HEX_B22_PCREL:
+ case R_HEX_PLT_B22_PCREL:
+ case R_HEX_GD_PLT_B22_PCREL:
+ return std::make_unique<HexagonThunk>(ctx, isec, rel, s);
+ default:
+ Fatal(ctx) << "unrecognized relocation " << rel.type << " to " << &s
+ << " for hexagon target";
+ llvm_unreachable("");
+ }
+}
+
static std::unique_ptr<Thunk> addThunkMips(Ctx &ctx, RelType type, Symbol &s) {
if ((s.stOther & STO_MIPS_MICROMIPS) && isMipsR6(ctx))
return std::make_unique<MicroMipsR6Thunk>(ctx, s);
@@ -1761,8 +1828,11 @@ std::unique_ptr<Thunk> elf::addThunk(Ctx &ctx, const InputSection &isec,
return addThunkPPC32(ctx, isec, rel, s);
case EM_PPC64:
return addThunkPPC64(ctx, rel.type, s, a);
+ case EM_HEXAGON:
+ return addThunkHexagon(ctx, isec, rel, s);
default:
- llvm_unreachable("add Thunk only supported for ARM, AVR, Mips and PowerPC");
+ llvm_unreachable(
+ "add Thunk only supported for ARM, AVR, Hexagon, Mips and PowerPC");
}
}
diff --git a/lld/test/ELF/hexagon-jump-error.s b/lld/test/ELF/hexagon-jump-error.s
deleted file mode 100644
index 53860b5daf2b1..0000000000000
--- a/lld/test/ELF/hexagon-jump-error.s
+++ /dev/null
@@ -1,32 +0,0 @@
-# REQUIRES: hexagon
-# RUN: llvm-mc -filetype=obj -triple=hexagon-unknown-elf %s -o %t.o
-## Use --threads=1 to keep emitted warnings across sections sequential.
-# RUN: not ld.lld %t.o -o /dev/null --threads=1 2>&1 | FileCheck --implicit-check-not "out of range" %s
-
- .globl _start
- .type _start, @function
-_start:
-
-# CHECK: relocation R_HEX_B9_PCREL out of range: 1028 is not in [-1024, 1023]
-{r0 = #0; jump #1f}
-.space (1<<10)
-.section b9, "ax"
-1:
-
-# CHECK: relocation R_HEX_B13_PCREL out of range: 16388 is not in [-16384, 16383]
-if (r0==#0) jump:t #1f
-.space (1<<14)
-.section b13, "ax"
-1:
-
-# CHECK: relocation R_HEX_B15_PCREL out of range: 65540 is not in [-65536, 65535]
-if (p0) jump #1f
-.space (1<<16)
-.section b15, "ax"
-1:
-
-# CHECK: relocation R_HEX_B22_PCREL out of range: 8388612 is not in [-8388608, 8388607]
-jump #1f
-.space (1<<23)
-.section b22, "ax"
-1:
diff --git a/lld/test/ELF/hexagon-thunk-range-b22rel.s b/lld/test/ELF/hexagon-thunk-range-b22rel.s
new file mode 100644
index 0000000000000..08e37bf0a5552
--- /dev/null
+++ b/lld/test/ELF/hexagon-thunk-range-b22rel.s
@@ -0,0 +1,115 @@
+# REQUIRES: hexagon
+# RUN: rm -rf %t && split-file %s %t && cd %t
+# RUN: llvm-mc -filetype=obj -triple=hexagon-unknown-elf main.s -o main.o
+# RUN: ld.lld main.o -o test
+# RUN: llvm-objdump -d --no-show-raw-insn test | FileCheck %s
+
+## Test thunk range scenarios for Hexagon R_HEX_B22_PCREL relocations.
+## R_HEX_B22_PCREL has a range of +/- 8MB (0x800000 bytes).
+
+#--- main.s
+.globl _start
+.type _start, %function
+_start:
+ call target_within_range_max
+ call target_beyond_range
+ call target_within_range_min
+ call target_beyond_range_min
+ call target_multiple_calls
+ call target_multiple_calls
+ call target_close
+ jumpr r31
+
+target_close:
+ jumpr r31
+
+## Target at maximum positive range (8MB - 4 bytes from _start)
+## We need to account for the instructions above: 7 calls + 1 jumpr = 8 * 4 = 32 bytes
+.skip 0X7fffbc
+.globl target_within_range_max
+.type target_within_range_max, %function
+target_within_range_max:
+ jumpr r31
+
+## Target just beyond maximum positive range (needs thunk)
+.skip 8
+.globl target_beyond_range
+.type target_beyond_range, %function
+target_beyond_range:
+ call target_within_range_max
+ jumpr r31
+
+## Target for multiple calls test
+.skip 0x100000
+.globl target_multiple_calls
+.type target_multiple_calls, %function
+target_multiple_calls:
+ jumpr r31
+
+## Now place targets at maximum negative range
+## We'll put these before _start in memory layout
+.section .text_negative, "ax", %progbits
+
+## Target at maximum negative range (-8MB + 4 bytes from _start)
+.globl target_within_range_min
+.type target_within_range_min, %function
+target_within_range_min:
+ call target_close
+ jumpr r31
+
+.skip 0X7ffff4
+
+## Target beyond maximum negative range (needs thunk)
+.globl target_beyond_range_min
+.type target_beyond_range_min, %function
+target_beyond_range_min:
+ jumpr r31
+
+## Verify thunk generation for targets beyond B22_PCREL range
+# CHECK: <__hexagon_thunk_target_within_range_min_from_.text.thunk>:
+# CHECK-NEXT: 200b4: { immext(#0x900000)
+# CHECK-NEXT: jump 0x9200cc <target_within_range_min> }
+
+# CHECK: <__hexagon_thunk_target_beyond_range_min_from_.text.thunk>:
+# CHECK-NEXT: 200bc: { immext(#0x1100000)
+# CHECK-NEXT: jump 0x11200c8 <target_beyond_range_min> }
+
+# CHECK: <__hexagon_thunk_target_multiple_calls_from_.text.thunk>:
+# CHECK-NEXT: 200c4: { immext(#0x8fffc0)
+# CHECK-NEXT: jump 0x9200c0 <target_multiple_calls> }
+
+## Verify _start calls - some direct, some via thunks
+# CHECK: <_start>:
+# CHECK-NEXT: 200cc: { call 0x8200ac <target_within_range_max> }
+# CHECK-NEXT: { call 0x8200b8 <target_beyond_range> }
+# CHECK-NEXT: { call 0x200b4 <__hexagon_thunk_target_within_range_min_from_.text.thunk> }
+# CHECK-NEXT: { call 0x200bc <__hexagon_thunk_target_beyond_range_min_from_.text.thunk> }
+# CHECK-NEXT: { call 0x200c4 <__hexagon_thunk_target_multiple_calls_from_.text.thunk> }
+# CHECK-NEXT: { call 0x200c4 <__hexagon_thunk_target_multiple_calls_from_.text.thunk> }
+# CHECK-NEXT: { call 0x200ec <target_close> }
+
+# CHECK: <target_close>:
+# CHECK-NEXT: 200ec: { jumpr r31 }
+
+## Verify targets at maximum positive range (direct calls, no thunks needed)
+# CHECK: <target_within_range_max>:
+# CHECK-NEXT: 8200ac: { jumpr r31 }
+
+# CHECK: <target_beyond_range>:
+# CHECK-NEXT: 8200b8: { call 0x8200ac <target_within_range_max> }
+# CHECK-NEXT: { jumpr r31 }
+
+# CHECK: <target_multiple_calls>:
+# CHECK-NEXT: 9200c0: { jumpr r31 }
+
+## Verify targets in negative section and thunk for calling back to main section
+# CHECK: <__hexagon_thunk__from_.text.thunk>:
+# CHECK-NEXT: 9200c4: { immext(#0xff700000)
+# CHECK-NEXT: jump 0x200cc <_start> }
+
+# CHECK: <target_within_range_min>:
+# CHECK-NEXT: 9200cc: { call 0x9200c4 <__hexagon_thunk__from_.text.thunk> }
+# CHECK-NEXT: { jumpr r31 }
+
+# CHECK: <target_beyond_range_min>:
+# CHECK-NEXT: 11200c8: { jumpr r31 }
diff --git a/lld/test/ELF/hexagon-thunk-range-gdplt.s b/lld/test/ELF/hexagon-thunk-range-gdplt.s
new file mode 100644
index 0000000000000..77fd0e5754568
--- /dev/null
+++ b/lld/test/ELF/hexagon-thunk-range-gdplt.s
@@ -0,0 +1,95 @@
+# REQUIRES: hexagon
+# RUN: rm -rf %t && split-file %s %t && cd %t
+# RUN: llvm-mc -filetype=obj -triple=hexagon-unknown-elf main.s -o main.o
+# RUN: ld.lld -shared main.o -o test.so
+# RUN: llvm-objdump -d --no-show-raw-insn test.so | FileCheck %s
+
+## Test thunk range scenarios for Hexagon R_HEX_GD_PLT_B22_PCREL relocations.
+## Same ±8MB range as regular calls.
+
+#--- main.s
+.globl _start
+.type _start, @function
+_start:
+ ## Setup for TLS Global Dynamic calls
+ r2 = add(pc,##_GLOBAL_OFFSET_TABLE_@PCREL)
+
+ ## Test TLS GD PLT calls
+ r0 = add(r2,##tls_var_close@GDGOT)
+ call tls_var_close@GDPLT
+
+ r0 = add(r2,##tls_var_far@GDGOT)
+ call tls_var_far@GDPLT
+
+ jumpr r31
+
+.skip 0x400000
+
+more_code:
+ r0 = add(r2,##tls_var_distant@GDGOT)
+ call tls_var_distant@GDPLT
+ jumpr r31
+
+## TLS variables in .tdata section
+.section .tdata,"awT",@progbits
+.globl tls_var_close, tls_var_far, tls_var_distant
+.type tls_var_close, @object
+.type tls_var_far, @object
+.type tls_var_distant, @object
+
+tls_var_close:
+ .word 0x1234
+
+tls_var_far:
+ .word 0x5678
+
+tls_var_distant:
+ .word 0x9abc
+
+# CHECK: Disassembly of section .text:
+# CHECK: <_start>:
+# CHECK-NEXT: 102d4: { immext(#0x420100)
+# CHECK-NEXT: r2 = add(pc,##0x420130) }
+# CHECK-NEXT: { immext(#0xfffeffc0)
+# CHECK-NEXT: r0 = add(r2,##-0x10018) }
+# CHECK-NEXT: { call 0x410360 <__tls_get_addr@plt> }
+# CHECK-NEXT: { immext(#0xfffeffc0)
+# CHECK-NEXT: r0 = add(r2,##-0x10010) }
+# CHECK-NEXT: { call 0x410360 <__tls_get_addr@plt> }
+# CHECK-NEXT: { jumpr r31 }
+
+# CHECK: <more_code>:
+# CHECK-NEXT: 4102f8: { immext(#0xfffeffc0)
+# CHECK-NEXT: r0 = add(r2,##-0x10008) }
+# CHECK-NEXT: { call 0x410360 <__tls_get_addr@plt> }
+# CHECK-NEXT: { jumpr r31 }
+
+## Verify PLT entries are created for TLS
+# CHECK: Disassembly of section .plt:
+# CHECK: <.plt>:
+# CHECK-NEXT: 410310: { immext(#0x200c0)
+# CHECK-NEXT: r28 = add(pc,##0x200f4) }
+# 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) }
+
+# CHECK: <tls_var_far@plt>:
+# CHECK-NEXT: 410340: { immext(#0x200c0)
+# CHECK-NEXT: r14 = add(pc,##0x200d8) }
+# CHECK-NEXT: { r28 = memw(r14+#0x0) }
+# CHECK-NEXT: { jumpr r28 }
+
+# CHECK: <tls_var_distant@plt>:
+# CHECK-NEXT: 410350: { immext(#0x200c0)
+# CHECK-NEXT: r14 = add(pc,##0x200cc) }
+# CHECK-NEXT: { r28 = memw(r14+#0x0) }
+# CHECK-NEXT: { jumpr r28 }
+
+# CHECK: <__tls_get_addr@plt>:
+# CHECK-NEXT: 410360: { immext(#0x200c0)
+# CHECK-NEXT: r14 = add(pc,##0x200c0) }
+# CHECK-NEXT: { r28 = memw(r14+#0x0) }
+# CHECK-NEXT: { jumpr r28 }
diff --git a/lld/test/ELF/hexagon-thunk-range-plt.s b/lld/test/ELF/hexagon-thunk-range-plt.s
new file mode 100644
index 0000000000000..3a8f50b681d81
--- /dev/null
+++ b/lld/test/ELF/hexagon-thunk-range-plt.s
@@ -0,0 +1,75 @@
+# REQUIRES: hexagon
+# 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
+# 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
+
+## 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.
+
+#--- 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:
+ jumpr r31
+
+#--- main.s
+.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
+ 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 }
+
+## Verify PLT header and entries are created with exact addresses
+# CHECK: Disassembly of section .plt:
+# CHECK: <.plt>:
+# CHECK-NEXT: 220230: { immext(#0x20080)
+# CHECK-NEXT: ...
[truncated]
|
Backport b42f96b
Requested by: @androm3da