1/// This code was based on the code by Fedor Korotkiy (prime@yandex-team.ru) for YT product in Yandex.
2
3#if defined(__has_feature)
4 #if __has_feature(address_sanitizer)
5 #define ADDRESS_SANITIZER 1
6 #endif
7 #if __has_feature(thread_sanitizer)
8 #define THREAD_SANITIZER 1
9 #endif
10#else
11 #if defined(__SANITIZE_ADDRESS__)
12 #define ADDRESS_SANITIZER 1
13 #endif
14 #if defined(__SANITIZE_THREAD__)
15 #define THREAD_SANITIZER 1
16 #endif
17#endif
18
19#if defined(__linux__) && !defined(THREAD_SANITIZER)
20 #define USE_PHDR_CACHE 1
21#endif
22
23#define __msan_unpoison(X, Y)
24#if defined(__has_feature)
25# if __has_feature(memory_sanitizer)
26# undef __msan_unpoison
27# include <sanitizer/msan_interface.h>
28# endif
29#endif
30
31/// Thread Sanitizer uses dl_iterate_phdr function on initialization and fails if we provide our own.
32#ifdef USE_PHDR_CACHE
33
34#include <link.h>
35#include <dlfcn.h>
36#include <vector>
37#include <atomic>
38#include <cstddef>
39#include <stdexcept>
40
41
42namespace
43{
44
45// This is adapted from
46// https://github.com/scylladb/seastar/blob/master/core/exception_hacks.hh
47// https://github.com/scylladb/seastar/blob/master/core/exception_hacks.cc
48
49using DLIterateFunction = int (*) (int (*callback) (dl_phdr_info * info, size_t size, void * data), void * data);
50
51DLIterateFunction getOriginalDLIteratePHDR()
52{
53 void * func = dlsym(RTLD_NEXT, "dl_iterate_phdr");
54 if (!func)
55 throw std::runtime_error("Cannot find dl_iterate_phdr function with dlsym");
56 return reinterpret_cast<DLIterateFunction>(func);
57}
58
59
60using PHDRCache = std::vector<dl_phdr_info>;
61std::atomic<PHDRCache *> phdr_cache {};
62
63}
64
65
66extern "C"
67#ifndef __clang__
68[[gnu::visibility("default")]]
69[[gnu::externally_visible]]
70#endif
71int dl_iterate_phdr(int (*callback) (dl_phdr_info * info, size_t size, void * data), void * data)
72{
73 auto current_phdr_cache = phdr_cache.load();
74 if (!current_phdr_cache)
75 {
76 // Cache is not yet populated, pass through to the original function.
77 return getOriginalDLIteratePHDR()(callback, data);
78 }
79
80 int result = 0;
81 for (auto & entry : *current_phdr_cache)
82 {
83 result = callback(&entry, offsetof(dl_phdr_info, dlpi_adds), data);
84 if (result != 0)
85 break;
86 }
87 return result;
88}
89
90
91extern "C"
92{
93#ifdef ADDRESS_SANITIZER
94void __lsan_ignore_object(const void *);
95#else
96void __lsan_ignore_object(const void *) {}
97#endif
98}
99
100
101void updatePHDRCache()
102{
103 // Fill out ELF header cache for access without locking.
104 // This assumes no dynamic object loading/unloading after this point
105
106 PHDRCache * new_phdr_cache = new PHDRCache;
107 getOriginalDLIteratePHDR()([] (dl_phdr_info * info, size_t /*size*/, void * data)
108 {
109 // `info` is created by dl_iterate_phdr, which is a non-instrumented
110 // libc function, so we have to unpoison it manually.
111 __msan_unpoison(info, sizeof(*info));
112
113 reinterpret_cast<PHDRCache *>(data)->push_back(*info);
114 return 0;
115 }, new_phdr_cache);
116 phdr_cache.store(new_phdr_cache);
117
118 /// Memory is intentionally leaked.
119 __lsan_ignore_object(new_phdr_cache);
120}
121
122
123bool hasPHDRCache()
124{
125 return phdr_cache.load() != nullptr;
126}
127
128#else
129
130void updatePHDRCache() {}
131bool hasPHDRCache() { return false; }
132
133#endif
134