xref: /freebsd/contrib/llvm-project/compiler-rt/lib/xray/xray_riscv.cpp (revision 700637cbb5e582861067a11aaca4d053546871d2)
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