1 //===-- xray_arm.cpp --------------------------------------------*- C++ -*-===//
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
7 //===----------------------------------------------------------------------===//
9 // This file is a part of XRay, a dynamic runtime instrumentation system.
11 // Implementation of ARM-specific routines (32-bit).
13 //===----------------------------------------------------------------------===//
14 #include "sanitizer_common/sanitizer_common.h"
15 #include "xray_defs.h"
16 #include "xray_interface_internal.h"
20 extern "C" void __clear_cache(void *start
, void *end
);
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
32 // 0xUUUUWXYZ -> 0x000W0XYZ
33 inline static uint32_t getMovwMask(const uint32_t Value
) XRAY_NEVER_INSTRUMENT
{
34 return (Value
& 0xfff) | ((Value
& 0xf000) << 4);
37 // 0xWXYZUUUU -> 0x000W0XYZ
38 inline static uint32_t getMovtMask(const uint32_t Value
) XRAY_NEVER_INSTRUMENT
{
39 return getMovwMask(Value
>> 16);
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
));
53 // MOVT R, #0xWXYZ in machine code is 0xE34WRXYZ
54 *Address
= (0xE3400000 | (uint32_t(regNo
) << 12) | getMovtMask(Value
));
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
);
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
);
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):
86 // With the following runtime patch:
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>
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
102 // When |Enable|==false, we set back the first instruction in the sled to be
105 uint32_t *FirstAddress
= reinterpret_cast<uint32_t *>(Sled
.address());
106 uint32_t *CurAddress
= FirstAddress
+ 1;
109 write32bitLoadR0(CurAddress
, reinterpret_cast<uint32_t>(FuncId
));
111 write32bitLoadIP(CurAddress
, reinterpret_cast<uint32_t>(TracingHook
));
112 *CurAddress
= uint32_t(PatchOpcodes::PO_BlxIp
);
114 *CurAddress
= uint32_t(PatchOpcodes::PO_PopR0Lr
);
116 std::atomic_store_explicit(
117 reinterpret_cast<std::atomic
<uint32_t> *>(FirstAddress
),
118 uint32_t(PatchOpcodes::PO_PushR0Lr
), std::memory_order_release
);
120 std::atomic_store_explicit(
121 reinterpret_cast<std::atomic
<uint32_t> *>(FirstAddress
),
122 uint32_t(PatchOpcodes::PO_B20
), std::memory_order_release
);
124 __clear_cache(reinterpret_cast<char *>(FirstAddress
),
125 reinterpret_cast<char *>(CurAddress
));
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
);
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
);
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
);
145 bool patchCustomEvent(const bool Enable
, const uint32_t FuncId
,
146 const XRaySledEntry
&Sled
)
147 XRAY_NEVER_INSTRUMENT
{ // FIXME: Implement in arm?
151 bool patchTypedEvent(const bool Enable
, const uint32_t FuncId
,
152 const XRaySledEntry
&Sled
) XRAY_NEVER_INSTRUMENT
{
153 // FIXME: Implement in arm?
157 // FIXME: Maybe implement this better?
158 bool probeRequiredCPUFeatures() XRAY_NEVER_INSTRUMENT
{ return true; }
160 } // namespace __xray
162 extern "C" void __xray_ArgLoggerEntry() XRAY_NEVER_INSTRUMENT
{
163 // FIXME: this will have to be implemented in the trampoline assembly file