Skip to content
Merged
Show file tree
Hide file tree
Changes from 7 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions clang/lib/Driver/XRayArgs.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,8 @@ XRayArgs::XRayArgs(const ToolChain &TC, const ArgList &Args) {
case llvm::Triple::mips64:
case llvm::Triple::mips64el:
case llvm::Triple::systemz:
case llvm::Triple::riscv32:
case llvm::Triple::riscv64:
break;
default:
D.Diag(diag::err_drv_unsupported_opt_for_target)
Expand Down
2 changes: 1 addition & 1 deletion compiler-rt/cmake/Modules/AllSupportedArchDefs.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,7 @@ if(APPLE)
set(ALL_XRAY_SUPPORTED_ARCH ${X86_64} ${ARM64})
else()
set(ALL_XRAY_SUPPORTED_ARCH ${X86_64} ${ARM32} ${ARM64} ${MIPS32} ${MIPS64}
powerpc64le ${HEXAGON} ${LOONGARCH64})
powerpc64le ${HEXAGON} ${LOONGARCH64} ${RISCV32} ${RISCV64})
endif()
set(ALL_XRAY_DSO_SUPPORTED_ARCH ${X86_64} ${ARM64})
set(ALL_SHADOWCALLSTACK_SUPPORTED_ARCH ${ARM64})
Expand Down
12 changes: 12 additions & 0 deletions compiler-rt/lib/xray/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,16 @@ set(hexagon_SOURCES
xray_trampoline_hexagon.S
)

set(riscv32_SOURCES
xray_riscv.cpp
xray_trampoline_riscv32.S
)

set(riscv64_SOURCES
xray_riscv.cpp
xray_trampoline_riscv64.S
)

