]>
Commit | Line | Data |
---|---|---|
1a4d82fc JJ |
1 | //=-- lsan_common_linux.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 is a part of LeakSanitizer. | |
11 | // Implementation of common leak checking functionality. Linux-specific code. | |
12 | // | |
13 | //===----------------------------------------------------------------------===// | |
14 | ||
15 | #include "sanitizer_common/sanitizer_platform.h" | |
16 | #include "lsan_common.h" | |
17 | ||
18 | #if CAN_SANITIZE_LEAKS && SANITIZER_LINUX | |
19 | #include <link.h> | |
20 | ||
21 | #include "sanitizer_common/sanitizer_common.h" | |
22 | #include "sanitizer_common/sanitizer_flags.h" | |
2c00a5a8 | 23 | #include "sanitizer_common/sanitizer_getauxval.h" |
1a4d82fc JJ |
24 | #include "sanitizer_common/sanitizer_linux.h" |
25 | #include "sanitizer_common/sanitizer_stackdepot.h" | |
26 | ||
27 | namespace __lsan { | |
28 | ||
29 | static const char kLinkerName[] = "ld"; | |
5bcae85e SL |
30 | |
31 | static char linker_placeholder[sizeof(LoadedModule)] ALIGNED(64); | |
92a42be0 | 32 | static LoadedModule *linker = nullptr; |
1a4d82fc | 33 | |
2c00a5a8 XL |
34 | static bool IsLinker(const LoadedModule& module) { |
35 | #if SANITIZER_USE_GETAUXVAL | |
36 | return module.base_address() == getauxval(AT_BASE); | |
37 | #else | |
38 | return LibraryNameIs(module.full_name(), kLinkerName); | |
39 | #endif // SANITIZER_USE_GETAUXVAL | |
40 | } | |
41 | ||
42 | __attribute__((tls_model("initial-exec"))) | |
43 | THREADLOCAL int disable_counter; | |
44 | bool DisabledInThisThread() { return disable_counter > 0; } | |
45 | void DisableInThisThread() { disable_counter++; } | |
46 | void EnableInThisThread() { | |
47 | if (disable_counter == 0) { | |
48 | DisableCounterUnderflow(); | |
49 | } | |
50 | disable_counter--; | |
1a4d82fc JJ |
51 | } |
52 | ||
53 | void InitializePlatformSpecificModules() { | |
5bcae85e SL |
54 | ListOfModules modules; |
55 | modules.init(); | |
56 | for (LoadedModule &module : modules) { | |
2c00a5a8 XL |
57 | if (!IsLinker(module)) |
58 | continue; | |
5bcae85e SL |
59 | if (linker == nullptr) { |
60 | linker = reinterpret_cast<LoadedModule *>(linker_placeholder); | |
61 | *linker = module; | |
62 | module = LoadedModule(); | |
63 | } else { | |
64 | VReport(1, "LeakSanitizer: Multiple modules match \"%s\". " | |
2c00a5a8 XL |
65 | "TLS and other allocations originating from linker might be " |
66 | "falsely reported as leaks.\n", kLinkerName); | |
5bcae85e SL |
67 | linker->clear(); |
68 | linker = nullptr; | |
69 | return; | |
70 | } | |
1a4d82fc | 71 | } |
2c00a5a8 XL |
72 | if (linker == nullptr) { |
73 | VReport(1, "LeakSanitizer: Dynamic linker not found. TLS and other " | |
74 | "allocations originating from linker might be falsely reported " | |
75 | "as leaks.\n"); | |
76 | } | |
1a4d82fc JJ |
77 | } |
78 | ||
79 | static int ProcessGlobalRegionsCallback(struct dl_phdr_info *info, size_t size, | |
80 | void *data) { | |
81 | Frontier *frontier = reinterpret_cast<Frontier *>(data); | |
82 | for (uptr j = 0; j < info->dlpi_phnum; j++) { | |
83 | const ElfW(Phdr) *phdr = &(info->dlpi_phdr[j]); | |
84 | // We're looking for .data and .bss sections, which reside in writeable, | |
85 | // loadable segments. | |
86 | if (!(phdr->p_flags & PF_W) || (phdr->p_type != PT_LOAD) || | |
87 | (phdr->p_memsz == 0)) | |
88 | continue; | |
89 | uptr begin = info->dlpi_addr + phdr->p_vaddr; | |
90 | uptr end = begin + phdr->p_memsz; | |
2c00a5a8 | 91 | ScanGlobalRange(begin, end, frontier); |
1a4d82fc JJ |
92 | } |
93 | return 0; | |
94 | } | |
95 | ||
96 | // Scans global variables for heap pointers. | |
97 | void ProcessGlobalRegions(Frontier *frontier) { | |
98 | if (!flags()->use_globals) return; | |
1a4d82fc JJ |
99 | dl_iterate_phdr(ProcessGlobalRegionsCallback, frontier); |
100 | } | |
101 | ||
2c00a5a8 | 102 | LoadedModule *GetLinker() { return linker; } |
1a4d82fc | 103 | |
2c00a5a8 | 104 | void ProcessPlatformSpecificAllocations(Frontier *frontier) {} |
1a4d82fc | 105 | |
92a42be0 SL |
106 | struct DoStopTheWorldParam { |
107 | StopTheWorldCallback callback; | |
108 | void *argument; | |
109 | }; | |
110 | ||
2c00a5a8 XL |
111 | // While calling Die() here is undefined behavior and can potentially |
112 | // cause race conditions, it isn't possible to intercept exit on linux, | |
113 | // so we have no choice but to call Die() from the atexit handler. | |
114 | void HandleLeaks() { | |
115 | if (common_flags()->exitcode) Die(); | |
116 | } | |
117 | ||
92a42be0 SL |
118 | static int DoStopTheWorldCallback(struct dl_phdr_info *info, size_t size, |
119 | void *data) { | |
120 | DoStopTheWorldParam *param = reinterpret_cast<DoStopTheWorldParam *>(data); | |
121 | StopTheWorld(param->callback, param->argument); | |
122 | return 1; | |
123 | } | |
124 | ||
125 | // LSan calls dl_iterate_phdr() from the tracer task. This may deadlock: if one | |
126 | // of the threads is frozen while holding the libdl lock, the tracer will hang | |
127 | // in dl_iterate_phdr() forever. | |
128 | // Luckily, (a) the lock is reentrant and (b) libc can't distinguish between the | |
129 | // tracer task and the thread that spawned it. Thus, if we run the tracer task | |
130 | // while holding the libdl lock in the parent thread, we can safely reenter it | |
131 | // in the tracer. The solution is to run stoptheworld from a dl_iterate_phdr() | |
132 | // callback in the parent thread. | |
133 | void DoStopTheWorld(StopTheWorldCallback callback, void *argument) { | |
134 | DoStopTheWorldParam param = {callback, argument}; | |
135 | dl_iterate_phdr(DoStopTheWorldCallback, ¶m); | |
136 | } | |
137 | ||
138 | } // namespace __lsan | |
139 | ||
140 | #endif // CAN_SANITIZE_LEAKS && SANITIZER_LINUX |