Path: blob/main/contrib/llvm-project/compiler-rt/lib/xray/xray_interface.cpp
35265 views
//===-- xray_interface.cpp --------------------------------------*- C++ -*-===//1//2// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.3// See https://llvm.org/LICENSE.txt for license information.4// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception5//6//===----------------------------------------------------------------------===//7//8// This file is a part of XRay, a dynamic runtime instrumentation system.9//10// Implementation of the API functions.11//12//===----------------------------------------------------------------------===//1314#include "xray_interface_internal.h"1516#include <cinttypes>17#include <cstdio>18#include <errno.h>19#include <limits>20#include <string.h>21#include <sys/mman.h>2223#if SANITIZER_FUCHSIA24#include <zircon/process.h>25#include <zircon/sanitizer.h>26#include <zircon/status.h>27#include <zircon/syscalls.h>28#endif2930#include "sanitizer_common/sanitizer_addrhashmap.h"31#include "sanitizer_common/sanitizer_common.h"3233#include "xray_defs.h"34#include "xray_flags.h"3536extern __sanitizer::SpinMutex XRayInstrMapMutex;37extern __sanitizer::atomic_uint8_t XRayInitialized;38extern __xray::XRaySledMap XRayInstrMap;3940namespace __xray {4142#if defined(__x86_64__)43static const int16_t cSledLength = 12;44#elif defined(__aarch64__)45static const int16_t cSledLength = 32;46#elif defined(__arm__)47static const int16_t cSledLength = 28;48#elif SANITIZER_LOONGARCH6449static const int16_t cSledLength = 48;50#elif SANITIZER_MIPS3251static const int16_t cSledLength = 48;52#elif SANITIZER_MIPS6453static const int16_t cSledLength = 64;54#elif defined(__powerpc64__)55static const int16_t cSledLength = 8;56#elif defined(__hexagon__)57static const int16_t cSledLength = 20;58#else59#error "Unsupported CPU Architecture"60#endif /* CPU architecture */6162// This is the function to call when we encounter the entry or exit sleds.63atomic_uintptr_t XRayPatchedFunction{0};6465// This is the function to call from the arg1-enabled sleds/trampolines.66atomic_uintptr_t XRayArgLogger{0};6768// This is the function to call when we encounter a custom event log call.69atomic_uintptr_t XRayPatchedCustomEvent{0};7071// This is the function to call when we encounter a typed event log call.72atomic_uintptr_t XRayPatchedTypedEvent{0};7374// This is the global status to determine whether we are currently75// patching/unpatching.76atomic_uint8_t XRayPatching{0};7778struct TypeDescription {79uint32_t type_id;80std::size_t description_string_length;81};8283using TypeDescriptorMapType = AddrHashMap<TypeDescription, 11>;84// An address map from immutable descriptors to type ids.85TypeDescriptorMapType TypeDescriptorAddressMap{};8687atomic_uint32_t TypeEventDescriptorCounter{0};8889// MProtectHelper is an RAII wrapper for calls to mprotect(...) that will90// undo any successful mprotect(...) changes. This is used to make a page91// writeable and executable, and upon destruction if it was successful in92// doing so returns the page into a read-only and executable page.93//94// This is only used specifically for runtime-patching of the XRay95// instrumentation points. This assumes that the executable pages are96// originally read-and-execute only.97class MProtectHelper {98void *PageAlignedAddr;99std::size_t MProtectLen;100bool MustCleanup;101102public:103explicit MProtectHelper(void *PageAlignedAddr,104std::size_t MProtectLen,105std::size_t PageSize) XRAY_NEVER_INSTRUMENT106: PageAlignedAddr(PageAlignedAddr),107MProtectLen(MProtectLen),108MustCleanup(false) {109#if SANITIZER_FUCHSIA110MProtectLen = RoundUpTo(MProtectLen, PageSize);111#endif112}113114int MakeWriteable() XRAY_NEVER_INSTRUMENT {115#if SANITIZER_FUCHSIA116auto R = __sanitizer_change_code_protection(117reinterpret_cast<uintptr_t>(PageAlignedAddr), MProtectLen, true);118if (R != ZX_OK) {119Report("XRay: cannot change code protection: %s\n",120_zx_status_get_string(R));121return -1;122}123MustCleanup = true;124return 0;125#else126auto R = mprotect(PageAlignedAddr, MProtectLen,127PROT_READ | PROT_WRITE | PROT_EXEC);128if (R != -1)129MustCleanup = true;130return R;131#endif132}133134~MProtectHelper() XRAY_NEVER_INSTRUMENT {135if (MustCleanup) {136#if SANITIZER_FUCHSIA137auto R = __sanitizer_change_code_protection(138reinterpret_cast<uintptr_t>(PageAlignedAddr), MProtectLen, false);139if (R != ZX_OK) {140Report("XRay: cannot change code protection: %s\n",141_zx_status_get_string(R));142}143#else144mprotect(PageAlignedAddr, MProtectLen, PROT_READ | PROT_EXEC);145#endif146}147}148};149150namespace {151152bool patchSled(const XRaySledEntry &Sled, bool Enable,153int32_t FuncId) XRAY_NEVER_INSTRUMENT {154bool Success = false;155switch (Sled.Kind) {156case XRayEntryType::ENTRY:157Success = patchFunctionEntry(Enable, FuncId, Sled, __xray_FunctionEntry);158break;159case XRayEntryType::EXIT:160Success = patchFunctionExit(Enable, FuncId, Sled);161break;162case XRayEntryType::TAIL:163Success = patchFunctionTailExit(Enable, FuncId, Sled);164break;165case XRayEntryType::LOG_ARGS_ENTRY:166Success = patchFunctionEntry(Enable, FuncId, Sled, __xray_ArgLoggerEntry);167break;168case XRayEntryType::CUSTOM_EVENT:169Success = patchCustomEvent(Enable, FuncId, Sled);170break;171case XRayEntryType::TYPED_EVENT:172Success = patchTypedEvent(Enable, FuncId, Sled);173break;174default:175Report("Unsupported sled kind '%" PRIu64 "' @%04x\n", Sled.Address,176int(Sled.Kind));177return false;178}179return Success;180}181182const XRayFunctionSledIndex183findFunctionSleds(int32_t FuncId,184const XRaySledMap &InstrMap) XRAY_NEVER_INSTRUMENT {185int32_t CurFn = 0;186uint64_t LastFnAddr = 0;187XRayFunctionSledIndex Index = {nullptr, 0};188189for (std::size_t I = 0; I < InstrMap.Entries && CurFn <= FuncId; I++) {190const auto &Sled = InstrMap.Sleds[I];191const auto Function = Sled.function();192if (Function != LastFnAddr) {193CurFn++;194LastFnAddr = Function;195}196197if (CurFn == FuncId) {198if (Index.Begin == nullptr)199Index.Begin = &Sled;200Index.Size = &Sled - Index.Begin + 1;201}202}203204return Index;205}206207XRayPatchingStatus patchFunction(int32_t FuncId,208bool Enable) XRAY_NEVER_INSTRUMENT {209if (!atomic_load(&XRayInitialized,210memory_order_acquire))211return XRayPatchingStatus::NOT_INITIALIZED; // Not initialized.212213uint8_t NotPatching = false;214if (!atomic_compare_exchange_strong(215&XRayPatching, &NotPatching, true, memory_order_acq_rel))216return XRayPatchingStatus::ONGOING; // Already patching.217218// Next, we look for the function index.219XRaySledMap InstrMap;220{221SpinMutexLock Guard(&XRayInstrMapMutex);222InstrMap = XRayInstrMap;223}224225// If we don't have an index, we can't patch individual functions.226if (InstrMap.Functions == 0)227return XRayPatchingStatus::NOT_INITIALIZED;228229// FuncId must be a positive number, less than the number of functions230// instrumented.231if (FuncId <= 0 || static_cast<size_t>(FuncId) > InstrMap.Functions) {232Report("Invalid function id provided: %d\n", FuncId);233return XRayPatchingStatus::FAILED;234}235236// Now we patch ths sleds for this specific function.237XRayFunctionSledIndex SledRange;238if (InstrMap.SledsIndex) {239SledRange = {InstrMap.SledsIndex[FuncId - 1].fromPCRelative(),240InstrMap.SledsIndex[FuncId - 1].Size};241} else {242SledRange = findFunctionSleds(FuncId, InstrMap);243}244auto *f = SledRange.Begin;245bool SucceedOnce = false;246for (size_t i = 0; i != SledRange.Size; ++i)247SucceedOnce |= patchSled(f[i], Enable, FuncId);248249atomic_store(&XRayPatching, false,250memory_order_release);251252if (!SucceedOnce) {253Report("Failed patching any sled for function '%d'.", FuncId);254return XRayPatchingStatus::FAILED;255}256257return XRayPatchingStatus::SUCCESS;258}259260// controlPatching implements the common internals of the patching/unpatching261// implementation. |Enable| defines whether we're enabling or disabling the262// runtime XRay instrumentation.263XRayPatchingStatus controlPatching(bool Enable) XRAY_NEVER_INSTRUMENT {264if (!atomic_load(&XRayInitialized,265memory_order_acquire))266return XRayPatchingStatus::NOT_INITIALIZED; // Not initialized.267268uint8_t NotPatching = false;269if (!atomic_compare_exchange_strong(270&XRayPatching, &NotPatching, true, memory_order_acq_rel))271return XRayPatchingStatus::ONGOING; // Already patching.272273uint8_t PatchingSuccess = false;274auto XRayPatchingStatusResetter =275at_scope_exit([&PatchingSuccess] {276if (!PatchingSuccess)277atomic_store(&XRayPatching, false,278memory_order_release);279});280281XRaySledMap InstrMap;282{283SpinMutexLock Guard(&XRayInstrMapMutex);284InstrMap = XRayInstrMap;285}286if (InstrMap.Entries == 0)287return XRayPatchingStatus::NOT_INITIALIZED;288289uint32_t FuncId = 1;290uint64_t CurFun = 0;291292// First we want to find the bounds for which we have instrumentation points,293// and try to get as few calls to mprotect(...) as possible. We're assuming294// that all the sleds for the instrumentation map are contiguous as a single295// set of pages. When we do support dynamic shared object instrumentation,296// we'll need to do this for each set of page load offsets per DSO loaded. For297// now we're assuming we can mprotect the whole section of text between the298// minimum sled address and the maximum sled address (+ the largest sled299// size).300auto *MinSled = &InstrMap.Sleds[0];301auto *MaxSled = &InstrMap.Sleds[InstrMap.Entries - 1];302for (std::size_t I = 0; I < InstrMap.Entries; I++) {303const auto &Sled = InstrMap.Sleds[I];304if (Sled.address() < MinSled->address())305MinSled = &Sled;306if (Sled.address() > MaxSled->address())307MaxSled = &Sled;308}309310const size_t PageSize = flags()->xray_page_size_override > 0311? flags()->xray_page_size_override312: GetPageSizeCached();313if ((PageSize == 0) || ((PageSize & (PageSize - 1)) != 0)) {314Report("System page size is not a power of two: %zu\n", PageSize);315return XRayPatchingStatus::FAILED;316}317318void *PageAlignedAddr =319reinterpret_cast<void *>(MinSled->address() & ~(PageSize - 1));320size_t MProtectLen =321(MaxSled->address() - reinterpret_cast<uptr>(PageAlignedAddr)) +322cSledLength;323MProtectHelper Protector(PageAlignedAddr, MProtectLen, PageSize);324if (Protector.MakeWriteable() == -1) {325Report("Failed mprotect: %d\n", errno);326return XRayPatchingStatus::FAILED;327}328329for (std::size_t I = 0; I < InstrMap.Entries; ++I) {330auto &Sled = InstrMap.Sleds[I];331auto F = Sled.function();332if (CurFun == 0)333CurFun = F;334if (F != CurFun) {335++FuncId;336CurFun = F;337}338patchSled(Sled, Enable, FuncId);339}340atomic_store(&XRayPatching, false,341memory_order_release);342PatchingSuccess = true;343return XRayPatchingStatus::SUCCESS;344}345346XRayPatchingStatus mprotectAndPatchFunction(int32_t FuncId,347bool Enable) XRAY_NEVER_INSTRUMENT {348XRaySledMap InstrMap;349{350SpinMutexLock Guard(&XRayInstrMapMutex);351InstrMap = XRayInstrMap;352}353354// FuncId must be a positive number, less than the number of functions355// instrumented.356if (FuncId <= 0 || static_cast<size_t>(FuncId) > InstrMap.Functions) {357Report("Invalid function id provided: %d\n", FuncId);358return XRayPatchingStatus::FAILED;359}360361const size_t PageSize = flags()->xray_page_size_override > 0362? flags()->xray_page_size_override363: GetPageSizeCached();364if ((PageSize == 0) || ((PageSize & (PageSize - 1)) != 0)) {365Report("Provided page size is not a power of two: %zu\n", PageSize);366return XRayPatchingStatus::FAILED;367}368369// Here we compute the minimum sled and maximum sled associated with a370// particular function ID.371XRayFunctionSledIndex SledRange;372if (InstrMap.SledsIndex) {373SledRange = {InstrMap.SledsIndex[FuncId - 1].fromPCRelative(),374InstrMap.SledsIndex[FuncId - 1].Size};375} else {376SledRange = findFunctionSleds(FuncId, InstrMap);377}378auto *f = SledRange.Begin;379auto *e = SledRange.Begin + SledRange.Size;380auto *MinSled = f;381auto *MaxSled = e - 1;382while (f != e) {383if (f->address() < MinSled->address())384MinSled = f;385if (f->address() > MaxSled->address())386MaxSled = f;387++f;388}389390void *PageAlignedAddr =391reinterpret_cast<void *>(MinSled->address() & ~(PageSize - 1));392size_t MProtectLen =393(MaxSled->address() - reinterpret_cast<uptr>(PageAlignedAddr)) +394cSledLength;395MProtectHelper Protector(PageAlignedAddr, MProtectLen, PageSize);396if (Protector.MakeWriteable() == -1) {397Report("Failed mprotect: %d\n", errno);398return XRayPatchingStatus::FAILED;399}400return patchFunction(FuncId, Enable);401}402403} // namespace404405} // namespace __xray406407using namespace __xray;408409// The following functions are declared `extern "C" {...}` in the header, hence410// they're defined in the global namespace.411412int __xray_set_handler(void (*entry)(int32_t,413XRayEntryType)) XRAY_NEVER_INSTRUMENT {414if (atomic_load(&XRayInitialized,415memory_order_acquire)) {416417atomic_store(&__xray::XRayPatchedFunction,418reinterpret_cast<uintptr_t>(entry),419memory_order_release);420return 1;421}422return 0;423}424425int __xray_set_customevent_handler(void (*entry)(void *, size_t))426XRAY_NEVER_INSTRUMENT {427if (atomic_load(&XRayInitialized,428memory_order_acquire)) {429atomic_store(&__xray::XRayPatchedCustomEvent,430reinterpret_cast<uintptr_t>(entry),431memory_order_release);432return 1;433}434return 0;435}436437int __xray_set_typedevent_handler(void (*entry)(size_t, const void *,438size_t)) XRAY_NEVER_INSTRUMENT {439if (atomic_load(&XRayInitialized,440memory_order_acquire)) {441atomic_store(&__xray::XRayPatchedTypedEvent,442reinterpret_cast<uintptr_t>(entry),443memory_order_release);444return 1;445}446return 0;447}448449int __xray_remove_handler() XRAY_NEVER_INSTRUMENT {450return __xray_set_handler(nullptr);451}452453int __xray_remove_customevent_handler() XRAY_NEVER_INSTRUMENT {454return __xray_set_customevent_handler(nullptr);455}456457int __xray_remove_typedevent_handler() XRAY_NEVER_INSTRUMENT {458return __xray_set_typedevent_handler(nullptr);459}460461uint16_t __xray_register_event_type(462const char *const event_type) XRAY_NEVER_INSTRUMENT {463TypeDescriptorMapType::Handle h(&TypeDescriptorAddressMap, (uptr)event_type);464if (h.created()) {465h->type_id = atomic_fetch_add(466&TypeEventDescriptorCounter, 1, memory_order_acq_rel);467h->description_string_length = strnlen(event_type, 1024);468}469return h->type_id;470}471472XRayPatchingStatus __xray_patch() XRAY_NEVER_INSTRUMENT {473return controlPatching(true);474}475476XRayPatchingStatus __xray_unpatch() XRAY_NEVER_INSTRUMENT {477return controlPatching(false);478}479480XRayPatchingStatus __xray_patch_function(int32_t FuncId) XRAY_NEVER_INSTRUMENT {481return mprotectAndPatchFunction(FuncId, true);482}483484XRayPatchingStatus485__xray_unpatch_function(int32_t FuncId) XRAY_NEVER_INSTRUMENT {486return mprotectAndPatchFunction(FuncId, false);487}488489int __xray_set_handler_arg1(void (*entry)(int32_t, XRayEntryType, uint64_t)) {490if (!atomic_load(&XRayInitialized,491memory_order_acquire))492return 0;493494// A relaxed write might not be visible even if the current thread gets495// scheduled on a different CPU/NUMA node. We need to wait for everyone to496// have this handler installed for consistency of collected data across CPUs.497atomic_store(&XRayArgLogger, reinterpret_cast<uint64_t>(entry),498memory_order_release);499return 1;500}501502int __xray_remove_handler_arg1() { return __xray_set_handler_arg1(nullptr); }503504uintptr_t __xray_function_address(int32_t FuncId) XRAY_NEVER_INSTRUMENT {505XRaySledMap InstrMap;506{507SpinMutexLock Guard(&XRayInstrMapMutex);508InstrMap = XRayInstrMap;509}510511if (FuncId <= 0 || static_cast<size_t>(FuncId) > InstrMap.Functions)512return 0;513const XRaySledEntry *Sled =514InstrMap.SledsIndex ? InstrMap.SledsIndex[FuncId - 1].fromPCRelative()515: findFunctionSleds(FuncId, InstrMap).Begin;516return Sled->function()517// On PPC, function entries are always aligned to 16 bytes. The beginning of a518// sled might be a local entry, which is always +8 based on the global entry.519// Always return the global entry.520#ifdef __PPC__521& ~0xf522#endif523;524}525526size_t __xray_max_function_id() XRAY_NEVER_INSTRUMENT {527SpinMutexLock Guard(&XRayInstrMapMutex);528return XRayInstrMap.Functions;529}530531532