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 = DemangleSwiftAndCXX(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 = DemangleSwiftAndCXX(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 bool ParseCommandOutput(const char *str, uptr addr, char **out_name,
83                               char **out_module, char **out_file, uptr *line,
84                               uptr *start_address) {
85  // Trim ending newlines.
86  char *trim;
87  ExtractTokenUpToDelimiter(str, "\n", &trim);
88
89  // The line from `atos` is in one of these formats:
90  //   myfunction (in library.dylib) (sourcefile.c:17)
91  //   myfunction (in library.dylib) + 0x1fe
92  //   myfunction (in library.dylib) + 15
93  //   0xdeadbeef (in library.dylib) + 0x1fe
94  //   0xdeadbeef (in library.dylib) + 15
95  //   0xdeadbeef (in library.dylib)
96  //   0xdeadbeef
97
98  const char *rest = trim;
99  char *symbol_name;
100  rest = ExtractTokenUpToDelimiter(rest, " (in ", &symbol_name);
101  if (rest[0] == '\0') {
102    InternalFree(symbol_name);
103    InternalFree(trim);
104    return false;
105  }
106
107  if (internal_strncmp(symbol_name, "0x", 2) != 0)
108    *out_name = symbol_name;
109  else
110    InternalFree(symbol_name);
111  rest = ExtractTokenUpToDelimiter(rest, ") ", out_module);
112
113  if (rest[0] == '(') {
114    if (out_file) {
115      rest++;
116      rest = ExtractTokenUpToDelimiter(rest, ":", out_file);
117      char *extracted_line_number;
118      rest = ExtractTokenUpToDelimiter(rest, ")", &extracted_line_number);
119      if (line) *line = (uptr)internal_atoll(extracted_line_number);
120      InternalFree(extracted_line_number);
121    }
122  } else if (rest[0] == '+') {
123    rest += 2;
124    uptr offset = internal_atoll(rest);
125    if (start_address) *start_address = addr - offset;
126  }
127
128  InternalFree(trim);
129  return true;
130}
131
132AtosSymbolizer::AtosSymbolizer(const char *path, LowLevelAllocator *allocator)
133    : process_(new(*allocator) AtosSymbolizerProcess(path, getpid())) {}
134
135bool AtosSymbolizer::SymbolizePC(uptr addr, SymbolizedStack *stack) {
136  if (!process_) return false;
137  if (addr == 0) return false;
138  char command[32];
139  internal_snprintf(command, sizeof(command), "0x%zx\n", addr);
140  const char *buf = process_->SendCommand(command);
141  if (!buf) return false;
142  uptr line;
143  if (!ParseCommandOutput(buf, addr, &stack->info.function, &stack->info.module,
144                          &stack->info.file, &line, nullptr)) {
145    process_ = nullptr;
146    return false;
147  }
148  stack->info.line = (int)line;
149  return true;
150}
151
152bool AtosSymbolizer::SymbolizeData(uptr addr, DataInfo *info) {
153  if (!process_) return false;
154  char command[32];
155  internal_snprintf(command, sizeof(command), "0x%zx\n", addr);
156  const char *buf = process_->SendCommand(command);
157  if (!buf) return false;
158  if (!ParseCommandOutput(buf, addr, &info->name, &info->module, nullptr,
159                          nullptr, &info->start)) {
160    process_ = nullptr;
161    return false;
162  }
163  return true;
164}
165
166}  // namespace __sanitizer
167
168#endif  // SANITIZER_MAC
169