Skip to content

Commit d715634

Browse files
MaskRayjiegec
authored andcommitted
[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 bbaf7e5 commit d715634

File tree

10 files changed

+156
-14
lines changed

10 files changed

+156
-14
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;
@@ -194,6 +195,13 @@ class MCAsmBackend {
194195
return false;
195196
}
196197

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

199207
/// 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
@@ -25,6 +25,7 @@ enum MCFixupKind {
2525
FK_Data_4, ///< A four-byte fixup.
2626
FK_Data_8, ///< A eight-byte fixup.
2727
FK_Data_6b, ///< A six-bits fixup.
28+
FK_Data_leb128, ///< A leb128 fixup.
2829
FK_PCRel_1, ///< A one-byte pc relative fixup.
2930
FK_PCRel_2, ///< A two-byte pc relative fixup.
3031
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
@@ -89,6 +89,7 @@ const MCFixupKindInfo &MCAsmBackend::getFixupKindInfo(MCFixupKind Kind) const {
8989
{"FK_Data_4", 0, 32, 0},
9090
{"FK_Data_8", 0, 64, 0},
9191
{"FK_Data_6b", 0, 6, 0},
92+
{"FK_Data_leb128", 0, 0, 0},
9293
{"FK_PCRel_1", 0, 8, MCFixupKindInfo::FKF_IsPCRel},
9394
{"FK_PCRel_2", 0, 16, MCFixupKindInfo::FKF_IsPCRel},
9495
{"FK_PCRel_4", 0, 32, MCFixupKindInfo::FKF_IsPCRel},

llvm/lib/MC/MCAssembler.cpp

Lines changed: 28 additions & 7 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,22 +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);
1012-
if (!Abs)
1013-
report_fatal_error("sleb128 and uleb128 expressions must be absolute");
1014-
SmallString<8> &Data = LF.getContents();
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);
1026+
if (!Abs) {
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));
1035+
}
10151036
Data.clear();
10161037
raw_svector_ostream OSE(Data);
10171038
// The compiler can generate EH table assembly that is impossible to assemble
10181039
// without either adding padding to an LEB fragment or adding extra padding
10191040
// to a later alignment fragment. To accommodate such tables, relaxation can
10201041
// only increase an LEB fragment size here, not decrease it. See PR35809.
10211042
if (LF.isSigned())
1022-
encodeSLEB128(Value, OSE, OldSize);
1043+
encodeSLEB128(Value, OSE, PadTo);
10231044
else
1024-
encodeULEB128(Value, OSE, OldSize);
1045+
encodeULEB128(Value, OSE, PadTo);
10251046
return OldSize != LF.getContents().size();
10261047
}
10271048

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

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
#include "llvm/MC/MCObjectWriter.h"
2020
#include "llvm/MC/MCSymbol.h"
2121
#include "llvm/MC/MCValue.h"
22+
#include "llvm/Support/CommandLine.h"
2223
#include "llvm/Support/Endian.h"
2324
#include "llvm/Support/EndianStream.h"
2425
#include "llvm/Support/ErrorHandling.h"
@@ -27,6 +28,13 @@
2728

2829
using namespace llvm;
2930

31+
// Temporary workaround for old linkers that do not support ULEB128 relocations,
32+
// which are abused by DWARF v5 DW_LLE_offset_pair/DW_RLE_offset_pair
33+
// implemented in Clang/LLVM.
34+
static cl::opt<bool> ULEB128Reloc(
35+
"riscv-uleb128-reloc", cl::init(true), cl::Hidden,
36+
cl::desc("Emit R_RISCV_SET_ULEB128/E_RISCV_SUB_ULEB128 if appropriate"));
37+
3038
std::optional<MCFixupKind> RISCVAsmBackend::getFixupKind(StringRef Name) const {
3139
if (STI.getTargetTriple().isOSBinFormatELF()) {
3240
unsigned Type;
@@ -126,6 +134,7 @@ bool RISCVAsmBackend::shouldForceRelocation(const MCAssembler &Asm,
126134
case FK_Data_2:
127135
case FK_Data_4:
128136
case FK_Data_8:
137+
case FK_Data_leb128:
129138
if (Target.isAbsolute())
130139
return false;
131140
break;
@@ -330,6 +339,18 @@ bool RISCVAsmBackend::relaxDwarfCFA(MCDwarfCallFrameFragment &DF,
330339
return true;
331340
}
332341

342+
bool RISCVAsmBackend::relaxLEB128(MCLEBFragment &LF, MCAsmLayout &Layout,
343+
int64_t &Value) const {
344+
if (LF.isSigned())
345+
return false;
346+
const MCExpr &Expr = LF.getValue();
347+
if (ULEB128Reloc) {
348+
LF.getFixups().push_back(
349+
MCFixup::create(0, &Expr, FK_Data_leb128, Expr.getLoc()));
350+
}
351+
return Expr.evaluateKnownAbsolute(Value, Layout);
352+
}
353+
333354
// Given a compressed control flow instruction this function returns
334355
// the expanded instruction.
335356
unsigned RISCVAsmBackend::getRelaxedOpcode(unsigned Op) const {
@@ -416,6 +437,7 @@ static uint64_t adjustFixupValue(const MCFixup &Fixup, uint64_t Value,
416437
case FK_Data_4:
417438
case FK_Data_8:
418439
case FK_Data_6b:
440+
case FK_Data_leb128:
419441
return Value;
420442
case RISCV::fixup_riscv_set_6b:
421443
return Value & 0x03;
@@ -596,6 +618,10 @@ bool RISCVAsmBackend::handleAddSubRelocations(const MCAsmLayout &Layout,
596618
TA = ELF::R_RISCV_ADD64;
597619
TB = ELF::R_RISCV_SUB64;
598620
break;
621+
case llvm::FK_Data_leb128:
622+
TA = ELF::R_RISCV_SET_ULEB128;
623+
TB = ELF::R_RISCV_SUB_ULEB128;
624+
break;
599625
default:
600626
llvm_unreachable("unsupported fixup size");
601627
}

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)