Skip to content

Commit 5c6b63f

Browse files
MaskRaytstellar
authored andcommitted
[ELF] Implement R_RISCV_TLSDESC for RISC-V
Support R_RISCV_TLSDESC_HI20/R_RISCV_TLSDESC_LOAD_LO12/R_RISCV_TLSDESC_ADD_LO12/R_RISCV_TLSDESC_CALL. LOAD_LO12/ADD_LO12/CALL relocations reference a label at the HI20 location, which requires special handling. We save the value of HI20 to be reused. Two interleaved TLSDESC code sequences, which compilers do not generate, are unsupported. 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): the first two instructions are converted to NOP while the latter two are converted to a GOT load or a lui+addi. The first two instructions, which would be converted to NOP, are removed instead in the presence of relaxation. Relaxation is eligible as long as the R_RISCV_TLSDESC_HI20 relocation has a pairing R_RISCV_RELAX, regardless of whether the following instructions have a R_RISCV_RELAX. In addition, for the TLSDESC to LE optimization (`lui a0,<hi20>; addi a0,a0,<lo12>`), `lui` can be removed (i.e. use the short form) if hi20 is 0. ``` // TLSDESC to LE/IE optimization .Ltlsdesc_hi2: auipc a4, %tlsdesc_hi(c) # if relax: remove; otherwise, NOP load a5, %tlsdesc_load_lo(.Ltlsdesc_hi2)(a4) # if relax: remove; otherwise, NOP addi a0, a4, %tlsdesc_add_lo(.Ltlsdesc_hi2) # if LE && !hi20 {if relax: remove; otherwise, NOP} jalr t0, 0(a5), %tlsdesc_call(.Ltlsdesc_hi2) add a0, a0, tp ``` The implementation carefully ensures that an instruction unrelated to the current TLSDESC code sequence, if immediately follows a removable instruction (HI20 or LOAD_LO12 OR (LE-specific) ADD_LO12), is not converted to NOP. * `riscv64-tlsdesc.s` is inspired by `i386-tlsdesc-gd.s` (https://reviews.llvm.org/D112582). * `riscv64-tlsdesc-relax.s` tests linker relaxation. * `riscv-tlsdesc-gd-mixed.s` is inspired by `x86-64-tlsdesc-gd-mixed.s` (https://reviews.llvm.org/D116900). Link: riscv-non-isa/riscv-elf-psabi-doc#373 Reviewed By: ilovepi Pull Request: llvm#79239 (cherry picked from commit 1117fdd)
1 parent ca64fe2 commit 5c6b63f

File tree

5 files changed

+551
-6
lines changed

5 files changed

+551
-6
lines changed

lld/ELF/Arch/RISCV.cpp

Lines changed: 130 additions & 4 deletions
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

@@ -139,6 +141,7 @@ RISCV::RISCV() {
139141
tlsGotRel = R_RISCV_TLS_TPREL32;
140142
}
141143
gotRel = symbolicRel;
144+
tlsDescRel = R_RISCV_TLSDESC;
142145

143146
// .got[0] = _DYNAMIC
144147
gotHeaderEntriesNum = 1;
@@ -207,6 +210,8 @@ int64_t RISCV::getImplicitAddend(const uint8_t *buf, RelType type) const {
207210
case R_RISCV_JUMP_SLOT:
208211
// These relocations are defined as not having an implicit addend.
209212
return 0;
213+
case R_RISCV_TLSDESC:
214+
return config->is64 ? read64le(buf + 8) : read32le(buf + 4);
210215
}
211216
}
212217

