diff --git a/VERSION b/VERSION index 22cacb48..3c9dc8b7 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -a12-151-g28b12c85f4+1 \ No newline at end of file +a12-153-g611216286e+1 \ No newline at end of file diff --git a/client/CMakeLists.txt b/client/CMakeLists.txt index 8023428c..be6adc6b 100644 --- a/client/CMakeLists.txt +++ b/client/CMakeLists.txt @@ -33,8 +33,9 @@ add_compile_options( "-ffunction-sections" "$<$:-O0;-g3;-ggdb>" ) -set(CMAKE_EXE_LINKER FLAGS "-Wl,--gc-sections") -set(CMAKE_C_STANDARD 11) + +set(EXE_FLAGS "-Wl,--gc-sections") +set(CMAKE_C_STANDARD 11) find_package(PkgConfig) pkg_check_modules(PKGCONFIG REQUIRED @@ -67,7 +68,8 @@ include_directories( link_libraries( ${PKGCONFIG_LIBRARIES} ${GMP_LIBRARIES} - rt m + rt + m ) set(SOURCES @@ -89,6 +91,7 @@ add_subdirectory(decoders) add_executable(looking-glass-client ${SOURCES}) target_compile_options(looking-glass-client PUBLIC ${PKGCONFIG_CFLAGS_OTHER}) target_link_libraries(looking-glass-client + ${EXE_FLAGS} lg_common spice renderers diff --git a/client/src/main.c b/client/src/main.c index dd35fe51..3496262d 100644 --- a/client/src/main.c +++ b/client/src/main.c @@ -36,6 +36,7 @@ Place, Suite 330, Boston, MA 02111-1307 USA #include #include "common/debug.h" +#include "common/crash.h" #include "common/KVMFR.h" #include "utils.h" #include "kb.h" @@ -1295,6 +1296,9 @@ int run() int main(int argc, char * argv[]) { + if (!installCrashHandler(argv[0])) + DEBUG_WARN("Failed to install the crash handler"); + if (!config_load(argc, argv)) return -1; diff --git a/common/CMakeLists.txt b/common/CMakeLists.txt index ef35fa83..47b61309 100644 --- a/common/CMakeLists.txt +++ b/common/CMakeLists.txt @@ -9,11 +9,9 @@ if(WIN32) add_library(lg_common STATIC src/crash.windows.c) else() add_library(lg_common STATIC src/crash.linux.c) + target_link_libraries(lg_common bfd) endif() -#target_link_libraries(lg_common -#) - target_include_directories(lg_common INTERFACE include diff --git a/common/include/common/crash.h b/common/include/common/crash.h index 9abc1b45..8b5871c2 100644 --- a/common/include/common/crash.h +++ b/common/include/common/crash.h @@ -19,4 +19,4 @@ Place, Suite 330, Boston, MA 02111-1307 USA #include -bool installCrashHandler(); \ No newline at end of file +bool installCrashHandler(const char * exe); \ No newline at end of file diff --git a/common/src/crash.linux.c b/common/src/crash.linux.c index 03d02056..4a8609e3 100644 --- a/common/src/crash.linux.c +++ b/common/src/crash.linux.c @@ -17,6 +17,7 @@ this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ +#define _GNU_SOURCE #include "common/crash.h" #include "common/debug.h" @@ -27,63 +28,187 @@ Place, Suite 330, Boston, MA 02111-1307 USA #include #include #include +#include +#include -/* - Large portions of this code comes from @jschmier @ https://stackoverflow.com/a/1925461/637874 -*/ +#include +#include -/* This structure mirrors the one found in /usr/include/asm/ucontext.h */ -typedef struct _sig_ucontext +struct range { - unsigned long uc_flags; - struct ucontext *uc_link; - stack_t uc_stack; - struct sigcontext uc_mcontext; - sigset_t uc_sigmask; + intptr_t start, end; +}; + +struct crash +{ + char * exe; + struct range * ranges; + int rangeCount; + bfd * fd; + asection * section; + asymbol ** syms; + long symCount; +}; + +static struct crash crash = {0}; + +static void load_symbols() +{ + bfd_init(); + crash.fd = bfd_openr(crash.exe, NULL); + if (!crash.fd) + { + DEBUG_ERROR("failed to open '%s'", crash.exe); + return; + } + + crash.fd->flags |= BFD_DECOMPRESS; + + char **matching; + if (!bfd_check_format_matches(crash.fd, bfd_object, &matching)) + { + DEBUG_ERROR("executable is not a bfd_object"); + return; + } + + crash.section = bfd_get_section_by_name(crash.fd, ".text"); + if (!crash.section) + { + DEBUG_ERROR("failed to find .text section"); + return; + } + + if ((bfd_get_file_flags(crash.fd) & HAS_SYMS) == 0) + { + DEBUG_ERROR("executable '%s' has no symbols", crash.exe); + return; + } + + long storage = bfd_get_symtab_upper_bound(crash.fd); + crash.syms = (asymbol **)malloc(storage); + crash.symCount = bfd_canonicalize_symtab(crash.fd, crash.syms); + if (crash.symCount < 0) + { + DEBUG_ERROR("failed to get the symbol count"); + return; + } +} + +static bool lookup_address(bfd_vma pc, const char ** filename, const char ** function, unsigned int * line, unsigned int * discriminator) +{ + if ((bfd_get_section_flags(crash.fd, crash.section) & SEC_ALLOC) == 0) + return false; + + bfd_size_type size = bfd_get_section_size(crash.section); + if (pc >= size) + return false; + + if (!bfd_find_nearest_line_discriminator( + crash.fd, + crash.section, + crash.syms, + pc, + filename, + function, + line, + discriminator + )) + return false; + + if (!*filename) + return false; + + return true; +} + +static void cleanup() +{ + if (crash.syms) + free(crash.syms); + + if (crash.fd) + bfd_close(crash.fd); + + if (crash.ranges) + free(crash.ranges); + + if (crash.exe) + free(crash.exe); +} + +static int dl_iterate_phdr_callback(struct dl_phdr_info * info, size_t size, void * data) +{ + // we are not a module, and as such we don't have a name + if (strlen(info->dlpi_name) != 0) + return 0; + + size_t ttl = 0; + for(int i = 0; i < info->dlpi_phnum; ++i) + { + const ElfW(Phdr) hdr = info->dlpi_phdr[i]; + if (hdr.p_type == PT_LOAD && (hdr.p_flags & PF_X) == PF_X) + ttl += hdr.p_memsz; + } + + crash.ranges = realloc(crash.ranges, sizeof(struct range) * (crash.rangeCount + 1)); + crash.ranges[crash.rangeCount].start = info->dlpi_addr; + crash.ranges[crash.rangeCount].end = info->dlpi_addr + ttl; + ++crash.rangeCount; + + return 0; } -sig_ucontext_t; static void crit_err_hdlr(int sig_num, siginfo_t * info, void * ucontext) { void * array[50]; - void * caller_address; char ** messages; int size, i; - sig_ucontext_t * uc; - uc = (sig_ucontext_t *)ucontext; + dl_iterate_phdr(dl_iterate_phdr_callback, NULL); + load_symbols(); - /* Get the address at the time the signal was raised */ - #if defined(__i386__) // gcc specific - caller_address = (void *) uc->uc_mcontext.eip; // EIP: x86 specific - #elif defined(__x86_64__) // gcc specific - caller_address = (void *) uc->uc_mcontext.rip; // RIP: x86_64 specific - #else - #error Unsupported architecture. // TODO: Add support for other arch. - #endif - - DEBUG_ERROR("signal %d (%s), address is %p from %p", sig_num, strsignal(sig_num), info->si_addr, (void *)caller_address); - - size = backtrace(array, 50); - - /* overwrite sigaction with caller's address */ - array[1] = caller_address; + DEBUG_ERROR("==== FATAL CRASH (" BUILD_VERSION ") ===="); + DEBUG_ERROR("signal %d (%s), address is %p", sig_num, strsignal(sig_num), info->si_addr); + size = backtrace(array, 50); messages = backtrace_symbols(array, size); - /* skip first stack frame (points here) */ - for (i = 1; i < size && messages != NULL; ++i) - DEBUG_ERROR("[bt]: (%d) %s", i, messages[i]); + for (i = 2; i < size && messages != NULL; ++i) + { + intptr_t base = -1; + for(int c = 0; c < crash.rangeCount; ++c) + { + if ((intptr_t)array[i] >= crash.ranges[c].start && (intptr_t)array[i] < crash.ranges[c].end) + { + base = crash.ranges[c].start + crash.section->vma; + break; + } + } + + if (base != -1) + { + const char * filename, * function; + unsigned int line, discriminator; + if (lookup_address((intptr_t)array[i] - base, &filename, &function, &line, &discriminator)) + { + DEBUG_ERROR("[trace]: (%d) %s:%u (%s)", i - 2, filename, line, function); + continue; + } + } + + DEBUG_ERROR("[trace]: (%d) %s", i - 2, messages[i]); + } free(messages); - + cleanup(); exit(EXIT_FAILURE); } -bool installCrashHandler() +bool installCrashHandler(const char * exe) { struct sigaction sigact; + crash.exe = realpath(exe, NULL); sigact.sa_sigaction = crit_err_hdlr; sigact.sa_flags = SA_RESTART | SA_SIGINFO; diff --git a/common/src/crash.windows.c b/common/src/crash.windows.c index 7d04dc18..9c6d5985 100644 --- a/common/src/crash.windows.c +++ b/common/src/crash.windows.c @@ -19,7 +19,7 @@ Place, Suite 330, Boston, MA 02111-1307 USA #include "common/crash.h" -bool installCrashHandler() +bool installCrashHandler(const char * exe) { //TODO return true;