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 = demangled ? internal_strdup(demangled) : nullptr;
37 return true;
38 }
39
SymbolizeData(uptr addr,DataInfo * datainfo)40 bool DlAddrSymbolizer::SymbolizeData(uptr addr, DataInfo *datainfo) {
41 Dl_info info;
42 int result = dladdr((const void *)addr, &info);
43 if (!result) return false;
44 const char *demangled = DemangleCXXABI(info.dli_sname);
45 datainfo->name = internal_strdup(demangled);
46 datainfo->start = (uptr)info.dli_saddr;
47 return true;
48 }
49
50 class AtosSymbolizerProcess : public SymbolizerProcess {
51 public:
AtosSymbolizerProcess(const char * path,pid_t parent_pid)52 explicit AtosSymbolizerProcess(const char *path, pid_t parent_pid)
53 : SymbolizerProcess(path, /*use_forkpty*/ true) {
54 // Put the string command line argument in the object so that it outlives
55 // the call to GetArgV.
56 internal_snprintf(pid_str_, sizeof(pid_str_), "%d", parent_pid);
57 }
58
59 private:
ReachedEndOfOutput(const char * buffer,uptr length) const60 bool ReachedEndOfOutput(const char *buffer, uptr length) const override {
61 return (length >= 1 && buffer[length - 1] == '\n');
62 }
63
GetArgV(const char * path_to_binary,const char * (& argv)[kArgVMax]) const64 void GetArgV(const char *path_to_binary,
65 const char *(&argv)[kArgVMax]) const override {
66 int i = 0;
67 argv[i++] = path_to_binary;
68 argv[i++] = "-p";
69 argv[i++] = &pid_str_[0];
70 if (GetMacosVersion() == MACOS_VERSION_MAVERICKS) {
71 // On Mavericks atos prints a deprecation warning which we suppress by
72 // passing -d. The warning isn't present on other OSX versions, even the
73 // newer ones.
74 argv[i++] = "-d";
75 }
76 argv[i++] = nullptr;
77 }
78
79 char pid_str_[16];
80 };
81
82 static const char *kAtosErrorMessages[] = {
83 "atos cannot examine process",
84 "unable to get permission to examine process",
85 "An admin user name and password is required",
86 "could not load inserted library",
87 "architecture mismatch between analysis process",
88 };
89
IsAtosErrorMessage(const char * str)90 static bool IsAtosErrorMessage(const char *str) {
91 for (uptr i = 0; i < ARRAY_SIZE(kAtosErrorMessages); i++) {
92 if (internal_strstr(str, kAtosErrorMessages[i])) {
93 return true;
94 }
95 }
96 return false;
97 }
98
ParseCommandOutput(const char * str,uptr addr,char ** out_name,char ** out_module,char ** out_file,uptr * line,uptr * start_address)99 static bool ParseCommandOutput(const char *str, uptr addr, char **out_name,
100 char **out_module, char **out_file, uptr *line,
101 uptr *start_address) {
102 // Trim ending newlines.
103 char *trim;
104 ExtractTokenUpToDelimiter(str, "\n", &trim);
105
106 // The line from `atos` is in one of these formats:
107 // myfunction (in library.dylib) (sourcefile.c:17)
108 // myfunction (in library.dylib) + 0x1fe
109 // myfunction (in library.dylib) + 15
110 // 0xdeadbeef (in library.dylib) + 0x1fe
111 // 0xdeadbeef (in library.dylib) + 15
112 // 0xdeadbeef (in library.dylib)
113 // 0xdeadbeef
114
115 if (IsAtosErrorMessage(trim)) {
116 Report("atos returned an error: %s\n", trim);
117 InternalFree(trim);
118 return false;
119 }
120
121 const char *rest = trim;
122 char *symbol_name;
123 rest = ExtractTokenUpToDelimiter(rest, " (in ", &symbol_name);
124 if (rest[0] == '\0') {
125 InternalFree(symbol_name);
126 InternalFree(trim);
127 return false;
128 }
129
130 if (internal_strncmp(symbol_name, "0x", 2) != 0)
131 *out_name = symbol_name;
132 else
133 InternalFree(symbol_name);
134 rest = ExtractTokenUpToDelimiter(rest, ") ", out_module);
135
136 if (rest[0] == '(') {
137 if (out_file) {
138 rest++;
139 rest = ExtractTokenUpToDelimiter(rest, ":", out_file);
140 char *extracted_line_number;
141 rest = ExtractTokenUpToDelimiter(rest, ")", &extracted_line_number);
142 if (line) *line = (uptr)internal_atoll(extracted_line_number);
143 InternalFree(extracted_line_number);
144 }
145 } else if (rest[0] == '+') {
146 rest += 2;
147 uptr offset = internal_atoll(rest);
148 if (start_address) *start_address = addr - offset;
149 }
150
151 InternalFree(trim);
152 return true;
153 }
154
AtosSymbolizer(const char * path,LowLevelAllocator * allocator)155 AtosSymbolizer::AtosSymbolizer(const char *path, LowLevelAllocator *allocator)
156 : process_(new(*allocator) AtosSymbolizerProcess(path, getpid())) {}
157
SymbolizePC(uptr addr,SymbolizedStack * stack)158 bool AtosSymbolizer::SymbolizePC(uptr addr, SymbolizedStack *stack) {
159 if (!process_) return false;
160 char command[32];
161 internal_snprintf(command, sizeof(command), "0x%zx\n", addr);
162 const char *buf = process_->SendCommand(command);
163 if (!buf) return false;
164 uptr line;
165 if (!ParseCommandOutput(buf, addr, &stack->info.function, &stack->info.module,
166 &stack->info.file, &line, nullptr)) {
167 process_ = nullptr;
168 return false;
169 }
170 stack->info.line = (int)line;
171 return true;
172 }
173
SymbolizeData(uptr addr,DataInfo * info)174 bool AtosSymbolizer::SymbolizeData(uptr addr, DataInfo *info) {
175 if (!process_) return false;
176 char command[32];
177 internal_snprintf(command, sizeof(command), "0x%zx\n", addr);
178 const char *buf = process_->SendCommand(command);
179 if (!buf) return false;
180 if (!ParseCommandOutput(buf, addr, &info->name, &info->module, nullptr,
181 nullptr, &info->start)) {
182 process_ = nullptr;
183 return false;
184 }
185 return true;
186 }
187
188 } // namespace __sanitizer
189
190 #endif // SANITIZER_MAC
191