1b3018603SNico Weber //===-- xray_init.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 // XRay initialisation logic. 12b3018603SNico Weber //===----------------------------------------------------------------------===// 13b3018603SNico Weber 14b3018603SNico Weber #include <fcntl.h> 15b3018603SNico Weber #include <strings.h> 16b3018603SNico Weber #include <unistd.h> 17b3018603SNico Weber 18b3018603SNico Weber #include "sanitizer_common/sanitizer_common.h" 19*e738a5d8SSebastian Kreutzer #include "xray/xray_interface.h" 20*e738a5d8SSebastian Kreutzer #include "xray_allocator.h" 21b3018603SNico Weber #include "xray_defs.h" 22b3018603SNico Weber #include "xray_flags.h" 23b3018603SNico Weber #include "xray_interface_internal.h" 24b3018603SNico Weber 25b3018603SNico Weber extern "C" { 26b3018603SNico Weber void __xray_init(); 27b3018603SNico Weber extern const XRaySledEntry __start_xray_instr_map[] __attribute__((weak)); 28b3018603SNico Weber extern const XRaySledEntry __stop_xray_instr_map[] __attribute__((weak)); 29b3018603SNico Weber extern const XRayFunctionSledIndex __start_xray_fn_idx[] __attribute__((weak)); 30b3018603SNico Weber extern const XRayFunctionSledIndex __stop_xray_fn_idx[] __attribute__((weak)); 31b3018603SNico Weber 328246b2e1SMariusz Borsa #if SANITIZER_APPLE 33b3018603SNico Weber // HACK: This is a temporary workaround to make XRay build on 34b3018603SNico Weber // Darwin, but it will probably not work at runtime. 35b3018603SNico Weber const XRaySledEntry __start_xray_instr_map[] = {}; 36b3018603SNico Weber extern const XRaySledEntry __stop_xray_instr_map[] = {}; 37b3018603SNico Weber extern const XRayFunctionSledIndex __start_xray_fn_idx[] = {}; 38b3018603SNico Weber extern const XRayFunctionSledIndex __stop_xray_fn_idx[] = {}; 39b3018603SNico Weber #endif 40b3018603SNico Weber } 41b3018603SNico Weber 42b3018603SNico Weber using namespace __xray; 43b3018603SNico Weber 44b3018603SNico Weber // When set to 'true' this means the XRay runtime has been initialised. We use 45b3018603SNico Weber // the weak symbols defined above (__start_xray_inst_map and 46b3018603SNico Weber // __stop_xray_instr_map) to initialise the instrumentation map that XRay uses 47b3018603SNico Weber // for runtime patching/unpatching of instrumentation points. 48b3018603SNico Weber atomic_uint8_t XRayInitialized{0}; 49b3018603SNico Weber 50b3018603SNico Weber // This should always be updated before XRayInitialized is updated. 51b3018603SNico Weber SpinMutex XRayInstrMapMutex; 52*e738a5d8SSebastian Kreutzer 53*e738a5d8SSebastian Kreutzer // Contains maps for the main executable as well as DSOs. 54*e738a5d8SSebastian Kreutzer XRaySledMap *XRayInstrMaps; 55*e738a5d8SSebastian Kreutzer 56*e738a5d8SSebastian Kreutzer // Number of binary objects registered. 57*e738a5d8SSebastian Kreutzer atomic_uint32_t XRayNumObjects{0}; 58b3018603SNico Weber 59b3018603SNico Weber // Global flag to determine whether the flags have been initialized. 60b3018603SNico Weber atomic_uint8_t XRayFlagsInitialized{0}; 61b3018603SNico Weber 62b3018603SNico Weber // A mutex to allow only one thread to initialize the XRay data structures. 63b3018603SNico Weber SpinMutex XRayInitMutex; 64b3018603SNico Weber 65*e738a5d8SSebastian Kreutzer // Registers XRay sleds and trampolines coming from the main executable or one 66*e738a5d8SSebastian Kreutzer // of the linked DSOs. 67*e738a5d8SSebastian Kreutzer // Returns the object ID if registration is successful, -1 otherwise. 68*e738a5d8SSebastian Kreutzer int32_t 69*e738a5d8SSebastian Kreutzer __xray_register_sleds(const XRaySledEntry *SledsBegin, 70*e738a5d8SSebastian Kreutzer const XRaySledEntry *SledsEnd, 71*e738a5d8SSebastian Kreutzer const XRayFunctionSledIndex *FnIndexBegin, 72*e738a5d8SSebastian Kreutzer const XRayFunctionSledIndex *FnIndexEnd, bool FromDSO, 73*e738a5d8SSebastian Kreutzer XRayTrampolines Trampolines) XRAY_NEVER_INSTRUMENT { 74*e738a5d8SSebastian Kreutzer if (!SledsBegin || !SledsEnd) { 75*e738a5d8SSebastian Kreutzer Report("Invalid XRay sleds.\n"); 76*e738a5d8SSebastian Kreutzer return -1; 77*e738a5d8SSebastian Kreutzer } 78*e738a5d8SSebastian Kreutzer XRaySledMap SledMap; 79*e738a5d8SSebastian Kreutzer SledMap.FromDSO = FromDSO; 80*e738a5d8SSebastian Kreutzer SledMap.Loaded = true; 81*e738a5d8SSebastian Kreutzer SledMap.Trampolines = Trampolines; 82*e738a5d8SSebastian Kreutzer SledMap.Sleds = SledsBegin; 83*e738a5d8SSebastian Kreutzer SledMap.Entries = SledsEnd - SledsBegin; 84*e738a5d8SSebastian Kreutzer if (FnIndexBegin != nullptr) { 85*e738a5d8SSebastian Kreutzer SledMap.SledsIndex = FnIndexBegin; 86*e738a5d8SSebastian Kreutzer SledMap.Functions = FnIndexEnd - FnIndexBegin; 87*e738a5d8SSebastian Kreutzer } else { 88*e738a5d8SSebastian Kreutzer size_t CountFunctions = 0; 89*e738a5d8SSebastian Kreutzer uint64_t LastFnAddr = 0; 90*e738a5d8SSebastian Kreutzer 91*e738a5d8SSebastian Kreutzer for (std::size_t I = 0; I < SledMap.Entries; I++) { 92*e738a5d8SSebastian Kreutzer const auto &Sled = SledMap.Sleds[I]; 93*e738a5d8SSebastian Kreutzer const auto Function = Sled.function(); 94*e738a5d8SSebastian Kreutzer if (Function != LastFnAddr) { 95*e738a5d8SSebastian Kreutzer CountFunctions++; 96*e738a5d8SSebastian Kreutzer LastFnAddr = Function; 97*e738a5d8SSebastian Kreutzer } 98*e738a5d8SSebastian Kreutzer } 99*e738a5d8SSebastian Kreutzer SledMap.SledsIndex = nullptr; 100*e738a5d8SSebastian Kreutzer SledMap.Functions = CountFunctions; 101*e738a5d8SSebastian Kreutzer } 102*e738a5d8SSebastian Kreutzer if (SledMap.Functions >= XRayMaxFunctions) { 103*e738a5d8SSebastian Kreutzer Report("Too many functions! Maximum is %ld\n", XRayMaxFunctions); 104*e738a5d8SSebastian Kreutzer return -1; 105*e738a5d8SSebastian Kreutzer } 106*e738a5d8SSebastian Kreutzer 107*e738a5d8SSebastian Kreutzer if (Verbosity()) 108*e738a5d8SSebastian Kreutzer Report("Registering %d new functions!\n", SledMap.Functions); 109*e738a5d8SSebastian Kreutzer 110*e738a5d8SSebastian Kreutzer { 111*e738a5d8SSebastian Kreutzer SpinMutexLock Guard(&XRayInstrMapMutex); 112*e738a5d8SSebastian Kreutzer auto Idx = atomic_fetch_add(&XRayNumObjects, 1, memory_order_acq_rel); 113*e738a5d8SSebastian Kreutzer if (Idx >= XRayMaxObjects) { 114*e738a5d8SSebastian Kreutzer Report("Too many objects registered! Maximum is %ld\n", XRayMaxObjects); 115*e738a5d8SSebastian Kreutzer return -1; 116*e738a5d8SSebastian Kreutzer } 117*e738a5d8SSebastian Kreutzer XRayInstrMaps[Idx] = std::move(SledMap); 118*e738a5d8SSebastian Kreutzer return Idx; 119*e738a5d8SSebastian Kreutzer } 120*e738a5d8SSebastian Kreutzer } 121*e738a5d8SSebastian Kreutzer 122b3018603SNico Weber // __xray_init() will do the actual loading of the current process' memory map 123b3018603SNico Weber // and then proceed to look for the .xray_instr_map section/segment. 124b3018603SNico Weber void __xray_init() XRAY_NEVER_INSTRUMENT { 125b3018603SNico Weber SpinMutexLock Guard(&XRayInitMutex); 126b3018603SNico Weber // Short-circuit if we've already initialized XRay before. 127b3018603SNico Weber if (atomic_load(&XRayInitialized, memory_order_acquire)) 128b3018603SNico Weber return; 129b3018603SNico Weber 130b3018603SNico Weber // XRAY is not compatible with PaX MPROTECT 131b3018603SNico Weber CheckMPROTECT(); 132b3018603SNico Weber 133b3018603SNico Weber if (!atomic_load(&XRayFlagsInitialized, memory_order_acquire)) { 134b3018603SNico Weber initializeFlags(); 135b3018603SNico Weber atomic_store(&XRayFlagsInitialized, true, memory_order_release); 136b3018603SNico Weber } 137b3018603SNico Weber 138b3018603SNico Weber if (__start_xray_instr_map == nullptr) { 139b3018603SNico Weber if (Verbosity()) 140b3018603SNico Weber Report("XRay instrumentation map missing. Not initializing XRay.\n"); 141b3018603SNico Weber return; 142b3018603SNico Weber } 143b3018603SNico Weber 144*e738a5d8SSebastian Kreutzer atomic_store(&XRayNumObjects, 0, memory_order_release); 1457c7c8e0dSIan Levesque 146*e738a5d8SSebastian Kreutzer // Pre-allocation takes up approx. 5kB for XRayMaxObjects=64. 147*e738a5d8SSebastian Kreutzer XRayInstrMaps = allocateBuffer<XRaySledMap>(XRayMaxObjects); 148*e738a5d8SSebastian Kreutzer 149*e738a5d8SSebastian Kreutzer int MainBinaryId = 150*e738a5d8SSebastian Kreutzer __xray_register_sleds(__start_xray_instr_map, __stop_xray_instr_map, 151*e738a5d8SSebastian Kreutzer __start_xray_fn_idx, __stop_xray_fn_idx, false, {}); 152*e738a5d8SSebastian Kreutzer 153*e738a5d8SSebastian Kreutzer // The executable should always get ID 0. 154*e738a5d8SSebastian Kreutzer if (MainBinaryId != 0) { 155*e738a5d8SSebastian Kreutzer Report("Registering XRay sleds failed.\n"); 156*e738a5d8SSebastian Kreutzer return; 1577c7c8e0dSIan Levesque } 1587c7c8e0dSIan Levesque 159b3018603SNico Weber atomic_store(&XRayInitialized, true, memory_order_release); 160b3018603SNico Weber 161b3018603SNico Weber #ifndef XRAY_NO_PREINIT 162b3018603SNico Weber if (flags()->patch_premain) 163b3018603SNico Weber __xray_patch(); 164b3018603SNico Weber #endif 165b3018603SNico Weber } 166b3018603SNico Weber 167*e738a5d8SSebastian Kreutzer // Registers XRay sleds and trampolines of an instrumented DSO. 168*e738a5d8SSebastian Kreutzer // Returns the object ID if registration is successful, -1 otherwise. 169*e738a5d8SSebastian Kreutzer // 170*e738a5d8SSebastian Kreutzer // Default visibility is hidden, so we have to explicitly make it visible to 171*e738a5d8SSebastian Kreutzer // DSO. 172*e738a5d8SSebastian Kreutzer SANITIZER_INTERFACE_ATTRIBUTE int32_t __xray_register_dso( 173*e738a5d8SSebastian Kreutzer const XRaySledEntry *SledsBegin, const XRaySledEntry *SledsEnd, 174*e738a5d8SSebastian Kreutzer const XRayFunctionSledIndex *FnIndexBegin, 175*e738a5d8SSebastian Kreutzer const XRayFunctionSledIndex *FnIndexEnd, 176*e738a5d8SSebastian Kreutzer XRayTrampolines Trampolines) XRAY_NEVER_INSTRUMENT { 177*e738a5d8SSebastian Kreutzer // Make sure XRay has been initialized in the main executable. 178*e738a5d8SSebastian Kreutzer __xray_init(); 179*e738a5d8SSebastian Kreutzer 180*e738a5d8SSebastian Kreutzer if (__xray_num_objects() == 0) { 181*e738a5d8SSebastian Kreutzer if (Verbosity()) 182*e738a5d8SSebastian Kreutzer Report("No XRay instrumentation map in main executable. Not initializing " 183*e738a5d8SSebastian Kreutzer "XRay for DSO.\n"); 184*e738a5d8SSebastian Kreutzer return -1; 185*e738a5d8SSebastian Kreutzer } 186*e738a5d8SSebastian Kreutzer 187*e738a5d8SSebastian Kreutzer // Register sleds in global map. 188*e738a5d8SSebastian Kreutzer int ObjId = __xray_register_sleds(SledsBegin, SledsEnd, FnIndexBegin, 189*e738a5d8SSebastian Kreutzer FnIndexEnd, true, Trampolines); 190*e738a5d8SSebastian Kreutzer 191*e738a5d8SSebastian Kreutzer #ifndef XRAY_NO_PREINIT 192*e738a5d8SSebastian Kreutzer if (ObjId >= 0 && flags()->patch_premain) 193*e738a5d8SSebastian Kreutzer __xray_patch_object(ObjId); 194*e738a5d8SSebastian Kreutzer #endif 195*e738a5d8SSebastian Kreutzer 196*e738a5d8SSebastian Kreutzer return ObjId; 197*e738a5d8SSebastian Kreutzer } 198*e738a5d8SSebastian Kreutzer 199*e738a5d8SSebastian Kreutzer // Deregisters a DSO from the main XRay runtime. 200*e738a5d8SSebastian Kreutzer // Called from the DSO-local runtime when the library is unloaded (e.g. if 201*e738a5d8SSebastian Kreutzer // dlclose is called). 202*e738a5d8SSebastian Kreutzer // Returns true if the object ID is valid and the DSO was successfully 203*e738a5d8SSebastian Kreutzer // deregistered. 204*e738a5d8SSebastian Kreutzer SANITIZER_INTERFACE_ATTRIBUTE bool 205*e738a5d8SSebastian Kreutzer __xray_deregister_dso(int32_t ObjId) XRAY_NEVER_INSTRUMENT { 206*e738a5d8SSebastian Kreutzer 207*e738a5d8SSebastian Kreutzer if (!atomic_load(&XRayInitialized, memory_order_acquire)) { 208*e738a5d8SSebastian Kreutzer if (Verbosity()) 209*e738a5d8SSebastian Kreutzer Report("XRay has not been initialized. Cannot deregister DSO.\n"); 210*e738a5d8SSebastian Kreutzer return false; 211*e738a5d8SSebastian Kreutzer } 212*e738a5d8SSebastian Kreutzer 213*e738a5d8SSebastian Kreutzer if (ObjId <= 0 || static_cast<uint32_t>(ObjId) >= __xray_num_objects()) { 214*e738a5d8SSebastian Kreutzer if (Verbosity()) 215*e738a5d8SSebastian Kreutzer Report("Can't deregister object with ID %d: ID is invalid.\n", ObjId); 216*e738a5d8SSebastian Kreutzer return false; 217*e738a5d8SSebastian Kreutzer } 218*e738a5d8SSebastian Kreutzer 219*e738a5d8SSebastian Kreutzer { 220*e738a5d8SSebastian Kreutzer SpinMutexLock Guard(&XRayInstrMapMutex); 221*e738a5d8SSebastian Kreutzer auto &Entry = XRayInstrMaps[ObjId]; 222*e738a5d8SSebastian Kreutzer if (!Entry.FromDSO) { 223*e738a5d8SSebastian Kreutzer if (Verbosity()) 224*e738a5d8SSebastian Kreutzer Report("Can't deregister object with ID %d: object does not correspond " 225*e738a5d8SSebastian Kreutzer "to a shared library.\n", 226*e738a5d8SSebastian Kreutzer ObjId); 227*e738a5d8SSebastian Kreutzer return false; 228*e738a5d8SSebastian Kreutzer } 229*e738a5d8SSebastian Kreutzer if (!Entry.Loaded) { 230*e738a5d8SSebastian Kreutzer if (Verbosity()) 231*e738a5d8SSebastian Kreutzer Report("Can't deregister object with ID %d: object is not loaded.\n", 232*e738a5d8SSebastian Kreutzer ObjId); 233*e738a5d8SSebastian Kreutzer return true; 234*e738a5d8SSebastian Kreutzer } 235*e738a5d8SSebastian Kreutzer // Mark DSO as unloaded. No need to unpatch. 236*e738a5d8SSebastian Kreutzer Entry.Loaded = false; 237*e738a5d8SSebastian Kreutzer } 238*e738a5d8SSebastian Kreutzer 239*e738a5d8SSebastian Kreutzer if (Verbosity()) 240*e738a5d8SSebastian Kreutzer Report("Deregistered object with ID %d.\n", ObjId); 241*e738a5d8SSebastian Kreutzer 242*e738a5d8SSebastian Kreutzer return true; 243*e738a5d8SSebastian Kreutzer } 244*e738a5d8SSebastian Kreutzer 245b3018603SNico Weber // FIXME: Make check-xray tests work on FreeBSD without 246b3018603SNico Weber // SANITIZER_CAN_USE_PREINIT_ARRAY. 247b3018603SNico Weber // See sanitizer_internal_defs.h where the macro is defined. 248b3018603SNico Weber // Calling unresolved PLT functions in .preinit_array can lead to deadlock on 249b3018603SNico Weber // FreeBSD but here it seems benign. 250b3018603SNico Weber #if !defined(XRAY_NO_PREINIT) && \ 251b3018603SNico Weber (SANITIZER_CAN_USE_PREINIT_ARRAY || SANITIZER_FREEBSD) 252b3018603SNico Weber // Only add the preinit array initialization if the sanitizers can. 253b3018603SNico Weber __attribute__((section(".preinit_array"), 254b3018603SNico Weber used)) void (*__local_xray_preinit)(void) = __xray_init; 255b3018603SNico Weber #else 256b3018603SNico Weber // If we cannot use the .preinit_array section, we should instead use dynamic 257b3018603SNico Weber // initialisation. 258b3018603SNico Weber __attribute__ ((constructor (0))) 259b3018603SNico Weber static void __local_xray_dyninit() { 260b3018603SNico Weber __xray_init(); 261b3018603SNico Weber } 262b3018603SNico Weber #endif 263