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