1 //===-- sanitizer_symbolizer_mac.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 various sanitizers' runtime libraries.
11 //
12 // Implementation of Mac-specific "atos" symbolizer.
13 //===----------------------------------------------------------------------===//
14
15 #include "sanitizer_platform.h"
16 #if SANITIZER_MAC
17
18 #include "sanitizer_allocator_internal.h"
19 #include "sanitizer_mac.h"
20 #include "sanitizer_symbolizer_mac.h"
21
22 namespace __sanitizer {
23
24 #include <dlfcn.h>
25 #include <errno.h>
26 #include <stdlib.h>
27 #include <sys/wait.h>
28 #include <unistd.h>
29 #include <util.h>
30
SymbolizePC(uptr addr,SymbolizedStack * stack)31 bool DlAddrSymbolizer::SymbolizePC(uptr addr, SymbolizedStack *stack) {
32 Dl_info info;
33 int result = dladdr((const void *)addr, &info);
34 if (!result) return false;
35 const char *demangled = DemangleCXXABI(info.dli_sname);
36 stack->info.function = internal_strdup(demangled);
37 return true;
38 }
39
SymbolizeData(uptr addr,DataInfo * info)40 bool DlAddrSymbolizer::SymbolizeData(uptr addr, DataInfo *info) {
41 return false;
42 }
43
44 class AtosSymbolizerProcess : public SymbolizerProcess {
45 public:
AtosSymbolizerProcess(const char * path,pid_t parent_pid)46 explicit AtosSymbolizerProcess(const char *path, pid_t parent_pid)
47 : SymbolizerProcess(path, /*use_forkpty*/ true),
48 parent_pid_(parent_pid) {}
49
50 private:
ReachedEndOfOutput(const char * buffer,uptr length) const51 bool ReachedEndOfOutput(const char *buffer, uptr length) const override {
52 return (length >= 1 && buffer[length - 1] == '\n');
53 }
54
ExecuteWithDefaultArgs(const char * path_to_binary) const55 void ExecuteWithDefaultArgs(const char *path_to_binary) const override {
56 char pid_str[16];
57 internal_snprintf(pid_str, sizeof(pid_str), "%d", parent_pid_);
58 if (GetMacosVersion() == MACOS_VERSION_MAVERICKS) {
59 // On Mavericks atos prints a deprecation warning which we suppress by
60 // passing -d. The warning isn't present on other OSX versions, even the
61 // newer ones.
62 execl(path_to_binary, path_to_binary, "-p", pid_str, "-d", (char *)0);
63 } else {
64 execl(path_to_binary, path_to_binary, "-p", pid_str, (char *)0);
65 }
66 }
67
68 pid_t parent_pid_;
69 };
70
71 static const char *kAtosErrorMessages[] = {
72 "atos cannot examine process",
73 "unable to get permission to examine process",
74 "An admin user name and password is required",
75 "could not load inserted library",
76 "architecture mismatch between analysis process",
77 };
78
IsAtosErrorMessage(const char * str)79 static bool IsAtosErrorMessage(const char *str) {
80 for (uptr i = 0; i < ARRAY_SIZE(kAtosErrorMessages); i++) {
81 if (internal_strstr(str, kAtosErrorMessages[i])) {
82 return true;
83 }
84 }
85 return false;
86 }
87
ParseCommandOutput(const char * str,SymbolizedStack * res)88 static bool ParseCommandOutput(const char *str, SymbolizedStack *res) {
89 // Trim ending newlines.
90 char *trim;
91 ExtractTokenUpToDelimiter(str, "\n", &trim);
92
93 // The line from `atos` is in one of these formats:
94 // myfunction (in library.dylib) (sourcefile.c:17)
95 // myfunction (in library.dylib) + 0x1fe
96 // 0xdeadbeef (in library.dylib) + 0x1fe
97 // 0xdeadbeef (in library.dylib)
98 // 0xdeadbeef
99
100 if (IsAtosErrorMessage(trim)) {
101 Report("atos returned an error: %s\n", trim);
102 InternalFree(trim);
103 return false;
104 }
105
106 const char *rest = trim;
107 char *function_name;
108 rest = ExtractTokenUpToDelimiter(rest, " (in ", &function_name);
109 if (internal_strncmp(function_name, "0x", 2) != 0)
110 res->info.function = function_name;
111 else
112 InternalFree(function_name);
113 rest = ExtractTokenUpToDelimiter(rest, ") ", &res->info.module);
114
115 if (rest[0] == '(') {
116 rest++;
117 rest = ExtractTokenUpToDelimiter(rest, ":", &res->info.file);
118 char *extracted_line_number;
119 rest = ExtractTokenUpToDelimiter(rest, ")", &extracted_line_number);
120 res->info.line = internal_atoll(extracted_line_number);
121 InternalFree(extracted_line_number);
122 }
123
124 InternalFree(trim);
125 return true;
126 }
127
AtosSymbolizer(const char * path,LowLevelAllocator * allocator)128 AtosSymbolizer::AtosSymbolizer(const char *path, LowLevelAllocator *allocator)
129 : process_(new(*allocator) AtosSymbolizerProcess(path, getpid())) {}
130
SymbolizePC(uptr addr,SymbolizedStack * stack)131 bool AtosSymbolizer::SymbolizePC(uptr addr, SymbolizedStack *stack) {
132 if (!process_) return false;
133 char command[32];
134 internal_snprintf(command, sizeof(command), "0x%zx\n", addr);
135 const char *buf = process_->SendCommand(command);
136 if (!buf) return false;
137 if (!ParseCommandOutput(buf, stack)) {
138 process_ = nullptr;
139 return false;
140 }
141 return true;
142 }
143
SymbolizeData(uptr addr,DataInfo * info)144 bool AtosSymbolizer::SymbolizeData(uptr addr, DataInfo *info) { return false; }
145
146 } // namespace __sanitizer
147
148 #endif // SANITIZER_MAC
149