1 | //===-- xray_init.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 | // XRay initialisation logic. |
12 | //===----------------------------------------------------------------------===// |
13 | |
14 | #include <fcntl.h> |
15 | #include <strings.h> |
16 | #include <unistd.h> |
17 | |
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" |
24 | |
25 | extern "C" { |
26 | void __xray_init(); |
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)); |
31 | |
32 | #if SANITIZER_APPLE |
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[] = {}; |
39 | #endif |
40 | } |
41 | |
42 | using namespace __xray; |
43 | |
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{.val_dont_use: 0}; |
49 | |
50 | // This should always be updated before XRayInitialized is updated. |
51 | SpinMutex XRayInstrMapMutex; |
52 | |
53 | // Contains maps for the main executable as well as DSOs. |
54 | XRaySledMap *XRayInstrMaps; |
55 | |
56 | // Number of binary objects registered. |
57 | atomic_uint32_t XRayNumObjects{.val_dont_use: 0}; |
58 | |
59 | // Global flag to determine whether the flags have been initialized. |
60 | atomic_uint8_t XRayFlagsInitialized{.val_dont_use: 0}; |
61 | |
62 | // A mutex to allow only one thread to initialize the XRay data structures. |
63 | SpinMutex XRayInitMutex; |
64 | |
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. |
68 | int32_t |
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(format: "Invalid XRay sleds.\n" ); |
76 | return -1; |
77 | } |
78 | XRaySledMap SledMap; |
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; |
87 | } else { |
88 | size_t CountFunctions = 0; |
89 | uint64_t LastFnAddr = 0; |
90 | |
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) { |
95 | CountFunctions++; |
96 | LastFnAddr = Function; |
97 | } |
98 | } |
99 | SledMap.SledsIndex = nullptr; |
100 | SledMap.Functions = CountFunctions; |
101 | } |
102 | if (SledMap.Functions >= XRayMaxFunctions) { |
103 | Report(format: "Too many functions! Maximum is %ld\n" , XRayMaxFunctions); |
104 | return -1; |
105 | } |
106 | |
107 | if (Verbosity()) |
108 | Report(format: "Registering %d new functions!\n" , SledMap.Functions); |
109 | |
110 | { |
111 | SpinMutexLock Guard(&XRayInstrMapMutex); |
112 | auto Idx = atomic_fetch_add(a: &XRayNumObjects, v: 1, mo: memory_order_acq_rel); |
113 | if (Idx >= XRayMaxObjects) { |
114 | Report(format: "Too many objects registered! Maximum is %ld\n" , XRayMaxObjects); |
115 | return -1; |
116 | } |
117 | XRayInstrMaps[Idx] = std::move(t&: SledMap); |
118 | return Idx; |
119 | } |
120 | } |
121 | |
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(a: &XRayInitialized, mo: memory_order_acquire)) |
128 | return; |
129 | |
130 | // XRAY is not compatible with PaX MPROTECT |
131 | CheckMPROTECT(); |
132 | |
133 | if (!atomic_load(a: &XRayFlagsInitialized, mo: memory_order_acquire)) { |
134 | initializeFlags(); |
135 | atomic_store(a: &XRayFlagsInitialized, v: true, mo: memory_order_release); |
136 | } |
137 | |
138 | if (__start_xray_instr_map == nullptr) { |
139 | if (Verbosity()) |
140 | Report(format: "XRay instrumentation map missing. Not initializing XRay.\n" ); |
141 | return; |
142 | } |
143 | |
144 | atomic_store(a: &XRayNumObjects, v: 0, mo: memory_order_release); |
145 | |
146 | // Pre-allocation takes up approx. 5kB for XRayMaxObjects=64. |
147 | XRayInstrMaps = allocateBuffer<XRaySledMap>(S: XRayMaxObjects); |
148 | |
149 | int MainBinaryId = |
150 | __xray_register_sleds(SledsBegin: __start_xray_instr_map, SledsEnd: __stop_xray_instr_map, |
151 | FnIndexBegin: __start_xray_fn_idx, FnIndexEnd: __stop_xray_fn_idx, FromDSO: false, Trampolines: {}); |
152 | |
153 | // The executable should always get ID 0. |
154 | if (MainBinaryId != 0) { |
155 | Report(format: "Registering XRay sleds failed.\n" ); |
156 | return; |
157 | } |
158 | |
159 | atomic_store(a: &XRayInitialized, v: true, mo: memory_order_release); |
160 | |
161 | #ifndef XRAY_NO_PREINIT |
162 | if (flags()->patch_premain) |
163 | __xray_patch(); |
164 | #endif |
165 | } |
166 | |
167 | // Registers XRay sleds and trampolines of an instrumented DSO. |
168 | // Returns the object ID if registration is successful, -1 otherwise. |
169 | // |
170 | // Default visibility is hidden, so we have to explicitly make it visible to |
171 | // DSO. |
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. |
178 | __xray_init(); |
179 | |
180 | if (__xray_num_objects() == 0) { |
181 | if (Verbosity()) |
182 | Report(format: "No XRay instrumentation map in main executable. Not initializing " |
183 | "XRay for DSO.\n" ); |
184 | return -1; |
185 | } |
186 | |
187 | // Register sleds in global map. |
188 | int ObjId = __xray_register_sleds(SledsBegin, SledsEnd, FnIndexBegin, |
189 | FnIndexEnd, FromDSO: true, Trampolines); |
190 | |
191 | #ifndef XRAY_NO_PREINIT |
192 | if (ObjId >= 0 && flags()->patch_premain) |
193 | __xray_patch_object(ObjId); |
194 | #endif |
195 | |
196 | return ObjId; |
197 | } |
198 | |
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 |
203 | // deregistered. |
204 | SANITIZER_INTERFACE_ATTRIBUTE bool |
205 | __xray_deregister_dso(int32_t ObjId) XRAY_NEVER_INSTRUMENT { |
206 | |
207 | if (!atomic_load(a: &XRayInitialized, mo: memory_order_acquire)) { |
208 | if (Verbosity()) |
209 | Report(format: "XRay has not been initialized. Cannot deregister DSO.\n" ); |
210 | return false; |
211 | } |
212 | |
213 | if (ObjId <= 0 || static_cast<uint32_t>(ObjId) >= __xray_num_objects()) { |
214 | if (Verbosity()) |
215 | Report(format: "Can't deregister object with ID %d: ID is invalid.\n" , ObjId); |
216 | return false; |
217 | } |
218 | |
219 | { |
220 | SpinMutexLock Guard(&XRayInstrMapMutex); |
221 | auto &Entry = XRayInstrMaps[ObjId]; |
222 | if (!Entry.FromDSO) { |
223 | if (Verbosity()) |
224 | Report(format: "Can't deregister object with ID %d: object does not correspond " |
225 | "to a shared library.\n" , |
226 | ObjId); |
227 | return false; |
228 | } |
229 | if (!Entry.Loaded) { |
230 | if (Verbosity()) |
231 | Report(format: "Can't deregister object with ID %d: object is not loaded.\n" , |
232 | ObjId); |
233 | return true; |
234 | } |
235 | // Mark DSO as unloaded. No need to unpatch. |
236 | Entry.Loaded = false; |
237 | } |
238 | |
239 | if (Verbosity()) |
240 | Report(format: "Deregistered object with ID %d.\n" , ObjId); |
241 | |
242 | return true; |
243 | } |
244 | |
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; |
255 | #else |
256 | // If we cannot use the .preinit_array section, we should instead use dynamic |
257 | // initialisation. |
258 | __attribute__ ((constructor (0))) |
259 | static void __local_xray_dyninit() { |
260 | __xray_init(); |
261 | } |
262 | #endif |
263 | |