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 22 namespace __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 31 bool 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 40 bool 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 50 class 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 82 static 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 90 static 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 99 static 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 155 AtosSymbolizer::AtosSymbolizer(const char *path, LowLevelAllocator *allocator) 156 : process_(new(*allocator) AtosSymbolizerProcess(path, getpid())) {} 157 158 bool 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 174 bool 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