blob: fc8a7d91ac73a7b362e8bf511fc6828eec7bd8d6 [file] [log] [blame]
Alexey Samsonovff0ce7f2013-06-11 08:14:24 +00001//===-- sanitizer_symbolizer_posix_libcdep.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 shared between AddressSanitizer and ThreadSanitizer
11// run-time libraries.
12// POSIX-specific implementation of symbolizer parts.
13//===----------------------------------------------------------------------===//
14
15#include "sanitizer_platform.h"
16#if SANITIZER_POSIX
Alexey Samsonov7847d772013-09-10 14:36:16 +000017#include "sanitizer_allocator_internal.h"
Alexey Samsonovff0ce7f2013-06-11 08:14:24 +000018#include "sanitizer_common.h"
Stephen Hines2d1fdb22014-05-28 23:58:16 -070019#include "sanitizer_flags.h"
Alexey Samsonovff0ce7f2013-06-11 08:14:24 +000020#include "sanitizer_internal_defs.h"
Alexey Samsonov7847d772013-09-10 14:36:16 +000021#include "sanitizer_linux.h"
22#include "sanitizer_placement_new.h"
Pirama Arumuga Nainar799172d2016-03-03 15:50:30 -080023#include "sanitizer_posix.h"
Alexey Samsonov7847d772013-09-10 14:36:16 +000024#include "sanitizer_procmaps.h"
Pirama Arumuga Nainar7c915052015-04-08 08:58:29 -070025#include "sanitizer_symbolizer_internal.h"
Stephen Hines2d1fdb22014-05-28 23:58:16 -070026#include "sanitizer_symbolizer_libbacktrace.h"
Pirama Arumuga Nainar7c915052015-04-08 08:58:29 -070027#include "sanitizer_symbolizer_mac.h"
Alexey Samsonovff0ce7f2013-06-11 08:14:24 +000028
Pirama Arumuga Nainar799172d2016-03-03 15:50:30 -080029#include <errno.h>
30#include <stdlib.h>
31#include <sys/wait.h>
Alexey Samsonovff0ce7f2013-06-11 08:14:24 +000032#include <unistd.h>
33
Pirama Arumuga Nainar799172d2016-03-03 15:50:30 -080034#if SANITIZER_MAC
35#include <util.h> // for forkpty()
36#endif // SANITIZER_MAC
37
Alexey Samsonov7847d772013-09-10 14:36:16 +000038// C++ demangling function, as required by Itanium C++ ABI. This is weak,
39// because we do not require a C++ ABI library to be linked to a program
40// using sanitizers; if it's not present, we'll just use the mangled name.
41namespace __cxxabiv1 {
42 extern "C" SANITIZER_WEAK_ATTRIBUTE
43 char *__cxa_demangle(const char *mangled, char *buffer,
44 size_t *length, int *status);
45}
46
Alexey Samsonovff0ce7f2013-06-11 08:14:24 +000047namespace __sanitizer {
48
Alexey Samsonov7847d772013-09-10 14:36:16 +000049// Attempts to demangle the name via __cxa_demangle from __cxxabiv1.
Pirama Arumuga Nainar7c915052015-04-08 08:58:29 -070050const char *DemangleCXXABI(const char *name) {
Alexey Samsonov7847d772013-09-10 14:36:16 +000051 // FIXME: __cxa_demangle aggressively insists on allocating memory.
52 // There's not much we can do about that, short of providing our
53 // own demangler (libc++abi's implementation could be adapted so that
54 // it does not allocate). For now, we just call it anyway, and we leak
55 // the returned value.
56 if (__cxxabiv1::__cxa_demangle)
57 if (const char *demangled_name =
58 __cxxabiv1::__cxa_demangle(name, 0, 0, 0))
59 return demangled_name;
60
61 return name;
62}
63
Pirama Arumuga Nainar799172d2016-03-03 15:50:30 -080064bool SymbolizerProcess::StartSymbolizerSubprocess() {
65 if (!FileExists(path_)) {
66 if (!reported_invalid_path_) {
67 Report("WARNING: invalid path to external symbolizer!\n");
68 reported_invalid_path_ = true;
Pirama Arumuga Nainar7c915052015-04-08 08:58:29 -070069 }
70 return false;
71 }
72
Pirama Arumuga Nainar799172d2016-03-03 15:50:30 -080073 int pid;
74 if (use_forkpty_) {
75#if SANITIZER_MAC
76 fd_t fd = kInvalidFd;
77 // Use forkpty to disable buffering in the new terminal.
78 pid = internal_forkpty(&fd);
79 if (pid == -1) {
80 // forkpty() failed.
81 Report("WARNING: failed to fork external symbolizer (errno: %d)\n",
82 errno);
83 return false;
84 } else if (pid == 0) {
85 // Child subprocess.
86 const char *argv[kArgVMax];
87 GetArgV(path_, argv);
88 execv(path_, const_cast<char **>(&argv[0]));
89 internal__exit(1);
Pirama Arumuga Nainar7c915052015-04-08 08:58:29 -070090 }
Pirama Arumuga Nainar799172d2016-03-03 15:50:30 -080091
92 // Continue execution in parent process.
93 input_fd_ = output_fd_ = fd;
94
95 // Disable echo in the new terminal, disable CR.
96 struct termios termflags;
97 tcgetattr(fd, &termflags);
98 termflags.c_oflag &= ~ONLCR;
99 termflags.c_lflag &= ~ECHO;
100 tcsetattr(fd, TCSANOW, &termflags);
101#else // SANITIZER_MAC
102 UNIMPLEMENTED();
103#endif // SANITIZER_MAC
104 } else {
105 int *infd = NULL;
106 int *outfd = NULL;
107 // The client program may close its stdin and/or stdout and/or stderr
108 // thus allowing socketpair to reuse file descriptors 0, 1 or 2.
109 // In this case the communication between the forked processes may be
110 // broken if either the parent or the child tries to close or duplicate
111 // these descriptors. The loop below produces two pairs of file
112 // descriptors, each greater than 2 (stderr).
113 int sock_pair[5][2];
114 for (int i = 0; i < 5; i++) {
115 if (pipe(sock_pair[i]) == -1) {
116 for (int j = 0; j < i; j++) {
117 internal_close(sock_pair[j][0]);
118 internal_close(sock_pair[j][1]);
119 }
120 Report("WARNING: Can't create a socket pair to start "
121 "external symbolizer (errno: %d)\n", errno);
122 return false;
123 } else if (sock_pair[i][0] > 2 && sock_pair[i][1] > 2) {
124 if (infd == NULL) {
125 infd = sock_pair[i];
126 } else {
127 outfd = sock_pair[i];
128 for (int j = 0; j < i; j++) {
129 if (sock_pair[j] == infd) continue;
130 internal_close(sock_pair[j][0]);
131 internal_close(sock_pair[j][1]);
132 }
133 break;
134 }
135 }
136 }
137 CHECK(infd);
138 CHECK(outfd);
139
140 // Real fork() may call user callbacks registered with pthread_atfork().
141 pid = internal_fork();
142 if (pid == -1) {
143 // Fork() failed.
144 internal_close(infd[0]);
145 internal_close(infd[1]);
146 internal_close(outfd[0]);
147 internal_close(outfd[1]);
148 Report("WARNING: failed to fork external symbolizer "
149 " (errno: %d)\n", errno);
150 return false;
151 } else if (pid == 0) {
152 // Child subprocess.
153 internal_close(STDOUT_FILENO);
154 internal_close(STDIN_FILENO);
155 internal_dup2(outfd[0], STDIN_FILENO);
156 internal_dup2(infd[1], STDOUT_FILENO);
157 internal_close(outfd[0]);
158 internal_close(outfd[1]);
159 internal_close(infd[0]);
160 internal_close(infd[1]);
161 for (int fd = sysconf(_SC_OPEN_MAX); fd > 2; fd--)
162 internal_close(fd);
163 const char *argv[kArgVMax];
164 GetArgV(path_, argv);
165 execv(path_, const_cast<char **>(&argv[0]));
166 internal__exit(1);
167 }
168
169 // Continue execution in parent process.
170 internal_close(outfd[0]);
171 internal_close(infd[1]);
172 input_fd_ = infd[0];
173 output_fd_ = outfd[1];
174 }
175
176 // Check that symbolizer subprocess started successfully.
177 int pid_status;
178 SleepForMillis(kSymbolizerStartupTimeMillis);
179 int exited_pid = waitpid(pid, &pid_status, WNOHANG);
180 if (exited_pid != 0) {
181 // Either waitpid failed, or child has already exited.
182 Report("WARNING: external symbolizer didn't start up correctly!\n");
Pirama Arumuga Nainar7c915052015-04-08 08:58:29 -0700183 return false;
184 }
185
Pirama Arumuga Nainar799172d2016-03-03 15:50:30 -0800186 return true;
187}
Pirama Arumuga Nainar7c915052015-04-08 08:58:29 -0700188
Stephen Hines2d1fdb22014-05-28 23:58:16 -0700189class Addr2LineProcess : public SymbolizerProcess {
190 public:
191 Addr2LineProcess(const char *path, const char *module_name)
192 : SymbolizerProcess(path), module_name_(internal_strdup(module_name)) {}
193
194 const char *module_name() const { return module_name_; }
195
196 private:
Pirama Arumuga Nainar799172d2016-03-03 15:50:30 -0800197 void GetArgV(const char *path_to_binary,
198 const char *(&argv)[kArgVMax]) const override {
199 int i = 0;
200 argv[i++] = path_to_binary;
201 argv[i++] = "-iCfe";
202 argv[i++] = module_name_;
203 argv[i++] = nullptr;
Stephen Hines2d1fdb22014-05-28 23:58:16 -0700204 }
205
Pirama Arumuga Nainar799172d2016-03-03 15:50:30 -0800206 bool ReachedEndOfOutput(const char *buffer, uptr length) const override;
207
208 bool ReadFromSymbolizer(char *buffer, uptr max_length) override {
209 if (!SymbolizerProcess::ReadFromSymbolizer(buffer, max_length))
210 return false;
211 // We should cut out output_terminator_ at the end of given buffer,
212 // appended by addr2line to mark the end of its meaningful output.
213 // We cannot scan buffer from it's beginning, because it is legal for it
214 // to start with output_terminator_ in case given offset is invalid. So,
215 // scanning from second character.
216 char *garbage = internal_strstr(buffer + 1, output_terminator_);
217 // This should never be NULL since buffer must end up with
218 // output_terminator_.
219 CHECK(garbage);
220 // Trim the buffer.
221 garbage[0] = '\0';
222 return true;
Stephen Hines2d1fdb22014-05-28 23:58:16 -0700223 }
224
225 const char *module_name_; // Owned, leaked.
Pirama Arumuga Nainar799172d2016-03-03 15:50:30 -0800226 static const char output_terminator_[];
Stephen Hines2d1fdb22014-05-28 23:58:16 -0700227};
228
Pirama Arumuga Nainar799172d2016-03-03 15:50:30 -0800229const char Addr2LineProcess::output_terminator_[] = "??\n??:0\n";
230
231bool Addr2LineProcess::ReachedEndOfOutput(const char *buffer,
232 uptr length) const {
233 const size_t kTerminatorLen = sizeof(output_terminator_) - 1;
234 // Skip, if we read just kTerminatorLen bytes, because Addr2Line output
235 // should consist at least of two pairs of lines:
236 // 1. First one, corresponding to given offset to be symbolized
237 // (may be equal to output_terminator_, if offset is not valid).
238 // 2. Second one for output_terminator_, itself to mark the end of output.
239 if (length <= kTerminatorLen) return false;
240 // Addr2Line output should end up with output_terminator_.
241 return !internal_memcmp(buffer + length - kTerminatorLen,
242 output_terminator_, kTerminatorLen);
243}
244
Pirama Arumuga Nainar7c915052015-04-08 08:58:29 -0700245class Addr2LinePool : public SymbolizerTool {
Stephen Hines2d1fdb22014-05-28 23:58:16 -0700246 public:
247 explicit Addr2LinePool(const char *addr2line_path,
248 LowLevelAllocator *allocator)
249 : addr2line_path_(addr2line_path), allocator_(allocator),
250 addr2line_pool_(16) {}
251
Pirama Arumuga Nainar7c915052015-04-08 08:58:29 -0700252 bool SymbolizePC(uptr addr, SymbolizedStack *stack) override {
253 if (const char *buf =
254 SendCommand(stack->info.module, stack->info.module_offset)) {
255 ParseSymbolizePCOutput(buf, stack);
256 return true;
257 }
258 return false;
259 }
260
261 bool SymbolizeData(uptr addr, DataInfo *info) override {
262 return false;
263 }
264
265 private:
266 const char *SendCommand(const char *module_name, uptr module_offset) {
Stephen Hines2d1fdb22014-05-28 23:58:16 -0700267 Addr2LineProcess *addr2line = 0;
268 for (uptr i = 0; i < addr2line_pool_.size(); ++i) {
269 if (0 ==
270 internal_strcmp(module_name, addr2line_pool_[i]->module_name())) {
271 addr2line = addr2line_pool_[i];
272 break;
273 }
274 }
275 if (!addr2line) {
276 addr2line =
277 new(*allocator_) Addr2LineProcess(addr2line_path_, module_name);
278 addr2line_pool_.push_back(addr2line);
279 }
Pirama Arumuga Nainar7c915052015-04-08 08:58:29 -0700280 CHECK_EQ(0, internal_strcmp(module_name, addr2line->module_name()));
Pirama Arumuga Nainar799172d2016-03-03 15:50:30 -0800281 char buffer[kBufferSize];
282 internal_snprintf(buffer, kBufferSize, "0x%zx\n0x%zx\n",
283 module_offset, dummy_address_);
284 return addr2line->SendCommand(buffer);
Stephen Hines2d1fdb22014-05-28 23:58:16 -0700285 }
286
Pirama Arumuga Nainar799172d2016-03-03 15:50:30 -0800287 static const uptr kBufferSize = 64;
Stephen Hines2d1fdb22014-05-28 23:58:16 -0700288 const char *addr2line_path_;
289 LowLevelAllocator *allocator_;
290 InternalMmapVector<Addr2LineProcess*> addr2line_pool_;
Pirama Arumuga Nainar799172d2016-03-03 15:50:30 -0800291 static const uptr dummy_address_ =
292 FIRST_32_SECOND_64(UINT32_MAX, UINT64_MAX);
Alexey Samsonov7847d772013-09-10 14:36:16 +0000293};
294
Alexey Samsonov7847d772013-09-10 14:36:16 +0000295#if SANITIZER_SUPPORTS_WEAK_HOOKS
296extern "C" {
297SANITIZER_INTERFACE_ATTRIBUTE SANITIZER_WEAK_ATTRIBUTE
298bool __sanitizer_symbolize_code(const char *ModuleName, u64 ModuleOffset,
299 char *Buffer, int MaxLength);
300SANITIZER_INTERFACE_ATTRIBUTE SANITIZER_WEAK_ATTRIBUTE
301bool __sanitizer_symbolize_data(const char *ModuleName, u64 ModuleOffset,
302 char *Buffer, int MaxLength);
303SANITIZER_INTERFACE_ATTRIBUTE SANITIZER_WEAK_ATTRIBUTE
304void __sanitizer_symbolize_flush();
305SANITIZER_INTERFACE_ATTRIBUTE SANITIZER_WEAK_ATTRIBUTE
306int __sanitizer_symbolize_demangle(const char *Name, char *Buffer,
307 int MaxLength);
308} // extern "C"
309
Pirama Arumuga Nainar7c915052015-04-08 08:58:29 -0700310class InternalSymbolizer : public SymbolizerTool {
Alexey Samsonov7847d772013-09-10 14:36:16 +0000311 public:
Peter Collingbournec1a1ed62013-10-25 23:03:29 +0000312 static InternalSymbolizer *get(LowLevelAllocator *alloc) {
Alexey Samsonov7847d772013-09-10 14:36:16 +0000313 if (__sanitizer_symbolize_code != 0 &&
314 __sanitizer_symbolize_data != 0) {
Peter Collingbournec1a1ed62013-10-25 23:03:29 +0000315 return new(*alloc) InternalSymbolizer();
Alexey Samsonov7847d772013-09-10 14:36:16 +0000316 }
317 return 0;
318 }
319
Pirama Arumuga Nainar7c915052015-04-08 08:58:29 -0700320 bool SymbolizePC(uptr addr, SymbolizedStack *stack) override {
321 bool result = __sanitizer_symbolize_code(
322 stack->info.module, stack->info.module_offset, buffer_, kBufferSize);
323 if (result) ParseSymbolizePCOutput(buffer_, stack);
324 return result;
Alexey Samsonov7847d772013-09-10 14:36:16 +0000325 }
326
Pirama Arumuga Nainar7c915052015-04-08 08:58:29 -0700327 bool SymbolizeData(uptr addr, DataInfo *info) override {
328 bool result = __sanitizer_symbolize_data(info->module, info->module_offset,
329 buffer_, kBufferSize);
330 if (result) {
331 ParseSymbolizeDataOutput(buffer_, info);
332 info->start += (addr - info->module_offset); // Add the base address.
333 }
334 return result;
335 }
336
337 void Flush() override {
Alexey Samsonov7847d772013-09-10 14:36:16 +0000338 if (__sanitizer_symbolize_flush)
339 __sanitizer_symbolize_flush();
340 }
341
Pirama Arumuga Nainar7c915052015-04-08 08:58:29 -0700342 const char *Demangle(const char *name) override {
Alexey Samsonov7847d772013-09-10 14:36:16 +0000343 if (__sanitizer_symbolize_demangle) {
344 for (uptr res_length = 1024;
345 res_length <= InternalSizeClassMap::kMaxSize;) {
346 char *res_buff = static_cast<char*>(InternalAlloc(res_length));
347 uptr req_length =
348 __sanitizer_symbolize_demangle(name, res_buff, res_length);
349 if (req_length > res_length) {
350 res_length = req_length + 1;
351 InternalFree(res_buff);
352 continue;
353 }
354 return res_buff;
355 }
356 }
357 return name;
358 }
359
360 private:
361 InternalSymbolizer() { }
362
363 static const int kBufferSize = 16 * 1024;
364 static const int kMaxDemangledNameSize = 1024;
365 char buffer_[kBufferSize];
366};
367#else // SANITIZER_SUPPORTS_WEAK_HOOKS
368
Pirama Arumuga Nainar7c915052015-04-08 08:58:29 -0700369class InternalSymbolizer : public SymbolizerTool {
Alexey Samsonov7847d772013-09-10 14:36:16 +0000370 public:
Peter Collingbournec1a1ed62013-10-25 23:03:29 +0000371 static InternalSymbolizer *get(LowLevelAllocator *alloc) { return 0; }
Alexey Samsonov7847d772013-09-10 14:36:16 +0000372};
373
374#endif // SANITIZER_SUPPORTS_WEAK_HOOKS
375
Pirama Arumuga Nainar259f7062015-05-06 11:49:53 -0700376const char *Symbolizer::PlatformDemangle(const char *name) {
377 return DemangleCXXABI(name);
378}
Peter Collingbournec1a1ed62013-10-25 23:03:29 +0000379
Pirama Arumuga Nainarcdce50b2015-07-01 12:26:56 -0700380void Symbolizer::PlatformPrepareForSandboxing() {}
Alexey Samsonov7847d772013-09-10 14:36:16 +0000381
Pirama Arumuga Nainar7c915052015-04-08 08:58:29 -0700382static SymbolizerTool *ChooseExternalSymbolizer(LowLevelAllocator *allocator) {
383 const char *path = common_flags()->external_symbolizer_path;
384 const char *binary_name = path ? StripModuleName(path) : "";
385 if (path && path[0] == '\0') {
386 VReport(2, "External symbolizer is explicitly disabled.\n");
387 return nullptr;
388 } else if (!internal_strcmp(binary_name, "llvm-symbolizer")) {
389 VReport(2, "Using llvm-symbolizer at user-specified path: %s\n", path);
390 return new(*allocator) LLVMSymbolizer(path, allocator);
391 } else if (!internal_strcmp(binary_name, "atos")) {
392#if SANITIZER_MAC
393 VReport(2, "Using atos at user-specified path: %s\n", path);
394 return new(*allocator) AtosSymbolizer(path, allocator);
395#else // SANITIZER_MAC
396 Report("ERROR: Using `atos` is only supported on Darwin.\n");
397 Die();
398#endif // SANITIZER_MAC
399 } else if (!internal_strcmp(binary_name, "addr2line")) {
400 VReport(2, "Using addr2line at user-specified path: %s\n", path);
401 return new(*allocator) Addr2LinePool(path, allocator);
402 } else if (path) {
403 Report("ERROR: External symbolizer path is set to '%s' which isn't "
404 "a known symbolizer. Please set the path to the llvm-symbolizer "
405 "binary or other known tool.\n", path);
406 Die();
Stephen Hines2d1fdb22014-05-28 23:58:16 -0700407 }
Alexey Samsonov7847d772013-09-10 14:36:16 +0000408
Pirama Arumuga Nainar7c915052015-04-08 08:58:29 -0700409 // Otherwise symbolizer program is unknown, let's search $PATH
410 CHECK(path == nullptr);
411 if (const char *found_path = FindPathToBinary("llvm-symbolizer")) {
412 VReport(2, "Using llvm-symbolizer found at: %s\n", found_path);
413 return new(*allocator) LLVMSymbolizer(found_path, allocator);
414 }
415#if SANITIZER_MAC
416 if (const char *found_path = FindPathToBinary("atos")) {
417 VReport(2, "Using atos found at: %s\n", found_path);
418 return new(*allocator) AtosSymbolizer(found_path, allocator);
419 }
420#endif // SANITIZER_MAC
421 if (common_flags()->allow_addr2line) {
422 if (const char *found_path = FindPathToBinary("addr2line")) {
423 VReport(2, "Using addr2line found at: %s\n", found_path);
424 return new(*allocator) Addr2LinePool(found_path, allocator);
Alexey Samsonove059bd32013-09-10 16:16:27 +0000425 }
426 }
Pirama Arumuga Nainar7c915052015-04-08 08:58:29 -0700427 return nullptr;
428}
Peter Collingbournec1a1ed62013-10-25 23:03:29 +0000429
Pirama Arumuga Nainar7c915052015-04-08 08:58:29 -0700430static void ChooseSymbolizerTools(IntrusiveList<SymbolizerTool> *list,
431 LowLevelAllocator *allocator) {
432 if (!common_flags()->symbolize) {
433 VReport(2, "Symbolizer is disabled.\n");
434 return;
435 }
436 if (SymbolizerTool *tool = InternalSymbolizer::get(allocator)) {
437 VReport(2, "Using internal symbolizer.\n");
438 list->push_back(tool);
439 return;
440 }
441 if (SymbolizerTool *tool = LibbacktraceSymbolizer::get(allocator)) {
442 VReport(2, "Using libbacktrace symbolizer.\n");
443 list->push_back(tool);
444 return;
445 }
446
447 if (SymbolizerTool *tool = ChooseExternalSymbolizer(allocator)) {
448 list->push_back(tool);
Pirama Arumuga Nainar7c915052015-04-08 08:58:29 -0700449 }
450
451#if SANITIZER_MAC
452 VReport(2, "Using dladdr symbolizer.\n");
453 list->push_back(new(*allocator) DlAddrSymbolizer());
454#endif // SANITIZER_MAC
455}
456
457Symbolizer *Symbolizer::PlatformInit() {
458 IntrusiveList<SymbolizerTool> list;
459 list.clear();
460 ChooseSymbolizerTools(&list, &symbolizer_allocator_);
Pirama Arumuga Nainar259f7062015-05-06 11:49:53 -0700461 return new(symbolizer_allocator_) Symbolizer(list);
Alexey Samsonov7847d772013-09-10 14:36:16 +0000462}
Alexey Samsonovff0ce7f2013-06-11 08:14:24 +0000463
464} // namespace __sanitizer
465
466#endif // SANITIZER_POSIX