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