Skip to content

Commit 760616d

Browse files
androm3daquic-akaryaki
authored andcommitted
[lld] Add thunks for hexagon (#111217)
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 #149689 Co-authored-by: Alexey Karyakin <akaryaki@quicinc.com> --------- Co-authored-by: Alexey Karyakin <akaryaki@quicinc.com> (cherry picked from commit b42f96b)
1 parent 02b2a77 commit 760616d

9 files changed

+618
-46
lines changed

lld/ELF/Arch/Hexagon.cpp

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
#include "Symbols.h"
1212
#include "SyntheticSections.h"
1313
#include "Target.h"
14+
#include "Thunks.h"
1415
#include "lld/Common/ErrorHandler.h"
1516
#include "llvm/ADT/SmallVector.h"
1617
#include "llvm/BinaryFormat/ELF.h"
@@ -36,6 +37,10 @@ class Hexagon final : public TargetInfo {
3637
const uint8_t *loc) const override;
3738
RelType getDynRel(RelType type) const override;
3839
int64_t getImplicitAddend(const uint8_t *buf, RelType type) const override;
40+
bool needsThunk(RelExpr expr, RelType type, const InputFile *file,
41+
uint64_t branchAddr, const Symbol &s,
42+
int64_t a) const override;
43+
bool inBranchRange(RelType type, uint64_t src, uint64_t dst) const override;
3944
void relocate(uint8_t *loc, const Relocation &rel,
4045
uint64_t val) const override;
4146
void writePltHeader(uint8_t *buf) const override;
@@ -63,6 +68,8 @@ Hexagon::Hexagon(Ctx &ctx) : TargetInfo(ctx) {
6368
tlsGotRel = R_HEX_TPREL_32;
6469
tlsModuleIndexRel = R_HEX_DTPMOD_32;
6570
tlsOffsetRel = R_HEX_DTPREL_32;
71+
72+
needsThunks = true;
6673
}
6774

6875
uint32_t Hexagon::calcEFlags() const {
@@ -258,6 +265,46 @@ static uint32_t findMaskR16(Ctx &ctx, uint32_t insn) {
258265

259266
static void or32le(uint8_t *p, int32_t v) { write32le(p, read32le(p) | v); }
260267

268+
bool Hexagon::inBranchRange(RelType type, uint64_t src, uint64_t dst) const {
269+
int64_t offset = dst - src;
270+
switch (type) {
271+
case llvm::ELF::R_HEX_B22_PCREL:
272+
case llvm::ELF::R_HEX_PLT_B22_PCREL:
273+
case llvm::ELF::R_HEX_GD_PLT_B22_PCREL:
274+
case llvm::ELF::R_HEX_LD_PLT_B22_PCREL:
275+
return llvm::isInt<22>(offset >> 2);
276+
case llvm::ELF::R_HEX_B15_PCREL:
277+
return llvm::isInt<15>(offset >> 2);
278+
break;
279+
case llvm::ELF::R_HEX_B13_PCREL:
280+
return llvm::isInt<13>(offset >> 2);
281+
break;
282+
case llvm::ELF::R_HEX_B9_PCREL:
283+
return llvm::isInt<9>(offset >> 2);
284+
default:
285+
return true;
286+
}
287+
llvm_unreachable("unsupported relocation");
288+
}
289+
290+
bool Hexagon::needsThunk(RelExpr expr, RelType type, const InputFile *file,
291+
uint64_t branchAddr, const Symbol &s,
292+
int64_t a) const {
293+
// Only check branch range for supported branch relocation types
294+
switch (type) {
295+
case R_HEX_B22_PCREL:
296+
case R_HEX_PLT_B22_PCREL:
297+
case R_HEX_GD_PLT_B22_PCREL:
298+
case R_HEX_LD_PLT_B22_PCREL:
299+
case R_HEX_B15_PCREL:
300+
case R_HEX_B13_PCREL:
301+
case R_HEX_B9_PCREL:
302+
return !ctx.target->inBranchRange(type, branchAddr, s.getVA(ctx, a));
303+
default:
304+
return false;
305+
}
306+
}
307+
261308
void Hexagon::relocate(uint8_t *loc, const Relocation &rel,
262309
uint64_t val) const {
263310
switch (rel.type) {

lld/ELF/Relocations.cpp

Lines changed: 40 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -2139,17 +2139,44 @@ void ThunkCreator::mergeThunks(ArrayRef<OutputSection *> outputSections) {
21392139
});
21402140
}
21412141

2142-
static int64_t getPCBias(Ctx &ctx, RelType type) {
2143-
if (ctx.arg.emachine != EM_ARM)
2144-
return 0;
2145-
switch (type) {
2146-
case R_ARM_THM_JUMP19:
2147-
case R_ARM_THM_JUMP24:
2148-
case R_ARM_THM_CALL:
2149-
return 4;
2150-
default:
2151-
return 8;
2142+
constexpr uint32_t HEXAGON_MASK_END_PACKET = 3 << 14;
2143+
constexpr uint32_t HEXAGON_END_OF_PACKET = 3 << 14;
2144+
constexpr uint32_t HEXAGON_END_OF_DUPLEX = 0 << 14;
2145+
2146+
// Return the distance between the packet start and the instruction in the
2147+
// relocation.
2148+
static int getHexagonPacketOffset(const InputSection &isec,
2149+
const Relocation &rel) {
2150+
const ArrayRef<uint8_t> data = isec.content();
2151+
2152+
// Search back as many as 3 instructions.
2153+
for (unsigned i = 0;; i++) {
2154+
if (i == 3 || rel.offset < (i + 1) * 4)
2155+
return i * 4;
2156+
uint32_t instWord = 0;
2157+
const ArrayRef<uint8_t> instWordContents =
2158+
data.drop_front(rel.offset - (i + 1) * 4);
2159+
memcpy(&instWord, instWordContents.data(), sizeof(instWord));
2160+
if (((instWord & HEXAGON_MASK_END_PACKET) == HEXAGON_END_OF_PACKET) ||
2161+
((instWord & HEXAGON_MASK_END_PACKET) == HEXAGON_END_OF_DUPLEX))
2162+
return i * 4;
2163+
}
2164+
}
2165+
static int64_t getPCBias(Ctx &ctx, const InputSection &isec,
2166+
const Relocation &rel) {
2167+
if (ctx.arg.emachine == EM_ARM) {
2168+
switch (rel.type) {
2169+
case R_ARM_THM_JUMP19:
2170+
case R_ARM_THM_JUMP24:
2171+
case R_ARM_THM_CALL:
2172+
return 4;
2173+
default:
2174+
return 8;
2175+
}
21522176
}
2177+
if (ctx.arg.emachine == EM_HEXAGON)
2178+
return -getHexagonPacketOffset(isec, rel);
2179+
return 0;
21532180
}
21542181

21552182
// Find or create a ThunkSection within the InputSectionDescription (ISD) that
@@ -2161,7 +2188,7 @@ ThunkSection *ThunkCreator::getISDThunkSec(OutputSection *os,
21612188
const Relocation &rel,
21622189
uint64_t src) {
21632190
// See the comment in getThunk for -pcBias below.
2164-
const int64_t pcBias = getPCBias(ctx, rel.type);
2191+
const int64_t pcBias = getPCBias(ctx, *isec, rel);
21652192
for (std::pair<ThunkSection *, uint32_t> tp : isd->thunkSections) {
21662193
ThunkSection *ts = tp.first;
21672194
uint64_t tsBase = os->addr + ts->outSecOff - pcBias;
@@ -2322,7 +2349,7 @@ std::pair<Thunk *, bool> ThunkCreator::getThunk(InputSection *isec,
23222349
// out in the relocation addend. We compensate for the PC bias so that
23232350
// an Arm and Thumb relocation to the same destination get the same keyAddend,
23242351
// which is usually 0.
2325-
const int64_t pcBias = getPCBias(ctx, rel.type);
2352+
const int64_t pcBias = getPCBias(ctx, *isec, rel);
23262353
const int64_t keyAddend = rel.addend + pcBias;
23272354

23282355
// We use a ((section, offset), addend) pair to find the thunk position if
@@ -2481,7 +2508,7 @@ bool ThunkCreator::createThunks(uint32_t pass,
24812508
// STT_SECTION + non-zero addend, clear the addend after
24822509
// redirection.
24832510
if (ctx.arg.emachine != EM_MIPS)
2484-
rel.addend = -getPCBias(ctx, rel.type);
2511+
rel.addend = -getPCBias(ctx, *isec, rel);
24852512
}
24862513

24872514
for (auto &p : isd->thunkSections)

lld/ELF/Thunks.cpp

Lines changed: 71 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -415,6 +415,22 @@ class AVRThunk : public Thunk {
415415
void addSymbols(ThunkSection &isec) override;
416416
};
417417

418+
// Hexagon CPUs need thunks for R_HEX_B{9,1{3,5},22}_PCREL,
419+
// R_HEX_{,GD_}PLT_B22_PCREL when their destination is out of
420+
// range.
421+
class HexagonThunk : public Thunk {
422+
public:
423+
HexagonThunk(Ctx &ctx, const InputSection &isec, Relocation &rel,
424+
Symbol &dest)
425+
: Thunk(ctx, dest, 0), relOffset(rel.offset) {
426+
alignment = 4;
427+
}
428+
uint32_t relOffset;
429+
uint32_t size() override { return ctx.arg.isPic ? 12 : 8; }
430+
void writeTo(uint8_t *buf) override;
431+
void addSymbols(ThunkSection &isec) override;
432+
};
433+
418434
// MIPS LA25 thunk
419435
class MipsThunk final : public Thunk {
420436
public:
@@ -1519,6 +1535,39 @@ bool PPC64LongBranchThunk::isCompatibleWith(const InputSection &isec,
15191535
return rel.type == R_PPC64_REL24 || rel.type == R_PPC64_REL14;
15201536
}
15211537

1538+
// Hexagon Target Thunks
1539+
static uint64_t getHexagonThunkDestVA(Ctx &ctx, const Symbol &s, int64_t a) {
1540+
uint64_t v = s.isInPlt(ctx) ? s.getPltVA(ctx) : s.getVA(ctx, a);
1541+
return SignExtend64<32>(v);
1542+
}
1543+
1544+
void HexagonThunk::writeTo(uint8_t *buf) {
1545+
uint64_t s = getHexagonThunkDestVA(ctx, destination, addend);
1546+
uint64_t p = getThunkTargetSym()->getVA(ctx);
1547+
1548+
if (ctx.arg.isPic) {
1549+
write32(ctx, buf + 0, 0x00004000); // { immext(#0)
1550+
ctx.target->relocateNoSym(buf, R_HEX_B32_PCREL_X, s - p);
1551+
write32(ctx, buf + 4, 0x6a49c00e); // r14 = add(pc,##0) }
1552+
ctx.target->relocateNoSym(buf + 4, R_HEX_6_PCREL_X, s - p);
1553+
1554+
write32(ctx, buf + 8, 0x528ec000); // { jumpr r14 }
1555+
} else {
1556+
write32(ctx, buf + 0, 0x00004000); // { immext
1557+
ctx.target->relocateNoSym(buf, R_HEX_B32_PCREL_X, s - p);
1558+
write32(ctx, buf + 4, 0x5800c000); // jump <> }
1559+
ctx.target->relocateNoSym(buf + 4, R_HEX_B22_PCREL_X, s - p);
1560+
}
1561+
}
1562+
void HexagonThunk::addSymbols(ThunkSection &isec) {
1563+
Symbol *enclosing = isec.getEnclosingSymbol(relOffset);
1564+
StringRef src = enclosing ? enclosing->getName() : isec.name;
1565+
1566+
addSymbol(
1567+
saver().save("__hexagon_thunk_" + destination.getName() + "_from_" + src),
1568+
STT_FUNC, 0, isec);
1569+
}
1570+
15221571
Thunk::Thunk(Ctx &ctx, Symbol &d, int64_t a)
15231572
: ctx(ctx), destination(d), addend(a), offset(0) {
15241573
destination.thunkAccessed = true;
@@ -1692,6 +1741,24 @@ static std::unique_ptr<Thunk> addThunkAVR(Ctx &ctx, RelType type, Symbol &s,
16921741
}
16931742
}
16941743

1744+
static std::unique_ptr<Thunk> addThunkHexagon(Ctx &ctx,
1745+
const InputSection &isec,
1746+
Relocation &rel, Symbol &s) {
1747+
switch (rel.type) {
1748+
case R_HEX_B9_PCREL:
1749+
case R_HEX_B13_PCREL:
1750+
case R_HEX_B15_PCREL:
1751+
case R_HEX_B22_PCREL:
1752+
case R_HEX_PLT_B22_PCREL:
1753+
case R_HEX_GD_PLT_B22_PCREL:
1754+
return std::make_unique<HexagonThunk>(ctx, isec, rel, s);
1755+
default:
1756+
Fatal(ctx) << "unrecognized relocation " << rel.type << " to " << &s
1757+
<< " for hexagon target";
1758+
llvm_unreachable("");
1759+
}
1760+
}
1761+
16951762
static std::unique_ptr<Thunk> addThunkMips(Ctx &ctx, RelType type, Symbol &s) {
16961763
if ((s.stOther & STO_MIPS_MICROMIPS) && isMipsR6(ctx))
16971764
return std::make_unique<MicroMipsR6Thunk>(ctx, s);
@@ -1761,8 +1828,11 @@ std::unique_ptr<Thunk> elf::addThunk(Ctx &ctx, const InputSection &isec,
17611828
return addThunkPPC32(ctx, isec, rel, s);
17621829
case EM_PPC64:
17631830
return addThunkPPC64(ctx, rel.type, s, a);
1831+
case EM_HEXAGON:
1832+
return addThunkHexagon(ctx, isec, rel, s);
17641833
default:
1765-
llvm_unreachable("add Thunk only supported for ARM, AVR, Mips and PowerPC");
1834+
llvm_unreachable(
1835+
"add Thunk only supported for ARM, AVR, Hexagon, Mips and PowerPC");
17661836
}
17671837
}
17681838

lld/test/ELF/hexagon-jump-error.s

Lines changed: 0 additions & 32 deletions
This file was deleted.

0 commit comments

Comments
 (0)