1 //=-- lsan_common_linux.cc ------------------------------------------------===//
3 // The LLVM Compiler Infrastructure
5 // This file is distributed under the University of Illinois Open Source
6 // License. See LICENSE.TXT for details.
8 //===----------------------------------------------------------------------===//
10 // This file is a part of LeakSanitizer.
11 // Implementation of common leak checking functionality. Linux-specific code.
13 //===----------------------------------------------------------------------===//
15 #include "sanitizer_common/sanitizer_platform.h"
16 #include "lsan_common.h"
18 #if CAN_SANITIZE_LEAKS && SANITIZER_LINUX
21 #include "sanitizer_common/sanitizer_common.h"
22 #include "sanitizer_common/sanitizer_flags.h"
23 #include "sanitizer_common/sanitizer_linux.h"
24 #include "sanitizer_common/sanitizer_stackdepot.h"
28 static const char kLinkerName[] = "ld";
30 static char linker_placeholder[sizeof(LoadedModule)] ALIGNED(64);
31 static LoadedModule *linker = nullptr;
33 static bool IsLinker(const char* full_name) {
34 return LibraryNameIs(full_name, kLinkerName);
37 __attribute__((tls_model("initial-exec")))
38 THREADLOCAL int disable_counter;
39 bool DisabledInThisThread() { return disable_counter > 0; }
40 void DisableInThisThread() { disable_counter++; }
41 void EnableInThisThread() {
42 if (disable_counter == 0) {
43 DisableCounterUnderflow();
48 void InitializePlatformSpecificModules() {
49 ListOfModules modules;
51 for (LoadedModule &module : modules) {
52 if (!IsLinker(module.full_name())) continue;
53 if (linker == nullptr) {
54 linker = reinterpret_cast<LoadedModule *>(linker_placeholder);
56 module = LoadedModule();
58 VReport(1, "LeakSanitizer: Multiple modules match \"%s\". "
59 "TLS will not be handled correctly.\n", kLinkerName);
65 if (linker == nullptr) {
66 VReport(1, "LeakSanitizer: Dynamic linker not found. "
67 "TLS will not be handled correctly.\n");
71 static int ProcessGlobalRegionsCallback(struct dl_phdr_info *info, size_t size,
73 Frontier *frontier = reinterpret_cast<Frontier *>(data);
74 for (uptr j = 0; j < info->dlpi_phnum; j++) {
75 const ElfW(Phdr) *phdr = &(info->dlpi_phdr[j]);
76 // We're looking for .data and .bss sections, which reside in writeable,
78 if (!(phdr->p_flags & PF_W) || (phdr->p_type != PT_LOAD) ||
81 uptr begin = info->dlpi_addr + phdr->p_vaddr;
82 uptr end = begin + phdr->p_memsz;
83 ScanGlobalRange(begin, end, frontier);
88 // Scans global variables for heap pointers.
89 void ProcessGlobalRegions(Frontier *frontier) {
90 if (!flags()->use_globals) return;
91 dl_iterate_phdr(ProcessGlobalRegionsCallback, frontier);
94 LoadedModule *GetLinker() { return linker; }
96 void ProcessPlatformSpecificAllocations(Frontier *frontier) {}
98 struct DoStopTheWorldParam {
99 StopTheWorldCallback callback;
103 // While calling Die() here is undefined behavior and can potentially
104 // cause race conditions, it isn't possible to intercept exit on linux,
105 // so we have no choice but to call Die() from the atexit handler.
107 if (common_flags()->exitcode) Die();
110 static int DoStopTheWorldCallback(struct dl_phdr_info *info, size_t size,
112 DoStopTheWorldParam *param = reinterpret_cast<DoStopTheWorldParam *>(data);
113 StopTheWorld(param->callback, param->argument);
117 // LSan calls dl_iterate_phdr() from the tracer task. This may deadlock: if one
118 // of the threads is frozen while holding the libdl lock, the tracer will hang
119 // in dl_iterate_phdr() forever.
120 // Luckily, (a) the lock is reentrant and (b) libc can't distinguish between the
121 // tracer task and the thread that spawned it. Thus, if we run the tracer task
122 // while holding the libdl lock in the parent thread, we can safely reenter it
123 // in the tracer. The solution is to run stoptheworld from a dl_iterate_phdr()
124 // callback in the parent thread.
125 void DoStopTheWorld(StopTheWorldCallback callback, void *argument) {
126 DoStopTheWorldParam param = {callback, argument};
127 dl_iterate_phdr(DoStopTheWorldCallback, ¶m);
130 } // namespace __lsan
132 #endif // CAN_SANITIZE_LEAKS && SANITIZER_LINUX