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" |
| 26 | |
Dean Michael Berris | 938c503 | 2016-07-21 07:39:55 +0000 | [diff] [blame] | 27 | namespace __xray { |
| 28 | |
Dean Michael Berris | d1617cd | 2016-09-20 14:35:57 +0000 | [diff] [blame] | 29 | #if defined(__x86_64__) |
Dean Michael Berris | 4ef1a69 | 2016-10-06 07:09:40 +0000 | [diff] [blame] | 30 | // FIXME: The actual length is 11 bytes. Why was length 12 passed to mprotect() |
| 31 | // ? |
| 32 | static const int16_t cSledLength = 12; |
Dean Michael Berris | d1617cd | 2016-09-20 14:35:57 +0000 | [diff] [blame] | 33 | #elif defined(__arm__) |
Dean Michael Berris | 4ef1a69 | 2016-10-06 07:09:40 +0000 | [diff] [blame] | 34 | static const int16_t cSledLength = 28; |
Dean Michael Berris | d1617cd | 2016-09-20 14:35:57 +0000 | [diff] [blame] | 35 | #else |
Dean Michael Berris | 4ef1a69 | 2016-10-06 07:09:40 +0000 | [diff] [blame] | 36 | #error "Unsupported CPU Architecture" |
Dean Michael Berris | d1617cd | 2016-09-20 14:35:57 +0000 | [diff] [blame] | 37 | #endif /* CPU architecture */ |
| 38 | |
Dean Michael Berris | 938c503 | 2016-07-21 07:39:55 +0000 | [diff] [blame] | 39 | // This is the function to call when we encounter the entry or exit sleds. |
| 40 | std::atomic<void (*)(int32_t, XRayEntryType)> XRayPatchedFunction{nullptr}; |
| 41 | |
Dean Michael Berris | 9a0c446 | 2016-07-27 04:30:25 +0000 | [diff] [blame] | 42 | // 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. |
| 50 | class MProtectHelper { |
| 51 | void *PageAlignedAddr; |
| 52 | std::size_t MProtectLen; |
| 53 | bool MustCleanup; |
| 54 | |
| 55 | public: |
| 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 Berris | 938c503 | 2016-07-21 07:39:55 +0000 | [diff] [blame] | 75 | } // namespace __xray |
| 76 | |
Dean Michael Berris | 938c503 | 2016-07-21 07:39:55 +0000 | [diff] [blame] | 77 | extern std::atomic<bool> XRayInitialized; |
| 78 | extern std::atomic<__xray::XRaySledMap> XRayInstrMap; |
| 79 | |
| 80 | int __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 Berris | 68e7484 | 2016-08-08 03:10:22 +0000 | [diff] [blame] | 88 | int __xray_remove_handler() { return __xray_set_handler(nullptr); } |
| 89 | |
Dean Michael Berris | 938c503 | 2016-07-21 07:39:55 +0000 | [diff] [blame] | 90 | std::atomic<bool> XRayPatching{false}; |
| 91 | |
Dean Michael Berris | 9a0c446 | 2016-07-27 04:30:25 +0000 | [diff] [blame] | 92 | using namespace __xray; |
| 93 | |
Dean Michael Berris | 17a586e | 2016-07-29 07:11:58 +0000 | [diff] [blame] | 94 | // FIXME: Figure out whether we can move this class to sanitizer_common instead |
| 95 | // as a generic "scope guard". |
| 96 | template <class Function> class CleanupInvoker { |
| 97 | Function Fn; |
| 98 | |
| 99 | public: |
| 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 | |
| 108 | template <class Function> CleanupInvoker<Function> ScopeCleanup(Function Fn) { |
| 109 | return CleanupInvoker<Function>{Fn}; |
| 110 | } |
| 111 | |
Dean Michael Berris | 68e7484 | 2016-08-08 03:10:22 +0000 | [diff] [blame] | 112 | // ControlPatching implements the common internals of the patching/unpatching |
| 113 | // implementation. |Enable| defines whether we're enabling or disabling the |
| 114 | // runtime XRay instrumentation. |
| 115 | XRayPatchingStatus ControlPatching(bool Enable) { |
Dean Michael Berris | 938c503 | 2016-07-21 07:39:55 +0000 | [diff] [blame] | 116 | 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 Berris | 17a586e | 2016-07-29 07:11:58 +0000 | [diff] [blame] | 126 | bool PatchingSuccess = false; |
| 127 | auto XRayPatchingStatusResetter = ScopeCleanup([&PatchingSuccess] { |
| 128 | if (!PatchingSuccess) { |
| 129 | XRayPatching.store(false, std::memory_order_release); |
| 130 | } |
| 131 | }); |
| 132 | |
Dean Michael Berris | 938c503 | 2016-07-21 07:39:55 +0000 | [diff] [blame] | 133 | // Step 1: Compute the function id, as a unique identifier per function in the |
| 134 | // instrumentation map. |
Dean Michael Berris | 9a0c446 | 2016-07-27 04:30:25 +0000 | [diff] [blame] | 135 | XRaySledMap InstrMap = XRayInstrMap.load(std::memory_order_acquire); |
Dean Michael Berris | 938c503 | 2016-07-21 07:39:55 +0000 | [diff] [blame] | 136 | if (InstrMap.Entries == 0) |
| 137 | return XRayPatchingStatus::NOT_INITIALIZED; |
| 138 | |
Dean Michael Berris | d1617cd | 2016-09-20 14:35:57 +0000 | [diff] [blame] | 139 | const uint64_t PageSize = GetPageSizeCached(); |
Dean Michael Berris | 4ef1a69 | 2016-10-06 07:09:40 +0000 | [diff] [blame] | 140 | if ((PageSize == 0) || ((PageSize & (PageSize - 1)) != 0)) { |
Dean Michael Berris | d1617cd | 2016-09-20 14:35:57 +0000 | [diff] [blame] | 141 | 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 Berris | 938c503 | 2016-07-21 07:39:55 +0000 | [diff] [blame] | 146 | 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 Berris | 4ef1a69 | 2016-10-06 07:09:40 +0000 | [diff] [blame] | 160 | reinterpret_cast<void *>(Sled.Address & ~(PageSize - 1)); |
| 161 | std::size_t MProtectLen = (Sled.Address + cSledLength) - |
| 162 | reinterpret_cast<uint64_t>(PageAlignedAddr); |
Dean Michael Berris | 9a0c446 | 2016-07-27 04:30:25 +0000 | [diff] [blame] | 163 | MProtectHelper Protector(PageAlignedAddr, MProtectLen); |
| 164 | if (Protector.MakeWriteable() == -1) { |
Dean Michael Berris | 938c503 | 2016-07-21 07:39:55 +0000 | [diff] [blame] | 165 | printf("Failed mprotect: %d\n", errno); |
| 166 | return XRayPatchingStatus::FAILED; |
| 167 | } |
| 168 | |
Dean Michael Berris | d1617cd | 2016-09-20 14:35:57 +0000 | [diff] [blame] | 169 | bool Success = false; |
Dean Michael Berris | 4ef1a69 | 2016-10-06 07:09:40 +0000 | [diff] [blame] | 170 | switch (Sled.Kind) { |
Dean Michael Berris | d1617cd | 2016-09-20 14:35:57 +0000 | [diff] [blame] | 171 | 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 Berris | 1b09aae | 2016-10-13 23:56:54 +0000 | [diff] [blame^] | 177 | case XRayEntryType::TAIL: |
| 178 | Success = patchFunctionTailExit(Enable, FuncId, Sled); |
| 179 | break; |
Dean Michael Berris | d1617cd | 2016-09-20 14:35:57 +0000 | [diff] [blame] | 180 | default: |
| 181 | Report("Unsupported sled kind: %d", int(Sled.Kind)); |
| 182 | continue; |
Dean Michael Berris | 938c503 | 2016-07-21 07:39:55 +0000 | [diff] [blame] | 183 | } |
Dean Michael Berris | d1617cd | 2016-09-20 14:35:57 +0000 | [diff] [blame] | 184 | (void)Success; |
Dean Michael Berris | 938c503 | 2016-07-21 07:39:55 +0000 | [diff] [blame] | 185 | } |
| 186 | XRayPatching.store(false, std::memory_order_release); |
Dean Michael Berris | 17a586e | 2016-07-29 07:11:58 +0000 | [diff] [blame] | 187 | PatchingSuccess = true; |
| 188 | return XRayPatchingStatus::SUCCESS; |
Dean Michael Berris | 938c503 | 2016-07-21 07:39:55 +0000 | [diff] [blame] | 189 | } |
Dean Michael Berris | 68e7484 | 2016-08-08 03:10:22 +0000 | [diff] [blame] | 190 | |
| 191 | XRayPatchingStatus __xray_patch() { return ControlPatching(true); } |
| 192 | |
| 193 | XRayPatchingStatus __xray_unpatch() { return ControlPatching(false); } |