blob: 306dbcae7206b00b830025d7650fd4b826ff9b4c [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"
Dean Michael Berris4031e4b2016-11-16 01:01:13 +000026#include "xray_defs.h"
Dean Michael Berris17a586e2016-07-29 07:11:58 +000027
Dean Michael Berris938c5032016-07-21 07:39:55 +000028namespace __xray {
29
Dean Michael Berrisd1617cd2016-09-20 14:35:57 +000030#if defined(__x86_64__)
Dean Michael Berris4ef1a692016-10-06 07:09:40 +000031// FIXME: The actual length is 11 bytes. Why was length 12 passed to mprotect()
32// ?
33static const int16_t cSledLength = 12;
Dean Michael Berrisbad8f0f2016-11-21 03:20:43 +000034#elif defined(__aarch64__)
35static const int16_t cSledLength = 32;
Serge Rogatch882fc5d2016-12-05 23:29:56 +000036#elif defined(__arm__)
37static const int16_t cSledLength = 28;
Sagar Thakurea831e42017-02-15 10:54:09 +000038#elif SANITIZER_MIPS32
39static const int16_t cSledLength = 48;
40#elif SANITIZER_MIPS64
41static const int16_t cSledLength = 64;
Tim Shenc6ce73b2017-02-15 22:40:29 +000042#elif defined(__powerpc64__)
43static const int16_t cSledLength = 8;
Dean Michael Berrisd1617cd2016-09-20 14:35:57 +000044#else
Dean Michael Berris4ef1a692016-10-06 07:09:40 +000045#error "Unsupported CPU Architecture"
Dean Michael Berrisd1617cd2016-09-20 14:35:57 +000046#endif /* CPU architecture */
47
Dean Michael Berris938c5032016-07-21 07:39:55 +000048// This is the function to call when we encounter the entry or exit sleds.
49std::atomic<void (*)(int32_t, XRayEntryType)> XRayPatchedFunction{nullptr};
50
Dean Michael Berris9a0c4462016-07-27 04:30:25 +000051// MProtectHelper is an RAII wrapper for calls to mprotect(...) that will undo
52// any successful mprotect(...) changes. This is used to make a page writeable
53// and executable, and upon destruction if it was successful in doing so returns
54// the page into a read-only and executable page.
55//
56// This is only used specifically for runtime-patching of the XRay
57// instrumentation points. This assumes that the executable pages are originally
58// read-and-execute only.
59class MProtectHelper {
60 void *PageAlignedAddr;
61 std::size_t MProtectLen;
62 bool MustCleanup;
63
64public:
Dean Michael Berris4031e4b2016-11-16 01:01:13 +000065 explicit MProtectHelper(void *PageAlignedAddr,
66 std::size_t MProtectLen) XRAY_NEVER_INSTRUMENT
67 : PageAlignedAddr(PageAlignedAddr),
68 MProtectLen(MProtectLen),
Dean Michael Berris9a0c4462016-07-27 04:30:25 +000069 MustCleanup(false) {}
70
Dean Michael Berris4031e4b2016-11-16 01:01:13 +000071 int MakeWriteable() XRAY_NEVER_INSTRUMENT {
Dean Michael Berris9a0c4462016-07-27 04:30:25 +000072 auto R = mprotect(PageAlignedAddr, MProtectLen,
73 PROT_READ | PROT_WRITE | PROT_EXEC);
74 if (R != -1)
75 MustCleanup = true;
76 return R;
77 }
78
Dean Michael Berris4031e4b2016-11-16 01:01:13 +000079 ~MProtectHelper() XRAY_NEVER_INSTRUMENT {
Dean Michael Berris9a0c4462016-07-27 04:30:25 +000080 if (MustCleanup) {
81 mprotect(PageAlignedAddr, MProtectLen, PROT_READ | PROT_EXEC);
82 }
83 }
84};
85
Dean Michael Berris938c5032016-07-21 07:39:55 +000086} // namespace __xray
87
Dean Michael Berris938c5032016-07-21 07:39:55 +000088extern std::atomic<bool> XRayInitialized;
89extern std::atomic<__xray::XRaySledMap> XRayInstrMap;
90
Dean Michael Berris4031e4b2016-11-16 01:01:13 +000091int __xray_set_handler(void (*entry)(int32_t,
92 XRayEntryType)) XRAY_NEVER_INSTRUMENT {
Dean Michael Berris938c5032016-07-21 07:39:55 +000093 if (XRayInitialized.load(std::memory_order_acquire)) {
94 __xray::XRayPatchedFunction.store(entry, std::memory_order_release);
95 return 1;
96 }
97 return 0;
98}
99
Dean Michael Berris4031e4b2016-11-16 01:01:13 +0000100int __xray_remove_handler() XRAY_NEVER_INSTRUMENT {
101 return __xray_set_handler(nullptr);
102}
Dean Michael Berris68e74842016-08-08 03:10:22 +0000103
Dean Michael Berris938c5032016-07-21 07:39:55 +0000104std::atomic<bool> XRayPatching{false};
105
Dean Michael Berris9a0c4462016-07-27 04:30:25 +0000106using namespace __xray;
107
Dean Michael Berris17a586e2016-07-29 07:11:58 +0000108// FIXME: Figure out whether we can move this class to sanitizer_common instead
109// as a generic "scope guard".
110template <class Function> class CleanupInvoker {
111 Function Fn;
112
113public:
Dean Michael Berris4031e4b2016-11-16 01:01:13 +0000114 explicit CleanupInvoker(Function Fn) XRAY_NEVER_INSTRUMENT : Fn(Fn) {}
115 CleanupInvoker(const CleanupInvoker &) XRAY_NEVER_INSTRUMENT = default;
116 CleanupInvoker(CleanupInvoker &&) XRAY_NEVER_INSTRUMENT = default;
117 CleanupInvoker &
118 operator=(const CleanupInvoker &) XRAY_NEVER_INSTRUMENT = delete;
119 CleanupInvoker &operator=(CleanupInvoker &&) XRAY_NEVER_INSTRUMENT = delete;
120 ~CleanupInvoker() XRAY_NEVER_INSTRUMENT { Fn(); }
Dean Michael Berris17a586e2016-07-29 07:11:58 +0000121};
122
Dean Michael Berris4031e4b2016-11-16 01:01:13 +0000123template <class Function>
Dean Michael Berrisea9042c2017-02-07 23:35:34 +0000124CleanupInvoker<Function> scopeCleanup(Function Fn) XRAY_NEVER_INSTRUMENT {
Dean Michael Berris17a586e2016-07-29 07:11:58 +0000125 return CleanupInvoker<Function>{Fn};
126}
127
Dean Michael Berrisea9042c2017-02-07 23:35:34 +0000128// controlPatching implements the common internals of the patching/unpatching
Dean Michael Berris68e74842016-08-08 03:10:22 +0000129// implementation. |Enable| defines whether we're enabling or disabling the
130// runtime XRay instrumentation.
Dean Michael Berrisea9042c2017-02-07 23:35:34 +0000131XRayPatchingStatus controlPatching(bool Enable) XRAY_NEVER_INSTRUMENT {
Dean Michael Berris938c5032016-07-21 07:39:55 +0000132 if (!XRayInitialized.load(std::memory_order_acquire))
133 return XRayPatchingStatus::NOT_INITIALIZED; // Not initialized.
134
135 static bool NotPatching = false;
136 if (!XRayPatching.compare_exchange_strong(NotPatching, true,
137 std::memory_order_acq_rel,
138 std::memory_order_acquire)) {
139 return XRayPatchingStatus::ONGOING; // Already patching.
140 }
141
Dean Michael Berris17a586e2016-07-29 07:11:58 +0000142 bool PatchingSuccess = false;
Dean Michael Berrisea9042c2017-02-07 23:35:34 +0000143 auto XRayPatchingStatusResetter = scopeCleanup([&PatchingSuccess] {
Dean Michael Berris17a586e2016-07-29 07:11:58 +0000144 if (!PatchingSuccess) {
145 XRayPatching.store(false, std::memory_order_release);
146 }
147 });
148
Dean Michael Berris938c5032016-07-21 07:39:55 +0000149 // Step 1: Compute the function id, as a unique identifier per function in the
150 // instrumentation map.
Dean Michael Berris9a0c4462016-07-27 04:30:25 +0000151 XRaySledMap InstrMap = XRayInstrMap.load(std::memory_order_acquire);
Dean Michael Berris938c5032016-07-21 07:39:55 +0000152 if (InstrMap.Entries == 0)
153 return XRayPatchingStatus::NOT_INITIALIZED;
154
Dean Michael Berrisd1617cd2016-09-20 14:35:57 +0000155 const uint64_t PageSize = GetPageSizeCached();
Dean Michael Berris4ef1a692016-10-06 07:09:40 +0000156 if ((PageSize == 0) || ((PageSize & (PageSize - 1)) != 0)) {
Dean Michael Berris3076d432016-11-23 04:47:41 +0000157 Report("System page size is not a power of two: %lld\n", PageSize);
Dean Michael Berrisd1617cd2016-09-20 14:35:57 +0000158 return XRayPatchingStatus::FAILED;
159 }
160
161 uint32_t FuncId = 1;
Dean Michael Berris938c5032016-07-21 07:39:55 +0000162 uint64_t CurFun = 0;
163 for (std::size_t I = 0; I < InstrMap.Entries; I++) {
164 auto Sled = InstrMap.Sleds[I];
165 auto F = Sled.Function;
166 if (CurFun == 0)
167 CurFun = F;
168 if (F != CurFun) {
169 ++FuncId;
170 CurFun = F;
171 }
172
173 // While we're here, we should patch the nop sled. To do that we mprotect
174 // the page containing the function to be writeable.
175 void *PageAlignedAddr =
Dean Michael Berris4ef1a692016-10-06 07:09:40 +0000176 reinterpret_cast<void *>(Sled.Address & ~(PageSize - 1));
177 std::size_t MProtectLen = (Sled.Address + cSledLength) -
178 reinterpret_cast<uint64_t>(PageAlignedAddr);
Dean Michael Berris9a0c4462016-07-27 04:30:25 +0000179 MProtectHelper Protector(PageAlignedAddr, MProtectLen);
180 if (Protector.MakeWriteable() == -1) {
Dean Michael Berris938c5032016-07-21 07:39:55 +0000181 printf("Failed mprotect: %d\n", errno);
182 return XRayPatchingStatus::FAILED;
183 }
184
Dean Michael Berrisd1617cd2016-09-20 14:35:57 +0000185 bool Success = false;
Dean Michael Berris4ef1a692016-10-06 07:09:40 +0000186 switch (Sled.Kind) {
Dean Michael Berrisd1617cd2016-09-20 14:35:57 +0000187 case XRayEntryType::ENTRY:
188 Success = patchFunctionEntry(Enable, FuncId, Sled);
189 break;
190 case XRayEntryType::EXIT:
191 Success = patchFunctionExit(Enable, FuncId, Sled);
192 break;
Dean Michael Berris1b09aae2016-10-13 23:56:54 +0000193 case XRayEntryType::TAIL:
194 Success = patchFunctionTailExit(Enable, FuncId, Sled);
195 break;
Dean Michael Berrisd1617cd2016-09-20 14:35:57 +0000196 default:
Dean Michael Berris3076d432016-11-23 04:47:41 +0000197 Report("Unsupported sled kind: %d\n", int(Sled.Kind));
Dean Michael Berrisd1617cd2016-09-20 14:35:57 +0000198 continue;
Dean Michael Berris938c5032016-07-21 07:39:55 +0000199 }
Dean Michael Berrisd1617cd2016-09-20 14:35:57 +0000200 (void)Success;
Dean Michael Berris938c5032016-07-21 07:39:55 +0000201 }
202 XRayPatching.store(false, std::memory_order_release);
Dean Michael Berris17a586e2016-07-29 07:11:58 +0000203 PatchingSuccess = true;
204 return XRayPatchingStatus::SUCCESS;
Dean Michael Berris938c5032016-07-21 07:39:55 +0000205}
Dean Michael Berris68e74842016-08-08 03:10:22 +0000206
Dean Michael Berris4031e4b2016-11-16 01:01:13 +0000207XRayPatchingStatus __xray_patch() XRAY_NEVER_INSTRUMENT {
Dean Michael Berrisea9042c2017-02-07 23:35:34 +0000208 return controlPatching(true);
Dean Michael Berris4031e4b2016-11-16 01:01:13 +0000209}
Dean Michael Berris68e74842016-08-08 03:10:22 +0000210
Dean Michael Berris4031e4b2016-11-16 01:01:13 +0000211XRayPatchingStatus __xray_unpatch() XRAY_NEVER_INSTRUMENT {
Dean Michael Berrisea9042c2017-02-07 23:35:34 +0000212 return controlPatching(false);
Dean Michael Berris4031e4b2016-11-16 01:01:13 +0000213}