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_defs.h" |
20 | #include "xray_flags.h" |
21 | #include "xray_interface_internal.h" |
22 | |
23 | extern "C" { |
24 | void __xray_init(); |
25 | extern const XRaySledEntry __start_xray_instr_map[] __attribute__((weak)); |
26 | extern const XRaySledEntry __stop_xray_instr_map[] __attribute__((weak)); |
27 | extern const XRayFunctionSledIndex __start_xray_fn_idx[] __attribute__((weak)); |
28 | extern const XRayFunctionSledIndex __stop_xray_fn_idx[] __attribute__((weak)); |
29 | |
30 | #if SANITIZER_APPLE |
31 | // HACK: This is a temporary workaround to make XRay build on |
32 | // Darwin, but it will probably not work at runtime. |
33 | const XRaySledEntry __start_xray_instr_map[] = {}; |
34 | extern const XRaySledEntry __stop_xray_instr_map[] = {}; |
35 | extern const XRayFunctionSledIndex __start_xray_fn_idx[] = {}; |
36 | extern const XRayFunctionSledIndex __stop_xray_fn_idx[] = {}; |
37 | #endif |
38 | } |
39 | |
40 | using namespace __xray; |
41 | |
42 | // When set to 'true' this means the XRay runtime has been initialised. We use |
43 | // the weak symbols defined above (__start_xray_inst_map and |
44 | // __stop_xray_instr_map) to initialise the instrumentation map that XRay uses |
45 | // for runtime patching/unpatching of instrumentation points. |
46 | // |
47 | // FIXME: Support DSO instrumentation maps too. The current solution only works |
48 | // for statically linked executables. |
49 | atomic_uint8_t XRayInitialized{.val_dont_use: 0}; |
50 | |
51 | // This should always be updated before XRayInitialized is updated. |
52 | SpinMutex XRayInstrMapMutex; |
53 | XRaySledMap XRayInstrMap; |
54 | |
55 | // Global flag to determine whether the flags have been initialized. |
56 | atomic_uint8_t XRayFlagsInitialized{.val_dont_use: 0}; |
57 | |
58 | // A mutex to allow only one thread to initialize the XRay data structures. |
59 | SpinMutex XRayInitMutex; |
60 | |
61 | // __xray_init() will do the actual loading of the current process' memory map |
62 | // and then proceed to look for the .xray_instr_map section/segment. |
63 | void __xray_init() XRAY_NEVER_INSTRUMENT { |
64 | SpinMutexLock Guard(&XRayInitMutex); |
65 | // Short-circuit if we've already initialized XRay before. |
66 | if (atomic_load(a: &XRayInitialized, mo: memory_order_acquire)) |
67 | return; |
68 | |
69 | // XRAY is not compatible with PaX MPROTECT |
70 | CheckMPROTECT(); |
71 | |
72 | if (!atomic_load(a: &XRayFlagsInitialized, mo: memory_order_acquire)) { |
73 | initializeFlags(); |
74 | atomic_store(a: &XRayFlagsInitialized, v: true, mo: memory_order_release); |
75 | } |
76 | |
77 | if (__start_xray_instr_map == nullptr) { |
78 | if (Verbosity()) |
79 | Report(format: "XRay instrumentation map missing. Not initializing XRay.\n" ); |
80 | return; |
81 | } |
82 | |
83 | { |
84 | SpinMutexLock Guard(&XRayInstrMapMutex); |
85 | XRayInstrMap.Sleds = __start_xray_instr_map; |
86 | XRayInstrMap.Entries = __stop_xray_instr_map - __start_xray_instr_map; |
87 | if (__start_xray_fn_idx != nullptr) { |
88 | XRayInstrMap.SledsIndex = __start_xray_fn_idx; |
89 | XRayInstrMap.Functions = __stop_xray_fn_idx - __start_xray_fn_idx; |
90 | } else { |
91 | size_t CountFunctions = 0; |
92 | uint64_t LastFnAddr = 0; |
93 | |
94 | for (std::size_t I = 0; I < XRayInstrMap.Entries; I++) { |
95 | const auto &Sled = XRayInstrMap.Sleds[I]; |
96 | const auto Function = Sled.function(); |
97 | if (Function != LastFnAddr) { |
98 | CountFunctions++; |
99 | LastFnAddr = Function; |
100 | } |
101 | } |
102 | |
103 | XRayInstrMap.Functions = CountFunctions; |
104 | } |
105 | } |
106 | atomic_store(a: &XRayInitialized, v: true, mo: memory_order_release); |
107 | |
108 | #ifndef XRAY_NO_PREINIT |
109 | if (flags()->patch_premain) |
110 | __xray_patch(); |
111 | #endif |
112 | } |
113 | |
114 | // FIXME: Make check-xray tests work on FreeBSD without |
115 | // SANITIZER_CAN_USE_PREINIT_ARRAY. |
116 | // See sanitizer_internal_defs.h where the macro is defined. |
117 | // Calling unresolved PLT functions in .preinit_array can lead to deadlock on |
118 | // FreeBSD but here it seems benign. |
119 | #if !defined(XRAY_NO_PREINIT) && \ |
120 | (SANITIZER_CAN_USE_PREINIT_ARRAY || SANITIZER_FREEBSD) |
121 | // Only add the preinit array initialization if the sanitizers can. |
122 | __attribute__((section(".preinit_array" ), |
123 | used)) void (*__local_xray_preinit)(void) = __xray_init; |
124 | #else |
125 | // If we cannot use the .preinit_array section, we should instead use dynamic |
126 | // initialisation. |
127 | __attribute__ ((constructor (0))) |
128 | static void __local_xray_dyninit() { |
129 | __xray_init(); |
130 | } |
131 | #endif |
132 | |