aboutsummaryrefslogtreecommitdiff
path: root/lib/xray/xray_arm.cc
blob: da4efcdd2b170a7d6574386cb05f322679bf1501 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
//===-- xray_arm.cc ---------------------------------------------*- C++ -*-===//
//
//                     The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// This file is a part of XRay, a dynamic runtime instrumentation system.
//
// Implementation of ARM-specific routines (32-bit).
//
//===----------------------------------------------------------------------===//
#include "sanitizer_common/sanitizer_common.h"
#include "xray_defs.h"
#include "xray_interface_internal.h"
#include <atomic>
#include <cassert>

extern "C" void __clear_cache(void *start, void *end);

namespace __xray {

// The machine codes for some instructions used in runtime patching.
enum class PatchOpcodes : uint32_t {
  PO_PushR0Lr = 0xE92D4001, // PUSH {r0, lr}
  PO_BlxIp = 0xE12FFF3C,    // BLX ip
  PO_PopR0Lr = 0xE8BD4001,  // POP {r0, lr}
  PO_B20 = 0xEA000005       // B #20
};

// 0xUUUUWXYZ -> 0x000W0XYZ
inline static uint32_t getMovwMask(const uint32_t Value) XRAY_NEVER_INSTRUMENT {
  return (Value & 0xfff) | ((Value & 0xf000) << 4);
}

// 0xWXYZUUUU -> 0x000W0XYZ
inline static uint32_t getMovtMask(const uint32_t Value) XRAY_NEVER_INSTRUMENT {
  return getMovwMask(Value >> 16);
}

// Writes the following instructions:
//   MOVW R<regNo>, #<lower 16 bits of the |Value|>
//   MOVT R<regNo>, #<higher 16 bits of the |Value|>
inline static uint32_t *
write32bitLoadReg(uint8_t regNo, uint32_t *Address,
                  const uint32_t Value) XRAY_NEVER_INSTRUMENT {
  // This is a fatal error: we cannot just report it and continue execution.
  assert(regNo <= 15 && "Register number must be 0 to 15.");
  // MOVW R, #0xWXYZ in machine code is 0xE30WRXYZ
  *Address = (0xE3000000 | (uint32_t(regNo) << 12) | getMovwMask(Value));
  Address++;
  // MOVT R, #0xWXYZ in machine code is 0xE34WRXYZ
  *Address = (0xE3400000 | (uint32_t(regNo) << 12) | getMovtMask(Value));
  return Address + 1;
}

// Writes the following instructions:
//   MOVW r0, #<lower 16 bits of the |Value|>
//   MOVT r0, #<higher 16 bits of the |Value|>
inline static uint32_t *
write32bitLoadR0(uint32_t *Address,
                 const uint32_t Value) XRAY_NEVER_INSTRUMENT {
  return write32bitLoadReg(0, Address, Value);
}

// Writes the following instructions:
//   MOVW ip, #<lower 16 bits of the |Value|>
//   MOVT ip, #<higher 16 bits of the |Value|>
inline static uint32_t *
write32bitLoadIP(uint32_t *Address,
                 const uint32_t Value) XRAY_NEVER_INSTRUMENT {
  return write32bitLoadReg(12, Address, Value);
}

inline static bool patchSled(const bool Enable, const uint32_t FuncId,
                             const XRaySledEntry &Sled,
                             void (*TracingHook)()) XRAY_NEVER_INSTRUMENT {
  // When |Enable| == true,
  // We replace the following compile-time stub (sled):
  //
  // xray_sled_n:
  //   B #20
  //   6 NOPs (24 bytes)
  //
  // With the following runtime patch:
  //
  // xray_sled_n:
  //   PUSH {r0, lr}
  //   MOVW r0, #<lower 16 bits of function ID>
  //   MOVT r0, #<higher 16 bits of function ID>
  //   MOVW ip, #<lower 16 bits of address of TracingHook>
  //   MOVT ip, #<higher 16 bits of address of TracingHook>
  //   BLX ip
  //   POP {r0, lr}
  //
  // Replacement of the first 4-byte instruction should be the last and atomic
  // operation, so that the user code which reaches the sled concurrently
  // either jumps over the whole sled, or executes the whole sled when the
  // latter is ready.
  //
  // When |Enable|==false, we set back the first instruction in the sled to be
  //   B #20

  uint32_t *FirstAddress = reinterpret_cast<uint32_t *>(Sled.Address);
  uint32_t *CurAddress = FirstAddress + 1;
  if (Enable) {
    CurAddress =
        write32bitLoadR0(CurAddress, reinterpret_cast<uint32_t>(FuncId));
    CurAddress =
        write32bitLoadIP(CurAddress, reinterpret_cast<uint32_t>(TracingHook));
    *CurAddress = uint32_t(PatchOpcodes::PO_BlxIp);
    CurAddress++;
    *CurAddress = uint32_t(PatchOpcodes::PO_PopR0Lr);
    CurAddress++;
    std::atomic_store_explicit(
        reinterpret_cast<std::atomic<uint32_t> *>(FirstAddress),
        uint32_t(PatchOpcodes::PO_PushR0Lr), std::memory_order_release);
  } else {
    std::atomic_store_explicit(
        reinterpret_cast<std::atomic<uint32_t> *>(FirstAddress),
        uint32_t(PatchOpcodes::PO_B20), std::memory_order_release);
  }
  __clear_cache(reinterpret_cast<char *>(FirstAddress),
                reinterpret_cast<char *>(CurAddress));
  return true;
}

bool patchFunctionEntry(const bool Enable, const uint32_t FuncId,
                        const XRaySledEntry &Sled,
                        void (*Trampoline)()) XRAY_NEVER_INSTRUMENT {
  return patchSled(Enable, FuncId, Sled, Trampoline);
}

bool patchFunctionExit(const bool Enable, const uint32_t FuncId,
                       const XRaySledEntry &Sled) XRAY_NEVER_INSTRUMENT {
  return patchSled(Enable, FuncId, Sled, __xray_FunctionExit);
}

bool patchFunctionTailExit(const bool Enable, const uint32_t FuncId,
                           const XRaySledEntry &Sled) XRAY_NEVER_INSTRUMENT {
  return patchSled(Enable, FuncId, Sled, __xray_FunctionTailExit);
}

bool patchCustomEvent(const bool Enable, const uint32_t FuncId,
                      const XRaySledEntry &Sled)
    XRAY_NEVER_INSTRUMENT { // FIXME: Implement in arm?
  return false;
}

// FIXME: Maybe implement this better?
bool probeRequiredCPUFeatures() XRAY_NEVER_INSTRUMENT { return true; }

} // namespace __xray

extern "C" void __xray_ArgLoggerEntry() XRAY_NEVER_INSTRUMENT {
  // FIXME: this will have to be implemented in the trampoline assembly file
}