diff --git a/config.lib b/config.lib index a9ceaf6a82..a1f5ac6a08 100644 --- a/config.lib +++ b/config.lib @@ -1571,22 +1571,6 @@ EOL fi rm tmp.config.libdl - "$cc_host" $CFLAGS $LDFLAGS -o tmp.config.demangle -x c++ - -lstdc++ 2> /dev/null << EOL - #include - int main() { - int status = -1; - char *demangled = abi::__cxa_demangle("test", 0, 0, &status); - return 0; - } -EOL - if [ $? -ne 0 ]; then - log 1 "checking abi::__cxa_demangle... no" - else - 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 @@ -1629,10 +1613,53 @@ EOL else log 1 "checking dbghelp... found" CFLAGS="$CFLAGS -DWITH_DBGHELP" + + "$cc_host" $CFLAGS $LDFLAGS -o tmp.config.bfd -x c++ - -lbfd -liberty -lintl 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 -liberty -lintl" + CFLAGS="$CFLAGS -DWITH_BFD" + + if [ $enable_debug -lt 1 ]; then + CFLAGS="$CFLAGS -g1" + fi + fi + rm -f tmp.config.bfd fi rm -f tmp.config.dbghelp fi + if [ "$os" != "CYGWIN" ] && [ "$os" != "HAIKU" ] && [ "$os" != "MORPHOS" ] && [ "$os" != "OSX" ] && [ "$os" != "DOS" ] && [ "$os" != "WINCE" ] && [ "$os" != "PSP" ] && [ "$os" != "OS2" ]; then + "$cc_host" $CFLAGS $LDFLAGS -o tmp.config.demangle -x c++ - -lstdc++ 2> /dev/null << EOL + #include + int main() { + int status = -1; + char *demangled = abi::__cxa_demangle("test", 0, 0, &status); + return 0; + } +EOL + if [ $? -ne 0 ]; then + log 1 "checking abi::__cxa_demangle... no" + else + log 1 "checking abi::__cxa_demangle... found" + CFLAGS="$CFLAGS -DWITH_DEMANGLE" + fi + rm tmp.config.demangle + fi + if [ "$os" != "CYGWIN" ] && [ "$os" != "HAIKU" ] && [ "$os" != "MINGW" ] && [ "$os" != "DOS" ] && [ "$os" != "WINCE" ]; then LIBS="$LIBS -lc" fi diff --git a/source.list b/source.list index df35cdd26e..2277a22139 100644 --- a/source.list +++ b/source.list @@ -165,6 +165,7 @@ console_internal.h console_type.h cpu.h crashlog.h +crashlog_bfd.h currency.h date_func.h date_gui.h diff --git a/src/crashlog.cpp b/src/crashlog.cpp index 7ddc2131f9..19a5898623 100644 --- a/src/crashlog.cpp +++ b/src/crashlog.cpp @@ -11,6 +11,7 @@ #include "stdafx.h" #include "crashlog.h" +#include "crashlog_bfd.h" #include "gamelog.h" #include "date_func.h" #include "map_func.h" @@ -493,3 +494,72 @@ bool CrashLog::MakeCrashLog() const if (SoundDriver::GetInstance() != NULL) SoundDriver::GetInstance()->Stop(); if (VideoDriver::GetInstance() != NULL) VideoDriver::GetInstance()->Stop(); } + +#if defined(WITH_BFD) +sym_info_bfd::sym_info_bfd(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) {} + +sym_info_bfd::~sym_info_bfd() +{ + free(syms); + if (abfd != NULL) bfd_close(abfd); +} + +static void find_address_in_section(bfd *abfd, asection *section, void *data) +{ + sym_info_bfd *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 && info->function_name) { + 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; + } + } + } else if (info->found) { + bfd_vma target = info->addr - vma; + bfd_vma best_diff = size; + for (long i = 0; i < info->sym_count; i++) { + asymbol *sym = info->syms[i]; + if (!(sym->flags & (BSF_LOCAL | BSF_GLOBAL))) continue; + if (sym->value > target) continue; + bfd_vma diff = target - sym->value; + if (diff < best_diff) { + best_diff = diff; + info->function_name = sym->name; + info->function_addr = sym->value + vma; + } + } + } +} + +void lookup_addr_bfd(const char *obj_file_name, sym_info_bfd &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 diff --git a/src/crashlog_bfd.h b/src/crashlog_bfd.h new file mode 100644 index 0000000000..b9af007b4f --- /dev/null +++ b/src/crashlog_bfd.h @@ -0,0 +1,45 @@ +/* $Id$ */ + +/* + * This file is part of OpenTTD. + * OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2. + * OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see . + */ + +/** @file crashlog_bfd.h Definitions for utility functions for using libbfd as part of logging a crash */ + +#ifndef CRASHLOG_BFD_H +#define CRASHLOG_BFD_H + +#if defined(WITH_BFD) +/* this is because newer versions of libbfd insist on seeing these, even though they aren't used for anything */ +#define PACKAGE 1 +#define PACKAGE_VERSION 1 +#include +#undef PACKAGE +#undef PACKAGE_VERSION +#endif + +#if defined(WITH_BFD) +struct sym_info_bfd; +void lookup_addr_bfd(const char *obj_file_name, sym_info_bfd &info); + +struct sym_info_bfd { + 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; + + sym_info_bfd(bfd_vma addr_); + ~sym_info_bfd(); +}; + +#endif + +#endif /* CRASHLOG_BFD_H */ diff --git a/src/os/unix/crashlog_unix.cpp b/src/os/unix/crashlog_unix.cpp index 803433427e..1e1354c003 100644 --- a/src/os/unix/crashlog_unix.cpp +++ b/src/os/unix/crashlog_unix.cpp @@ -11,6 +11,7 @@ #include "../../stdafx.h" #include "../../crashlog.h" +#include "../../crashlog_bfd.h" #include "../../string_func.h" #include "../../gamelog.h" #include "../../saveload/saveload.h" @@ -42,73 +43,6 @@ #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. */ @@ -202,7 +136,7 @@ class CrashLogUnix : public CrashLog { 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); + sym_info_bfd 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; diff --git a/src/os/windows/crashlog_win.cpp b/src/os/windows/crashlog_win.cpp index 484e4ee3fc..461c0d27a5 100644 --- a/src/os/windows/crashlog_win.cpp +++ b/src/os/windows/crashlog_win.cpp @@ -11,6 +11,7 @@ #include "../../stdafx.h" #include "../../crashlog.h" +#include "../../crashlog_bfd.h" #include "win32.h" #include "../../core/alloc_func.hpp" #include "../../core/math_func.hpp" @@ -20,6 +21,9 @@ #include "../../gamelog.h" #include "../../saveload/saveload.h" #include "../../video/video_driver.hpp" +#if defined(WITH_DEMANGLE) +#include +#endif #include #include @@ -413,11 +417,13 @@ char *CrashLogWindows::AppendDecodedStacktrace(char *buffer, const char *last) c /* Get module name. */ const char *mod_name = "???"; + const char *image_name = NULL; IMAGEHLP_MODULE64 module; module.SizeOfStruct = sizeof(module); if (proc.pSymGetModuleInfo64(hCur, frame.AddrPC.Offset, &module)) { mod_name = module.ModuleName; + image_name = module.ImageName; } /* Print module and instruction pointer. */ @@ -434,6 +440,35 @@ char *CrashLogWindows::AppendDecodedStacktrace(char *buffer, const char *last) c if (proc.pSymGetLineFromAddr64(hCur, frame.AddrPC.Offset, &line_offs, &line)) { buffer += seprintf(buffer, last, " (%s:%d)", line.FileName, line.LineNumber); } + } else if (image_name != NULL) { +#if defined (WITH_BFD) + /* subtract one to get the line before the return address, i.e. the function call line */ + sym_info_bfd bfd_info(reinterpret_cast(frame.AddrPC.Offset) - 1); + lookup_addr_bfd(image_name, bfd_info); + if (bfd_info.function_name != NULL) { + const char *func_name = bfd_info.function_name; +#if defined(WITH_DEMANGLE) + int status = -1; + char *demangled = abi::__cxa_demangle(func_name, NULL, 0, &status); + if (demangled != NULL && status == 0) { + func_name = demangled; + } +#endif + bool symbol_ok = strncmp(func_name, ".rdata$", 7) != 0 && strncmp(func_name, ".debug_loc", 10) != 0; + if (symbol_ok) { + buffer += seprintf(buffer, last, " %s", func_name); + } +#if defined(WITH_DEMANGLE) + free(demangled); +#endif + if (symbol_ok && bfd_info.function_addr) { + buffer += seprintf(buffer, last, " + %I64u", reinterpret_cast(frame.AddrPC.Offset) - bfd_info.function_addr); + } + } + if (bfd_info.file_name != NULL) { + buffer += seprintf(buffer, last, " (%s:%d)", bfd_info.file_name, bfd_info.line); + } +#endif } buffer += seprintf(buffer, last, "\n"); }