@@ -315,6 +320,12 @@ RelExpr RISCV::getRelExpr(const RelType type, const Symbol &s,
315320
case R_RISCV_PCREL_LO12_I:
316321
case R_RISCV_PCREL_LO12_S:
317322
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;
318329
case R_RISCV_TLS_GD_HI20:
319330
return R_TLSGD_PC;
320331
case R_RISCV_TLS_GOT_HI20:
@@ -439,6 +450,7 @@ void RISCV::relocate(uint8_t *loc, const Relocation &rel, uint64_t val) const {
439450

440451
case R_RISCV_GOT_HI20:
441452
case R_RISCV_PCREL_HI20:
453+
case R_RISCV_TLSDESC_HI20:
442454
case R_RISCV_TLS_GD_HI20:
443455
case R_RISCV_TLS_GOT_HI20:
444456
case R_RISCV_TPREL_HI20:
@@ -450,6 +462,8 @@ void RISCV::relocate(uint8_t *loc, const Relocation &rel, uint64_t val) const {
450462
}
451463

452464
case R_RISCV_PCREL_LO12_I:
465+
case R_RISCV_TLSDESC_LOAD_LO12:
466+
case R_RISCV_TLSDESC_ADD_LO12:
453467
case R_RISCV_TPREL_LO12_I:
454468
case R_RISCV_LO12_I: {
455469
uint64_t hi = (val + 0x800) >> 12;
@@ -533,8 +547,14 @@ void RISCV::relocate(uint8_t *loc, const Relocation &rel, uint64_t val) const {
533547
break;
534548

535549
case R_RISCV_RELAX:
536-
return; // Ignored (for now)
537-
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;
538558
default:
539559
llvm_unreachable("unknown relocation");
540560
}
@@ -544,23 +564,113 @@ static bool relaxable(ArrayRef<Relocation> relocs, size_t i) {
544564
return i + 1 != relocs.size() && relocs[i + 1].type == R_RISCV_RELAX;
545565
}
546566

567+
static void tlsdescToIe(uint8_t *loc, const Relocation &rel, uint64_t val) {
568+
switch (rel.type) {
569+
case R_RISCV_TLSDESC_HI20:
570+
case R_RISCV_TLSDESC_LOAD_LO12:
571+
write32le(loc, 0x00000013); // nop
572+
break;
573+
case R_RISCV_TLSDESC_ADD_LO12:
574+
write32le(loc, utype(AUIPC, X_A0, hi20(val))); // auipc a0,<hi20>
575+
break;
576+
case R_RISCV_TLSDESC_CALL:
577+
if (config->is64)
578+
write32le(loc, itype(LD, X_A0, X_A0, lo12(val))); // ld a0,<lo12>(a0)
579+
else
580+
write32le(loc, itype(LW, X_A0, X_A0, lo12(val))); // lw a0,<lo12>(a0)
581+
break;
582+
default:
583+
llvm_unreachable("unsupported relocation for TLSDESC to IE");
584+
}
585+
}
586+
587+
static void tlsdescToLe(uint8_t *loc, const Relocation &rel, uint64_t val) {
588+
switch (rel.type) {
589+
case R_RISCV_TLSDESC_HI20:
590+
case R_RISCV_TLSDESC_LOAD_LO12:
591+
write32le(loc, 0x00000013); // nop
592+
return;
593+
case R_RISCV_TLSDESC_ADD_LO12:
594+
if (isInt<12>(val))
595+
write32le(loc, 0x00000013); // nop
596+
else
597+
write32le(loc, utype(LUI, X_A0, hi20(val))); // lui a0,<hi20>
598+
return;
599+
case R_RISCV_TLSDESC_CALL:
600+
if (isInt<12>(val))
601+
write32le(loc, itype(ADDI, X_A0, 0, val)); // addi a0,zero,<lo12>
602+
else
603+
write32le(loc, itype(ADDI, X_A0, X_A0, lo12(val))); // addi a0,a0,<lo12>
604+
return;
605+
default:
606+
llvm_unreachable("unsupported relocation for TLSDESC to LE");
607+
}
608+
}
609+
547610
void RISCV::relocateAlloc(InputSectionBase &sec, uint8_t *buf) const {
548611
uint64_t secAddr = sec.getOutputSection()->addr;
549612
if (auto *s = dyn_cast<InputSection>(&sec))
550613
secAddr += s->outSecOff;
551614
else if (auto *ehIn = dyn_cast<EhInputSection>(&sec))
552615
secAddr += ehIn->getParent()->outSecOff;
616+
uint64_t tlsdescVal = 0;
617+
bool tlsdescRelax = false, isToLe = false;
553618
const ArrayRef<Relocation> relocs = sec.relocs();
554619
for (size_t i = 0, size = relocs.size(); i != size; ++i) {
555620
const Relocation &rel = relocs[i];
556621
uint8_t *loc = buf + rel.offset;
557-
const uint64_t val =
622+
uint64_t val =
558623
sec.getRelocTargetVA(sec.file, rel.type, rel.addend,
559624
secAddr + rel.offset, *rel.sym, rel.expr);
560625

561626
switch (rel.expr) {
562627
case R_RELAX_HINT:
628+
continue;
629+
case R_TLSDESC_PC:
630+
// For R_RISCV_TLSDESC_HI20, store &got(sym)-PC to be used by the
631+
// following two instructions L[DW] and ADDI.
632+
if (rel.type == R_RISCV_TLSDESC_HI20)
633+
tlsdescVal = val;
634+
else
635+
val = tlsdescVal;
563636
break;
637+
case R_RELAX_TLS_GD_TO_IE:
638+
// Only R_RISCV_TLSDESC_HI20 reaches here. tlsdescVal will be finalized
639+
// after we see R_RISCV_TLSDESC_ADD_LO12 in the R_RELAX_TLS_GD_TO_LE case.
640+
// The net effect is that tlsdescVal will be smaller than `val` to take
641+
// into account of NOP instructions (in the absence of R_RISCV_RELAX)
642+
// before AUIPC.
643+
tlsdescVal = val + rel.offset;
644+
isToLe = false;
645+
tlsdescRelax = relaxable(relocs, i);
646+
if (!tlsdescRelax)
647+
tlsdescToIe(loc, rel, val);
648+
continue;
649+
case R_RELAX_TLS_GD_TO_LE:
650+
// See the comment in handleTlsRelocation. For TLSDESC=>IE,
651+
// R_RISCV_TLSDESC_{LOAD_LO12,ADD_LO12,CALL} also reach here. If isToIe is
652+
// true, this is actually TLSDESC=>IE optimization.
653+
if (rel.type == R_RISCV_TLSDESC_HI20) {
654+
tlsdescVal = val;
655+
isToLe = true;
656+
tlsdescRelax = relaxable(relocs, i);
657+
} else {
658+
if (!isToLe && rel.type == R_RISCV_TLSDESC_ADD_LO12)
659+
tlsdescVal -= rel.offset;
660+
val = tlsdescVal;
661+
}
662+
// When NOP conversion is eligible and relaxation applies, don't write a
663+
// NOP in case an unrelated instruction follows the current instruction.
664+
if (tlsdescRelax &&
665+
(rel.type == R_RISCV_TLSDESC_HI20 ||
666+
rel.type == R_RISCV_TLSDESC_LOAD_LO12 ||
667+
(rel.type == R_RISCV_TLSDESC_ADD_LO12 && isToLe && !hi20(val))))
668+
continue;
669+
if (isToLe)
670+
tlsdescToLe(loc, rel, val);
671+
else
672+
tlsdescToIe(loc, rel, val);
673+
continue;
564674
case R_RISCV_LEB128:
565675
if (i + 1 < size) {
566676
const Relocation &rel1 = relocs[i + 1];
@@ -579,9 +689,9 @@ void RISCV::relocateAlloc(InputSectionBase &sec, uint8_t *buf) const {
579689
": R_RISCV_SET_ULEB128 not paired with R_RISCV_SUB_SET128");
580690
return;
581691
default:
582-
relocate(loc, rel, val);
583692
break;
584693
}
694+
relocate(loc, rel, val);
585695
}
586696
}
587697

@@ -725,6 +835,7 @@ static bool relax(InputSection &sec) {
725835
bool changed = false;
726836
ArrayRef<SymbolAnchor> sa = ArrayRef(aux.anchors);
727837
uint64_t delta = 0;
838+
bool tlsdescRelax = false, toLeShortForm = false;
728839

729840
std::fill_n(aux.relocTypes.get(), relocs.size(), R_RISCV_NONE);
730841
aux.writes.clear();
@@ -765,6 +876,21 @@ static bool relax(InputSection &sec) {
765876
if (relaxable(relocs, i))
766877
relaxHi20Lo12(sec, i, loc, r, remove);
767878
break;
879+
case R_RISCV_TLSDESC_HI20:
880+
// For TLSDESC=>LE, we can use the short form if hi20 is zero.
881+
tlsdescRelax = relaxable(relocs, i);
882+
toLeShortForm = tlsdescRelax && r.expr == R_RELAX_TLS_GD_TO_LE &&
883+
!hi20(r.sym->getVA(r.addend));
884+
[[fallthrough]];
885+
case R_RISCV_TLSDESC_LOAD_LO12:
886+
// For TLSDESC=>LE/IE, AUIPC and L[DW] are removed if relaxable.
887+
if (tlsdescRelax && r.expr != R_TLSDESC_PC)
888+
remove = 4;
889+
break;
890+
case R_RISCV_TLSDESC_ADD_LO12:
891+
if (toLeShortForm)
892+
remove = 4;
893+
break;
768894
}
769895

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

lld/ELF/Relocations.cpp

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1274,25 +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,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
12891293
// 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
// optimization as well.
12921297
bool execOptimize =
12931298
!config->shared && config->emachine != EM_ARM &&
12941299
config->emachine != EM_HEXAGON && config->emachine != EM_LOONGARCH &&
1295-
config->emachine != EM_RISCV && !c.file->ppc64DisableTLSRelax;
1300+
!(isRISCV && expr != R_TLSDESC_PC && expr != R_TLSDESC_CALL) &&
1301+
!c.file->ppc64DisableTLSRelax;
12961302

12971303
// If we are producing an executable and the symbol is non-preemptable, it
12981304
// must be defined and the code sequence can be optimized to use Local-Exec.
@@ -1349,6 +1355,10 @@ static unsigned handleTlsRelocation(RelType type, Symbol &sym,
13491355

13501356
// Global-Dynamic/TLSDESC can be optimized to Initial-Exec or Local-Exec
13511357
// depending on the symbol being locally defined or not.
1358+
//
1359+
// R_RISCV_TLSDESC_{LOAD_LO12,ADD_LO12_I,CALL} reference a non-preemptible
1360+
// label, so the LE optimization will be categorized as
1361+
// R_RELAX_TLS_GD_TO_LE. We fix the categorization in RISCV::relocateAlloc.
13521362
if (sym.isPreemptible) {
13531363
sym.setFlags(NEEDS_TLSGD_TO_IE);
13541364
c.addReloc({target->adjustTlsExpr(type, R_RELAX_TLS_GD_TO_IE), type,

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

Lines changed: 26 additions & 0 deletions
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)