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     // The `atos` binary has some issues with DYLD_ROOT_PATH on i386.
57     unsetenv("DYLD_ROOT_PATH");
58 
59     char pid_str[16];
60     internal_snprintf(pid_str, sizeof(pid_str), "%d", parent_pid_);
61     if (GetMacosVersion() == MACOS_VERSION_MAVERICKS) {
62       // On Mavericks atos prints a deprecation warning which we suppress by
63       // passing -d. The warning isn't present on other OSX versions, even the
64       // newer ones.
65       execl(path_to_binary, path_to_binary, "-p", pid_str, "-d", (char *)0);
66     } else {
67       execl(path_to_binary, path_to_binary, "-p", pid_str, (char *)0);
68     }
69   }
70 
71   pid_t parent_pid_;
72 };
73 
74 static const char *kAtosErrorMessages[] = {
75   "atos cannot examine process",
76   "unable to get permission to examine process",
77   "An admin user name and password is required",
78   "could not load inserted library",
79   "architecture mismatch between analysis process",
80 };
81 
IsAtosErrorMessage(const char * str)82 static bool IsAtosErrorMessage(const char *str) {
83   for (uptr i = 0; i < ARRAY_SIZE(kAtosErrorMessages); i++) {
84     if (internal_strstr(str, kAtosErrorMessages[i])) {
85       return true;
86     }
87   }
88   return false;
89 }
90 
ParseCommandOutput(const char * str,SymbolizedStack * res)91 static bool ParseCommandOutput(const char *str, SymbolizedStack *res) {
92   // Trim ending newlines.
93   char *trim;
94   ExtractTokenUpToDelimiter(str, "\n", &trim);
95 
96   // The line from `atos` is in one of these formats:
97   //   myfunction (in library.dylib) (sourcefile.c:17)
98   //   myfunction (in library.dylib) + 0x1fe
99   //   0xdeadbeef (in library.dylib) + 0x1fe
100   //   0xdeadbeef (in library.dylib)
101   //   0xdeadbeef
102 
103   if (IsAtosErrorMessage(trim)) {
104     Report("atos returned an error: %s\n", trim);
105     InternalFree(trim);
106     return false;
107   }
108 
109   const char *rest = trim;
110   char *function_name;
111   rest = ExtractTokenUpToDelimiter(rest, " (in ", &function_name);
112   if (internal_strncmp(function_name, "0x", 2) != 0)
113     res->info.function = function_name;
114   else
115     InternalFree(function_name);
116   rest = ExtractTokenUpToDelimiter(rest, ") ", &res->info.module);
117 
118   if (rest[0] == '(') {
119     rest++;
120     rest = ExtractTokenUpToDelimiter(rest, ":", &res->info.file);
121     char *extracted_line_number;
122     rest = ExtractTokenUpToDelimiter(rest, ")", &extracted_line_number);
123     res->info.line = internal_atoll(extracted_line_number);
124     InternalFree(extracted_line_number);
125   }
126 
127   InternalFree(trim);
128   return true;
129 }
130 
AtosSymbolizer(const char * path,LowLevelAllocator * allocator)131 AtosSymbolizer::AtosSymbolizer(const char *path, LowLevelAllocator *allocator)
132     : process_(new(*allocator) AtosSymbolizerProcess(path, getpid())) {}
133 
SymbolizePC(uptr addr,SymbolizedStack * stack)134 bool AtosSymbolizer::SymbolizePC(uptr addr, SymbolizedStack *stack) {
135   if (!process_) return false;
136   char command[32];
137   internal_snprintf(command, sizeof(command), "0x%zx\n", addr);
138   const char *buf = process_->SendCommand(command);
139   if (!buf) return false;
140   if (!ParseCommandOutput(buf, stack)) {
141     process_ = nullptr;
142     return false;
143   }
144   return true;
145 }
146 
SymbolizeData(uptr addr,DataInfo * info)147 bool AtosSymbolizer::SymbolizeData(uptr addr, DataInfo *info) { return false; }
148 
149 }  // namespace __sanitizer
150 
151 #endif  // SANITIZER_MAC
152