Use free Windows DDK and compile with latest MinGW releases.
[reactos.git] / reactos / ntoskrnl / ke / catch.c
1 /*
2 * ReactOS kernel
3 * Copyright (C) 2000 ReactOS Team
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
18 */
19 /* $Id: catch.c,v 1.23 2002/09/07 15:12:56 chorns Exp $
20 *
21 * PROJECT: ReactOS kernel
22 * FILE: ntoskrnl/ke/catch.c
23 * PURPOSE: Exception handling
24 * PROGRAMMER: David Welch (welch@mcmail.com)
25 */
26
27 /* INCLUDES *****************************************************************/
28
29 #include <ntoskrnl.h>
30
31 #define NDEBUG
32 #include <internal/debug.h>
33
34
35 /* FUNCTIONS ****************************************************************/
36
37 EXCEPTION_DISPOSITION
38 RtlpExecuteHandlerForException(
39 PEXCEPTION_RECORD ExceptionRecord,
40 PEXCEPTION_REGISTRATION ExceptionRegistration,
41 PCONTEXT Context,
42 PVOID DispatcherContext,
43 PEXCEPTION_HANDLER Handler);
44
45
46 #ifndef NDEBUG
47
48 VOID RtlpDumpExceptionRegistrations(VOID)
49 {
50 PEXCEPTION_REGISTRATION Current;
51 PKTHREAD Thread;
52
53 DbgPrint("Dumping exception registrations:\n");
54
55 Thread = KeGetCurrentThread();
56
57 assert(Thread);
58 assert(Thread->TrapFrame);
59
60 Current = Thread->TrapFrame->ExceptionList;
61
62 if ((ULONG_PTR)Current != -1)
63 {
64 while ((ULONG_PTR)Current != -1)
65 {
66 DbgPrint(" (0x%08X) HANDLER (0x%08X)\n", Current, Current->handler);
67 Current = Current->prev;
68 }
69 DbgPrint(" End-Of-List\n");
70 } else {
71 DbgPrint(" No exception registrations exists.\n");
72 }
73 }
74
75 #endif /* NDEBUG */
76
77 EXCEPTION_DISPOSITION
78 RtlpDispatchException(
79 PEXCEPTION_RECORD ExceptionRecord,
80 PCONTEXT Context)
81 {
82 PEXCEPTION_REGISTRATION RegistrationFrame;
83 DWORD DispatcherContext;
84 DWORD ReturnValue;
85 PKPCR KPCR;
86 PKTHREAD Thread;
87
88 DPRINT("RtlpDispatchException() called\n");
89 #ifndef NDEBUG
90 RtlpDumpExceptionRegistrations();
91 #endif /* NDEBUG */
92 Thread = KeGetCurrentThread();
93
94 DPRINT("Thread is 0x%X\n", Thread);
95
96 KPCR = KeGetCurrentKPCR();
97
98 RegistrationFrame = Thread->TrapFrame->ExceptionList;
99
100 DPRINT("RegistrationFrame is 0x%X\n", RegistrationFrame);
101
102 while ((ULONG_PTR)RegistrationFrame != -1)
103 {
104 EXCEPTION_RECORD ExceptionRecord2;
105 DWORD Temp = 0;
106 //PVOID RegistrationFrameEnd = (PVOID)RegistrationFrame + 8;
107
108 // Make sure the registration frame is located within the stack
109
110 DPRINT("Error checking\n");
111 #if 0
112 if (Thread->KernelStack > RegistrationFrameEnd)
113 {
114 ExceptionRecord->ExceptionFlags |= EH_STACK_INVALID;
115 return ExceptionDismiss;
116 }
117 // FIXME: Correct?
118 if (Thread->StackLimit < RegistrationFrameEnd)
119 {
120 ExceptionRecord->ExceptionFlags |= EH_STACK_INVALID;
121 return ExceptionDismiss;
122 }
123
124 // Make sure stack is DWORD aligned
125 if ((ULONG_PTR)RegistrationFrame & 3)
126 {
127 ExceptionRecord->ExceptionFlags |= EH_STACK_INVALID;
128 return ExceptionDismiss;
129 }
130 #endif
131
132 DPRINT("Calling handler at 0x%X\n", RegistrationFrame->handler);
133
134 ReturnValue = RtlpExecuteHandlerForException(
135 ExceptionRecord,
136 RegistrationFrame,
137 Context,
138 &DispatcherContext,
139 RegistrationFrame->handler);
140
141 if (RegistrationFrame == NULL)
142 {
143 ExceptionRecord->ExceptionFlags &= ~EH_NESTED_CALL; // Turn off flag
144 }
145
146 if (ReturnValue == ExceptionContinueExecution)
147 {
148 /* Copy the changed context back to the trap frame and return */
149 NtContinue(Context, FALSE);
150 return ExceptionContinueExecution;
151 }
152 else if (ReturnValue == ExceptionDismiss)
153 {
154 if (ExceptionRecord->ExceptionFlags & EH_NONCONTINUABLE)
155 {
156 ExceptionRecord2.ExceptionRecord = ExceptionRecord;
157 ExceptionRecord2.ExceptionCode = STATUS_NONCONTINUABLE_EXCEPTION;
158 ExceptionRecord2.ExceptionFlags = EH_NONCONTINUABLE;
159 ExceptionRecord2.NumberParameters = 0;
160 RtlRaiseException(&ExceptionRecord2);
161 }
162 /* Else continue search */
163 }
164 else if (ReturnValue == ExceptionNestedException)
165 {
166 ExceptionRecord->ExceptionFlags |= EH_EXIT_UNWIND;
167 if (DispatcherContext > Temp)
168 Temp = DispatcherContext;
169 }
170 else if (ReturnValue == ExceptionCollidedUnwind)
171 {
172 ExceptionRecord2.ExceptionRecord = ExceptionRecord;
173 ExceptionRecord2.ExceptionCode = STATUS_INVALID_DISPOSITION;
174 ExceptionRecord2.ExceptionFlags = EH_NONCONTINUABLE;
175 ExceptionRecord2.NumberParameters = 0;
176 RtlRaiseException(&ExceptionRecord2);
177 }
178
179 RegistrationFrame = RegistrationFrame->prev; // Go to previous frame
180 }
181
182 /* No exception handler will handle this exception */
183
184 return ExceptionDismiss;
185 }
186
187
188 VOID
189 KiDispatchException(PEXCEPTION_RECORD ExceptionRecord,
190 PCONTEXT Context,
191 PKTRAP_FRAME Tf,
192 KPROCESSOR_MODE PreviousMode,
193 BOOLEAN SearchFrames)
194 {
195 EXCEPTION_DISPOSITION Value;
196 CONTEXT TContext;
197
198 DPRINT("KiDispatchException() called\n");
199
200 /* PCR->KeExceptionDispatchCount++; */
201
202 if (Context == NULL)
203 {
204 TContext.ContextFlags = CONTEXT_FULL;
205 if (PreviousMode == UserMode)
206 {
207 TContext.ContextFlags = TContext.ContextFlags | CONTEXT_DEBUGGER;
208 }
209
210 KeTrapFrameToContext(Tf, &TContext);
211
212 Context = &TContext;
213 }
214 #if 0
215 if (ExceptionRecord->ExceptionCode == STATUS_BREAKPOINT)
216 {
217 Context->Eip--;
218 }
219 #endif
220 if (PreviousMode == UserMode)
221 {
222 if (SearchFrames)
223 {
224 PULONG Stack;
225 ULONG CDest;
226
227 /* FIXME: Give the kernel debugger a chance */
228
229 /* FIXME: Forward exception to user mode debugger */
230
231 /* FIXME: Check user mode stack for enough space */
232
233
234 /*
235 * Let usermode try and handle the exception
236 */
237 Tf->Esp = Tf->Esp -
238 (12 + sizeof(EXCEPTION_RECORD) + sizeof(CONTEXT));
239 Stack = (PULONG)Tf->Esp;
240 CDest = 3 + (ROUND_UP(sizeof(EXCEPTION_RECORD), 4) / 4);
241 /* Return address */
242 Stack[0] = 0;
243 /* Pointer to EXCEPTION_RECORD structure */
244 Stack[1] = (ULONG)&Stack[3];
245 /* Pointer to CONTEXT structure */
246 Stack[2] = (ULONG)&Stack[CDest];
247 memcpy(&Stack[3], ExceptionRecord, sizeof(EXCEPTION_RECORD));
248 memcpy(&Stack[CDest], Context, sizeof(CONTEXT));
249
250 Tf->Eip = (ULONG)LdrpGetSystemDllExceptionDispatcher();
251 return;
252 }
253
254 /* FIXME: Forward the exception to the debugger */
255
256 /* FIXME: Forward the exception to the process exception port */
257
258 /* Terminate the offending thread */
259 ZwTerminateThread(NtCurrentThread(), ExceptionRecord->ExceptionCode);
260
261 /* If that fails then bugcheck */
262 DbgPrint("Could not terminate thread\n");
263 KeBugCheck(KMODE_EXCEPTION_NOT_HANDLED);
264 }
265 else
266 {
267 KD_CONTINUE_TYPE Action;
268
269 /* PreviousMode == KernelMode */
270
271 if (KdDebuggerEnabled && KdDebugState & KD_DEBUG_GDB)
272 {
273 Action = KdEnterDebuggerException (ExceptionRecord, Context, Tf);
274 }
275 #ifdef KDBG
276 else if (KdDebuggerEnabled && KdDebugState & KD_DEBUG_KDB)
277 {
278 Action = KdbEnterDebuggerException (ExceptionRecord, Context, Tf);
279 }
280 #endif /* KDBG */
281 else
282 {
283 KeBugCheckWithTf (KMODE_EXCEPTION_NOT_HANDLED, 0, 0, 0, 0, Tf);
284 }
285 if (Action != kdHandleException)
286 {
287 Value = RtlpDispatchException (ExceptionRecord, Context);
288
289 DPRINT("RtlpDispatchException() returned with 0x%X\n", Value);
290 /*
291 * If RtlpDispatchException() does not handle the exception then
292 * bugcheck
293 */
294 if (Value != ExceptionContinueExecution)
295 {
296 KeBugCheck (KMODE_EXCEPTION_NOT_HANDLED);
297 }
298 }
299 else
300 {
301 KeContextToTrapFrame (Context, KeGetCurrentThread()->TrapFrame);
302 }
303 }
304 }
305
306 VOID STDCALL
307 ExRaiseAccessViolation (VOID)
308 {
309 ExRaiseStatus (STATUS_ACCESS_VIOLATION);
310 }
311
312 VOID STDCALL
313 ExRaiseDatatypeMisalignment (VOID)
314 {
315 ExRaiseStatus (STATUS_DATATYPE_MISALIGNMENT);
316 }
317
318 VOID STDCALL
319 ExRaiseStatus (IN NTSTATUS Status)
320 {
321 EXCEPTION_RECORD ExceptionRecord;
322
323 DPRINT("ExRaiseStatus(%x)\n", Status);
324
325 ExceptionRecord.ExceptionRecord = NULL;
326 ExceptionRecord.NumberParameters = 0;
327 ExceptionRecord.ExceptionCode = Status;
328 ExceptionRecord.ExceptionFlags = 0;
329
330 RtlRaiseException(&ExceptionRecord);
331 }
332
333
334 NTSTATUS STDCALL
335 NtRaiseException (IN PEXCEPTION_RECORD ExceptionRecord,
336 IN PCONTEXT Context,
337 IN BOOLEAN SearchFrames)
338 {
339 KiDispatchException(ExceptionRecord,
340 Context,
341 PsGetCurrentThread()->Tcb.TrapFrame,
342 ExGetPreviousMode(),
343 SearchFrames);
344 return(STATUS_SUCCESS);
345 }
346
347
348 VOID STDCALL
349 RtlRaiseException(PEXCEPTION_RECORD ExceptionRecord)
350 {
351 ZwRaiseException(ExceptionRecord, NULL, TRUE);
352 }
353
354
355 inline
356 EXCEPTION_DISPOSITION
357 RtlpExecuteHandler(
358 PEXCEPTION_RECORD ExceptionRecord,
359 PEXCEPTION_REGISTRATION ExceptionRegistration,
360 PCONTEXT Context,
361 PVOID DispatcherContext,
362 PEXCEPTION_HANDLER Handler,
363 PEXCEPTION_HANDLER RawHandler)
364 {
365 EXCEPTION_DISPOSITION Value;
366
367 // Set up an EXCEPTION_REGISTRATION
368 __asm__ ("pushl %0;pushl %%fs:0;movl %%esp,%%fs:0;" : : "g" (RawHandler));
369
370 // Invoke the exception callback function
371 Value = Handler(
372 ExceptionRecord,
373 ExceptionRegistration,
374 Context,
375 DispatcherContext);
376
377 // Remove the minimal EXCEPTION_REGISTRATION frame
378 //__asm__ ("movl %fs:0,%esp; popl %fs:0");
379
380 __asm__ ("movl (%%esp),%%eax;movl %%eax,%%fs:0;addl $8,%%esp;" : : : "%eax");
381
382 return Value;
383 }
384
385
386 EXCEPTION_DISPOSITION
387 RtlpExceptionHandler(
388 PEXCEPTION_RECORD ExceptionRecord,
389 PEXCEPTION_REGISTRATION ExceptionRegistration,
390 PCONTEXT Context,
391 PVOID DispatcherContext)
392 {
393 // If unwind flag set, return DISPOSITION_CONTINUE_SEARCH, else
394 // assign DispatcherContext context and return DISPOSITION_NESTED_EXCEPTION
395
396 if (ExceptionRecord->ExceptionFlags & EH_UNWINDING)
397 {
398 DPRINT("RtlpExceptionHandler(). Returning ExceptionContinueSearch\n");
399 return ExceptionContinueSearch;
400 }
401 else
402 {
403 DPRINT("RtlpExceptionHandler(). Returning ExceptionNestedException\n");
404 *(PEXCEPTION_REGISTRATION*)DispatcherContext = ExceptionRegistration->prev;
405 return ExceptionNestedException;
406 }
407 }
408
409
410 EXCEPTION_DISPOSITION
411 RtlpUnwindHandler(
412 PEXCEPTION_RECORD ExceptionRecord,
413 PEXCEPTION_REGISTRATION ExceptionRegistration,
414 PCONTEXT Context,
415 PVOID DispatcherContext)
416 {
417 // If unwind flag set, return DISPOSITION_CONTINUE_SEARCH, else
418 // assign DispatcherContext and return DISPOSITION_COLLIDED_UNWIND
419
420 if (ExceptionRecord->ExceptionFlags & EH_UNWINDING)
421 {
422 DPRINT("RtlpUnwindHandler(). Returning ExceptionContinueSearch\n");
423 return ExceptionContinueSearch;
424 }
425 else
426 {
427 DPRINT("RtlpUnwindHandler(). Returning ExceptionCollidedUnwind\n");
428 *(PEXCEPTION_REGISTRATION*)DispatcherContext = ExceptionRegistration->prev;
429 return ExceptionCollidedUnwind;
430 }
431 }
432
433
434 EXCEPTION_DISPOSITION
435 RtlpExecuteHandlerForException(
436 PEXCEPTION_RECORD ExceptionRecord,
437 PEXCEPTION_REGISTRATION ExceptionRegistration,
438 PCONTEXT Context,
439 PVOID DispatcherContext,
440 PEXCEPTION_HANDLER Handler)
441 {
442 return RtlpExecuteHandler(
443 ExceptionRecord,
444 ExceptionRegistration,
445 Context,
446 DispatcherContext,
447 Handler,
448 (PEXCEPTION_HANDLER)RtlpExceptionHandler);
449 }
450
451
452 EXCEPTION_DISPOSITION
453 RtlpExecuteHandlerForUnwind(
454 PEXCEPTION_RECORD ExceptionRecord,
455 PEXCEPTION_REGISTRATION ExceptionRegistration,
456 PCONTEXT Context,
457 PVOID DispatcherContext,
458 PEXCEPTION_HANDLER Handler)
459 {
460 return RtlpExecuteHandler(
461 ExceptionRecord,
462 ExceptionRegistration,
463 Context,
464 DispatcherContext,
465 Handler,
466 (PEXCEPTION_HANDLER)RtlpUnwindHandler);
467 }
468
469
470 VOID STDCALL
471 RtlUnwind(
472 PEXCEPTION_REGISTRATION RegistrationFrame,
473 PVOID ReturnAddress,
474 PEXCEPTION_RECORD ExceptionRecord,
475 DWORD EaxValue)
476 {
477 PEXCEPTION_REGISTRATION ERHead;
478 PEXCEPTION_RECORD pExceptRec;
479 EXCEPTION_RECORD TempER;
480 CONTEXT Context;
481 //PVOID Stack;
482 PKTHREAD Thread;
483
484 DPRINT("RtlUnwind() called. RegistrationFrame 0x%X\n", RegistrationFrame);
485 #ifndef NDEBUG
486 RtlpDumpExceptionRegistrations();
487 #endif /* NDEBUG */
488 Thread = KeGetCurrentThread();
489
490 ERHead = Thread->TrapFrame->ExceptionList;
491
492 if (ExceptionRecord == NULL) // The normal case
493 {
494 pExceptRec = &TempER;
495
496 pExceptRec->ExceptionFlags = 0;
497 pExceptRec->ExceptionCode = STATUS_UNWIND;
498 pExceptRec->ExceptionRecord = NULL;
499 // FIXME: Find out if NT retrieves the return address from the stack instead
500 pExceptRec->ExceptionAddress = ReturnAddress;
501 //pExceptRec->ExceptionInformation[0] = 0;
502 }
503
504 if (RegistrationFrame)
505 pExceptRec->ExceptionFlags |= EH_UNWINDING;
506 else
507 pExceptRec->ExceptionFlags |= (EH_UNWINDING|EH_EXIT_UNWIND);
508
509 Context.ContextFlags =
510 (CONTEXT_i386 | CONTEXT_CONTROL | CONTEXT_INTEGER | CONTEXT_SEGMENTS);
511
512 KeTrapFrameToContext(Thread->TrapFrame, &Context);
513
514 Context.Esp += 0x10;
515 Context.Eax = EaxValue;
516
517 // Begin traversing the list of EH_REGISTRATION
518 while ((ULONG_PTR)ERHead != -1)
519 {
520 EXCEPTION_RECORD er2;
521
522 DPRINT("ERHead 0x%X\n", ERHead);
523
524 if (ERHead == RegistrationFrame)
525 {
526 DPRINT("Continueing execution\n");
527 NtContinue(&Context, FALSE);
528 return;
529 }
530 else
531 {
532 // If there's an exception frame, but it's lower on the stack
533 // then the head of the exception list, something's wrong!
534 if (RegistrationFrame && (RegistrationFrame <= ERHead))
535 {
536 DPRINT("The exception frame is bad\n");
537
538 // Generate an exception to bail out
539 er2.ExceptionRecord = pExceptRec;
540 er2.NumberParameters = 0;
541 er2.ExceptionCode = STATUS_INVALID_UNWIND_TARGET;
542 er2.ExceptionFlags = EH_NONCONTINUABLE;
543
544 RtlRaiseException(&er2);
545 }
546 }
547
548 #if 0
549 Stack = ERHead + sizeof(EXCEPTION_REGISTRATION);
550 if ( (KPCR->StackBase <= (PVOID)ERHead ) // Make sure that ERHead
551 && (KPCR->StackLimit >= (PVOID)Stack ) // is in range, and a multiple
552 && (0 == ((ULONG_PTR)ERHead & 3)) ) // of 4 (i.e., sane)
553 {
554 #else
555 if (1) {
556 #endif
557 PEXCEPTION_REGISTRATION NewERHead;
558 PEXCEPTION_REGISTRATION pCurrExceptReg;
559 EXCEPTION_DISPOSITION ReturnValue;
560
561 DPRINT("Executing handler at 0x%X for unwind\n", ERHead->handler);
562
563 ReturnValue = RtlpExecuteHandlerForUnwind(
564 pExceptRec,
565 ERHead,
566 &Context,
567 &NewERHead,
568 ERHead->handler);
569
570 DPRINT("Handler at 0x%X returned 0x%X\n", ERHead->handler, ReturnValue);
571
572 if (ReturnValue != ExceptionContinueSearch)
573 {
574 if (ReturnValue != ExceptionCollidedUnwind)
575 {
576 DPRINT("Bad return value\n");
577
578 er2.ExceptionRecord = pExceptRec;
579 er2.NumberParameters = 0;
580 er2.ExceptionCode = STATUS_INVALID_DISPOSITION;
581 er2.ExceptionFlags = EH_NONCONTINUABLE;
582
583 RtlRaiseException(&er2);
584 } else
585 ERHead = NewERHead;
586 }
587
588 pCurrExceptReg = ERHead;
589 ERHead = ERHead->prev;
590
591 DPRINT("New ERHead is 0x%X\n", ERHead);
592
593 DPRINT("Setting exception registration at 0x%X as current\n",
594 RegistrationFrame->prev);
595
596 // Unlink the exception handler
597 KeGetCurrentKPCR()->Tib.ExceptionList = RegistrationFrame->prev;
598 }
599 else // The stack looks goofy! Raise an exception to bail out
600 {
601 DPRINT("Bad stack\n");
602
603 er2.ExceptionRecord = pExceptRec;
604 er2.NumberParameters = 0;
605 er2.ExceptionCode = STATUS_BAD_STACK;
606 er2.ExceptionFlags = EH_NONCONTINUABLE;
607
608 RtlRaiseException(&er2);
609 }
610 }
611
612 // If we get here, we reached the end of the EXCEPTION_REGISTRATION list.
613 // This shouldn't happen normally.
614
615 DPRINT("Ran out of exception registrations. RegistrationFrame is (0x%X)\n",
616 RegistrationFrame);
617
618 if ((ULONG_PTR)RegistrationFrame == -1)
619 NtContinue(&Context, FALSE);
620 else
621 NtRaiseException(pExceptRec, &Context, 0);
622 }
623
624 /* EOF */