Path: blob/main/contrib/llvm-project/compiler-rt/lib/xray/xray_riscv.cpp
213766 views
//===-- xray_riscv.cpp ----------------------------------------*- C++ -*-===//1//2// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.3// See https://llvm.org/LICENSE.txt for license information.4// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception5//6//===----------------------------------------------------------------------===//7//8// This file is a part of XRay, a dynamic runtime instrumentation system.9//10// Implementation of RISC-V specific routines (32- and 64-bit).11//12//===----------------------------------------------------------------------===//13#include "sanitizer_common/sanitizer_common.h"14#include "xray_defs.h"15#include "xray_interface_internal.h"16#include <atomic>1718namespace __xray {1920// The machine codes for some instructions used in runtime patching.21enum PatchOpcodes : uint32_t {22PO_ADDI = 0x00000013, // addi rd, rs1, imm23PO_ADD = 0x00000033, // add rd, rs1, rs224PO_SW = 0x00002023, // sw rs2, imm(rs1)25PO_SD = 0x00003023, // sd rs2, imm(rs1)26PO_LUI = 0x00000037, // lui rd, imm27PO_OR = 0x00006033, // or rd, rs1, rs228PO_SLLI = 0x00001013, // slli rd, rs1, shamt29PO_JALR = 0x00000067, // jalr rd, rs130PO_LW = 0x00002003, // lw rd, imm(rs1)31PO_LD = 0x00003003, // ld rd, imm(rs1)32PO_J = 0x0000006f, // jal imm33PO_NOP = PO_ADDI, // addi x0, x0, 034};3536enum RegNum : uint32_t {37RN_X0 = 0,38RN_RA = 1,39RN_SP = 2,40RN_T1 = 6,41RN_A0 = 10,42};4344static inline uint32_t encodeRTypeInstruction(uint32_t Opcode, uint32_t Rs1,45uint32_t Rs2, uint32_t Rd) {46return Rs2 << 20 | Rs1 << 15 | Rd << 7 | Opcode;47}4849static inline uint32_t encodeITypeInstruction(uint32_t Opcode, uint32_t Rs1,50uint32_t Rd, uint32_t Imm) {51return Imm << 20 | Rs1 << 15 | Rd << 7 | Opcode;52}5354static inline uint32_t encodeSTypeInstruction(uint32_t Opcode, uint32_t Rs1,55uint32_t Rs2, uint32_t Imm) {56uint32_t ImmMSB = (Imm & 0xfe0) << 20;57uint32_t ImmLSB = (Imm & 0x01f) << 7;58return ImmMSB | Rs2 << 20 | Rs1 << 15 | ImmLSB | Opcode;59}6061static inline uint32_t encodeUTypeInstruction(uint32_t Opcode, uint32_t Rd,62uint32_t Imm) {63return Imm << 12 | Rd << 7 | Opcode;64}6566static inline uint32_t encodeJTypeInstruction(uint32_t Opcode, uint32_t Rd,67uint32_t Imm) {68uint32_t ImmMSB = (Imm & 0x100000) << 11;69uint32_t ImmLSB = (Imm & 0x7fe) << 20;70uint32_t Imm11 = (Imm & 0x800) << 9;71uint32_t Imm1912 = (Imm & 0xff000);72return ImmMSB | ImmLSB | Imm11 | Imm1912 | Rd << 7 | Opcode;73}7475static uint32_t hi20(uint32_t val) { return (val + 0x800) >> 12; }76static uint32_t lo12(uint32_t val) { return val & 0xfff; }7778static inline bool patchSled(const bool Enable, const uint32_t FuncId,79const XRaySledEntry &Sled,80void (*TracingHook)()) XRAY_NEVER_INSTRUMENT {81// When |Enable| == true,82// We replace the following compile-time stub (sled):83//84// xray_sled_n:85// J .tmpN86// 21 or 33 C.NOPs (42 or 66 bytes)87// .tmpN88//89// With one of the following runtime patches:90//91// xray_sled_n (32-bit):92// addi sp, sp, -16 ;create stack frame93// sw ra, 12(sp) ;save return address94// sw a0, 8(sp) ;save register a095// lui ra, %hi(__xray_FunctionEntry/Exit)96// addi ra, ra, %lo(__xray_FunctionEntry/Exit)97// lui a0, %hi(function_id)98// addi a0, a0, %lo(function_id) ;pass function id99// jalr ra ;call Tracing hook100// lw a0, 8(sp) ;restore register a0101// lw ra, 12(sp) ;restore return address102// addi sp, sp, 16 ;delete stack frame103//104// xray_sled_n (64-bit):105// addi sp, sp, -32 ;create stack frame106// sd ra, 24(sp) ;save return address107// sd a0, 16(sp) ;save register a0108// sd t1, 8(sp) ;save register t1109// lui t1, %highest(__xray_FunctionEntry/Exit)110// addi t1, t1, %higher(__xray_FunctionEntry/Exit)111// slli t1, t1, 32112// lui ra, ra, %hi(__xray_FunctionEntry/Exit)113// addi ra, ra, %lo(__xray_FunctionEntry/Exit)114// add ra, t1, ra115// lui a0, %hi(function_id)116// addi a0, a0, %lo(function_id) ;pass function id117// jalr ra ;call Tracing hook118// ld t1, 8(sp) ;restore register t1119// ld a0, 16(sp) ;restore register a0120// ld ra, 24(sp) ;restore return address121// addi sp, sp, 32 ;delete stack frame122//123// Replacement of the first 4-byte instruction should be the last and atomic124// operation, so that the user code which reaches the sled concurrently125// either jumps over the whole sled, or executes the whole sled when the126// latter is ready.127//128// When |Enable|==false, we set back the first instruction in the sled to be129// J 44 bytes (rv32)130// J 68 bytes (rv64)131132uint32_t *Address = reinterpret_cast<uint32_t *>(Sled.address());133if (Enable) {134#if __riscv_xlen == 64135// If the ISA is RV64, the Tracing Hook needs to be typecast to a 64 bit136// value.137uint32_t LoTracingHookAddr = lo12(reinterpret_cast<uint64_t>(TracingHook));138uint32_t HiTracingHookAddr = hi20(reinterpret_cast<uint64_t>(TracingHook));139uint32_t HigherTracingHookAddr =140lo12((reinterpret_cast<uint64_t>(TracingHook) + 0x80000000) >> 32);141uint32_t HighestTracingHookAddr =142hi20((reinterpret_cast<uint64_t>(TracingHook) + 0x80000000) >> 32);143#elif __riscv_xlen == 32144// We typecast the Tracing Hook to a 32 bit value for RV32145uint32_t LoTracingHookAddr = lo12(reinterpret_cast<uint32_t>(TracingHook));146uint32_t HiTracingHookAddr = hi20((reinterpret_cast<uint32_t>(TracingHook));147#endif148uint32_t LoFunctionID = lo12(FuncId);149uint32_t HiFunctionID = hi20(FuncId);150151// The sled that is patched in for RISCV64 defined below. We need the entire152// sleds corresponding to both ISAs to be protected by defines because the153// first few instructions are all different, because we store doubles in154// case of RV64 and store words for RV32. Subsequently, we have LUI - and in155// case of RV64, we need extra instructions from this point on, so we see156// differences in addresses to which instructions are stored.157size_t Idx = 1U;158const uint32_t XLenBytes = __riscv_xlen / 8;159#if __riscv_xlen == 64160const uint32_t LoadOp = PatchOpcodes::PO_LD;161const uint32_t StoreOp = PatchOpcodes::PO_SD;162#elif __riscv_xlen == 32163const uint32_t LoadOp = PatchOpcodes::PO_LW;164const uint32_t StoreOp = PatchOpcodes::PO_SW;165#endif166167Address[Idx++] = encodeSTypeInstruction(StoreOp, RegNum::RN_SP,168RegNum::RN_RA, 3 * XLenBytes);169Address[Idx++] = encodeSTypeInstruction(StoreOp, RegNum::RN_SP,170RegNum::RN_A0, 2 * XLenBytes);171172#if __riscv_xlen == 64173Address[Idx++] = encodeSTypeInstruction(StoreOp, RegNum::RN_SP,174RegNum::RN_T1, XLenBytes);175Address[Idx++] = encodeUTypeInstruction(PatchOpcodes::PO_LUI, RegNum::RN_T1,176HighestTracingHookAddr);177Address[Idx++] =178encodeITypeInstruction(PatchOpcodes::PO_ADDI, RegNum::RN_T1,179RegNum::RN_T1, HigherTracingHookAddr);180Address[Idx++] = encodeITypeInstruction(PatchOpcodes::PO_SLLI,181RegNum::RN_T1, RegNum::RN_T1, 32);182#endif183Address[Idx++] = encodeUTypeInstruction(PatchOpcodes::PO_LUI, RegNum::RN_RA,184HiTracingHookAddr);185Address[Idx++] = encodeITypeInstruction(186PatchOpcodes::PO_ADDI, RegNum::RN_RA, RegNum::RN_RA, LoTracingHookAddr);187#if __riscv_xlen == 64188Address[Idx++] = encodeRTypeInstruction(PatchOpcodes::PO_ADD, RegNum::RN_RA,189RegNum::RN_T1, RegNum::RN_RA);190#endif191Address[Idx++] = encodeUTypeInstruction(PatchOpcodes::PO_LUI, RegNum::RN_A0,192HiFunctionID);193Address[Idx++] = encodeITypeInstruction(194PatchOpcodes::PO_ADDI, RegNum::RN_A0, RegNum::RN_A0, LoFunctionID);195Address[Idx++] = encodeITypeInstruction(PatchOpcodes::PO_JALR,196RegNum::RN_RA, RegNum::RN_RA, 0);197198#if __riscv_xlen == 64199Address[Idx++] =200encodeITypeInstruction(LoadOp, RegNum::RN_SP, RegNum::RN_T1, XLenBytes);201#endif202Address[Idx++] = encodeITypeInstruction(LoadOp, RegNum::RN_SP,203RegNum::RN_A0, 2 * XLenBytes);204Address[Idx++] = encodeITypeInstruction(LoadOp, RegNum::RN_SP,205RegNum::RN_RA, 3 * XLenBytes);206Address[Idx++] = encodeITypeInstruction(207PatchOpcodes::PO_ADDI, RegNum::RN_SP, RegNum::RN_SP, 4 * XLenBytes);208209uint32_t CreateStackSpace = encodeITypeInstruction(210PatchOpcodes::PO_ADDI, RegNum::RN_SP, RegNum::RN_SP, -4 * XLenBytes);211212std::atomic_store_explicit(213reinterpret_cast<std::atomic<uint32_t> *>(Address), CreateStackSpace,214std::memory_order_release);215} else {216uint32_t CreateBranch = encodeJTypeInstruction(217// Jump distance is different in both ISAs due to difference in size of218// sleds219#if __riscv_xlen == 64220PatchOpcodes::PO_J, RegNum::RN_X0,22168); // jump encodes an offset of 68222#elif __riscv_xlen == 32223PatchOpcodes::PO_J, RegNum::RN_X0,22444); // jump encodes an offset of 44225#endif226std::atomic_store_explicit(227reinterpret_cast<std::atomic<uint32_t> *>(Address), CreateBranch,228std::memory_order_release);229}230return true;231}232233bool patchFunctionEntry(const bool Enable, const uint32_t FuncId,234const XRaySledEntry &Sled,235const XRayTrampolines &Trampolines,236bool LogArgs) XRAY_NEVER_INSTRUMENT {237// We don't support logging argument at this moment, so we always238// use EntryTrampoline.239return patchSled(Enable, FuncId, Sled, Trampolines.EntryTrampoline);240}241242bool patchFunctionExit(243const bool Enable, const uint32_t FuncId, const XRaySledEntry &Sled,244const XRayTrampolines &Trampolines) XRAY_NEVER_INSTRUMENT {245return patchSled(Enable, FuncId, Sled, Trampolines.ExitTrampoline);246}247248bool patchFunctionTailExit(249const bool Enable, const uint32_t FuncId, const XRaySledEntry &Sled,250const XRayTrampolines &Trampolines) XRAY_NEVER_INSTRUMENT {251return patchSled(Enable, FuncId, Sled, Trampolines.TailExitTrampoline);252}253254bool patchCustomEvent(const bool Enable, const uint32_t FuncId,255const XRaySledEntry &Sled) XRAY_NEVER_INSTRUMENT {256return false;257}258259bool patchTypedEvent(const bool Enable, const uint32_t FuncId,260const XRaySledEntry &Sled) XRAY_NEVER_INSTRUMENT {261return false;262}263} // namespace __xray264265extern "C" void __xray_ArgLoggerEntry() XRAY_NEVER_INSTRUMENT {}266267268