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