//===-- sanitizer_symbolizer_libbacktrace.cpp -----------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // // This file is shared between AddressSanitizer and ThreadSanitizer // run-time libraries. // Libbacktrace implementation of symbolizer parts. //===----------------------------------------------------------------------===// #include "sanitizer_platform.h" #include "sanitizer_internal_defs.h" #include "sanitizer_symbolizer.h" #include "sanitizer_symbolizer_libbacktrace.h" #if SANITIZER_LIBBACKTRACE # error #include "backtrace-supported.h" # if SANITIZER_POSIX && BACKTRACE_SUPPORTED && !BACKTRACE_USES_MALLOC # error #include "backtrace.h" # if SANITIZER_CP_DEMANGLE # undef ARRAY_SIZE # error #include "demangle.h" # endif # else # define SANITIZER_LIBBACKTRACE 0 # endif #endif namespace __sanitizer { static char *DemangleAlloc(const char *name, bool always_alloc); #if SANITIZER_LIBBACKTRACE namespace { # if SANITIZER_CP_DEMANGLE struct CplusV3DemangleData { char *buf; uptr size, allocated; }; extern "C" { static void CplusV3DemangleCallback(const char *s, size_t l, void *vdata) { CplusV3DemangleData *data = (CplusV3DemangleData *)vdata; uptr needed = data->size + l + 1; if (needed > data->allocated) { data->allocated *= 2; if (needed > data->allocated) data->allocated = needed; char *buf = (char *)InternalAlloc(data->allocated); if (data->buf) { internal_memcpy(buf, data->buf, data->size); InternalFree(data->buf); } data->buf = buf; } internal_memcpy(data->buf + data->size, s, l); data->buf[data->size + l] = '\0'; data->size += l; } } // extern "C" char *CplusV3Demangle(const char *name) { CplusV3DemangleData data; data.buf = 0; data.size = 0; data.allocated = 0; if (cplus_demangle_v3_callback(name, DMGL_PARAMS | DMGL_ANSI, CplusV3DemangleCallback, &data)) { if (data.size + 64 > data.allocated) return data.buf; char *buf = internal_strdup(data.buf); InternalFree(data.buf); return buf; } if (data.buf) InternalFree(data.buf); return 0; } # endif // SANITIZER_CP_DEMANGLE struct SymbolizeCodeCallbackArg { SymbolizedStack *first; SymbolizedStack *last; uptr frames_symbolized; AddressInfo *get_new_frame(uintptr_t addr) { CHECK(last); if (frames_symbolized > 0) { SymbolizedStack *cur = SymbolizedStack::New(addr); AddressInfo *info = &cur->info; info->FillModuleInfo(first->info.module, first->info.module_offset, first->info.module_arch); last->next = cur; last = cur; } CHECK_EQ(addr, first->info.address); CHECK_EQ(addr, last->info.address); return &last->info; } }; extern "C" { static int SymbolizeCodePCInfoCallback(void *vdata, uintptr_t addr, const char *filename, int lineno, const char *function) { SymbolizeCodeCallbackArg *cdata = (SymbolizeCodeCallbackArg *)vdata; if (function) { AddressInfo *info = cdata->get_new_frame(addr); info->function = DemangleAlloc(function, /*always_alloc*/ true); if (filename) info->file = internal_strdup(filename); info->line = lineno; cdata->frames_symbolized++; } return 0; } static void SymbolizeCodeCallback(void *vdata, uintptr_t addr, const char *symname, uintptr_t, uintptr_t) { SymbolizeCodeCallbackArg *cdata = (SymbolizeCodeCallbackArg *)vdata; if (symname) { AddressInfo *info = cdata->get_new_frame(addr); info->function = DemangleAlloc(symname, /*always_alloc*/ true); cdata->frames_symbolized++; } } static void SymbolizeDataCallback(void *vdata, uintptr_t, const char *symname, uintptr_t symval, uintptr_t symsize) { DataInfo *info = (DataInfo *)vdata; if (symname && symval) { info->name = DemangleAlloc(symname, /*always_alloc*/ true); info->start = symval; info->size = symsize; } } static void ErrorCallback(void *, const char *, int) {} } // extern "C" } // namespace LibbacktraceSymbolizer *LibbacktraceSymbolizer::get(LowLevelAllocator *alloc) { // State created in backtrace_create_state is leaked. void *state = (void *)(backtrace_create_state("/proc/self/exe", 0, ErrorCallback, NULL)); if (!state) return 0; return new(*alloc) LibbacktraceSymbolizer(state); } bool LibbacktraceSymbolizer::SymbolizePC(uptr addr, SymbolizedStack *stack) { SymbolizeCodeCallbackArg data; data.first = stack; data.last = stack; data.frames_symbolized = 0; backtrace_pcinfo((backtrace_state *)state_, addr, SymbolizeCodePCInfoCallback, ErrorCallback, &data); if (data.frames_symbolized > 0) return true; backtrace_syminfo((backtrace_state *)state_, addr, SymbolizeCodeCallback, ErrorCallback, &data); return (data.frames_symbolized > 0); } bool LibbacktraceSymbolizer::SymbolizeData(uptr addr, DataInfo *info) { backtrace_syminfo((backtrace_state *)state_, addr, SymbolizeDataCallback, ErrorCallback, info); return true; } #else // SANITIZER_LIBBACKTRACE LibbacktraceSymbolizer *LibbacktraceSymbolizer::get(LowLevelAllocator *alloc) { return 0; } bool LibbacktraceSymbolizer::SymbolizePC(uptr addr, SymbolizedStack *stack) { (void)state_; return false; } bool LibbacktraceSymbolizer::SymbolizeData(uptr addr, DataInfo *info) { return false; } #endif // SANITIZER_LIBBACKTRACE static char *DemangleAlloc(const char *name, bool always_alloc) { #if SANITIZER_LIBBACKTRACE && SANITIZER_CP_DEMANGLE if (char *demangled = CplusV3Demangle(name)) return demangled; #endif if (always_alloc) return internal_strdup(name); return 0; } const char *LibbacktraceSymbolizer::Demangle(const char *name) { return DemangleAlloc(name, /*always_alloc*/ false); } } // namespace __sanitizer