xref: /NextBSD/contrib/compiler-rt/lib/sanitizer_common/sanitizer_symbolizer_mac.cc (revision 84d351007654069f9643c8e4b4802a7f5f08ee42)
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