Implemented stripped down SEH functionality
[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   if (ExceptionRecord == NULL) // The normal case
366   {
367         DPRINT("ExceptionRecord == NULL (normal)\n");
368
369     pExceptRec = &TempER;
370     pExceptRec->ExceptionFlags = 0;
371     pExceptRec->ExceptionCode = STATUS_UNWIND;
372     pExceptRec->ExceptionRecord = NULL;
373     pExceptRec->ExceptionAddress = ReturnAddress;
374     pExceptRec->ExceptionInformation[0] = 0;
375   }
376
377   if (RegistrationFrame)
378     pExceptRec->ExceptionFlags |= EXCEPTION_UNWINDING;
379   else
380     pExceptRec->ExceptionFlags |= (EXCEPTION_UNWINDING|EXCEPTION_EXIT_UNWIND);
381
382 #ifndef NDEBUG
383   DPRINT("ExceptionFlags == 0x%x:\n", pExceptRec->ExceptionFlags);
384   if (pExceptRec->ExceptionFlags & EXCEPTION_UNWINDING)
385   {
386           DPRINT("  * EXCEPTION_UNWINDING (0x%x)\n", EXCEPTION_UNWINDING);
387   }
388   if (pExceptRec->ExceptionFlags & EXCEPTION_EXIT_UNWIND)
389   {
390           DPRINT("  * EXCEPTION_EXIT_UNWIND (0x%x)\n", EXCEPTION_EXIT_UNWIND);
391   }
392 #endif /* NDEBUG */
393
394 #ifndef LIBCAPTIVE
395   Context.ContextFlags =
396     (CONTEXT_i386 | CONTEXT_CONTROL | CONTEXT_INTEGER | CONTEXT_SEGMENTS);
397
398   SehpCaptureContext(&Context);
399
400   DPRINT("Context.Eip = 0x%.08x\n", Context.Eip);
401   DPRINT("Context.Ebp = 0x%.08x\n", Context.Ebp);
402   DPRINT("Context.Esp = 0x%.08x\n", Context.Esp);
403
404   Context.Esp += 0x10;
405   Context.Eax = EaxValue;
406 #endif /* LIBCAPTIVE */
407  
408   // Begin traversing the list of EXCEPTION_REGISTRATION
409   while ((ULONG_PTR)ERHead != (ULONG_PTR)-1)
410   {
411     EXCEPTION_RECORD er2;
412  
413     DPRINT("ERHead 0x%X\n", ERHead);
414
415     if (ERHead == RegistrationFrame)
416     {
417       DPRINT("Continueing execution\n");
418 #ifndef LIBCAPTIVE
419       NtContinue(&Context, FALSE);
420 #else /* !LIBCAPTIVE */
421       /* FIXME: What to do there? */
422 #endif /* LIBCAPTIVE */
423       return;
424     }
425     else
426     {
427       // If there's an exception frame, but it's lower on the stack
428       // than the head of the exception list, something's wrong!
429       if (RegistrationFrame && (RegistrationFrame <= ERHead))
430       {
431         DPRINT("The exception frame is bad\n");
432
433         // Generate an exception to bail out
434         er2.ExceptionRecord = pExceptRec;
435         er2.NumberParameters = 0;
436         er2.ExceptionCode = STATUS_INVALID_UNWIND_TARGET;
437         er2.ExceptionFlags = EXCEPTION_NONCONTINUABLE;    
438  
439         RtlRaiseException(&er2);
440       }
441     }
442  
443 #if 0
444     Stack = ERHead + sizeof(EXCEPTION_REGISTRATION);
445     if ( (Teb->Tib.StackBase <= (PVOID)ERHead )      // Make sure that ERHead
446       && (Teb->Tib.->StackLimit >= (PVOID)Stack )      // is in range, and a multiple
447       && (0 == ((ULONG_PTR)ERHead & 3)) )         // of 4 (i.e., sane)
448     {
449 #else
450     if (1) {
451 #endif
452       PEXCEPTION_REGISTRATION NewERHead;
453       PEXCEPTION_REGISTRATION pCurrExceptReg;
454       EXCEPTION_DISPOSITION ReturnValue;
455   
456       DPRINT("Executing handler at 0x%X for unwind\n", ERHead->handler);
457
458       ReturnValue = RtlpExecuteHandlerForUnwind(
459         pExceptRec,
460         ERHead,
461 #ifndef LIBCAPTIVE
462         &Context,
463 #else /* !LIBCAPTIVE */
464         NULL,
465 #endif /* LIBCAPTIVE */
466         &NewERHead,
467         ERHead->handler);
468  
469       DPRINT("Handler at 0x%X returned 0x%X\n", ERHead->handler, ReturnValue);
470
471       if (ReturnValue != ExceptionContinueSearch)
472       {
473         if (ReturnValue != ExceptionCollidedUnwind)
474         {
475           DPRINT("Bad return value\n");
476
477           er2.ExceptionRecord = pExceptRec;
478           er2.NumberParameters = 0;
479           er2.ExceptionCode = STATUS_INVALID_DISPOSITION;
480           er2.ExceptionFlags = EXCEPTION_NONCONTINUABLE;    
481  
482           RtlRaiseException(&er2);
483         }
484         else
485         {
486           ERHead = NewERHead;
487         }
488       }
489  
490       pCurrExceptReg = ERHead;
491       ERHead = ERHead->prev;
492
493       DPRINT("New ERHead is 0x%X\n", ERHead);
494
495       DPRINT("Setting exception registration at 0x%X as current\n",
496         RegistrationFrame->prev);
497
498       // Unlink the exception handler
499       SehpSetExceptionList(RegistrationFrame->prev);
500     }
501     else // The stack looks goofy! Raise an exception to bail out
502     {
503       DPRINT("Bad stack\n");
504
505       er2.ExceptionRecord = pExceptRec;
506       er2.NumberParameters = 0;
507       er2.ExceptionCode = STATUS_BAD_STACK;
508       er2.ExceptionFlags = EXCEPTION_NONCONTINUABLE;    
509  
510       RtlRaiseException(&er2);
511     }
512   }
513  
514   // If we get here, we reached the end of the EXCEPTION_REGISTRATION list.
515   // This shouldn't happen normally.
516
517   DPRINT("Ran out of exception registrations. RegistrationFrame is (0x%X)\n",
518     RegistrationFrame);
519
520 #ifndef LIBCAPTIVE
521   if ((ULONG_PTR)RegistrationFrame == (ULONG_PTR)-1)
522     NtContinue(&Context, FALSE);
523   else
524     NtRaiseException(pExceptRec, &Context, 0);
525 #else /* !LIBCAPTIVE */
526   KeBugCheck(0);
527 #endif /* LIBCAPTIVE */
528 }
529
530 /* EOF */