Added assembly implementation of EXC_CallHandler.
[wine] / dlls / ntdll / exception.c
1 /*
2  * NT exception handling routines
3  *
4  * Copyright 1999 Turchanov Sergey
5  * Copyright 1999 Alexandre Julliard
6  *
7  * This library is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Lesser General Public
9  * License as published by the Free Software Foundation; either
10  * version 2.1 of the License, or (at your option) any later version.
11  *
12  * This library is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * Lesser General Public License for more details.
16  *
17  * You should have received a copy of the GNU Lesser General Public
18  * License along with this library; if not, write to the Free Software
19  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
20  */
21
22 #include "config.h"
23 #include "wine/port.h"
24
25 #include <assert.h>
26 #include <signal.h>
27 #include <stdarg.h>
28
29 #include "ntstatus.h"
30 #include "windef.h"
31 #include "winternl.h"
32 #include "wine/exception.h"
33 #include "wine/server.h"
34 #include "wine/list.h"
35 #include "wine/debug.h"
36 #include "excpt.h"
37 #include "ntdll_misc.h"
38
39 WINE_DEFAULT_DEBUG_CHANNEL(seh);
40
41 /* Exception record for handling exceptions happening inside exception handlers */
42 typedef struct
43 {
44     EXCEPTION_REGISTRATION_RECORD frame;
45     EXCEPTION_REGISTRATION_RECORD *prevFrame;
46 } EXC_NESTED_FRAME;
47
48 typedef struct
49 {
50     struct list                 entry;
51     PVECTORED_EXCEPTION_HANDLER func;
52 } VECTORED_HANDLER;
53
54 static struct list vectored_handlers = LIST_INIT(vectored_handlers);
55
56 static RTL_CRITICAL_SECTION vectored_handlers_section;
57 static RTL_CRITICAL_SECTION_DEBUG critsect_debug =
58 {
59     0, 0, &vectored_handlers_section,
60     { &critsect_debug.ProcessLocksList, &critsect_debug.ProcessLocksList },
61       0, 0, { (DWORD_PTR)(__FILE__ ": vectored_handlers_section") }
62 };
63 static RTL_CRITICAL_SECTION vectored_handlers_section = { &critsect_debug, -1, 0, 0, 0, 0 };
64
65 #ifdef __i386__
66 # define GET_IP(context) ((LPVOID)(context)->Eip)
67 #elif defined(__sparc__)
68 # define GET_IP(context) ((LPVOID)(context)->pc)
69 #elif defined(__powerpc__)
70 # define GET_IP(context) ((LPVOID)(context)->Iar)
71 #elif defined(__ALPHA__)
72 # define GET_IP(context) ((LPVOID)(context)->Fir)
73 #elif defined(__x86_64__)
74 # define GET_IP(context) ((LPVOID)(context)->Rip)
75 #else
76 # error You must define GET_IP for this CPU
77 #endif
78
79
80 /*******************************************************************
81  *         EXC_RaiseHandler
82  *
83  * Handler for exceptions happening inside a handler.
84  */
85 static DWORD EXC_RaiseHandler( EXCEPTION_RECORD *rec, EXCEPTION_REGISTRATION_RECORD *frame,
86                                CONTEXT *context, EXCEPTION_REGISTRATION_RECORD **dispatcher )
87 {
88     if (rec->ExceptionFlags & (EH_UNWINDING | EH_EXIT_UNWIND))
89         return ExceptionContinueSearch;
90     /* We shouldn't get here so we store faulty frame in dispatcher */
91     *dispatcher = ((EXC_NESTED_FRAME*)frame)->prevFrame;
92     return ExceptionNestedException;
93 }
94
95
96 /*******************************************************************
97  *         EXC_UnwindHandler
98  *
99  * Handler for exceptions happening inside an unwind handler.
100  */
101 static DWORD EXC_UnwindHandler( EXCEPTION_RECORD *rec, EXCEPTION_REGISTRATION_RECORD *frame,
102                                 CONTEXT *context, EXCEPTION_REGISTRATION_RECORD **dispatcher )
103 {
104     if (!(rec->ExceptionFlags & (EH_UNWINDING | EH_EXIT_UNWIND)))
105         return ExceptionContinueSearch;
106     /* We shouldn't get here so we store faulty frame in dispatcher */
107     *dispatcher = ((EXC_NESTED_FRAME*)frame)->prevFrame;
108     return ExceptionCollidedUnwind;
109 }
110
111
112 /*******************************************************************
113  *         EXC_CallHandler
114  *
115  * Call an exception handler, setting up an exception frame to catch exceptions
116  * happening during the handler execution.
117  * Please do not change the first 4 parameters order in any way - some exceptions handlers
118  * rely on Base Pointer (EBP) to have a fixed position related to the exception frame
119  *
120  * For i386 this function is implemented in assembler in signal_i386.c.
121  */
122 #ifndef __i386__
123 static DWORD EXC_CallHandler( EXCEPTION_RECORD *record, EXCEPTION_REGISTRATION_RECORD *frame,
124                               CONTEXT *context, EXCEPTION_REGISTRATION_RECORD **dispatcher,
125                               PEXCEPTION_HANDLER handler, PEXCEPTION_HANDLER nested_handler)
126 {
127     EXC_NESTED_FRAME newframe;
128     DWORD ret;
129
130     newframe.frame.Handler = nested_handler;
131     newframe.prevFrame     = frame;
132     __wine_push_frame( &newframe.frame );
133     TRACE( "calling handler at %p code=%lx flags=%lx\n",
134            handler, record->ExceptionCode, record->ExceptionFlags );
135     ret = handler( record, frame, context, dispatcher );
136     TRACE( "handler returned %lx\n", ret );
137     __wine_pop_frame( &newframe.frame );
138     return ret;
139 }
140 #else
141 /* in signal_i386.c */
142 extern DWORD EXC_CallHandler( EXCEPTION_RECORD *record, EXCEPTION_REGISTRATION_RECORD *frame,
143                               CONTEXT *context, EXCEPTION_REGISTRATION_RECORD **dispatcher,
144                               PEXCEPTION_HANDLER handler, PEXCEPTION_HANDLER nested_handler);
145 #endif
146
147 /**********************************************************************
148  *           send_debug_event
149  *
150  * Send an EXCEPTION_DEBUG_EVENT event to the debugger.
151  */
152 static int send_debug_event( EXCEPTION_RECORD *rec, int first_chance, CONTEXT *context )
153 {
154     int ret;
155     HANDLE handle = 0;
156
157     if (!NtCurrentTeb()->Peb->BeingDebugged) return 0;  /* no debugger present */
158
159     SERVER_START_REQ( queue_exception_event )
160     {
161         req->first   = first_chance;
162         wine_server_add_data( req, context, sizeof(*context) );
163         wine_server_add_data( req, rec, sizeof(*rec) );
164         if (!wine_server_call( req )) handle = reply->handle;
165     }
166     SERVER_END_REQ;
167     if (!handle) return 0;
168
169     /* No need to wait on the handle since the process gets suspended
170      * once the event is passed to the debugger, so when we get back
171      * here the event has been continued already.
172      */
173     SERVER_START_REQ( get_exception_status )
174     {
175         req->handle = handle;
176         wine_server_set_reply( req, context, sizeof(*context) );
177         wine_server_call( req );
178         ret = reply->status;
179     }
180     SERVER_END_REQ;
181     NtClose( handle );
182     return ret;
183 }
184
185
186 /**********************************************************************
187  *           call_vectored_handlers
188  *
189  * Call the vectored handlers chain.
190  */
191 static LONG call_vectored_handlers( EXCEPTION_RECORD *rec, CONTEXT *context )
192 {
193     struct list *ptr;
194     LONG ret = EXCEPTION_CONTINUE_SEARCH;
195     EXCEPTION_POINTERS except_ptrs;
196
197     except_ptrs.ExceptionRecord = rec;
198     except_ptrs.ContextRecord = context;
199
200     RtlEnterCriticalSection( &vectored_handlers_section );
201     LIST_FOR_EACH( ptr, &vectored_handlers )
202     {
203         VECTORED_HANDLER *handler = LIST_ENTRY( ptr, VECTORED_HANDLER, entry );
204         ret = handler->func( &except_ptrs );
205         if (ret == EXCEPTION_CONTINUE_EXECUTION) break;
206     }
207     RtlLeaveCriticalSection( &vectored_handlers_section );
208     return ret;
209 }
210
211
212 /*******************************************************************
213  *         EXC_DefaultHandling
214  *
215  * Default handling for exceptions. Called when we didn't find a suitable handler.
216  */
217 static void EXC_DefaultHandling( EXCEPTION_RECORD *rec, CONTEXT *context )
218 {
219     if (send_debug_event( rec, FALSE, context ) == DBG_CONTINUE) return;  /* continue execution */
220
221     if (rec->ExceptionFlags & EH_STACK_INVALID)
222         ERR("Exception frame is not in stack limits => unable to dispatch exception.\n");
223     else if (rec->ExceptionCode == STATUS_NONCONTINUABLE_EXCEPTION)
224         ERR("Process attempted to continue execution after noncontinuable exception.\n");
225     else
226         ERR("Unhandled exception code %lx flags %lx addr %p\n",
227             rec->ExceptionCode, rec->ExceptionFlags, rec->ExceptionAddress );
228     NtTerminateProcess( NtCurrentProcess(), 1 );
229 }
230
231
232 /***********************************************************************
233  *              RtlRaiseException (NTDLL.@)
234  */
235 void WINAPI __regs_RtlRaiseException( EXCEPTION_RECORD *rec, CONTEXT *context )
236 {
237     EXCEPTION_REGISTRATION_RECORD *frame, *dispatch, *nested_frame;
238     EXCEPTION_RECORD newrec;
239     DWORD res, c;
240
241     TRACE( "code=%lx flags=%lx addr=%p\n", rec->ExceptionCode, rec->ExceptionFlags, rec->ExceptionAddress );
242     for (c=0; c<rec->NumberParameters; c++) TRACE(" info[%ld]=%08lx\n", c, rec->ExceptionInformation[c]);
243     if (rec->ExceptionCode == EXCEPTION_WINE_STUB)
244     {
245         if (HIWORD(rec->ExceptionInformation[1]))
246             MESSAGE( "wine: Call from %p to unimplemented function %s.%s, aborting\n",
247                    rec->ExceptionAddress,
248                    (char*)rec->ExceptionInformation[0], (char*)rec->ExceptionInformation[1] );
249         else
250             MESSAGE( "wine: Call from %p to unimplemented function %s.%ld, aborting\n",
251                    rec->ExceptionAddress,
252                    (char*)rec->ExceptionInformation[0], rec->ExceptionInformation[1] );
253     }
254 #ifdef __i386__
255     else
256     {
257         TRACE(" eax=%08lx ebx=%08lx ecx=%08lx edx=%08lx esi=%08lx edi=%08lx\n",
258               context->Eax, context->Ebx, context->Ecx,
259               context->Edx, context->Esi, context->Edi );
260         TRACE(" ebp=%08lx esp=%08lx cs=%04lx ds=%04lx es=%04lx fs=%04lx gs=%04lx flags=%08lx\n",
261               context->Ebp, context->Esp, context->SegCs, context->SegDs,
262               context->SegEs, context->SegFs, context->SegGs, context->EFlags );
263     }
264 #endif
265
266     if (send_debug_event( rec, TRUE, context ) == DBG_CONTINUE) return;  /* continue execution */
267
268     if (call_vectored_handlers( rec, context ) == EXCEPTION_CONTINUE_EXECUTION) return;
269
270     frame = NtCurrentTeb()->Tib.ExceptionList;
271     nested_frame = NULL;
272     while (frame != (EXCEPTION_REGISTRATION_RECORD*)~0UL)
273     {
274         /* Check frame address */
275         if (((void*)frame < NtCurrentTeb()->Tib.StackLimit) ||
276             ((void*)(frame+1) > NtCurrentTeb()->Tib.StackBase) ||
277             (ULONG_PTR)frame & 3)
278         {
279             rec->ExceptionFlags |= EH_STACK_INVALID;
280             break;
281         }
282
283         /* Call handler */
284         res = EXC_CallHandler( rec, frame, context, &dispatch, frame->Handler, EXC_RaiseHandler );
285         if (frame == nested_frame)
286         {
287             /* no longer nested */
288             nested_frame = NULL;
289             rec->ExceptionFlags &= ~EH_NESTED_CALL;
290         }
291
292         switch(res)
293         {
294         case ExceptionContinueExecution:
295             if (!(rec->ExceptionFlags & EH_NONCONTINUABLE)) return;
296             newrec.ExceptionCode    = STATUS_NONCONTINUABLE_EXCEPTION;
297             newrec.ExceptionFlags   = EH_NONCONTINUABLE;
298             newrec.ExceptionRecord  = rec;
299             newrec.NumberParameters = 0;
300             RtlRaiseException( &newrec );  /* never returns */
301             break;
302         case ExceptionContinueSearch:
303             break;
304         case ExceptionNestedException:
305             if (nested_frame < dispatch) nested_frame = dispatch;
306             rec->ExceptionFlags |= EH_NESTED_CALL;
307             break;
308         default:
309             newrec.ExceptionCode    = STATUS_INVALID_DISPOSITION;
310             newrec.ExceptionFlags   = EH_NONCONTINUABLE;
311             newrec.ExceptionRecord  = rec;
312             newrec.NumberParameters = 0;
313             RtlRaiseException( &newrec );  /* never returns */
314             break;
315         }
316         frame = frame->Prev;
317     }
318     EXC_DefaultHandling( rec, context );
319 }
320
321 /**********************************************************************/
322
323 #ifdef DEFINE_REGS_ENTRYPOINT
324 DEFINE_REGS_ENTRYPOINT( RtlRaiseException, 4, 4 );
325 #else
326 void WINAPI RtlRaiseException( EXCEPTION_RECORD *rec )
327 {
328     CONTEXT context;
329     memset( &context, 0, sizeof(context) );
330     __regs_RtlRaiseException( rec, &context );
331 }
332 #endif
333
334
335 /*******************************************************************
336  *              RtlUnwind (NTDLL.@)
337  */
338 void WINAPI __regs_RtlUnwind( EXCEPTION_REGISTRATION_RECORD* pEndFrame, PVOID unusedEip,
339                               PEXCEPTION_RECORD pRecord, PVOID returnEax, CONTEXT *context )
340 {
341     EXCEPTION_RECORD record, newrec;
342     EXCEPTION_REGISTRATION_RECORD *frame, *dispatch;
343
344 #ifdef __i386__
345     context->Eax = (DWORD)returnEax;
346 #endif
347
348     /* build an exception record, if we do not have one */
349     if (!pRecord)
350     {
351         record.ExceptionCode    = STATUS_UNWIND;
352         record.ExceptionFlags   = 0;
353         record.ExceptionRecord  = NULL;
354         record.ExceptionAddress = GET_IP(context);
355         record.NumberParameters = 0;
356         pRecord = &record;
357     }
358
359     pRecord->ExceptionFlags |= EH_UNWINDING | (pEndFrame ? 0 : EH_EXIT_UNWIND);
360
361     TRACE( "code=%lx flags=%lx\n", pRecord->ExceptionCode, pRecord->ExceptionFlags );
362
363     /* get chain of exception frames */
364     frame = NtCurrentTeb()->Tib.ExceptionList;
365     while ((frame != (EXCEPTION_REGISTRATION_RECORD*)~0UL) && (frame != pEndFrame))
366     {
367         /* Check frame address */
368         if (pEndFrame && (frame > pEndFrame))
369         {
370             newrec.ExceptionCode    = STATUS_INVALID_UNWIND_TARGET;
371             newrec.ExceptionFlags   = EH_NONCONTINUABLE;
372             newrec.ExceptionRecord  = pRecord;
373             newrec.NumberParameters = 0;
374             RtlRaiseException( &newrec );  /* never returns */
375         }
376         if (((void*)frame < NtCurrentTeb()->Tib.StackLimit) ||
377             ((void*)(frame+1) > NtCurrentTeb()->Tib.StackBase) ||
378             (UINT_PTR)frame & 3)
379         {
380             newrec.ExceptionCode    = STATUS_BAD_STACK;
381             newrec.ExceptionFlags   = EH_NONCONTINUABLE;
382             newrec.ExceptionRecord  = pRecord;
383             newrec.NumberParameters = 0;
384             RtlRaiseException( &newrec );  /* never returns */
385         }
386
387         /* Call handler */
388         switch(EXC_CallHandler( pRecord, frame, context, &dispatch,
389                                 frame->Handler, EXC_UnwindHandler ))
390         {
391         case ExceptionContinueSearch:
392             break;
393         case ExceptionCollidedUnwind:
394             frame = dispatch;
395             break;
396         default:
397             newrec.ExceptionCode    = STATUS_INVALID_DISPOSITION;
398             newrec.ExceptionFlags   = EH_NONCONTINUABLE;
399             newrec.ExceptionRecord  = pRecord;
400             newrec.NumberParameters = 0;
401             RtlRaiseException( &newrec );  /* never returns */
402             break;
403         }
404         frame = __wine_pop_frame( frame );
405     }
406 }
407
408 /**********************************************************************/
409
410 #ifdef DEFINE_REGS_ENTRYPOINT
411 DEFINE_REGS_ENTRYPOINT( RtlUnwind, 16, 16 );
412 #else
413 void WINAPI RtlUnwind( PVOID pEndFrame, PVOID unusedEip,
414                        PEXCEPTION_RECORD pRecord, PVOID returnEax )
415 {
416     CONTEXT context;
417     memset( &context, 0, sizeof(context) );
418     __regs_RtlUnwind( pEndFrame, unusedEip, pRecord, returnEax, &context );
419 }
420 #endif
421
422
423 /*******************************************************************
424  *              NtRaiseException (NTDLL.@)
425  */
426 void WINAPI __regs_NtRaiseException( EXCEPTION_RECORD *rec, CONTEXT *ctx,
427                                   BOOL first, CONTEXT *context )
428 {
429     __regs_RtlRaiseException( rec, ctx );
430     *context = *ctx;
431 }
432
433 #ifdef DEFINE_REGS_ENTRYPOINT
434 DEFINE_REGS_ENTRYPOINT( NtRaiseException, 12, 12 );
435 #else
436 void WINAPI NtRaiseException( EXCEPTION_RECORD *rec, CONTEXT *ctx, BOOL first )
437 {
438     CONTEXT context;
439     memset( &context, 0, sizeof(context) );
440     __regs_NtRaiseException( rec, ctx, first, &context );
441 }
442 #endif
443
444
445 /***********************************************************************
446  *            RtlRaiseStatus  (NTDLL.@)
447  *
448  * Raise an exception with ExceptionCode = status
449  */
450 void WINAPI RtlRaiseStatus( NTSTATUS status )
451 {
452     EXCEPTION_RECORD ExceptionRec;
453
454     ExceptionRec.ExceptionCode    = status;
455     ExceptionRec.ExceptionFlags   = EH_NONCONTINUABLE;
456     ExceptionRec.ExceptionRecord  = NULL;
457     ExceptionRec.NumberParameters = 0;
458     RtlRaiseException( &ExceptionRec );
459 }
460
461
462 /*******************************************************************
463  *         RtlAddVectoredExceptionHandler   (NTDLL.@)
464  */
465 PVOID WINAPI RtlAddVectoredExceptionHandler( ULONG first, PVECTORED_EXCEPTION_HANDLER func )
466 {
467     VECTORED_HANDLER *handler = RtlAllocateHeap( GetProcessHeap(), 0, sizeof(*handler) );
468     if (handler)
469     {
470         handler->func = func;
471         RtlEnterCriticalSection( &vectored_handlers_section );
472         if (first) list_add_head( &vectored_handlers, &handler->entry );
473         else list_add_tail( &vectored_handlers, &handler->entry );
474         RtlLeaveCriticalSection( &vectored_handlers_section );
475     }
476     return handler;
477 }
478
479
480 /*******************************************************************
481  *         RtlRemoveVectoredExceptionHandler   (NTDLL.@)
482  */
483 ULONG WINAPI RtlRemoveVectoredExceptionHandler( PVOID handler )
484 {
485     struct list *ptr;
486     ULONG ret = FALSE;
487
488     RtlEnterCriticalSection( &vectored_handlers_section );
489     LIST_FOR_EACH( ptr, &vectored_handlers )
490     {
491         VECTORED_HANDLER *curr_handler = LIST_ENTRY( ptr, VECTORED_HANDLER, entry );
492         if (curr_handler == handler)
493         {
494             list_remove( ptr );
495             ret = TRUE;
496             break;
497         }
498     }
499     RtlLeaveCriticalSection( &vectored_handlers_section );
500     if (ret) RtlFreeHeap( GetProcessHeap(), 0, handler );
501     return ret;
502 }
503
504
505 /*************************************************************
506  *            __wine_exception_handler (NTDLL.@)
507  *
508  * Exception handler for exception blocks declared in Wine code.
509  */
510 DWORD __wine_exception_handler( EXCEPTION_RECORD *record, EXCEPTION_REGISTRATION_RECORD *frame,
511                                 CONTEXT *context, EXCEPTION_REGISTRATION_RECORD **pdispatcher )
512 {
513     __WINE_FRAME *wine_frame = (__WINE_FRAME *)frame;
514
515     if (record->ExceptionFlags & (EH_UNWINDING | EH_EXIT_UNWIND | EH_NESTED_CALL))
516         return ExceptionContinueSearch;
517     if (wine_frame->u.filter)
518     {
519         EXCEPTION_POINTERS ptrs;
520         ptrs.ExceptionRecord = record;
521         ptrs.ContextRecord = context;
522         switch(wine_frame->u.filter( &ptrs ))
523         {
524         case EXCEPTION_CONTINUE_SEARCH:
525             return ExceptionContinueSearch;
526         case EXCEPTION_CONTINUE_EXECUTION:
527             return ExceptionContinueExecution;
528         case EXCEPTION_EXECUTE_HANDLER:
529             break;
530         default:
531             MESSAGE( "Invalid return value from exception filter\n" );
532             assert( FALSE );
533         }
534     }
535     /* hack to make GetExceptionCode() work in handler */
536     wine_frame->ExceptionCode   = record->ExceptionCode;
537     wine_frame->ExceptionRecord = wine_frame;
538
539     RtlUnwind( frame, 0, record, 0 );
540     __wine_pop_frame( frame );
541     siglongjmp( wine_frame->jmp, 1 );
542 }
543
544
545 /*************************************************************
546  *            __wine_finally_handler (NTDLL.@)
547  *
548  * Exception handler for try/finally blocks declared in Wine code.
549  */
550 DWORD __wine_finally_handler( EXCEPTION_RECORD *record, EXCEPTION_REGISTRATION_RECORD *frame,
551                               CONTEXT *context, EXCEPTION_REGISTRATION_RECORD **pdispatcher )
552 {
553     if (record->ExceptionFlags & (EH_UNWINDING | EH_EXIT_UNWIND))
554     {
555         __WINE_FRAME *wine_frame = (__WINE_FRAME *)frame;
556         wine_frame->u.finally_func( FALSE );
557     }
558     return ExceptionContinueSearch;
559 }
560
561
562 /*************************************************************
563  *            __wine_spec_unimplemented_stub
564  *
565  * ntdll-specific implementation to avoid depending on kernel functions.
566  * Can be removed once ntdll.spec no longer contains stubs.
567  */
568 void __wine_spec_unimplemented_stub( const char *module, const char *function )
569 {
570     EXCEPTION_RECORD record;
571
572     record.ExceptionCode    = EXCEPTION_WINE_STUB;
573     record.ExceptionFlags   = EH_NONCONTINUABLE;
574     record.ExceptionRecord  = NULL;
575     record.ExceptionAddress = __wine_spec_unimplemented_stub;
576     record.NumberParameters = 2;
577     record.ExceptionInformation[0] = (ULONG_PTR)module;
578     record.ExceptionInformation[1] = (ULONG_PTR)function;
579     RtlRaiseException( &record );
580 }