1 //===-- xray_arm.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 ARM-specific routines (32-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 #include <cassert> 19 20 extern "C" void __clear_cache(void *start, void *end); 21 22 namespace __xray { 23 24 // The machine codes for some instructions used in runtime patching. 25 enum class PatchOpcodes : uint32_t { 26 PO_PushR0Lr = 0xE92D4001, // PUSH {r0, lr} 27 PO_BlxIp = 0xE12FFF3C, // BLX ip 28 PO_PopR0Lr = 0xE8BD4001, // POP {r0, lr} 29 PO_B20 = 0xEA000005 // B #20 30 }; 31 32 // 0xUUUUWXYZ -> 0x000W0XYZ 33 inline static uint32_t getMovwMask(const uint32_t Value) XRAY_NEVER_INSTRUMENT { 34 return (Value & 0xfff) | ((Value & 0xf000) << 4); 35 } 36 37 // 0xWXYZUUUU -> 0x000W0XYZ 38 inline static uint32_t getMovtMask(const uint32_t Value) XRAY_NEVER_INSTRUMENT { 39 return getMovwMask(Value >> 16); 40 } 41 42 // Writes the following instructions: 43 // MOVW R<regNo>, #<lower 16 bits of the |Value|> 44 // MOVT R<regNo>, #<higher 16 bits of the |Value|> 45 inline static uint32_t * 46 write32bitLoadReg(uint8_t regNo, uint32_t *Address, 47 const uint32_t Value) XRAY_NEVER_INSTRUMENT { 48 // This is a fatal error: we cannot just report it and continue execution. 49 assert(regNo <= 15 && "Register number must be 0 to 15."); 50 // MOVW R, #0xWXYZ in machine code is 0xE30WRXYZ 51 *Address = (0xE3000000 | (uint32_t(regNo) << 12) | getMovwMask(Value)); 52 Address++; 53 // MOVT R, #0xWXYZ in machine code is 0xE34WRXYZ 54 *Address = (0xE3400000 | (uint32_t(regNo) << 12) | getMovtMask(Value)); 55 return Address + 1; 56 } 57 58 // Writes the following instructions: 59 // MOVW r0, #<lower 16 bits of the |Value|> 60 // MOVT r0, #<higher 16 bits of the |Value|> 61 inline static uint32_t * 62 write32bitLoadR0(uint32_t *Address, 63 const uint32_t Value) XRAY_NEVER_INSTRUMENT { 64 return write32bitLoadReg(0, Address, Value); 65 } 66 67 // Writes the following instructions: 68 // MOVW ip, #<lower 16 bits of the |Value|> 69 // MOVT ip, #<higher 16 bits of the |Value|> 70 inline static uint32_t * 71 write32bitLoadIP(uint32_t *Address, 72 const uint32_t Value) XRAY_NEVER_INSTRUMENT { 73 return write32bitLoadReg(12, Address, Value); 74 } 75 76 inline static bool patchSled(const bool Enable, const uint32_t FuncId, 77 const XRaySledEntry &Sled, 78 void (*TracingHook)()) XRAY_NEVER_INSTRUMENT { 79 // When |Enable| == true, 80 // We replace the following compile-time stub (sled): 81 // 82 // xray_sled_n: 83 // B #20 84 // 6 NOPs (24 bytes) 85 // 86 // With the following runtime patch: 87 // 88 // xray_sled_n: 89 // PUSH {r0, lr} 90 // MOVW r0, #<lower 16 bits of function ID> 91 // MOVT r0, #<higher 16 bits of function ID> 92 // MOVW ip, #<lower 16 bits of address of TracingHook> 93 // MOVT ip, #<higher 16 bits of address of TracingHook> 94 // BLX ip 95 // POP {r0, lr} 96 // 97 // Replacement of the first 4-byte instruction should be the last and atomic 98 // operation, so that the user code which reaches the sled concurrently 99 // either jumps over the whole sled, or executes the whole sled when the 100 // latter is ready. 101 // 102 // When |Enable|==false, we set back the first instruction in the sled to be 103 // B #20 104 105 uint32_t *FirstAddress = reinterpret_cast<uint32_t *>(Sled.address()); 106 uint32_t *CurAddress = FirstAddress + 1; 107 if (Enable) { 108 CurAddress = 109 write32bitLoadR0(CurAddress, reinterpret_cast<uint32_t>(FuncId)); 110 CurAddress = 111 write32bitLoadIP(CurAddress, reinterpret_cast<uint32_t>(TracingHook)); 112 *CurAddress = uint32_t(PatchOpcodes::PO_BlxIp); 113 CurAddress++; 114 *CurAddress = uint32_t(PatchOpcodes::PO_PopR0Lr); 115 CurAddress++; 116 std::atomic_store_explicit( 117 reinterpret_cast<std::atomic<uint32_t> *>(FirstAddress), 118 uint32_t(PatchOpcodes::PO_PushR0Lr), std::memory_order_release); 119 } else { 120 std::atomic_store_explicit( 121 reinterpret_cast<std::atomic<uint32_t> *>(FirstAddress), 122 uint32_t(PatchOpcodes::PO_B20), std::memory_order_release); 123 } 124 __clear_cache(reinterpret_cast<char *>(FirstAddress), 125 reinterpret_cast<char *>(CurAddress)); 126 return true; 127 } 128 129 bool patchFunctionEntry(const bool Enable, const uint32_t FuncId, 130 const XRaySledEntry &Sled, 131 void (*Trampoline)()) XRAY_NEVER_INSTRUMENT { 132 return patchSled(Enable, FuncId, Sled, Trampoline); 133 } 134 135 bool patchFunctionExit(const bool Enable, const uint32_t FuncId, 136 const XRaySledEntry &Sled) XRAY_NEVER_INSTRUMENT { 137 return patchSled(Enable, FuncId, Sled, __xray_FunctionExit); 138 } 139 140 bool patchFunctionTailExit(const bool Enable, const uint32_t FuncId, 141 const XRaySledEntry &Sled) XRAY_NEVER_INSTRUMENT { 142 return patchSled(Enable, FuncId, Sled, __xray_FunctionTailExit); 143 } 144 145 bool patchCustomEvent(const bool Enable, const uint32_t FuncId, 146 const XRaySledEntry &Sled) 147 XRAY_NEVER_INSTRUMENT { // FIXME: Implement in arm? 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 arm? 154 return false; 155 } 156 157 // FIXME: Maybe implement this better? 158 bool probeRequiredCPUFeatures() XRAY_NEVER_INSTRUMENT { return true; } 159 160 } // namespace __xray 161 162 extern "C" void __xray_ArgLoggerEntry() XRAY_NEVER_INSTRUMENT { 163 // FIXME: this will have to be implemented in the trampoline assembly file 164 } 165