Bug Summary

File:projects/compiler-rt/lib/cfi/cfi.cc
Warning:line 225, column 35
Access to field 'st_name' results in a dereference of a null pointer (loaded from variable 'p')

Annotated Source Code

1//===-------- cfi.cc ------------------------------------------------------===//
2//
3// The LLVM Compiler Infrastructure
4//
5// This file is distributed under the University of Illinois Open Source
6// License. See LICENSE.TXT for details.
7//
8//===----------------------------------------------------------------------===//
9//
10// This file implements the runtime support for the cross-DSO CFI.
11//
12//===----------------------------------------------------------------------===//
13
14#include <assert.h>
15#include <elf.h>
16#include <link.h>
17#include <string.h>
18#include <sys/mman.h>
19
20typedef ElfW(Phdr)Elf32_Phdr Elf_Phdr;
21typedef ElfW(Ehdr)Elf32_Ehdr Elf_Ehdr;
22
23#include "interception/interception.h"
24#include "sanitizer_common/sanitizer_common.h"
25#include "sanitizer_common/sanitizer_flag_parser.h"
26#include "ubsan/ubsan_init.h"
27#include "ubsan/ubsan_flags.h"
28
29#ifdef CFI_ENABLE_DIAG1
30#include "ubsan/ubsan_handlers.h"
31#endif
32
33using namespace __sanitizer;
34
35namespace __cfi {
36
37#define kCfiShadowLimitsStorageSize4096 4096 // 1 page
38// Lets hope that the data segment is mapped with 4K pages.
39// The pointer to the cfi shadow region is stored at the start of this page.
40// The rest of the page is unused and re-mapped read-only.
41static union {
42 char space[kCfiShadowLimitsStorageSize4096];
43 struct {
44 uptr start;
45 uptr size;
46 } limits;
47} cfi_shadow_limits_storage
48 __attribute__((aligned(kCfiShadowLimitsStorageSize4096)));
49static constexpr uptr kShadowGranularity = 12;
50static constexpr uptr kShadowAlign = 1UL << kShadowGranularity; // 4096
51
52static constexpr uint16_t kInvalidShadow = 0;
53static constexpr uint16_t kUncheckedShadow = 0xFFFFU;
54
55// Get the start address of the CFI shadow region.
56uptr GetShadow() {
57 return cfi_shadow_limits_storage.limits.start;
58}
59
60uptr GetShadowSize() {
61 return cfi_shadow_limits_storage.limits.size;
62}
63
64// This will only work while the shadow is not allocated.
65void SetShadowSize(uptr size) {
66 cfi_shadow_limits_storage.limits.size = size;
67}
68
69uptr MemToShadowOffset(uptr x) {
70 return (x >> kShadowGranularity) << 1;
71}
72
73uint16_t *MemToShadow(uptr x, uptr shadow_base) {
74 return (uint16_t *)(shadow_base + MemToShadowOffset(x));
75}
76
77typedef int (*CFICheckFn)(u64, void *, void *);
78
79// This class reads and decodes the shadow contents.
80class ShadowValue {
81 uptr addr;
82 uint16_t v;
83 explicit ShadowValue(uptr addr, uint16_t v) : addr(addr), v(v) {}
84
85public:
86 bool is_invalid() const { return v == kInvalidShadow; }
87
88 bool is_unchecked() const { return v == kUncheckedShadow; }
89
90 CFICheckFn get_cfi_check() const {
91 assert(!is_invalid() && !is_unchecked())((!is_invalid() && !is_unchecked()) ? static_cast<
void> (0) : __assert_fail ("!is_invalid() && !is_unchecked()"
, "/tmp/buildd/llvm-toolchain-snapshot-5.0~svn298304/projects/compiler-rt/lib/cfi/cfi.cc"
, 91, __PRETTY_FUNCTION__))
;
92 uptr aligned_addr = addr & ~(kShadowAlign - 1);
93 uptr p = aligned_addr - (((uptr)v - 1) << kShadowGranularity);
94 return reinterpret_cast<CFICheckFn>(p);
95 }
96
97 // Load a shadow value for the given application memory address.
98 static const ShadowValue load(uptr addr) {
99 uptr shadow_base = GetShadow();
100 uptr shadow_offset = MemToShadowOffset(addr);
101 if (shadow_offset > GetShadowSize())
102 return ShadowValue(addr, kInvalidShadow);
103 else
104 return ShadowValue(
105 addr, *reinterpret_cast<uint16_t *>(shadow_base + shadow_offset));
106 }
107};
108
109class ShadowBuilder {
110 uptr shadow_;
111
112public:
113 // Allocate a new empty shadow (for the entire address space) on the side.
114 void Start();
115 // Mark the given address range as unchecked.
116 // This is used for uninstrumented libraries like libc.
117 // Any CFI check with a target in that range will pass.
118 void AddUnchecked(uptr begin, uptr end);
119 // Mark the given address range as belonging to a library with the given
120 // cfi_check function.
121 void Add(uptr begin, uptr end, uptr cfi_check);
122 // Finish shadow construction. Atomically switch the current active shadow
123 // region with the newly constructed one and deallocate the former.
124 void Install();
125};
126
127void ShadowBuilder::Start() {
128 shadow_ = (uptr)MmapNoReserveOrDie(GetShadowSize(), "CFI shadow");
129 VReport(1, "CFI: shadow at %zx .. %zx\n", shadow_, shadow_ + GetShadowSize())do { if ((uptr)Verbosity() >= (1)) Report("CFI: shadow at %zx .. %zx\n"
, shadow_, shadow_ + GetShadowSize()); } while (0)
;
130}
131
132void ShadowBuilder::AddUnchecked(uptr begin, uptr end) {
133 uint16_t *shadow_begin = MemToShadow(begin, shadow_);
134 uint16_t *shadow_end = MemToShadow(end - 1, shadow_) + 1;
135 memset(shadow_begin, kUncheckedShadow,
136 (shadow_end - shadow_begin) * sizeof(*shadow_begin));
137}
138
139void ShadowBuilder::Add(uptr begin, uptr end, uptr cfi_check) {
140 assert((cfi_check & (kShadowAlign - 1)) == 0)(((cfi_check & (kShadowAlign - 1)) == 0) ? static_cast<
void> (0) : __assert_fail ("(cfi_check & (kShadowAlign - 1)) == 0"
, "/tmp/buildd/llvm-toolchain-snapshot-5.0~svn298304/projects/compiler-rt/lib/cfi/cfi.cc"
, 140, __PRETTY_FUNCTION__))
;
141
142 // Don't fill anything below cfi_check. We can not represent those addresses
143 // in the shadow, and must make sure at codegen to place all valid call
144 // targets above cfi_check.
145 begin = Max(begin, cfi_check);
146 uint16_t *s = MemToShadow(begin, shadow_);
147 uint16_t *s_end = MemToShadow(end - 1, shadow_) + 1;
148 uint16_t sv = ((begin - cfi_check) >> kShadowGranularity) + 1;
149 for (; s < s_end; s++, sv++)
150 *s = sv;
151}
152
153#if SANITIZER_LINUX1
154void ShadowBuilder::Install() {
155 MprotectReadOnly(shadow_, GetShadowSize());
156 uptr main_shadow = GetShadow();
157 if (main_shadow) {
158 // Update.
159 void *res = mremap((void *)shadow_, GetShadowSize(), GetShadowSize(),
160 MREMAP_MAYMOVE1 | MREMAP_FIXED2, (void *)main_shadow);
161 CHECK(res != MAP_FAILED)do { __sanitizer::u64 v1 = (u64)((res != ((void *) -1))); __sanitizer
::u64 v2 = (u64)(0); if (__builtin_expect(!!(!(v1 != v2)), 0)
) __sanitizer::CheckFailed("/tmp/buildd/llvm-toolchain-snapshot-5.0~svn298304/projects/compiler-rt/lib/cfi/cfi.cc"
, 161, "(" "(res != ((void *) -1))" ") " "!=" " (" "0" ")", v1
, v2); } while (false)
;
162 } else {
163 // Initial setup.
164 CHECK_EQ(kCfiShadowLimitsStorageSize, GetPageSizeCached())do { __sanitizer::u64 v1 = (u64)((4096)); __sanitizer::u64 v2
= (u64)((GetPageSizeCached())); if (__builtin_expect(!!(!(v1
== v2)), 0)) __sanitizer::CheckFailed("/tmp/buildd/llvm-toolchain-snapshot-5.0~svn298304/projects/compiler-rt/lib/cfi/cfi.cc"
, 164, "(" "(4096)" ") " "==" " (" "(GetPageSizeCached())" ")"
, v1, v2); } while (false)
;
165 CHECK_EQ(0, GetShadow())do { __sanitizer::u64 v1 = (u64)((0)); __sanitizer::u64 v2 = (
u64)((GetShadow())); if (__builtin_expect(!!(!(v1 == v2)), 0)
) __sanitizer::CheckFailed("/tmp/buildd/llvm-toolchain-snapshot-5.0~svn298304/projects/compiler-rt/lib/cfi/cfi.cc"
, 165, "(" "(0)" ") " "==" " (" "(GetShadow())" ")", v1, v2);
} while (false)
;
166 cfi_shadow_limits_storage.limits.start = shadow_;
167 MprotectReadOnly((uptr)&cfi_shadow_limits_storage,
168 sizeof(cfi_shadow_limits_storage));
169 CHECK_EQ(shadow_, GetShadow())do { __sanitizer::u64 v1 = (u64)((shadow_)); __sanitizer::u64
v2 = (u64)((GetShadow())); if (__builtin_expect(!!(!(v1 == v2
)), 0)) __sanitizer::CheckFailed("/tmp/buildd/llvm-toolchain-snapshot-5.0~svn298304/projects/compiler-rt/lib/cfi/cfi.cc"
, 169, "(" "(shadow_)" ") " "==" " (" "(GetShadow())" ")", v1
, v2); } while (false)
;
170 }
171}
172#else
173#error not implemented
174#endif
175
176// This is a workaround for a glibc bug:
177// https://sourceware.org/bugzilla/show_bug.cgi?id=15199
178// Other platforms can, hopefully, just do
179// dlopen(RTLD_NOLOAD | RTLD_LAZY)
180// dlsym("__cfi_check").
181uptr find_cfi_check_in_dso(dl_phdr_info *info) {
182 const ElfW(Dyn)Elf32_Dyn *dynamic = nullptr;
183 for (int i = 0; i < info->dlpi_phnum; ++i) {
2
Assuming the condition is true
3
Loop condition is true. Entering loop body
6
Assuming the condition is true
7
Loop condition is true. Entering loop body
184 if (info->dlpi_phdr[i].p_type == PT_DYNAMIC2) {
4
Assuming the condition is false
5
Taking false branch
8
Assuming the condition is true
9
Taking true branch
185 dynamic =
186 (const ElfW(Dyn)Elf32_Dyn *)(info->dlpi_addr + info->dlpi_phdr[i].p_vaddr);
187 break;
10
Execution continues on line 190
188 }
189 }
190 if (!dynamic) return 0;
11
Assuming 'dynamic' is non-null
12
Taking false branch
191 uptr strtab = 0, symtab = 0;
13
'symtab' initialized to 0
192 for (const ElfW(Dyn)Elf32_Dyn *p = dynamic; p->d_tag != PT_NULL0; ++p) {
14
Assuming the condition is true
15
Loop condition is true. Entering loop body
20
Assuming the condition is false
21
Loop condition is false. Execution continues on line 199
193 if (p->d_tag == DT_SYMTAB6)
16
Assuming the condition is false
17
Taking false branch
194 symtab = p->d_un.d_ptr;
195 else if (p->d_tag == DT_STRTAB5)
18
Assuming the condition is true
19
Taking true branch
196 strtab = p->d_un.d_ptr;
197 }
198
199 if (symtab > strtab) {
22
Taking false branch
200 VReport(1, "Can not handle: symtab > strtab (%p > %zx)\n", symtab, strtab)do { if ((uptr)Verbosity() >= (1)) Report("Can not handle: symtab > strtab (%p > %zx)\n"
, symtab, strtab); } while (0)
;
201 return 0;
202 }
203
204 // Verify that strtab and symtab are inside of the same LOAD segment.
205 // This excludes VDSO, which has (very high) bogus strtab and symtab pointers.
206 int phdr_idx;
207 for (phdr_idx = 0; phdr_idx < info->dlpi_phnum; phdr_idx++) {
23
Loop condition is true. Entering loop body
208 const Elf_Phdr *phdr = &info->dlpi_phdr[phdr_idx];
209 if (phdr->p_type == PT_LOAD1) {
24
Assuming the condition is true
25
Taking true branch
210 uptr beg = info->dlpi_addr + phdr->p_vaddr;
211 uptr end = beg + phdr->p_memsz;
212 if (strtab >= beg && strtab < end && symtab >= beg && symtab < end)
26
Assuming 'symtab' is >= 'beg'
27
Assuming 'symtab' is < 'end'
28
Taking true branch
213 break;
29
Execution continues on line 216
214 }
215 }
216 if (phdr_idx == info->dlpi_phnum) {
30
Taking false branch
217 // Nope, either different segments or just bogus pointers.
218 // Can not handle this.
219 VReport(1, "Can not handle: symtab %p, strtab %zx\n", symtab, strtab)do { if ((uptr)Verbosity() >= (1)) Report("Can not handle: symtab %p, strtab %zx\n"
, symtab, strtab); } while (0)
;
220 return 0;
221 }
222
223 for (const ElfW(Sym)Elf32_Sym *p = (const ElfW(Sym)Elf32_Sym *)symtab; (ElfW(Addr)Elf32_Addr)p < strtab;
31
'p' initialized to a null pointer value
32
Assuming 'p' is < 'strtab'
33
Loop condition is true. Entering loop body
224 ++p) {
225 char *name = (char*)(strtab + p->st_name);
34
Access to field 'st_name' results in a dereference of a null pointer (loaded from variable 'p')
226 if (strcmp(name, "__cfi_check") == 0) {
227 assert(p->st_info == ELF32_ST_INFO(STB_GLOBAL, STT_FUNC))((p->st_info == (((1) << 4) + ((2) & 0xf))) ? static_cast
<void> (0) : __assert_fail ("p->st_info == ELF32_ST_INFO(STB_GLOBAL, STT_FUNC)"
, "/tmp/buildd/llvm-toolchain-snapshot-5.0~svn298304/projects/compiler-rt/lib/cfi/cfi.cc"
, 227, __PRETTY_FUNCTION__))
;
228 uptr addr = info->dlpi_addr + p->st_value;
229 return addr;
230 }
231 }
232 return 0;
233}
234
235int dl_iterate_phdr_cb(dl_phdr_info *info, size_t size, void *data) {
236 uptr cfi_check = find_cfi_check_in_dso(info);
1
Calling 'find_cfi_check_in_dso'
237 if (cfi_check)
238 VReport(1, "Module '%s' __cfi_check %zx\n", info->dlpi_name, cfi_check)do { if ((uptr)Verbosity() >= (1)) Report("Module '%s' __cfi_check %zx\n"
, info->dlpi_name, cfi_check); } while (0)
;
239
240 ShadowBuilder *b = reinterpret_cast<ShadowBuilder *>(data);
241
242 for (int i = 0; i < info->dlpi_phnum; i++) {
243 const Elf_Phdr *phdr = &info->dlpi_phdr[i];
244 if (phdr->p_type == PT_LOAD1) {
245 // Jump tables are in the executable segment.
246 // VTables are in the non-executable one.
247 // Need to fill shadow for both.
248 // FIXME: reject writable if vtables are in the r/o segment. Depend on
249 // PT_RELRO?
250 uptr cur_beg = info->dlpi_addr + phdr->p_vaddr;
251 uptr cur_end = cur_beg + phdr->p_memsz;
252 if (cfi_check) {
253 VReport(1, " %zx .. %zx\n", cur_beg, cur_end)do { if ((uptr)Verbosity() >= (1)) Report(" %zx .. %zx\n"
, cur_beg, cur_end); } while (0)
;
254 b->Add(cur_beg, cur_end, cfi_check);
255 } else {
256 b->AddUnchecked(cur_beg, cur_end);
257 }
258 }
259 }
260 return 0;
261}
262
263// Init or update shadow for the current set of loaded libraries.
264void UpdateShadow() {
265 ShadowBuilder b;
266 b.Start();
267 dl_iterate_phdr(dl_iterate_phdr_cb, &b);
268 b.Install();
269}
270
271void InitShadow() {
272 CHECK_EQ(0, GetShadow())do { __sanitizer::u64 v1 = (u64)((0)); __sanitizer::u64 v2 = (
u64)((GetShadow())); if (__builtin_expect(!!(!(v1 == v2)), 0)
) __sanitizer::CheckFailed("/tmp/buildd/llvm-toolchain-snapshot-5.0~svn298304/projects/compiler-rt/lib/cfi/cfi.cc"
, 272, "(" "(0)" ") " "==" " (" "(GetShadow())" ")", v1, v2);
} while (false)
;
273 CHECK_EQ(0, GetShadowSize())do { __sanitizer::u64 v1 = (u64)((0)); __sanitizer::u64 v2 = (
u64)((GetShadowSize())); if (__builtin_expect(!!(!(v1 == v2))
, 0)) __sanitizer::CheckFailed("/tmp/buildd/llvm-toolchain-snapshot-5.0~svn298304/projects/compiler-rt/lib/cfi/cfi.cc"
, 273, "(" "(0)" ") " "==" " (" "(GetShadowSize())" ")", v1, v2
); } while (false)
;
274
275 uptr vma = GetMaxVirtualAddress();
276 // Shadow is 2 -> 2**kShadowGranularity.
277 SetShadowSize((vma >> (kShadowGranularity - 1)) + 1);
278 VReport(1, "CFI: VMA size %zx, shadow size %zx\n", vma, GetShadowSize())do { if ((uptr)Verbosity() >= (1)) Report("CFI: VMA size %zx, shadow size %zx\n"
, vma, GetShadowSize()); } while (0)
;
279
280 UpdateShadow();
281}
282
283THREADLOCAL__thread int in_loader;
284BlockingMutex shadow_update_lock(LINKER_INITIALIZED);
285
286void EnterLoader() {
287 if (in_loader == 0) {
288 shadow_update_lock.Lock();
289 }
290 ++in_loader;
291}
292
293void ExitLoader() {
294 CHECK(in_loader > 0)do { __sanitizer::u64 v1 = (u64)((in_loader > 0)); __sanitizer
::u64 v2 = (u64)(0); if (__builtin_expect(!!(!(v1 != v2)), 0)
) __sanitizer::CheckFailed("/tmp/buildd/llvm-toolchain-snapshot-5.0~svn298304/projects/compiler-rt/lib/cfi/cfi.cc"
, 294, "(" "(in_loader > 0)" ") " "!=" " (" "0" ")", v1, v2
); } while (false)
;
295 --in_loader;
296 UpdateShadow();
297 if (in_loader == 0) {
298 shadow_update_lock.Unlock();
299 }
300}
301
302ALWAYS_INLINEinline __attribute__((always_inline)) void CfiSlowPathCommon(u64 CallSiteTypeId, void *Ptr,
303 void *DiagData) {
304 uptr Addr = (uptr)Ptr;
305 VReport(3, "__cfi_slowpath: %llx, %p\n", CallSiteTypeId, Ptr)do { if ((uptr)Verbosity() >= (3)) Report("__cfi_slowpath: %llx, %p\n"
, CallSiteTypeId, Ptr); } while (0)
;
306 ShadowValue sv = ShadowValue::load(Addr);
307 if (sv.is_invalid()) {
308 VReport(1, "CFI: invalid memory region for a check target: %p\n", Ptr)do { if ((uptr)Verbosity() >= (1)) Report("CFI: invalid memory region for a check target: %p\n"
, Ptr); } while (0)
;
309#ifdef CFI_ENABLE_DIAG1
310 if (DiagData) {
311 __ubsan_handle_cfi_check_fail(
312 reinterpret_cast<__ubsan::CFICheckFailData *>(DiagData), Addr, false);
313 return;
314 }
315#endif
316 Trap();
317 }
318 if (sv.is_unchecked()) {
319 VReport(2, "CFI: unchecked call (shadow=FFFF): %p\n", Ptr)do { if ((uptr)Verbosity() >= (2)) Report("CFI: unchecked call (shadow=FFFF): %p\n"
, Ptr); } while (0)
;
320 return;
321 }
322 CFICheckFn cfi_check = sv.get_cfi_check();
323 VReport(2, "__cfi_check at %p\n", cfi_check)do { if ((uptr)Verbosity() >= (2)) Report("__cfi_check at %p\n"
, cfi_check); } while (0)
;
324 cfi_check(CallSiteTypeId, Ptr, DiagData);
325}
326
327void InitializeFlags() {
328 SetCommonFlagsDefaults();
329#ifdef CFI_ENABLE_DIAG1
330 __ubsan::Flags *uf = __ubsan::flags();
331 uf->SetDefaults();
332#endif
333
334 FlagParser cfi_parser;
335 RegisterCommonFlags(&cfi_parser);
336 cfi_parser.ParseString(GetEnv("CFI_OPTIONS"));
337
338#ifdef CFI_ENABLE_DIAG1
339 FlagParser ubsan_parser;
340 __ubsan::RegisterUbsanFlags(&ubsan_parser, uf);
341 RegisterCommonFlags(&ubsan_parser);
342
343 const char *ubsan_default_options = __ubsan::MaybeCallUbsanDefaultOptions();
344 ubsan_parser.ParseString(ubsan_default_options);
345 ubsan_parser.ParseString(GetEnv("UBSAN_OPTIONS"));
346#endif
347
348 InitializeCommonFlags();
349
350 if (Verbosity())
351 ReportUnrecognizedFlags();
352
353 if (common_flags()->help) {
354 cfi_parser.PrintFlagDescriptions();
355 }
356}
357
358} // namespace __cfi
359
360using namespace __cfi;
361
362extern "C" SANITIZER_INTERFACE_ATTRIBUTE__attribute__((visibility("default"))) void
363__cfi_slowpath(u64 CallSiteTypeId, void *Ptr) {
364 CfiSlowPathCommon(CallSiteTypeId, Ptr, nullptr);
365}
366
367#ifdef CFI_ENABLE_DIAG1
368extern "C" SANITIZER_INTERFACE_ATTRIBUTE__attribute__((visibility("default"))) void
369__cfi_slowpath_diag(u64 CallSiteTypeId, void *Ptr, void *DiagData) {
370 CfiSlowPathCommon(CallSiteTypeId, Ptr, DiagData);
371}
372#endif
373
374// Setup shadow for dlopen()ed libraries.
375// The actual shadow setup happens after dlopen() returns, which means that
376// a library can not be a target of any CFI checks while its constructors are
377// running. It's unclear how to fix this without some extra help from libc.
378// In glibc, mmap inside dlopen is not interceptable.
379// Maybe a seccomp-bpf filter?
380// We could insert a high-priority constructor into the library, but that would
381// not help with the uninstrumented libraries.
382INTERCEPTOR(void*, dlopen, const char *filename, int flag)typedef void* (*dlopen_f)(const char *filename, int flag); namespace
__interception { dlopen_f real_dlopen; } extern "C" void* dlopen
(const char *filename, int flag) __attribute__((weak, alias("__interceptor_"
"dlopen"), visibility("default"))); extern "C" __attribute__
((visibility("default"))) void* __interceptor_dlopen(const char
*filename, int flag)
{
383 EnterLoader();
384 void *handle = REAL(dlopen)__interception::real_dlopen(filename, flag);
385 ExitLoader();
386 return handle;
387}
388
389INTERCEPTOR(int, dlclose, void *handle)typedef int (*dlclose_f)(void *handle); namespace __interception
{ dlclose_f real_dlclose; } extern "C" int dlclose(void *handle
) __attribute__((weak, alias("__interceptor_" "dlclose"), visibility
("default"))); extern "C" __attribute__((visibility("default"
))) int __interceptor_dlclose(void *handle)
{
390 EnterLoader();
391 int res = REAL(dlclose)__interception::real_dlclose(handle);
392 ExitLoader();
393 return res;
394}
395
396extern "C" SANITIZER_INTERFACE_ATTRIBUTE__attribute__((visibility("default")))
397#if !SANITIZER_CAN_USE_PREINIT_ARRAY1
398// On ELF platforms, the constructor is invoked using .preinit_array (see below)
399__attribute__((constructor(0)))
400#endif
401void __cfi_init() {
402 SanitizerToolName = "CFI";
403 InitializeFlags();
404 InitShadow();
405
406 INTERCEPT_FUNCTION(dlopen)::__interception::GetRealFunctionAddress( "dlopen", (::__interception
::uptr *)&__interception::real_dlopen, (::__interception::
uptr) & (dlopen), (::__interception::uptr) & __interceptor_dlopen
)
;
407 INTERCEPT_FUNCTION(dlclose)::__interception::GetRealFunctionAddress( "dlclose", (::__interception
::uptr *)&__interception::real_dlclose, (::__interception
::uptr) & (dlclose), (::__interception::uptr) & __interceptor_dlclose
)
;
408
409#ifdef CFI_ENABLE_DIAG1
410 __ubsan::InitAsPlugin();
411#endif
412}
413
414#if SANITIZER_CAN_USE_PREINIT_ARRAY1
415// On ELF platforms, run cfi initialization before any other constructors.
416// On other platforms we use the constructor attribute to arrange to run our
417// initialization early.
418extern "C" {
419__attribute__((section(".preinit_array"),
420 used)) void (*__cfi_preinit)(void) = __cfi_init;
421}
422#endif