Start using the exported TEB structure from winternl.h where
[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
28 #include "windef.h"
29 #include "thread.h"
30 #include "winternl.h"
31 #include "wine/exception.h"
32 #include "wine/server.h"
33 #include "wine/debug.h"
34 #include "excpt.h"
35
36 WINE_DEFAULT_DEBUG_CHANNEL(seh);
37
38 /* Exception record for handling exceptions happening inside exception handlers */
39 typedef struct
40 {
41     EXCEPTION_REGISTRATION_RECORD frame;
42     EXCEPTION_REGISTRATION_RECORD *prevFrame;
43 } EXC_NESTED_FRAME;
44
45 #ifdef __i386__
46 # define GET_IP(context) ((LPVOID)(context)->Eip)
47 #elif defined(__sparc__)
48 # define GET_IP(context) ((LPVOID)(context)->pc)
49 #elif defined(__powerpc__)
50 # define GET_IP(context) ((LPVOID)(context)->Iar)
51 #else
52 # error You must define GET_IP for this CPU
53 #endif
54
55 void WINAPI EXC_RtlRaiseException( PEXCEPTION_RECORD, PCONTEXT );
56 void WINAPI EXC_RtlUnwind( PEXCEPTION_REGISTRATION_RECORD, LPVOID,
57                            PEXCEPTION_RECORD, DWORD, PCONTEXT );
58 void WINAPI EXC_NtRaiseException( PEXCEPTION_RECORD, PCONTEXT,
59                                   BOOL, PCONTEXT );
60
61 /*******************************************************************
62  *         EXC_RaiseHandler
63  *
64  * Handler for exceptions happening inside a handler.
65  */
66 static DWORD EXC_RaiseHandler( EXCEPTION_RECORD *rec, EXCEPTION_REGISTRATION_RECORD *frame,
67                                CONTEXT *context, EXCEPTION_REGISTRATION_RECORD **dispatcher )
68 {
69     if (rec->ExceptionFlags & (EH_UNWINDING | EH_EXIT_UNWIND))
70         return ExceptionContinueSearch;
71     /* We shouldn't get here so we store faulty frame in dispatcher */
72     *dispatcher = ((EXC_NESTED_FRAME*)frame)->prevFrame;
73     return ExceptionNestedException;
74 }
75
76
77 /*******************************************************************
78  *         EXC_UnwindHandler
79  *
80  * Handler for exceptions happening inside an unwind handler.
81  */
82 static DWORD EXC_UnwindHandler( EXCEPTION_RECORD *rec, EXCEPTION_REGISTRATION_RECORD *frame,
83                                 CONTEXT *context, EXCEPTION_REGISTRATION_RECORD **dispatcher )
84 {
85     if (!(rec->ExceptionFlags & (EH_UNWINDING | EH_EXIT_UNWIND)))
86         return ExceptionContinueSearch;
87     /* We shouldn't get here so we store faulty frame in dispatcher */
88     *dispatcher = ((EXC_NESTED_FRAME*)frame)->prevFrame;
89     return ExceptionCollidedUnwind;
90 }
91
92
93 /*******************************************************************
94  *         EXC_CallHandler
95  *
96  * Call an exception handler, setting up an exception frame to catch exceptions
97  * happening during the handler execution.
98  * Please do not change the first 4 parameters order in any way - some exceptions handlers
99  * rely on Base Pointer (EBP) to have a fixed position related to the exception frame
100  */
101 static DWORD EXC_CallHandler( EXCEPTION_RECORD *record, EXCEPTION_REGISTRATION_RECORD *frame,
102                               CONTEXT *context, EXCEPTION_REGISTRATION_RECORD **dispatcher,
103                               PEXCEPTION_HANDLER handler, PEXCEPTION_HANDLER nested_handler)
104 {
105     EXC_NESTED_FRAME newframe;
106     DWORD ret;
107
108     newframe.frame.Handler = nested_handler;
109     newframe.prevFrame     = frame;
110     __wine_push_frame( &newframe.frame );
111     TRACE( "calling handler at %p code=%lx flags=%lx\n",
112            handler, record->ExceptionCode, record->ExceptionFlags );
113     ret = handler( record, frame, context, dispatcher );
114     TRACE( "handler returned %lx\n", ret );
115     __wine_pop_frame( &newframe.frame );
116     return ret;
117 }
118
119
120 /**********************************************************************
121  *           send_debug_event
122  *
123  * Send an EXCEPTION_DEBUG_EVENT event to the debugger.
124  */
125 static int send_debug_event( EXCEPTION_RECORD *rec, int first_chance, CONTEXT *context )
126 {
127     int ret;
128     HANDLE handle = 0;
129
130     SERVER_START_REQ( queue_exception_event )
131     {
132         req->first   = first_chance;
133         wine_server_add_data( req, context, sizeof(*context) );
134         wine_server_add_data( req, rec, sizeof(*rec) );
135         if (!wine_server_call( req )) handle = reply->handle;
136     }
137     SERVER_END_REQ;
138     if (!handle) return 0;  /* no debugger present or other error */
139
140     /* No need to wait on the handle since the process gets suspended
141      * once the event is passed to the debugger, so when we get back
142      * here the event has been continued already.
143      */
144     SERVER_START_REQ( get_exception_status )
145     {
146         req->handle = handle;
147         wine_server_set_reply( req, context, sizeof(*context) );
148         wine_server_call( req );
149         ret = reply->status;
150     }
151     SERVER_END_REQ;
152     NtClose( handle );
153     return ret;
154 }
155
156
157 /*******************************************************************
158  *         EXC_DefaultHandling
159  *
160  * Default handling for exceptions. Called when we didn't find a suitable handler.
161  */
162 static void EXC_DefaultHandling( EXCEPTION_RECORD *rec, CONTEXT *context )
163 {
164     if (send_debug_event( rec, FALSE, context ) == DBG_CONTINUE) return;  /* continue execution */
165
166     if (rec->ExceptionFlags & EH_STACK_INVALID)
167         ERR("Exception frame is not in stack limits => unable to dispatch exception.\n");
168     else if (rec->ExceptionCode == EXCEPTION_NONCONTINUABLE_EXCEPTION)
169         ERR("Process attempted to continue execution after noncontinuable exception.\n");
170     else
171         ERR("Unhandled exception code %lx flags %lx addr %p\n",
172             rec->ExceptionCode, rec->ExceptionFlags, rec->ExceptionAddress );
173     NtTerminateProcess( NtCurrentProcess(), 1 );
174 }
175
176
177 /***********************************************************************
178  *              RtlRaiseException (NTDLL.@)
179  */
180 DEFINE_REGS_ENTRYPOINT_1( RtlRaiseException, EXC_RtlRaiseException, EXCEPTION_RECORD * );
181 void WINAPI EXC_RtlRaiseException( EXCEPTION_RECORD *rec, CONTEXT *context )
182 {
183     PEXCEPTION_REGISTRATION_RECORD frame, dispatch, nested_frame;
184     EXCEPTION_RECORD newrec;
185     DWORD res, c;
186
187     TRACE( "code=%lx flags=%lx addr=%p\n", rec->ExceptionCode, rec->ExceptionFlags, rec->ExceptionAddress );
188     for (c=0; c<rec->NumberParameters; c++) TRACE(" info[%ld]=%08lx\n", c, rec->ExceptionInformation[c]);
189     if (rec->ExceptionCode == EXCEPTION_WINE_STUB)
190         FIXME( "call to unimplemented function %s.%s\n",
191                (char*)rec->ExceptionInformation[0], (char*)rec->ExceptionInformation[1] );
192
193     if (send_debug_event( rec, TRUE, context ) == DBG_CONTINUE) return;  /* continue execution */
194
195     SIGNAL_Unblock(); /* we may be in a signal handler, and exception handlers may jump out */
196
197     frame = NtCurrentTeb()->Tib.ExceptionList;
198     nested_frame = NULL;
199     while (frame != (PEXCEPTION_REGISTRATION_RECORD)~0UL)
200     {
201         /* Check frame address */
202         if (((void*)frame < NtCurrentTeb()->Tib.StackLimit) ||
203             ((void*)(frame+1) > NtCurrentTeb()->Tib.StackBase) ||
204             (int)frame & 3)
205         {
206             rec->ExceptionFlags |= EH_STACK_INVALID;
207             break;
208         }
209
210         /* Call handler */
211         res = EXC_CallHandler( rec, frame, context, &dispatch, frame->Handler, EXC_RaiseHandler );
212         if (frame == nested_frame)
213         {
214             /* no longer nested */
215             nested_frame = NULL;
216             rec->ExceptionFlags &= ~EH_NESTED_CALL;
217         }
218
219         switch(res)
220         {
221         case ExceptionContinueExecution:
222             if (!(rec->ExceptionFlags & EH_NONCONTINUABLE)) return;
223             newrec.ExceptionCode    = STATUS_NONCONTINUABLE_EXCEPTION;
224             newrec.ExceptionFlags   = EH_NONCONTINUABLE;
225             newrec.ExceptionRecord  = rec;
226             newrec.NumberParameters = 0;
227             RtlRaiseException( &newrec );  /* never returns */
228             break;
229         case ExceptionContinueSearch:
230             break;
231         case ExceptionNestedException:
232             if (nested_frame < dispatch) nested_frame = dispatch;
233             rec->ExceptionFlags |= EH_NESTED_CALL;
234             break;
235         default:
236             newrec.ExceptionCode    = STATUS_INVALID_DISPOSITION;
237             newrec.ExceptionFlags   = EH_NONCONTINUABLE;
238             newrec.ExceptionRecord  = rec;
239             newrec.NumberParameters = 0;
240             RtlRaiseException( &newrec );  /* never returns */
241             break;
242         }
243         frame = frame->Prev;
244     }
245     EXC_DefaultHandling( rec, context );
246 }
247
248
249 /*******************************************************************
250  *              RtlUnwind (NTDLL.@)
251  */
252 DEFINE_REGS_ENTRYPOINT_4( RtlUnwind, EXC_RtlUnwind,
253                           PVOID, PVOID, PEXCEPTION_RECORD, PVOID );
254 void WINAPI EXC_RtlUnwind( PEXCEPTION_REGISTRATION_RECORD pEndFrame, LPVOID unusedEip,
255                            PEXCEPTION_RECORD pRecord, DWORD returnEax,
256                            CONTEXT *context )
257 {
258     EXCEPTION_RECORD record, newrec;
259     PEXCEPTION_REGISTRATION_RECORD frame, dispatch;
260
261 #ifdef __i386__
262     context->Eax = returnEax;
263 #endif
264
265     /* build an exception record, if we do not have one */
266     if (!pRecord)
267     {
268         record.ExceptionCode    = STATUS_UNWIND;
269         record.ExceptionFlags   = 0;
270         record.ExceptionRecord  = NULL;
271         record.ExceptionAddress = GET_IP(context);
272         record.NumberParameters = 0;
273         pRecord = &record;
274     }
275
276     pRecord->ExceptionFlags |= EH_UNWINDING | (pEndFrame ? 0 : EH_EXIT_UNWIND);
277
278     TRACE( "code=%lx flags=%lx\n", pRecord->ExceptionCode, pRecord->ExceptionFlags );
279
280     /* get chain of exception frames */
281     frame = NtCurrentTeb()->Tib.ExceptionList;
282     while ((frame != (PEXCEPTION_REGISTRATION_RECORD)~0UL) && (frame != pEndFrame))
283     {
284         /* Check frame address */
285         if (pEndFrame && (frame > pEndFrame))
286         {
287             newrec.ExceptionCode    = STATUS_INVALID_UNWIND_TARGET;
288             newrec.ExceptionFlags   = EH_NONCONTINUABLE;
289             newrec.ExceptionRecord  = pRecord;
290             newrec.NumberParameters = 0;
291             RtlRaiseException( &newrec );  /* never returns */
292         }
293         if (((void*)frame < NtCurrentTeb()->Tib.StackLimit) ||
294             ((void*)(frame+1) > NtCurrentTeb()->Tib.StackBase) ||
295             (int)frame & 3)
296         {
297             newrec.ExceptionCode    = STATUS_BAD_STACK;
298             newrec.ExceptionFlags   = EH_NONCONTINUABLE;
299             newrec.ExceptionRecord  = pRecord;
300             newrec.NumberParameters = 0;
301             RtlRaiseException( &newrec );  /* never returns */
302         }
303
304         /* Call handler */
305         switch(EXC_CallHandler( pRecord, frame, context, &dispatch,
306                                 frame->Handler, EXC_UnwindHandler ))
307         {
308         case ExceptionContinueSearch:
309             break;
310         case ExceptionCollidedUnwind:
311             frame = dispatch;
312             break;
313         default:
314             newrec.ExceptionCode    = STATUS_INVALID_DISPOSITION;
315             newrec.ExceptionFlags   = EH_NONCONTINUABLE;
316             newrec.ExceptionRecord  = pRecord;
317             newrec.NumberParameters = 0;
318             RtlRaiseException( &newrec );  /* never returns */
319             break;
320         }
321         frame = __wine_pop_frame( frame );
322     }
323 }
324
325
326 /*******************************************************************
327  *              NtRaiseException (NTDLL.@)
328  */
329 DEFINE_REGS_ENTRYPOINT_3( NtRaiseException, EXC_NtRaiseException,
330                           EXCEPTION_RECORD *, CONTEXT *, BOOL );
331 void WINAPI EXC_NtRaiseException( EXCEPTION_RECORD *rec, CONTEXT *ctx,
332                                   BOOL first, CONTEXT *context )
333 {
334     EXC_RtlRaiseException( rec, ctx );
335     *context = *ctx;
336 }
337
338
339 /***********************************************************************
340  *            RtlRaiseStatus  (NTDLL.@)
341  *
342  * Raise an exception with ExceptionCode = status
343  */
344 void WINAPI RtlRaiseStatus( NTSTATUS status )
345 {
346     EXCEPTION_RECORD ExceptionRec;
347
348     ExceptionRec.ExceptionCode    = status;
349     ExceptionRec.ExceptionFlags   = EH_NONCONTINUABLE;
350     ExceptionRec.ExceptionRecord  = NULL;
351     ExceptionRec.NumberParameters = 0;
352     RtlRaiseException( &ExceptionRec );
353 }
354
355
356 /*************************************************************
357  *            __wine_exception_handler (NTDLL.@)
358  *
359  * Exception handler for exception blocks declared in Wine code.
360  */
361 DWORD __wine_exception_handler( EXCEPTION_RECORD *record, EXCEPTION_REGISTRATION_RECORD *frame,
362                                 CONTEXT *context, EXCEPTION_REGISTRATION_RECORD **pdispatcher )
363 {
364     __WINE_FRAME *wine_frame = (__WINE_FRAME *)frame;
365
366     if (record->ExceptionFlags & (EH_UNWINDING | EH_EXIT_UNWIND | EH_NESTED_CALL))
367         return ExceptionContinueSearch;
368     if (wine_frame->u.filter)
369     {
370         EXCEPTION_POINTERS ptrs;
371         ptrs.ExceptionRecord = record;
372         ptrs.ContextRecord = context;
373         switch(wine_frame->u.filter( &ptrs ))
374         {
375         case EXCEPTION_CONTINUE_SEARCH:
376             return ExceptionContinueSearch;
377         case EXCEPTION_CONTINUE_EXECUTION:
378             return ExceptionContinueExecution;
379         case EXCEPTION_EXECUTE_HANDLER:
380             break;
381         default:
382             MESSAGE( "Invalid return value from exception filter\n" );
383             assert( FALSE );
384         }
385     }
386     /* hack to make GetExceptionCode() work in handler */
387     wine_frame->ExceptionCode   = record->ExceptionCode;
388     wine_frame->ExceptionRecord = wine_frame;
389
390     RtlUnwind( frame, 0, record, 0 );
391     __wine_pop_frame( frame );
392     longjmp( wine_frame->jmp, 1 );
393 }
394
395
396 /*************************************************************
397  *            __wine_finally_handler (NTDLL.@)
398  *
399  * Exception handler for try/finally blocks declared in Wine code.
400  */
401 DWORD __wine_finally_handler( EXCEPTION_RECORD *record, EXCEPTION_REGISTRATION_RECORD *frame,
402                               CONTEXT *context, EXCEPTION_REGISTRATION_RECORD **pdispatcher )
403 {
404     if (record->ExceptionFlags & (EH_UNWINDING | EH_EXIT_UNWIND))
405     {
406         __WINE_FRAME *wine_frame = (__WINE_FRAME *)frame;
407         wine_frame->u.finally_func( FALSE );
408     }
409     return ExceptionContinueSearch;
410 }