Dean Michael Berris | 938c503 | 2016-07-21 07:39:55 +0000 | [diff] [blame] | 1 | //===-- 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 Berris | 17a586e | 2016-07-29 07:11:58 +0000 | [diff] [blame] | 17 | |
Dean Michael Berris | 938c503 | 2016-07-21 07:39:55 +0000 | [diff] [blame] | 18 | #include <atomic> |
| 19 | #include <cstdint> |
| 20 | #include <cstdio> |
| 21 | #include <errno.h> |
| 22 | #include <limits> |
| 23 | #include <sys/mman.h> |
| 24 | |
Dean Michael Berris | 17a586e | 2016-07-29 07:11:58 +0000 | [diff] [blame] | 25 | #include "sanitizer_common/sanitizer_common.h" |
Dean Michael Berris | 4031e4b | 2016-11-16 01:01:13 +0000 | [diff] [blame] | 26 | #include "xray_defs.h" |
Dean Michael Berris | 17a586e | 2016-07-29 07:11:58 +0000 | [diff] [blame] | 27 | |
Dean Michael Berris | 938c503 | 2016-07-21 07:39:55 +0000 | [diff] [blame] | 28 | namespace __xray { |
| 29 | |
Dean Michael Berris | d1617cd | 2016-09-20 14:35:57 +0000 | [diff] [blame] | 30 | #if defined(__x86_64__) |
Dean Michael Berris | 4ef1a69 | 2016-10-06 07:09:40 +0000 | [diff] [blame] | 31 | // FIXME: The actual length is 11 bytes. Why was length 12 passed to mprotect() |
| 32 | // ? |
| 33 | static const int16_t cSledLength = 12; |
Dean Michael Berris | bad8f0f | 2016-11-21 03:20:43 +0000 | [diff] [blame] | 34 | #elif defined(__aarch64__) |
| 35 | static const int16_t cSledLength = 32; |
Serge Rogatch | 882fc5d | 2016-12-05 23:29:56 +0000 | [diff] [blame] | 36 | #elif defined(__arm__) |
| 37 | static const int16_t cSledLength = 28; |
Sagar Thakur | ea831e4 | 2017-02-15 10:54:09 +0000 | [diff] [blame] | 38 | #elif SANITIZER_MIPS32 |
| 39 | static const int16_t cSledLength = 48; |
| 40 | #elif SANITIZER_MIPS64 |
| 41 | static const int16_t cSledLength = 64; |
Tim Shen | c6ce73b | 2017-02-15 22:40:29 +0000 | [diff] [blame] | 42 | #elif defined(__powerpc64__) |
| 43 | static const int16_t cSledLength = 8; |
Dean Michael Berris | d1617cd | 2016-09-20 14:35:57 +0000 | [diff] [blame] | 44 | #else |
Dean Michael Berris | 4ef1a69 | 2016-10-06 07:09:40 +0000 | [diff] [blame] | 45 | #error "Unsupported CPU Architecture" |
Dean Michael Berris | d1617cd | 2016-09-20 14:35:57 +0000 | [diff] [blame] | 46 | #endif /* CPU architecture */ |
| 47 | |
Dean Michael Berris | 938c503 | 2016-07-21 07:39:55 +0000 | [diff] [blame] | 48 | // This is the function to call when we encounter the entry or exit sleds. |
| 49 | std::atomic<void (*)(int32_t, XRayEntryType)> XRayPatchedFunction{nullptr}; |
| 50 | |
Dean Michael Berris | a814c94 | 2017-03-06 07:25:41 +0000 | [diff] [blame^] | 51 | // This is the function to call from the arg1-enabled sleds/trampolines. |
| 52 | std::atomic<void (*)(int32_t, XRayEntryType, uint64_t)> XRayArgLogger{nullptr}; |
| 53 | |
Dean Michael Berris | 9a0c446 | 2016-07-27 04:30:25 +0000 | [diff] [blame] | 54 | // MProtectHelper is an RAII wrapper for calls to mprotect(...) that will undo |
| 55 | // any successful mprotect(...) changes. This is used to make a page writeable |
| 56 | // and executable, and upon destruction if it was successful in doing so returns |
| 57 | // the page into a read-only and executable page. |
| 58 | // |
| 59 | // This is only used specifically for runtime-patching of the XRay |
| 60 | // instrumentation points. This assumes that the executable pages are originally |
| 61 | // read-and-execute only. |
| 62 | class MProtectHelper { |
| 63 | void *PageAlignedAddr; |
| 64 | std::size_t MProtectLen; |
| 65 | bool MustCleanup; |
| 66 | |
| 67 | public: |
Dean Michael Berris | 4031e4b | 2016-11-16 01:01:13 +0000 | [diff] [blame] | 68 | explicit MProtectHelper(void *PageAlignedAddr, |
| 69 | std::size_t MProtectLen) XRAY_NEVER_INSTRUMENT |
| 70 | : PageAlignedAddr(PageAlignedAddr), |
| 71 | MProtectLen(MProtectLen), |
Dean Michael Berris | 9a0c446 | 2016-07-27 04:30:25 +0000 | [diff] [blame] | 72 | MustCleanup(false) {} |
| 73 | |
Dean Michael Berris | 4031e4b | 2016-11-16 01:01:13 +0000 | [diff] [blame] | 74 | int MakeWriteable() XRAY_NEVER_INSTRUMENT { |
Dean Michael Berris | 9a0c446 | 2016-07-27 04:30:25 +0000 | [diff] [blame] | 75 | auto R = mprotect(PageAlignedAddr, MProtectLen, |
| 76 | PROT_READ | PROT_WRITE | PROT_EXEC); |
| 77 | if (R != -1) |
| 78 | MustCleanup = true; |
| 79 | return R; |
| 80 | } |
| 81 | |
Dean Michael Berris | 4031e4b | 2016-11-16 01:01:13 +0000 | [diff] [blame] | 82 | ~MProtectHelper() XRAY_NEVER_INSTRUMENT { |
Dean Michael Berris | 9a0c446 | 2016-07-27 04:30:25 +0000 | [diff] [blame] | 83 | if (MustCleanup) { |
| 84 | mprotect(PageAlignedAddr, MProtectLen, PROT_READ | PROT_EXEC); |
| 85 | } |
| 86 | } |
| 87 | }; |
| 88 | |
Dean Michael Berris | 938c503 | 2016-07-21 07:39:55 +0000 | [diff] [blame] | 89 | } // namespace __xray |
| 90 | |
Dean Michael Berris | 938c503 | 2016-07-21 07:39:55 +0000 | [diff] [blame] | 91 | extern std::atomic<bool> XRayInitialized; |
| 92 | extern std::atomic<__xray::XRaySledMap> XRayInstrMap; |
| 93 | |
Dean Michael Berris | 4031e4b | 2016-11-16 01:01:13 +0000 | [diff] [blame] | 94 | int __xray_set_handler(void (*entry)(int32_t, |
| 95 | XRayEntryType)) XRAY_NEVER_INSTRUMENT { |
Dean Michael Berris | 938c503 | 2016-07-21 07:39:55 +0000 | [diff] [blame] | 96 | if (XRayInitialized.load(std::memory_order_acquire)) { |
| 97 | __xray::XRayPatchedFunction.store(entry, std::memory_order_release); |
| 98 | return 1; |
| 99 | } |
| 100 | return 0; |
| 101 | } |
| 102 | |
Dean Michael Berris | 4031e4b | 2016-11-16 01:01:13 +0000 | [diff] [blame] | 103 | int __xray_remove_handler() XRAY_NEVER_INSTRUMENT { |
| 104 | return __xray_set_handler(nullptr); |
| 105 | } |
Dean Michael Berris | 68e7484 | 2016-08-08 03:10:22 +0000 | [diff] [blame] | 106 | |
Dean Michael Berris | 938c503 | 2016-07-21 07:39:55 +0000 | [diff] [blame] | 107 | std::atomic<bool> XRayPatching{false}; |
| 108 | |
Dean Michael Berris | 9a0c446 | 2016-07-27 04:30:25 +0000 | [diff] [blame] | 109 | using namespace __xray; |
| 110 | |
Dean Michael Berris | 17a586e | 2016-07-29 07:11:58 +0000 | [diff] [blame] | 111 | // FIXME: Figure out whether we can move this class to sanitizer_common instead |
| 112 | // as a generic "scope guard". |
| 113 | template <class Function> class CleanupInvoker { |
| 114 | Function Fn; |
| 115 | |
| 116 | public: |
Dean Michael Berris | 4031e4b | 2016-11-16 01:01:13 +0000 | [diff] [blame] | 117 | explicit CleanupInvoker(Function Fn) XRAY_NEVER_INSTRUMENT : Fn(Fn) {} |
| 118 | CleanupInvoker(const CleanupInvoker &) XRAY_NEVER_INSTRUMENT = default; |
| 119 | CleanupInvoker(CleanupInvoker &&) XRAY_NEVER_INSTRUMENT = default; |
| 120 | CleanupInvoker & |
| 121 | operator=(const CleanupInvoker &) XRAY_NEVER_INSTRUMENT = delete; |
| 122 | CleanupInvoker &operator=(CleanupInvoker &&) XRAY_NEVER_INSTRUMENT = delete; |
| 123 | ~CleanupInvoker() XRAY_NEVER_INSTRUMENT { Fn(); } |
Dean Michael Berris | 17a586e | 2016-07-29 07:11:58 +0000 | [diff] [blame] | 124 | }; |
| 125 | |
Dean Michael Berris | 4031e4b | 2016-11-16 01:01:13 +0000 | [diff] [blame] | 126 | template <class Function> |
Dean Michael Berris | ea9042c | 2017-02-07 23:35:34 +0000 | [diff] [blame] | 127 | CleanupInvoker<Function> scopeCleanup(Function Fn) XRAY_NEVER_INSTRUMENT { |
Dean Michael Berris | 17a586e | 2016-07-29 07:11:58 +0000 | [diff] [blame] | 128 | return CleanupInvoker<Function>{Fn}; |
| 129 | } |
| 130 | |
Dean Michael Berris | ea9042c | 2017-02-07 23:35:34 +0000 | [diff] [blame] | 131 | // controlPatching implements the common internals of the patching/unpatching |
Dean Michael Berris | 68e7484 | 2016-08-08 03:10:22 +0000 | [diff] [blame] | 132 | // implementation. |Enable| defines whether we're enabling or disabling the |
| 133 | // runtime XRay instrumentation. |
Dean Michael Berris | ea9042c | 2017-02-07 23:35:34 +0000 | [diff] [blame] | 134 | XRayPatchingStatus controlPatching(bool Enable) XRAY_NEVER_INSTRUMENT { |
Dean Michael Berris | 938c503 | 2016-07-21 07:39:55 +0000 | [diff] [blame] | 135 | if (!XRayInitialized.load(std::memory_order_acquire)) |
| 136 | return XRayPatchingStatus::NOT_INITIALIZED; // Not initialized. |
| 137 | |
| 138 | static bool NotPatching = false; |
| 139 | if (!XRayPatching.compare_exchange_strong(NotPatching, true, |
| 140 | std::memory_order_acq_rel, |
| 141 | std::memory_order_acquire)) { |
| 142 | return XRayPatchingStatus::ONGOING; // Already patching. |
| 143 | } |
| 144 | |
Dean Michael Berris | 17a586e | 2016-07-29 07:11:58 +0000 | [diff] [blame] | 145 | bool PatchingSuccess = false; |
Dean Michael Berris | ea9042c | 2017-02-07 23:35:34 +0000 | [diff] [blame] | 146 | auto XRayPatchingStatusResetter = scopeCleanup([&PatchingSuccess] { |
Dean Michael Berris | 17a586e | 2016-07-29 07:11:58 +0000 | [diff] [blame] | 147 | if (!PatchingSuccess) { |
| 148 | XRayPatching.store(false, std::memory_order_release); |
| 149 | } |
| 150 | }); |
| 151 | |
Dean Michael Berris | 938c503 | 2016-07-21 07:39:55 +0000 | [diff] [blame] | 152 | // Step 1: Compute the function id, as a unique identifier per function in the |
| 153 | // instrumentation map. |
Dean Michael Berris | 9a0c446 | 2016-07-27 04:30:25 +0000 | [diff] [blame] | 154 | XRaySledMap InstrMap = XRayInstrMap.load(std::memory_order_acquire); |
Dean Michael Berris | 938c503 | 2016-07-21 07:39:55 +0000 | [diff] [blame] | 155 | if (InstrMap.Entries == 0) |
| 156 | return XRayPatchingStatus::NOT_INITIALIZED; |
| 157 | |
Dean Michael Berris | d1617cd | 2016-09-20 14:35:57 +0000 | [diff] [blame] | 158 | const uint64_t PageSize = GetPageSizeCached(); |
Dean Michael Berris | 4ef1a69 | 2016-10-06 07:09:40 +0000 | [diff] [blame] | 159 | if ((PageSize == 0) || ((PageSize & (PageSize - 1)) != 0)) { |
Dean Michael Berris | 3076d43 | 2016-11-23 04:47:41 +0000 | [diff] [blame] | 160 | Report("System page size is not a power of two: %lld\n", PageSize); |
Dean Michael Berris | d1617cd | 2016-09-20 14:35:57 +0000 | [diff] [blame] | 161 | return XRayPatchingStatus::FAILED; |
| 162 | } |
| 163 | |
| 164 | uint32_t FuncId = 1; |
Dean Michael Berris | 938c503 | 2016-07-21 07:39:55 +0000 | [diff] [blame] | 165 | uint64_t CurFun = 0; |
| 166 | for (std::size_t I = 0; I < InstrMap.Entries; I++) { |
| 167 | auto Sled = InstrMap.Sleds[I]; |
| 168 | auto F = Sled.Function; |
| 169 | if (CurFun == 0) |
| 170 | CurFun = F; |
| 171 | if (F != CurFun) { |
| 172 | ++FuncId; |
| 173 | CurFun = F; |
| 174 | } |
| 175 | |
| 176 | // While we're here, we should patch the nop sled. To do that we mprotect |
| 177 | // the page containing the function to be writeable. |
| 178 | void *PageAlignedAddr = |
Dean Michael Berris | 4ef1a69 | 2016-10-06 07:09:40 +0000 | [diff] [blame] | 179 | reinterpret_cast<void *>(Sled.Address & ~(PageSize - 1)); |
| 180 | std::size_t MProtectLen = (Sled.Address + cSledLength) - |
| 181 | reinterpret_cast<uint64_t>(PageAlignedAddr); |
Dean Michael Berris | 9a0c446 | 2016-07-27 04:30:25 +0000 | [diff] [blame] | 182 | MProtectHelper Protector(PageAlignedAddr, MProtectLen); |
| 183 | if (Protector.MakeWriteable() == -1) { |
Dean Michael Berris | 938c503 | 2016-07-21 07:39:55 +0000 | [diff] [blame] | 184 | printf("Failed mprotect: %d\n", errno); |
| 185 | return XRayPatchingStatus::FAILED; |
| 186 | } |
| 187 | |
Dean Michael Berris | d1617cd | 2016-09-20 14:35:57 +0000 | [diff] [blame] | 188 | bool Success = false; |
Dean Michael Berris | 4ef1a69 | 2016-10-06 07:09:40 +0000 | [diff] [blame] | 189 | switch (Sled.Kind) { |
Dean Michael Berris | d1617cd | 2016-09-20 14:35:57 +0000 | [diff] [blame] | 190 | case XRayEntryType::ENTRY: |
Dean Michael Berris | a814c94 | 2017-03-06 07:25:41 +0000 | [diff] [blame^] | 191 | Success = patchFunctionEntry(Enable, FuncId, Sled, __xray_FunctionEntry); |
Dean Michael Berris | d1617cd | 2016-09-20 14:35:57 +0000 | [diff] [blame] | 192 | break; |
| 193 | case XRayEntryType::EXIT: |
| 194 | Success = patchFunctionExit(Enable, FuncId, Sled); |
| 195 | break; |
Dean Michael Berris | 1b09aae | 2016-10-13 23:56:54 +0000 | [diff] [blame] | 196 | case XRayEntryType::TAIL: |
| 197 | Success = patchFunctionTailExit(Enable, FuncId, Sled); |
| 198 | break; |
Dean Michael Berris | a814c94 | 2017-03-06 07:25:41 +0000 | [diff] [blame^] | 199 | case XRayEntryType::LOG_ARGS_ENTRY: |
| 200 | Success = patchFunctionEntry(Enable, FuncId, Sled, __xray_ArgLoggerEntry); |
| 201 | break; |
Dean Michael Berris | d1617cd | 2016-09-20 14:35:57 +0000 | [diff] [blame] | 202 | default: |
Dean Michael Berris | 3076d43 | 2016-11-23 04:47:41 +0000 | [diff] [blame] | 203 | Report("Unsupported sled kind: %d\n", int(Sled.Kind)); |
Dean Michael Berris | d1617cd | 2016-09-20 14:35:57 +0000 | [diff] [blame] | 204 | continue; |
Dean Michael Berris | 938c503 | 2016-07-21 07:39:55 +0000 | [diff] [blame] | 205 | } |
Dean Michael Berris | d1617cd | 2016-09-20 14:35:57 +0000 | [diff] [blame] | 206 | (void)Success; |
Dean Michael Berris | 938c503 | 2016-07-21 07:39:55 +0000 | [diff] [blame] | 207 | } |
| 208 | XRayPatching.store(false, std::memory_order_release); |
Dean Michael Berris | 17a586e | 2016-07-29 07:11:58 +0000 | [diff] [blame] | 209 | PatchingSuccess = true; |
| 210 | return XRayPatchingStatus::SUCCESS; |
Dean Michael Berris | 938c503 | 2016-07-21 07:39:55 +0000 | [diff] [blame] | 211 | } |
Dean Michael Berris | 68e7484 | 2016-08-08 03:10:22 +0000 | [diff] [blame] | 212 | |
Dean Michael Berris | 4031e4b | 2016-11-16 01:01:13 +0000 | [diff] [blame] | 213 | XRayPatchingStatus __xray_patch() XRAY_NEVER_INSTRUMENT { |
Dean Michael Berris | ea9042c | 2017-02-07 23:35:34 +0000 | [diff] [blame] | 214 | return controlPatching(true); |
Dean Michael Berris | 4031e4b | 2016-11-16 01:01:13 +0000 | [diff] [blame] | 215 | } |
Dean Michael Berris | 68e7484 | 2016-08-08 03:10:22 +0000 | [diff] [blame] | 216 | |
Dean Michael Berris | 4031e4b | 2016-11-16 01:01:13 +0000 | [diff] [blame] | 217 | XRayPatchingStatus __xray_unpatch() XRAY_NEVER_INSTRUMENT { |
Dean Michael Berris | ea9042c | 2017-02-07 23:35:34 +0000 | [diff] [blame] | 218 | return controlPatching(false); |
Dean Michael Berris | 4031e4b | 2016-11-16 01:01:13 +0000 | [diff] [blame] | 219 | } |
Dean Michael Berris | a814c94 | 2017-03-06 07:25:41 +0000 | [diff] [blame^] | 220 | |
| 221 | int __xray_set_handler_arg1(void (*Handler)(int32_t, XRayEntryType, uint64_t)) |
| 222 | { |
| 223 | if (!XRayInitialized.load(std::memory_order_acquire)) { |
| 224 | return 0; |
| 225 | } |
| 226 | // A relaxed write might not be visible even if the current thread gets |
| 227 | // scheduled on a different CPU/NUMA node. We need to wait for everyone to |
| 228 | // have this handler installed for consistency of collected data across CPUs. |
| 229 | XRayArgLogger.store(Handler, std::memory_order_release); |
| 230 | return 1; |
| 231 | } |
| 232 | int __xray_remove_handler_arg1() { return __xray_set_handler_arg1(nullptr); } |