diff --git a/src/signal-handling.c b/src/signal-handling.c index 75c551bcf1b38..e6edffd1f83d3 100644 --- a/src/signal-handling.c +++ b/src/signal-handling.c @@ -3,8 +3,13 @@ #include #include #include +#include #include "julia.h" #include "julia_internal.h" +#ifndef _OS_WINDOWS_ +#include +#include +#endif #ifdef __cplusplus extern "C" { @@ -88,12 +93,133 @@ JL_DLLEXPORT void jl_exit_on_sigint(int on) exit_on_sigint = on; } +static uintptr_t jl_get_pc_from_ctx(const void *_ctx); +void jl_show_sigill(void *_ctx); +static size_t jl_safe_read_mem(const volatile char *ptr, char *out, size_t len) +{ + jl_ptls_t ptls = jl_get_ptls_states(); + jl_jmp_buf *old_buf = ptls->safe_restore; + jl_jmp_buf buf; + ptls->safe_restore = &buf; + volatile size_t i = 0; + if (!jl_setjmp(buf, 0)) { + for (;i < len;i++) { + out[i] = ptr[i]; + } + } + ptls->safe_restore = old_buf; + return i; +} + #if defined(_WIN32) #include "signals-win.c" #else #include "signals-unix.c" #endif +static uintptr_t jl_get_pc_from_ctx(const void *_ctx) +{ +#if defined(_OS_LINUX_) && defined(_CPU_X86_64_) + return ((ucontext_t*)_ctx)->uc_mcontext.gregs[REG_RIP]; +#elif defined(_OS_FREEBSD_) && defined(_CPU_X86_64_) + return ((ucontext_t*)_ctx)->uc_mcontext.mc_rip; +#elif defined(_OS_LINUX_) && defined(_CPU_X86_) + return ((ucontext_t*)_ctx)->uc_mcontext.gregs[REG_EIP]; +#elif defined(_OS_FREEBSD_) && defined(_CPU_X86_) + return ((ucontext_t*)_ctx)->uc_mcontext.mc_eip; +#elif defined(_OS_DARWIN_) + return ((ucontext64_t*)_ctx)->uc_mcontext64->__ss.__rip; +#elif defined(_OS_WINDOWS_) && defined(_CPU_X86_) + return ((CONTEXT*)_ctx)->Eip; +#elif defined(_OS_WINDOWS_) && defined(_CPU_X86_64_) + return ((CONTEXT*)_ctx)->Rip; +#elif defined(_OS_LINUX_) && defined(_CPU_AARCH64_) + return ((ucontext_t*)_ctx)->uc_mcontext.pc; +#elif defined(_OS_LINUX_) && defined(_CPU_ARM_) + return ((ucontext_t*)_ctx)->uc_mcontext.arm_pc; +#else + // TODO for PPC + return 0; +#endif +} + +void jl_show_sigill(void *_ctx) +{ + char *pc = (char*)jl_get_pc_from_ctx(_ctx); + // unsupported platform + if (!pc) + return; +#if defined(_CPU_X86_64_) || defined(_CPU_X86_) + uint8_t inst[15]; // max length of x86 instruction + size_t len = jl_safe_read_mem(pc, (char*)inst, sizeof(inst)); + // ud2 + if (len >= 2 && inst[0] == 0x0f && inst[1] == 0x0b) { + jl_safe_printf("Unreachable reached at %p\n", (void*)pc); + } + else { + jl_safe_printf("Invalid instruction at %p: ", (void*)pc); + for (int i = 0;i < len;i++) { + if (i == 0) { + jl_safe_printf("0x%02" PRIx8, inst[i]); + } + else { + jl_safe_printf(", 0x%02" PRIx8, inst[i]); + } + } + jl_safe_printf("\n"); + } +#elif defined(_OS_LINUX_) && defined(_CPU_AARCH64_) + uint32_t inst = 0; + size_t len = jl_safe_read_mem(ptr, (char*)&inst, 4); + if (len < 4) + jl_safe_printf("Fault when reading instruction: %d bytes read\n". (int)len); + if (inst == 0xd4200020) { // brk #0x1 + // The signal might actually be SIGTRAP instead, doesn't hurt to handle it here though. + jl_safe_printf("Unreachable reached at %p\n", pc); + } + else { + jl_safe_printf("Invalid instruction at %p: 0x%08" PRIx32 "\n", pc, inst); + } +#elif defined(_OS_LINUX_) && defined(_CPU_ARM_) + ucontext_t *ctx = (ucontext_t*)_ctx; + if (ctx->uc_mcontext.arm_cpsr & (1 << 5)) { + // Thumb + uint16_t inst[2] = {0, 0}; + size_t len = jl_safe_read_mem(ptr, (char*)&inst, 4); + if (len < 2) + jl_safe_printf("Fault when reading Thumb instruction: %d bytes read\n". (int)len); + // LLVM and GCC uses different code for the trap... + if (inst[0] == 0xdefe || inst[0] == 0xdeff) { + // The signal might actually be SIGTRAP instead, doesn't hurt to handle it here though. + jl_safe_printf("Unreachable reached in Thumb mode at %p: 0x%04" PRIx16 "\n", + (void*)pc, inst[0]); + } + else { + jl_safe_printf("Invalid Thumb instruction at %p: 0x%04" PRIx16 ", 0x%04" PRIx16 "\n", + (void*)pc, inst[0], inst[1]); + } + } + else { + uint32_t inst = 0; + size_t len = jl_safe_read_mem(ptr, (char*)&inst, 4); + if (len < 4) + jl_safe_printf("Fault when reading instruction: %d bytes read\n". (int)len); + // LLVM and GCC uses different code for the trap... + if (inst == 0xe7ffdefe || inst == 0xe7f000f0) { + // The signal might actually be SIGTRAP instead, doesn't hurt to handle it here though. + jl_safe_printf("Unreachable reached in ARM mode at %p: 0x%08" PRIx32 "\n", + (void*)pc, inst); + } + else { + jl_safe_printf("Invalid ARM instruction at %p: 0x%08" PRIx32 "\n", (void*)pc, inst); + } + } +#else + // TODO for PPC + (void)_ctx; +#endif +} + // what to do on a critical error void jl_critical_error(int sig, bt_context_t *context, uintptr_t *bt_data, size_t *bt_size) { diff --git a/src/signals-unix.c b/src/signals-unix.c index 00beeb9babd90..9820dbc356df9 100644 --- a/src/signals-unix.c +++ b/src/signals-unix.c @@ -177,6 +177,8 @@ void sigdie_handler(int sig, siginfo_t *info, void *context) jl_ptls_t ptls = jl_get_ptls_states(); sigset_t sset; uv_tty_reset_mode(); + if (sig == SIGILL) + jl_show_sigill(context); jl_critical_error(sig, jl_to_bt_context(context), ptls->bt_data, &ptls->bt_size); sigfillset(&sset); diff --git a/src/signals-win.c b/src/signals-win.c index a54d7e5736d5c..6b056b340bd29 100644 --- a/src/signals-win.c +++ b/src/signals-win.c @@ -88,6 +88,8 @@ void __cdecl crt_sig_handler(int sig, int num) default: // SIGSEGV, (SSIGTERM, IGILL) memset(&Context, 0, sizeof(Context)); RtlCaptureContext(&Context); + if (sig == SIGILL) + jl_show_sigill(&Context); jl_critical_error(sig, &Context, ptls->bt_data, &ptls->bt_size); raise(sig); } @@ -226,6 +228,10 @@ static LONG WINAPI _exception_handler(struct _EXCEPTION_POINTERS *ExceptionInfo, return EXCEPTION_CONTINUE_EXECUTION; } } + if (ExceptionInfo->ExceptionRecord->ExceptionCode == EXCEPTION_ILLEGAL_INSTRUCTION) { + jl_safe_printf("\n"); + jl_show_sigill(ExceptionInfo->ContextRecord); + } jl_safe_printf("\nPlease submit a bug report with steps to reproduce this fault, and any error messages that follow (in their entirety). Thanks.\nException: "); switch (ExceptionInfo->ExceptionRecord->ExceptionCode) { case EXCEPTION_ACCESS_VIOLATION: