summaryrefslogtreecommitdiff
path: root/src/w32fns.c
diff options
context:
space:
mode:
authorEli Zaretskii <eliz@gnu.org>2015-08-08 11:12:06 +0300
committerEli Zaretskii <eliz@gnu.org>2015-08-08 11:12:06 +0300
commit7afa4f300b9dc38bf3f33b18fa83bfe35e21a479 (patch)
tree032ab5a75ec1ff00cee51ffe6216fceb16a53e6c /src/w32fns.c
parent35656b6fa473a4c422875a61d24ebb736c1be4e9 (diff)
downloademacs-7afa4f300b9dc38bf3f33b18fa83bfe35e21a479.tar.gz
Support recovery from C stack overflow on MS-Windows
* src/w32fns.c (w32_reset_stack_overflow_guard) (stack_overflow_handler): New functions for handling C stack overflow exceptions. (my_exception_handler): Handle EXCEPTION_STACK_OVERFLOW exceptions specially, and zero out except_addr if we do. (globals_of_w32fns): Initialize dwMainThreadId in non-interactive mode. * src/sysdep.c [HAVE_STACK_OVERFLOW_HANDLING]: Add !WINDOWSNT to the condition, as HAVE_STACK_OVERFLOW_HANDLING is now defined for the MinGW build, but the code guarded by that is for Posix hosts. * src/keyboard.c (command_loop) [WINDOWSNT]: Call w32_reset_stack_overflow_guard. * nt/inc/ms-w32.h (sigjmp_buf): New typedef. (sigsetjmp): New macro. (w32_reset_stack_overflow_guard): Declare the prototype. * configure.ac (HAVE_STACK_OVERFLOW_HANDLING): Set to 1 for MinGW.
Diffstat (limited to 'src/w32fns.c')
-rw-r--r--src/w32fns.c61
1 files changed, 59 insertions, 2 deletions
diff --git a/src/w32fns.c b/src/w32fns.c
index ad93bd41851..4532fb9f469 100644
--- a/src/w32fns.c
+++ b/src/w32fns.c
@@ -9239,18 +9239,71 @@ static DWORD except_code;
static PVOID except_addr;
#ifndef CYGWIN
+
+/* Stack overflow recovery. */
+
+/* Re-establish the guard page at stack limit. This is needed because
+ when a stack overflow is detected, Windows removes the guard bit
+ from the guard page, so if we don't re-establish that protection,
+ the next stack overflow will cause a crash. */
+void
+w32_reset_stack_overflow_guard (void)
+{
+ /* MinGW headers don't declare this (should be in malloc.h). */
+ _CRTIMP int __cdecl _resetstkoflw (void);
+
+ /* We ignore the return value. If _resetstkoflw fails, the next
+ stack overflow will crash the program. */
+ (void)_resetstkoflw ();
+}
+
+static void
+stack_overflow_handler (void)
+{
+ /* Hard GC error may lead to stack overflow caused by
+ too nested calls to mark_object. No way to survive. */
+ if (gc_in_progress)
+ terminate_due_to_signal (SIGSEGV, 40);
+#ifdef _WIN64
+ /* See ms-w32.h: MinGW64's longjmp crashes if invoked in this context. */
+ __builtin_longjmp (return_to_command_loop, 1);
+#else
+ sys_longjmp (return_to_command_loop, 1);
+#endif
+}
+
/* This handler records the exception code and the address where it
was triggered so that this info could be included in the backtrace.
Without that, the backtrace in some cases has no information
whatsoever about the offending code, and looks as if the top-level
- exception handler in the MinGW startup code di the one that
- crashed. */
+ exception handler in the MinGW startup code was the one that
+ crashed. We also recover from stack overflow, by calling our stack
+ overflow handler that jumps back to top level. */
static LONG CALLBACK
my_exception_handler (EXCEPTION_POINTERS * exception_data)
{
except_code = exception_data->ExceptionRecord->ExceptionCode;
except_addr = exception_data->ExceptionRecord->ExceptionAddress;
+ /* If this is a stack overflow exception, attempt to recover. */
+ if (exception_data->ExceptionRecord->ExceptionCode == EXCEPTION_STACK_OVERFLOW
+ && exception_data->ExceptionRecord->NumberParameters == 2
+ /* We can only longjmp to top level from the main thread. */
+ && GetCurrentThreadId () == dwMainThreadId)
+ {
+ /* Call stack_overflow_handler (). */
+#ifdef _WIN64
+ exception_data->ContextRecord->Rip = (DWORD_PTR) &stack_overflow_handler;
+#else
+ exception_data->ContextRecord->Eip = (DWORD_PTR) &stack_overflow_handler;
+#endif
+ /* Zero this out, so the stale address of the stack overflow
+ exception we handled is not displayed in some future
+ unrelated crash. */
+ except_addr = 0;
+ return EXCEPTION_CONTINUE_EXECUTION;
+ }
+
if (prev_exception_handler)
return prev_exception_handler (exception_data);
return EXCEPTION_EXECUTE_HANDLER;
@@ -9448,6 +9501,10 @@ globals_of_w32fns (void)
InitCommonControls ();
syms_of_w32uniscribe ();
+
+ /* Needed for recovery from C stack overflows in batch mode. */
+ if (noninteractive)
+ dwMainThreadId = GetCurrentThreadId ();
}
#ifdef NTGUI_UNICODE