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