Skip to content

Commit 1df5ea2

Browse files
committed
[RISCV] Support R_RISCV_SET_ULEB128/R_RISCV_SUB_ULEB128 for .uleb128 directives
For a label difference like `.uleb128 A-B`, MC folds A-B even if A and B are separated by a RISC-V linker-relaxable instruction. This incorrect behavior is currently abused by DWARF v5 .debug_loclists/.debug_rnglists (DW_LLE_offset_pair/DW_RLE_offset_pair entry kinds) implemented in Clang/LLVM (see ClangBuiltLinux/linux#1719 for an instance). riscv-non-isa/riscv-elf-psabi-doc@96d6e19 defined R_RISCV_SET_ULEB128/R_RISCV_SUB_ULEB128. This patch generates such a pair of relocations to represent A-B that should not be folded. GNU assembler computes the directive size by ignoring shrinkable section content, therefore after linking the value of A-B cannot use more bytes than the reserved number (`final size of uleb128 value at offset ... exceeds available space`). We make the same assumption. ``` w1: call foo w2: .space 120 w3: .uleb128 w2-w1 # 1 byte, 0x08 .uleb128 w3-w1 # 2 bytes, 0x80 0x01 ``` We do not conservatively reserve 10 bytes (maximum size of an uleb128 for uint64_t) as that would pessimize DWARF v5 DW_LLE_offset_pair/DW_RLE_offset_pair, nullifying the benefits of introducing R_RISCV_SET_ULEB128/R_RISCV_SUB_ULEB128 relocations. The supported expressions are limited. For example, * non-subtraction `.uleb128 A` is not allowed * `.uleb128 A-B`: report an error unless A and B are both defined and in the same section The new cl::opt `-riscv-uleb128-reloc` can be used to suppress the relocations. Reviewed By: asb Differential Revision: https://reviews.llvm.org/D157657
1 parent e17efa6 commit 1df5ea2

File tree

10 files changed

+152
-15
lines changed

10 files changed

+152
-15
lines changed

llvm/include/llvm/BinaryFormat/ELFRelocs/RISCV.def

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,3 +55,5 @@ ELF_RELOC(R_RISCV_SET32, 56)
5555
ELF_RELOC(R_RISCV_32_PCREL, 57)
5656
ELF_RELOC(R_RISCV_IRELATIVE, 58)
5757
ELF_RELOC(R_RISCV_PLT32, 59)
58+
ELF_RELOC(R_RISCV_SET_ULEB128, 60)
59+
ELF_RELOC(R_RISCV_SUB_ULEB128, 61)

llvm/include/llvm/MC/MCAsmBackend.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ class MCAlignFragment;
2121
class MCDwarfCallFrameFragment;
2222
class MCDwarfLineAddrFragment;
2323
class MCFragment;
24+
class MCLEBFragment;
2425
class MCRelaxableFragment;
2526
class MCSymbol;
2627
class MCAsmLayout;
@@ -193,6 +194,13 @@ class MCAsmBackend {
193194
return false;
194195
}
195196

197+
// Defined by linker relaxation targets to possibly emit LEB128 relocations
198+
// and set Value at the relocated location.
199+
virtual bool relaxLEB128(MCLEBFragment &LF, MCAsmLayout &Layout,
200+
int64_t &Value) const {
201+
return false;
202+
}
203+
196204
/// @}
197205

198206
/// Returns the minimum size of a nop in bytes on this target. The assembler

llvm/include/llvm/MC/MCFixup.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ enum MCFixupKind {
2424
FK_Data_2, ///< A two-byte fixup.
2525
FK_Data_4, ///< A four-byte fixup.
2626
FK_Data_8, ///< A eight-byte fixup.
27+
FK_Data_leb128, ///< A leb128 fixup.
2728
FK_PCRel_1, ///< A one-byte pc relative fixup.
2829
FK_PCRel_2, ///< A two-byte pc relative fixup.
2930
FK_PCRel_4, ///< A four-byte pc relative fixup.

llvm/include/llvm/MC/MCFragment.h

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -428,7 +428,7 @@ class MCOrgFragment : public MCFragment {
428428
}
429429
};
430430

431-
class MCLEBFragment : public MCFragment {
431+
class MCLEBFragment final : public MCEncodedFragmentWithFixups<10, 1> {
432432
/// True if this is a sleb128, false if uleb128.
433433
bool IsSigned;
434434

@@ -438,18 +438,17 @@ class MCLEBFragment : public MCFragment {
438438
SmallString<8> Contents;
439439

440440
public:
441-
MCLEBFragment(const MCExpr &Value_, bool IsSigned_, MCSection *Sec = nullptr)
442-
: MCFragment(FT_LEB, false, Sec), IsSigned(IsSigned_), Value(&Value_) {
441+
MCLEBFragment(const MCExpr &Value, bool IsSigned, MCSection *Sec = nullptr)
442+
: MCEncodedFragmentWithFixups<10, 1>(FT_LEB, false, Sec),
443+
IsSigned(IsSigned), Value(&Value) {
443444
Contents.push_back(0);
444445
}
445446

446447
const MCExpr &getValue() const { return *Value; }
448+
void setValue(const MCExpr *Expr) { Value = Expr; }
447449

448450
bool isSigned() const { return IsSigned; }
449451

450-
SmallString<8> &getContents() { return Contents; }
451-
const SmallString<8> &getContents() const { return Contents; }
452-
453452
/// @}
454453

455454
static bool classof(const MCFragment *F) {

llvm/lib/MC/MCAsmBackend.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,7 @@ const MCFixupKindInfo &MCAsmBackend::getFixupKindInfo(MCFixupKind Kind) const {
9292
{"FK_Data_2", 0, 16, 0},
9393
{"FK_Data_4", 0, 32, 0},
9494
{"FK_Data_8", 0, 64, 0},
95+
{"FK_Data_leb128", 0, 0, 0},
9596
{"FK_PCRel_1", 0, 8, MCFixupKindInfo::FKF_IsPCRel},
9697
{"FK_PCRel_2", 0, 16, MCFixupKindInfo::FKF_IsPCRel},
9798
{"FK_PCRel_4", 0, 32, MCFixupKindInfo::FKF_IsPCRel},

llvm/lib/MC/MCAssembler.cpp

Lines changed: 26 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -918,6 +918,12 @@ void MCAssembler::layout(MCAsmLayout &Layout) {
918918
Contents = DF.getContents();
919919
break;
920920
}
921+
case MCFragment::FT_LEB: {
922+
auto &LF = cast<MCLEBFragment>(Frag);
923+
Fixups = LF.getFixups();
924+
Contents = LF.getContents();
925+
break;
926+
}
921927
case MCFragment::FT_PseudoProbe: {
922928
MCPseudoProbeAddrFragment &PF = cast<MCPseudoProbeAddrFragment>(Frag);
923929
Fixups = PF.getFixups();
@@ -1006,25 +1012,37 @@ bool MCAssembler::relaxInstruction(MCAsmLayout &Layout,
10061012
}
10071013

10081014
bool MCAssembler::relaxLEB(MCAsmLayout &Layout, MCLEBFragment &LF) {
1009-
uint64_t OldSize = LF.getContents().size();
1015+
const unsigned OldSize = static_cast<unsigned>(LF.getContents().size());
1016+
unsigned PadTo = OldSize;
10101017
int64_t Value;
1011-
bool Abs = LF.getValue().evaluateKnownAbsolute(Value, Layout);
1018+
SmallVectorImpl<char> &Data = LF.getContents();
1019+
LF.getFixups().clear();
1020+
// Use evaluateKnownAbsolute for Mach-O as a hack: .subsections_via_symbols
1021+
// requires that .uleb128 A-B is foldable where A and B reside in different
1022+
// fragments. This is used by __gcc_except_table.
1023+
bool Abs = getSubsectionsViaSymbols()
1024+
? LF.getValue().evaluateKnownAbsolute(Value, Layout)
1025+
: LF.getValue().evaluateAsAbsolute(Value, Layout);
10121026
if (!Abs) {
1013-
getContext().reportError(LF.getValue().getLoc(),
1014-
Twine(LF.isSigned() ? ".s" : ".u") +
1015-
"leb128 expression is not absolute");
1027+
if (!getBackend().relaxLEB128(LF, Layout, Value)) {
1028+
getContext().reportError(LF.getValue().getLoc(),
1029+
Twine(LF.isSigned() ? ".s" : ".u") +
1030+
"leb128 expression is not absolute");
1031+
LF.setValue(MCConstantExpr::create(0, Context));
1032+
}
1033+
uint8_t Tmp[10]; // maximum size: ceil(64/7)
1034+
PadTo = std::max(PadTo, encodeULEB128(uint64_t(Value), Tmp));
10161035
}
1017-
SmallString<8> &Data = LF.getContents();
10181036
Data.clear();
10191037
raw_svector_ostream OSE(Data);
10201038
// The compiler can generate EH table assembly that is impossible to assemble
10211039
// without either adding padding to an LEB fragment or adding extra padding
10221040
// to a later alignment fragment. To accommodate such tables, relaxation can
10231041
// only increase an LEB fragment size here, not decrease it. See PR35809.
10241042
if (LF.isSigned())
1025-
encodeSLEB128(Value, OSE, OldSize);
1043+
encodeSLEB128(Value, OSE, PadTo);
10261044
else
1027-
encodeULEB128(Value, OSE, OldSize);
1045+
encodeULEB128(Value, OSE, PadTo);
10281046
return OldSize != LF.getContents().size();
10291047
}
10301048

llvm/lib/Target/RISCV/MCTargetDesc/RISCVAsmBackend.cpp

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,12 @@ using namespace llvm;
3030

3131
static cl::opt<bool> RelaxBranches("riscv-asm-relax-branches", cl::init(true),
3232
cl::Hidden);
33+
// Temporary workaround for old linkers that do not support ULEB128 relocations,
34+
// which are abused by DWARF v5 DW_LLE_offset_pair/DW_RLE_offset_pair
35+
// implemented in Clang/LLVM.
36+
static cl::opt<bool> ULEB128Reloc(
37+
"riscv-uleb128-reloc", cl::init(true), cl::Hidden,
38+
cl::desc("Emit R_RISCV_SET_ULEB128/E_RISCV_SUB_ULEB128 if appropriate"));
3339

3440
std::optional<MCFixupKind> RISCVAsmBackend::getFixupKind(StringRef Name) const {
3541
if (STI.getTargetTriple().isOSBinFormatELF()) {
@@ -112,6 +118,7 @@ bool RISCVAsmBackend::shouldForceRelocation(const MCAssembler &Asm,
112118
case FK_Data_2:
113119
case FK_Data_4:
114120
case FK_Data_8:
121+
case FK_Data_leb128:
115122
if (Target.isAbsolute())
116123
return false;
117124
break;
@@ -321,6 +328,18 @@ bool RISCVAsmBackend::relaxDwarfCFA(MCDwarfCallFrameFragment &DF,
321328
return true;
322329
}
323330

331+
bool RISCVAsmBackend::relaxLEB128(MCLEBFragment &LF, MCAsmLayout &Layout,
332+
int64_t &Value) const {
333+
if (LF.isSigned())
334+
return false;
335+
const MCExpr &Expr = LF.getValue();
336+
if (ULEB128Reloc) {
337+
LF.getFixups().push_back(
338+
MCFixup::create(0, &Expr, FK_Data_leb128, Expr.getLoc()));
339+
}
340+
return Expr.evaluateKnownAbsolute(Value, Layout);
341+
}
342+
324343
// Given a compressed control flow instruction this function returns
325344
// the expanded instruction.
326345
unsigned RISCVAsmBackend::getRelaxedOpcode(unsigned Op) const {
@@ -395,6 +414,7 @@ static uint64_t adjustFixupValue(const MCFixup &Fixup, uint64_t Value,
395414
case FK_Data_2:
396415
case FK_Data_4:
397416
case FK_Data_8:
417+
case FK_Data_leb128:
398418
return Value;
399419
case RISCV::fixup_riscv_lo12_i:
400420
case RISCV::fixup_riscv_pcrel_lo12_i:
@@ -577,6 +597,10 @@ bool RISCVAsmBackend::handleAddSubRelocations(const MCAsmLayout &Layout,
577597
TA = ELF::R_RISCV_ADD64;
578598
TB = ELF::R_RISCV_SUB64;
579599
break;
600+
case llvm::FK_Data_leb128:
601+
TA = ELF::R_RISCV_SET_ULEB128;
602+
TB = ELF::R_RISCV_SUB_ULEB128;
603+
break;
580604
default:
581605
llvm_unreachable("unsupported fixup size");
582606
}

llvm/lib/Target/RISCV/MCTargetDesc/RISCVAsmBackend.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,8 @@ class RISCVAsmBackend : public MCAsmBackend {
9999
bool &WasRelaxed) const override;
100100
bool relaxDwarfCFA(MCDwarfCallFrameFragment &DF, MCAsmLayout &Layout,
101101
bool &WasRelaxed) const override;
102+
bool relaxLEB128(MCLEBFragment &LF, MCAsmLayout &Layout,
103+
int64_t &Value) const override;
102104

103105
bool writeNopData(raw_ostream &OS, uint64_t Count,
104106
const MCSubtargetInfo *STI) const override;

llvm/test/MC/ELF/RISCV/gen-dwarf.s

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,9 +48,10 @@
4848
# RELOC-NEXT: 0x34 R_RISCV_32_PCREL <null> 0x0
4949
# RELOC-NEXT: }
5050

51-
## TODO A section needs two relocations.
5251
# RELOC: Section ([[#]]) .rela.debug_rnglists {
5352
# RELOC-NEXT: 0xD R_RISCV_64 .text.foo 0x0
53+
# RELOC-NEXT: 0x15 R_RISCV_SET_ULEB128 <null> 0x0
54+
# RELOC-NEXT: 0x15 R_RISCV_SUB_ULEB128 .text.foo 0x0
5455
# RELOC-NEXT: 0x17 R_RISCV_64 .text.bar 0x0
5556
# RELOC-NEXT: }
5657

llvm/test/MC/RISCV/leb128.s

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
# RUN: llvm-mc -filetype=obj -triple=riscv32 -mattr=-relax %s -o %t
2+
# RUN: llvm-readobj -r -x .alloc_w %t| FileCheck %s
3+
# RUN: llvm-mc -filetype=obj -triple=riscv32 -mattr=+relax %s -o %t.relax
4+
# RUN: llvm-readobj -r -x .alloc_w %t.relax | FileCheck %s --check-prefixes=CHECK,RELAX
5+
6+
# RUN: llvm-mc -filetype=obj -triple=riscv64 -mattr=-relax %s -o %t
7+
# RUN: llvm-readobj -r -x .alloc_w %t | FileCheck %s
8+
# RUN: llvm-mc -filetype=obj -triple=riscv64 -mattr=+relax %s -o %t.relax
9+
# RUN: llvm-readobj -r -x .alloc_w %t.relax | FileCheck %s --check-prefixes=CHECK,RELAX
10+
11+
## Test temporary workaround for suppressting relocations for actually-non-foldable
12+
## DWARF v5 DW_LLE_offset_pair/DW_RLE_offset_pair.
13+
# RUN: llvm-mc -filetype=obj -triple=riscv64 -mattr=-relax -riscv-uleb128-reloc=0 %s -o %t0
14+
# RUN: llvm-readobj -r -x .alloc_w %t0 | FileCheck %s --check-prefix=CHECK0
15+
# RUN: llvm-mc -filetype=obj -triple=riscv64 -mattr=+relax -riscv-uleb128-reloc=0 %s -o %t0.relax
16+
# RUN: llvm-readobj -r -x .alloc_w %t0.relax | FileCheck %s --check-prefixes=CHECK0,RELAX0
17+
18+
# RUN: not llvm-mc -filetype=obj -triple=riscv64 -mattr=-relax --defsym ERR=1 %s -o /dev/null 2>&1 | \
19+
# RUN: FileCheck %s --check-prefix=ERR
20+
# RUN: not llvm-mc -filetype=obj -triple=riscv64 -mattr=+relax --defsym ERR=1 %s -o /dev/null 2>&1 | \
21+
# RUN: FileCheck %s --check-prefix=ERR
22+
23+
# CHECK0: Relocations [
24+
# CHECK0-NEXT: .rela.alloc_w {
25+
# CHECK0-NEXT: 0x2 R_RISCV_CALL_PLT foo 0x0
26+
# RELAX0-NEXT: 0x2 R_RISCV_RELAX - 0x0
27+
# CHECK0-NEXT: }
28+
# CHECK0-NEXT: ]
29+
30+
# CHECK: Relocations [
31+
# CHECK-NEXT: .rela.alloc_w {
32+
# CHECK-NEXT: 0x0 R_RISCV_SET_ULEB128 w1 0x0
33+
# CHECK-NEXT: 0x0 R_RISCV_SUB_ULEB128 w 0x0
34+
# RELAX-NEXT: 0x1 R_RISCV_SET_ULEB128 w2 0x0
35+
# RELAX-NEXT: 0x1 R_RISCV_SUB_ULEB128 w1 0x0
36+
# CHECK-NEXT: 0x2 R_RISCV_CALL_PLT foo 0x0
37+
# RELAX-NEXT: 0x2 R_RISCV_RELAX - 0x0
38+
# RELAX-NEXT: 0xA R_RISCV_SET_ULEB128 w2 0x0
39+
# RELAX-NEXT: 0xA R_RISCV_SUB_ULEB128 w1 0x0
40+
# RELAX-NEXT: 0xB R_RISCV_SET_ULEB128 w2 0x78
41+
# RELAX-NEXT: 0xB R_RISCV_SUB_ULEB128 w1 0x0
42+
# RELAX-NEXT: 0xD R_RISCV_SET_ULEB128 w1 0x0
43+
# RELAX-NEXT: 0xD R_RISCV_SUB_ULEB128 w2 0x0
44+
# CHECK-NEXT: }
45+
# CHECK-NEXT: ]
46+
47+
## R_RISCV_SET_ULEB128 relocated locations contain values not accounting for linker relaxation.
48+
# CHECK: Hex dump of section '.alloc_w':
49+
# CHECK-NEXT: 0x00000000 02089700 0000e780 00000880 01f8ffff ................
50+
# CHECK-NEXT: 0x00000010 ffffffff ffff01 .......
51+
52+
.section .alloc_w,"ax",@progbits; w:
53+
.uleb128 w1-w # w1 is later defined in the same section
54+
.uleb128 w2-w1 # w1 and w2 are separated by a linker relaxable instruction
55+
w1:
56+
call foo
57+
w2:
58+
.uleb128 w2-w1 # 0x08
59+
.uleb128 w2-w1+120 # 0x0180
60+
.uleb128 -(w2-w1) # 0x01fffffffffffffffff8
61+
62+
.ifdef ERR
63+
# ERR: :[[#@LINE+1]]:16: error: .uleb128 expression is not absolute
64+
.uleb128 extern-w # extern is undefined
65+
# ERR: :[[#@LINE+1]]:11: error: .uleb128 expression is not absolute
66+
.uleb128 w-extern
67+
# ERR: :[[#@LINE+1]]:11: error: .uleb128 expression is not absolute
68+
.uleb128 x-w # x is later defined in another section
69+
70+
.section .alloc_x,"aw",@progbits; x:
71+
# ERR: :[[#@LINE+1]]:11: error: .uleb128 expression is not absolute
72+
.uleb128 y-x
73+
.section .alloc_y,"aw",@progbits; y:
74+
# ERR: :[[#@LINE+1]]:11: error: .uleb128 expression is not absolute
75+
.uleb128 x-y
76+
77+
# ERR: :[[#@LINE+1]]:10: error: .uleb128 expression is not absolute
78+
.uleb128 extern
79+
# ERR: :[[#@LINE+1]]:10: error: .uleb128 expression is not absolute
80+
.uleb128 y
81+
.endif

0 commit comments

Comments
 (0)