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