blob: fb49ff3a81b7ff2082c56347cec05c61571efe4a [file] [log] [blame]
Dean Michael Berris938c5032016-07-21 07:39:55 +00001//===-- xray_interface.cpp --------------------------------------*- 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 the API functions.
13//
14//===----------------------------------------------------------------------===//
15
16#include "xray_interface_internal.h"
Dean Michael Berris17a586e2016-07-29 07:11:58 +000017
Dean Michael Berris938c5032016-07-21 07:39:55 +000018#include <atomic>
19#include <cstdint>
20#include <cstdio>
21#include <errno.h>
22#include <limits>
23#include <sys/mman.h>
24
Dean Michael Berris17a586e2016-07-29 07:11:58 +000025#include "sanitizer_common/sanitizer_common.h"
26
Dean Michael Berris938c5032016-07-21 07:39:55 +000027namespace __xray {
28
Dean Michael Berrisd1617cd2016-09-20 14:35:57 +000029#if defined(__x86_64__)
Dean Michael Berris4ef1a692016-10-06 07:09:40 +000030// FIXME: The actual length is 11 bytes. Why was length 12 passed to mprotect()
31// ?
32static const int16_t cSledLength = 12;
Dean Michael Berrisd1617cd2016-09-20 14:35:57 +000033#elif defined(__arm__)
Dean Michael Berris4ef1a692016-10-06 07:09:40 +000034static const int16_t cSledLength = 28;
Dean Michael Berrisd1617cd2016-09-20 14:35:57 +000035#else
Dean Michael Berris4ef1a692016-10-06 07:09:40 +000036#error "Unsupported CPU Architecture"
Dean Michael Berrisd1617cd2016-09-20 14:35:57 +000037#endif /* CPU architecture */
38
Dean Michael Berris938c5032016-07-21 07:39:55 +000039// This is the function to call when we encounter the entry or exit sleds.
40std::atomic<void (*)(int32_t, XRayEntryType)> XRayPatchedFunction{nullptr};
41
Dean Michael Berris9a0c4462016-07-27 04:30:25 +000042// MProtectHelper is an RAII wrapper for calls to mprotect(...) that will undo
43// any successful mprotect(...) changes. This is used to make a page writeable
44// and executable, and upon destruction if it was successful in doing so returns
45// the page into a read-only and executable page.
46//
47// This is only used specifically for runtime-patching of the XRay
48// instrumentation points. This assumes that the executable pages are originally
49// read-and-execute only.
50class MProtectHelper {
51 void *PageAlignedAddr;
52 std::size_t MProtectLen;
53 bool MustCleanup;
54
55public:
56 explicit MProtectHelper(void *PageAlignedAddr, std::size_t MProtectLen)
57 : PageAlignedAddr(PageAlignedAddr), MProtectLen(MProtectLen),
58 MustCleanup(false) {}
59
60 int MakeWriteable() {
61 auto R = mprotect(PageAlignedAddr, MProtectLen,
62 PROT_READ | PROT_WRITE | PROT_EXEC);
63 if (R != -1)
64 MustCleanup = true;
65 return R;
66 }
67
68 ~MProtectHelper() {
69 if (MustCleanup) {
70 mprotect(PageAlignedAddr, MProtectLen, PROT_READ | PROT_EXEC);
71 }
72 }
73};
74
Dean Michael Berris938c5032016-07-21 07:39:55 +000075} // namespace __xray
76
Dean Michael Berris938c5032016-07-21 07:39:55 +000077extern std::atomic<bool> XRayInitialized;
78extern std::atomic<__xray::XRaySledMap> XRayInstrMap;
79
80int __xray_set_handler(void (*entry)(int32_t, XRayEntryType)) {
81 if (XRayInitialized.load(std::memory_order_acquire)) {
82 __xray::XRayPatchedFunction.store(entry, std::memory_order_release);
83 return 1;
84 }
85 return 0;
86}
87
Dean Michael Berris68e74842016-08-08 03:10:22 +000088int __xray_remove_handler() { return __xray_set_handler(nullptr); }
89
Dean Michael Berris938c5032016-07-21 07:39:55 +000090std::atomic<bool> XRayPatching{false};
91
Dean Michael Berris9a0c4462016-07-27 04:30:25 +000092using namespace __xray;
93
Dean Michael Berris17a586e2016-07-29 07:11:58 +000094// FIXME: Figure out whether we can move this class to sanitizer_common instead
95// as a generic "scope guard".
96template <class Function> class CleanupInvoker {
97 Function Fn;
98
99public:
100 explicit CleanupInvoker(Function Fn) : Fn(Fn) {}
101 CleanupInvoker(const CleanupInvoker &) = default;
102 CleanupInvoker(CleanupInvoker &&) = default;
103 CleanupInvoker &operator=(const CleanupInvoker &) = delete;
104 CleanupInvoker &operator=(CleanupInvoker &&) = delete;
105 ~CleanupInvoker() { Fn(); }
106};
107
108template <class Function> CleanupInvoker<Function> ScopeCleanup(Function Fn) {
109 return CleanupInvoker<Function>{Fn};
110}
111
Dean Michael Berris68e74842016-08-08 03:10:22 +0000112// ControlPatching implements the common internals of the patching/unpatching
113// implementation. |Enable| defines whether we're enabling or disabling the
114// runtime XRay instrumentation.
115XRayPatchingStatus ControlPatching(bool Enable) {
Dean Michael Berris938c5032016-07-21 07:39:55 +0000116 if (!XRayInitialized.load(std::memory_order_acquire))
117 return XRayPatchingStatus::NOT_INITIALIZED; // Not initialized.
118
119 static bool NotPatching = false;
120 if (!XRayPatching.compare_exchange_strong(NotPatching, true,
121 std::memory_order_acq_rel,
122 std::memory_order_acquire)) {
123 return XRayPatchingStatus::ONGOING; // Already patching.
124 }
125
Dean Michael Berris17a586e2016-07-29 07:11:58 +0000126 bool PatchingSuccess = false;
127 auto XRayPatchingStatusResetter = ScopeCleanup([&PatchingSuccess] {
128 if (!PatchingSuccess) {
129 XRayPatching.store(false, std::memory_order_release);
130 }
131 });
132
Dean Michael Berris938c5032016-07-21 07:39:55 +0000133 // Step 1: Compute the function id, as a unique identifier per function in the
134 // instrumentation map.
Dean Michael Berris9a0c4462016-07-27 04:30:25 +0000135 XRaySledMap InstrMap = XRayInstrMap.load(std::memory_order_acquire);
Dean Michael Berris938c5032016-07-21 07:39:55 +0000136 if (InstrMap.Entries == 0)
137 return XRayPatchingStatus::NOT_INITIALIZED;
138
Dean Michael Berrisd1617cd2016-09-20 14:35:57 +0000139 const uint64_t PageSize = GetPageSizeCached();
Dean Michael Berris4ef1a692016-10-06 07:09:40 +0000140 if ((PageSize == 0) || ((PageSize & (PageSize - 1)) != 0)) {
Dean Michael Berrisd1617cd2016-09-20 14:35:57 +0000141 Report("System page size is not a power of two: %lld", PageSize);
142 return XRayPatchingStatus::FAILED;
143 }
144
145 uint32_t FuncId = 1;
Dean Michael Berris938c5032016-07-21 07:39:55 +0000146 uint64_t CurFun = 0;
147 for (std::size_t I = 0; I < InstrMap.Entries; I++) {
148 auto Sled = InstrMap.Sleds[I];
149 auto F = Sled.Function;
150 if (CurFun == 0)
151 CurFun = F;
152 if (F != CurFun) {
153 ++FuncId;
154 CurFun = F;
155 }
156
157 // While we're here, we should patch the nop sled. To do that we mprotect
158 // the page containing the function to be writeable.
159 void *PageAlignedAddr =
Dean Michael Berris4ef1a692016-10-06 07:09:40 +0000160 reinterpret_cast<void *>(Sled.Address & ~(PageSize - 1));
161 std::size_t MProtectLen = (Sled.Address + cSledLength) -
162 reinterpret_cast<uint64_t>(PageAlignedAddr);
Dean Michael Berris9a0c4462016-07-27 04:30:25 +0000163 MProtectHelper Protector(PageAlignedAddr, MProtectLen);
164 if (Protector.MakeWriteable() == -1) {
Dean Michael Berris938c5032016-07-21 07:39:55 +0000165 printf("Failed mprotect: %d\n", errno);
166 return XRayPatchingStatus::FAILED;
167 }
168
Dean Michael Berrisd1617cd2016-09-20 14:35:57 +0000169 bool Success = false;
Dean Michael Berris4ef1a692016-10-06 07:09:40 +0000170 switch (Sled.Kind) {
Dean Michael Berrisd1617cd2016-09-20 14:35:57 +0000171 case XRayEntryType::ENTRY:
172 Success = patchFunctionEntry(Enable, FuncId, Sled);
173 break;
174 case XRayEntryType::EXIT:
175 Success = patchFunctionExit(Enable, FuncId, Sled);
176 break;
Dean Michael Berris1b09aae2016-10-13 23:56:54 +0000177 case XRayEntryType::TAIL:
178 Success = patchFunctionTailExit(Enable, FuncId, Sled);
179 break;
Dean Michael Berrisd1617cd2016-09-20 14:35:57 +0000180 default:
181 Report("Unsupported sled kind: %d", int(Sled.Kind));
182 continue;
Dean Michael Berris938c5032016-07-21 07:39:55 +0000183 }
Dean Michael Berrisd1617cd2016-09-20 14:35:57 +0000184 (void)Success;
Dean Michael Berris938c5032016-07-21 07:39:55 +0000185 }
186 XRayPatching.store(false, std::memory_order_release);
Dean Michael Berris17a586e2016-07-29 07:11:58 +0000187 PatchingSuccess = true;
188 return XRayPatchingStatus::SUCCESS;
Dean Michael Berris938c5032016-07-21 07:39:55 +0000189}
Dean Michael Berris68e74842016-08-08 03:10:22 +0000190
191XRayPatchingStatus __xray_patch() { return ControlPatching(true); }
192
193XRayPatchingStatus __xray_unpatch() { return ControlPatching(false); }