1 //===-- xray_interface.cpp --------------------------------------*- C++ -*-===// 2 // 3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4 // See https://llvm.org/LICENSE.txt for license information. 5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6 // 7 //===----------------------------------------------------------------------===// 8 // 9 // This file is a part of XRay, a dynamic runtime instrumentation system. 10 // 11 // Implementation of the API functions. 12 // 13 //===----------------------------------------------------------------------===// 14 15 #include "xray_interface_internal.h" 16 17 #include <cinttypes> 18 #include <cstdio> 19 #include <errno.h> 20 #include <limits> 21 #include <string.h> 22 #include <sys/mman.h> 23 24 #if SANITIZER_FUCHSIA 25 #include <zircon/process.h> 26 #include <zircon/sanitizer.h> 27 #include <zircon/status.h> 28 #include <zircon/syscalls.h> 29 #endif 30 31 #include "sanitizer_common/sanitizer_addrhashmap.h" 32 #include "sanitizer_common/sanitizer_common.h" 33 34 #include "xray_defs.h" 35 #include "xray_flags.h" 36 37 extern __sanitizer::SpinMutex XRayInstrMapMutex; 38 extern __sanitizer::atomic_uint8_t XRayInitialized; 39 extern __xray::XRaySledMap XRayInstrMap; 40 41 namespace __xray { 42 43 #if defined(__x86_64__) 44 static const int16_t cSledLength = 12; 45 #elif defined(__aarch64__) 46 static const int16_t cSledLength = 32; 47 #elif defined(__arm__) 48 static const int16_t cSledLength = 28; 49 #elif SANITIZER_MIPS32 50 static const int16_t cSledLength = 48; 51 #elif SANITIZER_MIPS64 52 static const int16_t cSledLength = 64; 53 #elif defined(__powerpc64__) 54 static const int16_t cSledLength = 8; 55 #else 56 #error "Unsupported CPU Architecture" 57 #endif /* CPU architecture */ 58 59 // This is the function to call when we encounter the entry or exit sleds. 60 atomic_uintptr_t XRayPatchedFunction{0}; 61 62 // This is the function to call from the arg1-enabled sleds/trampolines. 63 atomic_uintptr_t XRayArgLogger{0}; 64 65 // This is the function to call when we encounter a custom event log call. 66 atomic_uintptr_t XRayPatchedCustomEvent{0}; 67 68 // This is the function to call when we encounter a typed event log call. 69 atomic_uintptr_t XRayPatchedTypedEvent{0}; 70 71 // This is the global status to determine whether we are currently 72 // patching/unpatching. 73 atomic_uint8_t XRayPatching{0}; 74 75 struct TypeDescription { 76 uint32_t type_id; 77 std::size_t description_string_length; 78 }; 79 80 using TypeDescriptorMapType = AddrHashMap<TypeDescription, 11>; 81 // An address map from immutable descriptors to type ids. 82 TypeDescriptorMapType TypeDescriptorAddressMap{}; 83 84 atomic_uint32_t TypeEventDescriptorCounter{0}; 85 86 // MProtectHelper is an RAII wrapper for calls to mprotect(...) that will 87 // undo any successful mprotect(...) changes. This is used to make a page 88 // writeable and executable, and upon destruction if it was successful in 89 // doing so returns the page into a read-only and executable page. 90 // 91 // This is only used specifically for runtime-patching of the XRay 92 // instrumentation points. This assumes that the executable pages are 93 // originally read-and-execute only. 94 class MProtectHelper { 95 void *PageAlignedAddr; 96 std::size_t MProtectLen; 97 bool MustCleanup; 98 99 public: 100 explicit MProtectHelper(void *PageAlignedAddr, 101 std::size_t MProtectLen, 102 std::size_t PageSize) XRAY_NEVER_INSTRUMENT 103 : PageAlignedAddr(PageAlignedAddr), 104 MProtectLen(MProtectLen), 105 MustCleanup(false) { 106 #if SANITIZER_FUCHSIA 107 MProtectLen = RoundUpTo(MProtectLen, PageSize); 108 #endif 109 } 110 111 int MakeWriteable() XRAY_NEVER_INSTRUMENT { 112 #if SANITIZER_FUCHSIA 113 auto R = __sanitizer_change_code_protection( 114 reinterpret_cast<uintptr_t>(PageAlignedAddr), MProtectLen, true); 115 if (R != ZX_OK) { 116 Report("XRay: cannot change code protection: %s\n", 117 _zx_status_get_string(R)); 118 return -1; 119 } 120 MustCleanup = true; 121 return 0; 122 #else 123 auto R = mprotect(PageAlignedAddr, MProtectLen, 124 PROT_READ | PROT_WRITE | PROT_EXEC); 125 if (R != -1) 126 MustCleanup = true; 127 return R; 128 #endif 129 } 130 131 ~MProtectHelper() XRAY_NEVER_INSTRUMENT { 132 if (MustCleanup) { 133 #if SANITIZER_FUCHSIA 134 auto R = __sanitizer_change_code_protection( 135 reinterpret_cast<uintptr_t>(PageAlignedAddr), MProtectLen, false); 136 if (R != ZX_OK) { 137 Report("XRay: cannot change code protection: %s\n", 138 _zx_status_get_string(R)); 139 } 140 #else 141 mprotect(PageAlignedAddr, MProtectLen, PROT_READ | PROT_EXEC); 142 #endif 143 } 144 } 145 }; 146 147 namespace { 148 149 bool patchSled(const XRaySledEntry &Sled, bool Enable, 150 int32_t FuncId) XRAY_NEVER_INSTRUMENT { 151 bool Success = false; 152 switch (Sled.Kind) { 153 case XRayEntryType::ENTRY: 154 Success = patchFunctionEntry(Enable, FuncId, Sled, __xray_FunctionEntry); 155 break; 156 case XRayEntryType::EXIT: 157 Success = patchFunctionExit(Enable, FuncId, Sled); 158 break; 159 case XRayEntryType::TAIL: 160 Success = patchFunctionTailExit(Enable, FuncId, Sled); 161 break; 162 case XRayEntryType::LOG_ARGS_ENTRY: 163 Success = patchFunctionEntry(Enable, FuncId, Sled, __xray_ArgLoggerEntry); 164 break; 165 case XRayEntryType::CUSTOM_EVENT: 166 Success = patchCustomEvent(Enable, FuncId, Sled); 167 break; 168 case XRayEntryType::TYPED_EVENT: 169 Success = patchTypedEvent(Enable, FuncId, Sled); 170 break; 171 default: 172 Report("Unsupported sled kind '%" PRIu64 "' @%04x\n", Sled.Address, 173 int(Sled.Kind)); 174 return false; 175 } 176 return Success; 177 } 178 179 const XRayFunctionSledIndex 180 findFunctionSleds(int32_t FuncId, 181 const XRaySledMap &InstrMap) XRAY_NEVER_INSTRUMENT { 182 int32_t CurFn = 0; 183 uint64_t LastFnAddr = 0; 184 XRayFunctionSledIndex Index = {nullptr, nullptr}; 185 186 for (std::size_t I = 0; I < InstrMap.Entries && CurFn <= FuncId; I++) { 187 const auto &Sled = InstrMap.Sleds[I]; 188 const auto Function = Sled.function(); 189 if (Function != LastFnAddr) { 190 CurFn++; 191 LastFnAddr = Function; 192 } 193 194 if (CurFn == FuncId) { 195 if (Index.Begin == nullptr) 196 Index.Begin = &Sled; 197 Index.End = &Sled; 198 } 199 } 200 201 Index.End += 1; 202 203 return Index; 204 } 205 206 XRayPatchingStatus patchFunction(int32_t FuncId, 207 bool Enable) XRAY_NEVER_INSTRUMENT { 208 if (!atomic_load(&XRayInitialized, 209 memory_order_acquire)) 210 return XRayPatchingStatus::NOT_INITIALIZED; // Not initialized. 211 212 uint8_t NotPatching = false; 213 if (!atomic_compare_exchange_strong( 214 &XRayPatching, &NotPatching, true, memory_order_acq_rel)) 215 return XRayPatchingStatus::ONGOING; // Already patching. 216 217 // Next, we look for the function index. 218 XRaySledMap InstrMap; 219 { 220 SpinMutexLock Guard(&XRayInstrMapMutex); 221 InstrMap = XRayInstrMap; 222 } 223 224 // If we don't have an index, we can't patch individual functions. 225 if (InstrMap.Functions == 0) 226 return XRayPatchingStatus::NOT_INITIALIZED; 227 228 // FuncId must be a positive number, less than the number of functions 229 // instrumented. 230 if (FuncId <= 0 || static_cast<size_t>(FuncId) > InstrMap.Functions) { 231 Report("Invalid function id provided: %d\n", FuncId); 232 return XRayPatchingStatus::FAILED; 233 } 234 235 // Now we patch ths sleds for this specific function. 236 auto SledRange = InstrMap.SledsIndex ? InstrMap.SledsIndex[FuncId - 1] 237 : findFunctionSleds(FuncId, InstrMap); 238 auto *f = SledRange.Begin; 239 auto *e = SledRange.End; 240 bool SucceedOnce = false; 241 while (f != e) 242 SucceedOnce |= patchSled(*f++, Enable, FuncId); 243 244 atomic_store(&XRayPatching, false, 245 memory_order_release); 246 247 if (!SucceedOnce) { 248 Report("Failed patching any sled for function '%d'.", FuncId); 249 return XRayPatchingStatus::FAILED; 250 } 251 252 return XRayPatchingStatus::SUCCESS; 253 } 254 255 // controlPatching implements the common internals of the patching/unpatching 256 // implementation. |Enable| defines whether we're enabling or disabling the 257 // runtime XRay instrumentation. 258 XRayPatchingStatus controlPatching(bool Enable) XRAY_NEVER_INSTRUMENT { 259 if (!atomic_load(&XRayInitialized, 260 memory_order_acquire)) 261 return XRayPatchingStatus::NOT_INITIALIZED; // Not initialized. 262 263 uint8_t NotPatching = false; 264 if (!atomic_compare_exchange_strong( 265 &XRayPatching, &NotPatching, true, memory_order_acq_rel)) 266 return XRayPatchingStatus::ONGOING; // Already patching. 267 268 uint8_t PatchingSuccess = false; 269 auto XRayPatchingStatusResetter = 270 at_scope_exit([&PatchingSuccess] { 271 if (!PatchingSuccess) 272 atomic_store(&XRayPatching, false, 273 memory_order_release); 274 }); 275 276 XRaySledMap InstrMap; 277 { 278 SpinMutexLock Guard(&XRayInstrMapMutex); 279 InstrMap = XRayInstrMap; 280 } 281 if (InstrMap.Entries == 0) 282 return XRayPatchingStatus::NOT_INITIALIZED; 283 284 uint32_t FuncId = 1; 285 uint64_t CurFun = 0; 286 287 // First we want to find the bounds for which we have instrumentation points, 288 // and try to get as few calls to mprotect(...) as possible. We're assuming 289 // that all the sleds for the instrumentation map are contiguous as a single 290 // set of pages. When we do support dynamic shared object instrumentation, 291 // we'll need to do this for each set of page load offsets per DSO loaded. For 292 // now we're assuming we can mprotect the whole section of text between the 293 // minimum sled address and the maximum sled address (+ the largest sled 294 // size). 295 auto *MinSled = &InstrMap.Sleds[0]; 296 auto *MaxSled = &InstrMap.Sleds[InstrMap.Entries - 1]; 297 for (std::size_t I = 0; I < InstrMap.Entries; I++) { 298 const auto &Sled = InstrMap.Sleds[I]; 299 if (Sled.address() < MinSled->address()) 300 MinSled = &Sled; 301 if (Sled.address() > MaxSled->address()) 302 MaxSled = &Sled; 303 } 304 305 const size_t PageSize = flags()->xray_page_size_override > 0 306 ? flags()->xray_page_size_override 307 : GetPageSizeCached(); 308 if ((PageSize == 0) || ((PageSize & (PageSize - 1)) != 0)) { 309 Report("System page size is not a power of two: %zu\n", PageSize); 310 return XRayPatchingStatus::FAILED; 311 } 312 313 void *PageAlignedAddr = 314 reinterpret_cast<void *>(MinSled->address() & ~(PageSize - 1)); 315 size_t MProtectLen = 316 (MaxSled->address() - reinterpret_cast<uptr>(PageAlignedAddr)) + 317 cSledLength; 318 MProtectHelper Protector(PageAlignedAddr, MProtectLen, PageSize); 319 if (Protector.MakeWriteable() == -1) { 320 Report("Failed mprotect: %d\n", errno); 321 return XRayPatchingStatus::FAILED; 322 } 323 324 for (std::size_t I = 0; I < InstrMap.Entries; ++I) { 325 auto &Sled = InstrMap.Sleds[I]; 326 auto F = Sled.function(); 327 if (CurFun == 0) 328 CurFun = F; 329 if (F != CurFun) { 330 ++FuncId; 331 CurFun = F; 332 } 333 patchSled(Sled, Enable, FuncId); 334 } 335 atomic_store(&XRayPatching, false, 336 memory_order_release); 337 PatchingSuccess = true; 338 return XRayPatchingStatus::SUCCESS; 339 } 340 341 XRayPatchingStatus mprotectAndPatchFunction(int32_t FuncId, 342 bool Enable) XRAY_NEVER_INSTRUMENT { 343 XRaySledMap InstrMap; 344 { 345 SpinMutexLock Guard(&XRayInstrMapMutex); 346 InstrMap = XRayInstrMap; 347 } 348 349 // FuncId must be a positive number, less than the number of functions 350 // instrumented. 351 if (FuncId <= 0 || static_cast<size_t>(FuncId) > InstrMap.Functions) { 352 Report("Invalid function id provided: %d\n", FuncId); 353 return XRayPatchingStatus::FAILED; 354 } 355 356 const size_t PageSize = flags()->xray_page_size_override > 0 357 ? flags()->xray_page_size_override 358 : GetPageSizeCached(); 359 if ((PageSize == 0) || ((PageSize & (PageSize - 1)) != 0)) { 360 Report("Provided page size is not a power of two: %zu\n", PageSize); 361 return XRayPatchingStatus::FAILED; 362 } 363 364 // Here we compute the minimum sled and maximum sled associated with a 365 // particular function ID. 366 auto SledRange = InstrMap.SledsIndex ? InstrMap.SledsIndex[FuncId - 1] 367 : findFunctionSleds(FuncId, InstrMap); 368 auto *f = SledRange.Begin; 369 auto *e = SledRange.End; 370 auto *MinSled = f; 371 auto *MaxSled = (SledRange.End - 1); 372 while (f != e) { 373 if (f->address() < MinSled->address()) 374 MinSled = f; 375 if (f->address() > MaxSled->address()) 376 MaxSled = f; 377 ++f; 378 } 379 380 void *PageAlignedAddr = 381 reinterpret_cast<void *>(MinSled->address() & ~(PageSize - 1)); 382 size_t MProtectLen = 383 (MaxSled->address() - reinterpret_cast<uptr>(PageAlignedAddr)) + 384 cSledLength; 385 MProtectHelper Protector(PageAlignedAddr, MProtectLen, PageSize); 386 if (Protector.MakeWriteable() == -1) { 387 Report("Failed mprotect: %d\n", errno); 388 return XRayPatchingStatus::FAILED; 389 } 390 return patchFunction(FuncId, Enable); 391 } 392 393 } // namespace 394 395 } // namespace __xray 396 397 using namespace __xray; 398 399 // The following functions are declared `extern "C" {...}` in the header, hence 400 // they're defined in the global namespace. 401 402 int __xray_set_handler(void (*entry)(int32_t, 403 XRayEntryType)) XRAY_NEVER_INSTRUMENT { 404 if (atomic_load(&XRayInitialized, 405 memory_order_acquire)) { 406 407 atomic_store(&__xray::XRayPatchedFunction, 408 reinterpret_cast<uintptr_t>(entry), 409 memory_order_release); 410 return 1; 411 } 412 return 0; 413 } 414 415 int __xray_set_customevent_handler(void (*entry)(void *, size_t)) 416 XRAY_NEVER_INSTRUMENT { 417 if (atomic_load(&XRayInitialized, 418 memory_order_acquire)) { 419 atomic_store(&__xray::XRayPatchedCustomEvent, 420 reinterpret_cast<uintptr_t>(entry), 421 memory_order_release); 422 return 1; 423 } 424 return 0; 425 } 426 427 int __xray_set_typedevent_handler(void (*entry)( 428 uint16_t, const void *, size_t)) XRAY_NEVER_INSTRUMENT { 429 if (atomic_load(&XRayInitialized, 430 memory_order_acquire)) { 431 atomic_store(&__xray::XRayPatchedTypedEvent, 432 reinterpret_cast<uintptr_t>(entry), 433 memory_order_release); 434 return 1; 435 } 436 return 0; 437 } 438 439 int __xray_remove_handler() XRAY_NEVER_INSTRUMENT { 440 return __xray_set_handler(nullptr); 441 } 442 443 int __xray_remove_customevent_handler() XRAY_NEVER_INSTRUMENT { 444 return __xray_set_customevent_handler(nullptr); 445 } 446 447 int __xray_remove_typedevent_handler() XRAY_NEVER_INSTRUMENT { 448 return __xray_set_typedevent_handler(nullptr); 449 } 450 451 uint16_t __xray_register_event_type( 452 const char *const event_type) XRAY_NEVER_INSTRUMENT { 453 TypeDescriptorMapType::Handle h(&TypeDescriptorAddressMap, (uptr)event_type); 454 if (h.created()) { 455 h->type_id = atomic_fetch_add( 456 &TypeEventDescriptorCounter, 1, memory_order_acq_rel); 457 h->description_string_length = strnlen(event_type, 1024); 458 } 459 return h->type_id; 460 } 461 462 XRayPatchingStatus __xray_patch() XRAY_NEVER_INSTRUMENT { 463 return controlPatching(true); 464 } 465 466 XRayPatchingStatus __xray_unpatch() XRAY_NEVER_INSTRUMENT { 467 return controlPatching(false); 468 } 469 470 XRayPatchingStatus __xray_patch_function(int32_t FuncId) XRAY_NEVER_INSTRUMENT { 471 return mprotectAndPatchFunction(FuncId, true); 472 } 473 474 XRayPatchingStatus 475 __xray_unpatch_function(int32_t FuncId) XRAY_NEVER_INSTRUMENT { 476 return mprotectAndPatchFunction(FuncId, false); 477 } 478 479 int __xray_set_handler_arg1(void (*entry)(int32_t, XRayEntryType, uint64_t)) { 480 if (!atomic_load(&XRayInitialized, 481 memory_order_acquire)) 482 return 0; 483 484 // A relaxed write might not be visible even if the current thread gets 485 // scheduled on a different CPU/NUMA node. We need to wait for everyone to 486 // have this handler installed for consistency of collected data across CPUs. 487 atomic_store(&XRayArgLogger, reinterpret_cast<uint64_t>(entry), 488 memory_order_release); 489 return 1; 490 } 491 492 int __xray_remove_handler_arg1() { return __xray_set_handler_arg1(nullptr); } 493 494 uintptr_t __xray_function_address(int32_t FuncId) XRAY_NEVER_INSTRUMENT { 495 XRaySledMap InstrMap; 496 { 497 SpinMutexLock Guard(&XRayInstrMapMutex); 498 InstrMap = XRayInstrMap; 499 } 500 501 if (FuncId <= 0 || static_cast<size_t>(FuncId) > InstrMap.Functions) 502 return 0; 503 const XRaySledEntry *Sled = InstrMap.SledsIndex 504 ? InstrMap.SledsIndex[FuncId - 1].Begin 505 : findFunctionSleds(FuncId, InstrMap).Begin; 506 return Sled->function() 507 // On PPC, function entries are always aligned to 16 bytes. The beginning of a 508 // sled might be a local entry, which is always +8 based on the global entry. 509 // Always return the global entry. 510 #ifdef __PPC__ 511 & ~0xf 512 #endif 513 ; 514 } 515 516 size_t __xray_max_function_id() XRAY_NEVER_INSTRUMENT { 517 SpinMutexLock Guard(&XRayInstrMapMutex); 518 return XRayInstrMap.Functions; 519 } 520