Bump version number.
[libsigsegv/ericb.git] / src / handler-win32.c
blob5fb6cae050a7d1427378dfd9257c3e724a8efb1f
1 /* Fault handler information. Woe32 version.
2 Copyright (C) 1993-1999, 2002-2003, 2007-2009 Bruno Haible <bruno@clisp.org>
3 Copyright (C) 2003 Paolo Bonzini <bonzini@gnu.org>
4 Copyright (C) 2009 Eric Blake <ebb9@byu.net>
6 This program is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2, or (at your option)
9 any later version.
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with this program; if not, write to the Free Software Foundation,
18 Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */
20 #include "sigsegv.h"
22 #define WIN32_LEAN_AND_MEAN /* avoid including junk */
23 #include <windows.h>
24 #include <winerror.h>
26 * extern LPTOP_LEVEL_EXCEPTION_FILTER SetUnhandledExceptionFilter (LPTOP_LEVEL_EXCEPTION_FILTER TopLevelExceptionFilter);
27 * extern DWORD VirtualQuery (LPCVOID Address, PMEMORY_BASIC_INFORMATION Buffer, DWORD Length);
28 * extern BOOL VirtualProtect (LPVOID Address, DWORD Size, DWORD NewProtect, PDWORD OldProtect);
29 * extern DWORD GetLastError (void);
32 /* On Cygwin, the "system calls" are actually library calls. If the user
33 asks for them to return with errno = EFAULT, we do this by combining
34 the Win32 and the Unix approaches:
35 1. Install a Win32 exception filter and use it to peek at the fault
36 context, but without actually handling the fault.
37 2. Install a Unix signal handler.
38 If a fault occurs outside a "system call", our exception filter will
39 take a note and defer, Cygwin's exception filter will invoke our Unix
40 signal handler.
41 If a fault occurs inside a "system call", our exception filter will
42 take a note and defer, Cygwin's exception filter will handle it, and
43 our signal handler will not be called.
44 For stack overflow, we use the pure Win32 approach, since Cygwin does
45 not have sigaltstack(). */
46 #if defined __CYGWIN__ && ENABLE_EFAULT
48 # define MIXING_UNIX_SIGSEGV_AND_WIN32_STACKOVERFLOW_HANDLING 1
50 # if OLD_CYGWIN_WORKAROUND
51 /* Last seen fault address. */
52 static void *last_seen_fault_address;
53 # endif
55 /* Import the relevant part of the Unix approach. */
56 # undef HAVE_STACK_OVERFLOW_RECOVERY
57 # define HAVE_STACK_OVERFLOW_RECOVERY 0
58 # define sigsegv_install_handler sigsegv_install_handler_unix
59 # include "handler-unix.c"
60 # undef sigsegv_install_handler
62 #else
64 /* User's SIGSEGV handler. */
65 static sigsegv_handler_t user_handler = (sigsegv_handler_t) NULL;
67 #endif
69 /* Stack overflow handling is tricky:
70 First, we must catch a STATUS_STACK_OVERFLOW exception. This is signalled
71 when the guard page at the end of the stack has been touched. The operating
72 system remaps the page with protection PAGE_READWRITE and only then calls
73 our exception handler. Actually, it's even more complicated: The stack has
74 the following layout:
76 | |guard|----------stack-----------|
78 and when the guard page is touched, the system maps it PAGE_READWRITE and
79 allocates a new guard page below it:
81 | |guard|-------------stack--------------|
83 Only when no new guard page can be allocated (because the maximum stack
84 size has been reached), will we see an exception.
86 |guard|-------------------------stack--------------------------|
88 Second, we must reinstall the guard page. Otherwise, on the next stack
89 overflow, the application will simply crash (on WinNT: silently, on Win95:
90 with an error message box and freezing the system).
91 But since we don't know where %esp points to during the exception handling,
92 we must first leave the exception handler, before we can restore the guard
93 page. And %esp must be made to point to a reasonable value before we do
94 this.
96 Note: On WinNT, the guard page has protection PAGE_READWRITE|PAGE_GUARD.
97 On Win95, which doesn't know PAGE_GUARD, it has protection PAGE_NOACCESS.
100 static stackoverflow_handler_t stk_user_handler =
101 (stackoverflow_handler_t) NULL;
102 static unsigned long stk_extra_stack;
103 static unsigned long stk_extra_stack_size;
105 static void
106 stack_overflow_handler (unsigned long faulting_page_address, stackoverflow_context_t context)
108 MEMORY_BASIC_INFORMATION info;
109 DWORD oldprot;
110 unsigned long base;
111 unsigned long address;
113 /* First get stack's base address. */
114 if (VirtualQuery ((void*) faulting_page_address, &info, sizeof (info))
115 != sizeof (info))
116 goto failed;
117 base = (unsigned long) info.AllocationBase;
119 /* Now search for the first existing page. */
120 address = base;
121 for (;;)
123 if (VirtualQuery ((void*) address, &info, sizeof (info)) != sizeof (info))
124 goto failed;
125 if (address != (unsigned long) info.BaseAddress)
126 goto failed;
127 if (info.State != MEM_FREE)
129 if ((unsigned long) info.AllocationBase != base)
130 goto failed;
131 if (info.State == MEM_COMMIT)
132 break;
134 address = (unsigned long) info.BaseAddress + info.RegionSize;
137 /* Now add the PAGE_GUARD bit to the first existing page. */
138 /* On WinNT this works... */
139 if (VirtualProtect (info.BaseAddress, 0x1000, info.Protect | PAGE_GUARD,
140 &oldprot))
141 goto ok;
142 if (GetLastError () == ERROR_INVALID_PARAMETER)
143 /* ... but on Win95 we need this: */
144 if (VirtualProtect (info.BaseAddress, 0x1000, PAGE_NOACCESS, &oldprot))
145 goto ok;
146 failed:
147 for (;;)
148 (*stk_user_handler) (1, context);
150 for (;;)
151 (*stk_user_handler) (0, context);
154 /* This is the stack overflow and page fault handler. */
155 static LONG WINAPI
156 main_exception_filter (EXCEPTION_POINTERS *ExceptionInfo)
158 if ((stk_user_handler
159 && ExceptionInfo->ExceptionRecord->ExceptionCode == STATUS_STACK_OVERFLOW
161 #if !MIXING_UNIX_SIGSEGV_AND_WIN32_STACKOVERFLOW_HANDLING || OLD_CYGWIN_WORKAROUND
163 (ExceptionInfo->ExceptionRecord->ExceptionCode == EXCEPTION_ACCESS_VIOLATION
164 #if !MIXING_UNIX_SIGSEGV_AND_WIN32_STACKOVERFLOW_HANDLING
165 && user_handler != (sigsegv_handler_t)NULL
166 #endif
168 #endif
171 #if 0 /* for debugging only */
172 printf ("Exception!\n");
173 printf ("Code = 0x%x\n",
174 ExceptionInfo->ExceptionRecord->ExceptionCode);
175 printf ("Flags = 0x%x\n",
176 ExceptionInfo->ExceptionRecord->ExceptionFlags);
177 printf ("Address = 0x%x\n",
178 ExceptionInfo->ExceptionRecord->ExceptionAddress);
179 printf ("Params:");
181 DWORD i;
182 for (i = 0; i < ExceptionInfo->ExceptionRecord->NumberParameters; i++)
183 printf (" 0x%x,",
184 ExceptionInfo->ExceptionRecord->ExceptionInformation[i]);
186 printf ("\n");
187 printf ("Registers:\n");
188 printf ("eip = 0x%x\n", ExceptionInfo->ContextRecord->Eip);
189 printf ("eax = 0x%x, ", ExceptionInfo->ContextRecord->Eax);
190 printf ("ebx = 0x%x, ", ExceptionInfo->ContextRecord->Ebx);
191 printf ("ecx = 0x%x, ", ExceptionInfo->ContextRecord->Ecx);
192 printf ("edx = 0x%x\n", ExceptionInfo->ContextRecord->Edx);
193 printf ("esi = 0x%x, ", ExceptionInfo->ContextRecord->Esi);
194 printf ("edi = 0x%x, ", ExceptionInfo->ContextRecord->Edi);
195 printf ("ebp = 0x%x, ", ExceptionInfo->ContextRecord->Ebp);
196 printf ("esp = 0x%x\n", ExceptionInfo->ContextRecord->Esp);
197 #endif
198 if (ExceptionInfo->ExceptionRecord->NumberParameters == 2)
200 if (stk_user_handler
201 && ExceptionInfo->ExceptionRecord->ExceptionCode == STATUS_STACK_OVERFLOW)
203 char *address = (char *) ExceptionInfo->ExceptionRecord->ExceptionInformation[1];
204 /* Restart the program, giving it a sane value for %esp.
205 At the same time, copy the contents of
206 ExceptionInfo->ContextRecord (which, on Windows XP, happens
207 to be allocated in the guard page, where it will be
208 inaccessible as soon as we restore the PAGE_GUARD bit!) to
209 this new stack. */
210 unsigned long faulting_page_address = (unsigned long)address & -0x1000;
211 unsigned long new_safe_esp = ((stk_extra_stack + stk_extra_stack_size) & -16);
212 CONTEXT *orig_context = ExceptionInfo->ContextRecord;
213 CONTEXT *safe_context = (CONTEXT *) (new_safe_esp -= sizeof (CONTEXT)); /* make room */
214 memcpy (safe_context, orig_context, sizeof (CONTEXT));
215 new_safe_esp -= 8; /* make room for arguments */
216 new_safe_esp &= -16; /* align */
217 new_safe_esp -= 4; /* make room for (unused) return address slot */
218 ExceptionInfo->ContextRecord->Esp = new_safe_esp;
219 /* Call stack_overflow_handler(faulting_page_address,safe_context). */
220 ExceptionInfo->ContextRecord->Eip = (unsigned long)&stack_overflow_handler;
221 *(unsigned long *)(new_safe_esp + 4) = faulting_page_address;
222 *(unsigned long *)(new_safe_esp + 8) = (unsigned long) safe_context;
223 return EXCEPTION_CONTINUE_EXECUTION;
225 #if !MIXING_UNIX_SIGSEGV_AND_WIN32_STACKOVERFLOW_HANDLING || OLD_CYGWIN_WORKAROUND
226 if (ExceptionInfo->ExceptionRecord->ExceptionCode == EXCEPTION_ACCESS_VIOLATION)
228 #if MIXING_UNIX_SIGSEGV_AND_WIN32_STACKOVERFLOW_HANDLING
229 /* Store the fault address. Then pass control to Cygwin's
230 exception filter, which will decide whether to recognize
231 a fault inside a "system call" (and return errno = EFAULT)
232 or to pass it on to the program (by invoking the Unix signal
233 handler). */
234 last_seen_fault_address = (void *) ExceptionInfo->ExceptionRecord->ExceptionInformation[1];
235 #else
236 if (user_handler != (sigsegv_handler_t) NULL)
238 /* ExceptionInfo->ExceptionRecord->ExceptionInformation[0] is
239 1 if it's a write access, 0 if it's a read access. But we
240 don't need this info because we don't have it on Unix
241 either. */
242 void *address = (void *) ExceptionInfo->ExceptionRecord->ExceptionInformation[1];
243 if ((*user_handler) (address, 1))
244 return EXCEPTION_CONTINUE_EXECUTION;
246 #endif
248 #endif
251 return EXCEPTION_CONTINUE_SEARCH;
254 #if defined __CYGWIN__ && defined __i386__
256 /* In Cygwin programs, SetUnhandledExceptionFilter has no effect because Cygwin
257 installs a global exception handler. We have to dig deep in order to install
258 our main_exception_filter. */
260 /* Data structures for the current thread's exception handler chain.
261 On the x86 Windows uses register fs, offset 0 to point to the current
262 exception handler; Cygwin mucks with it, so we must do the same... :-/ */
264 /* Magic taken from winsup/cygwin/include/exceptions.h. */
266 struct exception_list
268 struct exception_list *prev;
269 int (*handler) (EXCEPTION_RECORD *, void *, CONTEXT *, void *);
271 typedef struct exception_list exception_list;
273 /* Magic taken from winsup/cygwin/exceptions.cc. */
275 __asm__ (".equ __except_list,0");
277 extern exception_list *_except_list __asm__ ("%fs:__except_list");
279 /* For debugging. _except_list is not otherwise accessible from gdb. */
280 static
281 #if __GNUC__ >= 3
282 __attribute__ ((__unused__)) /* avoid GCC warning */
283 #endif
284 exception_list *
285 debug_get_except_list ()
287 return _except_list;
290 /* Cygwin's original exception handler. */
291 static int (*cygwin_exception_handler) (EXCEPTION_RECORD *, void *, CONTEXT *, void *);
293 /* Our exception handler. */
294 static int
295 libsigsegv_exception_handler (EXCEPTION_RECORD *exception, void *frame, CONTEXT *context, void *dispatch)
297 EXCEPTION_POINTERS ExceptionInfo;
298 ExceptionInfo.ExceptionRecord = exception;
299 ExceptionInfo.ContextRecord = context;
300 if (main_exception_filter (&ExceptionInfo) == EXCEPTION_CONTINUE_SEARCH)
301 return cygwin_exception_handler (exception, frame, context, dispatch);
302 else
303 return 0;
306 static void
307 do_install_main_exception_filter ()
309 /* We cannot insert any handler into the chain, because such handlers
310 must lie on the stack (?). Instead, we have to replace(!) Cygwin's
311 global exception handler. */
312 cygwin_exception_handler = _except_list->handler;
313 _except_list->handler = libsigsegv_exception_handler;
316 #else
318 static void
319 do_install_main_exception_filter ()
321 SetUnhandledExceptionFilter ((LPTOP_LEVEL_EXCEPTION_FILTER) &main_exception_filter);
324 #endif
326 static void
327 install_main_exception_filter ()
329 static int main_exception_filter_installed = 0;
331 if (!main_exception_filter_installed)
333 do_install_main_exception_filter ();
334 main_exception_filter_installed = 1;
338 #if MIXING_UNIX_SIGSEGV_AND_WIN32_STACKOVERFLOW_HANDLING
341 sigsegv_install_handler (sigsegv_handler_t handler)
343 install_main_exception_filter ();
344 return sigsegv_install_handler_unix (handler);
347 #else
350 sigsegv_install_handler (sigsegv_handler_t handler)
352 user_handler = handler;
353 install_main_exception_filter ();
354 return 0;
357 void
358 sigsegv_deinstall_handler (void)
360 user_handler = (sigsegv_handler_t) NULL;
364 sigsegv_leave_handler (void (*continuation) (void*, void*, void*),
365 void* cont_arg1, void* cont_arg2, void* cont_arg3)
367 (*continuation) (cont_arg1, cont_arg2, cont_arg3);
368 return 1;
371 #endif /* !MIXING_UNIX_SIGSEGV_AND_WIN32_STACKOVERFLOW_HANDLING */
374 stackoverflow_install_handler (stackoverflow_handler_t handler,
375 void *extra_stack, unsigned long extra_stack_size)
377 stk_user_handler = handler;
378 stk_extra_stack = (unsigned long) extra_stack;
379 stk_extra_stack_size = extra_stack_size;
380 install_main_exception_filter ();
381 return 0;
384 void
385 stackoverflow_deinstall_handler (void)
387 stk_user_handler = (stackoverflow_handler_t) NULL;