diff --git a/config.lib b/config.lib index 5c7c994267..2d3e437ca5 100644 --- a/config.lib +++ b/config.lib @@ -1555,7 +1555,7 @@ make_cflags_and_ldflags() { LIBS="$LIBS -lpthread" fi if [ "$os" != "CYGWIN" ] && [ "$os" != "HAIKU" ] && [ "$os" != "MINGW" ] && [ "$os" != "MORPHOS" ] && [ "$os" != "OSX" ] && [ "$os" != "DOS" ] && [ "$os" != "WINCE" ] && [ "$os" != "PSP" ] && [ "$os" != "OS2" ]; then - "$cc_host" -o /dev/null -x c++ - -ldl 2> /dev/null << EOL + "$cc_host" $CFLAGS $LDFLAGS -o tmp.config.libdl -x c++ - -ldl 2> /dev/null << EOL #include int main() { Dl_info info; @@ -1569,8 +1569,9 @@ EOL LIBS="$LIBS -ldl" CFLAGS="$CFLAGS -DWITH_DL" fi + rm tmp.config.libdl - "$cc_host" -o /dev/null -x c++ - -lstdc++ 2> /dev/null << EOL + "$cc_host" $CFLAGS $LDFLAGS -o tmp.config.demangle -x c++ - -lstdc++ 2> /dev/null << EOL #include int main() { int status = -1; @@ -1584,6 +1585,28 @@ EOL log 1 "checking abi::__cxa_demangle... found" CFLAGS="$CFLAGS -DWITH_DEMANGLE" fi + rm tmp.config.demangle + + "$cc_host" $CFLAGS $LDFLAGS -o tmp.config.bfd -x c++ - -lbfd 2> /dev/null << EOL + #define PACKAGE 1 + #define PACKAGE_VERSION 1 + #include + int main() { + bfd_init(); + unsigned int size; + asymbol *syms = 0; + long symcount = bfd_read_minisymbols((bfd *) 0, false, (void**) &syms, &size); + return 0; + } +EOL + if [ $? -ne 0 ]; then + log 1 "checking libbfd... no" + else + log 1 "checking libbfd... found" + LIBS="$LIBS -lbfd" + CFLAGS="$CFLAGS -DWITH_BFD" + fi + rm tmp.config.bfd fi if [ "$os" != "CYGWIN" ] && [ "$os" != "HAIKU" ] && [ "$os" != "MINGW" ] && [ "$os" != "DOS" ] && [ "$os" != "WINCE" ]; then diff --git a/src/os/unix/crashlog_unix.cpp b/src/os/unix/crashlog_unix.cpp index 1bcbe63fde..803433427e 100644 --- a/src/os/unix/crashlog_unix.cpp +++ b/src/os/unix/crashlog_unix.cpp @@ -28,6 +28,9 @@ #if defined(WITH_DEMANGLE) # include #endif +#if defined(WITH_BFD) +# include +#endif #elif defined(SUNOS) # include # include @@ -39,6 +42,73 @@ #include "../../safeguards.h" +#if defined(WITH_BFD) +struct line_info { + bfd_vma addr; + bfd *abfd; + asymbol **syms; + long sym_count; + const char *file_name; + const char *function_name; + bfd_vma function_addr; + unsigned int line; + bool found; + + line_info(bfd_vma addr_) : addr(addr_), abfd(NULL), syms(NULL), sym_count(0), + file_name(NULL), function_name(NULL), function_addr(0), line(0), found(false) {} + + ~line_info() + { + free(syms); + if (abfd != NULL) bfd_close(abfd); + } +}; + +static void find_address_in_section(bfd *abfd, asection *section, void *data) +{ + line_info *info = static_cast(data); + if (info->found) return; + + if ((bfd_get_section_flags(abfd, section) & SEC_ALLOC) == 0) return; + + bfd_vma vma = bfd_get_section_vma(abfd, section); + if (info->addr < vma) return; + + bfd_size_type size = bfd_section_size(abfd, section); + if (info->addr >= vma + size) return; + + info->found = bfd_find_nearest_line(abfd, section, info->syms, info->addr - vma, + &(info->file_name), &(info->function_name), &(info->line)); + + if (info->found) { + for (long i = 0; i < info->sym_count; i++) { + asymbol *sym = info->syms[i]; + if (sym->flags & (BSF_LOCAL | BSF_GLOBAL) && strcmp(sym->name, info->function_name) == 0) { + info->function_addr = sym->value + vma; + } + } + } +} + +void lookup_addr_bfd(const char *obj_file_name, line_info &info) +{ + info.abfd = bfd_openr(obj_file_name, NULL); + + if (info.abfd == NULL) return; + + if (!bfd_check_format(info.abfd, bfd_object) || (bfd_get_file_flags(info.abfd) & HAS_SYMS) == 0) return; + + unsigned int size; + info.sym_count = bfd_read_minisymbols(info.abfd, false, (void**) &(info.syms), &size); + if (info.sym_count <= 0) { + info.sym_count = bfd_read_minisymbols(info.abfd, true, (void**) &(info.syms), &size); + } + if (info.sym_count <= 0) return; + + bfd_map_over_sections(info.abfd, find_address_in_section, &info); +} +#endif + /** * Unix implementation for the crash logger. */ @@ -115,6 +185,9 @@ class CrashLogUnix : public CrashLog { { buffer += seprintf(buffer, last, "Stacktrace:\n"); #if defined(__GLIBC__) +#if defined(WITH_BFD) + bfd_init(); +#endif void *trace[64]; int trace_size = backtrace(trace, lengthof(trace)); @@ -123,22 +196,43 @@ class CrashLogUnix : public CrashLog { #if defined(WITH_DL) Dl_info info; int dladdr_result = dladdr(trace[i], &info); - if (dladdr_result && info.dli_sname) { + const char *func_name = info.dli_sname; + void *func_addr = info.dli_saddr; + const char *file_name = NULL; + unsigned int line_num = 0; +#if defined(WITH_BFD) + /* subtract one to get the line before the return address, i.e. the function call line */ + line_info bfd_info(reinterpret_cast(trace[i]) - 1); + if (dladdr_result && info.dli_fname) { + lookup_addr_bfd(info.dli_fname, bfd_info); + if (bfd_info.file_name != NULL) file_name = bfd_info.file_name; + if (bfd_info.function_name != NULL) func_name = bfd_info.function_name; + if (bfd_info.function_addr != 0) func_addr = reinterpret_cast(bfd_info.function_addr); + line_num = bfd_info.line; + } +#endif + bool ok = true; + const int ptr_str_size = (2 + sizeof(void*) * 2); + if (dladdr_result && func_name) { int status = -1; char *demangled = NULL; #if defined(WITH_DEMANGLE) - demangled = abi::__cxa_demangle(info.dli_sname, NULL, 0, &status); + demangled = abi::__cxa_demangle(func_name, NULL, 0, &status); #endif - const char *name = (demangled != NULL && status == 0) ? demangled : info.dli_sname; - buffer += seprintf(buffer, last, " [%02i] %*p %-40s %s + 0x%zx\n", i, int(2 + sizeof(void*) * 2), - trace[i], info.dli_fname, name, (char *)trace[i] - (char *)info.dli_saddr); + const char *name = (demangled != NULL && status == 0) ? demangled : func_name; + buffer += seprintf(buffer, last, " [%02i] %*p %-40s %s + 0x%zx\n", i, ptr_str_size, + trace[i], info.dli_fname, name, (char *)trace[i] - (char *)func_addr); free(demangled); - continue; } else if (dladdr_result && info.dli_fname) { - buffer += seprintf(buffer, last, " [%02i] %*p %-40s + 0x%zx\n", i, int(2 + sizeof(void*) * 2), + buffer += seprintf(buffer, last, " [%02i] %*p %-40s + 0x%zx\n", i, ptr_str_size, trace[i], info.dli_fname, (char *)trace[i] - (char *)info.dli_fbase); - continue; + } else { + ok = false; } + if (file_name != NULL) { + buffer += seprintf(buffer, last, "%*s%s:%u\n", 7 + ptr_str_size, "", file_name, line_num); + } + if (ok) continue; #endif buffer += seprintf(buffer, last, " [%02i] %s\n", i, messages[i]); }