xref: /llvm-project/compiler-rt/lib/xray/xray_interface.cpp (revision ea76b2d8d83d6885bf5707832cbc4b7655e21b08)
1b3018603SNico Weber //===-- xray_interface.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 // Implementation of the API functions.
12b3018603SNico Weber //
13b3018603SNico Weber //===----------------------------------------------------------------------===//
14b3018603SNico Weber 
15b3018603SNico Weber #include "xray_interface_internal.h"
16b3018603SNico Weber 
17bbba9d8cSDimitry Andric #include <cinttypes>
18b3018603SNico Weber #include <cstdio>
19b3018603SNico Weber #include <errno.h>
20b3018603SNico Weber #include <limits>
21b3018603SNico Weber #include <string.h>
22b3018603SNico Weber #include <sys/mman.h>
23b3018603SNico Weber 
24b3018603SNico Weber #if SANITIZER_FUCHSIA
25b3018603SNico Weber #include <zircon/process.h>
26b3018603SNico Weber #include <zircon/sanitizer.h>
27b3018603SNico Weber #include <zircon/status.h>
28b3018603SNico Weber #include <zircon/syscalls.h>
29b3018603SNico Weber #endif
30b3018603SNico Weber 
31b3018603SNico Weber #include "sanitizer_common/sanitizer_addrhashmap.h"
32b3018603SNico Weber #include "sanitizer_common/sanitizer_common.h"
33b3018603SNico Weber 
34b3018603SNico Weber #include "xray_defs.h"
35b3018603SNico Weber #include "xray_flags.h"
36b3018603SNico Weber 
37b3018603SNico Weber extern __sanitizer::SpinMutex XRayInstrMapMutex;
38b3018603SNico Weber extern __sanitizer::atomic_uint8_t XRayInitialized;
39e738a5d8SSebastian Kreutzer extern __xray::XRaySledMap *XRayInstrMaps;
40e738a5d8SSebastian Kreutzer extern __sanitizer::atomic_uint32_t XRayNumObjects;
41b3018603SNico Weber 
42b3018603SNico Weber namespace __xray {
43b3018603SNico Weber 
44b3018603SNico Weber #if defined(__x86_64__)
45b3018603SNico Weber static const int16_t cSledLength = 12;
46b3018603SNico Weber #elif defined(__aarch64__)
47b3018603SNico Weber static const int16_t cSledLength = 32;
48b3018603SNico Weber #elif defined(__arm__)
49b3018603SNico Weber static const int16_t cSledLength = 28;
50ef33d6cbSWeining Lu #elif SANITIZER_LOONGARCH64
51ef33d6cbSWeining Lu static const int16_t cSledLength = 48;
52b3018603SNico Weber #elif SANITIZER_MIPS32
53b3018603SNico Weber static const int16_t cSledLength = 48;
54b3018603SNico Weber #elif SANITIZER_MIPS64
55b3018603SNico Weber static const int16_t cSledLength = 64;
56b3018603SNico Weber #elif defined(__powerpc64__)
57b3018603SNico Weber static const int16_t cSledLength = 8;
581e68c799SBrian Cain #elif defined(__hexagon__)
591e68c799SBrian Cain static const int16_t cSledLength = 20;
60*ea76b2d8SMin-Yih Hsu #elif defined(__riscv) && (__riscv_xlen == 64)
61*ea76b2d8SMin-Yih Hsu static const int16_t cSledLength = 68;
62*ea76b2d8SMin-Yih Hsu #elif defined(__riscv) && (__riscv_xlen == 32)
63*ea76b2d8SMin-Yih Hsu static const int16_t cSledLength = 52;
64b3018603SNico Weber #else
65b3018603SNico Weber #error "Unsupported CPU Architecture"
66b3018603SNico Weber #endif /* CPU architecture */
67b3018603SNico Weber 
68b3018603SNico Weber // This is the function to call when we encounter the entry or exit sleds.
69e738a5d8SSebastian Kreutzer atomic_uintptr_t XRayPatchedFunction SANITIZER_INTERFACE_ATTRIBUTE{0};
70b3018603SNico Weber 
71b3018603SNico Weber // This is the function to call from the arg1-enabled sleds/trampolines.
72e738a5d8SSebastian Kreutzer atomic_uintptr_t XRayArgLogger SANITIZER_INTERFACE_ATTRIBUTE{0};
73b3018603SNico Weber 
74b3018603SNico Weber // This is the function to call when we encounter a custom event log call.
75e738a5d8SSebastian Kreutzer atomic_uintptr_t XRayPatchedCustomEvent SANITIZER_INTERFACE_ATTRIBUTE{0};
76b3018603SNico Weber 
77b3018603SNico Weber // This is the function to call when we encounter a typed event log call.
78e738a5d8SSebastian Kreutzer atomic_uintptr_t XRayPatchedTypedEvent SANITIZER_INTERFACE_ATTRIBUTE{0};
79b3018603SNico Weber 
80b3018603SNico Weber // This is the global status to determine whether we are currently
81b3018603SNico Weber // patching/unpatching.
82b3018603SNico Weber atomic_uint8_t XRayPatching{0};
83b3018603SNico Weber 
84b3018603SNico Weber struct TypeDescription {
85b3018603SNico Weber   uint32_t type_id;
86b3018603SNico Weber   std::size_t description_string_length;
87b3018603SNico Weber };
88b3018603SNico Weber 
89b3018603SNico Weber using TypeDescriptorMapType = AddrHashMap<TypeDescription, 11>;
90b3018603SNico Weber // An address map from immutable descriptors to type ids.
91b3018603SNico Weber TypeDescriptorMapType TypeDescriptorAddressMap{};
92b3018603SNico Weber 
93b3018603SNico Weber atomic_uint32_t TypeEventDescriptorCounter{0};
94b3018603SNico Weber 
95b3018603SNico Weber // MProtectHelper is an RAII wrapper for calls to mprotect(...) that will
96b3018603SNico Weber // undo any successful mprotect(...) changes. This is used to make a page
97b3018603SNico Weber // writeable and executable, and upon destruction if it was successful in
98b3018603SNico Weber // doing so returns the page into a read-only and executable page.
99b3018603SNico Weber //
100b3018603SNico Weber // This is only used specifically for runtime-patching of the XRay
101b3018603SNico Weber // instrumentation points. This assumes that the executable pages are
102b3018603SNico Weber // originally read-and-execute only.
103b3018603SNico Weber class MProtectHelper {
104b3018603SNico Weber   void *PageAlignedAddr;
105b3018603SNico Weber   std::size_t MProtectLen;
106b3018603SNico Weber   bool MustCleanup;
107b3018603SNico Weber 
108b3018603SNico Weber public:
109b3018603SNico Weber   explicit MProtectHelper(void *PageAlignedAddr,
110b3018603SNico Weber                           std::size_t MProtectLen,
111b3018603SNico Weber                           std::size_t PageSize) XRAY_NEVER_INSTRUMENT
112b3018603SNico Weber       : PageAlignedAddr(PageAlignedAddr),
113b3018603SNico Weber         MProtectLen(MProtectLen),
114b3018603SNico Weber         MustCleanup(false) {
115b3018603SNico Weber #if SANITIZER_FUCHSIA
116b3018603SNico Weber     MProtectLen = RoundUpTo(MProtectLen, PageSize);
117b3018603SNico Weber #endif
118b3018603SNico Weber   }
119b3018603SNico Weber 
120b3018603SNico Weber   int MakeWriteable() XRAY_NEVER_INSTRUMENT {
121b3018603SNico Weber #if SANITIZER_FUCHSIA
122b3018603SNico Weber     auto R = __sanitizer_change_code_protection(
123b3018603SNico Weber         reinterpret_cast<uintptr_t>(PageAlignedAddr), MProtectLen, true);
124b3018603SNico Weber     if (R != ZX_OK) {
125b3018603SNico Weber       Report("XRay: cannot change code protection: %s\n",
126b3018603SNico Weber              _zx_status_get_string(R));
127b3018603SNico Weber       return -1;
128b3018603SNico Weber     }
129b3018603SNico Weber     MustCleanup = true;
130b3018603SNico Weber     return 0;
131b3018603SNico Weber #else
132b3018603SNico Weber     auto R = mprotect(PageAlignedAddr, MProtectLen,
133b3018603SNico Weber                       PROT_READ | PROT_WRITE | PROT_EXEC);
134b3018603SNico Weber     if (R != -1)
135b3018603SNico Weber       MustCleanup = true;
136b3018603SNico Weber     return R;
137b3018603SNico Weber #endif
138b3018603SNico Weber   }
139b3018603SNico Weber 
140b3018603SNico Weber   ~MProtectHelper() XRAY_NEVER_INSTRUMENT {
141b3018603SNico Weber     if (MustCleanup) {
142b3018603SNico Weber #if SANITIZER_FUCHSIA
143b3018603SNico Weber       auto R = __sanitizer_change_code_protection(
144b3018603SNico Weber           reinterpret_cast<uintptr_t>(PageAlignedAddr), MProtectLen, false);
145b3018603SNico Weber       if (R != ZX_OK) {
146b3018603SNico Weber         Report("XRay: cannot change code protection: %s\n",
147b3018603SNico Weber                _zx_status_get_string(R));
148b3018603SNico Weber       }
149b3018603SNico Weber #else
150b3018603SNico Weber       mprotect(PageAlignedAddr, MProtectLen, PROT_READ | PROT_EXEC);
151b3018603SNico Weber #endif
152b3018603SNico Weber     }
153b3018603SNico Weber   }
154b3018603SNico Weber };
155b3018603SNico Weber 
156b3018603SNico Weber namespace {
157b3018603SNico Weber 
158e738a5d8SSebastian Kreutzer bool isObjectLoaded(int32_t ObjId) {
159e738a5d8SSebastian Kreutzer   SpinMutexLock Guard(&XRayInstrMapMutex);
160e738a5d8SSebastian Kreutzer   if (ObjId < 0 || static_cast<uint32_t>(ObjId) >=
161e738a5d8SSebastian Kreutzer                        atomic_load(&XRayNumObjects, memory_order_acquire)) {
162e738a5d8SSebastian Kreutzer     return false;
163e738a5d8SSebastian Kreutzer   }
164e738a5d8SSebastian Kreutzer   return XRayInstrMaps[ObjId].Loaded;
165e738a5d8SSebastian Kreutzer }
166e738a5d8SSebastian Kreutzer 
167e738a5d8SSebastian Kreutzer bool patchSled(const XRaySledEntry &Sled, bool Enable, int32_t FuncId,
168e738a5d8SSebastian Kreutzer                const XRayTrampolines &Trampolines) XRAY_NEVER_INSTRUMENT {
169b3018603SNico Weber   bool Success = false;
170b3018603SNico Weber   switch (Sled.Kind) {
171b3018603SNico Weber   case XRayEntryType::ENTRY:
172e738a5d8SSebastian Kreutzer     Success = patchFunctionEntry(Enable, FuncId, Sled, Trampolines,
173e738a5d8SSebastian Kreutzer                                  /*LogArgs=*/false);
174b3018603SNico Weber     break;
175b3018603SNico Weber   case XRayEntryType::EXIT:
176e738a5d8SSebastian Kreutzer     Success = patchFunctionExit(Enable, FuncId, Sled, Trampolines);
177b3018603SNico Weber     break;
178b3018603SNico Weber   case XRayEntryType::TAIL:
179e738a5d8SSebastian Kreutzer     Success = patchFunctionTailExit(Enable, FuncId, Sled, Trampolines);
180b3018603SNico Weber     break;
181b3018603SNico Weber   case XRayEntryType::LOG_ARGS_ENTRY:
182e738a5d8SSebastian Kreutzer     Success = patchFunctionEntry(Enable, FuncId, Sled, Trampolines,
183e738a5d8SSebastian Kreutzer                                  /*LogArgs=*/true);
184b3018603SNico Weber     break;
185b3018603SNico Weber   case XRayEntryType::CUSTOM_EVENT:
18690627a5aSMikhail Goncharov     Success = patchCustomEvent(Enable, FuncId, Sled);
187b3018603SNico Weber     break;
188b3018603SNico Weber   case XRayEntryType::TYPED_EVENT:
18990627a5aSMikhail Goncharov     Success = patchTypedEvent(Enable, FuncId, Sled);
190b3018603SNico Weber     break;
191b3018603SNico Weber   default:
192bbba9d8cSDimitry Andric     Report("Unsupported sled kind '%" PRIu64 "' @%04x\n", Sled.Address,
193bbba9d8cSDimitry Andric            int(Sled.Kind));
194b3018603SNico Weber     return false;
195b3018603SNico Weber   }
196b3018603SNico Weber   return Success;
197b3018603SNico Weber }
198b3018603SNico Weber 
1997c7c8e0dSIan Levesque const XRayFunctionSledIndex
2007c7c8e0dSIan Levesque findFunctionSleds(int32_t FuncId,
2017c7c8e0dSIan Levesque                   const XRaySledMap &InstrMap) XRAY_NEVER_INSTRUMENT {
2027c7c8e0dSIan Levesque   int32_t CurFn = 0;
2037c7c8e0dSIan Levesque   uint64_t LastFnAddr = 0;
204e0a6561eSFangrui Song   XRayFunctionSledIndex Index = {nullptr, 0};
2057c7c8e0dSIan Levesque 
2067c7c8e0dSIan Levesque   for (std::size_t I = 0; I < InstrMap.Entries && CurFn <= FuncId; I++) {
2077c7c8e0dSIan Levesque     const auto &Sled = InstrMap.Sleds[I];
2087c7c8e0dSIan Levesque     const auto Function = Sled.function();
2097c7c8e0dSIan Levesque     if (Function != LastFnAddr) {
2107c7c8e0dSIan Levesque       CurFn++;
2117c7c8e0dSIan Levesque       LastFnAddr = Function;
2127c7c8e0dSIan Levesque     }
2137c7c8e0dSIan Levesque 
2147c7c8e0dSIan Levesque     if (CurFn == FuncId) {
2157c7c8e0dSIan Levesque       if (Index.Begin == nullptr)
2167c7c8e0dSIan Levesque         Index.Begin = &Sled;
217e0a6561eSFangrui Song       Index.Size = &Sled - Index.Begin + 1;
2187c7c8e0dSIan Levesque     }
2197c7c8e0dSIan Levesque   }
2207c7c8e0dSIan Levesque 
2217c7c8e0dSIan Levesque   return Index;
2227c7c8e0dSIan Levesque }
2237c7c8e0dSIan Levesque 
224e738a5d8SSebastian Kreutzer XRayPatchingStatus patchFunction(int32_t FuncId, int32_t ObjId,
225b3018603SNico Weber                                  bool Enable) XRAY_NEVER_INSTRUMENT {
226e738a5d8SSebastian Kreutzer   if (!atomic_load(&XRayInitialized, memory_order_acquire))
227b3018603SNico Weber     return XRayPatchingStatus::NOT_INITIALIZED; // Not initialized.
228b3018603SNico Weber 
229b3018603SNico Weber   uint8_t NotPatching = false;
230b3018603SNico Weber   if (!atomic_compare_exchange_strong(
231b3018603SNico Weber           &XRayPatching, &NotPatching, true, memory_order_acq_rel))
232b3018603SNico Weber     return XRayPatchingStatus::ONGOING; // Already patching.
233b3018603SNico Weber 
234b3018603SNico Weber   // Next, we look for the function index.
235b3018603SNico Weber   XRaySledMap InstrMap;
236b3018603SNico Weber   {
237b3018603SNico Weber     SpinMutexLock Guard(&XRayInstrMapMutex);
238e738a5d8SSebastian Kreutzer     if (ObjId < 0 || static_cast<uint32_t>(ObjId) >=
239e738a5d8SSebastian Kreutzer                          atomic_load(&XRayNumObjects, memory_order_acquire)) {
240e738a5d8SSebastian Kreutzer       Report("Unable to patch function: invalid sled map index: %d", ObjId);
241e738a5d8SSebastian Kreutzer       return XRayPatchingStatus::FAILED;
242e738a5d8SSebastian Kreutzer     }
243e738a5d8SSebastian Kreutzer     InstrMap = XRayInstrMaps[ObjId];
244b3018603SNico Weber   }
245b3018603SNico Weber 
246b3018603SNico Weber   // If we don't have an index, we can't patch individual functions.
247b3018603SNico Weber   if (InstrMap.Functions == 0)
248b3018603SNico Weber     return XRayPatchingStatus::NOT_INITIALIZED;
249b3018603SNico Weber 
250e738a5d8SSebastian Kreutzer   // Check if the corresponding DSO has been unloaded.
251e738a5d8SSebastian Kreutzer   if (!InstrMap.Loaded) {
252e738a5d8SSebastian Kreutzer     Report("Invalid function id provided: %d\n", FuncId);
253e738a5d8SSebastian Kreutzer     return XRayPatchingStatus::NOT_INITIALIZED;
254e738a5d8SSebastian Kreutzer   }
255e738a5d8SSebastian Kreutzer 
256b3018603SNico Weber   // FuncId must be a positive number, less than the number of functions
257b3018603SNico Weber   // instrumented.
258b3018603SNico Weber   if (FuncId <= 0 || static_cast<size_t>(FuncId) > InstrMap.Functions) {
259b3018603SNico Weber     Report("Invalid function id provided: %d\n", FuncId);
260b3018603SNico Weber     return XRayPatchingStatus::FAILED;
261b3018603SNico Weber   }
262b3018603SNico Weber 
263e738a5d8SSebastian Kreutzer   auto PackedId = __xray::MakePackedId(FuncId, ObjId);
264e738a5d8SSebastian Kreutzer 
265b3018603SNico Weber   // Now we patch ths sleds for this specific function.
266e0a6561eSFangrui Song   XRayFunctionSledIndex SledRange;
267e0a6561eSFangrui Song   if (InstrMap.SledsIndex) {
268e0a6561eSFangrui Song     SledRange = {InstrMap.SledsIndex[FuncId - 1].fromPCRelative(),
269e0a6561eSFangrui Song                  InstrMap.SledsIndex[FuncId - 1].Size};
270e0a6561eSFangrui Song   } else {
271e0a6561eSFangrui Song     SledRange = findFunctionSleds(FuncId, InstrMap);
272e0a6561eSFangrui Song   }
273e738a5d8SSebastian Kreutzer 
274b3018603SNico Weber   auto *f = SledRange.Begin;
275b3018603SNico Weber   bool SucceedOnce = false;
276e0a6561eSFangrui Song   for (size_t i = 0; i != SledRange.Size; ++i)
277e738a5d8SSebastian Kreutzer     SucceedOnce |= patchSled(f[i], Enable, PackedId, InstrMap.Trampolines);
278b3018603SNico Weber 
279e738a5d8SSebastian Kreutzer   atomic_store(&XRayPatching, false, memory_order_release);
280b3018603SNico Weber 
281b3018603SNico Weber   if (!SucceedOnce) {
282b3018603SNico Weber     Report("Failed patching any sled for function '%d'.", FuncId);
283b3018603SNico Weber     return XRayPatchingStatus::FAILED;
284b3018603SNico Weber   }
285b3018603SNico Weber 
286b3018603SNico Weber   return XRayPatchingStatus::SUCCESS;
287b3018603SNico Weber }
288b3018603SNico Weber 
289b3018603SNico Weber // controlPatching implements the common internals of the patching/unpatching
290b3018603SNico Weber // implementation. |Enable| defines whether we're enabling or disabling the
291b3018603SNico Weber // runtime XRay instrumentation.
292e738a5d8SSebastian Kreutzer // This function should only be called after ensuring that XRay is initialized
293e738a5d8SSebastian Kreutzer // and no other thread is currently patching.
294e738a5d8SSebastian Kreutzer XRayPatchingStatus controlPatchingObjectUnchecked(bool Enable, int32_t ObjId) {
295b3018603SNico Weber   XRaySledMap InstrMap;
296b3018603SNico Weber   {
297b3018603SNico Weber     SpinMutexLock Guard(&XRayInstrMapMutex);
298e738a5d8SSebastian Kreutzer     if (ObjId < 0 || static_cast<uint32_t>(ObjId) >=
299e738a5d8SSebastian Kreutzer                          atomic_load(&XRayNumObjects, memory_order_acquire)) {
300e738a5d8SSebastian Kreutzer       Report("Unable to patch functions: invalid sled map index: %d\n", ObjId);
301e738a5d8SSebastian Kreutzer       return XRayPatchingStatus::FAILED;
302e738a5d8SSebastian Kreutzer     }
303e738a5d8SSebastian Kreutzer     InstrMap = XRayInstrMaps[ObjId];
304b3018603SNico Weber   }
305b3018603SNico Weber   if (InstrMap.Entries == 0)
306b3018603SNico Weber     return XRayPatchingStatus::NOT_INITIALIZED;
307b3018603SNico Weber 
308e738a5d8SSebastian Kreutzer   if (Verbosity())
309e738a5d8SSebastian Kreutzer     Report("Patching object %d with %d functions.\n", ObjId, InstrMap.Entries);
310e738a5d8SSebastian Kreutzer 
311e738a5d8SSebastian Kreutzer   // Check if the corresponding DSO has been unloaded.
312e738a5d8SSebastian Kreutzer   if (!InstrMap.Loaded) {
313e738a5d8SSebastian Kreutzer     Report("Object is not loaded at index: %d\n", ObjId);
314e738a5d8SSebastian Kreutzer     return XRayPatchingStatus::FAILED;
315e738a5d8SSebastian Kreutzer   }
316e738a5d8SSebastian Kreutzer 
317b3018603SNico Weber   uint32_t FuncId = 1;
318b3018603SNico Weber   uint64_t CurFun = 0;
319b3018603SNico Weber 
320b3018603SNico Weber   // First we want to find the bounds for which we have instrumentation points,
321b3018603SNico Weber   // and try to get as few calls to mprotect(...) as possible. We're assuming
322b3018603SNico Weber   // that all the sleds for the instrumentation map are contiguous as a single
323b3018603SNico Weber   // set of pages. When we do support dynamic shared object instrumentation,
324b3018603SNico Weber   // we'll need to do this for each set of page load offsets per DSO loaded. For
325b3018603SNico Weber   // now we're assuming we can mprotect the whole section of text between the
326b3018603SNico Weber   // minimum sled address and the maximum sled address (+ the largest sled
327b3018603SNico Weber   // size).
3285771c985SFangrui Song   auto *MinSled = &InstrMap.Sleds[0];
3295771c985SFangrui Song   auto *MaxSled = &InstrMap.Sleds[InstrMap.Entries - 1];
330b3018603SNico Weber   for (std::size_t I = 0; I < InstrMap.Entries; I++) {
331b3018603SNico Weber     const auto &Sled = InstrMap.Sleds[I];
3325771c985SFangrui Song     if (Sled.address() < MinSled->address())
3335771c985SFangrui Song       MinSled = &Sled;
3345771c985SFangrui Song     if (Sled.address() > MaxSled->address())
3355771c985SFangrui Song       MaxSled = &Sled;
336b3018603SNico Weber   }
337b3018603SNico Weber 
338b3018603SNico Weber   const size_t PageSize = flags()->xray_page_size_override > 0
339b3018603SNico Weber                               ? flags()->xray_page_size_override
340b3018603SNico Weber                               : GetPageSizeCached();
341b3018603SNico Weber   if ((PageSize == 0) || ((PageSize & (PageSize - 1)) != 0)) {
342bbba9d8cSDimitry Andric     Report("System page size is not a power of two: %zu\n", PageSize);
343b3018603SNico Weber     return XRayPatchingStatus::FAILED;
344b3018603SNico Weber   }
345b3018603SNico Weber 
346b3018603SNico Weber   void *PageAlignedAddr =
3475771c985SFangrui Song       reinterpret_cast<void *>(MinSled->address() & ~(PageSize - 1));
348b3018603SNico Weber   size_t MProtectLen =
3495771c985SFangrui Song       (MaxSled->address() - reinterpret_cast<uptr>(PageAlignedAddr)) +
3505771c985SFangrui Song       cSledLength;
351b3018603SNico Weber   MProtectHelper Protector(PageAlignedAddr, MProtectLen, PageSize);
352b3018603SNico Weber   if (Protector.MakeWriteable() == -1) {
353b3018603SNico Weber     Report("Failed mprotect: %d\n", errno);
354b3018603SNico Weber     return XRayPatchingStatus::FAILED;
355b3018603SNico Weber   }
356b3018603SNico Weber 
357b3018603SNico Weber   for (std::size_t I = 0; I < InstrMap.Entries; ++I) {
358b3018603SNico Weber     auto &Sled = InstrMap.Sleds[I];
35910bc1258SFangrui Song     auto F = Sled.function();
360b3018603SNico Weber     if (CurFun == 0)
361b3018603SNico Weber       CurFun = F;
362b3018603SNico Weber     if (F != CurFun) {
363b3018603SNico Weber       ++FuncId;
364b3018603SNico Weber       CurFun = F;
365b3018603SNico Weber     }
366e738a5d8SSebastian Kreutzer     auto PackedId = __xray::MakePackedId(FuncId, ObjId);
367e738a5d8SSebastian Kreutzer     patchSled(Sled, Enable, PackedId, InstrMap.Trampolines);
368b3018603SNico Weber   }
369e738a5d8SSebastian Kreutzer   atomic_store(&XRayPatching, false, memory_order_release);
370b3018603SNico Weber   return XRayPatchingStatus::SUCCESS;
371b3018603SNico Weber }
372b3018603SNico Weber 
373e738a5d8SSebastian Kreutzer // Controls patching for all registered objects.
374e738a5d8SSebastian Kreutzer // Returns: SUCCESS, if patching succeeds for all objects.
375e738a5d8SSebastian Kreutzer //          NOT_INITIALIZED, if one or more objects returned NOT_INITIALIZED
376e738a5d8SSebastian Kreutzer //             but none failed.
377e738a5d8SSebastian Kreutzer //          FAILED, if patching of one or more objects failed.
378e738a5d8SSebastian Kreutzer XRayPatchingStatus controlPatching(bool Enable) XRAY_NEVER_INSTRUMENT {
379e738a5d8SSebastian Kreutzer   if (!atomic_load(&XRayInitialized, memory_order_acquire))
380e738a5d8SSebastian Kreutzer     return XRayPatchingStatus::NOT_INITIALIZED; // Not initialized.
381e738a5d8SSebastian Kreutzer 
382e738a5d8SSebastian Kreutzer   uint8_t NotPatching = false;
383e738a5d8SSebastian Kreutzer   if (!atomic_compare_exchange_strong(&XRayPatching, &NotPatching, true,
384e738a5d8SSebastian Kreutzer                                       memory_order_acq_rel))
385e738a5d8SSebastian Kreutzer     return XRayPatchingStatus::ONGOING; // Already patching.
386e738a5d8SSebastian Kreutzer 
387e738a5d8SSebastian Kreutzer   auto XRayPatchingStatusResetter = at_scope_exit(
388e738a5d8SSebastian Kreutzer       [] { atomic_store(&XRayPatching, false, memory_order_release); });
389e738a5d8SSebastian Kreutzer 
390e738a5d8SSebastian Kreutzer   unsigned NumObjects = __xray_num_objects();
391e738a5d8SSebastian Kreutzer 
392e738a5d8SSebastian Kreutzer   XRayPatchingStatus CombinedStatus{NOT_INITIALIZED};
393e738a5d8SSebastian Kreutzer   for (unsigned I = 0; I < NumObjects; ++I) {
394e738a5d8SSebastian Kreutzer     if (!isObjectLoaded(I))
395e738a5d8SSebastian Kreutzer       continue;
396e738a5d8SSebastian Kreutzer     auto LastStatus = controlPatchingObjectUnchecked(Enable, I);
397e738a5d8SSebastian Kreutzer     switch (LastStatus) {
398e738a5d8SSebastian Kreutzer     case SUCCESS:
399e738a5d8SSebastian Kreutzer       if (CombinedStatus == NOT_INITIALIZED)
400e738a5d8SSebastian Kreutzer         CombinedStatus = SUCCESS;
401e738a5d8SSebastian Kreutzer       break;
402e738a5d8SSebastian Kreutzer     case FAILED:
403e738a5d8SSebastian Kreutzer       // Report failure, but try to patch the remaining objects
404e738a5d8SSebastian Kreutzer       CombinedStatus = FAILED;
405e738a5d8SSebastian Kreutzer       break;
406e738a5d8SSebastian Kreutzer     case NOT_INITIALIZED:
407e738a5d8SSebastian Kreutzer       // XRay has been initialized but there are no sleds available for this
408e738a5d8SSebastian Kreutzer       // object. Try to patch remaining objects.
409e738a5d8SSebastian Kreutzer       if (CombinedStatus != FAILED)
410e738a5d8SSebastian Kreutzer         CombinedStatus = NOT_INITIALIZED;
411e738a5d8SSebastian Kreutzer       break;
412e738a5d8SSebastian Kreutzer     case ONGOING:
413e738a5d8SSebastian Kreutzer       UNREACHABLE("Status ONGOING should not appear at this point");
414e738a5d8SSebastian Kreutzer     }
415e738a5d8SSebastian Kreutzer   }
416e738a5d8SSebastian Kreutzer   return CombinedStatus;
417e738a5d8SSebastian Kreutzer }
418e738a5d8SSebastian Kreutzer 
419e738a5d8SSebastian Kreutzer // Controls patching for one object.
420e738a5d8SSebastian Kreutzer XRayPatchingStatus controlPatching(bool Enable,
421e738a5d8SSebastian Kreutzer                                    int32_t ObjId) XRAY_NEVER_INSTRUMENT {
422e738a5d8SSebastian Kreutzer 
423e738a5d8SSebastian Kreutzer   if (!atomic_load(&XRayInitialized, memory_order_acquire))
424e738a5d8SSebastian Kreutzer     return XRayPatchingStatus::NOT_INITIALIZED; // Not initialized.
425e738a5d8SSebastian Kreutzer 
426e738a5d8SSebastian Kreutzer   uint8_t NotPatching = false;
427e738a5d8SSebastian Kreutzer   if (!atomic_compare_exchange_strong(&XRayPatching, &NotPatching, true,
428e738a5d8SSebastian Kreutzer                                       memory_order_acq_rel))
429e738a5d8SSebastian Kreutzer     return XRayPatchingStatus::ONGOING; // Already patching.
430e738a5d8SSebastian Kreutzer 
431e738a5d8SSebastian Kreutzer   auto XRayPatchingStatusResetter = at_scope_exit(
432e738a5d8SSebastian Kreutzer       [] { atomic_store(&XRayPatching, false, memory_order_release); });
433e738a5d8SSebastian Kreutzer 
434e738a5d8SSebastian Kreutzer   return controlPatchingObjectUnchecked(Enable, ObjId);
435e738a5d8SSebastian Kreutzer }
436e738a5d8SSebastian Kreutzer 
437e738a5d8SSebastian Kreutzer XRayPatchingStatus mprotectAndPatchFunction(int32_t FuncId, int32_t ObjId,
438b3018603SNico Weber                                             bool Enable) XRAY_NEVER_INSTRUMENT {
439b3018603SNico Weber   XRaySledMap InstrMap;
440b3018603SNico Weber   {
441b3018603SNico Weber     SpinMutexLock Guard(&XRayInstrMapMutex);
442e738a5d8SSebastian Kreutzer     if (ObjId < 0 || static_cast<uint32_t>(ObjId) >=
443e738a5d8SSebastian Kreutzer                          atomic_load(&XRayNumObjects, memory_order_acquire)) {
444e738a5d8SSebastian Kreutzer       Report("Unable to patch function: invalid sled map index: %d\n", ObjId);
445e738a5d8SSebastian Kreutzer       return XRayPatchingStatus::FAILED;
446e738a5d8SSebastian Kreutzer     }
447e738a5d8SSebastian Kreutzer     InstrMap = XRayInstrMaps[ObjId];
448e738a5d8SSebastian Kreutzer   }
449e738a5d8SSebastian Kreutzer 
450e738a5d8SSebastian Kreutzer   // Check if the corresponding DSO has been unloaded.
451e738a5d8SSebastian Kreutzer   if (!InstrMap.Loaded) {
452e738a5d8SSebastian Kreutzer     Report("Object is not loaded at index: %d\n", ObjId);
453e738a5d8SSebastian Kreutzer     return XRayPatchingStatus::FAILED;
454b3018603SNico Weber   }
455b3018603SNico Weber 
456b3018603SNico Weber   // FuncId must be a positive number, less than the number of functions
457b3018603SNico Weber   // instrumented.
458b3018603SNico Weber   if (FuncId <= 0 || static_cast<size_t>(FuncId) > InstrMap.Functions) {
459b3018603SNico Weber     Report("Invalid function id provided: %d\n", FuncId);
460b3018603SNico Weber     return XRayPatchingStatus::FAILED;
461b3018603SNico Weber   }
462b3018603SNico Weber 
463b3018603SNico Weber   const size_t PageSize = flags()->xray_page_size_override > 0
464b3018603SNico Weber                               ? flags()->xray_page_size_override
465b3018603SNico Weber                               : GetPageSizeCached();
466b3018603SNico Weber   if ((PageSize == 0) || ((PageSize & (PageSize - 1)) != 0)) {
467bbba9d8cSDimitry Andric     Report("Provided page size is not a power of two: %zu\n", PageSize);
468b3018603SNico Weber     return XRayPatchingStatus::FAILED;
469b3018603SNico Weber   }
470b3018603SNico Weber 
471a1e7e401SKazuaki Ishizaki   // Here we compute the minimum sled and maximum sled associated with a
472b3018603SNico Weber   // particular function ID.
473e0a6561eSFangrui Song   XRayFunctionSledIndex SledRange;
474e0a6561eSFangrui Song   if (InstrMap.SledsIndex) {
475e0a6561eSFangrui Song     SledRange = {InstrMap.SledsIndex[FuncId - 1].fromPCRelative(),
476e0a6561eSFangrui Song                  InstrMap.SledsIndex[FuncId - 1].Size};
477e0a6561eSFangrui Song   } else {
478e0a6561eSFangrui Song     SledRange = findFunctionSleds(FuncId, InstrMap);
479e0a6561eSFangrui Song   }
480b3018603SNico Weber   auto *f = SledRange.Begin;
481e0a6561eSFangrui Song   auto *e = SledRange.Begin + SledRange.Size;
4825771c985SFangrui Song   auto *MinSled = f;
483e0a6561eSFangrui Song   auto *MaxSled = e - 1;
484b3018603SNico Weber   while (f != e) {
4855771c985SFangrui Song     if (f->address() < MinSled->address())
4865771c985SFangrui Song       MinSled = f;
4875771c985SFangrui Song     if (f->address() > MaxSled->address())
4885771c985SFangrui Song       MaxSled = f;
489b3018603SNico Weber     ++f;
490b3018603SNico Weber   }
491b3018603SNico Weber 
492b3018603SNico Weber   void *PageAlignedAddr =
4935771c985SFangrui Song       reinterpret_cast<void *>(MinSled->address() & ~(PageSize - 1));
494b3018603SNico Weber   size_t MProtectLen =
4955771c985SFangrui Song       (MaxSled->address() - reinterpret_cast<uptr>(PageAlignedAddr)) +
4965771c985SFangrui Song       cSledLength;
497b3018603SNico Weber   MProtectHelper Protector(PageAlignedAddr, MProtectLen, PageSize);
498b3018603SNico Weber   if (Protector.MakeWriteable() == -1) {
499b3018603SNico Weber     Report("Failed mprotect: %d\n", errno);
500b3018603SNico Weber     return XRayPatchingStatus::FAILED;
501b3018603SNico Weber   }
502e738a5d8SSebastian Kreutzer   return patchFunction(FuncId, ObjId, Enable);
503b3018603SNico Weber }
504b3018603SNico Weber 
505b3018603SNico Weber } // namespace
506b3018603SNico Weber 
507b3018603SNico Weber } // namespace __xray
508b3018603SNico Weber 
509b3018603SNico Weber using namespace __xray;
510b3018603SNico Weber 
511b3018603SNico Weber // The following functions are declared `extern "C" {...}` in the header, hence
512b3018603SNico Weber // they're defined in the global namespace.
513b3018603SNico Weber 
514b3018603SNico Weber int __xray_set_handler(void (*entry)(int32_t,
515b3018603SNico Weber                                      XRayEntryType)) XRAY_NEVER_INSTRUMENT {
516e738a5d8SSebastian Kreutzer   if (atomic_load(&XRayInitialized, memory_order_acquire)) {
517b3018603SNico Weber 
518b3018603SNico Weber     atomic_store(&__xray::XRayPatchedFunction,
519e738a5d8SSebastian Kreutzer                  reinterpret_cast<uintptr_t>(entry), memory_order_release);
520b3018603SNico Weber     return 1;
521b3018603SNico Weber   }
522b3018603SNico Weber   return 0;
523b3018603SNico Weber }
524b3018603SNico Weber 
525b3018603SNico Weber int __xray_set_customevent_handler(void (*entry)(void *, size_t))
526b3018603SNico Weber     XRAY_NEVER_INSTRUMENT {
527e738a5d8SSebastian Kreutzer   if (atomic_load(&XRayInitialized, memory_order_acquire)) {
528b3018603SNico Weber     atomic_store(&__xray::XRayPatchedCustomEvent,
529e738a5d8SSebastian Kreutzer                  reinterpret_cast<uintptr_t>(entry), memory_order_release);
530b3018603SNico Weber     return 1;
531b3018603SNico Weber   }
532b3018603SNico Weber   return 0;
533b3018603SNico Weber }
534b3018603SNico Weber 
5353fa3cb40SFangrui Song int __xray_set_typedevent_handler(void (*entry)(size_t, const void *,
5363fa3cb40SFangrui Song                                                 size_t)) XRAY_NEVER_INSTRUMENT {
537e738a5d8SSebastian Kreutzer   if (atomic_load(&XRayInitialized, memory_order_acquire)) {
538b3018603SNico Weber     atomic_store(&__xray::XRayPatchedTypedEvent,
539e738a5d8SSebastian Kreutzer                  reinterpret_cast<uintptr_t>(entry), memory_order_release);
540b3018603SNico Weber     return 1;
541b3018603SNico Weber   }
542b3018603SNico Weber   return 0;
543b3018603SNico Weber }
544b3018603SNico Weber 
545b3018603SNico Weber int __xray_remove_handler() XRAY_NEVER_INSTRUMENT {
546b3018603SNico Weber   return __xray_set_handler(nullptr);
547b3018603SNico Weber }
548b3018603SNico Weber 
549b3018603SNico Weber int __xray_remove_customevent_handler() XRAY_NEVER_INSTRUMENT {
550b3018603SNico Weber   return __xray_set_customevent_handler(nullptr);
551b3018603SNico Weber }
552b3018603SNico Weber 
553b3018603SNico Weber int __xray_remove_typedevent_handler() XRAY_NEVER_INSTRUMENT {
554b3018603SNico Weber   return __xray_set_typedevent_handler(nullptr);
555b3018603SNico Weber }
556b3018603SNico Weber 
557b3018603SNico Weber uint16_t __xray_register_event_type(
558b3018603SNico Weber     const char *const event_type) XRAY_NEVER_INSTRUMENT {
559b3018603SNico Weber   TypeDescriptorMapType::Handle h(&TypeDescriptorAddressMap, (uptr)event_type);
560b3018603SNico Weber   if (h.created()) {
561b3018603SNico Weber     h->type_id = atomic_fetch_add(
562b3018603SNico Weber         &TypeEventDescriptorCounter, 1, memory_order_acq_rel);
563b3018603SNico Weber     h->description_string_length = strnlen(event_type, 1024);
564b3018603SNico Weber   }
565b3018603SNico Weber   return h->type_id;
566b3018603SNico Weber }
567b3018603SNico Weber 
568b3018603SNico Weber XRayPatchingStatus __xray_patch() XRAY_NEVER_INSTRUMENT {
569b3018603SNico Weber   return controlPatching(true);
570b3018603SNico Weber }
571b3018603SNico Weber 
572e738a5d8SSebastian Kreutzer XRayPatchingStatus __xray_patch_object(int32_t ObjId) XRAY_NEVER_INSTRUMENT {
573e738a5d8SSebastian Kreutzer   return controlPatching(true, ObjId);
574e738a5d8SSebastian Kreutzer }
575e738a5d8SSebastian Kreutzer 
576b3018603SNico Weber XRayPatchingStatus __xray_unpatch() XRAY_NEVER_INSTRUMENT {
577b3018603SNico Weber   return controlPatching(false);
578b3018603SNico Weber }
579b3018603SNico Weber 
580e738a5d8SSebastian Kreutzer XRayPatchingStatus __xray_unpatch_object(int32_t ObjId) XRAY_NEVER_INSTRUMENT {
581e738a5d8SSebastian Kreutzer   return controlPatching(false, ObjId);
582e738a5d8SSebastian Kreutzer }
583e738a5d8SSebastian Kreutzer 
584b3018603SNico Weber XRayPatchingStatus __xray_patch_function(int32_t FuncId) XRAY_NEVER_INSTRUMENT {
585e738a5d8SSebastian Kreutzer   auto Ids = __xray::UnpackId(FuncId);
586e738a5d8SSebastian Kreutzer   auto ObjId = Ids.first;
587e738a5d8SSebastian Kreutzer   auto FnId = Ids.second;
588e738a5d8SSebastian Kreutzer   return mprotectAndPatchFunction(FnId, ObjId, true);
589e738a5d8SSebastian Kreutzer }
590e738a5d8SSebastian Kreutzer 
591e738a5d8SSebastian Kreutzer XRayPatchingStatus
592e738a5d8SSebastian Kreutzer __xray_patch_function_in_object(int32_t FuncId,
593e738a5d8SSebastian Kreutzer                                 int32_t ObjId) XRAY_NEVER_INSTRUMENT {
594e738a5d8SSebastian Kreutzer   return mprotectAndPatchFunction(FuncId, ObjId, true);
595b3018603SNico Weber }
596b3018603SNico Weber 
597b3018603SNico Weber XRayPatchingStatus
598b3018603SNico Weber __xray_unpatch_function(int32_t FuncId) XRAY_NEVER_INSTRUMENT {
599e738a5d8SSebastian Kreutzer   auto Ids = __xray::UnpackId(FuncId);
600e738a5d8SSebastian Kreutzer   auto ObjId = Ids.first;
601e738a5d8SSebastian Kreutzer   auto FnId = Ids.second;
602e738a5d8SSebastian Kreutzer   return mprotectAndPatchFunction(FnId, ObjId, false);
603e738a5d8SSebastian Kreutzer }
604e738a5d8SSebastian Kreutzer 
605e738a5d8SSebastian Kreutzer XRayPatchingStatus
606e738a5d8SSebastian Kreutzer __xray_unpatch_function_in_object(int32_t FuncId,
607e738a5d8SSebastian Kreutzer                                   int32_t ObjId) XRAY_NEVER_INSTRUMENT {
608e738a5d8SSebastian Kreutzer   return mprotectAndPatchFunction(FuncId, ObjId, false);
609b3018603SNico Weber }
610b3018603SNico Weber 
611b3018603SNico Weber int __xray_set_handler_arg1(void (*entry)(int32_t, XRayEntryType, uint64_t)) {
612e738a5d8SSebastian Kreutzer   if (!atomic_load(&XRayInitialized, memory_order_acquire))
613b3018603SNico Weber     return 0;
614b3018603SNico Weber 
615b3018603SNico Weber   // A relaxed write might not be visible even if the current thread gets
616b3018603SNico Weber   // scheduled on a different CPU/NUMA node.  We need to wait for everyone to
617b3018603SNico Weber   // have this handler installed for consistency of collected data across CPUs.
618b3018603SNico Weber   atomic_store(&XRayArgLogger, reinterpret_cast<uint64_t>(entry),
619b3018603SNico Weber                memory_order_release);
620b3018603SNico Weber   return 1;
621b3018603SNico Weber }
622b3018603SNico Weber 
623b3018603SNico Weber int __xray_remove_handler_arg1() { return __xray_set_handler_arg1(nullptr); }
624b3018603SNico Weber 
625e738a5d8SSebastian Kreutzer uintptr_t
626e738a5d8SSebastian Kreutzer __xray_function_address(int32_t CombinedFuncId) XRAY_NEVER_INSTRUMENT {
627e738a5d8SSebastian Kreutzer   auto Ids = __xray::UnpackId(CombinedFuncId);
628e738a5d8SSebastian Kreutzer   return __xray_function_address_in_object(Ids.second, Ids.first);
629e738a5d8SSebastian Kreutzer }
630e738a5d8SSebastian Kreutzer 
631e738a5d8SSebastian Kreutzer uintptr_t __xray_function_address_in_object(int32_t FuncId, int32_t ObjId)
632e738a5d8SSebastian Kreutzer     XRAY_NEVER_INSTRUMENT {
6337c7c8e0dSIan Levesque   XRaySledMap InstrMap;
6347c7c8e0dSIan Levesque   {
635b3018603SNico Weber     SpinMutexLock Guard(&XRayInstrMapMutex);
636e738a5d8SSebastian Kreutzer     auto count = atomic_load(&XRayNumObjects, memory_order_acquire);
637e738a5d8SSebastian Kreutzer     if (ObjId < 0 || static_cast<uint32_t>(ObjId) >= count) {
638e738a5d8SSebastian Kreutzer       Report("Unable to determine function address: invalid sled map index %d "
639e738a5d8SSebastian Kreutzer              "(size is %d)\n",
640e738a5d8SSebastian Kreutzer              ObjId, (int)count);
641e738a5d8SSebastian Kreutzer       return 0;
642e738a5d8SSebastian Kreutzer     }
643e738a5d8SSebastian Kreutzer     InstrMap = XRayInstrMaps[ObjId];
6447c7c8e0dSIan Levesque   }
6457c7c8e0dSIan Levesque 
6467c7c8e0dSIan Levesque   if (FuncId <= 0 || static_cast<size_t>(FuncId) > InstrMap.Functions)
647b3018603SNico Weber     return 0;
648e0a6561eSFangrui Song   const XRaySledEntry *Sled =
649e0a6561eSFangrui Song       InstrMap.SledsIndex ? InstrMap.SledsIndex[FuncId - 1].fromPCRelative()
6507c7c8e0dSIan Levesque                           : findFunctionSleds(FuncId, InstrMap).Begin;
6517c7c8e0dSIan Levesque   return Sled->function()
652b3018603SNico Weber // On PPC, function entries are always aligned to 16 bytes. The beginning of a
653b3018603SNico Weber // sled might be a local entry, which is always +8 based on the global entry.
654b3018603SNico Weber // Always return the global entry.
655b3018603SNico Weber #ifdef __PPC__
656b3018603SNico Weber          & ~0xf
657b3018603SNico Weber #endif
658b3018603SNico Weber       ;
659b3018603SNico Weber }
660b3018603SNico Weber 
661b3018603SNico Weber size_t __xray_max_function_id() XRAY_NEVER_INSTRUMENT {
662e738a5d8SSebastian Kreutzer   return __xray_max_function_id_in_object(0);
663e738a5d8SSebastian Kreutzer }
664e738a5d8SSebastian Kreutzer 
665e738a5d8SSebastian Kreutzer size_t __xray_max_function_id_in_object(int32_t ObjId) XRAY_NEVER_INSTRUMENT {
666b3018603SNico Weber   SpinMutexLock Guard(&XRayInstrMapMutex);
667e738a5d8SSebastian Kreutzer   if (ObjId < 0 || static_cast<uint32_t>(ObjId) >=
668e738a5d8SSebastian Kreutzer                        atomic_load(&XRayNumObjects, memory_order_acquire))
669e738a5d8SSebastian Kreutzer     return 0;
670e738a5d8SSebastian Kreutzer   return XRayInstrMaps[ObjId].Functions;
671e738a5d8SSebastian Kreutzer }
672e738a5d8SSebastian Kreutzer 
673e738a5d8SSebastian Kreutzer size_t __xray_num_objects() XRAY_NEVER_INSTRUMENT {
674e738a5d8SSebastian Kreutzer   SpinMutexLock Guard(&XRayInstrMapMutex);
675e738a5d8SSebastian Kreutzer   return atomic_load(&XRayNumObjects, memory_order_acquire);
676e738a5d8SSebastian Kreutzer }
677e738a5d8SSebastian Kreutzer 
678e738a5d8SSebastian Kreutzer int32_t __xray_unpack_function_id(int32_t PackedId) {
679e738a5d8SSebastian Kreutzer   return __xray::UnpackId(PackedId).second;
680e738a5d8SSebastian Kreutzer }
681e738a5d8SSebastian Kreutzer 
682e738a5d8SSebastian Kreutzer int32_t __xray_unpack_object_id(int32_t PackedId) {
683e738a5d8SSebastian Kreutzer   return __xray::UnpackId(PackedId).first;
684e738a5d8SSebastian Kreutzer }
685e738a5d8SSebastian Kreutzer 
686e738a5d8SSebastian Kreutzer int32_t __xray_pack_id(int32_t FuncId, int32_t ObjId) {
687e738a5d8SSebastian Kreutzer   return __xray::MakePackedId(FuncId, ObjId);
688b3018603SNico Weber }
689