Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Show failing instruction on SIGILL #22134

Merged
merged 2 commits into from
Jun 2, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
126 changes: 126 additions & 0 deletions src/signal-handling.c
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,13 @@
#include <stdlib.h>
#include <stddef.h>
#include <stdio.h>
#include <inttypes.h>
#include "julia.h"
#include "julia_internal.h"
#ifndef _OS_WINDOWS_
#include <unistd.h>
#include <sys/mman.h>
#endif

#ifdef __cplusplus
extern "C" {
Expand Down Expand Up @@ -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(pc, (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(pc, (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(pc, (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)
{
Expand Down
14 changes: 10 additions & 4 deletions src/signals-mach.c
Original file line number Diff line number Diff line change
Expand Up @@ -141,10 +141,12 @@ static void jl_throw_in_thread(int tid, mach_port_t thread, jl_value_t *exceptio
kern_return_t ret = thread_get_state(thread, x86_THREAD_STATE64, (thread_state_t)&state, &count);
HANDLE_MACH_ERROR("thread_get_state", ret);
jl_ptls_t ptls2 = jl_all_tls_states[tid];

ptls2->bt_size = rec_backtrace_ctx(ptls2->bt_data, JL_MAX_BT_SIZE,
(bt_context_t*)&state);
ptls2->exception_in_transit = exception;
if (!ptls2->safe_restore) {
assert(exception);
ptls2->bt_size = rec_backtrace_ctx(ptls2->bt_data, JL_MAX_BT_SIZE,
(bt_context_t*)&state);
ptls2->exception_in_transit = exception;
}
jl_call_in_state(ptls2, &state, &jl_rethrow);
ret = thread_set_state(thread, x86_THREAD_STATE64,
(thread_state_t)&state, count);
Expand Down Expand Up @@ -214,6 +216,10 @@ kern_return_t catch_exception_raise(mach_port_t exception_port,
}
return KERN_SUCCESS;
}
if (ptls2->safe_restore) {
jl_throw_in_thread(tid, thread, jl_stackovf_exception);
return KERN_SUCCESS;
}
#ifdef SEGV_EXCEPTION
if (1) {
#else
Expand Down
2 changes: 2 additions & 0 deletions src/signals-unix.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
22 changes: 18 additions & 4 deletions src/signals-win.c
Original file line number Diff line number Diff line change
Expand Up @@ -86,8 +86,12 @@ void __cdecl crt_sig_handler(int sig, int num)
}
break;
default: // SIGSEGV, (SSIGTERM, IGILL)
if (ptls->safe_restore)
jl_rethrow();
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);
}
Expand All @@ -105,17 +109,19 @@ void restore_signals(void)
void jl_throw_in_ctx(jl_value_t *excpt, CONTEXT *ctxThread, int bt)
{
jl_ptls_t ptls = jl_get_ptls_states();
assert(excpt != NULL);
#if defined(_CPU_X86_64_)
DWORD64 Rsp = (ctxThread->Rsp&(DWORD64)-16) - 8;
#elif defined(_CPU_X86_)
DWORD32 Esp = (ctxThread->Esp&(DWORD32)-16) - 4;
#else
#error WIN16 not supported :P
#endif
ptls->bt_size = bt ? rec_backtrace_ctx(ptls->bt_data, JL_MAX_BT_SIZE,
ctxThread) : 0;
ptls->exception_in_transit = excpt;
if (!ptls->safe_restore) {
assert(excpt != NULL);
ptls->bt_size = bt ? rec_backtrace_ctx(ptls->bt_data, JL_MAX_BT_SIZE,
ctxThread) : 0;
ptls->exception_in_transit = excpt;
}
#if defined(_CPU_X86_64_)
*(DWORD64*)Rsp = 0;
ctxThread->Rsp = Rsp;
Expand Down Expand Up @@ -220,12 +226,20 @@ static LONG WINAPI _exception_handler(struct _EXCEPTION_POINTERS *ExceptionInfo,
}
return EXCEPTION_CONTINUE_EXECUTION;
}
if (ptls->safe_restore) {
jl_throw_in_ctx(NULL, ExceptionInfo->ContextRecord, in_ctx);
return EXCEPTION_CONTINUE_EXECUTION;
}
if (ExceptionInfo->ExceptionRecord->ExceptionInformation[0] == 1) { // writing to read-only memory (e.g. mmap)
jl_throw_in_ctx(jl_readonlymemory_exception,
ExceptionInfo->ContextRecord,in_ctx);
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:
Expand Down