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