583 lines · cpp
1//===-- sanitizer_symbolizer_libcdep.cpp ----------------------------------===//2//3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.4// See https://llvm.org/LICENSE.txt for license information.5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception6//7//===----------------------------------------------------------------------===//8//9// This file is shared between AddressSanitizer and ThreadSanitizer10// run-time libraries.11//===----------------------------------------------------------------------===//12 13#include "sanitizer_allocator_internal.h"14#include "sanitizer_internal_defs.h"15#include "sanitizer_platform.h"16#include "sanitizer_symbolizer_internal.h"17 18namespace __sanitizer {19 20Symbolizer *Symbolizer::GetOrInit() {21 SpinMutexLock l(&init_mu_);22 if (symbolizer_)23 return symbolizer_;24 symbolizer_ = PlatformInit();25 CHECK(symbolizer_);26 return symbolizer_;27}28 29// See sanitizer_symbolizer_markup.cpp.30#if !SANITIZER_SYMBOLIZER_MARKUP31 32const char *ExtractToken(const char *str, const char *delims, char **result) {33 uptr prefix_len = internal_strcspn(str, delims);34 *result = (char *)InternalAlloc(prefix_len + 1);35 internal_memcpy(*result, str, prefix_len);36 (*result)[prefix_len] = '\0';37 const char *prefix_end = str + prefix_len;38 if (*prefix_end != '\0')39 prefix_end++;40 return prefix_end;41}42 43const char *ExtractInt(const char *str, const char *delims, int *result) {44 char *buff = nullptr;45 const char *ret = ExtractToken(str, delims, &buff);46 if (buff) {47 *result = (int)internal_atoll(buff);48 }49 InternalFree(buff);50 return ret;51}52 53const char *ExtractUptr(const char *str, const char *delims, uptr *result) {54 char *buff = nullptr;55 const char *ret = ExtractToken(str, delims, &buff);56 if (buff) {57 *result = (uptr)internal_atoll(buff);58 }59 InternalFree(buff);60 return ret;61}62 63const char *ExtractSptr(const char *str, const char *delims, sptr *result) {64 char *buff = nullptr;65 const char *ret = ExtractToken(str, delims, &buff);66 if (buff) {67 *result = (sptr)internal_atoll(buff);68 }69 InternalFree(buff);70 return ret;71}72 73const char *ExtractTokenUpToDelimiter(const char *str, const char *delimiter,74 char **result) {75 const char *found_delimiter = internal_strstr(str, delimiter);76 uptr prefix_len =77 found_delimiter ? found_delimiter - str : internal_strlen(str);78 *result = (char *)InternalAlloc(prefix_len + 1);79 internal_memcpy(*result, str, prefix_len);80 (*result)[prefix_len] = '\0';81 const char *prefix_end = str + prefix_len;82 if (*prefix_end != '\0')83 prefix_end += internal_strlen(delimiter);84 return prefix_end;85}86 87SymbolizedStack *Symbolizer::SymbolizePC(uptr addr) {88 Lock l(&mu_);89 SymbolizedStack *res = SymbolizedStack::New(addr);90 auto *mod = FindModuleForAddress(addr);91 if (!mod)92 return res;93 // Always fill data about module name and offset.94 res->info.FillModuleInfo(*mod);95 for (auto &tool : tools_) {96 SymbolizerScope sym_scope(this);97 if (tool.SymbolizePC(addr, res)) {98 return res;99 }100 }101 return res;102}103 104bool Symbolizer::SymbolizeData(uptr addr, DataInfo *info) {105 Lock l(&mu_);106 const char *module_name = nullptr;107 uptr module_offset;108 ModuleArch arch;109 if (!FindModuleNameAndOffsetForAddress(addr, &module_name, &module_offset,110 &arch))111 return false;112 info->Clear();113 info->module = internal_strdup(module_name);114 info->module_offset = module_offset;115 info->module_arch = arch;116 for (auto &tool : tools_) {117 SymbolizerScope sym_scope(this);118 if (tool.SymbolizeData(addr, info)) {119 return true;120 }121 }122 return false;123}124 125bool Symbolizer::SymbolizeFrame(uptr addr, FrameInfo *info) {126 Lock l(&mu_);127 const char *module_name = nullptr;128 if (!FindModuleNameAndOffsetForAddress(129 addr, &module_name, &info->module_offset, &info->module_arch))130 return false;131 info->module = internal_strdup(module_name);132 for (auto &tool : tools_) {133 SymbolizerScope sym_scope(this);134 if (tool.SymbolizeFrame(addr, info)) {135 return true;136 }137 }138 return false;139}140 141bool Symbolizer::GetModuleNameAndOffsetForPC(uptr pc, const char **module_name,142 uptr *module_address) {143 Lock l(&mu_);144 const char *internal_module_name = nullptr;145 ModuleArch arch;146 if (!FindModuleNameAndOffsetForAddress(pc, &internal_module_name,147 module_address, &arch))148 return false;149 150 if (module_name)151 *module_name = module_names_.GetOwnedCopy(internal_module_name);152 return true;153}154 155void Symbolizer::Flush() {156 Lock l(&mu_);157 for (auto &tool : tools_) {158 SymbolizerScope sym_scope(this);159 tool.Flush();160 }161}162 163const char *Symbolizer::Demangle(const char *name) {164 CHECK(name);165 Lock l(&mu_);166 for (auto &tool : tools_) {167 SymbolizerScope sym_scope(this);168 if (const char *demangled = tool.Demangle(name))169 return demangled;170 }171 if (const char *demangled = PlatformDemangle(name))172 return demangled;173 return name;174}175 176bool Symbolizer::FindModuleNameAndOffsetForAddress(uptr address,177 const char **module_name,178 uptr *module_offset,179 ModuleArch *module_arch) {180 const LoadedModule *module = FindModuleForAddress(address);181 if (!module)182 return false;183 *module_name = module->full_name();184 *module_offset = address - module->base_address();185 *module_arch = module->arch();186 return true;187}188 189void Symbolizer::RefreshModules() {190 modules_.init();191 fallback_modules_.fallbackInit();192 RAW_CHECK(modules_.size() > 0);193 modules_fresh_ = true;194}195 196const ListOfModules &Symbolizer::GetRefreshedListOfModules() {197 if (!modules_fresh_)198 RefreshModules();199 200 return modules_;201}202 203static const LoadedModule *SearchForModule(const ListOfModules &modules,204 uptr address) {205 for (uptr i = 0; i < modules.size(); i++) {206 if (modules[i].containsAddress(address)) {207 return &modules[i];208 }209 }210 return nullptr;211}212 213const LoadedModule *Symbolizer::FindModuleForAddress(uptr address) {214 bool modules_were_reloaded = false;215 if (!modules_fresh_) {216 RefreshModules();217 modules_were_reloaded = true;218 }219 const LoadedModule *module = SearchForModule(modules_, address);220 if (module)221 return module;222 223 // dlopen/dlclose interceptors invalidate the module list, but when224 // interception is disabled, we need to retry if the lookup fails in225 // case the module list changed.226# if !SANITIZER_INTERCEPT_DLOPEN_DLCLOSE227 if (!modules_were_reloaded) {228 RefreshModules();229 module = SearchForModule(modules_, address);230 if (module)231 return module;232 }233# endif234 235 if (fallback_modules_.size()) {236 module = SearchForModule(fallback_modules_, address);237 }238 return module;239}240 241// For now we assume the following protocol:242// For each request of the form243// <module_name> <module_offset>244// passed to STDIN, external symbolizer prints to STDOUT response:245// <function_name>246// <file_name>:<line_number>:<column_number>247// <function_name>248// <file_name>:<line_number>:<column_number>249// ...250// <empty line>251class LLVMSymbolizerProcess final : public SymbolizerProcess {252 public:253 explicit LLVMSymbolizerProcess(const char *path)254 : SymbolizerProcess(path, /*use_posix_spawn=*/SANITIZER_APPLE) {}255 256 private:257 bool ReachedEndOfOutput(const char *buffer, uptr length) const override {258 // Empty line marks the end of llvm-symbolizer output.259 return length >= 2 && buffer[length - 1] == '\n' &&260 buffer[length - 2] == '\n';261 }262 263 // When adding a new architecture, don't forget to also update264 // script/asan_symbolize.py and sanitizer_common.h.265 void GetArgV(const char *path_to_binary,266 const char *(&argv)[kArgVMax]) const override {267# if defined(__x86_64h__)268 const char *const kSymbolizerArch = "--default-arch=x86_64h";269# elif defined(__x86_64__)270 const char *const kSymbolizerArch = "--default-arch=x86_64";271# elif defined(__i386__)272 const char *const kSymbolizerArch = "--default-arch=i386";273# elif SANITIZER_LOONGARCH64274 const char *const kSymbolizerArch = "--default-arch=loongarch64";275# elif SANITIZER_RISCV64276 const char *const kSymbolizerArch = "--default-arch=riscv64";277# elif defined(__aarch64__)278 const char *const kSymbolizerArch = "--default-arch=arm64";279# elif defined(__arm__)280 const char *const kSymbolizerArch = "--default-arch=arm";281# elif defined(__powerpc64__) && __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__282 const char *const kSymbolizerArch = "--default-arch=powerpc64";283# elif defined(__powerpc64__) && __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__284 const char *const kSymbolizerArch = "--default-arch=powerpc64le";285# elif defined(__s390x__)286 const char *const kSymbolizerArch = "--default-arch=s390x";287# elif defined(__s390__)288 const char *const kSymbolizerArch = "--default-arch=s390";289# else290 const char *const kSymbolizerArch = "--default-arch=unknown";291# endif292 293 const char *const demangle_flag =294 common_flags()->demangle ? "--demangle" : "--no-demangle";295 const char *const inline_flag =296 common_flags()->symbolize_inline_frames ? "--inlines" : "--no-inlines";297 int i = 0;298 argv[i++] = path_to_binary;299 argv[i++] = demangle_flag;300 argv[i++] = inline_flag;301 argv[i++] = kSymbolizerArch;302 argv[i++] = nullptr;303 CHECK_LE(i, kArgVMax);304 }305};306 307LLVMSymbolizer::LLVMSymbolizer(const char *path, LowLevelAllocator *allocator)308 : symbolizer_process_(new(*allocator) LLVMSymbolizerProcess(path)) {}309 310// Parse a <file>:<line>[:<column>] buffer. The file path may contain colons on311// Windows, so extract tokens from the right hand side first. The column info is312// also optional.313static const char *ParseFileLineInfo(AddressInfo *info, const char *str) {314 char *file_line_info = nullptr;315 str = ExtractToken(str, "\n", &file_line_info);316 CHECK(file_line_info);317 318 if (uptr size = internal_strlen(file_line_info)) {319 char *back = file_line_info + size - 1;320 for (int i = 0; i < 2; ++i) {321 while (back > file_line_info && IsDigit(*back)) --back;322 if (*back != ':' || !IsDigit(back[1]))323 break;324 info->column = info->line;325 info->line = internal_atoll(back + 1);326 // Truncate the string at the colon to keep only filename.327 *back = '\0';328 --back;329 }330 ExtractToken(file_line_info, "", &info->file);331 }332 333 InternalFree(file_line_info);334 return str;335}336 337// Parses one or more two-line strings in the following format:338// <function_name>339// <file_name>:<line_number>[:<column_number>]340// Used by LLVMSymbolizer, Addr2LinePool and InternalSymbolizer, since all of341// them use the same output format.342void ParseSymbolizePCOutput(const char *str, SymbolizedStack *res) {343 bool top_frame = true;344 SymbolizedStack *last = res;345 while (true) {346 char *function_name = nullptr;347 str = ExtractToken(str, "\n", &function_name);348 CHECK(function_name);349 if (function_name[0] == '\0') {350 // There are no more frames.351 InternalFree(function_name);352 break;353 }354 SymbolizedStack *cur;355 if (top_frame) {356 cur = res;357 top_frame = false;358 } else {359 cur = SymbolizedStack::New(res->info.address);360 cur->info.FillModuleInfo(res->info.module, res->info.module_offset,361 res->info.module_arch);362 last->next = cur;363 last = cur;364 }365 366 AddressInfo *info = &cur->info;367 info->function = function_name;368 str = ParseFileLineInfo(info, str);369 370 // Functions and filenames can be "??", in which case we write 0371 // to address info to mark that names are unknown.372 if (0 == internal_strcmp(info->function, "??")) {373 InternalFree(info->function);374 info->function = 0;375 }376 if (info->file && 0 == internal_strcmp(info->file, "??")) {377 InternalFree(info->file);378 info->file = 0;379 }380 }381}382 383// Parses a two- or three-line string in the following format:384// <symbol_name>385// <start_address> <size>386// <filename>:<column>387// Used by LLVMSymbolizer and InternalSymbolizer. LLVMSymbolizer added support388// for symbolizing the third line in D123538, but we support the older two-line389// information as well.390void ParseSymbolizeDataOutput(const char *str, DataInfo *info) {391 str = ExtractToken(str, "\n", &info->name);392 str = ExtractUptr(str, " ", &info->start);393 str = ExtractUptr(str, "\n", &info->size);394 // Note: If the third line isn't present, these calls will set info.{file,395 // line} to empty strings.396 str = ExtractToken(str, ":", &info->file);397 str = ExtractUptr(str, "\n", &info->line);398}399 400void ParseSymbolizeFrameOutput(const char *str,401 InternalMmapVector<LocalInfo> *locals) {402 if (internal_strncmp(str, "??", 2) == 0)403 return;404 405 while (*str) {406 LocalInfo local;407 str = ExtractToken(str, "\n", &local.function_name);408 str = ExtractToken(str, "\n", &local.name);409 410 AddressInfo addr;411 str = ParseFileLineInfo(&addr, str);412 local.decl_file = addr.file;413 local.decl_line = addr.line;414 415 local.has_frame_offset = internal_strncmp(str, "??", 2) != 0;416 str = ExtractSptr(str, " ", &local.frame_offset);417 418 local.has_size = internal_strncmp(str, "??", 2) != 0;419 str = ExtractUptr(str, " ", &local.size);420 421 local.has_tag_offset = internal_strncmp(str, "??", 2) != 0;422 str = ExtractUptr(str, "\n", &local.tag_offset);423 424 locals->push_back(local);425 }426}427 428bool LLVMSymbolizer::SymbolizePC(uptr addr, SymbolizedStack *stack) {429 AddressInfo *info = &stack->info;430 const char *buf = FormatAndSendCommand(431 "CODE", info->module, info->module_offset, info->module_arch);432 if (!buf)433 return false;434 ParseSymbolizePCOutput(buf, stack);435 return true;436}437 438bool LLVMSymbolizer::SymbolizeData(uptr addr, DataInfo *info) {439 const char *buf = FormatAndSendCommand(440 "DATA", info->module, info->module_offset, info->module_arch);441 if (!buf)442 return false;443 ParseSymbolizeDataOutput(buf, info);444 info->start += (addr - info->module_offset); // Add the base address.445 return true;446}447 448bool LLVMSymbolizer::SymbolizeFrame(uptr addr, FrameInfo *info) {449 const char *buf = FormatAndSendCommand(450 "FRAME", info->module, info->module_offset, info->module_arch);451 if (!buf)452 return false;453 ParseSymbolizeFrameOutput(buf, &info->locals);454 return true;455}456 457const char *LLVMSymbolizer::FormatAndSendCommand(const char *command_prefix,458 const char *module_name,459 uptr module_offset,460 ModuleArch arch) {461 CHECK(module_name);462 int size_needed = 0;463 if (arch == kModuleArchUnknown)464 size_needed = internal_snprintf(buffer_, kBufferSize, "%s \"%s\" 0x%zx\n",465 command_prefix, module_name, module_offset);466 else467 size_needed = internal_snprintf(468 buffer_, kBufferSize, "%s \"%s:%s\" 0x%zx\n", command_prefix,469 module_name, ModuleArchToString(arch), module_offset);470 471 if (size_needed >= static_cast<int>(kBufferSize)) {472 Report("WARNING: Command buffer too small");473 return nullptr;474 }475 476 return symbolizer_process_->SendCommand(buffer_);477}478 479SymbolizerProcess::SymbolizerProcess(const char *path, bool use_posix_spawn)480 : path_(path),481 input_fd_(kInvalidFd),482 output_fd_(kInvalidFd),483 times_restarted_(0),484 failed_to_start_(false),485 reported_invalid_path_(false),486 use_posix_spawn_(use_posix_spawn) {487 CHECK(path_);488 CHECK_NE(path_[0], '\0');489}490 491static bool IsSameModule(const char *path) {492 if (const char *ProcessName = GetProcessName()) {493 if (const char *SymbolizerName = StripModuleName(path)) {494 return !internal_strcmp(ProcessName, SymbolizerName);495 }496 }497 return false;498}499 500const char *SymbolizerProcess::SendCommand(const char *command) {501 if (failed_to_start_)502 return nullptr;503 if (IsSameModule(path_)) {504 Report("WARNING: Symbolizer was blocked from starting itself!\n");505 failed_to_start_ = true;506 return nullptr;507 }508 for (; times_restarted_ < kMaxTimesRestarted; times_restarted_++) {509 // Start or restart symbolizer if we failed to send command to it.510 if (const char *res = SendCommandImpl(command))511 return res;512 Restart();513 }514 if (!failed_to_start_) {515 Report("WARNING: Failed to use and restart external symbolizer!\n");516 failed_to_start_ = true;517 }518 return nullptr;519}520 521const char *SymbolizerProcess::SendCommandImpl(const char *command) {522 if (input_fd_ == kInvalidFd || output_fd_ == kInvalidFd)523 return nullptr;524 if (!WriteToSymbolizer(command, internal_strlen(command)))525 return nullptr;526 if (!ReadFromSymbolizer())527 return nullptr;528 return buffer_.data();529}530 531bool SymbolizerProcess::Restart() {532 if (input_fd_ != kInvalidFd)533 CloseFile(input_fd_);534 if (output_fd_ != kInvalidFd)535 CloseFile(output_fd_);536 return StartSymbolizerSubprocess();537}538 539bool SymbolizerProcess::ReadFromSymbolizer() {540 buffer_.clear();541 constexpr uptr max_length = 1024;542 bool ret = true;543 do {544 uptr just_read = 0;545 uptr size_before = buffer_.size();546 buffer_.resize(size_before + max_length);547 buffer_.resize(buffer_.capacity());548 bool ret = ReadFromFile(input_fd_, &buffer_[size_before],549 buffer_.size() - size_before, &just_read);550 551 if (!ret)552 just_read = 0;553 554 buffer_.resize(size_before + just_read);555 556 // We can't read 0 bytes, as we don't expect external symbolizer to close557 // its stdout.558 if (just_read == 0) {559 Report("WARNING: Can't read from symbolizer at fd %d\n", input_fd_);560 ret = false;561 break;562 }563 } while (!ReachedEndOfOutput(buffer_.data(), buffer_.size()));564 buffer_.push_back('\0');565 return ret;566}567 568bool SymbolizerProcess::WriteToSymbolizer(const char *buffer, uptr length) {569 if (length == 0)570 return true;571 uptr write_len = 0;572 bool success = WriteToFile(output_fd_, buffer, length, &write_len);573 if (!success || write_len != length) {574 Report("WARNING: Can't write to symbolizer at fd %d\n", output_fd_);575 return false;576 }577 return true;578}579 580#endif // !SANITIZER_SYMBOLIZER_MARKUP581 582} // namespace __sanitizer583