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