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