migrate substitution keywords to SVN
[reactos.git] / reactos / 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 else
303 {
304 pExceptRec = ExceptionRecord;
305 }
306
307 if (RegistrationFrame)
308 pExceptRec->ExceptionFlags |= EXCEPTION_UNWINDING;
309 else
310 pExceptRec->ExceptionFlags |= (EXCEPTION_UNWINDING|EXCEPTION_EXIT_UNWIND);
311
312 #ifndef NDEBUG
313 DPRINT("ExceptionFlags == 0x%x:\n", pExceptRec->ExceptionFlags);
314 if (pExceptRec->ExceptionFlags & EXCEPTION_UNWINDING)
315 {
316 DPRINT(" * EXCEPTION_UNWINDING (0x%x)\n", EXCEPTION_UNWINDING);
317 }
318 if (pExceptRec->ExceptionFlags & EXCEPTION_EXIT_UNWIND)
319 {
320 DPRINT(" * EXCEPTION_EXIT_UNWIND (0x%x)\n", EXCEPTION_EXIT_UNWIND);
321 }
322 #endif /* NDEBUG */
323
324 Context.ContextFlags =
325 (CONTEXT_i386 | CONTEXT_CONTROL | CONTEXT_INTEGER | CONTEXT_SEGMENTS);
326
327 SehpCaptureContext(&Context);
328
329 DPRINT("Context.Eip = 0x%.08x\n", Context.Eip);
330 DPRINT("Context.Ebp = 0x%.08x\n", Context.Ebp);
331 DPRINT("Context.Esp = 0x%.08x\n", Context.Esp);
332
333 Context.Esp += 0x10;
334 Context.Eax = EaxValue;
335
336 // Begin traversing the list of EXCEPTION_REGISTRATION
337 while ((ULONG_PTR)ERHead != -1)
338 {
339 EXCEPTION_RECORD er2;
340
341 DPRINT("ERHead 0x%X\n", ERHead);
342
343 if (ERHead == RegistrationFrame)
344 {
345 DPRINT("Continueing execution\n");
346 SehpContinue(&Context, FALSE);
347 return;
348 }
349 else
350 {
351 // If there's an exception frame, but it's lower on the stack
352 // than the head of the exception list, something's wrong!
353 if (RegistrationFrame && (RegistrationFrame <= ERHead))
354 {
355 DPRINT("The exception frame is bad\n");
356
357 // Generate an exception to bail out
358 er2.ExceptionRecord = pExceptRec;
359 er2.NumberParameters = 0;
360 er2.ExceptionCode = STATUS_INVALID_UNWIND_TARGET;
361 er2.ExceptionFlags = EXCEPTION_NONCONTINUABLE;
362
363 RtlRaiseException(&er2);
364 }
365 }
366
367 #if 0
368 Stack = ERHead + sizeof(EXCEPTION_REGISTRATION);
369 if ( (Teb->Tib.StackBase <= (PVOID)ERHead ) // Make sure that ERHead
370 && (Teb->Tib.->StackLimit >= (PVOID)Stack ) // is in range, and a multiple
371 && (0 == ((ULONG_PTR)ERHead & 3)) ) // of 4 (i.e., sane)
372 {
373 #else
374 if (1) {
375 #endif
376 PEXCEPTION_REGISTRATION NewERHead;
377 PEXCEPTION_REGISTRATION pCurrExceptReg;
378 EXCEPTION_DISPOSITION ReturnValue;
379
380 DPRINT("Executing handler at 0x%X for unwind\n", ERHead->handler);
381
382 ReturnValue = RtlpExecuteHandlerForUnwind(
383 pExceptRec,
384 ERHead,
385 &Context,
386 &NewERHead,
387 ERHead->handler);
388
389 DPRINT("Handler at 0x%X returned 0x%X\n", ERHead->handler, ReturnValue);
390
391 if (ReturnValue != ExceptionContinueSearch)
392 {
393 if (ReturnValue != ExceptionCollidedUnwind)
394 {
395 DPRINT("Bad return value\n");
396
397 er2.ExceptionRecord = pExceptRec;
398 er2.NumberParameters = 0;
399 er2.ExceptionCode = STATUS_INVALID_DISPOSITION;
400 er2.ExceptionFlags = EXCEPTION_NONCONTINUABLE;
401
402 RtlRaiseException(&er2);
403 }
404 else
405 {
406 ERHead = NewERHead;
407 }
408 }
409
410 pCurrExceptReg = ERHead;
411 ERHead = ERHead->prev;
412
413 DPRINT("New ERHead is 0x%X\n", ERHead);
414
415 DPRINT("Setting exception registration at 0x%X as current\n",
416 RegistrationFrame->prev);
417
418 // Unlink the exception handler
419 SehpSetExceptionList(RegistrationFrame->prev);
420 }
421 else // The stack looks goofy! Raise an exception to bail out
422 {
423 DPRINT("Bad stack\n");
424
425 er2.ExceptionRecord = pExceptRec;
426 er2.NumberParameters = 0;
427 er2.ExceptionCode = STATUS_BAD_STACK;
428 er2.ExceptionFlags = EXCEPTION_NONCONTINUABLE;
429
430 RtlRaiseException(&er2);
431 }
432 }
433
434 // If we get here, we reached the end of the EXCEPTION_REGISTRATION list.
435 // This shouldn't happen normally.
436
437 DPRINT("Ran out of exception registrations. RegistrationFrame is (0x%X)\n",
438 RegistrationFrame);
439
440 if ((ULONG_PTR)RegistrationFrame == -1)
441 SehpContinue(&Context, FALSE);
442 else
443 NtRaiseException(pExceptRec, &Context, 0);
444 }
445
446 /* EOF */