Skip to content

Commit 836d7e7

Browse files
committed
[JITLink][RISCV] Implement .eh_frame handling
This patch enables .eh_frame handling on RISC-V by using the common `DWARFRecordSectionSplitter`, `EHFrameEdgeFixer`, and `EHFrameNullTerminator` passes. This mostly works out of the box but a minor change was needed for `EHFrameEdgeFixer`: on RISC-V, ADD/SUB relocations are used to calculate the length of a sequence of instructions when relaxation is enabled. Since both relocations are at the same offset, this caused an error to be raised by `EHFrameEdgeFixer`. I have solved this issue by simply ignoring relocations at the same offset on RISC-V. I believe this is fine since the DWARF fields where they are used (PC-range and `DW_CFA_advance_loc`) don't need any special handling. Besides this, two new edge kinds needed to be implemented for RISC-V: `Delta64` and `NegDelta32`
1 parent 4a32c48 commit 836d7e7

File tree

6 files changed

+235
-1
lines changed

6 files changed

+235
-1
lines changed

llvm/include/llvm/ExecutionEngine/JITLink/riscv.h

+21
Original file line numberDiff line numberDiff line change
@@ -203,6 +203,27 @@ enum EdgeKind_riscv : Edge::Kind {
203203
/// Fixup <- (Target - Fixup + Addend)
204204
R_RISCV_32_PCREL,
205205

206+
/// A 64-bit delta.
207+
///
208+
/// Delta from the fixup to the target.
209+
///
210+
/// Fixup expression:
211+
/// Fixup <- Target - Fixup + Addend : int64
212+
///
213+
Delta64,
214+
215+
/// A 32-bit negative delta.
216+
///
217+
/// Delta from the target back to the fixup.
218+
///
219+
/// Fixup expression:
220+
/// Fixup <- Fixup - Target + Addend : int32
221+
///
222+
/// Errors:
223+
/// - The result of the fixup expression must fit into an int32, otherwise
224+
/// an out-of-range error will be returned.
225+
NegDelta32,
226+
206227
/// An auipc/jalr pair eligible for linker relaxation.
207228
///
208229
/// Linker relaxation will replace this with R_RISCV_RVC_JUMP or R_RISCV_JAL

llvm/lib/ExecutionEngine/JITLink/EHFrameSupport.cpp

+8-1
Original file line numberDiff line numberDiff line change
@@ -129,11 +129,18 @@ Error EHFrameEdgeFixer::processBlock(ParseContext &PC, Block &B) {
129129
BlockEdgeMap BlockEdges;
130130
for (auto &E : B.edges())
131131
if (E.isRelocation()) {
132-
if (BlockEdges.count(E.getOffset()))
132+
if (BlockEdges.count(E.getOffset())) {
133+
// RISC-V may use ADD/SUB relocation pairs for PC-range and
134+
// DW_CFA_advance_loc. We don't need to process these fields here so
135+
// just ignore this on RISC-V.
136+
if (PC.G.getTargetTriple().isRISCV())
137+
continue;
138+
133139
return make_error<JITLinkError>(
134140
"Multiple relocations at offset " +
135141
formatv("{0:x16}", E.getOffset()) + " in " + EHFrameSectionName +
136142
" block at address " + formatv("{0:x16}", B.getAddress()));
143+
}
137144

138145
BlockEdges[E.getOffset()] = EdgeTarget(E);
139146
}

llvm/lib/ExecutionEngine/JITLink/ELF_riscv.cpp

+21
Original file line numberDiff line numberDiff line change
@@ -11,10 +11,12 @@
1111
//===----------------------------------------------------------------------===//
1212

1313
#include "llvm/ExecutionEngine/JITLink/ELF_riscv.h"
14+
#include "EHFrameSupportImpl.h"
1415
#include "ELFLinkGraphBuilder.h"
1516
#include "JITLinkGeneric.h"
1617
#include "PerGraphGOTAndPLTStubsBuilder.h"
1718
#include "llvm/BinaryFormat/ELF.h"
19+
#include "llvm/ExecutionEngine/JITLink/DWARFRecordSectionSplitter.h"
1820
#include "llvm/ExecutionEngine/JITLink/JITLink.h"
1921
#include "llvm/ExecutionEngine/JITLink/riscv.h"
2022
#include "llvm/Object/ELF.h"
@@ -453,6 +455,18 @@ class ELFJITLinker_riscv : public JITLinker<ELFJITLinker_riscv> {
453455
*(little32_t *)FixupPtr = Word32;
454456
break;
455457
}
458+
case Delta64: {
459+
int64_t Value = E.getTarget().getAddress() + E.getAddend() - FixupAddress;
460+
*(little64_t *)FixupPtr = Value;
461+
break;
462+
}
463+
case NegDelta32: {
464+
int64_t Value = FixupAddress - E.getTarget().getAddress() + E.getAddend();
465+
if (LLVM_UNLIKELY(!isInRangeForImm(Value, 32)))
466+
return makeTargetOutOfRangeError(G, B, E);
467+
*(little32_t *)FixupPtr = Value;
468+
break;
469+
}
456470
case AlignRelaxable:
457471
// Ignore when the relaxation pass did not run
458472
break;
@@ -959,6 +973,13 @@ void link_ELF_riscv(std::unique_ptr<LinkGraph> G,
959973
PassConfiguration Config;
960974
const Triple &TT = G->getTargetTriple();
961975
if (Ctx->shouldAddDefaultTargetPasses(TT)) {
976+
// Add eh-frame passses.
977+
Config.PrePrunePasses.push_back(DWARFRecordSectionSplitter(".eh_frame"));
978+
Config.PrePrunePasses.push_back(
979+
EHFrameEdgeFixer(".eh_frame", TT.isRISCV32() ? 4 : 8, riscv::R_RISCV_32,
980+
riscv::R_RISCV_64, riscv::R_RISCV_32_PCREL,
981+
riscv::Delta64, riscv::NegDelta32));
982+
Config.PrePrunePasses.push_back(EHFrameNullTerminator(".eh_frame"));
962983
if (auto MarkLive = Ctx->getMarkLivePass(TT))
963984
Config.PrePrunePasses.push_back(std::move(MarkLive));
964985
else

llvm/lib/ExecutionEngine/JITLink/riscv.cpp

+4
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,10 @@ const char *getEdgeKindName(Edge::Kind K) {
7878
return "R_RISCV_SET32";
7979
case R_RISCV_32_PCREL:
8080
return "R_RISCV_32_PCREL";
81+
case Delta64:
82+
return "Delta64";
83+
case NegDelta32:
84+
return "NegDelta32";
8185
case CallRelaxable:
8286
return "CallRelaxable";
8387
case AlignRelaxable:
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
# REQUIRES: asserts
2+
3+
# RUN: llvm-mc -triple=riscv32-linux-gnu -mattr=+relax -filetype=obj -o %t.32.o %s
4+
# RUN: llvm-jitlink -noexec -phony-externals -debug-only=jitlink %t.32.o 2>&1 | \
5+
# RUN: FileCheck %s
6+
7+
# RUN: llvm-mc -triple=riscv64-linux-gnu -mattr=+relax -filetype=obj -o %t.64.o %s
8+
# RUN: llvm-jitlink -noexec -phony-externals -debug-only=jitlink %t.64.o 2>&1 | \
9+
# RUN: FileCheck %s
10+
11+
# Check that splitting of eh-frame sections works.
12+
#
13+
# CHECK: DWARFRecordSectionSplitter: Processing .eh_frame...
14+
# CHECK: Processing block at
15+
# CHECK: Processing CFI record at
16+
# CHECK: Extracted {{.*}} section = .eh_frame
17+
# CHECK: Processing CFI record at
18+
# CHECK: Extracted {{.*}} section = .eh_frame
19+
# CHECK: EHFrameEdgeFixer: Processing .eh_frame in "{{.*}}"...
20+
# CHECK: Processing block at
21+
# CHECK: Processing CFI record at
22+
# CHECK: Record is CIE
23+
# CHECK: Processing block at
24+
# CHECK: Processing CFI record at
25+
# CHECK: Record is FDE
26+
# CHECK: Adding edge at {{.*}} to CIE at: {{.*}}
27+
# CHECK: Existing edge at {{.*}} to PC begin at {{.*}}
28+
# CHECK: Adding keep-alive edge from target at {{.*}} to FDE at {{.*}}
29+
# CHECK: Processing block at
30+
# CHECK: Processing CFI record at
31+
# CHECK: Record is FDE
32+
# CHECK: Adding edge at {{.*}} to CIE at: {{.*}}
33+
# CHECK: Existing edge at {{.*}} to PC begin at {{.*}}
34+
# CHECK: Adding keep-alive edge from target at {{.*}} to FDE at {{.*}}
35+
36+
## This is "int main { throw 1; }" compiled for riscv32. We use the 32-bit
37+
## version because it is also legal for riscv64.
38+
.text
39+
.globl main
40+
.p2align 1
41+
.type main,@function
42+
main:
43+
.cfi_startproc
44+
addi sp, sp, -16
45+
.cfi_def_cfa_offset 16
46+
sw ra, 12(sp)
47+
.cfi_offset ra, -4
48+
li a0, 4
49+
call __cxa_allocate_exception
50+
li a1, 1
51+
sw a1, 0(a0)
52+
lga a1, _ZTIi
53+
li a2, 0
54+
call __cxa_throw
55+
.Lfunc_end0:
56+
.size main, .Lfunc_end0-main
57+
.cfi_endproc
58+
59+
.globl dup
60+
.p2align 1
61+
.type dup,@function
62+
dup:
63+
.cfi_startproc
64+
addi sp, sp, -16
65+
.cfi_def_cfa_offset 16
66+
sw ra, 12(sp)
67+
.cfi_offset ra, -4
68+
li a0, 4
69+
call __cxa_allocate_exception
70+
li a1, 1
71+
sw a1, 0(a0)
72+
lga a1, _ZTIi
73+
li a2, 0
74+
call __cxa_throw
75+
.Lfunc_end1:
76+
.size dup, .Lfunc_end1-dup
77+
.cfi_endproc
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
# RUN: yaml2obj -DELFCLASS=ELFCLASS32 -o %t.32.o %s
2+
# RUN: llvm-jitlink -noexec -check %s %t.32.o
3+
# RUN: yaml2obj -DELFCLASS=ELFCLASS64 -o %t.64.o %s
4+
# RUN: llvm-jitlink -noexec -check %s %t.64.o
5+
6+
### Compiled from the following code with -mattr=+relax to force relocations for
7+
### address_range and DW_CFA_advance_loc (both needed for .balign).
8+
## .text
9+
## .globl main
10+
## .p2align 1
11+
## .type main,@function
12+
## main:
13+
## .cfi_startproc
14+
## .balign 8
15+
## addi sp, sp, -16
16+
## cfa_advance_loc:
17+
## .cfi_def_cfa_offset 16
18+
## nop
19+
## main_end:
20+
## .size main, main_end-main
21+
## .cfi_endproc
22+
23+
--- !ELF
24+
FileHeader:
25+
Class: [[ELFCLASS]]
26+
Data: ELFDATA2LSB
27+
Type: ET_REL
28+
Machine: EM_RISCV
29+
SectionHeaderStringTable: .strtab
30+
Sections:
31+
- Name: .text
32+
Type: SHT_PROGBITS
33+
Flags: [ SHF_ALLOC, SHF_EXECINSTR ]
34+
AddressAlign: 0x8
35+
Content: 13000000130101FF13000000
36+
- Name: .eh_frame
37+
Type: SHT_PROGBITS
38+
Flags: [ SHF_ALLOC ]
39+
AddressAlign: 0x8
40+
Content: 1000000000000000017A5200017801011B0C02001000000018000000000000000000000000400E10
41+
- Name: .rela.text
42+
Type: SHT_RELA
43+
Flags: [ SHF_INFO_LINK ]
44+
Link: .symtab
45+
AddressAlign: 0x8
46+
Info: .text
47+
Relocations:
48+
- Type: R_RISCV_ALIGN
49+
Addend: 4
50+
- Name: .rela.eh_frame
51+
Type: SHT_RELA
52+
Flags: [ SHF_INFO_LINK ]
53+
Link: .symtab
54+
AddressAlign: 0x8
55+
Info: .eh_frame
56+
Relocations:
57+
- Offset: 0x1C
58+
Symbol: main
59+
Type: R_RISCV_32_PCREL
60+
- Offset: 0x20
61+
Symbol: main_end
62+
Type: R_RISCV_ADD32
63+
- Offset: 0x20
64+
Symbol: main
65+
Type: R_RISCV_SUB32
66+
- Offset: 0x25
67+
Symbol: cfa_advance_loc
68+
Type: R_RISCV_SET6
69+
- Offset: 0x25
70+
Symbol: main
71+
Type: R_RISCV_SUB6
72+
- Type: SectionHeaderTable
73+
Sections:
74+
- Name: .strtab
75+
- Name: .text
76+
- Name: .rela.text
77+
- Name: .eh_frame
78+
- Name: .rela.eh_frame
79+
- Name: .symtab
80+
Symbols:
81+
- Name: cfa_advance_loc
82+
Section: .text
83+
Value: 0x8
84+
- Name: main_end
85+
Section: .text
86+
Value: 0xC
87+
- Name: main
88+
Type: STT_FUNC
89+
Section: .text
90+
Binding: STB_GLOBAL
91+
Size: 0xC
92+
- Name: eh_frame
93+
Type: STT_SECTION
94+
Binding: STB_GLOBAL
95+
Section: .eh_frame
96+
Size: 0x28
97+
...
98+
99+
## CIE_pointer
100+
# jitlink-check: *{4}(eh_frame + 0x1c) = main - (eh_frame + 0x1c)
101+
## address_range
102+
# jitlink-check: *{4}(eh_frame + 0x20) = main_end - main
103+
## DW_CFA_advance_loc
104+
# jitlink-check: (*{1}(eh_frame + 0x25)) & 0x3f = cfa_advance_loc - main

0 commit comments

Comments
 (0)