Skip to content

Commit afc7d7a

Browse files
MaskRayjiegec
authored andcommitted
[ELF] Support R_RISCV_SET_ULEB128/R_RISCV_SUB_ULEB128 in non-SHF_ALLOC sections (llvm#72610)
For a label difference like `.uleb128 A-B`, MC generates a pair of R_RISCV_SET_ULEB128/R_RISCV_SUB_ULEB128 if A-B cannot be folded as a constant. GNU assembler generates a pair of relocations in more cases (when A or B is in a code section with linker relaxation). `.uleb128 A-B` is primarily used by DWARF v5 .debug_loclists/.debug_rnglists (DW_LLE_offset_pair/DW_RLE_offset_pair entry kinds) implemented in Clang and GCC. `.uleb128 A-B` can be used in SHF_ALLOC sections as well (e.g. `.gcc_except_table`). This patch does not handle SHF_ALLOC. `-z dead-reloc-in-nonalloc=` can be used to change the relocated value, if the R_RISCV_SET_ULEB128 symbol is in a discarded section. We don't check the R_RISCV_SUB_ULEB128 symbol since for the expected cases A and B should be defined in the same input section.
1 parent d715634 commit afc7d7a

File tree

4 files changed

+170
-3
lines changed

4 files changed

+170
-3
lines changed

lld/ELF/Arch/RISCV.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -306,6 +306,8 @@ RelExpr RISCV::getRelExpr(const RelType type, const Symbol &s,
306306
case R_RISCV_TPREL_ADD:
307307
case R_RISCV_RELAX:
308308
return config->relax ? R_RELAX_HINT : R_NONE;
309+
case R_RISCV_SET_ULEB128:
310+
return R_RISCV_LEB128;
309311
default:
310312
error(getErrorLocation(loc) + "unknown relocation (" + Twine(type) +
311313
") against symbol " + toString(s));

lld/ELF/InputSection.cpp

Lines changed: 38 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -839,6 +839,16 @@ uint64_t InputSectionBase::getRelocTargetVA(const InputFile *file, RelType type,
839839
}
840840
}
841841

842+
// Overwrite a ULEB128 value and keep the original length.
843+
static uint64_t overwriteULEB128(uint8_t *bufLoc, uint64_t val) {
844+
while (*bufLoc & 0x80) {
845+
*bufLoc++ = 0x80 | (val & 0x7f);
846+
val >>= 7;
847+
}
848+
*bufLoc = val;
849+
return val;
850+
}
851+
842852
// This function applies relocations to sections without SHF_ALLOC bit.
843853
// Such sections are never mapped to memory at runtime. Debug sections are
844854
// an example. Relocations in non-alloc sections are much easier to
@@ -850,6 +860,7 @@ template <class ELFT, class RelTy>
850860
void InputSection::relocateNonAlloc(uint8_t *buf, ArrayRef<RelTy> rels) {
851861
const unsigned bits = sizeof(typename ELFT::uint) * 8;
852862
const TargetInfo &target = *elf::target;
863+
const auto emachine = config->emachine;
853864
const bool isDebug = isDebugSection(*this);
854865
const bool isDebugLocOrRanges =
855866
isDebug && (name == ".debug_loc" || name == ".debug_ranges");
@@ -861,14 +872,15 @@ void InputSection::relocateNonAlloc(uint8_t *buf, ArrayRef<RelTy> rels) {
861872
break;
862873
}
863874

864-
for (const RelTy &rel : rels) {
875+
for (size_t i = 0, relsSize = rels.size(); i != relsSize; ++i) {
876+
const RelTy &rel = rels[i];
865877
RelType type = rel.getType(config->isMips64EL);
866878

867879
// GCC 8.0 or earlier have a bug that they emit R_386_GOTPC relocations
868880
// against _GLOBAL_OFFSET_TABLE_ for .debug_info. The bug has been fixed
869881
// in 2017 (https://gcc.gnu.org/bugzilla/show_bug.cgi?id=82630), but we
870882
// need to keep this bug-compatible code for a while.
871-
if (config->emachine == EM_386 && type == R_386_GOTPC)
883+
if (emachine == EM_386 && type == R_386_GOTPC)
872884
continue;
873885

874886
uint64_t offset = rel.r_offset;
@@ -881,6 +893,30 @@ void InputSection::relocateNonAlloc(uint8_t *buf, ArrayRef<RelTy> rels) {
881893
RelExpr expr = target.getRelExpr(type, sym, bufLoc);
882894
if (expr == R_NONE)
883895
continue;
896+
auto *ds = dyn_cast<Defined>(&sym);
897+
898+
if (emachine == EM_RISCV && type == R_RISCV_SET_ULEB128) {
899+
if (++i < relsSize &&
900+
rels[i].getType(/*isMips64EL=*/false) == R_RISCV_SUB_ULEB128 &&
901+
rels[i].r_offset == offset) {
902+
uint64_t val;
903+
if (!ds && tombstone) {
904+
val = *tombstone;
905+
} else {
906+
val = sym.getVA(addend) -
907+
(getFile<ELFT>()->getRelocTargetSym(rels[i]).getVA(0) +
908+
getAddend<ELFT>(rels[i]));
909+
}
910+
if (overwriteULEB128(bufLoc, val) >= 0x80)
911+
errorOrWarn(getLocation(offset) + ": ULEB128 value " + Twine(val) +
912+
" exceeds available space; references '" +
913+
lld::toString(sym) + "'");
914+
continue;
915+
}
916+
errorOrWarn(getLocation(offset) +
917+
": R_RISCV_SET_ULEB128 not paired with R_RISCV_SUB_SET128");
918+
return;
919+
}
884920

885921
if (tombstone ||
886922
(isDebug && (type == target.symbolicRel || expr == R_DTPREL))) {
@@ -912,7 +948,6 @@ void InputSection::relocateNonAlloc(uint8_t *buf, ArrayRef<RelTy> rels) {
912948
//
913949
// TODO To reduce disruption, we use 0 instead of -1 as the tombstone
914950
// value. Enable -1 in a future release.
915-
auto *ds = dyn_cast<Defined>(&sym);
916951
if (!sym.getOutputSection() || (ds && ds->folded && !isDebugLine)) {
917952
// If -z dead-reloc-in-nonalloc= is specified, respect it.
918953
const uint64_t value = tombstone ? SignExtend64<bits>(*tombstone)

lld/ELF/Relocations.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,7 @@ enum RelExpr {
101101
R_PPC64_TOCBASE,
102102
R_PPC64_RELAX_GOT_PC,
103103
R_RISCV_ADD,
104+
R_RISCV_LEB128,
104105
R_RISCV_PC_INDIRECT,
105106
// Same as R_PC but with page-aligned semantics.
106107
R_LOONGARCH_PAGE_PC,

lld/test/ELF/riscv-reloc-leb128.s

Lines changed: 129 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,129 @@
1+
# REQUIRES: riscv
2+
# RUN: rm -rf %t && split-file %s %t && cd %t
3+
# RUN: llvm-mc -filetype=obj -triple=riscv64 -mattr=+relax a.s -o a.o
4+
# RUN: llvm-readobj -r -x .debug_rnglists -x .debug_loclists a.o | FileCheck %s --check-prefix=REL
5+
# RUN: ld.lld -shared --gc-sections a.o -o a.so
6+
# RUN: llvm-readelf -x .debug_rnglists -x .debug_loclists a.so | FileCheck %s
7+
8+
# REL: .rela.debug_rnglists {
9+
# REL-NEXT: 0x0 R_RISCV_SET_ULEB128 w1 0x83
10+
# REL-NEXT: 0x0 R_RISCV_SUB_ULEB128 w2 0x0
11+
# REL-NEXT: 0x1 R_RISCV_SET_ULEB128 w2 0x78
12+
# REL-NEXT: 0x1 R_RISCV_SUB_ULEB128 w1 0x0
13+
# REL-NEXT: 0x3 R_RISCV_SET_ULEB128 w1 0x89
14+
# REL-NEXT: 0x3 R_RISCV_SUB_ULEB128 w2 0x0
15+
# REL-NEXT: 0x5 R_RISCV_SET_ULEB128 w2 0x3FF8
16+
# REL-NEXT: 0x5 R_RISCV_SUB_ULEB128 w1 0x0
17+
# REL-NEXT: 0x8 R_RISCV_SET_ULEB128 w1 0x4009
18+
# REL-NEXT: 0x8 R_RISCV_SUB_ULEB128 w2 0x0
19+
# REL-NEXT: 0xB R_RISCV_SET_ULEB128 w2 0x1FFFF8
20+
# REL-NEXT: 0xB R_RISCV_SUB_ULEB128 w1 0x0
21+
# REL-NEXT: 0xF R_RISCV_SET_ULEB128 w1 0x200009
22+
# REL-NEXT: 0xF R_RISCV_SUB_ULEB128 w2 0x0
23+
# REL-NEXT: }
24+
# REL: .rela.debug_loclists {
25+
# REL-NEXT: 0x0 R_RISCV_SET_ULEB128 w2 0x3
26+
# REL-NEXT: 0x0 R_RISCV_SUB_ULEB128 w1 0x4
27+
# REL-NEXT: 0x1 R_RISCV_SET_ULEB128 x2 0x0
28+
# REL-NEXT: 0x1 R_RISCV_SUB_ULEB128 x1 0x0
29+
# REL-NEXT: }
30+
31+
# REL: Hex dump of section '.debug_rnglists':
32+
# REL-NEXT: 0x00000000 7b800181 01808001 81800180 80800181 {
33+
# REL-NEXT: 0x00000010 808001 .
34+
# REL: Hex dump of section '.debug_loclists':
35+
# REL-NEXT: 0x00000000 0008 .
36+
37+
# CHECK: Hex dump of section '.debug_rnglists':
38+
# CHECK-NEXT: 0x00000000 7ffc0085 01fcff00 858001fc ffff0085 .
39+
# CHECK-NEXT: 0x00000010 808001 .
40+
# CHECK: Hex dump of section '.debug_loclists':
41+
# CHECK-NEXT: 0x00000000 0300 .
42+
43+
# RUN: ld.lld -shared --gc-sections -z dead-reloc-in-nonalloc=.debug_loclists=0x7f a.o -o a127.so
44+
# RUN: llvm-readelf -x .debug_loclists a127.so | FileCheck %s --check-prefix=CHECK127
45+
# CHECK127: Hex dump of section '.debug_loclists':
46+
# CHECK127-NEXT: 0x00000000 037f .
47+
48+
# RUN: not ld.lld -shared --gc-sections -z dead-reloc-in-nonalloc=.debug_loclists=0x80 a.o 2>&1 | FileCheck %s --check-prefix=CHECK128
49+
# CHECK128: error: a.o:(.debug_loclists+0x1): ULEB128 value 128 exceeds available space; references 'x2'
50+
51+
# RUN: llvm-mc -filetype=obj -triple=riscv64 -mattr=+relax sub.s -o sub.o
52+
# RUN: not ld.lld -shared sub.o 2>&1 | FileCheck %s --check-prefix=SUB
53+
# SUB: error: sub.o:(.debug_rnglists+0x8): unknown relocation (61) against symbol w2
54+
55+
# RUN: llvm-mc -filetype=obj -triple=riscv64 -mattr=+relax unpaired1.s -o unpaired1.o
56+
# RUN: not ld.lld -shared unpaired1.o 2>&1 | FileCheck %s --check-prefix=UNPAIRED
57+
# RUN: llvm-mc -filetype=obj -triple=riscv64 -mattr=+relax unpaired2.s -o unpaired2.o
58+
# RUN: not ld.lld -shared unpaired2.o 2>&1 | FileCheck %s --check-prefix=UNPAIRED
59+
# RUN: llvm-mc -filetype=obj -triple=riscv64 -mattr=+relax unpaired3.s -o unpaired3.o
60+
# RUN: not ld.lld -shared unpaired3.o 2>&1 | FileCheck %s --check-prefix=UNPAIRED
61+
# UNPAIRED: error: {{.*}}.o:(.debug_rnglists+0x8): R_RISCV_SET_ULEB128 not paired with R_RISCV_SUB_SET128
62+
63+
# RUN: llvm-mc -filetype=obj -triple=riscv64 -mattr=+relax overflow.s -o overflow.o
64+
# RUN: not ld.lld -shared overflow.o 2>&1 | FileCheck %s --check-prefix=OVERFLOW
65+
# OVERFLOW: error: overflow.o:(.debug_rnglists+0x8): ULEB128 value 128 exceeds available space; references 'w2'
66+
67+
#--- a.s
68+
.section .text.w,"axR"
69+
w1:
70+
call foo # 4 bytes after relaxation
71+
w2:
72+
73+
.section .text.x,"ax"
74+
x1:
75+
call foo # 4 bytes after relaxation
76+
x2:
77+
78+
.section .debug_rnglists
79+
.uleb128 w1-w2+131 # initial value: 0x7b
80+
.uleb128 w2-w1+120 # initial value: 0x0180
81+
.uleb128 w1-w2+137 # initial value: 0x0181
82+
.uleb128 w2-w1+16376 # initial value: 0x018080
83+
.uleb128 w1-w2+16393 # initial value: 0x018081
84+
.uleb128 w2-w1+2097144 # initial value: 0x01808080
85+
.uleb128 w1-w2+2097161 # initial value: 0x01808081
86+
87+
.section .debug_loclists
88+
.reloc ., R_RISCV_SET_ULEB128, w2+3
89+
.reloc ., R_RISCV_SUB_ULEB128, w1+4 # SUB with a non-zero addend
90+
.byte 0
91+
.uleb128 x2-x1 # references discarded symbols
92+
93+
#--- sub.s
94+
w1: call foo; w2:
95+
.section .debug_rnglists
96+
.quad 0;
97+
.reloc ., R_RISCV_SUB_ULEB128, w2+120
98+
.byte 0x7f
99+
100+
#--- unpaired1.s
101+
w1: call foo; w2:
102+
.section .debug_rnglists
103+
.quad 0;
104+
.reloc ., R_RISCV_SET_ULEB128, w2+120
105+
.byte 0x7f
106+
107+
#--- unpaired2.s
108+
w1: call foo; w2:
109+
.section .debug_rnglists
110+
.quad 0
111+
.reloc ., R_RISCV_SET_ULEB128, w2+120
112+
.reloc .+1, R_RISCV_SUB_ULEB128, w1
113+
.byte 0x7f
114+
115+
#--- unpaired3.s
116+
w1: call foo; w2:
117+
.section .debug_rnglists
118+
.quad 0
119+
.reloc ., R_RISCV_SET_ULEB128, w2+120
120+
.reloc ., R_RISCV_SUB64, w1
121+
.byte 0x7f
122+
123+
#--- overflow.s
124+
w1: call foo; w2:
125+
.section .debug_rnglists
126+
.quad 0
127+
.reloc ., R_RISCV_SET_ULEB128, w2+124
128+
.reloc ., R_RISCV_SUB_ULEB128, w1
129+
.byte 0x7f

0 commit comments

Comments
 (0)