267 lines · cpp
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-exception6//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, imm24 PO_ADD = 0x00000033, // add rd, rs1, rs225 PO_SW = 0x00002023, // sw rs2, imm(rs1)26 PO_SD = 0x00003023, // sd rs2, imm(rs1)27 PO_LUI = 0x00000037, // lui rd, imm28 PO_OR = 0x00006033, // or rd, rs1, rs229 PO_SLLI = 0x00001013, // slli rd, rs1, shamt30 PO_JALR = 0x00000067, // jalr rd, rs131 PO_LW = 0x00002003, // lw rd, imm(rs1)32 PO_LD = 0x00003003, // ld rd, imm(rs1)33 PO_J = 0x0000006f, // jal imm34 PO_NOP = PO_ADDI, // addi x0, x0, 035};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 .tmpN87 // 21 or 33 C.NOPs (42 or 66 bytes)88 // .tmpN89 //90 // With one of the following runtime patches:91 //92 // xray_sled_n (32-bit):93 // addi sp, sp, -16 ;create stack frame94 // sw ra, 12(sp) ;save return address95 // sw a0, 8(sp) ;save register a096 // 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 id100 // jalr ra ;call Tracing hook101 // lw a0, 8(sp) ;restore register a0102 // lw ra, 12(sp) ;restore return address103 // addi sp, sp, 16 ;delete stack frame104 //105 // xray_sled_n (64-bit):106 // addi sp, sp, -32 ;create stack frame107 // sd ra, 24(sp) ;save return address108 // sd a0, 16(sp) ;save register a0109 // sd t1, 8(sp) ;save register t1110 // lui t1, %highest(__xray_FunctionEntry/Exit)111 // addi t1, t1, %higher(__xray_FunctionEntry/Exit)112 // slli t1, t1, 32113 // lui ra, ra, %hi(__xray_FunctionEntry/Exit)114 // addi ra, ra, %lo(__xray_FunctionEntry/Exit)115 // add ra, t1, ra116 // lui a0, %hi(function_id)117 // addi a0, a0, %lo(function_id) ;pass function id118 // jalr ra ;call Tracing hook119 // ld t1, 8(sp) ;restore register t1120 // ld a0, 16(sp) ;restore register a0121 // ld ra, 24(sp) ;restore return address122 // addi sp, sp, 32 ;delete stack frame123 //124 // Replacement of the first 4-byte instruction should be the last and atomic125 // operation, so that the user code which reaches the sled concurrently126 // either jumps over the whole sled, or executes the whole sled when the127 // latter is ready.128 //129 // When |Enable|==false, we set back the first instruction in the sled to be130 // 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 == 64136 // If the ISA is RV64, the Tracing Hook needs to be typecast to a 64 bit137 // 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 == 32145 // We typecast the Tracing Hook to a 32 bit value for RV32146 uint32_t LoTracingHookAddr = lo12(reinterpret_cast<uint32_t>(TracingHook));147 uint32_t HiTracingHookAddr = hi20((reinterpret_cast<uint32_t>(TracingHook));148#endif149 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 entire153 // sleds corresponding to both ISAs to be protected by defines because the154 // first few instructions are all different, because we store doubles in155 // case of RV64 and store words for RV32. Subsequently, we have LUI - and in156 // case of RV64, we need extra instructions from this point on, so we see157 // 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 == 64161 const uint32_t LoadOp = PatchOpcodes::PO_LD;162 const uint32_t StoreOp = PatchOpcodes::PO_SD;163#elif __riscv_xlen == 32164 const uint32_t LoadOp = PatchOpcodes::PO_LW;165 const uint32_t StoreOp = PatchOpcodes::PO_SW;166#endif167 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 == 64174 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#endif184 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 == 64189 Address[Idx++] = encodeRTypeInstruction(PatchOpcodes::PO_ADD, RegNum::RN_RA,190 RegNum::RN_T1, RegNum::RN_RA);191#endif192 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 == 64200 Address[Idx++] =201 encodeITypeInstruction(LoadOp, RegNum::RN_SP, RegNum::RN_T1, XLenBytes);202#endif203 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 of219 // sleds220#if __riscv_xlen == 64221 PatchOpcodes::PO_J, RegNum::RN_X0,222 68); // jump encodes an offset of 68223#elif __riscv_xlen == 32224 PatchOpcodes::PO_J, RegNum::RN_X0,225 44); // jump encodes an offset of 44226#endif227 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 always239 // use EntryTrampoline.240 return patchSled(Enable, FuncId, Sled, 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, 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, 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 __xray265 266extern "C" void __xray_ArgLoggerEntry() XRAY_NEVER_INSTRUMENT {}267