set(XRAY_SOURCE_ARCHS
arm
armhf
Expand Down Expand Up @@ -156,6 +166,8 @@ set(XRAY_ALL_SOURCE_FILES
${mips64_SOURCES}
${mips64el_SOURCES}
${powerpc64le_SOURCES}
${riscv32_SOURCES}
${riscv64_SOURCES}
${XRAY_IMPL_HEADERS}
)
list(REMOVE_DUPLICATES XRAY_ALL_SOURCE_FILES)
Expand Down
4 changes: 4 additions & 0 deletions compiler-rt/lib/xray/xray_interface.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,10 @@ static const int16_t cSledLength = 64;
static const int16_t cSledLength = 8;
#elif defined(__hexagon__)
static const int16_t cSledLength = 20;
#elif defined(__riscv) && (__riscv_xlen == 64)
static const int16_t cSledLength = 68;
#elif defined(__riscv) && (__riscv_xlen == 32)
static const int16_t cSledLength = 52;
#else
#error "Unsupported CPU Architecture"
#endif /* CPU architecture */
Expand Down
266 changes: 266 additions & 0 deletions compiler-rt/lib/xray/xray_riscv.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,266 @@
//===-- xray_riscv.cpp ----------------------------------------*- C++ -*-===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
// This file is a part of XRay, a dynamic runtime instrumentation system.
//
// Implementation of RISC-V specific routines (32- and 64-bit).
//
//===----------------------------------------------------------------------===//
#include "sanitizer_common/sanitizer_common.h"
#include "xray_defs.h"
#include "xray_interface_internal.h"
#include <atomic>

namespace __xray {

// The machine codes for some instructions used in runtime patching.
enum PatchOpcodes : uint32_t {
PO_ADDI = 0x00000013, // addi rd, rs1, imm
PO_ADD = 0x00000033, // add rd, rs1, rs2
PO_SW = 0x00002023, // sw rs2, imm(rs1)
PO_SD = 0x00003023, // sd rs2, imm(rs1)
PO_LUI = 0x00000037, // lui rd, imm
PO_OR = 0x00006033, // or rd, rs1, rs2
PO_SLLI = 0x00001013, // slli rd, rs1, shamt
PO_JALR = 0x00000067, // jalr rd, rs1
PO_LW = 0x00002003, // lw rd, imm(rs1)
PO_LD = 0x00003003, // ld rd, imm(rs1)
PO_J = 0x0000006f, // jal imm
PO_NOP = PO_ADDI, // addi x0, x0, 0
};

enum RegNum : uint32_t {
RN_X0 = 0,
RN_RA = 1,
RN_SP = 2,
RN_T1 = 6,
RN_A0 = 10,
};

static inline uint32_t encodeRTypeInstruction(uint32_t Opcode, uint32_t Rs1,
uint32_t Rs2, uint32_t Rd) {
return Rs2 << 20 | Rs1 << 15 | Rd << 7 | Opcode;
}

static inline uint32_t encodeITypeInstruction(uint32_t Opcode, uint32_t Rs1,
uint32_t Rd, uint32_t Imm) {
return Imm << 20 | Rs1 << 15 | Rd << 7 | Opcode;
}

static inline uint32_t encodeSTypeInstruction(uint32_t Opcode, uint32_t Rs1,
uint32_t Rs2, uint32_t Imm) {
uint32_t ImmMSB = (Imm & 0xfe0) << 20;
uint32_t ImmLSB = (Imm & 0x01f) << 7;
return ImmMSB | Rs2 << 20 | Rs1 << 15 | ImmLSB | Opcode;
}

static inline uint32_t encodeUTypeInstruction(uint32_t Opcode, uint32_t Rd,
uint32_t Imm) {
return Imm << 12 | Rd << 7 | Opcode;
}

static inline uint32_t encodeJTypeInstruction(uint32_t Opcode, uint32_t Rd,
uint32_t Imm) {
uint32_t ImmMSB = (Imm & 0x100000) << 11;
uint32_t ImmLSB = (Imm & 0x7fe) << 20;
uint32_t Imm11 = (Imm & 0x800) << 9;
uint32_t Imm1912 = (Imm & 0xff000);
return ImmMSB | ImmLSB | Imm11 | Imm1912 | Rd << 7 | Opcode;
}

static uint32_t hi20(uint32_t val) { return (val + 0x800) >> 12; }
static uint32_t lo12(uint32_t val) { return val & 0xfff; }

static inline bool patchSled(const bool Enable, const uint32_t FuncId,
const XRaySledEntry &Sled,
void (*TracingHook)()) XRAY_NEVER_INSTRUMENT {
// When |Enable| == true,
// We replace the following compile-time stub (sled):
//
// xray_sled_n:
// J .tmpN
// 25 or 33 C.NOPs (50 or 66 bytes)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should 50 here be 42?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed.

// .tmpN
//
// With one of the following runtime patches:
//
// xray_sled_n (32-bit):
// addi sp, sp, -16 ;create stack frame
// sw ra, 12(sp) ;save return address
// sw a0, 8(sp) ;save register a0
// lui ra, %hi(__xray_FunctionEntry/Exit)
// addi ra, ra, %lo(__xray_FunctionEntry/Exit)
// lui a0, %hi(function_id)
// addi a0, a0, %lo(function_id) ;pass function id
// jalr ra ;call Tracing hook
// lw a0, 8(sp) ;restore register a0
// lw ra, 12(sp) ;restore return address
// addi sp, sp, 16 ;delete stack frame
//
// xray_sled_n (64-bit):
// addi sp, sp, -32 ;create stack frame
// sd ra, 24(sp) ;save return address
// sd a0, 16(sp) ;save register a0
// sd t1, 8(sp) ;save register t1
// lui t1, %highest(__xray_FunctionEntry/Exit)
// addi t1, t1, %higher(__xray_FunctionEntry/Exit)
// slli t1, t1, 32
// lui ra, ra, %hi(__xray_FunctionEntry/Exit)
// addi ra, ra, %lo(__xray_FunctionEntry/Exit)
// add ra, t1, ra
// lui a0, %hi(function_id)
// addi a0, a0, %lo(function_id) ;pass function id
// jalr ra ;call Tracing hook
// ld t1, 8(sp) ;restore register t1
// ld a0, 16(sp) ;restore register a0
// ld ra, 24(sp) ;restore return address
// addi sp, sp, 32 ;delete stack frame
//
// Replacement of the first 4-byte instruction should be the last and atomic
// operation, so that the user code which reaches the sled concurrently
// either jumps over the whole sled, or executes the whole sled when the
// latter is ready.
//
// When |Enable|==false, we set back the first instruction in the sled to be
// J 44 bytes (rv32)
// J 68 bytes (rv64)

uint32_t *Address = reinterpret_cast<uint32_t *>(Sled.address());
if (Enable) {
#if __riscv_xlen == 64
// If the ISA is RV64, the Tracing Hook needs to be typecast to a 64 bit
// value.
uint32_t LoTracingHookAddr = lo12(reinterpret_cast<uint64_t>(TracingHook));
uint32_t HiTracingHookAddr = hi20(reinterpret_cast<uint64_t>(TracingHook));
uint32_t HigherTracingHookAddr =
lo12((reinterpret_cast<uint64_t>(TracingHook) + 0x80000000) >> 32);
uint32_t HighestTracingHookAddr =
hi20((reinterpret_cast<uint64_t>(TracingHook) + 0x80000000) >> 32);
#elif __riscv_xlen == 32
// We typecast the Tracing Hook to a 32 bit value for RV32
uint32_t LoTracingHookAddr = lo12(reinterpret_cast<uint32_t>(TracingHook));
uint32_t HiTracingHookAddr = hi20((reinterpret_cast<uint32_t>(TracingHook));
#endif
uint32_t LoFunctionID = lo12(FuncId);
uint32_t HiFunctionID = hi20(FuncId);

// The sled that is patched in for RISCV64 defined below. We need the entire
// sleds corresponding to both ISAs to be protected by defines because the
// first few instructions are all different, because we store doubles in
// case of RV64 and store words for RV32. Subsequently, we have LUI - and in
// case of RV64, we need extra instructions from this point on, so we see
// differences in addresses to which instructions are stored.
size_t Idx = 1U;
const uint32_t XLenBytes = __riscv_xlen / 8;
#if __riscv_xlen == 64
const unsigned LoadOp = PatchOpcodes::PO_LD;
const unsigned StoreOp = PatchOpcodes::PO_SD;
#elif __riscv_xlen == 32
const unsigned LoadOp = PatchOpcodes::PO_LW;
const unsigned StoreOp = PatchOpcodes::PO_SW;
#endif

Address[Idx++] = encodeSTypeInstruction(StoreOp, RegNum::RN_SP,
RegNum::RN_RA, 3 * XLenBytes);
Address[Idx++] = encodeSTypeInstruction(StoreOp, RegNum::RN_SP,
RegNum::RN_A0, 2 * XLenBytes);

#if __riscv_xlen == 64
Address[Idx++] = encodeSTypeInstruction(StoreOp, RegNum::RN_SP,
RegNum::RN_T1, XLenBytes);
Address[Idx++] = encodeUTypeInstruction(PatchOpcodes::PO_LUI, RegNum::RN_T1,
HighestTracingHookAddr);
Address[Idx++] =
encodeITypeInstruction(PatchOpcodes::PO_ADDI, RegNum::RN_T1,
RegNum::RN_T1, HigherTracingHookAddr);
Address[Idx++] = encodeITypeInstruction(PatchOpcodes::PO_SLLI,
RegNum::RN_T1, RegNum::RN_T1, 32);
#endif
Address[Idx++] = encodeUTypeInstruction(PatchOpcodes::PO_LUI, RegNum::RN_RA,
HiTracingHookAddr);
Address[Idx++] = encodeITypeInstruction(
PatchOpcodes::PO_ADDI, RegNum::RN_RA, RegNum::RN_RA, LoTracingHookAddr);
#if __riscv_xlen == 64
Address[Idx++] = encodeRTypeInstruction(PatchOpcodes::PO_ADD, RegNum::RN_RA,
RegNum::RN_T1, RegNum::RN_RA);
#endif
Address[Idx++] = encodeUTypeInstruction(PatchOpcodes::PO_LUI, RegNum::RN_A0,
HiFunctionID);
Address[Idx++] = encodeITypeInstruction(
PatchOpcodes::PO_ADDI, RegNum::RN_A0, RegNum::RN_A0, LoFunctionID);
Address[Idx++] = encodeITypeInstruction(PatchOpcodes::PO_JALR,
RegNum::RN_RA, RegNum::RN_RA, 0);

#if __riscv_xlen == 64
Address[Idx++] =
encodeITypeInstruction(LoadOp, RegNum::RN_SP, RegNum::RN_T1, XLenBytes);
#endif
Address[Idx++] = encodeITypeInstruction(LoadOp, RegNum::RN_SP,
RegNum::RN_A0, 2 * XLenBytes);
Address[Idx++] = encodeITypeInstruction(LoadOp, RegNum::RN_SP,
RegNum::RN_RA, 3 * XLenBytes);
Address[Idx++] = encodeITypeInstruction(
PatchOpcodes::PO_ADDI, RegNum::RN_SP, RegNum::RN_SP, 4 * XLenBytes);

uint32_t CreateStackSpace = encodeITypeInstruction(
PatchOpcodes::PO_ADDI, RegNum::RN_SP, RegNum::RN_SP, -4 * XLenBytes);

std::atomic_store_explicit(
reinterpret_cast<std::atomic<uint32_t> *>(Address), CreateStackSpace,
std::memory_order_release);
} else {
uint32_t CreateBranch = encodeJTypeInstruction(
// Jump distance is different in both ISAs due to difference in size of
// sleds
#if __riscv_xlen == 64
PatchOpcodes::PO_J, RegNum::RN_X0,
68); // jump encodes an offset of 68
#elif __riscv_xlen == 32
PatchOpcodes::PO_J, RegNum::RN_X0,
44); // jump encodes an offset of 44
#endif
std::atomic_store_explicit(
reinterpret_cast<std::atomic<uint32_t> *>(Address), CreateBranch,
std::memory_order_release);
}
return true;
}

bool patchFunctionEntry(const bool Enable, const uint32_t FuncId,
const XRaySledEntry &Sled,
const XRayTrampolines &Trampolines,
bool LogArgs) XRAY_NEVER_INSTRUMENT {
// We don't support logging argument at this moment, so we always
// use EntryTrampoline.
return patchSled(Enable, FuncId, Sled, Trampolines.EntryTrampoline);
}

bool patchFunctionExit(
const bool Enable, const uint32_t FuncId, const XRaySledEntry &Sled,
const XRayTrampolines &Trampolines) XRAY_NEVER_INSTRUMENT {
return patchSled(Enable, FuncId, Sled, Trampolines.ExitTrampoline);
}

bool patchFunctionTailExit(
const bool Enable, const uint32_t FuncId, const XRaySledEntry &Sled,
const XRayTrampolines &Trampolines) XRAY_NEVER_INSTRUMENT {
return patchSled(Enable, FuncId, Sled, Trampolines.TailExitTrampoline);
}

bool patchCustomEvent(const bool Enable, const uint32_t FuncId,
const XRaySledEntry &Sled) XRAY_NEVER_INSTRUMENT {
return false;
}

bool patchTypedEvent(const bool Enable, const uint32_t FuncId,
const XRaySledEntry &Sled) XRAY_NEVER_INSTRUMENT {
return false;
}
} // namespace __xray

extern "C" void __xray_ArgLoggerEntry() XRAY_NEVER_INSTRUMENT {}
Loading
Loading