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