aboutsummaryrefslogtreecommitdiffstats
path: root/libsigsegv/src/handler-win32.c
diff options
context:
space:
mode:
authorArnold D. Robbins <arnold@skeeve.com>2010-07-16 14:52:31 +0300
committerArnold D. Robbins <arnold@skeeve.com>2010-07-16 14:52:31 +0300
commit3ba50a15ebd976f7a88393e2e45dc14b6478b9a9 (patch)
tree6a6bbe6bed1141051fefe94b2d39eacd4854235a /libsigsegv/src/handler-win32.c
parent6a2caf2157d87b4b582b2494bdd7d6a688dd0b1f (diff)
downloadegawk-3ba50a15ebd976f7a88393e2e45dc14b6478b9a9.tar.gz
egawk-3ba50a15ebd976f7a88393e2e45dc14b6478b9a9.tar.bz2
egawk-3ba50a15ebd976f7a88393e2e45dc14b6478b9a9.zip
Move to gawk-3.1.7.
Diffstat (limited to 'libsigsegv/src/handler-win32.c')
-rw-r--r--libsigsegv/src/handler-win32.c317
1 files changed, 317 insertions, 0 deletions
diff --git a/libsigsegv/src/handler-win32.c b/libsigsegv/src/handler-win32.c
new file mode 100644
index 00000000..479bd030
--- /dev/null
+++ b/libsigsegv/src/handler-win32.c
@@ -0,0 +1,317 @@
+/* Fault handler information. Woe32 version.
+ Copyright (C) 1993-1999, 2002-2003, 2007-2008 Bruno Haible <bruno@clisp.org>
+ Copyright (C) 2003 Paolo Bonzini <bonzini@gnu.org>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software Foundation,
+ Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */
+
+#include "sigsegv.h"
+
+#define WIN32_LEAN_AND_MEAN /* avoid including junk */
+#include <windows.h>
+#include <winerror.h>
+/*
+ * extern LPTOP_LEVEL_EXCEPTION_FILTER SetUnhandledExceptionFilter (LPTOP_LEVEL_EXCEPTION_FILTER TopLevelExceptionFilter);
+ * extern DWORD VirtualQuery (LPCVOID Address, PMEMORY_BASIC_INFORMATION Buffer, DWORD Length);
+ * extern BOOL VirtualProtect (LPVOID Address, DWORD Size, DWORD NewProtect, PDWORD OldProtect);
+ * extern DWORD GetLastError (void);
+ */
+
+/* User's SIGSEGV handler. */
+static sigsegv_handler_t user_handler = (sigsegv_handler_t) NULL;
+
+/* Stack overflow handling is tricky:
+ First, we must catch a STATUS_STACK_OVERFLOW exception. This is signalled
+ when the guard page at the end of the stack has been touched. The operating
+ system remaps the page with protection PAGE_READWRITE and only then calls
+ our exception handler. Actually, it's even more complicated: The stack has
+ the following layout:
+
+ | |guard|----------stack-----------|
+
+ and when the guard page is touched, the system maps it PAGE_READWRITE and
+ allocates a new guard page below it:
+
+ | |guard|-------------stack--------------|
+
+ Only when no new guard page can be allocated (because the maximum stack
+ size has been reached), will we see an exception.
+
+ |guard|-------------------------stack--------------------------|
+
+ Second, we must reinstall the guard page. Otherwise, on the next stack
+ overflow, the application will simply crash (on WinNT: silently, on Win95:
+ with an error message box and freezing the system).
+ But since we don't know where %esp points to during the exception handling,
+ we must first leave the exception handler, before we can restore the guard
+ page. And %esp must be made to point to a reasonable value before we do
+ this.
+
+ Note: On WinNT, the guard page has protection PAGE_READWRITE|PAGE_GUARD.
+ On Win95, which doesn't know PAGE_GUARD, it has protection PAGE_NOACCESS.
+ */
+
+static stackoverflow_handler_t stk_user_handler =
+ (stackoverflow_handler_t) NULL;
+static unsigned long stk_extra_stack;
+static unsigned long stk_extra_stack_size;
+
+static void
+stack_overflow_handler (unsigned long faulting_page_address, stackoverflow_context_t context)
+{
+ MEMORY_BASIC_INFORMATION info;
+ DWORD oldprot;
+ unsigned long base;
+ unsigned long address;
+
+ /* First get stack's base address. */
+ if (VirtualQuery ((void*) faulting_page_address, &info, sizeof (info))
+ != sizeof (info))
+ goto failed;
+ base = (unsigned long) info.AllocationBase;
+
+ /* Now search for the first existing page. */
+ address = base;
+ for (;;)
+ {
+ if (VirtualQuery ((void*) address, &info, sizeof (info)) != sizeof (info))
+ goto failed;
+ if (address != (unsigned long) info.BaseAddress)
+ goto failed;
+ if (info.State != MEM_FREE)
+ {
+ if ((unsigned long) info.AllocationBase != base)
+ goto failed;
+ if (info.State == MEM_COMMIT)
+ break;
+ }
+ address = (unsigned long) info.BaseAddress + info.RegionSize;
+ }
+
+ /* Now add the PAGE_GUARD bit to the first existing page. */
+ /* On WinNT this works... */
+ if (VirtualProtect (info.BaseAddress, 0x1000, info.Protect | PAGE_GUARD,
+ &oldprot))
+ goto ok;
+ if (GetLastError () == ERROR_INVALID_PARAMETER)
+ /* ... but on Win95 we need this: */
+ if (VirtualProtect (info.BaseAddress, 0x1000, PAGE_NOACCESS, &oldprot))
+ goto ok;
+ failed:
+ for (;;)
+ (*stk_user_handler) (1, context);
+ ok:
+ for (;;)
+ (*stk_user_handler) (0, context);
+}
+
+/* This is the stack overflow and page fault handler. */
+static LONG WINAPI
+main_exception_filter (EXCEPTION_POINTERS *ExceptionInfo)
+{
+ if ((stk_user_handler
+ && ExceptionInfo->ExceptionRecord->ExceptionCode == STATUS_STACK_OVERFLOW
+ )
+ ||
+ (user_handler != (sigsegv_handler_t)NULL
+ && ExceptionInfo->ExceptionRecord->ExceptionCode == EXCEPTION_ACCESS_VIOLATION
+ ))
+ {
+#if 0 /* for debugging only */
+ printf ("Exception!\n");
+ printf ("Code = 0x%x\n",
+ ExceptionInfo->ExceptionRecord->ExceptionCode);
+ printf ("Flags = 0x%x\n",
+ ExceptionInfo->ExceptionRecord->ExceptionFlags);
+ printf ("Address = 0x%x\n",
+ ExceptionInfo->ExceptionRecord->ExceptionAddress);
+ printf ("Params:");
+ {
+ DWORD i;
+ for (i = 0; i < ExceptionInfo->ExceptionRecord->NumberParameters; i++)
+ printf (" 0x%x,",
+ ExceptionInfo->ExceptionRecord->ExceptionInformation[i]);
+ }
+ printf ("\n");
+ printf ("Registers:\n");
+ printf ("eip = 0x%x\n", ExceptionInfo->ContextRecord->Eip);
+ printf ("eax = 0x%x, ", ExceptionInfo->ContextRecord->Eax);
+ printf ("ebx = 0x%x, ", ExceptionInfo->ContextRecord->Ebx);
+ printf ("ecx = 0x%x, ", ExceptionInfo->ContextRecord->Ecx);
+ printf ("edx = 0x%x\n", ExceptionInfo->ContextRecord->Edx);
+ printf ("esi = 0x%x, ", ExceptionInfo->ContextRecord->Esi);
+ printf ("edi = 0x%x, ", ExceptionInfo->ContextRecord->Edi);
+ printf ("ebp = 0x%x, ", ExceptionInfo->ContextRecord->Ebp);
+ printf ("esp = 0x%x\n", ExceptionInfo->ContextRecord->Esp);
+#endif
+ if (ExceptionInfo->ExceptionRecord->NumberParameters == 2)
+ {
+ if (stk_user_handler
+ && ExceptionInfo->ExceptionRecord->ExceptionCode == STATUS_STACK_OVERFLOW)
+ {
+ char *address = (char *) ExceptionInfo->ExceptionRecord->ExceptionInformation[1];
+ /* Restart the program, giving it a sane value for %esp.
+ At the same time, copy the contents of
+ ExceptionInfo->ContextRecord (which, on Windows XP, happens
+ to be allocated in the guard page, where it will be
+ inaccessible as soon as we restore the PAGE_GUARD bit!) to
+ this new stack. */
+ unsigned long faulting_page_address = (unsigned long)address & -0x1000;
+ unsigned long new_safe_esp = ((stk_extra_stack + stk_extra_stack_size) & -16);
+ CONTEXT *orig_context = ExceptionInfo->ContextRecord;
+ CONTEXT *safe_context = (CONTEXT *) (new_safe_esp -= sizeof (CONTEXT)); /* make room */
+ memcpy (safe_context, orig_context, sizeof (CONTEXT));
+ new_safe_esp -= 8; /* make room for arguments */
+ new_safe_esp &= -16; /* align */
+ new_safe_esp -= 4; /* make room for (unused) return address slot */
+ ExceptionInfo->ContextRecord->Esp = new_safe_esp;
+ /* Call stack_overflow_handler(faulting_page_address,safe_context). */
+ ExceptionInfo->ContextRecord->Eip = (unsigned long)&stack_overflow_handler;
+ *(unsigned long *)(new_safe_esp + 4) = faulting_page_address;
+ *(unsigned long *)(new_safe_esp + 8) = (unsigned long) safe_context;
+ return EXCEPTION_CONTINUE_EXECUTION;
+ }
+ if (user_handler != (sigsegv_handler_t) NULL
+ && ExceptionInfo->ExceptionRecord->ExceptionCode == EXCEPTION_ACCESS_VIOLATION)
+ {
+ /* ExceptionInfo->ExceptionRecord->ExceptionInformation[0] is 1
+ if it's a write access, 0 if it's a read access. But we don't
+ need this info because we don't have it on Unix either. */
+ void *address = (void *) ExceptionInfo->ExceptionRecord->ExceptionInformation[1];
+ if ((*user_handler) (address, 1))
+ return EXCEPTION_CONTINUE_EXECUTION;
+ }
+ }
+ }
+ return EXCEPTION_CONTINUE_SEARCH;
+}
+
+#if defined __CYGWIN__ && defined __i386__
+
+/* In Cygwin programs, SetUnhandledExceptionFilter has no effect because Cygwin
+ installs a global exception handler. We have to dig deep in order to install
+ our main_exception_filter. */
+
+/* Data structures for the current thread's exception handler chain.
+ On the x86 Windows uses register fs, offset 0 to point to the current
+ exception handler; Cygwin mucks with it, so we must do the same... :-/ */
+
+/* Magic taken from winsup/cygwin/include/exceptions.h. */
+
+struct exception_list
+ {
+ struct exception_list *prev;
+ int (*handler) (EXCEPTION_RECORD *, void *, CONTEXT *, void *);
+ };
+typedef struct exception_list exception_list;
+
+/* Magic taken from winsup/cygwin/exceptions.cc. */
+
+__asm__ (".equ __except_list,0");
+
+extern exception_list *_except_list __asm__ ("%fs:__except_list");
+
+/* For debugging. _except_list is not otherwise accessible from gdb. */
+static exception_list *
+debug_get_except_list ()
+{
+ return _except_list;
+}
+
+/* Cygwin's original exception handler. */
+static int (*cygwin_exception_handler) (EXCEPTION_RECORD *, void *, CONTEXT *, void *);
+
+/* Our exception handler. */
+static int
+libsigsegv_exception_handler (EXCEPTION_RECORD *exception, void *frame, CONTEXT *context, void *dispatch)
+{
+ EXCEPTION_POINTERS ExceptionInfo;
+ ExceptionInfo.ExceptionRecord = exception;
+ ExceptionInfo.ContextRecord = context;
+ if (main_exception_filter (&ExceptionInfo) == EXCEPTION_CONTINUE_SEARCH)
+ return cygwin_exception_handler (exception, frame, context, dispatch);
+ else
+ return 0;
+}
+
+static void
+do_install_main_exception_filter ()
+{
+ /* We cannot insert any handler into the chain, because such handlers
+ must lie on the stack (?). Instead, we have to replace(!) Cygwin's
+ global exception handler. */
+ cygwin_exception_handler = _except_list->handler;
+ _except_list->handler = libsigsegv_exception_handler;
+}
+
+#else
+
+static void
+do_install_main_exception_filter ()
+{
+ SetUnhandledExceptionFilter ((LPTOP_LEVEL_EXCEPTION_FILTER) &main_exception_filter);
+}
+
+#endif
+
+static void
+install_main_exception_filter ()
+{
+ static int main_exception_filter_installed = 0;
+
+ if (!main_exception_filter_installed)
+ {
+ do_install_main_exception_filter ();
+ main_exception_filter_installed = 1;
+ }
+}
+
+int
+sigsegv_install_handler (sigsegv_handler_t handler)
+{
+ user_handler = handler;
+ install_main_exception_filter ();
+ return 0;
+}
+
+void
+sigsegv_deinstall_handler (void)
+{
+ user_handler = (sigsegv_handler_t) NULL;
+}
+
+int
+sigsegv_leave_handler (void (*continuation) (void*, void*, void*),
+ void* cont_arg1, void* cont_arg2, void* cont_arg3)
+{
+ (*continuation) (cont_arg1, cont_arg2, cont_arg3);
+ return 1;
+}
+
+int
+stackoverflow_install_handler (stackoverflow_handler_t handler,
+ void *extra_stack, unsigned long extra_stack_size)
+{
+ stk_user_handler = handler;
+ stk_extra_stack = (unsigned long) extra_stack;
+ stk_extra_stack_size = extra_stack_size;
+ install_main_exception_filter ();
+ return 0;
+}
+
+void
+stackoverflow_deinstall_handler (void)
+{
+ stk_user_handler = (stackoverflow_handler_t) NULL;
+}