diff --git a/make/vm/port_ch.xml b/make/vm/port_ch.xml index a238d89..18197d2 100644 --- a/make/vm/port_ch.xml +++ b/make/vm/port_ch.xml @@ -120,6 +120,7 @@ + diff --git a/vm/port/src/crash_handler/port_crash_handler.cpp b/vm/port/src/crash_handler/port_crash_handler.cpp index b699310..9431b0c 100644 --- a/vm/port/src/crash_handler/port_crash_handler.cpp +++ b/vm/port/src/crash_handler/port_crash_handler.cpp @@ -43,7 +43,7 @@ struct crash_additional_actions static crash_additional_actions *crash_actions = NULL; -static unsigned crash_output_flags; +static unsigned crash_output_flags = 0; static osmutex_t g_mutex; @@ -74,6 +74,15 @@ Boolean port_init_crash_handler( g_unwind_callback = unwind_callback; + // Set default flags + port_crash_handler_set_flags( + PORT_CRASH_DUMP_TO_STDERR | + PORT_CRASH_STACK_DUMP | + PORT_CRASH_PRINT_COMMAND_LINE | + PORT_CRASH_PRINT_ENVIRONMENT | + PORT_CRASH_PRINT_MODULES | + PORT_CRASH_PRINT_REGISTERS); + return TRUE; } @@ -90,12 +99,16 @@ unsigned port_crash_handler_get_capabilities() PORT_CRASH_PRINT_MODULES | PORT_CRASH_PRINT_REGISTERS | PORT_CRASH_DUMP_PROCESS_CORE); - } void port_crash_handler_set_flags(unsigned flags) { - crash_output_flags = flags; + unsigned new_flags = flags & port_crash_handler_get_capabilities(); + + sig_process_crash_flags_change(new_flags & ~crash_output_flags, + crash_output_flags & ~new_flags); + + crash_output_flags = new_flags; } unsigned port_crash_handler_get_flags() diff --git a/vm/port/src/crash_handler/stack_dump.cpp b/vm/port/src/crash_handler/stack_dump.cpp index 6a30e36..08e7ff1 100644 --- a/vm/port/src/crash_handler/stack_dump.cpp +++ b/vm/port/src/crash_handler/stack_dump.cpp @@ -117,7 +117,7 @@ static void sd_print_stack(Registers* regs, port_unwind_compiled_frame unwind) { if (!regs) { - fprintf(stderr, "No stack trace due to registers info absence\n"); + fprintf(stderr, "\nNo stack trace due to registers info absence\n"); return; } diff --git a/vm/port/src/signals/include/signals_internal.h b/vm/port/src/signals/include/signals_internal.h index 5196056..f37601b 100644 --- a/vm/port/src/signals/include/signals_internal.h +++ b/vm/port/src/signals/include/signals_internal.h @@ -36,10 +36,8 @@ typedef struct void* restart_address; #endif /* UNIX */ -#ifdef WIN32 - Boolean assert_dialog; -#endif - + /* Flag to indicate that debugger should be attached right in OS handler */ + Boolean debugger; /* Flag to produce minidump/core on the second exception catch */ Boolean produce_core; @@ -129,13 +127,15 @@ void port_set_longjump_regs(void* fn, Registers* regs, int num, ...); */ void port_transfer_to_function(void* fn, Registers* regs, int num, ...); +/* The function called to adjust signals processing upon flags change */ +void sig_process_crash_flags_change(unsigned added, unsigned removed); #define INSTRUMENTATION_BYTE_HLT 0xf4 // HLT instruction #define INSTRUMENTATION_BYTE_CLI 0xfa // CLI instruction #define INSTRUMENTATION_BYTE_INT3 0xcc // INT 3 instruction -#ifdef WINNT +#ifdef WIN32 #define INSTRUMENTATION_BYTE INSTRUMENTATION_BYTE_CLI #else #define INSTRUMENTATION_BYTE INSTRUMENTATION_BYTE_INT3 @@ -153,6 +153,8 @@ LONG NTAPI vectored_exception_handler(LPEXCEPTION_POINTERS nt_exception); /* Internal exception handler */ LONG NTAPI vectored_exception_handler_internal(LPEXCEPTION_POINTERS nt_exception); +void __cdecl port_win_dbg_break(void); + #else /* UNIX */ // diff --git a/vm/port/src/signals/linux/signals_common.cpp b/vm/port/src/signals/linux/signals_common.cpp index 2669de2..999a871 100644 --- a/vm/port/src/signals/linux/signals_common.cpp +++ b/vm/port/src/signals/linux/signals_common.cpp @@ -23,6 +23,7 @@ #include "open/platform_types.h" #include "port_crash_handler.h" +#include "port_malloc.h" #include "stack_dump.h" #include "../linux/include/gdb_crash_handler.h" #include "signals_internal.h" @@ -77,18 +78,8 @@ static void clear_stack_protection(Registers* regs, void* fault_addr) } static void c_handler(Registers* pregs, size_t signum, void* fault_addr) -{ // this exception handler is executed *after* VEH handler returned +{ // this exception handler is executed *after* OS signal handler returned int result; - Registers regs = *pregs; - - // Check if SIGSEGV is produced by port_read/write_memory - port_tls_data* tlsdata = get_private_tls_data(); - if (tlsdata && tlsdata->violation_flag) - { - tlsdata->violation_flag = 0; - pregs->set_ip(tlsdata->restart_address); - return; - } switch ((int)signum) { @@ -109,6 +100,9 @@ static void c_handler(Registers* pregs, size_t signum, void* fault_addr) case SIGQUIT: result = port_process_signal(PORT_SIGNAL_QUIT, pregs, fault_addr, FALSE); break; + case SIGABRT: + result = port_process_signal(PORT_SIGNAL_ABORT, NULL, fault_addr, FALSE); + break; default: result = port_process_signal(PORT_SIGNAL_UNKNOWN, pregs, fault_addr, TRUE); } @@ -117,10 +111,18 @@ static void c_handler(Registers* pregs, size_t signum, void* fault_addr) return; // We've got a crash - if (result > 0) - { // result > 0 - invoke debugger - bool result = gdb_crash_handler(®s); - // Continue with exiting process if not sucessful... + if (result > 0) // invoke debugger + { // Prepare second catch of signal to attach GDB from signal handler + port_tls_data* tlsdata = get_private_tls_data(); + if (!tlsdata) + { // STD_MALLOC can be harmful here + tlsdata = (port_tls_data*)STD_MALLOC(sizeof(port_tls_data)); + memset(tlsdata, 0, sizeof(port_tls_data)); + set_private_tls_data(tlsdata); + } + + tlsdata->debugger = TRUE; + return; // To produce signal again } // result < 0 - exit process @@ -129,6 +131,7 @@ static void c_handler(Registers* pregs, size_t signum, void* fault_addr) signal(signum, SIG_DFL); // setup default handler return; } + // No core needed - simply terminate _exit(-1); } @@ -136,16 +139,45 @@ static void c_handler(Registers* pregs, size_t signum, void* fault_addr) static void general_signal_handler(int signum, siginfo_t* info, void* context) { Registers regs; + + if (!context) + return; + // Convert OS context to Registers port_thread_context_to_regs(®s, (ucontext_t*)context); + // Check if SIGSEGV is produced by port_read/write_memory + port_tls_data* tlsdata = get_private_tls_data(); + if (tlsdata && tlsdata->violation_flag) + { + tlsdata->violation_flag = 0; + regs.set_ip(tlsdata->restart_address); + return; + } + + if (tlsdata && tlsdata->debugger) + { + bool result = gdb_crash_handler(®s); + _exit(-1); // Exit process if not sucessful... + } + + if (signum == SIGABRT && // SIGABRT can't be trown again from c_handler + (port_crash_handler_get_flags() & PORT_CRASH_CALL_DEBUGGER) != 0) + { // So attaching GDB right here + bool result = gdb_crash_handler(®s); + _exit(-1); // Exit process if not sucessful... + } + if (signum == SIGSEGV) clear_stack_protection(®s, info->si_addr); // Prepare registers for transfering control out of signal handler void* callback = (void*)&c_handler; + void* fault_addr = info ? info->si_addr : NULL; + port_set_longjump_regs(callback, ®s, 3, - ®s, (void*)(size_t)signum, info->si_addr); + ®s, (void*)(size_t)signum, fault_addr); + // Convert prepared Registers back to OS context port_thread_regs_to_context((ucontext_t*)context, ®s); // Return from signal handler to go to C handler @@ -218,3 +250,8 @@ int shutdown_signals() restore_signals(); return 0; } //shutdown_signals + +void sig_process_crash_flags_change(unsigned added, unsigned removed) +{ +// Still empty on Linux +} diff --git a/vm/port/src/signals/linux/signals_ipf.cpp b/vm/port/src/signals/linux/signals_ipf.cpp index 5a6e29c..69cbcac 100644 --- a/vm/port/src/signals/linux/signals_ipf.cpp +++ b/vm/port/src/signals/linux/signals_ipf.cpp @@ -62,7 +62,7 @@ static void general_signal_handler(int signum, siginfo_t* info, void* context) // Convert OS context to Registers port_thread_context_to_regs(®s, (ucontext_t*)context); - void* fault_addr = info->si_addr; + void* fault_addr = info ? info->si_addr : NULL; int result; switch ((int)signum) @@ -84,6 +84,9 @@ static void general_signal_handler(int signum, siginfo_t* info, void* context) case SIGQUIT: result = port_process_signal(PORT_SIGNAL_QUIT, ®s, fault_addr, FALSE); break; + case SIGABRT: + result = port_process_signal(PORT_SIGNAL_ABORT, NULL, fault_addr, FALSE); + break; default: result = port_process_signal(PORT_SIGNAL_UNKNOWN, ®s, fault_addr, TRUE); } @@ -178,6 +181,11 @@ int shutdown_signals() { return 0; } //shutdown_signals +void sig_process_crash_flags_change(unsigned added, unsigned removed) +{ +// Still empty on Linux +} + #if 0 // Variables used to locate the context from the signal handler diff --git a/vm/port/src/signals/win/signals_asm_em64t.asm b/vm/port/src/signals/win/signals_asm_em64t.asm index d967caf..df3acff 100644 --- a/vm/port/src/signals/win/signals_asm_em64t.asm +++ b/vm/port/src/signals/win/signals_asm_em64t.asm @@ -25,6 +25,15 @@ vectored_exception_handler PROC vectored_exception_handler ENDP +PUBLIC port_win_dbg_break + +port_win_dbg_break PROC +;void __declspec(naked) __cdecl port_win_dbg_break() + int 3 + ret +port_win_dbg_break ENDP + + ; struct Registers { ; uint64 rsp; ; 00h ; uint64 rbp; ; 08h diff --git a/vm/port/src/signals/win/signals_common.cpp b/vm/port/src/signals/win/signals_common.cpp index 951ce29..8a5ab26 100644 --- a/vm/port/src/signals/win/signals_common.cpp +++ b/vm/port/src/signals/win/signals_common.cpp @@ -17,9 +17,12 @@ #include +#include +#include #include "open/platform_types.h" #include "open/hythread_ext.h" #include "port_malloc.h" +#include "port_mutex.h" #include "port_crash_handler.h" #include "stack_dump.h" @@ -37,6 +40,19 @@ port_tls_key_t port_tls_key = TLS_OUT_OF_INDEXES; +typedef void (__cdecl *sigh_t)(int); // Signal handler type + +static PVOID veh = NULL; +static sigh_t prev_sig = (sigh_t)SIG_ERR; +// Mutex to protect access to the global data +static osmutex_t g_mutex; +// The global data protected by the mutex +static int report_modes[4]; +static _HFILE report_files[3]; +//-------- +static bool asserts_disabled = false; + + int init_private_tls_data() { DWORD key = TlsAlloc(); @@ -70,6 +86,10 @@ static void c_handler(Registers* pregs, result = port_process_signal(PORT_SIGNAL_GPF, pregs, fault_addr, iscrash); break; case STATUS_INTEGER_DIVIDE_BY_ZERO: + case EXCEPTION_FLT_DIVIDE_BY_ZERO: + case EXCEPTION_FLT_OVERFLOW: + case EXCEPTION_FLT_UNDERFLOW: + case EXCEPTION_INT_OVERFLOW: result = port_process_signal(PORT_SIGNAL_ARITHMETIC, pregs, fault_addr, iscrash); break; case JVMTI_EXCEPTION_STATUS: @@ -100,12 +120,12 @@ static void c_handler(Registers* pregs, if ((port_crash_handler_get_flags() & PORT_CRASH_DUMP_PROCESS_CORE) != 0) tlsdata->produce_core = TRUE; if (result > 0) - tlsdata->assert_dialog = TRUE; + tlsdata->debugger = TRUE; return; // To produce exception again } - ExitProcess((UINT)-1); + _exit(-1); } void prepare_assert_dialog(Registers* regs) @@ -128,11 +148,11 @@ LONG NTAPI vectored_exception_handler_internal(LPEXCEPTION_POINTERS nt_exception { tlsdata->produce_core = FALSE; create_minidump(nt_exception); - if (!tlsdata->assert_dialog) - ExitProcess((UINT)-1); + if (!tlsdata->debugger) + _exit(-1); } - if (tlsdata->assert_dialog) + if (tlsdata->debugger) { // Go to handler to restore CRT/VEH settings and crash once again port_set_longjump_regs(&prepare_assert_dialog, ®s, 1, ®s); @@ -145,11 +165,8 @@ LONG NTAPI vectored_exception_handler_internal(LPEXCEPTION_POINTERS nt_exception { case STATUS_STACK_OVERFLOW: case STATUS_ACCESS_VIOLATION: - case STATUS_INTEGER_DIVIDE_BY_ZERO: case JVMTI_EXCEPTION_STATUS: - case EXCEPTION_DATATYPE_MISALIGNMENT: - case EXCEPTION_ILLEGAL_INSTRUCTION: - case EXCEPTION_PRIV_INSTRUCTION: + case STATUS_INTEGER_DIVIDE_BY_ZERO: case EXCEPTION_FLT_DIVIDE_BY_ZERO: case EXCEPTION_FLT_OVERFLOW: case EXCEPTION_FLT_UNDERFLOW: @@ -197,8 +214,6 @@ BOOL ctrl_handler(DWORD ctrlType) return FALSE; } -static int report_modes[4]; -static _HFILE report_files[3]; static void disable_assert_dialogs() { @@ -226,10 +241,74 @@ static void restore_assert_dialogs() #endif // _DEBUG } -static PVOID veh = NULL; +static void show_debugger_dialog() +{ + int result = MessageBox(NULL, + "ABORT handler has requested to call the debugger\n\n" + "Press Retry to attach to the debugger\n" + "Press Cancel to terminate the application", + "Crash Handler", + MB_RETRYCANCEL | MB_ICONHAND | MB_SETFOREGROUND | MB_TASKMODAL); + + if (result == IDCANCEL) + { + _exit(3); + return; + } + + port_win_dbg_break(); // Call the debugger +} + +static void __cdecl sigabrt_handler(int signum) +{ + int result = port_process_signal(PORT_SIGNAL_ABORT, NULL, NULL, FALSE); + // There no reason for checking for 0 - abort() will do _exit(3) anyway +// if (result == 0) +// return; + + shutdown_signals(); // Remove handlers + + if (result > 0) // Assert dialog + show_debugger_dialog(); + + _exit(3); +} + +static void __cdecl final_sigabrt_handler(int signum) +{ + _exit(3); +} + +void sig_process_crash_flags_change(unsigned added, unsigned removed) +{ + apr_status_t aprarr = port_mutex_lock(&g_mutex); + if (aprarr != APR_SUCCESS) + return; + + if ((added & PORT_CRASH_CALL_DEBUGGER) != 0 && asserts_disabled) + { + restore_assert_dialogs(); + asserts_disabled = false; + signal(SIGABRT, (sigh_t)final_sigabrt_handler); + } + + if ((removed & PORT_CRASH_CALL_DEBUGGER) != 0 && !asserts_disabled) + { + disable_assert_dialogs(); + asserts_disabled = true; + signal(SIGABRT, (sigh_t)sigabrt_handler); + } + + port_mutex_unlock(&g_mutex); +} int initialize_signals() { + apr_status_t aprerr = port_mutex_create(&g_mutex, APR_THREAD_MUTEX_NESTED); + + if (aprerr != APR_SUCCESS) + return -1; + BOOL ok = SetConsoleCtrlHandler((PHANDLER_ROUTINE)ctrl_handler, TRUE); if (!ok) @@ -241,15 +320,28 @@ int initialize_signals() if (!veh) return -1; + prev_sig = signal(SIGABRT, (sigh_t)sigabrt_handler); + + if (prev_sig == SIG_ERR) + return -1; + disable_assert_dialogs(); + asserts_disabled = true; return 0; } int shutdown_signals() { - ULONG res; - res = RemoveVectoredExceptionHandler(veh); + if (asserts_disabled) + { + restore_assert_dialogs(); + asserts_disabled = false; + } + + signal(SIGABRT, prev_sig); + + ULONG res = RemoveVectoredExceptionHandler(veh); if (!res) return -1; @@ -259,7 +351,6 @@ int shutdown_signals() if (!ok) return -1; - restore_assert_dialogs(); - + port_mutex_destroy(&g_mutex); return 0; } //shutdown_signals diff --git a/vm/port/src/signals/win/signals_ia32.cpp b/vm/port/src/signals/win/signals_ia32.cpp index 0417793..6f32351 100644 --- a/vm/port/src/signals/win/signals_ia32.cpp +++ b/vm/port/src/signals/win/signals_ia32.cpp @@ -36,6 +36,11 @@ LONG __declspec(naked) NTAPI vectored_exception_handler(LPEXCEPTION_POINTERS nt_ } } +void __declspec(naked) __cdecl port_win_dbg_break() +{ +__asm { int 3 } +} + extern "C" void port_longjump_stub(void); #define DIR_FLAG ((uint32)0x00000400)