1*68d75effSDimitry Andric //===-- xray_mips.cpp -------------------------------------------*- C++ -*-===// 2*68d75effSDimitry Andric // 3*68d75effSDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4*68d75effSDimitry Andric // See https://llvm.org/LICENSE.txt for license information. 5*68d75effSDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6*68d75effSDimitry Andric // 7*68d75effSDimitry Andric //===----------------------------------------------------------------------===// 8*68d75effSDimitry Andric // 9*68d75effSDimitry Andric // This file is a part of XRay, a dynamic runtime instrumentation system. 10*68d75effSDimitry Andric // 11*68d75effSDimitry Andric // Implementation of MIPS-specific routines (32-bit). 12*68d75effSDimitry Andric // 13*68d75effSDimitry Andric //===----------------------------------------------------------------------===// 14*68d75effSDimitry Andric #include "sanitizer_common/sanitizer_common.h" 15*68d75effSDimitry Andric #include "xray_defs.h" 16*68d75effSDimitry Andric #include "xray_interface_internal.h" 17*68d75effSDimitry Andric #include <atomic> 18*68d75effSDimitry Andric 19*68d75effSDimitry Andric namespace __xray { 20*68d75effSDimitry Andric 21*68d75effSDimitry Andric // The machine codes for some instructions used in runtime patching. 22*68d75effSDimitry Andric enum PatchOpcodes : uint32_t { 23*68d75effSDimitry Andric PO_ADDIU = 0x24000000, // addiu rt, rs, imm 24*68d75effSDimitry Andric PO_SW = 0xAC000000, // sw rt, offset(sp) 25*68d75effSDimitry Andric PO_LUI = 0x3C000000, // lui rs, %hi(address) 26*68d75effSDimitry Andric PO_ORI = 0x34000000, // ori rt, rs, %lo(address) 27*68d75effSDimitry Andric PO_JALR = 0x0000F809, // jalr rs 28*68d75effSDimitry Andric PO_LW = 0x8C000000, // lw rt, offset(address) 29*68d75effSDimitry Andric PO_B44 = 0x1000000b, // b #44 30*68d75effSDimitry Andric PO_NOP = 0x0, // nop 31*68d75effSDimitry Andric }; 32*68d75effSDimitry Andric 33*68d75effSDimitry Andric enum RegNum : uint32_t { 34*68d75effSDimitry Andric RN_T0 = 0x8, 35*68d75effSDimitry Andric RN_T9 = 0x19, 36*68d75effSDimitry Andric RN_RA = 0x1F, 37*68d75effSDimitry Andric RN_SP = 0x1D, 38*68d75effSDimitry Andric }; 39*68d75effSDimitry Andric 40*68d75effSDimitry Andric inline static uint32_t encodeInstruction(uint32_t Opcode, uint32_t Rs, 41*68d75effSDimitry Andric uint32_t Rt, 42*68d75effSDimitry Andric uint32_t Imm) XRAY_NEVER_INSTRUMENT { 43*68d75effSDimitry Andric return (Opcode | Rs << 21 | Rt << 16 | Imm); 44*68d75effSDimitry Andric } 45*68d75effSDimitry Andric 46*68d75effSDimitry Andric inline static uint32_t 47*68d75effSDimitry Andric encodeSpecialInstruction(uint32_t Opcode, uint32_t Rs, uint32_t Rt, uint32_t Rd, 48*68d75effSDimitry Andric uint32_t Imm) XRAY_NEVER_INSTRUMENT { 49*68d75effSDimitry Andric return (Rs << 21 | Rt << 16 | Rd << 11 | Imm << 6 | Opcode); 50*68d75effSDimitry Andric } 51*68d75effSDimitry Andric 52*68d75effSDimitry Andric inline static bool patchSled(const bool Enable, const uint32_t FuncId, 53*68d75effSDimitry Andric const XRaySledEntry &Sled, 54*68d75effSDimitry Andric void (*TracingHook)()) XRAY_NEVER_INSTRUMENT { 55*68d75effSDimitry Andric // When |Enable| == true, 56*68d75effSDimitry Andric // We replace the following compile-time stub (sled): 57*68d75effSDimitry Andric // 58*68d75effSDimitry Andric // xray_sled_n: 59*68d75effSDimitry Andric // B .tmpN 60*68d75effSDimitry Andric // 11 NOPs (44 bytes) 61*68d75effSDimitry Andric // .tmpN 62*68d75effSDimitry Andric // ADDIU T9, T9, 44 63*68d75effSDimitry Andric // 64*68d75effSDimitry Andric // With the following runtime patch: 65*68d75effSDimitry Andric // 66*68d75effSDimitry Andric // xray_sled_n (32-bit): 67*68d75effSDimitry Andric // addiu sp, sp, -8 ;create stack frame 68*68d75effSDimitry Andric // nop 69*68d75effSDimitry Andric // sw ra, 4(sp) ;save return address 70*68d75effSDimitry Andric // sw t9, 0(sp) ;save register t9 71*68d75effSDimitry Andric // lui t9, %hi(__xray_FunctionEntry/Exit) 72*68d75effSDimitry Andric // ori t9, t9, %lo(__xray_FunctionEntry/Exit) 73*68d75effSDimitry Andric // lui t0, %hi(function_id) 74*68d75effSDimitry Andric // jalr t9 ;call Tracing hook 75*68d75effSDimitry Andric // ori t0, t0, %lo(function_id) ;pass function id (delay slot) 76*68d75effSDimitry Andric // lw t9, 0(sp) ;restore register t9 77*68d75effSDimitry Andric // lw ra, 4(sp) ;restore return address 78*68d75effSDimitry Andric // addiu sp, sp, 8 ;delete stack frame 79*68d75effSDimitry Andric // 80*68d75effSDimitry Andric // We add 44 bytes to t9 because we want to adjust the function pointer to 81*68d75effSDimitry Andric // the actual start of function i.e. the address just after the noop sled. 82*68d75effSDimitry Andric // We do this because gp displacement relocation is emitted at the start of 83*68d75effSDimitry Andric // of the function i.e after the nop sled and to correctly calculate the 84*68d75effSDimitry Andric // global offset table address, t9 must hold the address of the instruction 85*68d75effSDimitry Andric // containing the gp displacement relocation. 86*68d75effSDimitry Andric // FIXME: Is this correct for the static relocation model? 87*68d75effSDimitry Andric // 88*68d75effSDimitry Andric // Replacement of the first 4-byte instruction should be the last and atomic 89*68d75effSDimitry Andric // operation, so that the user code which reaches the sled concurrently 90*68d75effSDimitry Andric // either jumps over the whole sled, or executes the whole sled when the 91*68d75effSDimitry Andric // latter is ready. 92*68d75effSDimitry Andric // 93*68d75effSDimitry Andric // When |Enable|==false, we set back the first instruction in the sled to be 94*68d75effSDimitry Andric // B #44 95*68d75effSDimitry Andric 96*68d75effSDimitry Andric if (Enable) { 97*68d75effSDimitry Andric uint32_t LoTracingHookAddr = 98*68d75effSDimitry Andric reinterpret_cast<int32_t>(TracingHook) & 0xffff; 99*68d75effSDimitry Andric uint32_t HiTracingHookAddr = 100*68d75effSDimitry Andric (reinterpret_cast<int32_t>(TracingHook) >> 16) & 0xffff; 101*68d75effSDimitry Andric uint32_t LoFunctionID = FuncId & 0xffff; 102*68d75effSDimitry Andric uint32_t HiFunctionID = (FuncId >> 16) & 0xffff; 103*68d75effSDimitry Andric *reinterpret_cast<uint32_t *>(Sled.Address + 8) = encodeInstruction( 104*68d75effSDimitry Andric PatchOpcodes::PO_SW, RegNum::RN_SP, RegNum::RN_RA, 0x4); 105*68d75effSDimitry Andric *reinterpret_cast<uint32_t *>(Sled.Address + 12) = encodeInstruction( 106*68d75effSDimitry Andric PatchOpcodes::PO_SW, RegNum::RN_SP, RegNum::RN_T9, 0x0); 107*68d75effSDimitry Andric *reinterpret_cast<uint32_t *>(Sled.Address + 16) = encodeInstruction( 108*68d75effSDimitry Andric PatchOpcodes::PO_LUI, 0x0, RegNum::RN_T9, HiTracingHookAddr); 109*68d75effSDimitry Andric *reinterpret_cast<uint32_t *>(Sled.Address + 20) = encodeInstruction( 110*68d75effSDimitry Andric PatchOpcodes::PO_ORI, RegNum::RN_T9, RegNum::RN_T9, LoTracingHookAddr); 111*68d75effSDimitry Andric *reinterpret_cast<uint32_t *>(Sled.Address + 24) = encodeInstruction( 112*68d75effSDimitry Andric PatchOpcodes::PO_LUI, 0x0, RegNum::RN_T0, HiFunctionID); 113*68d75effSDimitry Andric *reinterpret_cast<uint32_t *>(Sled.Address + 28) = encodeSpecialInstruction( 114*68d75effSDimitry Andric PatchOpcodes::PO_JALR, RegNum::RN_T9, 0x0, RegNum::RN_RA, 0X0); 115*68d75effSDimitry Andric *reinterpret_cast<uint32_t *>(Sled.Address + 32) = encodeInstruction( 116*68d75effSDimitry Andric PatchOpcodes::PO_ORI, RegNum::RN_T0, RegNum::RN_T0, LoFunctionID); 117*68d75effSDimitry Andric *reinterpret_cast<uint32_t *>(Sled.Address + 36) = encodeInstruction( 118*68d75effSDimitry Andric PatchOpcodes::PO_LW, RegNum::RN_SP, RegNum::RN_T9, 0x0); 119*68d75effSDimitry Andric *reinterpret_cast<uint32_t *>(Sled.Address + 40) = encodeInstruction( 120*68d75effSDimitry Andric PatchOpcodes::PO_LW, RegNum::RN_SP, RegNum::RN_RA, 0x4); 121*68d75effSDimitry Andric *reinterpret_cast<uint32_t *>(Sled.Address + 44) = encodeInstruction( 122*68d75effSDimitry Andric PatchOpcodes::PO_ADDIU, RegNum::RN_SP, RegNum::RN_SP, 0x8); 123*68d75effSDimitry Andric uint32_t CreateStackSpaceInstr = encodeInstruction( 124*68d75effSDimitry Andric PatchOpcodes::PO_ADDIU, RegNum::RN_SP, RegNum::RN_SP, 0xFFF8); 125*68d75effSDimitry Andric std::atomic_store_explicit( 126*68d75effSDimitry Andric reinterpret_cast<std::atomic<uint32_t> *>(Sled.Address), 127*68d75effSDimitry Andric uint32_t(CreateStackSpaceInstr), std::memory_order_release); 128*68d75effSDimitry Andric } else { 129*68d75effSDimitry Andric std::atomic_store_explicit( 130*68d75effSDimitry Andric reinterpret_cast<std::atomic<uint32_t> *>(Sled.Address), 131*68d75effSDimitry Andric uint32_t(PatchOpcodes::PO_B44), std::memory_order_release); 132*68d75effSDimitry Andric } 133*68d75effSDimitry Andric return true; 134*68d75effSDimitry Andric } 135*68d75effSDimitry Andric 136*68d75effSDimitry Andric bool patchFunctionEntry(const bool Enable, const uint32_t FuncId, 137*68d75effSDimitry Andric const XRaySledEntry &Sled, 138*68d75effSDimitry Andric void (*Trampoline)()) XRAY_NEVER_INSTRUMENT { 139*68d75effSDimitry Andric return patchSled(Enable, FuncId, Sled, Trampoline); 140*68d75effSDimitry Andric } 141*68d75effSDimitry Andric 142*68d75effSDimitry Andric bool patchFunctionExit(const bool Enable, const uint32_t FuncId, 143*68d75effSDimitry Andric const XRaySledEntry &Sled) XRAY_NEVER_INSTRUMENT { 144*68d75effSDimitry Andric return patchSled(Enable, FuncId, Sled, __xray_FunctionExit); 145*68d75effSDimitry Andric } 146*68d75effSDimitry Andric 147*68d75effSDimitry Andric bool patchFunctionTailExit(const bool Enable, const uint32_t FuncId, 148*68d75effSDimitry Andric const XRaySledEntry &Sled) XRAY_NEVER_INSTRUMENT { 149*68d75effSDimitry Andric // FIXME: In the future we'd need to distinguish between non-tail exits and 150*68d75effSDimitry Andric // tail exits for better information preservation. 151*68d75effSDimitry Andric return patchSled(Enable, FuncId, Sled, __xray_FunctionExit); 152*68d75effSDimitry Andric } 153*68d75effSDimitry Andric 154*68d75effSDimitry Andric bool patchCustomEvent(const bool Enable, const uint32_t FuncId, 155*68d75effSDimitry Andric const XRaySledEntry &Sled) XRAY_NEVER_INSTRUMENT { 156*68d75effSDimitry Andric // FIXME: Implement in mips? 157*68d75effSDimitry Andric return false; 158*68d75effSDimitry Andric } 159*68d75effSDimitry Andric 160*68d75effSDimitry Andric bool patchTypedEvent(const bool Enable, const uint32_t FuncId, 161*68d75effSDimitry Andric const XRaySledEntry &Sled) XRAY_NEVER_INSTRUMENT { 162*68d75effSDimitry Andric // FIXME: Implement in mips? 163*68d75effSDimitry Andric return false; 164*68d75effSDimitry Andric } 165*68d75effSDimitry Andric 166*68d75effSDimitry Andric } // namespace __xray 167*68d75effSDimitry Andric 168*68d75effSDimitry Andric extern "C" void __xray_ArgLoggerEntry() XRAY_NEVER_INSTRUMENT { 169*68d75effSDimitry Andric // FIXME: this will have to be implemented in the trampoline assembly file 170*68d75effSDimitry Andric } 171