1 //===-------- xray_loongarch64.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 loongarch-specific routines. 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 enum RegNum : uint32_t { 22 RN_RA = 1, 23 RN_SP = 3, 24 RN_T0 = 12, 25 RN_T1 = 13, 26 }; 27 28 // Encode instructions in the 2RIx format, where the primary formats here 29 // are 2RI12-type and 2RI16-type. 30 static inline uint32_t 31 encodeInstruction2RIx(uint32_t Opcode, uint32_t Rd, uint32_t Rj, 32 uint32_t Imm) XRAY_NEVER_INSTRUMENT { 33 return Opcode | (Imm << 10) | (Rj << 5) | Rd; 34 } 35 36 // Encode instructions in 1RI20 format, e.g. lu12i.w/lu32i.d. 37 static inline uint32_t 38 encodeInstruction1RI20(uint32_t Opcode, uint32_t Rd, 39 uint32_t Imm) XRAY_NEVER_INSTRUMENT { 40 return Opcode | (Imm << 5) | Rd; 41 } 42 43 static inline bool patchSled(const bool Enable, const uint32_t FuncId, 44 const XRaySledEntry &Sled, 45 void (*TracingHook)()) XRAY_NEVER_INSTRUMENT { 46 // When |Enable| == true, 47 // We replace the following compile-time stub (sled): 48 // 49 // .Lxray_sled_beginN: 50 // B .Lxray_sled_endN 51 // 11 NOPs (44 bytes) 52 // .Lxray_sled_endN: 53 // 54 // With the following runtime patch: 55 // 56 // xray_sled_n: 57 // addi.d sp, sp, -16 ; create the stack frame 58 // st.d ra, sp, 8 ; save the return address 59 // lu12i.w t0, %abs_hi20(__xray_FunctionEntry/Exit) 60 // ori t0, t0, %abs_lo12(__xray_FunctionEntry/Exit) 61 // lu32i.d t0, %abs64_lo20(__xray_FunctionEntry/Exit) 62 // lu52i.d t0, t0, %abs64_hi12(__xray_FunctionEntry/Exit) 63 // lu12i.w t1, %abs_hi20(function_id) 64 // ori t1, t1, %abs_lo12(function_id) ; pass the function id 65 // jirl ra, t0, 0 ; call the tracing hook 66 // ld.d ra, sp, 8 ; restore the return address 67 // addi.d sp, sp, 16 ; de-allocate the stack frame 68 // 69 // Replacement of the first 4-byte instruction should be the last and atomic 70 // operation, so that the user code which reaches the sled concurrently 71 // either jumps over the whole sled, or executes the whole sled when the 72 // latter is ready. 73 // 74 // When |Enable|==false, we set the first instruction in the sled back to 75 // B #48 76 77 uint32_t *Address = reinterpret_cast<uint32_t *>(Sled.address()); 78 if (Enable) { 79 uint32_t LoTracingHookAddr = reinterpret_cast<int64_t>(TracingHook) & 0xfff; 80 uint32_t HiTracingHookAddr = 81 (reinterpret_cast<int64_t>(TracingHook) >> 12) & 0xfffff; 82 uint32_t HigherTracingHookAddr = 83 (reinterpret_cast<int64_t>(TracingHook) >> 32) & 0xfffff; 84 uint32_t HighestTracingHookAddr = 85 (reinterpret_cast<int64_t>(TracingHook) >> 52) & 0xfff; 86 uint32_t LoFunctionID = FuncId & 0xfff; 87 uint32_t HiFunctionID = (FuncId >> 12) & 0xfffff; 88 Address[1] = encodeInstruction2RIx(0x29c00000, RegNum::RN_RA, RegNum::RN_SP, 89 0x8); // st.d ra, sp, 8 90 Address[2] = encodeInstruction1RI20( 91 0x14000000, RegNum::RN_T0, 92 HiTracingHookAddr); // lu12i.w t0, HiTracingHookAddr 93 Address[3] = encodeInstruction2RIx( 94 0x03800000, RegNum::RN_T0, RegNum::RN_T0, 95 LoTracingHookAddr); // ori t0, t0, LoTracingHookAddr 96 Address[4] = encodeInstruction1RI20( 97 0x16000000, RegNum::RN_T0, 98 HigherTracingHookAddr); // lu32i.d t0, HigherTracingHookAddr 99 Address[5] = encodeInstruction2RIx( 100 0x03000000, RegNum::RN_T0, RegNum::RN_T0, 101 HighestTracingHookAddr); // lu52i.d t0, t0, HighestTracingHookAddr 102 Address[6] = 103 encodeInstruction1RI20(0x14000000, RegNum::RN_T1, 104 HiFunctionID); // lu12i.w t1, HiFunctionID 105 Address[7] = 106 encodeInstruction2RIx(0x03800000, RegNum::RN_T1, RegNum::RN_T1, 107 LoFunctionID); // ori t1, t1, LoFunctionID 108 Address[8] = encodeInstruction2RIx(0x4c000000, RegNum::RN_RA, RegNum::RN_T0, 109 0); // jirl ra, t0, 0 110 Address[9] = encodeInstruction2RIx(0x28c00000, RegNum::RN_RA, RegNum::RN_SP, 111 0x8); // ld.d ra, sp, 8 112 Address[10] = encodeInstruction2RIx( 113 0x02c00000, RegNum::RN_SP, RegNum::RN_SP, 0x10); // addi.d sp, sp, 16 114 uint32_t CreateStackSpace = encodeInstruction2RIx( 115 0x02c00000, RegNum::RN_SP, RegNum::RN_SP, 0xff0); // addi.d sp, sp, -16 116 std::atomic_store_explicit( 117 reinterpret_cast<std::atomic<uint32_t> *>(Address), CreateStackSpace, 118 std::memory_order_release); 119 } else { 120 std::atomic_store_explicit( 121 reinterpret_cast<std::atomic<uint32_t> *>(Address), 122 uint32_t(0x50003000), std::memory_order_release); // b #48 123 } 124 return true; 125 } 126 127 bool patchFunctionEntry(const bool Enable, const uint32_t FuncId, 128 const XRaySledEntry &Sled, 129 void (*Trampoline)()) XRAY_NEVER_INSTRUMENT { 130 return patchSled(Enable, FuncId, Sled, Trampoline); 131 } 132 133 bool patchFunctionExit(const bool Enable, const uint32_t FuncId, 134 const XRaySledEntry &Sled) XRAY_NEVER_INSTRUMENT { 135 return patchSled(Enable, FuncId, Sled, __xray_FunctionExit); 136 } 137 138 bool patchFunctionTailExit(const bool Enable, const uint32_t FuncId, 139 const XRaySledEntry &Sled) XRAY_NEVER_INSTRUMENT { 140 // TODO: In the future we'd need to distinguish between non-tail exits and 141 // tail exits for better information preservation. 142 return patchSled(Enable, FuncId, Sled, __xray_FunctionExit); 143 } 144 145 bool patchCustomEvent(const bool Enable, const uint32_t FuncId, 146 const XRaySledEntry &Sled) XRAY_NEVER_INSTRUMENT { 147 // FIXME: Implement in loongarch? 148 return false; 149 } 150 151 bool patchTypedEvent(const bool Enable, const uint32_t FuncId, 152 const XRaySledEntry &Sled) XRAY_NEVER_INSTRUMENT { 153 // FIXME: Implement in loongarch? 154 return false; 155 } 156 } // namespace __xray 157 158 extern "C" void __xray_ArgLoggerEntry() XRAY_NEVER_INSTRUMENT { 159 // TODO: This will have to be implemented in the trampoline assembly file. 160 } 161