xref: /freebsd-src/contrib/llvm-project/compiler-rt/lib/xray/xray_mips.cpp (revision 68d75eff68281c1b445e3010bb975eae07aac225)
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