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