168d75effSDimitry Andric //===-- sanitizer_symbolizer_mac.cpp --------------------------------------===// 268d75effSDimitry Andric // 368d75effSDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 468d75effSDimitry Andric // See https://llvm.org/LICENSE.txt for license information. 568d75effSDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 668d75effSDimitry Andric // 768d75effSDimitry Andric //===----------------------------------------------------------------------===// 868d75effSDimitry Andric // 968d75effSDimitry Andric // This file is shared between various sanitizers' runtime libraries. 1068d75effSDimitry Andric // 1168d75effSDimitry Andric // Implementation of Mac-specific "atos" symbolizer. 1268d75effSDimitry Andric //===----------------------------------------------------------------------===// 1368d75effSDimitry Andric 1468d75effSDimitry Andric #include "sanitizer_platform.h" 1581ad6265SDimitry Andric #if SANITIZER_APPLE 1668d75effSDimitry Andric 1768d75effSDimitry Andric # include <dlfcn.h> 1868d75effSDimitry Andric # include <errno.h> 1968d75effSDimitry Andric # include <stdlib.h> 2068d75effSDimitry Andric # include <sys/wait.h> 2168d75effSDimitry Andric # include <unistd.h> 2268d75effSDimitry Andric # include <util.h> 2368d75effSDimitry Andric 2406c3fb27SDimitry Andric # include "sanitizer_allocator_internal.h" 2506c3fb27SDimitry Andric # include "sanitizer_mac.h" 2606c3fb27SDimitry Andric # include "sanitizer_symbolizer_mac.h" 2706c3fb27SDimitry Andric 2868d75effSDimitry Andric namespace __sanitizer { 2968d75effSDimitry Andric 3068d75effSDimitry Andric bool DlAddrSymbolizer::SymbolizePC(uptr addr, SymbolizedStack *stack) { 3168d75effSDimitry Andric Dl_info info; 3268d75effSDimitry Andric int result = dladdr((const void *)addr, &info); 3368d75effSDimitry Andric if (!result) return false; 34480093f4SDimitry Andric 35e8d8bef9SDimitry Andric // Compute offset if possible. `dladdr()` doesn't always ensure that `addr >= 36e8d8bef9SDimitry Andric // sym_addr` so only compute the offset when this holds. Failure to find the 37e8d8bef9SDimitry Andric // function offset is not treated as a failure because it might still be 38e8d8bef9SDimitry Andric // possible to get the symbol name. 39e8d8bef9SDimitry Andric uptr sym_addr = reinterpret_cast<uptr>(info.dli_saddr); 40e8d8bef9SDimitry Andric if (addr >= sym_addr) { 41e8d8bef9SDimitry Andric stack->info.function_offset = addr - sym_addr; 42e8d8bef9SDimitry Andric } 43e8d8bef9SDimitry Andric 4468d75effSDimitry Andric const char *demangled = DemangleSwiftAndCXX(info.dli_sname); 45*5f757f3fSDimitry Andric if (!demangled) 46*5f757f3fSDimitry Andric demangled = info.dli_sname; 4768d75effSDimitry Andric stack->info.function = internal_strdup(demangled); 4868d75effSDimitry Andric return true; 4968d75effSDimitry Andric } 5068d75effSDimitry Andric 5168d75effSDimitry Andric bool DlAddrSymbolizer::SymbolizeData(uptr addr, DataInfo *datainfo) { 5268d75effSDimitry Andric Dl_info info; 5368d75effSDimitry Andric int result = dladdr((const void *)addr, &info); 5468d75effSDimitry Andric if (!result) return false; 5568d75effSDimitry Andric const char *demangled = DemangleSwiftAndCXX(info.dli_sname); 56*5f757f3fSDimitry Andric if (!demangled) 57*5f757f3fSDimitry Andric demangled = info.dli_sname; 5868d75effSDimitry Andric datainfo->name = internal_strdup(demangled); 5968d75effSDimitry Andric datainfo->start = (uptr)info.dli_saddr; 6068d75effSDimitry Andric return true; 6168d75effSDimitry Andric } 6268d75effSDimitry Andric 63e8d8bef9SDimitry Andric class AtosSymbolizerProcess final : public SymbolizerProcess { 6468d75effSDimitry Andric public: 655ffd83dbSDimitry Andric explicit AtosSymbolizerProcess(const char *path) 6668d75effSDimitry Andric : SymbolizerProcess(path, /*use_posix_spawn*/ true) { 675ffd83dbSDimitry Andric pid_str_[0] = '\0'; 685ffd83dbSDimitry Andric } 695ffd83dbSDimitry Andric 7068d75effSDimitry Andric private: 7168d75effSDimitry Andric bool StartSymbolizerSubprocess() override { 725ffd83dbSDimitry Andric // Put the string command line argument in the object so that it outlives 735ffd83dbSDimitry Andric // the call to GetArgV. 740eae32dcSDimitry Andric internal_snprintf(pid_str_, sizeof(pid_str_), "%d", (int)internal_getpid()); 755ffd83dbSDimitry Andric 760eae32dcSDimitry Andric // Configure sandbox before starting atos process. 7768d75effSDimitry Andric return SymbolizerProcess::StartSymbolizerSubprocess(); 7868d75effSDimitry Andric } 7968d75effSDimitry Andric 8068d75effSDimitry Andric bool ReachedEndOfOutput(const char *buffer, uptr length) const override { 8168d75effSDimitry Andric return (length >= 1 && buffer[length - 1] == '\n'); 8268d75effSDimitry Andric } 8368d75effSDimitry Andric 8468d75effSDimitry Andric void GetArgV(const char *path_to_binary, 8568d75effSDimitry Andric const char *(&argv)[kArgVMax]) const override { 8668d75effSDimitry Andric int i = 0; 8768d75effSDimitry Andric argv[i++] = path_to_binary; 8868d75effSDimitry Andric argv[i++] = "-p"; 8968d75effSDimitry Andric argv[i++] = &pid_str_[0]; 905ffd83dbSDimitry Andric if (GetMacosAlignedVersion() == MacosVersion(10, 9)) { 9168d75effSDimitry Andric // On Mavericks atos prints a deprecation warning which we suppress by 9268d75effSDimitry Andric // passing -d. The warning isn't present on other OSX versions, even the 9368d75effSDimitry Andric // newer ones. 9468d75effSDimitry Andric argv[i++] = "-d"; 9568d75effSDimitry Andric } 9668d75effSDimitry Andric argv[i++] = nullptr; 970eae32dcSDimitry Andric CHECK_LE(i, kArgVMax); 9868d75effSDimitry Andric } 9968d75effSDimitry Andric 10068d75effSDimitry Andric char pid_str_[16]; 10168d75effSDimitry Andric }; 10268d75effSDimitry Andric 1035ffd83dbSDimitry Andric #undef K_ATOS_ENV_VAR 1045ffd83dbSDimitry Andric 10568d75effSDimitry Andric static bool ParseCommandOutput(const char *str, uptr addr, char **out_name, 10668d75effSDimitry Andric char **out_module, char **out_file, uptr *line, 10768d75effSDimitry Andric uptr *start_address) { 10868d75effSDimitry Andric // Trim ending newlines. 10968d75effSDimitry Andric char *trim; 11068d75effSDimitry Andric ExtractTokenUpToDelimiter(str, "\n", &trim); 11168d75effSDimitry Andric 11268d75effSDimitry Andric // The line from `atos` is in one of these formats: 11368d75effSDimitry Andric // myfunction (in library.dylib) (sourcefile.c:17) 11468d75effSDimitry Andric // myfunction (in library.dylib) + 0x1fe 11568d75effSDimitry Andric // myfunction (in library.dylib) + 15 11668d75effSDimitry Andric // 0xdeadbeef (in library.dylib) + 0x1fe 11768d75effSDimitry Andric // 0xdeadbeef (in library.dylib) + 15 11868d75effSDimitry Andric // 0xdeadbeef (in library.dylib) 11968d75effSDimitry Andric // 0xdeadbeef 12068d75effSDimitry Andric 12168d75effSDimitry Andric const char *rest = trim; 12268d75effSDimitry Andric char *symbol_name; 12368d75effSDimitry Andric rest = ExtractTokenUpToDelimiter(rest, " (in ", &symbol_name); 12468d75effSDimitry Andric if (rest[0] == '\0') { 12568d75effSDimitry Andric InternalFree(symbol_name); 12668d75effSDimitry Andric InternalFree(trim); 12768d75effSDimitry Andric return false; 12868d75effSDimitry Andric } 12968d75effSDimitry Andric 13068d75effSDimitry Andric if (internal_strncmp(symbol_name, "0x", 2) != 0) 13168d75effSDimitry Andric *out_name = symbol_name; 13268d75effSDimitry Andric else 13368d75effSDimitry Andric InternalFree(symbol_name); 13468d75effSDimitry Andric rest = ExtractTokenUpToDelimiter(rest, ") ", out_module); 13568d75effSDimitry Andric 13668d75effSDimitry Andric if (rest[0] == '(') { 13768d75effSDimitry Andric if (out_file) { 13868d75effSDimitry Andric rest++; 13968d75effSDimitry Andric rest = ExtractTokenUpToDelimiter(rest, ":", out_file); 14068d75effSDimitry Andric char *extracted_line_number; 14168d75effSDimitry Andric rest = ExtractTokenUpToDelimiter(rest, ")", &extracted_line_number); 14268d75effSDimitry Andric if (line) *line = (uptr)internal_atoll(extracted_line_number); 14368d75effSDimitry Andric InternalFree(extracted_line_number); 14468d75effSDimitry Andric } 14568d75effSDimitry Andric } else if (rest[0] == '+') { 14668d75effSDimitry Andric rest += 2; 14768d75effSDimitry Andric uptr offset = internal_atoll(rest); 14868d75effSDimitry Andric if (start_address) *start_address = addr - offset; 14968d75effSDimitry Andric } 15068d75effSDimitry Andric 15168d75effSDimitry Andric InternalFree(trim); 15268d75effSDimitry Andric return true; 15368d75effSDimitry Andric } 15468d75effSDimitry Andric 15568d75effSDimitry Andric AtosSymbolizer::AtosSymbolizer(const char *path, LowLevelAllocator *allocator) 1565ffd83dbSDimitry Andric : process_(new (*allocator) AtosSymbolizerProcess(path)) {} 15768d75effSDimitry Andric 15868d75effSDimitry Andric bool AtosSymbolizer::SymbolizePC(uptr addr, SymbolizedStack *stack) { 15968d75effSDimitry Andric if (!process_) return false; 16068d75effSDimitry Andric if (addr == 0) return false; 16168d75effSDimitry Andric char command[32]; 16268d75effSDimitry Andric internal_snprintf(command, sizeof(command), "0x%zx\n", addr); 16368d75effSDimitry Andric const char *buf = process_->SendCommand(command); 16468d75effSDimitry Andric if (!buf) return false; 16568d75effSDimitry Andric uptr line; 166480093f4SDimitry Andric uptr start_address = AddressInfo::kUnknown; 16768d75effSDimitry Andric if (!ParseCommandOutput(buf, addr, &stack->info.function, &stack->info.module, 168480093f4SDimitry Andric &stack->info.file, &line, &start_address)) { 16906c3fb27SDimitry Andric Report("WARNING: atos failed to symbolize address \"0x%zx\"\n", addr); 17068d75effSDimitry Andric return false; 17168d75effSDimitry Andric } 17268d75effSDimitry Andric stack->info.line = (int)line; 173480093f4SDimitry Andric 174480093f4SDimitry Andric if (start_address == AddressInfo::kUnknown) { 175480093f4SDimitry Andric // Fallback to dladdr() to get function start address if atos doesn't report 176480093f4SDimitry Andric // it. 177480093f4SDimitry Andric Dl_info info; 178480093f4SDimitry Andric int result = dladdr((const void *)addr, &info); 179480093f4SDimitry Andric if (result) 180480093f4SDimitry Andric start_address = reinterpret_cast<uptr>(info.dli_saddr); 181480093f4SDimitry Andric } 182480093f4SDimitry Andric 183e8d8bef9SDimitry Andric // Only assign to `function_offset` if we were able to get the function's 184e8d8bef9SDimitry Andric // start address and we got a sensible `start_address` (dladdr doesn't always 185e8d8bef9SDimitry Andric // ensure that `addr >= sym_addr`). 186e8d8bef9SDimitry Andric if (start_address != AddressInfo::kUnknown && addr >= start_address) { 187480093f4SDimitry Andric stack->info.function_offset = addr - start_address; 188480093f4SDimitry Andric } 18968d75effSDimitry Andric return true; 19068d75effSDimitry Andric } 19168d75effSDimitry Andric 19268d75effSDimitry Andric bool AtosSymbolizer::SymbolizeData(uptr addr, DataInfo *info) { 19368d75effSDimitry Andric if (!process_) return false; 19468d75effSDimitry Andric char command[32]; 19568d75effSDimitry Andric internal_snprintf(command, sizeof(command), "0x%zx\n", addr); 19668d75effSDimitry Andric const char *buf = process_->SendCommand(command); 19768d75effSDimitry Andric if (!buf) return false; 19868d75effSDimitry Andric if (!ParseCommandOutput(buf, addr, &info->name, &info->module, nullptr, 19968d75effSDimitry Andric nullptr, &info->start)) { 20068d75effSDimitry Andric process_ = nullptr; 20168d75effSDimitry Andric return false; 20268d75effSDimitry Andric } 20368d75effSDimitry Andric return true; 20468d75effSDimitry Andric } 20568d75effSDimitry Andric 20668d75effSDimitry Andric } // namespace __sanitizer 20768d75effSDimitry Andric 20881ad6265SDimitry Andric #endif // SANITIZER_APPLE 209