1b3018603SNico Weber //===-- xray_interface.cpp --------------------------------------*- C++ -*-===// 2b3018603SNico Weber // 3b3018603SNico Weber // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4b3018603SNico Weber // See https://llvm.org/LICENSE.txt for license information. 5b3018603SNico Weber // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6b3018603SNico Weber // 7b3018603SNico Weber //===----------------------------------------------------------------------===// 8b3018603SNico Weber // 9b3018603SNico Weber // This file is a part of XRay, a dynamic runtime instrumentation system. 10b3018603SNico Weber // 11b3018603SNico Weber // Implementation of the API functions. 12b3018603SNico Weber // 13b3018603SNico Weber //===----------------------------------------------------------------------===// 14b3018603SNico Weber 15b3018603SNico Weber #include "xray_interface_internal.h" 16b3018603SNico Weber 17bbba9d8cSDimitry Andric #include <cinttypes> 18b3018603SNico Weber #include <cstdio> 19b3018603SNico Weber #include <errno.h> 20b3018603SNico Weber #include <limits> 21b3018603SNico Weber #include <string.h> 22b3018603SNico Weber #include <sys/mman.h> 23b3018603SNico Weber 24b3018603SNico Weber #if SANITIZER_FUCHSIA 25b3018603SNico Weber #include <zircon/process.h> 26b3018603SNico Weber #include <zircon/sanitizer.h> 27b3018603SNico Weber #include <zircon/status.h> 28b3018603SNico Weber #include <zircon/syscalls.h> 29b3018603SNico Weber #endif 30b3018603SNico Weber 31b3018603SNico Weber #include "sanitizer_common/sanitizer_addrhashmap.h" 32b3018603SNico Weber #include "sanitizer_common/sanitizer_common.h" 33b3018603SNico Weber 34b3018603SNico Weber #include "xray_defs.h" 35b3018603SNico Weber #include "xray_flags.h" 36b3018603SNico Weber 37b3018603SNico Weber extern __sanitizer::SpinMutex XRayInstrMapMutex; 38b3018603SNico Weber extern __sanitizer::atomic_uint8_t XRayInitialized; 39e738a5d8SSebastian Kreutzer extern __xray::XRaySledMap *XRayInstrMaps; 40e738a5d8SSebastian Kreutzer extern __sanitizer::atomic_uint32_t XRayNumObjects; 41b3018603SNico Weber 42b3018603SNico Weber namespace __xray { 43b3018603SNico Weber 44b3018603SNico Weber #if defined(__x86_64__) 45b3018603SNico Weber static const int16_t cSledLength = 12; 46b3018603SNico Weber #elif defined(__aarch64__) 47b3018603SNico Weber static const int16_t cSledLength = 32; 48b3018603SNico Weber #elif defined(__arm__) 49b3018603SNico Weber static const int16_t cSledLength = 28; 50ef33d6cbSWeining Lu #elif SANITIZER_LOONGARCH64 51ef33d6cbSWeining Lu static const int16_t cSledLength = 48; 52b3018603SNico Weber #elif SANITIZER_MIPS32 53b3018603SNico Weber static const int16_t cSledLength = 48; 54b3018603SNico Weber #elif SANITIZER_MIPS64 55b3018603SNico Weber static const int16_t cSledLength = 64; 56b3018603SNico Weber #elif defined(__powerpc64__) 57b3018603SNico Weber static const int16_t cSledLength = 8; 581e68c799SBrian Cain #elif defined(__hexagon__) 591e68c799SBrian Cain static const int16_t cSledLength = 20; 60*ea76b2d8SMin-Yih Hsu #elif defined(__riscv) && (__riscv_xlen == 64) 61*ea76b2d8SMin-Yih Hsu static const int16_t cSledLength = 68; 62*ea76b2d8SMin-Yih Hsu #elif defined(__riscv) && (__riscv_xlen == 32) 63*ea76b2d8SMin-Yih Hsu static const int16_t cSledLength = 52; 64b3018603SNico Weber #else 65b3018603SNico Weber #error "Unsupported CPU Architecture" 66b3018603SNico Weber #endif /* CPU architecture */ 67b3018603SNico Weber 68b3018603SNico Weber // This is the function to call when we encounter the entry or exit sleds. 69e738a5d8SSebastian Kreutzer atomic_uintptr_t XRayPatchedFunction SANITIZER_INTERFACE_ATTRIBUTE{0}; 70b3018603SNico Weber 71b3018603SNico Weber // This is the function to call from the arg1-enabled sleds/trampolines. 72e738a5d8SSebastian Kreutzer atomic_uintptr_t XRayArgLogger SANITIZER_INTERFACE_ATTRIBUTE{0}; 73b3018603SNico Weber 74b3018603SNico Weber // This is the function to call when we encounter a custom event log call. 75e738a5d8SSebastian Kreutzer atomic_uintptr_t XRayPatchedCustomEvent SANITIZER_INTERFACE_ATTRIBUTE{0}; 76b3018603SNico Weber 77b3018603SNico Weber // This is the function to call when we encounter a typed event log call. 78e738a5d8SSebastian Kreutzer atomic_uintptr_t XRayPatchedTypedEvent SANITIZER_INTERFACE_ATTRIBUTE{0}; 79b3018603SNico Weber 80b3018603SNico Weber // This is the global status to determine whether we are currently 81b3018603SNico Weber // patching/unpatching. 82b3018603SNico Weber atomic_uint8_t XRayPatching{0}; 83b3018603SNico Weber 84b3018603SNico Weber struct TypeDescription { 85b3018603SNico Weber uint32_t type_id; 86b3018603SNico Weber std::size_t description_string_length; 87b3018603SNico Weber }; 88b3018603SNico Weber 89b3018603SNico Weber using TypeDescriptorMapType = AddrHashMap<TypeDescription, 11>; 90b3018603SNico Weber // An address map from immutable descriptors to type ids. 91b3018603SNico Weber TypeDescriptorMapType TypeDescriptorAddressMap{}; 92b3018603SNico Weber 93b3018603SNico Weber atomic_uint32_t TypeEventDescriptorCounter{0}; 94b3018603SNico Weber 95b3018603SNico Weber // MProtectHelper is an RAII wrapper for calls to mprotect(...) that will 96b3018603SNico Weber // undo any successful mprotect(...) changes. This is used to make a page 97b3018603SNico Weber // writeable and executable, and upon destruction if it was successful in 98b3018603SNico Weber // doing so returns the page into a read-only and executable page. 99b3018603SNico Weber // 100b3018603SNico Weber // This is only used specifically for runtime-patching of the XRay 101b3018603SNico Weber // instrumentation points. This assumes that the executable pages are 102b3018603SNico Weber // originally read-and-execute only. 103b3018603SNico Weber class MProtectHelper { 104b3018603SNico Weber void *PageAlignedAddr; 105b3018603SNico Weber std::size_t MProtectLen; 106b3018603SNico Weber bool MustCleanup; 107b3018603SNico Weber 108b3018603SNico Weber public: 109b3018603SNico Weber explicit MProtectHelper(void *PageAlignedAddr, 110b3018603SNico Weber std::size_t MProtectLen, 111b3018603SNico Weber std::size_t PageSize) XRAY_NEVER_INSTRUMENT 112b3018603SNico Weber : PageAlignedAddr(PageAlignedAddr), 113b3018603SNico Weber MProtectLen(MProtectLen), 114b3018603SNico Weber MustCleanup(false) { 115b3018603SNico Weber #if SANITIZER_FUCHSIA 116b3018603SNico Weber MProtectLen = RoundUpTo(MProtectLen, PageSize); 117b3018603SNico Weber #endif 118b3018603SNico Weber } 119b3018603SNico Weber 120b3018603SNico Weber int MakeWriteable() XRAY_NEVER_INSTRUMENT { 121b3018603SNico Weber #if SANITIZER_FUCHSIA 122b3018603SNico Weber auto R = __sanitizer_change_code_protection( 123b3018603SNico Weber reinterpret_cast<uintptr_t>(PageAlignedAddr), MProtectLen, true); 124b3018603SNico Weber if (R != ZX_OK) { 125b3018603SNico Weber Report("XRay: cannot change code protection: %s\n", 126b3018603SNico Weber _zx_status_get_string(R)); 127b3018603SNico Weber return -1; 128b3018603SNico Weber } 129b3018603SNico Weber MustCleanup = true; 130b3018603SNico Weber return 0; 131b3018603SNico Weber #else 132b3018603SNico Weber auto R = mprotect(PageAlignedAddr, MProtectLen, 133b3018603SNico Weber PROT_READ | PROT_WRITE | PROT_EXEC); 134b3018603SNico Weber if (R != -1) 135b3018603SNico Weber MustCleanup = true; 136b3018603SNico Weber return R; 137b3018603SNico Weber #endif 138b3018603SNico Weber } 139b3018603SNico Weber 140b3018603SNico Weber ~MProtectHelper() XRAY_NEVER_INSTRUMENT { 141b3018603SNico Weber if (MustCleanup) { 142b3018603SNico Weber #if SANITIZER_FUCHSIA 143b3018603SNico Weber auto R = __sanitizer_change_code_protection( 144b3018603SNico Weber reinterpret_cast<uintptr_t>(PageAlignedAddr), MProtectLen, false); 145b3018603SNico Weber if (R != ZX_OK) { 146b3018603SNico Weber Report("XRay: cannot change code protection: %s\n", 147b3018603SNico Weber _zx_status_get_string(R)); 148b3018603SNico Weber } 149b3018603SNico Weber #else 150b3018603SNico Weber mprotect(PageAlignedAddr, MProtectLen, PROT_READ | PROT_EXEC); 151b3018603SNico Weber #endif 152b3018603SNico Weber } 153b3018603SNico Weber } 154b3018603SNico Weber }; 155b3018603SNico Weber 156b3018603SNico Weber namespace { 157b3018603SNico Weber 158e738a5d8SSebastian Kreutzer bool isObjectLoaded(int32_t ObjId) { 159e738a5d8SSebastian Kreutzer SpinMutexLock Guard(&XRayInstrMapMutex); 160e738a5d8SSebastian Kreutzer if (ObjId < 0 || static_cast<uint32_t>(ObjId) >= 161e738a5d8SSebastian Kreutzer atomic_load(&XRayNumObjects, memory_order_acquire)) { 162e738a5d8SSebastian Kreutzer return false; 163e738a5d8SSebastian Kreutzer } 164e738a5d8SSebastian Kreutzer return XRayInstrMaps[ObjId].Loaded; 165e738a5d8SSebastian Kreutzer } 166e738a5d8SSebastian Kreutzer 167e738a5d8SSebastian Kreutzer bool patchSled(const XRaySledEntry &Sled, bool Enable, int32_t FuncId, 168e738a5d8SSebastian Kreutzer const XRayTrampolines &Trampolines) XRAY_NEVER_INSTRUMENT { 169b3018603SNico Weber bool Success = false; 170b3018603SNico Weber switch (Sled.Kind) { 171b3018603SNico Weber case XRayEntryType::ENTRY: 172e738a5d8SSebastian Kreutzer Success = patchFunctionEntry(Enable, FuncId, Sled, Trampolines, 173e738a5d8SSebastian Kreutzer /*LogArgs=*/false); 174b3018603SNico Weber break; 175b3018603SNico Weber case XRayEntryType::EXIT: 176e738a5d8SSebastian Kreutzer Success = patchFunctionExit(Enable, FuncId, Sled, Trampolines); 177b3018603SNico Weber break; 178b3018603SNico Weber case XRayEntryType::TAIL: 179e738a5d8SSebastian Kreutzer Success = patchFunctionTailExit(Enable, FuncId, Sled, Trampolines); 180b3018603SNico Weber break; 181b3018603SNico Weber case XRayEntryType::LOG_ARGS_ENTRY: 182e738a5d8SSebastian Kreutzer Success = patchFunctionEntry(Enable, FuncId, Sled, Trampolines, 183e738a5d8SSebastian Kreutzer /*LogArgs=*/true); 184b3018603SNico Weber break; 185b3018603SNico Weber case XRayEntryType::CUSTOM_EVENT: 18690627a5aSMikhail Goncharov Success = patchCustomEvent(Enable, FuncId, Sled); 187b3018603SNico Weber break; 188b3018603SNico Weber case XRayEntryType::TYPED_EVENT: 18990627a5aSMikhail Goncharov Success = patchTypedEvent(Enable, FuncId, Sled); 190b3018603SNico Weber break; 191b3018603SNico Weber default: 192bbba9d8cSDimitry Andric Report("Unsupported sled kind '%" PRIu64 "' @%04x\n", Sled.Address, 193bbba9d8cSDimitry Andric int(Sled.Kind)); 194b3018603SNico Weber return false; 195b3018603SNico Weber } 196b3018603SNico Weber return Success; 197b3018603SNico Weber } 198b3018603SNico Weber 1997c7c8e0dSIan Levesque const XRayFunctionSledIndex 2007c7c8e0dSIan Levesque findFunctionSleds(int32_t FuncId, 2017c7c8e0dSIan Levesque const XRaySledMap &InstrMap) XRAY_NEVER_INSTRUMENT { 2027c7c8e0dSIan Levesque int32_t CurFn = 0; 2037c7c8e0dSIan Levesque uint64_t LastFnAddr = 0; 204e0a6561eSFangrui Song XRayFunctionSledIndex Index = {nullptr, 0}; 2057c7c8e0dSIan Levesque 2067c7c8e0dSIan Levesque for (std::size_t I = 0; I < InstrMap.Entries && CurFn <= FuncId; I++) { 2077c7c8e0dSIan Levesque const auto &Sled = InstrMap.Sleds[I]; 2087c7c8e0dSIan Levesque const auto Function = Sled.function(); 2097c7c8e0dSIan Levesque if (Function != LastFnAddr) { 2107c7c8e0dSIan Levesque CurFn++; 2117c7c8e0dSIan Levesque LastFnAddr = Function; 2127c7c8e0dSIan Levesque } 2137c7c8e0dSIan Levesque 2147c7c8e0dSIan Levesque if (CurFn == FuncId) { 2157c7c8e0dSIan Levesque if (Index.Begin == nullptr) 2167c7c8e0dSIan Levesque Index.Begin = &Sled; 217e0a6561eSFangrui Song Index.Size = &Sled - Index.Begin + 1; 2187c7c8e0dSIan Levesque } 2197c7c8e0dSIan Levesque } 2207c7c8e0dSIan Levesque 2217c7c8e0dSIan Levesque return Index; 2227c7c8e0dSIan Levesque } 2237c7c8e0dSIan Levesque 224e738a5d8SSebastian Kreutzer XRayPatchingStatus patchFunction(int32_t FuncId, int32_t ObjId, 225b3018603SNico Weber bool Enable) XRAY_NEVER_INSTRUMENT { 226e738a5d8SSebastian Kreutzer if (!atomic_load(&XRayInitialized, memory_order_acquire)) 227b3018603SNico Weber return XRayPatchingStatus::NOT_INITIALIZED; // Not initialized. 228b3018603SNico Weber 229b3018603SNico Weber uint8_t NotPatching = false; 230b3018603SNico Weber if (!atomic_compare_exchange_strong( 231b3018603SNico Weber &XRayPatching, &NotPatching, true, memory_order_acq_rel)) 232b3018603SNico Weber return XRayPatchingStatus::ONGOING; // Already patching. 233b3018603SNico Weber 234b3018603SNico Weber // Next, we look for the function index. 235b3018603SNico Weber XRaySledMap InstrMap; 236b3018603SNico Weber { 237b3018603SNico Weber SpinMutexLock Guard(&XRayInstrMapMutex); 238e738a5d8SSebastian Kreutzer if (ObjId < 0 || static_cast<uint32_t>(ObjId) >= 239e738a5d8SSebastian Kreutzer atomic_load(&XRayNumObjects, memory_order_acquire)) { 240e738a5d8SSebastian Kreutzer Report("Unable to patch function: invalid sled map index: %d", ObjId); 241e738a5d8SSebastian Kreutzer return XRayPatchingStatus::FAILED; 242e738a5d8SSebastian Kreutzer } 243e738a5d8SSebastian Kreutzer InstrMap = XRayInstrMaps[ObjId]; 244b3018603SNico Weber } 245b3018603SNico Weber 246b3018603SNico Weber // If we don't have an index, we can't patch individual functions. 247b3018603SNico Weber if (InstrMap.Functions == 0) 248b3018603SNico Weber return XRayPatchingStatus::NOT_INITIALIZED; 249b3018603SNico Weber 250e738a5d8SSebastian Kreutzer // Check if the corresponding DSO has been unloaded. 251e738a5d8SSebastian Kreutzer if (!InstrMap.Loaded) { 252e738a5d8SSebastian Kreutzer Report("Invalid function id provided: %d\n", FuncId); 253e738a5d8SSebastian Kreutzer return XRayPatchingStatus::NOT_INITIALIZED; 254e738a5d8SSebastian Kreutzer } 255e738a5d8SSebastian Kreutzer 256b3018603SNico Weber // FuncId must be a positive number, less than the number of functions 257b3018603SNico Weber // instrumented. 258b3018603SNico Weber if (FuncId <= 0 || static_cast<size_t>(FuncId) > InstrMap.Functions) { 259b3018603SNico Weber Report("Invalid function id provided: %d\n", FuncId); 260b3018603SNico Weber return XRayPatchingStatus::FAILED; 261b3018603SNico Weber } 262b3018603SNico Weber 263e738a5d8SSebastian Kreutzer auto PackedId = __xray::MakePackedId(FuncId, ObjId); 264e738a5d8SSebastian Kreutzer 265b3018603SNico Weber // Now we patch ths sleds for this specific function. 266e0a6561eSFangrui Song XRayFunctionSledIndex SledRange; 267e0a6561eSFangrui Song if (InstrMap.SledsIndex) { 268e0a6561eSFangrui Song SledRange = {InstrMap.SledsIndex[FuncId - 1].fromPCRelative(), 269e0a6561eSFangrui Song InstrMap.SledsIndex[FuncId - 1].Size}; 270e0a6561eSFangrui Song } else { 271e0a6561eSFangrui Song SledRange = findFunctionSleds(FuncId, InstrMap); 272e0a6561eSFangrui Song } 273e738a5d8SSebastian Kreutzer 274b3018603SNico Weber auto *f = SledRange.Begin; 275b3018603SNico Weber bool SucceedOnce = false; 276e0a6561eSFangrui Song for (size_t i = 0; i != SledRange.Size; ++i) 277e738a5d8SSebastian Kreutzer SucceedOnce |= patchSled(f[i], Enable, PackedId, InstrMap.Trampolines); 278b3018603SNico Weber 279e738a5d8SSebastian Kreutzer atomic_store(&XRayPatching, false, memory_order_release); 280b3018603SNico Weber 281b3018603SNico Weber if (!SucceedOnce) { 282b3018603SNico Weber Report("Failed patching any sled for function '%d'.", FuncId); 283b3018603SNico Weber return XRayPatchingStatus::FAILED; 284b3018603SNico Weber } 285b3018603SNico Weber 286b3018603SNico Weber return XRayPatchingStatus::SUCCESS; 287b3018603SNico Weber } 288b3018603SNico Weber 289b3018603SNico Weber // controlPatching implements the common internals of the patching/unpatching 290b3018603SNico Weber // implementation. |Enable| defines whether we're enabling or disabling the 291b3018603SNico Weber // runtime XRay instrumentation. 292e738a5d8SSebastian Kreutzer // This function should only be called after ensuring that XRay is initialized 293e738a5d8SSebastian Kreutzer // and no other thread is currently patching. 294e738a5d8SSebastian Kreutzer XRayPatchingStatus controlPatchingObjectUnchecked(bool Enable, int32_t ObjId) { 295b3018603SNico Weber XRaySledMap InstrMap; 296b3018603SNico Weber { 297b3018603SNico Weber SpinMutexLock Guard(&XRayInstrMapMutex); 298e738a5d8SSebastian Kreutzer if (ObjId < 0 || static_cast<uint32_t>(ObjId) >= 299e738a5d8SSebastian Kreutzer atomic_load(&XRayNumObjects, memory_order_acquire)) { 300e738a5d8SSebastian Kreutzer Report("Unable to patch functions: invalid sled map index: %d\n", ObjId); 301e738a5d8SSebastian Kreutzer return XRayPatchingStatus::FAILED; 302e738a5d8SSebastian Kreutzer } 303e738a5d8SSebastian Kreutzer InstrMap = XRayInstrMaps[ObjId]; 304b3018603SNico Weber } 305b3018603SNico Weber if (InstrMap.Entries == 0) 306b3018603SNico Weber return XRayPatchingStatus::NOT_INITIALIZED; 307b3018603SNico Weber 308e738a5d8SSebastian Kreutzer if (Verbosity()) 309e738a5d8SSebastian Kreutzer Report("Patching object %d with %d functions.\n", ObjId, InstrMap.Entries); 310e738a5d8SSebastian Kreutzer 311e738a5d8SSebastian Kreutzer // Check if the corresponding DSO has been unloaded. 312e738a5d8SSebastian Kreutzer if (!InstrMap.Loaded) { 313e738a5d8SSebastian Kreutzer Report("Object is not loaded at index: %d\n", ObjId); 314e738a5d8SSebastian Kreutzer return XRayPatchingStatus::FAILED; 315e738a5d8SSebastian Kreutzer } 316e738a5d8SSebastian Kreutzer 317b3018603SNico Weber uint32_t FuncId = 1; 318b3018603SNico Weber uint64_t CurFun = 0; 319b3018603SNico Weber 320b3018603SNico Weber // First we want to find the bounds for which we have instrumentation points, 321b3018603SNico Weber // and try to get as few calls to mprotect(...) as possible. We're assuming 322b3018603SNico Weber // that all the sleds for the instrumentation map are contiguous as a single 323b3018603SNico Weber // set of pages. When we do support dynamic shared object instrumentation, 324b3018603SNico Weber // we'll need to do this for each set of page load offsets per DSO loaded. For 325b3018603SNico Weber // now we're assuming we can mprotect the whole section of text between the 326b3018603SNico Weber // minimum sled address and the maximum sled address (+ the largest sled 327b3018603SNico Weber // size). 3285771c985SFangrui Song auto *MinSled = &InstrMap.Sleds[0]; 3295771c985SFangrui Song auto *MaxSled = &InstrMap.Sleds[InstrMap.Entries - 1]; 330b3018603SNico Weber for (std::size_t I = 0; I < InstrMap.Entries; I++) { 331b3018603SNico Weber const auto &Sled = InstrMap.Sleds[I]; 3325771c985SFangrui Song if (Sled.address() < MinSled->address()) 3335771c985SFangrui Song MinSled = &Sled; 3345771c985SFangrui Song if (Sled.address() > MaxSled->address()) 3355771c985SFangrui Song MaxSled = &Sled; 336b3018603SNico Weber } 337b3018603SNico Weber 338b3018603SNico Weber const size_t PageSize = flags()->xray_page_size_override > 0 339b3018603SNico Weber ? flags()->xray_page_size_override 340b3018603SNico Weber : GetPageSizeCached(); 341b3018603SNico Weber if ((PageSize == 0) || ((PageSize & (PageSize - 1)) != 0)) { 342bbba9d8cSDimitry Andric Report("System page size is not a power of two: %zu\n", PageSize); 343b3018603SNico Weber return XRayPatchingStatus::FAILED; 344b3018603SNico Weber } 345b3018603SNico Weber 346b3018603SNico Weber void *PageAlignedAddr = 3475771c985SFangrui Song reinterpret_cast<void *>(MinSled->address() & ~(PageSize - 1)); 348b3018603SNico Weber size_t MProtectLen = 3495771c985SFangrui Song (MaxSled->address() - reinterpret_cast<uptr>(PageAlignedAddr)) + 3505771c985SFangrui Song cSledLength; 351b3018603SNico Weber MProtectHelper Protector(PageAlignedAddr, MProtectLen, PageSize); 352b3018603SNico Weber if (Protector.MakeWriteable() == -1) { 353b3018603SNico Weber Report("Failed mprotect: %d\n", errno); 354b3018603SNico Weber return XRayPatchingStatus::FAILED; 355b3018603SNico Weber } 356b3018603SNico Weber 357b3018603SNico Weber for (std::size_t I = 0; I < InstrMap.Entries; ++I) { 358b3018603SNico Weber auto &Sled = InstrMap.Sleds[I]; 35910bc1258SFangrui Song auto F = Sled.function(); 360b3018603SNico Weber if (CurFun == 0) 361b3018603SNico Weber CurFun = F; 362b3018603SNico Weber if (F != CurFun) { 363b3018603SNico Weber ++FuncId; 364b3018603SNico Weber CurFun = F; 365b3018603SNico Weber } 366e738a5d8SSebastian Kreutzer auto PackedId = __xray::MakePackedId(FuncId, ObjId); 367e738a5d8SSebastian Kreutzer patchSled(Sled, Enable, PackedId, InstrMap.Trampolines); 368b3018603SNico Weber } 369e738a5d8SSebastian Kreutzer atomic_store(&XRayPatching, false, memory_order_release); 370b3018603SNico Weber return XRayPatchingStatus::SUCCESS; 371b3018603SNico Weber } 372b3018603SNico Weber 373e738a5d8SSebastian Kreutzer // Controls patching for all registered objects. 374e738a5d8SSebastian Kreutzer // Returns: SUCCESS, if patching succeeds for all objects. 375e738a5d8SSebastian Kreutzer // NOT_INITIALIZED, if one or more objects returned NOT_INITIALIZED 376e738a5d8SSebastian Kreutzer // but none failed. 377e738a5d8SSebastian Kreutzer // FAILED, if patching of one or more objects failed. 378e738a5d8SSebastian Kreutzer XRayPatchingStatus controlPatching(bool Enable) XRAY_NEVER_INSTRUMENT { 379e738a5d8SSebastian Kreutzer if (!atomic_load(&XRayInitialized, memory_order_acquire)) 380e738a5d8SSebastian Kreutzer return XRayPatchingStatus::NOT_INITIALIZED; // Not initialized. 381e738a5d8SSebastian Kreutzer 382e738a5d8SSebastian Kreutzer uint8_t NotPatching = false; 383e738a5d8SSebastian Kreutzer if (!atomic_compare_exchange_strong(&XRayPatching, &NotPatching, true, 384e738a5d8SSebastian Kreutzer memory_order_acq_rel)) 385e738a5d8SSebastian Kreutzer return XRayPatchingStatus::ONGOING; // Already patching. 386e738a5d8SSebastian Kreutzer 387e738a5d8SSebastian Kreutzer auto XRayPatchingStatusResetter = at_scope_exit( 388e738a5d8SSebastian Kreutzer [] { atomic_store(&XRayPatching, false, memory_order_release); }); 389e738a5d8SSebastian Kreutzer 390e738a5d8SSebastian Kreutzer unsigned NumObjects = __xray_num_objects(); 391e738a5d8SSebastian Kreutzer 392e738a5d8SSebastian Kreutzer XRayPatchingStatus CombinedStatus{NOT_INITIALIZED}; 393e738a5d8SSebastian Kreutzer for (unsigned I = 0; I < NumObjects; ++I) { 394e738a5d8SSebastian Kreutzer if (!isObjectLoaded(I)) 395e738a5d8SSebastian Kreutzer continue; 396e738a5d8SSebastian Kreutzer auto LastStatus = controlPatchingObjectUnchecked(Enable, I); 397e738a5d8SSebastian Kreutzer switch (LastStatus) { 398e738a5d8SSebastian Kreutzer case SUCCESS: 399e738a5d8SSebastian Kreutzer if (CombinedStatus == NOT_INITIALIZED) 400e738a5d8SSebastian Kreutzer CombinedStatus = SUCCESS; 401e738a5d8SSebastian Kreutzer break; 402e738a5d8SSebastian Kreutzer case FAILED: 403e738a5d8SSebastian Kreutzer // Report failure, but try to patch the remaining objects 404e738a5d8SSebastian Kreutzer CombinedStatus = FAILED; 405e738a5d8SSebastian Kreutzer break; 406e738a5d8SSebastian Kreutzer case NOT_INITIALIZED: 407e738a5d8SSebastian Kreutzer // XRay has been initialized but there are no sleds available for this 408e738a5d8SSebastian Kreutzer // object. Try to patch remaining objects. 409e738a5d8SSebastian Kreutzer if (CombinedStatus != FAILED) 410e738a5d8SSebastian Kreutzer CombinedStatus = NOT_INITIALIZED; 411e738a5d8SSebastian Kreutzer break; 412e738a5d8SSebastian Kreutzer case ONGOING: 413e738a5d8SSebastian Kreutzer UNREACHABLE("Status ONGOING should not appear at this point"); 414e738a5d8SSebastian Kreutzer } 415e738a5d8SSebastian Kreutzer } 416e738a5d8SSebastian Kreutzer return CombinedStatus; 417e738a5d8SSebastian Kreutzer } 418e738a5d8SSebastian Kreutzer 419e738a5d8SSebastian Kreutzer // Controls patching for one object. 420e738a5d8SSebastian Kreutzer XRayPatchingStatus controlPatching(bool Enable, 421e738a5d8SSebastian Kreutzer int32_t ObjId) XRAY_NEVER_INSTRUMENT { 422e738a5d8SSebastian Kreutzer 423e738a5d8SSebastian Kreutzer if (!atomic_load(&XRayInitialized, memory_order_acquire)) 424e738a5d8SSebastian Kreutzer return XRayPatchingStatus::NOT_INITIALIZED; // Not initialized. 425e738a5d8SSebastian Kreutzer 426e738a5d8SSebastian Kreutzer uint8_t NotPatching = false; 427e738a5d8SSebastian Kreutzer if (!atomic_compare_exchange_strong(&XRayPatching, &NotPatching, true, 428e738a5d8SSebastian Kreutzer memory_order_acq_rel)) 429e738a5d8SSebastian Kreutzer return XRayPatchingStatus::ONGOING; // Already patching. 430e738a5d8SSebastian Kreutzer 431e738a5d8SSebastian Kreutzer auto XRayPatchingStatusResetter = at_scope_exit( 432e738a5d8SSebastian Kreutzer [] { atomic_store(&XRayPatching, false, memory_order_release); }); 433e738a5d8SSebastian Kreutzer 434e738a5d8SSebastian Kreutzer return controlPatchingObjectUnchecked(Enable, ObjId); 435e738a5d8SSebastian Kreutzer } 436e738a5d8SSebastian Kreutzer 437e738a5d8SSebastian Kreutzer XRayPatchingStatus mprotectAndPatchFunction(int32_t FuncId, int32_t ObjId, 438b3018603SNico Weber bool Enable) XRAY_NEVER_INSTRUMENT { 439b3018603SNico Weber XRaySledMap InstrMap; 440b3018603SNico Weber { 441b3018603SNico Weber SpinMutexLock Guard(&XRayInstrMapMutex); 442e738a5d8SSebastian Kreutzer if (ObjId < 0 || static_cast<uint32_t>(ObjId) >= 443e738a5d8SSebastian Kreutzer atomic_load(&XRayNumObjects, memory_order_acquire)) { 444e738a5d8SSebastian Kreutzer Report("Unable to patch function: invalid sled map index: %d\n", ObjId); 445e738a5d8SSebastian Kreutzer return XRayPatchingStatus::FAILED; 446e738a5d8SSebastian Kreutzer } 447e738a5d8SSebastian Kreutzer InstrMap = XRayInstrMaps[ObjId]; 448e738a5d8SSebastian Kreutzer } 449e738a5d8SSebastian Kreutzer 450e738a5d8SSebastian Kreutzer // Check if the corresponding DSO has been unloaded. 451e738a5d8SSebastian Kreutzer if (!InstrMap.Loaded) { 452e738a5d8SSebastian Kreutzer Report("Object is not loaded at index: %d\n", ObjId); 453e738a5d8SSebastian Kreutzer return XRayPatchingStatus::FAILED; 454b3018603SNico Weber } 455b3018603SNico Weber 456b3018603SNico Weber // FuncId must be a positive number, less than the number of functions 457b3018603SNico Weber // instrumented. 458b3018603SNico Weber if (FuncId <= 0 || static_cast<size_t>(FuncId) > InstrMap.Functions) { 459b3018603SNico Weber Report("Invalid function id provided: %d\n", FuncId); 460b3018603SNico Weber return XRayPatchingStatus::FAILED; 461b3018603SNico Weber } 462b3018603SNico Weber 463b3018603SNico Weber const size_t PageSize = flags()->xray_page_size_override > 0 464b3018603SNico Weber ? flags()->xray_page_size_override 465b3018603SNico Weber : GetPageSizeCached(); 466b3018603SNico Weber if ((PageSize == 0) || ((PageSize & (PageSize - 1)) != 0)) { 467bbba9d8cSDimitry Andric Report("Provided page size is not a power of two: %zu\n", PageSize); 468b3018603SNico Weber return XRayPatchingStatus::FAILED; 469b3018603SNico Weber } 470b3018603SNico Weber 471a1e7e401SKazuaki Ishizaki // Here we compute the minimum sled and maximum sled associated with a 472b3018603SNico Weber // particular function ID. 473e0a6561eSFangrui Song XRayFunctionSledIndex SledRange; 474e0a6561eSFangrui Song if (InstrMap.SledsIndex) { 475e0a6561eSFangrui Song SledRange = {InstrMap.SledsIndex[FuncId - 1].fromPCRelative(), 476e0a6561eSFangrui Song InstrMap.SledsIndex[FuncId - 1].Size}; 477e0a6561eSFangrui Song } else { 478e0a6561eSFangrui Song SledRange = findFunctionSleds(FuncId, InstrMap); 479e0a6561eSFangrui Song } 480b3018603SNico Weber auto *f = SledRange.Begin; 481e0a6561eSFangrui Song auto *e = SledRange.Begin + SledRange.Size; 4825771c985SFangrui Song auto *MinSled = f; 483e0a6561eSFangrui Song auto *MaxSled = e - 1; 484b3018603SNico Weber while (f != e) { 4855771c985SFangrui Song if (f->address() < MinSled->address()) 4865771c985SFangrui Song MinSled = f; 4875771c985SFangrui Song if (f->address() > MaxSled->address()) 4885771c985SFangrui Song MaxSled = f; 489b3018603SNico Weber ++f; 490b3018603SNico Weber } 491b3018603SNico Weber 492b3018603SNico Weber void *PageAlignedAddr = 4935771c985SFangrui Song reinterpret_cast<void *>(MinSled->address() & ~(PageSize - 1)); 494b3018603SNico Weber size_t MProtectLen = 4955771c985SFangrui Song (MaxSled->address() - reinterpret_cast<uptr>(PageAlignedAddr)) + 4965771c985SFangrui Song cSledLength; 497b3018603SNico Weber MProtectHelper Protector(PageAlignedAddr, MProtectLen, PageSize); 498b3018603SNico Weber if (Protector.MakeWriteable() == -1) { 499b3018603SNico Weber Report("Failed mprotect: %d\n", errno); 500b3018603SNico Weber return XRayPatchingStatus::FAILED; 501b3018603SNico Weber } 502e738a5d8SSebastian Kreutzer return patchFunction(FuncId, ObjId, Enable); 503b3018603SNico Weber } 504b3018603SNico Weber 505b3018603SNico Weber } // namespace 506b3018603SNico Weber 507b3018603SNico Weber } // namespace __xray 508b3018603SNico Weber 509b3018603SNico Weber using namespace __xray; 510b3018603SNico Weber 511b3018603SNico Weber // The following functions are declared `extern "C" {...}` in the header, hence 512b3018603SNico Weber // they're defined in the global namespace. 513b3018603SNico Weber 514b3018603SNico Weber int __xray_set_handler(void (*entry)(int32_t, 515b3018603SNico Weber XRayEntryType)) XRAY_NEVER_INSTRUMENT { 516e738a5d8SSebastian Kreutzer if (atomic_load(&XRayInitialized, memory_order_acquire)) { 517b3018603SNico Weber 518b3018603SNico Weber atomic_store(&__xray::XRayPatchedFunction, 519e738a5d8SSebastian Kreutzer reinterpret_cast<uintptr_t>(entry), memory_order_release); 520b3018603SNico Weber return 1; 521b3018603SNico Weber } 522b3018603SNico Weber return 0; 523b3018603SNico Weber } 524b3018603SNico Weber 525b3018603SNico Weber int __xray_set_customevent_handler(void (*entry)(void *, size_t)) 526b3018603SNico Weber XRAY_NEVER_INSTRUMENT { 527e738a5d8SSebastian Kreutzer if (atomic_load(&XRayInitialized, memory_order_acquire)) { 528b3018603SNico Weber atomic_store(&__xray::XRayPatchedCustomEvent, 529e738a5d8SSebastian Kreutzer reinterpret_cast<uintptr_t>(entry), memory_order_release); 530b3018603SNico Weber return 1; 531b3018603SNico Weber } 532b3018603SNico Weber return 0; 533b3018603SNico Weber } 534b3018603SNico Weber 5353fa3cb40SFangrui Song int __xray_set_typedevent_handler(void (*entry)(size_t, const void *, 5363fa3cb40SFangrui Song size_t)) XRAY_NEVER_INSTRUMENT { 537e738a5d8SSebastian Kreutzer if (atomic_load(&XRayInitialized, memory_order_acquire)) { 538b3018603SNico Weber atomic_store(&__xray::XRayPatchedTypedEvent, 539e738a5d8SSebastian Kreutzer reinterpret_cast<uintptr_t>(entry), memory_order_release); 540b3018603SNico Weber return 1; 541b3018603SNico Weber } 542b3018603SNico Weber return 0; 543b3018603SNico Weber } 544b3018603SNico Weber 545b3018603SNico Weber int __xray_remove_handler() XRAY_NEVER_INSTRUMENT { 546b3018603SNico Weber return __xray_set_handler(nullptr); 547b3018603SNico Weber } 548b3018603SNico Weber 549b3018603SNico Weber int __xray_remove_customevent_handler() XRAY_NEVER_INSTRUMENT { 550b3018603SNico Weber return __xray_set_customevent_handler(nullptr); 551b3018603SNico Weber } 552b3018603SNico Weber 553b3018603SNico Weber int __xray_remove_typedevent_handler() XRAY_NEVER_INSTRUMENT { 554b3018603SNico Weber return __xray_set_typedevent_handler(nullptr); 555b3018603SNico Weber } 556b3018603SNico Weber 557b3018603SNico Weber uint16_t __xray_register_event_type( 558b3018603SNico Weber const char *const event_type) XRAY_NEVER_INSTRUMENT { 559b3018603SNico Weber TypeDescriptorMapType::Handle h(&TypeDescriptorAddressMap, (uptr)event_type); 560b3018603SNico Weber if (h.created()) { 561b3018603SNico Weber h->type_id = atomic_fetch_add( 562b3018603SNico Weber &TypeEventDescriptorCounter, 1, memory_order_acq_rel); 563b3018603SNico Weber h->description_string_length = strnlen(event_type, 1024); 564b3018603SNico Weber } 565b3018603SNico Weber return h->type_id; 566b3018603SNico Weber } 567b3018603SNico Weber 568b3018603SNico Weber XRayPatchingStatus __xray_patch() XRAY_NEVER_INSTRUMENT { 569b3018603SNico Weber return controlPatching(true); 570b3018603SNico Weber } 571b3018603SNico Weber 572e738a5d8SSebastian Kreutzer XRayPatchingStatus __xray_patch_object(int32_t ObjId) XRAY_NEVER_INSTRUMENT { 573e738a5d8SSebastian Kreutzer return controlPatching(true, ObjId); 574e738a5d8SSebastian Kreutzer } 575e738a5d8SSebastian Kreutzer 576b3018603SNico Weber XRayPatchingStatus __xray_unpatch() XRAY_NEVER_INSTRUMENT { 577b3018603SNico Weber return controlPatching(false); 578b3018603SNico Weber } 579b3018603SNico Weber 580e738a5d8SSebastian Kreutzer XRayPatchingStatus __xray_unpatch_object(int32_t ObjId) XRAY_NEVER_INSTRUMENT { 581e738a5d8SSebastian Kreutzer return controlPatching(false, ObjId); 582e738a5d8SSebastian Kreutzer } 583e738a5d8SSebastian Kreutzer 584b3018603SNico Weber XRayPatchingStatus __xray_patch_function(int32_t FuncId) XRAY_NEVER_INSTRUMENT { 585e738a5d8SSebastian Kreutzer auto Ids = __xray::UnpackId(FuncId); 586e738a5d8SSebastian Kreutzer auto ObjId = Ids.first; 587e738a5d8SSebastian Kreutzer auto FnId = Ids.second; 588e738a5d8SSebastian Kreutzer return mprotectAndPatchFunction(FnId, ObjId, true); 589e738a5d8SSebastian Kreutzer } 590e738a5d8SSebastian Kreutzer 591e738a5d8SSebastian Kreutzer XRayPatchingStatus 592e738a5d8SSebastian Kreutzer __xray_patch_function_in_object(int32_t FuncId, 593e738a5d8SSebastian Kreutzer int32_t ObjId) XRAY_NEVER_INSTRUMENT { 594e738a5d8SSebastian Kreutzer return mprotectAndPatchFunction(FuncId, ObjId, true); 595b3018603SNico Weber } 596b3018603SNico Weber 597b3018603SNico Weber XRayPatchingStatus 598b3018603SNico Weber __xray_unpatch_function(int32_t FuncId) XRAY_NEVER_INSTRUMENT { 599e738a5d8SSebastian Kreutzer auto Ids = __xray::UnpackId(FuncId); 600e738a5d8SSebastian Kreutzer auto ObjId = Ids.first; 601e738a5d8SSebastian Kreutzer auto FnId = Ids.second; 602e738a5d8SSebastian Kreutzer return mprotectAndPatchFunction(FnId, ObjId, false); 603e738a5d8SSebastian Kreutzer } 604e738a5d8SSebastian Kreutzer 605e738a5d8SSebastian Kreutzer XRayPatchingStatus 606e738a5d8SSebastian Kreutzer __xray_unpatch_function_in_object(int32_t FuncId, 607e738a5d8SSebastian Kreutzer int32_t ObjId) XRAY_NEVER_INSTRUMENT { 608e738a5d8SSebastian Kreutzer return mprotectAndPatchFunction(FuncId, ObjId, false); 609b3018603SNico Weber } 610b3018603SNico Weber 611b3018603SNico Weber int __xray_set_handler_arg1(void (*entry)(int32_t, XRayEntryType, uint64_t)) { 612e738a5d8SSebastian Kreutzer if (!atomic_load(&XRayInitialized, memory_order_acquire)) 613b3018603SNico Weber return 0; 614b3018603SNico Weber 615b3018603SNico Weber // A relaxed write might not be visible even if the current thread gets 616b3018603SNico Weber // scheduled on a different CPU/NUMA node. We need to wait for everyone to 617b3018603SNico Weber // have this handler installed for consistency of collected data across CPUs. 618b3018603SNico Weber atomic_store(&XRayArgLogger, reinterpret_cast<uint64_t>(entry), 619b3018603SNico Weber memory_order_release); 620b3018603SNico Weber return 1; 621b3018603SNico Weber } 622b3018603SNico Weber 623b3018603SNico Weber int __xray_remove_handler_arg1() { return __xray_set_handler_arg1(nullptr); } 624b3018603SNico Weber 625e738a5d8SSebastian Kreutzer uintptr_t 626e738a5d8SSebastian Kreutzer __xray_function_address(int32_t CombinedFuncId) XRAY_NEVER_INSTRUMENT { 627e738a5d8SSebastian Kreutzer auto Ids = __xray::UnpackId(CombinedFuncId); 628e738a5d8SSebastian Kreutzer return __xray_function_address_in_object(Ids.second, Ids.first); 629e738a5d8SSebastian Kreutzer } 630e738a5d8SSebastian Kreutzer 631e738a5d8SSebastian Kreutzer uintptr_t __xray_function_address_in_object(int32_t FuncId, int32_t ObjId) 632e738a5d8SSebastian Kreutzer XRAY_NEVER_INSTRUMENT { 6337c7c8e0dSIan Levesque XRaySledMap InstrMap; 6347c7c8e0dSIan Levesque { 635b3018603SNico Weber SpinMutexLock Guard(&XRayInstrMapMutex); 636e738a5d8SSebastian Kreutzer auto count = atomic_load(&XRayNumObjects, memory_order_acquire); 637e738a5d8SSebastian Kreutzer if (ObjId < 0 || static_cast<uint32_t>(ObjId) >= count) { 638e738a5d8SSebastian Kreutzer Report("Unable to determine function address: invalid sled map index %d " 639e738a5d8SSebastian Kreutzer "(size is %d)\n", 640e738a5d8SSebastian Kreutzer ObjId, (int)count); 641e738a5d8SSebastian Kreutzer return 0; 642e738a5d8SSebastian Kreutzer } 643e738a5d8SSebastian Kreutzer InstrMap = XRayInstrMaps[ObjId]; 6447c7c8e0dSIan Levesque } 6457c7c8e0dSIan Levesque 6467c7c8e0dSIan Levesque if (FuncId <= 0 || static_cast<size_t>(FuncId) > InstrMap.Functions) 647b3018603SNico Weber return 0; 648e0a6561eSFangrui Song const XRaySledEntry *Sled = 649e0a6561eSFangrui Song InstrMap.SledsIndex ? InstrMap.SledsIndex[FuncId - 1].fromPCRelative() 6507c7c8e0dSIan Levesque : findFunctionSleds(FuncId, InstrMap).Begin; 6517c7c8e0dSIan Levesque return Sled->function() 652b3018603SNico Weber // On PPC, function entries are always aligned to 16 bytes. The beginning of a 653b3018603SNico Weber // sled might be a local entry, which is always +8 based on the global entry. 654b3018603SNico Weber // Always return the global entry. 655b3018603SNico Weber #ifdef __PPC__ 656b3018603SNico Weber & ~0xf 657b3018603SNico Weber #endif 658b3018603SNico Weber ; 659b3018603SNico Weber } 660b3018603SNico Weber 661b3018603SNico Weber size_t __xray_max_function_id() XRAY_NEVER_INSTRUMENT { 662e738a5d8SSebastian Kreutzer return __xray_max_function_id_in_object(0); 663e738a5d8SSebastian Kreutzer } 664e738a5d8SSebastian Kreutzer 665e738a5d8SSebastian Kreutzer size_t __xray_max_function_id_in_object(int32_t ObjId) XRAY_NEVER_INSTRUMENT { 666b3018603SNico Weber SpinMutexLock Guard(&XRayInstrMapMutex); 667e738a5d8SSebastian Kreutzer if (ObjId < 0 || static_cast<uint32_t>(ObjId) >= 668e738a5d8SSebastian Kreutzer atomic_load(&XRayNumObjects, memory_order_acquire)) 669e738a5d8SSebastian Kreutzer return 0; 670e738a5d8SSebastian Kreutzer return XRayInstrMaps[ObjId].Functions; 671e738a5d8SSebastian Kreutzer } 672e738a5d8SSebastian Kreutzer 673e738a5d8SSebastian Kreutzer size_t __xray_num_objects() XRAY_NEVER_INSTRUMENT { 674e738a5d8SSebastian Kreutzer SpinMutexLock Guard(&XRayInstrMapMutex); 675e738a5d8SSebastian Kreutzer return atomic_load(&XRayNumObjects, memory_order_acquire); 676e738a5d8SSebastian Kreutzer } 677e738a5d8SSebastian Kreutzer 678e738a5d8SSebastian Kreutzer int32_t __xray_unpack_function_id(int32_t PackedId) { 679e738a5d8SSebastian Kreutzer return __xray::UnpackId(PackedId).second; 680e738a5d8SSebastian Kreutzer } 681e738a5d8SSebastian Kreutzer 682e738a5d8SSebastian Kreutzer int32_t __xray_unpack_object_id(int32_t PackedId) { 683e738a5d8SSebastian Kreutzer return __xray::UnpackId(PackedId).first; 684e738a5d8SSebastian Kreutzer } 685e738a5d8SSebastian Kreutzer 686e738a5d8SSebastian Kreutzer int32_t __xray_pack_id(int32_t FuncId, int32_t ObjId) { 687e738a5d8SSebastian Kreutzer return __xray::MakePackedId(FuncId, ObjId); 688b3018603SNico Weber } 689