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