Skip to content

Commit 803d140

Browse files
committed
[ELF] Implement R_RISCV_TLSDESC for RISC-V
Support R_RISCV_TLSDESC_HI20/R_RISCV_TLSDESC_LOAD_LO12_I/R_RISCV_TLSDESC_ADD_LO12_I/R_RISCV_TLSDESC_CALL. LOAD_LO12_I/ADD_LO12_I/CALL relocations reference a label at the HI20 location, which requires special handling. We save the value of HI20 to be reused. For -no-pie/-pie links, TLSDESC to initial-exec or local-exec optimizations are eligible. Implement the relevant hooks (R_RELAX_TLS_GD_TO_LE, R_RELAX_TLS_GD_TO_IE). AUIPC/L[DW] for initial-exec uses GOT offsets, which may be adjusted in the presence of linker relaxation. ``` // TLSDESC to LE/IE optimization .Ltlsdesc_hi2: auipc a4, %tlsdesc_hi(c) # if R_RISCV_RELAX: remove; otherwise, NOP load a5, %tlsdesc_load_lo(.Ltlsdesc_hi2)(a4) # if R_RISCV_RELAX: remove; otherwise, NOP addi a0, a4, %tlsdesc_add_lo(.Ltlsdesc_hi2) jalr t0, 0(a5), %tlsdesc_call(.Ltlsdesc_hi2) add a0, a0, tp ``` * `riscv64-tlsdesc.s` is inspired by `i386-tlsdesc-gd.s` (https://reviews.llvm.org/D112582). * `riscv64-tlsdesc-relax.s` tests linker relaxation. DO NOT SUBMIT: rebase after #66915 lands
1 parent cdf3804 commit 803d140

File tree

5 files changed

+492
-34
lines changed

5 files changed

+492
-34
lines changed

lld/ELF/Arch/RISCV.cpp

+132-26
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@ enum Op {
6161
AUIPC = 0x17,
6262
JALR = 0x67,
6363
LD = 0x3003,
64+
LUI = 0x37,
6465
LW = 0x2003,
6566
SRLI = 0x5013,
6667
SUB = 0x40000033,
@@ -73,6 +74,7 @@ enum Reg {
7374
X_T0 = 5,
7475
X_T1 = 6,
7576
X_T2 = 7,
77+
X_A0 = 10,
7678
X_T3 = 28,
7779
};
7880

@@ -102,6 +104,26 @@ static uint32_t setLO12_S(uint32_t insn, uint32_t imm) {
102104
(extractBits(imm, 4, 0) << 7);
103105
}
104106

107+
namespace {
108+
struct SymbolAnchor {
109+
uint64_t offset;
110+
Defined *d;
111+
bool end; // true for the anchor of st_value+st_size
112+
};
113+
} // namespace
114+
115+
struct elf::RISCVRelaxAux {
116+
// This records symbol start and end offsets which will be adjusted according
117+
// to the nearest relocDeltas element.
118+
SmallVector<SymbolAnchor, 0> anchors;
119+
// For relocations[i], the actual offset is r_offset - (i ? relocDeltas[i-1] :
120+
// 0).
121+
std::unique_ptr<uint32_t[]> relocDeltas;
122+
// For relocations[i], the actual type is relocTypes[i].
123+
std::unique_ptr<RelType[]> relocTypes;
124+
SmallVector<uint32_t, 0> writes;
125+
};
126+
105127
RISCV::RISCV() {
106128
copyRel = R_RISCV_COPY;
107129
pltRel = R_RISCV_JUMP_SLOT;
@@ -119,6 +141,7 @@ RISCV::RISCV() {
119141
tlsGotRel = R_RISCV_TLS_TPREL32;
120142
}
121143
gotRel = symbolicRel;
144+
tlsDescRel = R_RISCV_TLSDESC;
122145

123146
// .got[0] = _DYNAMIC
124147
gotHeaderEntriesNum = 1;
@@ -187,6 +210,8 @@ int64_t RISCV::getImplicitAddend(const uint8_t *buf, RelType type) const {
187210
case R_RISCV_JUMP_SLOT:
188211
// These relocations are defined as not having an implicit addend.
189212
return 0;
213+
case R_RISCV_TLSDESC:
214+
return config->is64 ? read64le(buf + 8) : read32le(buf + 4);
190215
}
191216
}
192217

@@ -295,6 +320,12 @@ RelExpr RISCV::getRelExpr(const RelType type, const Symbol &s,
295320
case R_RISCV_PCREL_LO12_I:
296321
case R_RISCV_PCREL_LO12_S:
297322
return R_RISCV_PC_INDIRECT;
323+
case R_RISCV_TLSDESC_HI20:
324+
case R_RISCV_TLSDESC_LOAD_LO12:
325+
case R_RISCV_TLSDESC_ADD_LO12:
326+
return R_TLSDESC_PC;
327+
case R_RISCV_TLSDESC_CALL:
328+
return R_TLSDESC_CALL;
298329
case R_RISCV_TLS_GD_HI20:
299330
return R_TLSGD_PC;
300331
case R_RISCV_TLS_GOT_HI20:
@@ -419,6 +450,7 @@ void RISCV::relocate(uint8_t *loc, const Relocation &rel, uint64_t val) const {
419450

420451
case R_RISCV_GOT_HI20:
421452
case R_RISCV_PCREL_HI20:
453+
case R_RISCV_TLSDESC_HI20:
422454
case R_RISCV_TLS_GD_HI20:
423455
case R_RISCV_TLS_GOT_HI20:
424456
case R_RISCV_TPREL_HI20:
@@ -430,6 +462,8 @@ void RISCV::relocate(uint8_t *loc, const Relocation &rel, uint64_t val) const {
430462
}
431463

432464
case R_RISCV_PCREL_LO12_I:
465+
case R_RISCV_TLSDESC_LOAD_LO12:
466+
case R_RISCV_TLSDESC_ADD_LO12:
433467
case R_RISCV_TPREL_LO12_I:
434468
case R_RISCV_LO12_I: {
435469
uint64_t hi = (val + 0x800) >> 12;
@@ -513,29 +547,113 @@ void RISCV::relocate(uint8_t *loc, const Relocation &rel, uint64_t val) const {
513547
break;
514548

515549
case R_RISCV_RELAX:
516-
return; // Ignored (for now)
517-
550+
return;
551+
case R_RISCV_TLSDESC:
552+
// The addend is stored in the second word.
553+
if (config->is64)
554+
write64le(loc + 8, val);
555+
else
556+
write32le(loc + 4, val);
557+
break;
518558
default:
519559
llvm_unreachable("unknown relocation");
520560
}
521561
}
522562

563+
static void tlsdescToIe(uint8_t *loc, const Relocation &rel, uint64_t val) {
564+
switch (rel.type) {
565+
case R_RISCV_TLSDESC_HI20:
566+
case R_RISCV_TLSDESC_LOAD_LO12:
567+
write32le(loc, 0x00000013); // nop
568+
return;
569+
case R_RISCV_TLSDESC_ADD_LO12:
570+
write32le(loc, utype(AUIPC, X_A0, hi20(val))); // auipc a0,<hi20>
571+
return;
572+
case R_RISCV_TLSDESC_CALL:
573+
if (config->is64)
574+
write32le(loc, itype(LD, X_A0, X_A0, lo12(val))); // ld a0,<lo12>(a0)
575+
else
576+
write32le(loc, itype(LW, X_A0, X_A0, lo12(val))); // lw a0,<lo12>(a0)
577+
return;
578+
default:
579+
llvm_unreachable("unsupported relocation for TLSDESC to IE relaxation");
580+
}
581+
}
582+
583+
static void tlsdescToLe(uint8_t *loc, const Relocation &rel, uint64_t val) {
584+
switch (rel.type) {
585+
case R_RISCV_TLSDESC_HI20:
586+
case R_RISCV_TLSDESC_LOAD_LO12:
587+
write32le(loc, 0x00000013); // nop
588+
return;
589+
case R_RISCV_TLSDESC_ADD_LO12:
590+
write32le(loc, utype(LUI, X_A0, hi20(val))); // lui a0,<hi20>
591+
return;
592+
case R_RISCV_TLSDESC_CALL:
593+
if (isInt<12>(val))
594+
write32le(loc, itype(ADDI, X_A0, 0, val)); // addi a0,zero,<lo12>
595+
else
596+
write32le(loc, itype(ADDI, X_A0, X_A0, lo12(val))); // addi a0,a0,<lo12>
597+
return;
598+
default:
599+
llvm_unreachable("unsupported relocation for TLSDESC to LE relaxation");
600+
}
601+
}
602+
523603
void RISCV::relocateAlloc(InputSectionBase &sec, uint8_t *buf) const {
524604
uint64_t secAddr = sec.getOutputSection()->addr;
525605
if (auto *s = dyn_cast<InputSection>(&sec))
526606
secAddr += s->outSecOff;
527607
else if (auto *ehIn = dyn_cast<EhInputSection>(&sec))
528608
secAddr += ehIn->getParent()->outSecOff;
529-
for (size_t i = 0, size = sec.relocs().size(); i != size; ++i) {
530-
const Relocation &rel = sec.relocs()[i];
609+
uint64_t tlsdescVal = 0;
610+
bool isToIe = true;
611+
const ArrayRef<Relocation> relocs = sec.relocs();
612+
for (size_t i = 0, size = relocs.size(); i != size; ++i) {
613+
const Relocation &rel = relocs[i];
531614
uint8_t *loc = buf + rel.offset;
532-
const uint64_t val =
615+
uint64_t val =
533616
sec.getRelocTargetVA(sec.file, rel.type, rel.addend,
534617
secAddr + rel.offset, *rel.sym, rel.expr);
535618

536619
switch (rel.expr) {
537620
case R_RELAX_HINT:
621+
continue;
622+
case R_TLSDESC_PC:
623+
// For R_RISCV_TLSDESC_HI20, store &got(sym)-PC to be used by the
624+
// following two instructions L[DW] and ADDI.
625+
if (rel.type == R_RISCV_TLSDESC_HI20)
626+
tlsdescVal = val;
627+
else
628+
val = tlsdescVal;
538629
break;
630+
case R_RELAX_TLS_GD_TO_IE:
631+
// Only R_RISCV_TLSDESC_HI20 reaches here. tlsdescVal will be finalized
632+
// after we see R_RISCV_TLSDESC_ADD_LO12 in the R_RELAX_TLS_GD_TO_LE case.
633+
// The net effect is that tlsdescVal will be smaller than `val` to take
634+
// into account of NOP instructions (in the absence of R_RISCV_RELAX)
635+
// before AUIPC.
636+
tlsdescVal = val + rel.offset;
637+
isToIe = true;
638+
tlsdescToIe(loc, rel, val);
639+
continue;
640+
case R_RELAX_TLS_GD_TO_LE:
641+
// See the comment in handleTlsRelocation. For TLSDESC=>IE,
642+
// R_RISCV_TLSDESC_{LOAD_LO12,ADD_LO12,CALL} also reach here. If isToIe is
643+
// true, this is actually TLSDESC=>IE optimization.
644+
if (rel.type == R_RISCV_TLSDESC_HI20) {
645+
tlsdescVal = val;
646+
isToIe = false;
647+
} else {
648+
if (isToIe && rel.type == R_RISCV_TLSDESC_ADD_LO12)
649+
tlsdescVal -= rel.offset;
650+
val = tlsdescVal;
651+
}
652+
if (isToIe)
653+
tlsdescToIe(loc, rel, val);
654+
else
655+
tlsdescToLe(loc, rel, val);
656+
continue;
539657
case R_RISCV_LEB128:
540658
if (i + 1 < size) {
541659
const Relocation &rel1 = sec.relocs()[i + 1];
@@ -554,32 +672,12 @@ void RISCV::relocateAlloc(InputSectionBase &sec, uint8_t *buf) const {
554672
": R_RISCV_SET_ULEB128 not paired with R_RISCV_SUB_SET128");
555673
return;
556674
default:
557-
relocate(loc, rel, val);
558675
break;
559676
}
677+
relocate(loc, rel, val);
560678
}
561679
}
562680

563-
namespace {
564-
struct SymbolAnchor {
565-
uint64_t offset;
566-
Defined *d;
567-
bool end; // true for the anchor of st_value+st_size
568-
};
569-
} // namespace
570-
571-
struct elf::RISCVRelaxAux {
572-
// This records symbol start and end offsets which will be adjusted according
573-
// to the nearest relocDeltas element.
574-
SmallVector<SymbolAnchor, 0> anchors;
575-
// For relocations[i], the actual offset is r_offset - (i ? relocDeltas[i-1] :
576-
// 0).
577-
std::unique_ptr<uint32_t[]> relocDeltas;
578-
// For relocations[i], the actual type is relocTypes[i].
579-
std::unique_ptr<RelType[]> relocTypes;
580-
SmallVector<uint32_t, 0> writes;
581-
};
582-
583681
static void initSymbolAnchors() {
584682
SmallVector<InputSection *, 0> storage;
585683
for (OutputSection *osec : outputSections) {
@@ -762,6 +860,14 @@ static bool relax(InputSection &sec) {
762860
sec.relocs()[i + 1].type == R_RISCV_RELAX)
763861
relaxHi20Lo12(sec, i, loc, r, remove);
764862
break;
863+
case R_RISCV_TLSDESC_HI20:
864+
case R_RISCV_TLSDESC_LOAD_LO12:
865+
// For LE or IE optimization, AUIPC and L[DW] are converted to a removable
866+
// NOP.
867+
if (r.expr != R_TLSDESC_PC && i + 1 != sec.relocs().size() &&
868+
sec.relocs()[i + 1].type == R_RISCV_RELAX)
869+
remove = 4;
870+
break;
765871
}
766872

767873
// For all anchors whose offsets are <= r.offset, they are preceded by

lld/ELF/Relocations.cpp

+17-8
Original file line numberDiff line numberDiff line change
@@ -1274,26 +1274,31 @@ static unsigned handleTlsRelocation(RelType type, Symbol &sym,
12741274

12751275
if (config->emachine == EM_MIPS)
12761276
return handleMipsTlsRelocation(type, sym, c, offset, addend, expr);
1277+
bool isRISCV = config->emachine == EM_RISCV;
12771278

12781279
if (oneof<R_AARCH64_TLSDESC_PAGE, R_TLSDESC, R_TLSDESC_CALL, R_TLSDESC_PC,
12791280
R_TLSDESC_GOTPLT>(expr) &&
12801281
config->shared) {
1282+
// R_RISCV_TLSDESC_{LOAD_LO12_I,ADD_LO12_I,CALL} reference a label. Do not
1283+
// set NEEDS_TLSDESC on the label.
12811284
if (expr != R_TLSDESC_CALL) {
1282-
sym.setFlags(NEEDS_TLSDESC);
1285+
if (!isRISCV || type == R_RISCV_TLSDESC_HI20)
1286+
sym.setFlags(NEEDS_TLSDESC);
12831287
c.addReloc({expr, type, offset, addend, &sym});
12841288
}
12851289
return 1;
12861290
}
12871291

12881292
// ARM, Hexagon, LoongArch and RISC-V do not support GD/LD to IE/LE
1289-
// relaxation.
1293+
// optimizations.
1294+
// RISC-V supports TLSDESC to IE/LE optimizations.
12901295
// For PPC64, if the file has missing R_PPC64_TLSGD/R_PPC64_TLSLD, disable
12911296
// relaxation as well.
1292-
bool toExecRelax = !config->shared && config->emachine != EM_ARM &&
1293-
config->emachine != EM_HEXAGON &&
1294-
config->emachine != EM_LOONGARCH &&
1295-
config->emachine != EM_RISCV &&
1296-
!c.file->ppc64DisableTLSRelax;
1297+
bool toExecRelax =
1298+
!config->shared && config->emachine != EM_ARM &&
1299+
config->emachine != EM_HEXAGON && config->emachine != EM_LOONGARCH &&
1300+
!(isRISCV && expr != R_TLSDESC_PC && expr != R_TLSDESC_CALL) &&
1301+
!c.file->ppc64DisableTLSRelax;
12971302

12981303
// If we are producing an executable and the symbol is non-preemptable, it
12991304
// must be defined and the code sequence can be relaxed to use Local-Exec.
@@ -1347,8 +1352,12 @@ static unsigned handleTlsRelocation(RelType type, Symbol &sym,
13471352
return 1;
13481353
}
13491354

1350-
// Global-Dynamic relocs can be relaxed to Initial-Exec or Local-Exec
1355+
// Global-Dynamic/TLSDESC can be relaxed to Initial-Exec or Local-Exec
13511356
// depending on the symbol being locally defined or not.
1357+
//
1358+
// R_RISCV_TLSDESC_{LOAD_LO12_I,ADD_LO12_I,CALL} reference a non-preemptible
1359+
// label, so the LE transition will be categorized as R_RELAX_TLS_GD_TO_LE.
1360+
// We fix the categorization in RISCV::relocateAlloc.
13521361
if (sym.isPreemptible) {
13531362
sym.setFlags(NEEDS_TLSGD_TO_IE);
13541363
c.addReloc({target->adjustTlsExpr(type, R_RELAX_TLS_GD_TO_IE), type,

lld/test/ELF/riscv-tlsdesc-gd-mixed.s

+26
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
# REQUIRES: riscv
2+
# RUN: llvm-mc -filetype=obj -triple=riscv64 %s -o %t.o
3+
# RUN: ld.lld -shared %t.o -o %t.so
4+
# RUN: llvm-readobj -r %t.so | FileCheck %s --check-prefix=RELA
5+
6+
## Both TLSDESC and DTPMOD64/DTPREL64 should be present.
7+
# RELA: .rela.dyn {
8+
# RELA-NEXT: 0x[[#%X,ADDR:]] R_RISCV_TLSDESC a 0x0
9+
# RELA-NEXT: 0x[[#ADDR+16]] R_RISCV_TLS_DTPMOD64 a 0x0
10+
# RELA-NEXT: 0x[[#ADDR+24]] R_RISCV_TLS_DTPREL64 a 0x0
11+
# RELA-NEXT: }
12+
13+
la.tls.gd a0,a
14+
call __tls_get_addr@plt
15+
16+
.Ltlsdesc_hi0:
17+
auipc a2, %tlsdesc_hi(a)
18+
ld a3, %tlsdesc_load_lo(.Ltlsdesc_hi0)(a2)
19+
addi a0, a2, %tlsdesc_add_lo(.Ltlsdesc_hi0)
20+
jalr t0, 0(a3), %tlsdesc_call(.Ltlsdesc_hi0)
21+
22+
.section .tbss,"awT",@nobits
23+
.globl a
24+
.zero 8
25+
a:
26+
.zero 4

0 commit comments

Comments
 (0)