xref: /llvm-project/compiler-rt/lib/xray/xray_init.cpp (revision e738a5d8e33911381dbd0e1bc5a0109e9ebb62f2)
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