1 //===-- xray_init.cpp -------------------------------------------*- C++ -*-===//
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
7 //===----------------------------------------------------------------------===//
9 // This file is a part of XRay, a dynamic runtime instrumentation system.
11 // XRay initialisation logic.
12 //===----------------------------------------------------------------------===//
18 #include "sanitizer_common/sanitizer_common.h"
19 #include "xray/xray_interface.h"
20 #include "xray_allocator.h"
21 #include "xray_defs.h"
22 #include "xray_flags.h"
23 #include "xray_interface_internal.h"
27 extern const XRaySledEntry __start_xray_instr_map
[] __attribute__((weak
));
28 extern const XRaySledEntry __stop_xray_instr_map
[] __attribute__((weak
));
29 extern const XRayFunctionSledIndex __start_xray_fn_idx
[] __attribute__((weak
));
30 extern const XRayFunctionSledIndex __stop_xray_fn_idx
[] __attribute__((weak
));
33 // HACK: This is a temporary workaround to make XRay build on
34 // Darwin, but it will probably not work at runtime.
35 const XRaySledEntry __start_xray_instr_map
[] = {};
36 extern const XRaySledEntry __stop_xray_instr_map
[] = {};
37 extern const XRayFunctionSledIndex __start_xray_fn_idx
[] = {};
38 extern const XRayFunctionSledIndex __stop_xray_fn_idx
[] = {};
42 using namespace __xray
;
44 // When set to 'true' this means the XRay runtime has been initialised. We use
45 // the weak symbols defined above (__start_xray_inst_map and
46 // __stop_xray_instr_map) to initialise the instrumentation map that XRay uses
47 // for runtime patching/unpatching of instrumentation points.
48 atomic_uint8_t XRayInitialized
{0};
50 // This should always be updated before XRayInitialized is updated.
51 SpinMutex XRayInstrMapMutex
;
53 // Contains maps for the main executable as well as DSOs.
54 XRaySledMap
*XRayInstrMaps
;
56 // Number of binary objects registered.
57 atomic_uint32_t XRayNumObjects
{0};
59 // Global flag to determine whether the flags have been initialized.
60 atomic_uint8_t XRayFlagsInitialized
{0};
62 // A mutex to allow only one thread to initialize the XRay data structures.
63 SpinMutex XRayInitMutex
;
65 // Registers XRay sleds and trampolines coming from the main executable or one
66 // of the linked DSOs.
67 // Returns the object ID if registration is successful, -1 otherwise.
69 __xray_register_sleds(const XRaySledEntry
*SledsBegin
,
70 const XRaySledEntry
*SledsEnd
,
71 const XRayFunctionSledIndex
*FnIndexBegin
,
72 const XRayFunctionSledIndex
*FnIndexEnd
, bool FromDSO
,
73 XRayTrampolines Trampolines
) XRAY_NEVER_INSTRUMENT
{
74 if (!SledsBegin
|| !SledsEnd
) {
75 Report("Invalid XRay sleds.\n");
79 SledMap
.FromDSO
= FromDSO
;
80 SledMap
.Loaded
= true;
81 SledMap
.Trampolines
= Trampolines
;
82 SledMap
.Sleds
= SledsBegin
;
83 SledMap
.Entries
= SledsEnd
- SledsBegin
;
84 if (FnIndexBegin
!= nullptr) {
85 SledMap
.SledsIndex
= FnIndexBegin
;
86 SledMap
.Functions
= FnIndexEnd
- FnIndexBegin
;
88 size_t CountFunctions
= 0;
89 uint64_t LastFnAddr
= 0;
91 for (std::size_t I
= 0; I
< SledMap
.Entries
; I
++) {
92 const auto &Sled
= SledMap
.Sleds
[I
];
93 const auto Function
= Sled
.function();
94 if (Function
!= LastFnAddr
) {
96 LastFnAddr
= Function
;
99 SledMap
.SledsIndex
= nullptr;
100 SledMap
.Functions
= CountFunctions
;
102 if (SledMap
.Functions
>= XRayMaxFunctions
) {
103 Report("Too many functions! Maximum is %ld\n", XRayMaxFunctions
);
108 Report("Registering %d new functions!\n", SledMap
.Functions
);
111 SpinMutexLock
Guard(&XRayInstrMapMutex
);
112 auto Idx
= atomic_fetch_add(&XRayNumObjects
, 1, memory_order_acq_rel
);
113 if (Idx
>= XRayMaxObjects
) {
114 Report("Too many objects registered! Maximum is %ld\n", XRayMaxObjects
);
117 XRayInstrMaps
[Idx
] = std::move(SledMap
);
122 // __xray_init() will do the actual loading of the current process' memory map
123 // and then proceed to look for the .xray_instr_map section/segment.
124 void __xray_init() XRAY_NEVER_INSTRUMENT
{
125 SpinMutexLock
Guard(&XRayInitMutex
);
126 // Short-circuit if we've already initialized XRay before.
127 if (atomic_load(&XRayInitialized
, memory_order_acquire
))
130 // XRAY is not compatible with PaX MPROTECT
133 if (!atomic_load(&XRayFlagsInitialized
, memory_order_acquire
)) {
135 atomic_store(&XRayFlagsInitialized
, true, memory_order_release
);
138 if (__start_xray_instr_map
== nullptr) {
140 Report("XRay instrumentation map missing. Not initializing XRay.\n");
144 atomic_store(&XRayNumObjects
, 0, memory_order_release
);
146 // Pre-allocation takes up approx. 5kB for XRayMaxObjects=64.
147 XRayInstrMaps
= allocateBuffer
<XRaySledMap
>(XRayMaxObjects
);
150 __xray_register_sleds(__start_xray_instr_map
, __stop_xray_instr_map
,
151 __start_xray_fn_idx
, __stop_xray_fn_idx
, false, {});
153 // The executable should always get ID 0.
154 if (MainBinaryId
!= 0) {
155 Report("Registering XRay sleds failed.\n");
159 atomic_store(&XRayInitialized
, true, memory_order_release
);
161 #ifndef XRAY_NO_PREINIT
162 if (flags()->patch_premain
)
167 // Registers XRay sleds and trampolines of an instrumented DSO.
168 // Returns the object ID if registration is successful, -1 otherwise.
170 // Default visibility is hidden, so we have to explicitly make it visible to
172 SANITIZER_INTERFACE_ATTRIBUTE
int32_t __xray_register_dso(
173 const XRaySledEntry
*SledsBegin
, const XRaySledEntry
*SledsEnd
,
174 const XRayFunctionSledIndex
*FnIndexBegin
,
175 const XRayFunctionSledIndex
*FnIndexEnd
,
176 XRayTrampolines Trampolines
) XRAY_NEVER_INSTRUMENT
{
177 // Make sure XRay has been initialized in the main executable.
180 if (__xray_num_objects() == 0) {
182 Report("No XRay instrumentation map in main executable. Not initializing "
187 // Register sleds in global map.
188 int ObjId
= __xray_register_sleds(SledsBegin
, SledsEnd
, FnIndexBegin
,
189 FnIndexEnd
, true, Trampolines
);
191 #ifndef XRAY_NO_PREINIT
192 if (ObjId
>= 0 && flags()->patch_premain
)
193 __xray_patch_object(ObjId
);
199 // Deregisters a DSO from the main XRay runtime.
200 // Called from the DSO-local runtime when the library is unloaded (e.g. if
201 // dlclose is called).
202 // Returns true if the object ID is valid and the DSO was successfully
204 SANITIZER_INTERFACE_ATTRIBUTE
bool
205 __xray_deregister_dso(int32_t ObjId
) XRAY_NEVER_INSTRUMENT
{
207 if (!atomic_load(&XRayInitialized
, memory_order_acquire
)) {
209 Report("XRay has not been initialized. Cannot deregister DSO.\n");
213 if (ObjId
<= 0 || static_cast<uint32_t>(ObjId
) >= __xray_num_objects()) {
215 Report("Can't deregister object with ID %d: ID is invalid.\n", ObjId
);
220 SpinMutexLock
Guard(&XRayInstrMapMutex
);
221 auto &Entry
= XRayInstrMaps
[ObjId
];
222 if (!Entry
.FromDSO
) {
224 Report("Can't deregister object with ID %d: object does not correspond "
225 "to a shared library.\n",
231 Report("Can't deregister object with ID %d: object is not loaded.\n",
235 // Mark DSO as unloaded. No need to unpatch.
236 Entry
.Loaded
= false;
240 Report("Deregistered object with ID %d.\n", ObjId
);
245 // FIXME: Make check-xray tests work on FreeBSD without
246 // SANITIZER_CAN_USE_PREINIT_ARRAY.
247 // See sanitizer_internal_defs.h where the macro is defined.
248 // Calling unresolved PLT functions in .preinit_array can lead to deadlock on
249 // FreeBSD but here it seems benign.
250 #if !defined(XRAY_NO_PREINIT) && \
251 (SANITIZER_CAN_USE_PREINIT_ARRAY || SANITIZER_FREEBSD)
252 // Only add the preinit array initialization if the sanitizers can.
253 __attribute__((section(".preinit_array"),
254 used
)) void (*__local_xray_preinit
)(void) = __xray_init
;
256 // If we cannot use the .preinit_array section, we should instead use dynamic
258 __attribute__ ((constructor (0)))
259 static void __local_xray_dyninit() {