]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - contrib/compiler-rt/lib/lsan/lsan_common_linux.cc
Merge llvm, clang, lld, lldb, compiler-rt and libc++ r304659, and update
[FreeBSD/FreeBSD.git] / contrib / compiler-rt / lib / lsan / lsan_common_linux.cc
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"
23 #include "sanitizer_common/sanitizer_linux.h"
24 #include "sanitizer_common/sanitizer_stackdepot.h"
25
26 #if SANITIZER_USE_GETAUXVAL
27 #include <sys/auxv.h>
28 #endif  // SANITIZER_USE_GETAUXVAL
29
30 namespace __lsan {
31
32 static const char kLinkerName[] = "ld";
33
34 static char linker_placeholder[sizeof(LoadedModule)] ALIGNED(64);
35 static LoadedModule *linker = nullptr;
36
37 static bool IsLinker(const LoadedModule& module) {
38 #if SANITIZER_USE_GETAUXVAL
39   return module.base_address() == getauxval(AT_BASE);
40 #else
41   return LibraryNameIs(module.full_name(), kLinkerName);
42 #endif  // SANITIZER_USE_GETAUXVAL
43 }
44
45 __attribute__((tls_model("initial-exec")))
46 THREADLOCAL int disable_counter;
47 bool DisabledInThisThread() { return disable_counter > 0; }
48 void DisableInThisThread() { disable_counter++; }
49 void EnableInThisThread() {
50   if (disable_counter == 0) {
51     DisableCounterUnderflow();
52   }
53   disable_counter--;
54 }
55
56 void InitializePlatformSpecificModules() {
57   ListOfModules modules;
58   modules.init();
59   for (LoadedModule &module : modules) {
60     if (!IsLinker(module))
61       continue;
62     if (linker == nullptr) {
63       linker = reinterpret_cast<LoadedModule *>(linker_placeholder);
64       *linker = module;
65       module = LoadedModule();
66     } else {
67       VReport(1, "LeakSanitizer: Multiple modules match \"%s\". "
68                  "TLS and other allocations originating from linker might be "
69                  "falsely reported as leaks.\n", kLinkerName);
70       linker->clear();
71       linker = nullptr;
72       return;
73     }
74   }
75   if (linker == nullptr) {
76     VReport(1, "LeakSanitizer: Dynamic linker not found. TLS and other "
77                "allocations originating from linker might be falsely reported "
78                 "as leaks.\n");
79   }
80 }
81
82 static int ProcessGlobalRegionsCallback(struct dl_phdr_info *info, size_t size,
83                                         void *data) {
84   Frontier *frontier = reinterpret_cast<Frontier *>(data);
85   for (uptr j = 0; j < info->dlpi_phnum; j++) {
86     const ElfW(Phdr) *phdr = &(info->dlpi_phdr[j]);
87     // We're looking for .data and .bss sections, which reside in writeable,
88     // loadable segments.
89     if (!(phdr->p_flags & PF_W) || (phdr->p_type != PT_LOAD) ||
90         (phdr->p_memsz == 0))
91       continue;
92     uptr begin = info->dlpi_addr + phdr->p_vaddr;
93     uptr end = begin + phdr->p_memsz;
94     ScanGlobalRange(begin, end, frontier);
95   }
96   return 0;
97 }
98
99 // Scans global variables for heap pointers.
100 void ProcessGlobalRegions(Frontier *frontier) {
101   if (!flags()->use_globals) return;
102   dl_iterate_phdr(ProcessGlobalRegionsCallback, frontier);
103 }
104
105 LoadedModule *GetLinker() { return linker; }
106
107 void ProcessPlatformSpecificAllocations(Frontier *frontier) {}
108
109 struct DoStopTheWorldParam {
110   StopTheWorldCallback callback;
111   void *argument;
112 };
113
114 static int DoStopTheWorldCallback(struct dl_phdr_info *info, size_t size,
115                                   void *data) {
116   DoStopTheWorldParam *param = reinterpret_cast<DoStopTheWorldParam *>(data);
117   StopTheWorld(param->callback, param->argument);
118   return 1;
119 }
120
121 // LSan calls dl_iterate_phdr() from the tracer task. This may deadlock: if one
122 // of the threads is frozen while holding the libdl lock, the tracer will hang
123 // in dl_iterate_phdr() forever.
124 // Luckily, (a) the lock is reentrant and (b) libc can't distinguish between the
125 // tracer task and the thread that spawned it. Thus, if we run the tracer task
126 // while holding the libdl lock in the parent thread, we can safely reenter it
127 // in the tracer. The solution is to run stoptheworld from a dl_iterate_phdr()
128 // callback in the parent thread.
129 void DoStopTheWorld(StopTheWorldCallback callback, void *argument) {
130   DoStopTheWorldParam param = {callback, argument};
131   dl_iterate_phdr(DoStopTheWorldCallback, &param);
132 }
133
134 } // namespace __lsan
135
136 #endif // CAN_SANITIZE_LEAKS && SANITIZER_LINUX