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