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