Skip to content

Commit a913ff1

Browse files
committed
[ELF][RISCV] Support .got.plt with lazy binding for CHERI-RISC-V
This is currently behind an opt-in -z cheri-riscv-jump-slot flag as it requires support in CheriBSD, but in future will become the default and only supported mode.
1 parent b20a2cb commit a913ff1

File tree

5 files changed

+127
-52
lines changed

5 files changed

+127
-52
lines changed

lld/ELF/Arch/RISCV.cpp

Lines changed: 8 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -221,6 +221,10 @@ void RISCV::writeGotHeader(uint8_t *buf) const {
221221
}
222222

223223
void RISCV::writeGotPlt(uint8_t *buf, const Symbol &s) const {
224+
// Initialised by __cap_relocs for CHERI
225+
if (config->isCheriAbi)
226+
return;
227+
224228
if (config->is64)
225229
write64le(buf, in.plt->getVA());
226230
else
@@ -237,14 +241,7 @@ void RISCV::writeIgotPlt(uint8_t *buf, const Symbol &s) const {
237241
}
238242

239243
void RISCV::writePltHeader(uint8_t *buf) const {
240-
// TODO: Remove once we have a CHERI .got.plt and R_RISCV_CHERI_JUMP_SLOT.
241-
// Without those there can be no lazy binding support (though the former
242-
// requirement can be relaxed provided .captable[0] is _dl_runtime_resolve,
243-
// at least when the PLT is non-empty), so for now we emit a header full of
244-
// trapping instructions to ensure we don't accidentally end up trying to use
245-
// it. Ideally we would have a header size of 0, but isCheriAbi isn't known
246-
// in the constructor.
247-
if (config->isCheriAbi) {
244+
if (config->isCheriAbi && !config->zCheriRiscvJumpSlot) {
248245
memset(buf, 0, pltHeaderSize);
249246
return;
250247
}
@@ -286,7 +283,9 @@ void RISCV::writePlt(uint8_t *buf, const Symbol &sym,
286283
// nop
287284
uint32_t ptrload = config->isCheriAbi ? config->is64 ? CLC_128 : CLC_64
288285
: config->is64 ? LD : LW;
289-
uint32_t entryva = config->isCheriAbi ? sym.getGotVA() : sym.getGotPltVA();
286+
uint32_t entryva = config->isCheriAbi && !config->zCheriRiscvJumpSlot
287+
? sym.getGotVA()
288+
: sym.getGotPltVA();
290289
uint32_t offset = entryva - pltEntryAddr;
291290
write32le(buf + 0, utype(AUIPC, X_T3, hi20(offset)));
292291
write32le(buf + 4, itype(ptrload, X_T3, X_T3, lo12(offset)));

lld/ELF/Config.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -308,6 +308,7 @@ struct Config {
308308
// -z captabledebug: add additional symbols $captable_load_<symbols> before
309309
// each captable clc instruction that indicates which symbol should be loaded
310310
bool zCapTableDebug;
311+
bool zCheriRiscvJumpSlot;
311312
bool zCombreloc;
312313
bool zCopyreloc;
313314
bool zForceBti;

lld/ELF/Driver.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -535,6 +535,7 @@ static uint8_t getZStartStopVisibility(opt::InputArgList &args) {
535535

536536
constexpr const char *knownZFlags[] = {
537537
"captabledebug",
538+
"cheri-riscv-jump-slot",
538539
"combreloc",
539540
"copyreloc",
540541
"defs",
@@ -1433,6 +1434,7 @@ static void readConfigs(opt::InputArgList &args) {
14331434
args.hasFlag(OPT_warn_symbol_ordering, OPT_no_warn_symbol_ordering, true);
14341435
config->whyExtract = args.getLastArgValue(OPT_why_extract);
14351436
config->zCapTableDebug = getZFlag(args, "captabledebug", "nocaptabledebug", false);
1437+
config->zCheriRiscvJumpSlot = hasZOption(args, "cheri-riscv-jump-slot");
14361438
config->zCombreloc = getZFlag(args, "combreloc", "nocombreloc", true);
14371439
config->zCopyreloc = getZFlag(args, "copyreloc", "nocopyreloc", true);
14381440
config->zForceBti = hasZOption(args, "force-bti");

lld/ELF/Relocations.cpp

Lines changed: 19 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -891,20 +891,29 @@ static void addPltEntry(PltSection &plt, GotPltSection &gotPlt,
891891
RelocationBaseSection &rel, RelType type, Symbol &sym) {
892892
plt.addEntry(sym);
893893

894-
// For CHERI-RISC-V we mark the symbol NEEDS_GOT so it will end up in .got as
895-
// a function pointer, and uses .rela.dyn rather than .rela.plt, so no rtld
896-
// changes are needed.
894+
// For CHERI-RISC-V if JUMP_SLOT relocations are disabled (to be compatible
895+
// with old CheriBSD) we mark the symbol NEEDS_GOT so it will end up in .got
896+
// as a function pointer, and uses .rela.dyn rather than .rela.plt, so no
897+
// rtld changes are needed.
897898
//
898-
// TODO: More normal .got.plt with lazy-binding rather than piggy-backing on
899-
// .got once rtld supports it.
900-
if (config->emachine == EM_RISCV && config->isCheriAbi)
899+
// TODO: Remove this option.
900+
if (config->emachine == EM_RISCV && config->isCheriAbi &&
901+
!config->zCheriRiscvJumpSlot)
901902
return;
902903

904+
if (config->isCheriAbi && !sym.isPreemptible)
905+
error("cannot create non-preemptible PLT entry on CHERI against symbol: " +
906+
toString(sym));
907+
903908
gotPlt.addEntry(sym);
904909
rel.addReloc({type, &gotPlt, sym.getGotPltOffset(),
905910
sym.isPreemptible ? DynamicReloc::AgainstSymbol
906911
: DynamicReloc::AddendOnlyWithTargetVA,
907912
sym, 0, R_ABS});
913+
if (config->isCheriAbi)
914+
invokeELFT(addCapabilityRelocation, &plt, *target->cheriCapRel, &gotPlt,
915+
sym.getGotPltOffset(), R_CHERI_CAPABILITY, 0, false,
916+
[] { return ""; });
908917
}
909918

910919
static void addGotEntry(Symbol &sym) {
@@ -1116,7 +1125,8 @@ void RelocationScanner::processAux(RelExpr expr, RelType type, uint64_t offset,
11161125
} else if (needsPlt(expr)) {
11171126
sym.setFlags(NEEDS_PLT);
11181127
// See addPltEntry
1119-
if (config->emachine == EM_RISCV && config->isCheriAbi)
1128+
if (config->emachine == EM_RISCV && config->isCheriAbi &&
1129+
!config->zCheriRiscvJumpSlot)
11201130
sym.setFlags(NEEDS_GOT);
11211131
} else if (LLVM_UNLIKELY(isIfunc)) {
11221132
sym.setFlags(HAS_DIRECT_RELOC);
@@ -1262,7 +1272,8 @@ void RelocationScanner::processAux(RelExpr expr, RelType type, uint64_t offset,
12621272
getLocation(*sec, sym, offset));
12631273
sym.setFlags(NEEDS_COPY | NEEDS_PLT);
12641274
// See addPltEntry
1265-
if (config->emachine == EM_RISCV && config->isCheriAbi)
1275+
if (config->emachine == EM_RISCV && config->isCheriAbi &&
1276+
!config->zCheriRiscvJumpSlot)
12661277
sym.setFlags(NEEDS_GOT);
12671278
sec->addReloc({expr, type, offset, addend, &sym});
12681279
return;

lld/test/ELF/cheri/riscv/plt.s

Lines changed: 97 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -4,20 +4,30 @@
44
# RUN: %riscv32_cheri_purecap_llvm-mc -filetype=obj %t1.s -o %t1.32.o
55
# RUN: ld.lld -shared %t1.32.o -soname=t1.32.so -o %t1.32.so
66
# RUN: %riscv32_cheri_purecap_llvm-mc -filetype=obj %s -o %t.32.o
7-
# RUN: ld.lld %t.32.o %t1.32.so -z separate-code -o %t.32
8-
# RUN: llvm-readelf -S -s %t.32 | FileCheck --check-prefixes=SEC,NM %s
9-
# RUN: llvm-readobj -r %t.32 | FileCheck --check-prefix=RELOC32 %s
10-
# RUN: llvm-readelf -x .got %t.32 | FileCheck --check-prefix=GOT32 %s
11-
# RUN: llvm-objdump -d --no-show-raw-insn %t.32 | FileCheck --check-prefixes=DIS,DIS32 %s
7+
# RUN: ld.lld %t.32.o %t1.32.so -z separate-code -o %t.32.got
8+
# RUN: llvm-readelf -S -s %t.32.got | FileCheck --check-prefixes=SEC,NM %s
9+
# RUN: llvm-readobj -r --cap-relocs %t.32.got | FileCheck --check-prefix=RELOCGOT32 %s
10+
# RUN: llvm-readelf -x .got %t.32.got | FileCheck --check-prefix=GOT32 %s
11+
# RUN: llvm-objdump -d --no-show-raw-insn %t.32.got | FileCheck --check-prefixes=DIS,DISGOT,DISGOT32 %s
12+
# RUN: ld.lld %t.32.o %t1.32.so -z separate-code -z cheri-riscv-jump-slot -o %t.32.got.plt
13+
# RUN: llvm-readelf -S -s %t.32.got.plt | FileCheck --check-prefixes=SEC,NM %s
14+
# RUN: llvm-readobj -r --cap-relocs %t.32.got.plt | FileCheck --check-prefix=RELOCGOTPLT32 %s
15+
# RUN: llvm-readelf -x .got.plt %t.32.got.plt | FileCheck --check-prefix=GOTPLT32 %s
16+
# RUN: llvm-objdump -d --no-show-raw-insn %t.32.got.plt | FileCheck --check-prefixes=DIS,DISGOTPLT,DISGOTPLT32 %s
1217

1318
# RUN: %riscv64_cheri_purecap_llvm-mc -filetype=obj %t1.s -o %t1.64.o
1419
# RUN: ld.lld -shared %t1.64.o -soname=t1.64.so -o %t1.64.so
1520
# RUN: %riscv64_cheri_purecap_llvm-mc -filetype=obj %s -o %t.64.o
16-
# RUN: ld.lld %t.64.o %t1.64.so -z separate-code -o %t.64
17-
# RUN: llvm-readelf -S -s %t.64 | FileCheck --check-prefixes=SEC,NM %s
18-
# RUN: llvm-readobj -r %t.64 | FileCheck --check-prefix=RELOC64 %s
19-
# RUN: llvm-readelf -x .got %t.64 | FileCheck --check-prefix=GOT64 %s
20-
# RUN: llvm-objdump -d --no-show-raw-insn %t.64 | FileCheck --check-prefixes=DIS,DIS64 %s
21+
# RUN: ld.lld %t.64.o %t1.64.so -z separate-code -o %t.64.got
22+
# RUN: llvm-readelf -S -s %t.64.got | FileCheck --check-prefixes=SEC,NM %s
23+
# RUN: llvm-readobj -r --cap-relocs %t.64.got | FileCheck --check-prefix=RELOCGOT64 %s
24+
# RUN: llvm-readelf -x .got %t.64.got | FileCheck --check-prefix=GOT64 %s
25+
# RUN: llvm-objdump -d --no-show-raw-insn %t.64.got | FileCheck --check-prefixes=DIS,DISGOT,DISGOT64 %s
26+
# RUN: ld.lld %t.64.o %t1.64.so -z separate-code -z cheri-riscv-jump-slot -o %t.64.got.plt
27+
# RUN: llvm-readelf -S -s %t.64.got.plt | FileCheck --check-prefixes=SEC,NM %s
28+
# RUN: llvm-readobj -r --cap-relocs %t.64.got.plt | FileCheck --check-prefix=RELOCGOTPLT64 %s
29+
# RUN: llvm-readelf -x .got.plt %t.64.got.plt | FileCheck --check-prefix=GOTPLT64 %s
30+
# RUN: llvm-objdump -d --no-show-raw-insn %t.64.got.plt | FileCheck --check-prefixes=DIS,DISGOTPLT,DISGOTPLT64 %s
2131

2232
# SEC: .plt PROGBITS {{0*}}00011030
2333

@@ -26,23 +36,51 @@
2636
# NM: {{0*}}00000000 0 FUNC GLOBAL DEFAULT UND bar
2737
# NM: {{0*}}00000000 0 FUNC WEAK DEFAULT UND weak
2838

29-
# RELOC32: .rela.dyn {
30-
# RELOC32-NEXT: 0x12068 R_RISCV_CHERI_CAPABILITY bar 0x0
31-
# RELOC32-NEXT: 0x12070 R_RISCV_CHERI_CAPABILITY weak 0x0
32-
# RELOC32-NEXT: }
39+
# RELOCGOT32: .rela.dyn {
40+
# RELOCGOT32-NEXT: 0x12068 R_RISCV_CHERI_CAPABILITY bar 0x0
41+
# RELOCGOT32-NEXT: 0x12070 R_RISCV_CHERI_CAPABILITY weak 0x0
42+
# RELOCGOT32-NEXT: }
43+
# RELOCGOT32: There is no __cap_relocs section in the file.
3344
# GOT32: section '.got'
3445
# GOT32-NEXT: 0x00012060 00200100 00000000 00000000 00000000
3546
# GOT32-NEXT: 0x00012070 00000000 00000000
3647

37-
# RELOC64: .rela.dyn {
38-
# RELOC64-NEXT: 0x120D0 R_RISCV_CHERI_CAPABILITY bar 0x0
39-
# RELOC64-NEXT: 0x120E0 R_RISCV_CHERI_CAPABILITY weak 0x0
40-
# RELOC64-NEXT: }
48+
# RELOCGOTPLT32: .rela.plt {
49+
# RELOCGOTPLT32-NEXT: 0x13088 R_RISCV_JUMP_SLOT bar 0x0
50+
# RELOCGOTPLT32-NEXT: 0x13090 R_RISCV_JUMP_SLOT weak 0x0
51+
# RELOCGOTPLT32-NEXT: }
52+
# RELOCGOTPLT32: CHERI __cap_relocs [
53+
# RELOCGOTPLT32-NEXT: 0x013088 Base: 0x11030 (<unknown symbol>+0) Length: 64 Perms: Function
54+
# RELOCGOTPLT32-NEXT: 0x013090 Base: 0x11030 (<unknown symbol>+0) Length: 64 Perms: Function
55+
# RELOCGOTPLT32-NEXT: ]
56+
# GOTPLT32: section '.got.plt'
57+
# GOTPLT32-NEXT: 0x00013078 00000000 00000000 00000000 00000000
58+
# GOTPLT32-NEXT: 0x00013088 00000000 00000000 00000000 00000000
59+
60+
# RELOCGOT64: .rela.dyn {
61+
# RELOCGOT64-NEXT: 0x120D0 R_RISCV_CHERI_CAPABILITY bar 0x0
62+
# RELOCGOT64-NEXT: 0x120E0 R_RISCV_CHERI_CAPABILITY weak 0x0
63+
# RELOCGOT64-NEXT: }
64+
# RELOCGOT64: There is no __cap_relocs section in the file.
4165
# GOT64: section '.got'
4266
# GOT64-NEXT: 0x000120c0 00200100 00000000 00000000 00000000
4367
# GOT64-NEXT: 0x000120d0 00000000 00000000 00000000 00000000
4468
# GOT64-NEXT: 0x000120e0 00000000 00000000 00000000 00000000
4569

70+
# RELOCGOTPLT64: .rela.plt {
71+
# RELOCGOTPLT64-NEXT: 0x13110 R_RISCV_JUMP_SLOT bar 0x0
72+
# RELOCGOTPLT64-NEXT: 0x13120 R_RISCV_JUMP_SLOT weak 0x0
73+
# RELOCGOTPLT64-NEXT: }
74+
# RELOCGOTPLT64: CHERI __cap_relocs [
75+
# RELOCGOTPLT64-NEXT: 0x013110 Base: 0x11030 (<unknown symbol>+0) Length: 64 Perms: Function
76+
# RELOCGOTPLT64-NEXT: 0x013120 Base: 0x11030 (<unknown symbol>+0) Length: 64 Perms: Function
77+
# RELOCGOTPLT64-NEXT: ]
78+
# GOTPLT64: section '.got.plt'
79+
# GOTPLT64-NEXT: 0x000130f0 00000000 00000000 00000000 00000000
80+
# GOTPLT64-NEXT: 0x00013100 00000000 00000000 00000000 00000000
81+
# GOTPLT64-NEXT: 0x00013110 00000000 00000000 00000000 00000000
82+
# GOTPLT64-NEXT: 0x00013120 00000000 00000000 00000000 00000000
83+
4684
# DIS: <_start>:
4785
## Direct call
4886
## foo - . = 0x11020-0x11000 = 32
@@ -60,25 +98,49 @@
6098
# DIS: <foo>:
6199
# DIS-NEXT: 11020:
62100

63-
# DIS: Disassembly of section .plt:
64-
# DIS: <.plt>:
65-
# DIS-NEXT: ...
101+
# DIS: Disassembly of section .plt:
102+
# DIS: <.plt>:
103+
# DISGOT-NEXT: ...
104+
# DISGOTPLT-NEXT: auipcc ct2, 2
105+
# DISGOTPLT-NEXT: sub t1, t1, t3
106+
## 32-bit: .got.plt - .plt = 0x13078 - 0x11030 = 4096*2+72
107+
## 64-bit: .got.plt - .plt = 0x130f0 - 0x11030 = 4096*2+192
108+
# DISGOTPLT32-NEXT: lc ct3, 72(ct2)
109+
# DISGOTPLT64-NEXT: lc ct3, 192(ct2)
110+
# DISGOTPLT-NEXT: addi t1, t1, -44
111+
# DISGOTPLT32-NEXT: cincoffset ct0, ct2, 72
112+
# DISGOTPLT64-NEXT: cincoffset ct0, ct2, 192
113+
# DISGOTPLT32-NEXT: srli t1, t1, 1
114+
# DISGOTPLT32-NEXT: lc ct0, 8(ct0)
115+
# DISGOTPLT64-NEXT: lc ct0, 16(ct0)
116+
# DISGOTPLT-NEXT: jr ct3
117+
# DISGOTPLT64-NEXT: nop
66118

67-
## 32-bit: &.got[bar]-. = 0x12068-0x11050 = 4096*1+24
68-
## 64-bit: &.got[bar]-. = 0x120d0-0x11050 = 4096*1+128
69-
# DIS: 11050: auipcc ct3, 1
70-
# DIS32-NEXT: lc ct3, 24(ct3)
71-
# DIS64-NEXT: lc ct3, 128(ct3)
72-
# DIS-NEXT: jalr ct1, ct3
73-
# DIS-NEXT: nop
119+
## 32-bit (.got): &.got[bar]-. = 0x12068-0x11050 = 4096*1+24
120+
## 64-bit (.got): &.got[bar]-. = 0x120d0-0x11050 = 4096*1+128
121+
## 32-bit (.got.plt): &.got.plt[bar]-. = 0x13088-0x11050 = 4096*2+56
122+
## 64-bit (.got.plt): &.got.plt[bar]-. = 0x13110-0x11050 = 4096*2+192
123+
# DISGOT: 11050: auipcc ct3, 1
124+
# DISGOTPLT: 11050: auipcc ct3, 2
125+
# DISGOT32-NEXT: lc ct3, 24(ct3)
126+
# DISGOT64-NEXT: lc ct3, 128(ct3)
127+
# DISGOTPLT32-NEXT: lc ct3, 56(ct3)
128+
# DISGOTPLT64-NEXT: lc ct3, 192(ct3)
129+
# DIS-NEXT: jalr ct1, ct3
130+
# DIS-NEXT: nop
74131

75-
## 32-bit: &.got[weak]-. = 0x12070-0x11060 = 4096*1+16
76-
## 64-bit: &.got[weak]-. = 0x120e0-0x11060 = 4096*1+128
77-
# DIS: 11060: auipcc ct3, 1
78-
# DIS32-NEXT: lc ct3, 16(ct3)
79-
# DIS64-NEXT: lc ct3, 128(ct3)
80-
# DIS-NEXT: jalr ct1, ct3
81-
# DIS-NEXT: nop
132+
## 32-bit (.got): &.got[weak]-. = 0x12070-0x11060 = 4096*1+16
133+
## 64-bit (.got): &.got[weak]-. = 0x120e0-0x11060 = 4096*1+128
134+
## 32-bit (.got.plt): &.got.plt[weak]-. = 0x13090-0x11060 = 4096*2+48
135+
## 64-bit (.got.plt): &.got.plt[weak]-. = 0x13120-0x11060 = 4096*2+192
136+
# DISGOT: 11060: auipcc ct3, 1
137+
# DISGOTPLT: 11060: auipcc ct3, 2
138+
# DISGOT32-NEXT: lc ct3, 16(ct3)
139+
# DISGOT64-NEXT: lc ct3, 128(ct3)
140+
# DISGOTPLT32-NEXT: lc ct3, 48(ct3)
141+
# DISGOTPLT64-NEXT: lc ct3, 192(ct3)
142+
# DIS-NEXT: jalr ct1, ct3
143+
# DIS-NEXT: nop
82144

83145
.global _start, foo, bar
84146
.weak weak

0 commit comments

Comments
 (0)