Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/contrib/llvm-project/compiler-rt/lib/xray/xray_riscv.cpp
213766 views
1
//===-- xray_riscv.cpp ----------------------------------------*- C++ -*-===//
2
//
3
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4
// See https://llvm.org/LICENSE.txt for license information.
5
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6
//
7
//===----------------------------------------------------------------------===//
8
//
9
// This file is a part of XRay, a dynamic runtime instrumentation system.
10
//
11
// Implementation of RISC-V specific routines (32- and 64-bit).
12
//
13
//===----------------------------------------------------------------------===//
14
#include "sanitizer_common/sanitizer_common.h"
15
#include "xray_defs.h"
16
#include "xray_interface_internal.h"
17
#include <atomic>
18
19
namespace __xray {
20
21
// The machine codes for some instructions used in runtime patching.
22
enum PatchOpcodes : uint32_t {
23
PO_ADDI = 0x00000013, // addi rd, rs1, imm
24
PO_ADD = 0x00000033, // add rd, rs1, rs2
25
PO_SW = 0x00002023, // sw rs2, imm(rs1)
26
PO_SD = 0x00003023, // sd rs2, imm(rs1)
27
PO_LUI = 0x00000037, // lui rd, imm
28
PO_OR = 0x00006033, // or rd, rs1, rs2
29
PO_SLLI = 0x00001013, // slli rd, rs1, shamt
30
PO_JALR = 0x00000067, // jalr rd, rs1
31
PO_LW = 0x00002003, // lw rd, imm(rs1)
32
PO_LD = 0x00003003, // ld rd, imm(rs1)
33
PO_J = 0x0000006f, // jal imm
34
PO_NOP = PO_ADDI, // addi x0, x0, 0
35
};
36
37
enum RegNum : uint32_t {
38
RN_X0 = 0,
39
RN_RA = 1,
40
RN_SP = 2,
41
RN_T1 = 6,
42
RN_A0 = 10,
43
};
44
45
static inline uint32_t encodeRTypeInstruction(uint32_t Opcode, uint32_t Rs1,
46
uint32_t Rs2, uint32_t Rd) {
47
return Rs2 << 20 | Rs1 << 15 | Rd << 7 | Opcode;
48
}
49
50
static inline uint32_t encodeITypeInstruction(uint32_t Opcode, uint32_t Rs1,
51
uint32_t Rd, uint32_t Imm) {
52
return Imm << 20 | Rs1 << 15 | Rd << 7 | Opcode;
53
}
54
55
static inline uint32_t encodeSTypeInstruction(uint32_t Opcode, uint32_t Rs1,
56
uint32_t Rs2, uint32_t Imm) {
57
uint32_t ImmMSB = (Imm & 0xfe0) << 20;
58
uint32_t ImmLSB = (Imm & 0x01f) << 7;
59
return ImmMSB | Rs2 << 20 | Rs1 << 15 | ImmLSB | Opcode;
60
}
61
62
static inline uint32_t encodeUTypeInstruction(uint32_t Opcode, uint32_t Rd,
63
uint32_t Imm) {
64
return Imm << 12 | Rd << 7 | Opcode;
65
}
66
67
static inline uint32_t encodeJTypeInstruction(uint32_t Opcode, uint32_t Rd,
68
uint32_t Imm) {
69
uint32_t ImmMSB = (Imm & 0x100000) << 11;
70
uint32_t ImmLSB = (Imm & 0x7fe) << 20;
71
uint32_t Imm11 = (Imm & 0x800) << 9;
72
uint32_t Imm1912 = (Imm & 0xff000);
73
return ImmMSB | ImmLSB | Imm11 | Imm1912 | Rd << 7 | Opcode;
74
}
75
76
static uint32_t hi20(uint32_t val) { return (val + 0x800) >> 12; }
77
static uint32_t lo12(uint32_t val) { return val & 0xfff; }
78
79
static inline bool patchSled(const bool Enable, const uint32_t FuncId,
80
const XRaySledEntry &Sled,
81
void (*TracingHook)()) XRAY_NEVER_INSTRUMENT {
82
// When |Enable| == true,
83
// We replace the following compile-time stub (sled):
84
//
85
// xray_sled_n:
86
// J .tmpN
87
// 21 or 33 C.NOPs (42 or 66 bytes)
88
// .tmpN
89
//
90
// With one of the following runtime patches:
91
//
92
// xray_sled_n (32-bit):
93
// addi sp, sp, -16 ;create stack frame
94
// sw ra, 12(sp) ;save return address
95
// sw a0, 8(sp) ;save register a0
96
// lui ra, %hi(__xray_FunctionEntry/Exit)
97
// addi ra, ra, %lo(__xray_FunctionEntry/Exit)
98
// lui a0, %hi(function_id)
99
// addi a0, a0, %lo(function_id) ;pass function id
100
// jalr ra ;call Tracing hook
101
// lw a0, 8(sp) ;restore register a0
102
// lw ra, 12(sp) ;restore return address
103
// addi sp, sp, 16 ;delete stack frame
104
//
105
// xray_sled_n (64-bit):
106
// addi sp, sp, -32 ;create stack frame
107
// sd ra, 24(sp) ;save return address
108
// sd a0, 16(sp) ;save register a0
109
// sd t1, 8(sp) ;save register t1
110
// lui t1, %highest(__xray_FunctionEntry/Exit)
111
// addi t1, t1, %higher(__xray_FunctionEntry/Exit)
112
// slli t1, t1, 32
113
// lui ra, ra, %hi(__xray_FunctionEntry/Exit)
114
// addi ra, ra, %lo(__xray_FunctionEntry/Exit)
115
// add ra, t1, ra
116
// lui a0, %hi(function_id)
117
// addi a0, a0, %lo(function_id) ;pass function id
118
// jalr ra ;call Tracing hook
119
// ld t1, 8(sp) ;restore register t1
120
// ld a0, 16(sp) ;restore register a0
121
// ld ra, 24(sp) ;restore return address
122
// addi sp, sp, 32 ;delete stack frame
123
//
124
// Replacement of the first 4-byte instruction should be the last and atomic
125
// operation, so that the user code which reaches the sled concurrently
126
// either jumps over the whole sled, or executes the whole sled when the
127
// latter is ready.
128
//
129
// When |Enable|==false, we set back the first instruction in the sled to be
130
// J 44 bytes (rv32)
131
// J 68 bytes (rv64)
132
133
uint32_t *Address = reinterpret_cast<uint32_t *>(Sled.address());
134
if (Enable) {
135
#if __riscv_xlen == 64
136
// If the ISA is RV64, the Tracing Hook needs to be typecast to a 64 bit
137
// value.
138
uint32_t LoTracingHookAddr = lo12(reinterpret_cast<uint64_t>(TracingHook));
139
uint32_t HiTracingHookAddr = hi20(reinterpret_cast<uint64_t>(TracingHook));
140
uint32_t HigherTracingHookAddr =
141
lo12((reinterpret_cast<uint64_t>(TracingHook) + 0x80000000) >> 32);
142
uint32_t HighestTracingHookAddr =
143
hi20((reinterpret_cast<uint64_t>(TracingHook) + 0x80000000) >> 32);
144
#elif __riscv_xlen == 32
145
// We typecast the Tracing Hook to a 32 bit value for RV32
146
uint32_t LoTracingHookAddr = lo12(reinterpret_cast<uint32_t>(TracingHook));
147
uint32_t HiTracingHookAddr = hi20((reinterpret_cast<uint32_t>(TracingHook));
148
#endif
149
uint32_t LoFunctionID = lo12(FuncId);
150
uint32_t HiFunctionID = hi20(FuncId);
151
152
// The sled that is patched in for RISCV64 defined below. We need the entire
153
// sleds corresponding to both ISAs to be protected by defines because the
154
// first few instructions are all different, because we store doubles in
155
// case of RV64 and store words for RV32. Subsequently, we have LUI - and in
156
// case of RV64, we need extra instructions from this point on, so we see
157
// differences in addresses to which instructions are stored.
158
size_t Idx = 1U;
159
const uint32_t XLenBytes = __riscv_xlen / 8;
160
#if __riscv_xlen == 64
161
const uint32_t LoadOp = PatchOpcodes::PO_LD;
162
const uint32_t StoreOp = PatchOpcodes::PO_SD;
163
#elif __riscv_xlen == 32
164
const uint32_t LoadOp = PatchOpcodes::PO_LW;
165
const uint32_t StoreOp = PatchOpcodes::PO_SW;
166
#endif
167
168
Address[Idx++] = encodeSTypeInstruction(StoreOp, RegNum::RN_SP,
169
RegNum::RN_RA, 3 * XLenBytes);
170
Address[Idx++] = encodeSTypeInstruction(StoreOp, RegNum::RN_SP,
171
RegNum::RN_A0, 2 * XLenBytes);
172
173
#if __riscv_xlen == 64
174
Address[Idx++] = encodeSTypeInstruction(StoreOp, RegNum::RN_SP,
175
RegNum::RN_T1, XLenBytes);
176
Address[Idx++] = encodeUTypeInstruction(PatchOpcodes::PO_LUI, RegNum::RN_T1,
177
HighestTracingHookAddr);
178
Address[Idx++] =
179
encodeITypeInstruction(PatchOpcodes::PO_ADDI, RegNum::RN_T1,
180
RegNum::RN_T1, HigherTracingHookAddr);
181
Address[Idx++] = encodeITypeInstruction(PatchOpcodes::PO_SLLI,
182
RegNum::RN_T1, RegNum::RN_T1, 32);
183
#endif
184
Address[Idx++] = encodeUTypeInstruction(PatchOpcodes::PO_LUI, RegNum::RN_RA,
185
HiTracingHookAddr);
186
Address[Idx++] = encodeITypeInstruction(
187
PatchOpcodes::PO_ADDI, RegNum::RN_RA, RegNum::RN_RA, LoTracingHookAddr);
188
#if __riscv_xlen == 64
189
Address[Idx++] = encodeRTypeInstruction(PatchOpcodes::PO_ADD, RegNum::RN_RA,
190
RegNum::RN_T1, RegNum::RN_RA);
191
#endif
192
Address[Idx++] = encodeUTypeInstruction(PatchOpcodes::PO_LUI, RegNum::RN_A0,
193
HiFunctionID);
194
Address[Idx++] = encodeITypeInstruction(
195
PatchOpcodes::PO_ADDI, RegNum::RN_A0, RegNum::RN_A0, LoFunctionID);
196
Address[Idx++] = encodeITypeInstruction(PatchOpcodes::PO_JALR,
197
RegNum::RN_RA, RegNum::RN_RA, 0);
198
199
#if __riscv_xlen == 64
200
Address[Idx++] =
201
encodeITypeInstruction(LoadOp, RegNum::RN_SP, RegNum::RN_T1, XLenBytes);
202
#endif
203
Address[Idx++] = encodeITypeInstruction(LoadOp, RegNum::RN_SP,
204
RegNum::RN_A0, 2 * XLenBytes);
205
Address[Idx++] = encodeITypeInstruction(LoadOp, RegNum::RN_SP,
206
RegNum::RN_RA, 3 * XLenBytes);
207
Address[Idx++] = encodeITypeInstruction(
208
PatchOpcodes::PO_ADDI, RegNum::RN_SP, RegNum::RN_SP, 4 * XLenBytes);
209
210
uint32_t CreateStackSpace = encodeITypeInstruction(
211
PatchOpcodes::PO_ADDI, RegNum::RN_SP, RegNum::RN_SP, -4 * XLenBytes);
212
213
std::atomic_store_explicit(
214
reinterpret_cast<std::atomic<uint32_t> *>(Address), CreateStackSpace,
215
std::memory_order_release);
216
} else {
217
uint32_t CreateBranch = encodeJTypeInstruction(
218
// Jump distance is different in both ISAs due to difference in size of
219
// sleds
220
#if __riscv_xlen == 64
221
PatchOpcodes::PO_J, RegNum::RN_X0,
222
68); // jump encodes an offset of 68
223
#elif __riscv_xlen == 32
224
PatchOpcodes::PO_J, RegNum::RN_X0,
225
44); // jump encodes an offset of 44
226
#endif
227
std::atomic_store_explicit(
228
reinterpret_cast<std::atomic<uint32_t> *>(Address), CreateBranch,
229
std::memory_order_release);
230
}
231
return true;
232
}
233
234
bool patchFunctionEntry(const bool Enable, const uint32_t FuncId,
235
const XRaySledEntry &Sled,
236
const XRayTrampolines &Trampolines,
237
bool LogArgs) XRAY_NEVER_INSTRUMENT {
238
// We don't support logging argument at this moment, so we always
239
// use EntryTrampoline.
240
return patchSled(Enable, FuncId, Sled, Trampolines.EntryTrampoline);
241
}
242
243
bool patchFunctionExit(
244
const bool Enable, const uint32_t FuncId, const XRaySledEntry &Sled,
245
const XRayTrampolines &Trampolines) XRAY_NEVER_INSTRUMENT {
246
return patchSled(Enable, FuncId, Sled, Trampolines.ExitTrampoline);
247
}
248
249
bool patchFunctionTailExit(
250
const bool Enable, const uint32_t FuncId, const XRaySledEntry &Sled,
251
const XRayTrampolines &Trampolines) XRAY_NEVER_INSTRUMENT {
252
return patchSled(Enable, FuncId, Sled, Trampolines.TailExitTrampoline);
253
}
254
255
bool patchCustomEvent(const bool Enable, const uint32_t FuncId,
256
const XRaySledEntry &Sled) XRAY_NEVER_INSTRUMENT {
257
return false;
258
}
259
260
bool patchTypedEvent(const bool Enable, const uint32_t FuncId,
261
const XRaySledEntry &Sled) XRAY_NEVER_INSTRUMENT {
262
return false;
263
}
264
} // namespace __xray
265
266
extern "C" void __xray_ArgLoggerEntry() XRAY_NEVER_INSTRUMENT {}
267
268