:pserver:cvsanon@mok.lvcm.com:/CVS/ReactOS reactos
[reactos.git] / lib / ntdll / rtl / i386 / exception.c
1 /* $Id$
2  *
3  * COPYRIGHT:         See COPYING in the top level directory
4  * PROJECT:           ReactOS kernel
5  * PURPOSE:           User-mode exception support for IA-32
6  * FILE:              lib/ntdll/rtl/i386/exception.c
7  * PROGRAMER:         Casper S. Hornstrup (chorns@users.sourceforge.net)
8  */
9
10 /* INCLUDES *****************************************************************/
11
12 #include <ddk/ntddk.h>
13 #include <windows.h>
14 #include <string.h>
15
16 #define NDEBUG
17 #include <debug.h>
18
19 /* FUNCTIONS ***************************************************************/
20
21 /* Implemented in except.s */
22
23 VOID
24 RtlpCaptureContext(PCONTEXT pContext);
25
26 /* Macros that will help streamline the SEH implementations for
27    kernel mode and user mode */
28
29 #define SehpGetStackLimits(StackBase, StackLimit) \
30 { \
31         (*(StackBase)) = NtCurrentTeb()->Tib->StackBase; \
32         (*(StackLimit)) = NtCurrentTeb()->Tib->StackLimit; \
33 }
34
35 #define SehpGetExceptionList() \
36         (PEXCEPTION_REGISTRATION)(NtCurrentTeb()->Tib.ExceptionList)
37
38 #define SehpSetExceptionList(NewExceptionList) \
39         NtCurrentTeb()->Tib.ExceptionList = (PVOID)(NewExceptionList)
40
41 #define SehpCaptureContext(Context) \
42 { \
43         RtlpCaptureContext(Context); \
44 }
45
46 /*** Code below this line is shared with ntoskrnl/rtl/i386/exception.c - please keep in sync ***/
47
48 VOID STDCALL
49 AsmDebug(ULONG Value)
50 {
51   DbgPrint("Value 0x%.08x\n", Value);
52 }
53
54
55 /* Declare a few prototypes for the functions in except.s */
56
57 EXCEPTION_DISPOSITION
58 RtlpExecuteHandlerForException(
59   PEXCEPTION_RECORD ExceptionRecord,
60   PEXCEPTION_REGISTRATION RegistrationFrame,
61   PCONTEXT Context,
62   PVOID DispatcherContext,
63   PEXCEPTION_HANDLER ExceptionHandler);
64
65 EXCEPTION_DISPOSITION
66 RtlpExecuteHandlerForUnwind(
67   PEXCEPTION_RECORD ExceptionRecord,
68   PEXCEPTION_REGISTRATION RegistrationFrame,
69   PCONTEXT Context,
70   PVOID DispatcherContext,
71   PEXCEPTION_HANDLER ExceptionHandler);
72
73
74 #ifndef NDEBUG
75
76 VOID RtlpDumpExceptionRegistrations(VOID)
77 {
78   PEXCEPTION_REGISTRATION Current;
79
80   DbgPrint("Dumping exception registrations:\n");
81
82   Current = SehpGetExceptionList();
83
84   if ((ULONG_PTR)Current != -1)
85   {
86     while ((ULONG_PTR)Current != -1)
87     {
88       DbgPrint("   (0x%08X)   HANDLER (0x%08X)\n", Current, Current->handler);
89       Current = Current->prev;
90     }
91     DbgPrint("   End-Of-List\n");
92   } else {
93     DbgPrint("   No exception registrations exists.\n");
94   }
95 }
96
97 #endif /* NDEBUG */
98
99 ULONG
100 RtlpDispatchException(IN PEXCEPTION_RECORD  ExceptionRecord,
101         IN PCONTEXT  Context)
102 {
103   PEXCEPTION_REGISTRATION RegistrationFrame;
104   DWORD DispatcherContext;
105   DWORD ReturnValue;
106
107   DPRINT("RtlpDispatchException()\n");
108
109 #ifndef NDEBUG
110   RtlpDumpExceptionRegistrations();
111 #endif /* NDEBUG */
112
113   RegistrationFrame = SehpGetExceptionList();
114  
115   DPRINT("RegistrationFrame is 0x%X\n", RegistrationFrame);
116
117   while ((ULONG_PTR)RegistrationFrame != -1)
118   {
119     EXCEPTION_RECORD ExceptionRecord2;
120     DWORD Temp = 0;
121     //PVOID RegistrationFrameEnd = (PVOID)RegistrationFrame + 8;
122
123     // Make sure the registration frame is located within the stack
124
125     DPRINT("Error checking\n");
126 #if 0
127     if (Teb->Tib.StackBase > RegistrationFrameEnd)
128     {
129       DPRINT("Teb->Tib.StackBase (0x%.08x) > RegistrationFrameEnd (0x%.08x)\n",
130         Teb->Tib.StackBase, RegistrationFrameEnd);
131       ExceptionRecord->ExceptionFlags |= EXCEPTION_STACK_INVALID;
132       return ExceptionContinueExecution;
133     }
134     // FIXME: Stack top, correct?
135     if (Teb->Tib.StackLimit < RegistrationFrameEnd)
136     {
137       DPRINT("Teb->Tib.StackLimit (0x%.08x) > RegistrationFrameEnd (0x%.08x)\n",
138         Teb->Tib.StackLimit, RegistrationFrameEnd);
139       ExceptionRecord->ExceptionFlags |= EXCEPTION_STACK_INVALID;
140       return ExceptionContinueExecution;
141     }
142  
143     // Make sure stack is DWORD aligned
144     if ((ULONG_PTR)RegistrationFrame & 3)
145     {
146       DPRINT("RegistrationFrameEnd (0x%.08x) is not DWORD aligned.\n",
147         RegistrationFrameEnd);
148       ExceptionRecord->ExceptionFlags |= EXCEPTION_STACK_INVALID;
149       return ExceptionContinueExecution;
150     }
151 #endif
152
153 #if 0
154     /* FIXME: */
155     if (someFlag)
156       RtlpLogLastExceptionDisposition( hLog, retValue );
157 #endif
158
159     DPRINT("Calling handler at 0x%X\n", RegistrationFrame->handler);
160     DPRINT("ExceptionRecord 0x%X\n", ExceptionRecord);
161     DPRINT("RegistrationFrame 0x%X\n", RegistrationFrame);
162     DPRINT("Context 0x%X\n", Context);
163     DPRINT("&DispatcherContext 0x%X\n", &DispatcherContext);
164
165     ReturnValue = RtlpExecuteHandlerForException(
166       ExceptionRecord,
167       RegistrationFrame,
168       Context,
169       &DispatcherContext,
170       RegistrationFrame->handler);
171
172     DPRINT("Exception handler said 0x%X\n", ReturnValue);
173         DPRINT("RegistrationFrame == 0x%.08x\n", RegistrationFrame);
174         {
175                 PULONG sp = (PULONG)((PVOID)RegistrationFrame - 0x08);
176                 DPRINT("StandardESP == 0x%.08x\n", sp[0]);
177                 DPRINT("Exception Pointers == 0x%.08x\n", sp[1]);
178                 DPRINT("PrevFrame == 0x%.08x\n", sp[2]);
179                 DPRINT("Handler == 0x%.08x\n", sp[3]);
180                 DPRINT("ScopeTable == 0x%.08x\n", sp[4]);
181                 DPRINT("TryLevel == 0x%.08x\n", sp[5]);
182                 DPRINT("EBP == 0x%.08x\n", sp[6]);
183         }
184
185     if (RegistrationFrame == NULL)
186     {
187       ExceptionRecord->ExceptionFlags &= ~EXCEPTION_NESTED_CALL;  // Turn off flag
188     }
189
190     if (ReturnValue == ExceptionContinueExecution)
191     {
192       DPRINT("ReturnValue == ExceptionContinueExecution\n");
193       if (ExceptionRecord->ExceptionFlags & EXCEPTION_NONCONTINUABLE)
194       {
195         DPRINT("(ExceptionRecord->ExceptionFlags & EXCEPTION_NONCONTINUABLE) == TRUE\n");
196
197         ExceptionRecord2.ExceptionRecord = ExceptionRecord;
198         ExceptionRecord2.ExceptionCode = STATUS_NONCONTINUABLE_EXCEPTION;
199         ExceptionRecord2.ExceptionFlags = EXCEPTION_NONCONTINUABLE;
200         ExceptionRecord2.NumberParameters = 0;
201         RtlRaiseException(&ExceptionRecord2);
202       }
203       else
204       {
205         /* Copy the (possibly changed) context back to the trap frame and return */
206         NtContinue(Context, FALSE);
207         return ExceptionContinueExecution;
208       }
209     }
210     else if (ReturnValue == ExceptionContinueSearch)
211     {
212       DPRINT("ReturnValue == ExceptionContinueSearch\n");
213
214       /* Nothing to do here */
215     }
216     else if (ReturnValue == ExceptionNestedException)
217     {
218       DPRINT("ReturnValue == ExceptionNestedException\n");
219
220       ExceptionRecord->ExceptionFlags |= EXCEPTION_EXIT_UNWIND;
221       if (DispatcherContext > Temp)
222           {
223           Temp = DispatcherContext;
224           }
225     }
226     else /* if (ReturnValue == ExceptionCollidedUnwind) */
227     {
228       DPRINT("ReturnValue == ExceptionCollidedUnwind or unknown\n");
229
230       ExceptionRecord2.ExceptionRecord = ExceptionRecord;
231       ExceptionRecord2.ExceptionCode = STATUS_INVALID_DISPOSITION;
232       ExceptionRecord2.ExceptionFlags = EXCEPTION_NONCONTINUABLE;
233       ExceptionRecord2.NumberParameters = 0;
234       RtlRaiseException(&ExceptionRecord2);
235     }
236
237     RegistrationFrame = RegistrationFrame->prev;  // Go to previous frame
238   }
239  
240   /* No exception handler will handle this exception */
241
242   DPRINT("RtlpDispatchException(): Return ExceptionContinueExecution\n");
243
244   return ExceptionContinueExecution;  
245 }
246
247 VOID STDCALL
248 RtlRaiseStatus(NTSTATUS Status)
249 {
250   EXCEPTION_RECORD ExceptionRecord;
251
252   DPRINT("RtlRaiseStatus(Status 0x%.08x)\n", Status);
253
254   ExceptionRecord.ExceptionCode    = Status;
255   ExceptionRecord.ExceptionRecord  = NULL;
256   ExceptionRecord.NumberParameters = 0;
257   ExceptionRecord.ExceptionFlags   = EXCEPTION_NONCONTINUABLE;
258   RtlRaiseException (& ExceptionRecord);
259 }
260
261 VOID STDCALL
262 RtlUnwind(PEXCEPTION_REGISTRATION RegistrationFrame,
263   PVOID ReturnAddress,
264   PEXCEPTION_RECORD ExceptionRecord,
265   DWORD EaxValue)
266 {
267   PEXCEPTION_REGISTRATION ERHead;
268   PEXCEPTION_RECORD pExceptRec;
269   EXCEPTION_RECORD TempER;    
270   CONTEXT Context;
271
272   DPRINT("RtlUnwind(). RegistrationFrame 0x%X\n", RegistrationFrame);
273
274 #ifndef NDEBUG
275   RtlpDumpExceptionRegistrations();
276 #endif /* NDEBUG */
277
278   ERHead = SehpGetExceptionList();
279  
280   DPRINT("ERHead is 0x%X\n", ERHead);
281
282   if (ExceptionRecord == NULL) // The normal case
283   {
284         DPRINT("ExceptionRecord == NULL (normal)\n");
285
286     pExceptRec = &TempER;
287     pExceptRec->ExceptionFlags = 0;
288     pExceptRec->ExceptionCode = STATUS_UNWIND;
289     pExceptRec->ExceptionRecord = NULL;
290     pExceptRec->ExceptionAddress = ReturnAddress;
291     pExceptRec->ExceptionInformation[0] = 0;
292   }
293
294   if (RegistrationFrame)
295     pExceptRec->ExceptionFlags |= EXCEPTION_UNWINDING;
296   else
297     pExceptRec->ExceptionFlags |= (EXCEPTION_UNWINDING|EXCEPTION_EXIT_UNWIND);
298
299 #ifndef NDEBUG
300   DPRINT("ExceptionFlags == 0x%x:\n", pExceptRec->ExceptionFlags);
301   if (pExceptRec->ExceptionFlags & EXCEPTION_UNWINDING)
302   {
303           DPRINT("  * EXCEPTION_UNWINDING (0x%x)\n", EXCEPTION_UNWINDING);
304   }
305   if (pExceptRec->ExceptionFlags & EXCEPTION_EXIT_UNWIND)
306   {
307           DPRINT("  * EXCEPTION_EXIT_UNWIND (0x%x)\n", EXCEPTION_EXIT_UNWIND);
308   }
309 #endif /* NDEBUG */
310
311   Context.ContextFlags =
312     (CONTEXT_i386 | CONTEXT_CONTROL | CONTEXT_INTEGER | CONTEXT_SEGMENTS);
313
314   SehpCaptureContext(&Context);
315
316   DPRINT("Context.Eip = 0x%.08x\n", Context.Eip);
317   DPRINT("Context.Ebp = 0x%.08x\n", Context.Ebp);
318   DPRINT("Context.Esp = 0x%.08x\n", Context.Esp);
319
320   Context.Esp += 0x10;
321   Context.Eax = EaxValue;
322  
323   // Begin traversing the list of EXCEPTION_REGISTRATION
324   while ((ULONG_PTR)ERHead != -1)
325   {
326     EXCEPTION_RECORD er2;
327  
328     DPRINT("ERHead 0x%X\n", ERHead);
329
330     if (ERHead == RegistrationFrame)
331     {
332       DPRINT("Continueing execution\n");
333       NtContinue(&Context, FALSE);
334       return;
335     }
336     else
337     {
338       // If there's an exception frame, but it's lower on the stack
339       // than the head of the exception list, something's wrong!
340       if (RegistrationFrame && (RegistrationFrame <= ERHead))
341       {
342         DPRINT("The exception frame is bad\n");
343
344         // Generate an exception to bail out
345         er2.ExceptionRecord = pExceptRec;
346         er2.NumberParameters = 0;
347         er2.ExceptionCode = STATUS_INVALID_UNWIND_TARGET;
348         er2.ExceptionFlags = EXCEPTION_NONCONTINUABLE;    
349  
350         RtlRaiseException(&er2);
351       }
352     }
353  
354 #if 0
355     Stack = ERHead + sizeof(EXCEPTION_REGISTRATION);
356     if ( (Teb->Tib.StackBase <= (PVOID)ERHead )      // Make sure that ERHead
357       && (Teb->Tib.->StackLimit >= (PVOID)Stack )      // is in range, and a multiple
358       && (0 == ((ULONG_PTR)ERHead & 3)) )         // of 4 (i.e., sane)
359     {
360 #else
361     if (1) {
362 #endif
363       PEXCEPTION_REGISTRATION NewERHead;
364       PEXCEPTION_REGISTRATION pCurrExceptReg;
365       EXCEPTION_DISPOSITION ReturnValue;
366   
367       DPRINT("Executing handler at 0x%X for unwind\n", ERHead->handler);
368
369       ReturnValue = RtlpExecuteHandlerForUnwind(
370         pExceptRec,
371         ERHead,
372         &Context,
373         &NewERHead,
374         ERHead->handler);
375  
376       DPRINT("Handler at 0x%X returned 0x%X\n", ERHead->handler, ReturnValue);
377
378       if (ReturnValue != ExceptionContinueSearch)
379       {
380         if (ReturnValue != ExceptionCollidedUnwind)
381         {
382           DPRINT("Bad return value\n");
383
384           er2.ExceptionRecord = pExceptRec;
385           er2.NumberParameters = 0;
386           er2.ExceptionCode = STATUS_INVALID_DISPOSITION;
387           er2.ExceptionFlags = EXCEPTION_NONCONTINUABLE;    
388  
389           RtlRaiseException(&er2);
390         }
391         else
392         {
393           ERHead = NewERHead;
394         }
395       }
396  
397       pCurrExceptReg = ERHead;
398       ERHead = ERHead->prev;
399
400       DPRINT("New ERHead is 0x%X\n", ERHead);
401
402       DPRINT("Setting exception registration at 0x%X as current\n",
403         RegistrationFrame->prev);
404
405       // Unlink the exception handler
406       SehpSetExceptionList(RegistrationFrame->prev);
407     }
408     else // The stack looks goofy! Raise an exception to bail out
409     {
410       DPRINT("Bad stack\n");
411
412       er2.ExceptionRecord = pExceptRec;
413       er2.NumberParameters = 0;
414       er2.ExceptionCode = STATUS_BAD_STACK;
415       er2.ExceptionFlags = EXCEPTION_NONCONTINUABLE;    
416  
417       RtlRaiseException(&er2);
418     }
419   }
420  
421   // If we get here, we reached the end of the EXCEPTION_REGISTRATION list.
422   // This shouldn't happen normally.
423
424   DPRINT("Ran out of exception registrations. RegistrationFrame is (0x%X)\n",
425     RegistrationFrame);
426
427   if ((ULONG_PTR)RegistrationFrame == -1)
428     NtContinue(&Context, FALSE);
429   else
430     NtRaiseException(pExceptRec, &Context, 0);
431 }
432
433 /* EOF */