Bugfix: Exception handlers are *not* WINAPI.
[wine] / dlls / ntdll / exception.c
1 /*
2  * NT exception handling routines
3  * 
4  * Copyright 1999 Turchanov Sergey
5  * Copyright 1999 Alexandre Julliard
6  */
7
8 #include <signal.h>
9 #include "winnt.h"
10 #include "ntddk.h"
11 #include "process.h"
12 #include "global.h"
13 #include "wine/exception.h"
14 #include "stackframe.h"
15 #include "miscemu.h"
16 #include "debugtools.h"
17
18 DEFAULT_DEBUG_CHANNEL(seh)
19
20 /* Exception record for handling exceptions happening inside exception handlers */
21 typedef struct
22 {
23     EXCEPTION_FRAME frame;
24     EXCEPTION_FRAME *prevFrame;
25 } EXC_NESTED_FRAME;
26
27  
28 #ifdef __i386__
29 # define GET_IP(context) ((LPVOID)(context)->Eip)
30 #else
31 # error You must define GET_IP for this CPU
32 #endif  /* __i386__ */
33
34 /* Default hook for built-in debugger */
35 static DWORD default_hook( EXCEPTION_RECORD *rec, CONTEXT *ctx, BOOL first )
36 {
37     if (!first)
38     {
39         DPRINTF( "stopping process due to unhandled exception %08lx.\n",
40                  rec->ExceptionCode );
41         raise( SIGSTOP );
42     }
43     return 0;  /* not handled */
44 }
45 static DEBUGHOOK debug_hook = default_hook;
46
47 /*******************************************************************
48  *         EXC_SetDebugEventHook
49  *         EXC_GetDebugEventHook
50  *
51  * Set/Get the hook for the built-in debugger.
52  *
53  * FIXME: the built-in debugger should use the normal debug events.
54  */
55 void EXC_SetDebugEventHook( DEBUGHOOK hook )
56 {
57     debug_hook = hook;
58 }
59 DEBUGHOOK EXC_GetDebugEventHook(void)
60 {
61     return debug_hook;
62 }
63
64 /*******************************************************************
65  *         EXC_RaiseHandler
66  *
67  * Handler for exceptions happening inside a handler.
68  */
69 static DWORD EXC_RaiseHandler( EXCEPTION_RECORD *rec, EXCEPTION_FRAME *frame,
70                                CONTEXT *context, EXCEPTION_FRAME **dispatcher )
71 {
72     if (rec->ExceptionFlags & (EH_UNWINDING | EH_EXIT_UNWIND))
73         return ExceptionContinueSearch;
74     /* We shouldn't get here so we store faulty frame in dispatcher */
75     *dispatcher = ((EXC_NESTED_FRAME*)frame)->prevFrame;
76     return ExceptionNestedException;
77 }
78
79
80 /*******************************************************************
81  *         EXC_UnwindHandler
82  *
83  * Handler for exceptions happening inside an unwind handler.
84  */
85 static DWORD EXC_UnwindHandler( EXCEPTION_RECORD *rec, EXCEPTION_FRAME *frame,
86                                 CONTEXT *context, EXCEPTION_FRAME **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 ExceptionCollidedUnwind;
93 }
94
95
96 /*******************************************************************
97  *         EXC_CallHandler
98  *
99  * Call an exception handler, setting up an exception frame to catch exceptions
100  * happening during the handler execution.
101  * Please do not change the first 4 parameters order in any way - some exceptions handlers
102  * rely on Base Pointer (EBP) to have a fixed position related to the exception frame
103  */
104 static DWORD EXC_CallHandler( EXCEPTION_RECORD *record, EXCEPTION_FRAME *frame,
105                               CONTEXT *context, EXCEPTION_FRAME **dispatcher, 
106                               PEXCEPTION_HANDLER handler, PEXCEPTION_HANDLER nested_handler)
107 {
108     EXC_NESTED_FRAME newframe;
109     DWORD ret;
110
111     newframe.frame.Handler = nested_handler;
112     newframe.prevFrame     = frame;
113     EXC_push_frame( &newframe.frame );
114     TRACE( "calling handler at %p code=%lx flags=%lx\n",
115            handler, record->ExceptionCode, record->ExceptionFlags );
116     ret = handler( record, frame, context, dispatcher );
117     TRACE( "handler returned %lx\n", ret );
118     EXC_pop_frame( &newframe.frame );
119     return ret;
120 }
121
122
123 /*******************************************************************
124  *         EXC_DefaultHandling
125  *
126  * Default handling for exceptions. Called when we didn't find a suitable handler.
127  */
128 static void EXC_DefaultHandling( EXCEPTION_RECORD *rec, CONTEXT *context )
129 {
130     if ((PROCESS_Current()->flags & PDB32_DEBUGGED) &&
131         (DEBUG_SendExceptionEvent( rec, FALSE ) == DBG_CONTINUE))
132         return;  /* continue execution */
133
134     if (debug_hook( rec, context, FALSE ) == DBG_CONTINUE)
135         return;  /* continue execution */
136
137     if (rec->ExceptionFlags & EH_STACK_INVALID)
138         ERR("Exception frame is not in stack limits => unable to dispatch exception.\n");
139     else if (rec->ExceptionCode == EXCEPTION_NONCONTINUABLE_EXCEPTION)
140         ERR("Process attempted to continue execution after noncontinuable exception.\n");
141     else
142         ERR("Unhandled exception code %lx flags %lx addr %p\n",
143             rec->ExceptionCode, rec->ExceptionFlags, rec->ExceptionAddress );
144     TerminateProcess( GetCurrentProcess(), 1 );
145 }
146
147
148 /***********************************************************************
149  *            RtlRaiseException  (NTDLL.464)
150  */
151 void WINAPI REGS_FUNC(RtlRaiseException)( EXCEPTION_RECORD *rec, CONTEXT *context )
152 {
153     PEXCEPTION_FRAME frame, dispatch, nested_frame;
154     EXCEPTION_RECORD newrec;
155     DWORD res;
156
157     TRACE( "code=%lx flags=%lx\n", rec->ExceptionCode, rec->ExceptionFlags );
158
159     if ((PROCESS_Current()->flags & PDB32_DEBUGGED) &&
160         (DEBUG_SendExceptionEvent( rec, TRUE ) == DBG_CONTINUE))
161         return;  /* continue execution */
162
163     if (debug_hook( rec, context, TRUE ) == DBG_CONTINUE)
164         return;  /* continue execution */
165
166     frame = NtCurrentTeb()->except;
167     nested_frame = NULL;
168     while (frame != (PEXCEPTION_FRAME)0xFFFFFFFF)
169     {
170         /* Check frame address */
171         if (((void*)frame < NtCurrentTeb()->stack_low) ||
172             ((void*)(frame+1) > NtCurrentTeb()->stack_top) ||
173             (int)frame & 3)
174         {
175             rec->ExceptionFlags |= EH_STACK_INVALID;
176             break;
177         }
178
179         /* Call handler */
180         res = EXC_CallHandler( rec, frame, context, &dispatch, frame->Handler, EXC_RaiseHandler );
181         if (frame == nested_frame)
182         {
183             /* no longer nested */
184             nested_frame = NULL;
185             rec->ExceptionFlags &= ~EH_NESTED_CALL;
186         }
187
188         switch(res)
189         {
190         case ExceptionContinueExecution:
191             if (!(rec->ExceptionFlags & EH_NONCONTINUABLE)) return;
192             newrec.ExceptionCode    = STATUS_NONCONTINUABLE_EXCEPTION;
193             newrec.ExceptionFlags   = EH_NONCONTINUABLE;
194             newrec.ExceptionRecord  = rec;
195             newrec.NumberParameters = 0;
196             RtlRaiseException( &newrec );  /* never returns */
197             break;
198         case ExceptionContinueSearch:
199             break;
200         case ExceptionNestedException:
201             if (nested_frame < dispatch) nested_frame = dispatch;
202             rec->ExceptionFlags |= EH_NESTED_CALL;
203             break;
204         default:
205             newrec.ExceptionCode    = STATUS_INVALID_DISPOSITION;
206             newrec.ExceptionFlags   = EH_NONCONTINUABLE;
207             newrec.ExceptionRecord  = rec;
208             newrec.NumberParameters = 0;
209             RtlRaiseException( &newrec );  /* never returns */
210             break;
211         }
212         frame = frame->Prev;
213     }
214     EXC_DefaultHandling( rec, context );
215 }
216
217
218 /*******************************************************************
219  *         RtlUnwind  (KERNEL32.590) (NTDLL.518)
220  */
221 void WINAPI REGS_FUNC(RtlUnwind)( PEXCEPTION_FRAME pEndFrame, LPVOID unusedEip, 
222                                   PEXCEPTION_RECORD pRecord, DWORD returnEax,
223                                   CONTEXT *context )
224 {
225     EXCEPTION_RECORD record, newrec;
226     PEXCEPTION_FRAME frame, dispatch;
227
228 #ifdef __i386__
229     context->Eax = returnEax;
230 #endif
231
232     /* build an exception record, if we do not have one */
233     if (!pRecord)
234     {
235         record.ExceptionCode    = STATUS_UNWIND;
236         record.ExceptionFlags   = 0;
237         record.ExceptionRecord  = NULL;
238         record.ExceptionAddress = GET_IP(context); 
239         record.NumberParameters = 0;
240         pRecord = &record;
241     }
242
243     pRecord->ExceptionFlags |= EH_UNWINDING | (pEndFrame ? 0 : EH_EXIT_UNWIND);
244
245     TRACE( "code=%lx flags=%lx\n", pRecord->ExceptionCode, pRecord->ExceptionFlags );
246
247     /* get chain of exception frames */
248     frame = NtCurrentTeb()->except;
249     while ((frame != (PEXCEPTION_FRAME)0xffffffff) && (frame != pEndFrame))
250     {
251         /* Check frame address */
252         if (pEndFrame && (frame > pEndFrame))
253         {
254             newrec.ExceptionCode    = STATUS_INVALID_UNWIND_TARGET;
255             newrec.ExceptionFlags   = EH_NONCONTINUABLE;
256             newrec.ExceptionRecord  = pRecord;
257             newrec.NumberParameters = 0;
258             RtlRaiseException( &newrec );  /* never returns */
259         }
260         if (((void*)frame < NtCurrentTeb()->stack_low) ||
261             ((void*)(frame+1) > NtCurrentTeb()->stack_top) ||
262             (int)frame & 3)
263         {
264             newrec.ExceptionCode    = STATUS_BAD_STACK;
265             newrec.ExceptionFlags   = EH_NONCONTINUABLE;
266             newrec.ExceptionRecord  = pRecord;
267             newrec.NumberParameters = 0;
268             RtlRaiseException( &newrec );  /* never returns */
269         }
270
271         /* Call handler */
272         switch(EXC_CallHandler( pRecord, frame, context, &dispatch,
273                                 frame->Handler, EXC_UnwindHandler ))
274         {
275         case ExceptionContinueSearch:
276             break;
277         case ExceptionCollidedUnwind:
278             frame = dispatch;
279             break;
280         default:
281             newrec.ExceptionCode    = STATUS_INVALID_DISPOSITION;
282             newrec.ExceptionFlags   = EH_NONCONTINUABLE;
283             newrec.ExceptionRecord  = pRecord;
284             newrec.NumberParameters = 0;
285             RtlRaiseException( &newrec );  /* never returns */
286             break;
287         }
288         frame = EXC_pop_frame( frame );
289     }
290 }
291
292
293 /*******************************************************************
294  *         NtRaiseException    (NTDLL.175)
295  *
296  * Real prototype:
297  *    DWORD WINAPI NtRaiseException( EXCEPTION_RECORD *rec, CONTEXT *ctx, BOOL first );
298  */
299 void WINAPI REGS_FUNC(NtRaiseException)( EXCEPTION_RECORD *rec, CONTEXT *ctx,
300                                          BOOL first, CONTEXT *context )
301 {
302     REGS_FUNC(RtlRaiseException)( rec, ctx );
303     *context = *ctx;
304 }
305
306
307 /***********************************************************************
308  *            RtlRaiseStatus  (NTDLL.465)
309  *
310  * Raise an exception with ExceptionCode = status
311  */
312 void WINAPI RtlRaiseStatus( NTSTATUS status )
313 {
314     EXCEPTION_RECORD ExceptionRec;
315
316     ExceptionRec.ExceptionCode    = status;
317     ExceptionRec.ExceptionFlags   = EH_NONCONTINUABLE;
318     ExceptionRec.ExceptionRecord  = NULL;
319     ExceptionRec.NumberParameters = 0;
320     RtlRaiseException( &ExceptionRec );
321 }
322
323
324 /***********************************************************************
325  *           DebugBreak   (KERNEL32.181)
326  */
327 void WINAPI REGS_FUNC(DebugBreak)( CONTEXT *context )
328 {
329     EXCEPTION_RECORD rec;
330
331     rec.ExceptionCode    = EXCEPTION_BREAKPOINT;
332     rec.ExceptionFlags   = 0;
333     rec.ExceptionRecord  = NULL;
334     rec.NumberParameters = 0;
335     REGS_FUNC(RtlRaiseException)( &rec, context );
336 }
337
338
339 /***********************************************************************
340  *           DebugBreak16   (KERNEL.203)
341  */
342 void WINAPI DebugBreak16( CONTEXT86 *context )
343 {
344 #ifdef __i386__
345     REGS_FUNC(DebugBreak)( context );
346 #endif  /* defined(__i386__) */
347 }