]> git.proxmox.com Git - rustc.git/blame - src/libcompiler_builtins/compiler-rt/lib/lsan/lsan_common_linux.cc
New upstream version 1.25.0+dfsg1
[rustc.git] / src / libcompiler_builtins / compiler-rt / lib / lsan / lsan_common_linux.cc
CommitLineData
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
27namespace __lsan {
28
29static const char kLinkerName[] = "ld";
5bcae85e
SL
30
31static char linker_placeholder[sizeof(LoadedModule)] ALIGNED(64);
92a42be0 32static LoadedModule *linker = nullptr;
1a4d82fc 33
2c00a5a8
XL
34static 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")))
43THREADLOCAL int disable_counter;
44bool DisabledInThisThread() { return disable_counter > 0; }
45void DisableInThisThread() { disable_counter++; }
46void EnableInThisThread() {
47 if (disable_counter == 0) {
48 DisableCounterUnderflow();
49 }
50 disable_counter--;
1a4d82fc
JJ
51}
52
53void 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
79static 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.
97void ProcessGlobalRegions(Frontier *frontier) {
98 if (!flags()->use_globals) return;
1a4d82fc
JJ
99 dl_iterate_phdr(ProcessGlobalRegionsCallback, frontier);
100}
101
2c00a5a8 102LoadedModule *GetLinker() { return linker; }
1a4d82fc 103
2c00a5a8 104void ProcessPlatformSpecificAllocations(Frontier *frontier) {}
1a4d82fc 105
92a42be0
SL
106struct 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.
114void HandleLeaks() {
115 if (common_flags()->exitcode) Die();
116}
117
92a42be0
SL
118static 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.
133void DoStopTheWorld(StopTheWorldCallback callback, void *argument) {
134 DoStopTheWorldParam param = {callback, argument};
135 dl_iterate_phdr(DoStopTheWorldCallback, &param);
136}
137
138} // namespace __lsan
139
140#endif // CAN_SANITIZE_LEAKS && SANITIZER_LINUX