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