Skip to content

Commit

Permalink
Show failing instruction on SIGILL
Browse files Browse the repository at this point in the history
  • Loading branch information
yuyichao committed May 30, 2017
1 parent 9b9c93b commit f8f391c
Show file tree
Hide file tree
Showing 3 changed files with 126 additions and 0 deletions.
118 changes: 118 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,125 @@ JL_DLLEXPORT void jl_exit_on_sigint(int on)
exit_on_sigint = on;
}

void jl_show_sigill(void *_ctx);

#if defined(_WIN32)
#include "signals-win.c"
#else
#include "signals-unix.c"
#endif

void jl_show_sigill(void *_ctx)
{
#if defined(_CPU_X86_64_) || defined(_CPU_X86_)
uintptr_t pc = 0;
# if defined(_OS_LINUX_) && defined(_CPU_X86_64_)
pc = ((ucontext_t*)_ctx)->uc_mcontext.gregs[REG_RIP];
# elif defined(_OS_FREEBSD_) && defined(_CPU_X86_64_)
pc = ((ucontext_t*)_ctx)->uc_mcontext.mc_rip;
# elif defined(_OS_LINUX_) && defined(_CPU_X86_)
pc = ((ucontext_t*)_ctx)->uc_mcontext.gregs[REG_EIP];
# elif defined(_OS_FREEBSD_) && defined(_CPU_X86_)
pc = ((ucontext_t*)_ctx)->uc_mcontext.mc_eip;
# elif defined(_OS_DARWIN_)
pc = ((ucontext64_t*)_ctx)->uc_mcontext64->__ss.__rip;
# elif defined(_OS_WINDOWS_) && defined(_CPU_X86_)
pc = ((CONTEXT*)_ctx)->Eip;
# elif defined(_OS_WINDOWS_) && defined(_CPU_X86_64_)
pc = ((CONTEXT*)_ctx)->Rip;
# endif
// unsupported platform
if (!pc)
return;
uint8_t inst[15]; // max length of x86 instruction
int len = sizeof(inst);
// since we got a SIGILL and not a SIGSEGV or SIGBUS assume that
// the `pc` is pointed to valid memory.
// However, this does not mean that `pc + 14` is valid memory.
uintptr_t page_start = pc / jl_page_size * jl_page_size;
uintptr_t next_page = page_start + jl_page_size;
if (next_page < pc + len) {
int valid = 0;
// The max instruction length crosses page boundary.
// Check if it's safe to read the next page
#ifdef _OS_WINDOWS_
MEMORY_BASIC_INFORMATION mbi;
if (VirtualQuery((void*)next_page, &mbi, sizeof(mbi))) {
if (mbi.State == MEM_COMMIT) {
// If the address is not both executable and readable,
// it'll not hold any part of the instruction.
if (mbi.Protect == PAGE_EXECUTE || mbi.Protect == PAGE_EXECUTE_READ ||
mbi.Protect == PAGE_EXECUTE_READWRITE ||
mbi.Protect == PAGE_EXECUTE_WRITECOPY) {
valid = 1;
}
}
}
#else
# if defined(_OS_FREEBSD_) || defined(_OS_DARWIN_)
char mvec;
# else
unsigned char mvec;
# endif
valid = mincore((void*)next_page, jl_page_size, &mvec) != -1;
#endif
if (!valid) {
len = next_page - pc;
}
}
memcpy(inst, (const void*)pc, len);
// 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_)
// trap does not raise SIGILL on AArch64
uintptr_t pc = ((ucontext_t*)_ctx)->uc_mcontext.pc;
uint32_t inst = *(uint32_t*)pc;
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", (void*)pc);
}
else {
jl_safe_printf("Invalid instruction at %p: 0x%08" PRIx32 "\n", (void*)pc, inst);
}
#elif defined(_OS_LINUX_) && defined(_CPU_ARM_)
ucontext_t *ctx = (ucontext_t*)_ctx;
uintptr_t pc = ctx->uc_mcontext.arm_pc;
if (ctx->uc_mcontext.arm_cpsr & (1 << 5)) {
// Thumb
// Too lazy to check for the validity of the address of the second `uint16_t` for now.
jl_safe_printf("Invalid Thumb instruction at %p: 0x%04" PRIx16 ", 0x%04" PRIx16 "\n",
(void*)pc, ((uint16_t*)pc)[0], ((uint16_t*)pc)[1]);
}
else {
uint32_t inst = *(uint32_t*)pc;
if (inst == 0xe7ffdefe) { // trap
// 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\n", (void*)pc);
}
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
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
6 changes: 6 additions & 0 deletions src/signals-win.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
Expand Down Expand Up @@ -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:
Expand Down

0 comments on commit f8f391c

Please sign in to comment.