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
19namespace __xray {
20
21// The machine codes for some instructions used in runtime patching.
22enum 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
37enum 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
45static 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
50static 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
55static 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
62static inline uint32_t encodeUTypeInstruction(uint32_t Opcode, uint32_t Rd,
63 uint32_t Imm) {
64 return Imm << 12 | Rd << 7 | Opcode;
65}
66
67static 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
76static uint32_t hi20(uint32_t val) { return (val + 0x800) >> 12; }
77static uint32_t lo12(uint32_t val) { return val & 0xfff; }
78
79static 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(val: FuncId);
150 uint32_t HiFunctionID = hi20(val: 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(Opcode: PatchOpcodes::PO_LUI, Rd: RegNum::RN_A0,
193 Imm: HiFunctionID);
194 Address[Idx++] = encodeITypeInstruction(
195 Opcode: PatchOpcodes::PO_ADDI, Rs1: RegNum::RN_A0, Rd: RegNum::RN_A0, Imm: LoFunctionID);
196 Address[Idx++] = encodeITypeInstruction(Opcode: PatchOpcodes::PO_JALR,
197 Rs1: RegNum::RN_RA, Rd: RegNum::RN_RA, Imm: 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 Opcode: PatchOpcodes::PO_ADDI, Rs1: RegNum::RN_SP, Rd: RegNum::RN_SP, Imm: 4 * XLenBytes);
209
210 uint32_t CreateStackSpace = encodeITypeInstruction(
211 Opcode: PatchOpcodes::PO_ADDI, Rs1: RegNum::RN_SP, Rd: RegNum::RN_SP, Imm: -4 * XLenBytes);
212
213 std::atomic_store_explicit(
214 a: reinterpret_cast<std::atomic<uint32_t> *>(Address), i: CreateStackSpace,
215 m: 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
234bool 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, TracingHook: Trampolines.EntryTrampoline);
241}
242
243bool 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, TracingHook: Trampolines.ExitTrampoline);
247}
248
249bool 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, TracingHook: Trampolines.TailExitTrampoline);
253}
254
255bool patchCustomEvent(const bool Enable, const uint32_t FuncId,
256 const XRaySledEntry &Sled) XRAY_NEVER_INSTRUMENT {
257 return false;
258}
259
260bool patchTypedEvent(const bool Enable, const uint32_t FuncId,
261 const XRaySledEntry &Sled) XRAY_NEVER_INSTRUMENT {
262 return false;
263}
264} // namespace __xray
265
266extern "C" void __xray_ArgLoggerEntry() XRAY_NEVER_INSTRUMENT {}
267

source code of compiler-rt/lib/xray/xray_riscv.cpp