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