xref: /netbsd-src/external/gpl3/gcc.old/dist/libsanitizer/sanitizer_common/sanitizer_symbolizer_mac.cc (revision 1debfc3d3fad8af6f31804271c18e67f77b4d718)
1 //===-- sanitizer_symbolizer_mac.cc ---------------------------------------===//
2 //
3 // This file is distributed under the University of Illinois Open Source
4 // License. See LICENSE.TXT for details.
5 //
6 //===----------------------------------------------------------------------===//
7 //
8 // This file is shared between various sanitizers' runtime libraries.
9 //
10 // Implementation of Mac-specific "atos" symbolizer.
11 //===----------------------------------------------------------------------===//
12 
13 #include "sanitizer_platform.h"
14 #if SANITIZER_MAC
15 
16 #include "sanitizer_allocator_internal.h"
17 #include "sanitizer_mac.h"
18 #include "sanitizer_symbolizer_mac.h"
19 
20 #include <dlfcn.h>
21 #include <errno.h>
22 #include <stdlib.h>
23 #include <sys/wait.h>
24 #include <unistd.h>
25 #include <util.h>
26 
27 namespace __sanitizer {
28 
SymbolizePC(uptr addr,SymbolizedStack * stack)29 bool DlAddrSymbolizer::SymbolizePC(uptr addr, SymbolizedStack *stack) {
30   Dl_info info;
31   int result = dladdr((const void *)addr, &info);
32   if (!result) return false;
33   const char *demangled = DemangleSwiftAndCXX(info.dli_sname);
34   if (!demangled) return false;
35   stack->info.function = internal_strdup(demangled);
36   return true;
37 }
38 
SymbolizeData(uptr addr,DataInfo * datainfo)39 bool DlAddrSymbolizer::SymbolizeData(uptr addr, DataInfo *datainfo) {
40   Dl_info info;
41   int result = dladdr((const void *)addr, &info);
42   if (!result) return false;
43   const char *demangled = DemangleSwiftAndCXX(info.dli_sname);
44   datainfo->name = internal_strdup(demangled);
45   datainfo->start = (uptr)info.dli_saddr;
46   return true;
47 }
48 
49 class AtosSymbolizerProcess : public SymbolizerProcess {
50  public:
AtosSymbolizerProcess(const char * path,pid_t parent_pid)51   explicit AtosSymbolizerProcess(const char *path, pid_t parent_pid)
52       : SymbolizerProcess(path, /*use_forkpty*/ true) {
53     // Put the string command line argument in the object so that it outlives
54     // the call to GetArgV.
55     internal_snprintf(pid_str_, sizeof(pid_str_), "%d", parent_pid);
56   }
57 
58  private:
ReachedEndOfOutput(const char * buffer,uptr length) const59   bool ReachedEndOfOutput(const char *buffer, uptr length) const override {
60     return (length >= 1 && buffer[length - 1] == '\n');
61   }
62 
GetArgV(const char * path_to_binary,const char * (& argv)[kArgVMax]) const63   void GetArgV(const char *path_to_binary,
64                const char *(&argv)[kArgVMax]) const override {
65     int i = 0;
66     argv[i++] = path_to_binary;
67     argv[i++] = "-p";
68     argv[i++] = &pid_str_[0];
69     if (GetMacosVersion() == MACOS_VERSION_MAVERICKS) {
70       // On Mavericks atos prints a deprecation warning which we suppress by
71       // passing -d. The warning isn't present on other OSX versions, even the
72       // newer ones.
73       argv[i++] = "-d";
74     }
75     argv[i++] = nullptr;
76   }
77 
78   char pid_str_[16];
79 };
80 
ParseCommandOutput(const char * str,uptr addr,char ** out_name,char ** out_module,char ** out_file,uptr * line,uptr * start_address)81 static bool ParseCommandOutput(const char *str, uptr addr, char **out_name,
82                                char **out_module, char **out_file, uptr *line,
83                                uptr *start_address) {
84   // Trim ending newlines.
85   char *trim;
86   ExtractTokenUpToDelimiter(str, "\n", &trim);
87 
88   // The line from `atos` is in one of these formats:
89   //   myfunction (in library.dylib) (sourcefile.c:17)
90   //   myfunction (in library.dylib) + 0x1fe
91   //   myfunction (in library.dylib) + 15
92   //   0xdeadbeef (in library.dylib) + 0x1fe
93   //   0xdeadbeef (in library.dylib) + 15
94   //   0xdeadbeef (in library.dylib)
95   //   0xdeadbeef
96 
97   const char *rest = trim;
98   char *symbol_name;
99   rest = ExtractTokenUpToDelimiter(rest, " (in ", &symbol_name);
100   if (rest[0] == '\0') {
101     InternalFree(symbol_name);
102     InternalFree(trim);
103     return false;
104   }
105 
106   if (internal_strncmp(symbol_name, "0x", 2) != 0)
107     *out_name = symbol_name;
108   else
109     InternalFree(symbol_name);
110   rest = ExtractTokenUpToDelimiter(rest, ") ", out_module);
111 
112   if (rest[0] == '(') {
113     if (out_file) {
114       rest++;
115       rest = ExtractTokenUpToDelimiter(rest, ":", out_file);
116       char *extracted_line_number;
117       rest = ExtractTokenUpToDelimiter(rest, ")", &extracted_line_number);
118       if (line) *line = (uptr)internal_atoll(extracted_line_number);
119       InternalFree(extracted_line_number);
120     }
121   } else if (rest[0] == '+') {
122     rest += 2;
123     uptr offset = internal_atoll(rest);
124     if (start_address) *start_address = addr - offset;
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   if (addr == 0) return false;
137   char command[32];
138   internal_snprintf(command, sizeof(command), "0x%zx\n", addr);
139   const char *buf = process_->SendCommand(command);
140   if (!buf) return false;
141   uptr line;
142   if (!ParseCommandOutput(buf, addr, &stack->info.function, &stack->info.module,
143                           &stack->info.file, &line, nullptr)) {
144     process_ = nullptr;
145     return false;
146   }
147   stack->info.line = (int)line;
148   return true;
149 }
150 
SymbolizeData(uptr addr,DataInfo * info)151 bool AtosSymbolizer::SymbolizeData(uptr addr, DataInfo *info) {
152   if (!process_) return false;
153   char command[32];
154   internal_snprintf(command, sizeof(command), "0x%zx\n", addr);
155   const char *buf = process_->SendCommand(command);
156   if (!buf) return false;
157   if (!ParseCommandOutput(buf, addr, &info->name, &info->module, nullptr,
158                           nullptr, &info->start)) {
159     process_ = nullptr;
160     return false;
161   }
162   return true;
163 }
164 
165 }  // namespace __sanitizer
166 
167 #endif  // SANITIZER_MAC
168