sanitizer_symbolizer_mac.cc revision 1.3
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
20namespace __sanitizer {
21
22#include <dlfcn.h>
23#include <errno.h>
24#include <stdlib.h>
25#include <sys/wait.h>
26#include <unistd.h>
27#include <util.h>
28
29bool 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 = DemangleCXXABI(info.dli_sname);
34  stack->info.function = internal_strdup(demangled);
35  return true;
36}
37
38bool DlAddrSymbolizer::SymbolizeData(uptr addr, DataInfo *datainfo) {
39  Dl_info info;
40  int result = dladdr((const void *)addr, &info);
41  if (!result) return false;
42  const char *demangled = DemangleCXXABI(info.dli_sname);
43  datainfo->name = internal_strdup(demangled);
44  datainfo->start = (uptr)info.dli_saddr;
45  return true;
46}
47
48class AtosSymbolizerProcess : public SymbolizerProcess {
49 public:
50  explicit AtosSymbolizerProcess(const char *path, pid_t parent_pid)
51      : SymbolizerProcess(path, /*use_forkpty*/ true) {
52    // Put the string command line argument in the object so that it outlives
53    // the call to GetArgV.
54    internal_snprintf(pid_str_, sizeof(pid_str_), "%d", parent_pid);
55  }
56
57 private:
58  bool ReachedEndOfOutput(const char *buffer, uptr length) const override {
59    return (length >= 1 && buffer[length - 1] == '\n');
60  }
61
62  void GetArgV(const char *path_to_binary,
63               const char *(&argv)[kArgVMax]) const override {
64    int i = 0;
65    argv[i++] = path_to_binary;
66    argv[i++] = "-p";
67    argv[i++] = &pid_str_[0];
68    if (GetMacosVersion() == MACOS_VERSION_MAVERICKS) {
69      // On Mavericks atos prints a deprecation warning which we suppress by
70      // passing -d. The warning isn't present on other OSX versions, even the
71      // newer ones.
72      argv[i++] = "-d";
73    }
74    argv[i++] = nullptr;
75  }
76
77  char pid_str_[16];
78};
79
80static const char *kAtosErrorMessages[] = {
81  "atos cannot examine process",
82  "unable to get permission to examine process",
83  "An admin user name and password is required",
84  "could not load inserted library",
85  "architecture mismatch between analysis process",
86};
87
88static bool IsAtosErrorMessage(const char *str) {
89  for (uptr i = 0; i < ARRAY_SIZE(kAtosErrorMessages); i++) {
90    if (internal_strstr(str, kAtosErrorMessages[i])) {
91      return true;
92    }
93  }
94  return false;
95}
96
97static bool ParseCommandOutput(const char *str, uptr addr, char **out_name,
98                               char **out_module, char **out_file, uptr *line,
99                               uptr *start_address) {
100  // Trim ending newlines.
101  char *trim;
102  ExtractTokenUpToDelimiter(str, "\n", &trim);
103
104  // The line from `atos` is in one of these formats:
105  //   myfunction (in library.dylib) (sourcefile.c:17)
106  //   myfunction (in library.dylib) + 0x1fe
107  //   myfunction (in library.dylib) + 15
108  //   0xdeadbeef (in library.dylib) + 0x1fe
109  //   0xdeadbeef (in library.dylib) + 15
110  //   0xdeadbeef (in library.dylib)
111  //   0xdeadbeef
112
113  if (IsAtosErrorMessage(trim)) {
114    Report("atos returned an error: %s\n", trim);
115    InternalFree(trim);
116    return false;
117  }
118
119  const char *rest = trim;
120  char *symbol_name;
121  rest = ExtractTokenUpToDelimiter(rest, " (in ", &symbol_name);
122  if (internal_strncmp(symbol_name, "0x", 2) != 0)
123    *out_name = symbol_name;
124  else
125    InternalFree(symbol_name);
126  rest = ExtractTokenUpToDelimiter(rest, ") ", out_module);
127
128  if (rest[0] == '(') {
129    if (out_file) {
130      rest++;
131      rest = ExtractTokenUpToDelimiter(rest, ":", out_file);
132      char *extracted_line_number;
133      rest = ExtractTokenUpToDelimiter(rest, ")", &extracted_line_number);
134      if (line) *line = (uptr)internal_atoll(extracted_line_number);
135      InternalFree(extracted_line_number);
136    }
137  } else if (rest[0] == '+') {
138    rest += 2;
139    uptr offset = internal_atoll(rest);
140    if (start_address) *start_address = addr - offset;
141  }
142
143  InternalFree(trim);
144  return true;
145}
146
147AtosSymbolizer::AtosSymbolizer(const char *path, LowLevelAllocator *allocator)
148    : process_(new(*allocator) AtosSymbolizerProcess(path, getpid())) {}
149
150bool AtosSymbolizer::SymbolizePC(uptr addr, SymbolizedStack *stack) {
151  if (!process_) return false;
152  char command[32];
153  internal_snprintf(command, sizeof(command), "0x%zx\n", addr);
154  const char *buf = process_->SendCommand(command);
155  if (!buf) return false;
156  uptr line;
157  if (!ParseCommandOutput(buf, addr, &stack->info.function, &stack->info.module,
158                          &stack->info.file, &line, nullptr)) {
159    process_ = nullptr;
160    return false;
161  }
162  stack->info.line = (int)line;
163  return true;
164}
165
166bool AtosSymbolizer::SymbolizeData(uptr addr, DataInfo *info) {
167  if (!process_) return false;
168  char command[32];
169  internal_snprintf(command, sizeof(command), "0x%zx\n", addr);
170  const char *buf = process_->SendCommand(command);
171  if (!buf) return false;
172  if (!ParseCommandOutput(buf, addr, &info->name, &info->module, nullptr,
173                          nullptr, &info->start)) {
174    process_ = nullptr;
175    return false;
176  }
177  return true;
178}
179
180}  // namespace __sanitizer
181
182#endif  // SANITIZER_MAC
183