| 1 | //===-- xray_hexagon.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 hexagon-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 <assert.h> |
| 18 | #include <atomic> |
| 19 | |
| 20 | namespace __xray { |
| 21 | |
| 22 | // The machine codes for some instructions used in runtime patching. |
| 23 | enum PatchOpcodes : uint32_t { |
| 24 | PO_JUMPI_14 = 0x5800c00a, // jump #0x014 (PC + 0x014) |
| 25 | PO_CALLR_R6 = 0x50a6c000, // indirect call: callr r6 |
| 26 | PO_TFR_IMM = 0x78000000, // transfer immed |
| 27 | // ICLASS 0x7 - S2-type A-type |
| 28 | PO_IMMEXT = 0x00000000, // constant extender |
| 29 | }; |
| 30 | |
| 31 | enum PacketWordParseBits : uint32_t { |
| 32 | PP_DUPLEX = 0x00 << 14, |
| 33 | PP_NOT_END = 0x01 << 14, |
| 34 | PP_PACKET_END = 0x03 << 14, |
| 35 | }; |
| 36 | |
| 37 | enum RegNum : uint32_t { |
| 38 | RN_R6 = 0x6, |
| 39 | RN_R7 = 0x7, |
| 40 | }; |
| 41 | |
| 42 | inline static uint32_t |
| 43 | encodeExtendedTransferImmediate(uint32_t Imm, RegNum DestReg, |
| 44 | bool PacketEnd = false) XRAY_NEVER_INSTRUMENT { |
| 45 | static const uint32_t REG_MASK = 0x1f; |
| 46 | assert((DestReg & (~REG_MASK)) == 0); |
| 47 | // The constant-extended register transfer encodes the 6 least |
| 48 | // significant bits of the effective constant: |
| 49 | Imm = Imm & 0x03f; |
| 50 | const PacketWordParseBits ParseBits = PacketEnd ? PP_PACKET_END : PP_NOT_END; |
| 51 | |
| 52 | return PO_TFR_IMM | ParseBits | (Imm << 5) | (DestReg & REG_MASK); |
| 53 | } |
| 54 | |
| 55 | inline static uint32_t |
| 56 | encodeConstantExtender(uint32_t Imm) XRAY_NEVER_INSTRUMENT { |
| 57 | // Bits Name Description |
| 58 | // ----- ------- ------------------------------------------ |
| 59 | // 31:28 ICLASS Instruction class = 0000 |
| 60 | // 27:16 high High 12 bits of 26-bit constant extension |
| 61 | // 15:14 Parse Parse bits |
| 62 | // 13:0 low Low 14 bits of 26-bit constant extension |
| 63 | static const uint32_t IMM_MASK_LOW = 0x03fff; |
| 64 | static const uint32_t IMM_MASK_HIGH = 0x00fff << 14; |
| 65 | |
| 66 | // The extender encodes the 26 most significant bits of the effective |
| 67 | // constant: |
| 68 | Imm = Imm >> 6; |
| 69 | |
| 70 | const uint32_t high = (Imm & IMM_MASK_HIGH) << 16; |
| 71 | const uint32_t low = Imm & IMM_MASK_LOW; |
| 72 | |
| 73 | return PO_IMMEXT | high | PP_NOT_END | low; |
| 74 | } |
| 75 | |
| 76 | static void WriteInstFlushCache(void *Addr, uint32_t NewInstruction) { |
| 77 | asm volatile("icinva(%[inst_addr])\n\t" |
| 78 | "isync\n\t" |
| 79 | "memw(%[inst_addr]) = %[new_inst]\n\t" |
| 80 | "dccleaninva(%[inst_addr])\n\t" |
| 81 | "syncht\n\t" |
| 82 | : |
| 83 | : [ inst_addr ] "r" (Addr), [ new_inst ] "r" (NewInstruction) |
| 84 | : "memory" ); |
| 85 | } |
| 86 | |
| 87 | inline static bool patchSled(const bool Enable, const uint32_t FuncId, |
| 88 | const XRaySledEntry &Sled, |
| 89 | void (*TracingHook)()) XRAY_NEVER_INSTRUMENT { |
| 90 | // When |Enable| == true, |
| 91 | // We replace the following compile-time stub (sled): |
| 92 | // |
| 93 | // .L_xray_sled_N: |
| 94 | // <xray_sled_base>: |
| 95 | // { jump .Ltmp0 } |
| 96 | // { nop |
| 97 | // nop |
| 98 | // nop |
| 99 | // nop } |
| 100 | // .Ltmp0: |
| 101 | |
| 102 | // With the following runtime patch: |
| 103 | // |
| 104 | // xray_sled_n (32-bit): |
| 105 | // |
| 106 | // <xray_sled_n>: |
| 107 | // { immext(#...) // upper 26-bits of func id |
| 108 | // r7 = ##... // lower 6-bits of func id |
| 109 | // immext(#...) // upper 26-bits of trampoline |
| 110 | // r6 = ##... } // lower 6 bits of trampoline |
| 111 | // { callr r6 } |
| 112 | // |
| 113 | // When |Enable|==false, we set back the first instruction in the sled to be |
| 114 | // { jump .Ltmp0 } |
| 115 | |
| 116 | uint32_t *FirstAddress = reinterpret_cast<uint32_t *>(Sled.address()); |
| 117 | if (Enable) { |
| 118 | uint32_t *CurAddress = FirstAddress + 1; |
| 119 | *CurAddress = encodeExtendedTransferImmediate(Imm: FuncId, DestReg: RN_R7); |
| 120 | CurAddress++; |
| 121 | *CurAddress = encodeConstantExtender(reinterpret_cast<uint32_t>(TracingHook)); |
| 122 | CurAddress++; |
| 123 | *CurAddress = |
| 124 | encodeExtendedTransferImmediate(reinterpret_cast<uint32_t>(TracingHook), RN_R6, true); |
| 125 | CurAddress++; |
| 126 | |
| 127 | *CurAddress = uint32_t(PO_CALLR_R6); |
| 128 | |
| 129 | WriteInstFlushCache(Addr: FirstAddress, NewInstruction: uint32_t(encodeConstantExtender(Imm: FuncId))); |
| 130 | } else { |
| 131 | WriteInstFlushCache(Addr: FirstAddress, NewInstruction: uint32_t(PatchOpcodes::PO_JUMPI_14)); |
| 132 | } |
| 133 | return true; |
| 134 | } |
| 135 | |
| 136 | bool patchFunctionEntry(const bool Enable, const uint32_t FuncId, |
| 137 | const XRaySledEntry &Sled, |
| 138 | const XRayTrampolines &Trampolines, |
| 139 | bool LogArgs) XRAY_NEVER_INSTRUMENT { |
| 140 | auto Trampoline = |
| 141 | LogArgs ? Trampolines.LogArgsTrampoline : Trampolines.EntryTrampoline; |
| 142 | return patchSled(Enable, FuncId, Sled, TracingHook: Trampoline); |
| 143 | } |
| 144 | |
| 145 | bool patchFunctionExit( |
| 146 | const bool Enable, const uint32_t FuncId, const XRaySledEntry &Sled, |
| 147 | const XRayTrampolines &Trampolines) XRAY_NEVER_INSTRUMENT { |
| 148 | return patchSled(Enable, FuncId, Sled, TracingHook: Trampolines.ExitTrampoline); |
| 149 | } |
| 150 | |
| 151 | bool patchFunctionTailExit( |
| 152 | const bool Enable, const uint32_t FuncId, const XRaySledEntry &Sled, |
| 153 | const XRayTrampolines &Trampolines) XRAY_NEVER_INSTRUMENT { |
| 154 | return patchSled(Enable, FuncId, Sled, TracingHook: Trampolines.TailExitTrampoline); |
| 155 | } |
| 156 | |
| 157 | bool patchCustomEvent(const bool Enable, const uint32_t FuncId, |
| 158 | const XRaySledEntry &Sled) XRAY_NEVER_INSTRUMENT { |
| 159 | // FIXME: Implement in hexagon? |
| 160 | return false; |
| 161 | } |
| 162 | |
| 163 | bool patchTypedEvent(const bool Enable, const uint32_t FuncId, |
| 164 | const XRaySledEntry &Sled) XRAY_NEVER_INSTRUMENT { |
| 165 | // FIXME: Implement in hexagon? |
| 166 | return false; |
| 167 | } |
| 168 | |
| 169 | } // namespace __xray |
| 170 | |
| 171 | extern "C" void __xray_ArgLoggerEntry() XRAY_NEVER_INSTRUMENT { |
| 172 | // FIXME: this will have to be implemented in the trampoline assembly file |
| 173 | } |
| 174 | |