3 * Copyright (C) 2000 ReactOS Team
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.
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.
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.
19 /* $Id: catch.c,v 1.24 2002/09/08 10:23:28 chorns Exp $
21 * PROJECT: ReactOS kernel
22 * FILE: ntoskrnl/ke/catch.c
23 * PURPOSE: Exception handling
24 * PROGRAMMER: David Welch (welch@mcmail.com)
27 /* INCLUDES *****************************************************************/
29 #include <ddk/ntddk.h>
31 #include <internal/ke.h>
32 #include <internal/ldr.h>
33 #include <internal/ps.h>
34 #include <internal/kd.h>
37 #include <internal/debug.h>
39 /* FUNCTIONS ****************************************************************/
42 RtlpExecuteHandlerForException(
43 PEXCEPTION_RECORD ExceptionRecord
,
44 PEXCEPTION_REGISTRATION ExceptionRegistration
,
46 PVOID DispatcherContext
,
47 PEXCEPTION_HANDLER Handler
);
52 VOID
RtlpDumpExceptionRegistrations(VOID
)
54 PEXCEPTION_REGISTRATION Current
;
57 DbgPrint("Dumping exception registrations:\n");
59 Thread
= KeGetCurrentThread();
62 assert(Thread
->TrapFrame
);
64 Current
= Thread
->TrapFrame
->ExceptionList
;
66 if ((ULONG_PTR
)Current
!= -1)
68 while ((ULONG_PTR
)Current
!= -1)
70 DbgPrint(" (0x%08X) HANDLER (0x%08X)\n", Current
, Current
->handler
);
71 Current
= Current
->prev
;
73 DbgPrint(" End-Of-List\n");
75 DbgPrint(" No exception registrations exists.\n");
82 RtlpDispatchException(
83 PEXCEPTION_RECORD ExceptionRecord
,
86 PEXCEPTION_REGISTRATION RegistrationFrame
;
87 DWORD DispatcherContext
;
92 DPRINT("RtlpDispatchException() called\n");
94 RtlpDumpExceptionRegistrations();
96 Thread
= KeGetCurrentThread();
98 DPRINT("Thread is 0x%X\n", Thread
);
100 KPCR
= KeGetCurrentKPCR();
102 RegistrationFrame
= Thread
->TrapFrame
->ExceptionList
;
104 DPRINT("RegistrationFrame is 0x%X\n", RegistrationFrame
);
106 while ((ULONG_PTR
)RegistrationFrame
!= -1)
108 EXCEPTION_RECORD ExceptionRecord2
;
110 //PVOID RegistrationFrameEnd = (PVOID)RegistrationFrame + 8;
112 // Make sure the registration frame is located within the stack
114 DPRINT("Error checking\n");
116 if (Thread
->KernelStack
> RegistrationFrameEnd
)
118 ExceptionRecord
->ExceptionFlags
|= EXCEPTION_STACK_INVALID
;
119 return ExceptionDismiss
;
122 if (Thread
->StackLimit
< RegistrationFrameEnd
)
124 ExceptionRecord
->ExceptionFlags
|= EXCEPTION_STACK_INVALID
;
125 return ExceptionDismiss
;
128 // Make sure stack is DWORD aligned
129 if ((ULONG_PTR
)RegistrationFrame
& 3)
131 ExceptionRecord
->ExceptionFlags
|= EXCEPTION_STACK_INVALID
;
132 return ExceptionDismiss
;
136 DPRINT("Calling handler at 0x%X\n", RegistrationFrame
->handler
);
138 ReturnValue
= RtlpExecuteHandlerForException(
143 RegistrationFrame
->handler
);
145 if (RegistrationFrame
== NULL
)
147 ExceptionRecord
->ExceptionFlags
&= ~EXCEPTION_NESTED_CALL
; // Turn off flag
150 if (ReturnValue
== ExceptionContinueExecution
)
152 /* Copy the changed context back to the trap frame and return */
153 NtContinue(Context
, FALSE
);
154 return ExceptionContinueExecution
;
156 else if (ReturnValue
== ExceptionDismiss
)
158 if (ExceptionRecord
->ExceptionFlags
& EXCEPTION_NONCONTINUABLE
)
160 ExceptionRecord2
.ExceptionRecord
= ExceptionRecord
;
161 ExceptionRecord2
.ExceptionCode
= STATUS_NONCONTINUABLE_EXCEPTION
;
162 ExceptionRecord2
.ExceptionFlags
= EXCEPTION_NONCONTINUABLE
;
163 ExceptionRecord2
.NumberParameters
= 0;
164 RtlRaiseException(&ExceptionRecord2
);
166 /* Else continue search */
168 else if (ReturnValue
== ExceptionNestedException
)
170 ExceptionRecord
->ExceptionFlags
|= EXCEPTION_EXIT_UNWIND
;
171 if (DispatcherContext
> Temp
)
172 Temp
= DispatcherContext
;
174 else if (ReturnValue
== ExceptionCollidedUnwind
)
176 ExceptionRecord2
.ExceptionRecord
= ExceptionRecord
;
177 ExceptionRecord2
.ExceptionCode
= STATUS_INVALID_DISPOSITION
;
178 ExceptionRecord2
.ExceptionFlags
= EXCEPTION_NONCONTINUABLE
;
179 ExceptionRecord2
.NumberParameters
= 0;
180 RtlRaiseException(&ExceptionRecord2
);
183 RegistrationFrame
= RegistrationFrame
->prev
; // Go to previous frame
186 /* No exception handler will handle this exception */
188 return ExceptionDismiss
;
193 KiDispatchException(PEXCEPTION_RECORD ExceptionRecord
,
196 KPROCESSOR_MODE PreviousMode
,
197 BOOLEAN SearchFrames
)
199 EXCEPTION_DISPOSITION Value
;
202 DPRINT("KiDispatchException() called\n");
204 /* PCR->KeExceptionDispatchCount++; */
208 TContext
.ContextFlags
= CONTEXT_FULL
;
209 if (PreviousMode
== UserMode
)
211 TContext
.ContextFlags
= TContext
.ContextFlags
| CONTEXT_DEBUGGER
;
214 KeTrapFrameToContext(Tf
, &TContext
);
219 if (ExceptionRecord
->ExceptionCode
== STATUS_BREAKPOINT
)
224 if (PreviousMode
== UserMode
)
231 /* FIXME: Give the kernel debugger a chance */
233 /* FIXME: Forward exception to user mode debugger */
235 /* FIXME: Check user mode stack for enough space */
239 * Let usermode try and handle the exception
242 (12 + sizeof(EXCEPTION_RECORD
) + sizeof(CONTEXT
));
243 Stack
= (PULONG
)Tf
->Esp
;
244 CDest
= 3 + (ROUND_UP(sizeof(EXCEPTION_RECORD
), 4) / 4);
247 /* Pointer to EXCEPTION_RECORD structure */
248 Stack
[1] = (ULONG
)&Stack
[3];
249 /* Pointer to CONTEXT structure */
250 Stack
[2] = (ULONG
)&Stack
[CDest
];
251 memcpy(&Stack
[3], ExceptionRecord
, sizeof(EXCEPTION_RECORD
));
252 memcpy(&Stack
[CDest
], Context
, sizeof(CONTEXT
));
254 Tf
->Eip
= (ULONG
)LdrpGetSystemDllExceptionDispatcher();
258 /* FIXME: Forward the exception to the debugger */
260 /* FIXME: Forward the exception to the process exception port */
262 /* Terminate the offending thread */
263 ZwTerminateThread(NtCurrentThread(), ExceptionRecord
->ExceptionCode
);
265 /* If that fails then bugcheck */
266 DbgPrint("Could not terminate thread\n");
267 KeBugCheck(KMODE_EXCEPTION_NOT_HANDLED
);
271 KD_CONTINUE_TYPE Action
;
273 /* PreviousMode == KernelMode */
275 if (KdDebuggerEnabled
&& KdDebugState
& KD_DEBUG_GDB
)
277 Action
= KdEnterDebuggerException (ExceptionRecord
, Context
, Tf
);
280 else if (KdDebuggerEnable
&& KdDebugState
& KD_DEBUG_KDB
)
282 Action
= KdbEnterDebuggerException (ExceptionRecord
, Context
, Tf
);
287 KeBugCheckWithTf (KMODE_EXCEPTION_NOT_HANDLED
, 0, 0, 0, 0, Tf
);
289 if (Action
!= kdHandleException
)
291 Value
= RtlpDispatchException (ExceptionRecord
, Context
);
293 DPRINT("RtlpDispatchException() returned with 0x%X\n", Value
);
295 * If RtlpDispatchException() does not handle the exception then
298 if (Value
!= ExceptionContinueExecution
)
300 KeBugCheck (KMODE_EXCEPTION_NOT_HANDLED
);
305 KeContextToTrapFrame (Context
, KeGetCurrentThread()->TrapFrame
);
311 ExRaiseAccessViolation (VOID
)
313 ExRaiseStatus (STATUS_ACCESS_VIOLATION
);
317 ExRaiseDatatypeMisalignment (VOID
)
319 ExRaiseStatus (STATUS_DATATYPE_MISALIGNMENT
);
323 ExRaiseStatus (IN NTSTATUS Status
)
325 EXCEPTION_RECORD ExceptionRecord
;
327 DPRINT("ExRaiseStatus(%x)\n", Status
);
329 ExceptionRecord
.ExceptionRecord
= NULL
;
330 ExceptionRecord
.NumberParameters
= 0;
331 ExceptionRecord
.ExceptionCode
= Status
;
332 ExceptionRecord
.ExceptionFlags
= 0;
334 RtlRaiseException(&ExceptionRecord
);
339 NtRaiseException (IN PEXCEPTION_RECORD ExceptionRecord
,
341 IN BOOLEAN SearchFrames
)
343 KiDispatchException(ExceptionRecord
,
345 PsGetCurrentThread()->Tcb
.TrapFrame
,
348 return(STATUS_SUCCESS
);
353 RtlRaiseException(PEXCEPTION_RECORD ExceptionRecord
)
355 ZwRaiseException(ExceptionRecord
, NULL
, TRUE
);
360 EXCEPTION_DISPOSITION
362 PEXCEPTION_RECORD ExceptionRecord
,
363 PEXCEPTION_REGISTRATION ExceptionRegistration
,
365 PVOID DispatcherContext
,
366 PEXCEPTION_HANDLER Handler
,
367 PEXCEPTION_HANDLER RawHandler
)
369 EXCEPTION_DISPOSITION Value
;
371 // Set up an EXCEPTION_REGISTRATION
372 __asm__ ("pushl %0;pushl %%fs:0;movl %%esp,%%fs:0;" : : "g" (RawHandler
));
374 // Invoke the exception callback function
377 ExceptionRegistration
,
381 // Remove the minimal EXCEPTION_REGISTRATION frame
382 //__asm__ ("movl %fs:0,%esp; popl %fs:0");
384 __asm__ ("movl (%%esp),%%eax;movl %%eax,%%fs:0;addl $8,%%esp;" : : : "%eax");
390 EXCEPTION_DISPOSITION
391 RtlpExceptionHandler(
392 PEXCEPTION_RECORD ExceptionRecord
,
393 PEXCEPTION_REGISTRATION ExceptionRegistration
,
395 PVOID DispatcherContext
)
397 // If unwind flag set, return DISPOSITION_CONTINUE_SEARCH, else
398 // assign DispatcherContext context and return DISPOSITION_NESTED_EXCEPTION
400 if (ExceptionRecord
->ExceptionFlags
& EXCEPTION_UNWINDING
)
402 DPRINT("RtlpExceptionHandler(). Returning ExceptionContinueSearch\n");
403 return ExceptionContinueSearch
;
407 DPRINT("RtlpExceptionHandler(). Returning ExceptionNestedException\n");
408 *(PEXCEPTION_REGISTRATION
*)DispatcherContext
= ExceptionRegistration
->prev
;
409 return ExceptionNestedException
;
414 EXCEPTION_DISPOSITION
416 PEXCEPTION_RECORD ExceptionRecord
,
417 PEXCEPTION_REGISTRATION ExceptionRegistration
,
419 PVOID DispatcherContext
)
421 // If unwind flag set, return DISPOSITION_CONTINUE_SEARCH, else
422 // assign DispatcherContext and return DISPOSITION_COLLIDED_UNWIND
424 if (ExceptionRecord
->ExceptionFlags
& EXCEPTION_UNWINDING
)
426 DPRINT("RtlpUnwindHandler(). Returning ExceptionContinueSearch\n");
427 return ExceptionContinueSearch
;
431 DPRINT("RtlpUnwindHandler(). Returning ExceptionCollidedUnwind\n");
432 *(PEXCEPTION_REGISTRATION
*)DispatcherContext
= ExceptionRegistration
->prev
;
433 return ExceptionCollidedUnwind
;
438 EXCEPTION_DISPOSITION
439 RtlpExecuteHandlerForException(
440 PEXCEPTION_RECORD ExceptionRecord
,
441 PEXCEPTION_REGISTRATION ExceptionRegistration
,
443 PVOID DispatcherContext
,
444 PEXCEPTION_HANDLER Handler
)
446 return RtlpExecuteHandler(
448 ExceptionRegistration
,
452 RtlpExceptionHandler
);
456 EXCEPTION_DISPOSITION
457 RtlpExecuteHandlerForUnwind(
458 PEXCEPTION_RECORD ExceptionRecord
,
459 PEXCEPTION_REGISTRATION ExceptionRegistration
,
461 PVOID DispatcherContext
,
462 PEXCEPTION_HANDLER Handler
)
464 return RtlpExecuteHandler(
466 ExceptionRegistration
,
476 PEXCEPTION_REGISTRATION RegistrationFrame
,
478 PEXCEPTION_RECORD ExceptionRecord
,
481 PEXCEPTION_REGISTRATION ERHead
;
482 PEXCEPTION_RECORD pExceptRec
;
483 EXCEPTION_RECORD TempER
;
488 DPRINT("RtlUnwind() called. RegistrationFrame 0x%X\n", RegistrationFrame
);
490 RtlpDumpExceptionRegistrations();
492 Thread
= KeGetCurrentThread();
494 ERHead
= Thread
->TrapFrame
->ExceptionList
;
496 if (ExceptionRecord
== NULL
) // The normal case
498 pExceptRec
= &TempER
;
500 pExceptRec
->ExceptionFlags
= 0;
501 pExceptRec
->ExceptionCode
= STATUS_UNWIND
;
502 pExceptRec
->ExceptionRecord
= NULL
;
503 // FIXME: Find out if NT retrieves the return address from the stack instead
504 pExceptRec
->ExceptionAddress
= ReturnAddress
;
505 //pExceptRec->ExceptionInformation[0] = 0;
508 if (RegistrationFrame
)
509 pExceptRec
->ExceptionFlags
|= EXCEPTION_UNWINDING
;
511 pExceptRec
->ExceptionFlags
|= (EXCEPTION_UNWINDING
|EXCEPTION_EXIT_UNWIND
);
513 Context
.ContextFlags
=
514 (CONTEXT_i386
| CONTEXT_CONTROL
| CONTEXT_INTEGER
| CONTEXT_SEGMENTS
);
516 KeTrapFrameToContext(Thread
->TrapFrame
, &Context
);
519 Context
.Eax
= EaxValue
;
521 // Begin traversing the list of EXCEPTION_REGISTRATION
522 while ((ULONG_PTR
)ERHead
!= -1)
524 EXCEPTION_RECORD er2
;
526 DPRINT("ERHead 0x%X\n", ERHead
);
528 if (ERHead
== RegistrationFrame
)
530 DPRINT("Continueing execution\n");
531 NtContinue(&Context
, FALSE
);
536 // If there's an exception frame, but it's lower on the stack
537 // then the head of the exception list, something's wrong!
538 if (RegistrationFrame
&& (RegistrationFrame
<= ERHead
))
540 DPRINT("The exception frame is bad\n");
542 // Generate an exception to bail out
543 er2
.ExceptionRecord
= pExceptRec
;
544 er2
.NumberParameters
= 0;
545 er2
.ExceptionCode
= STATUS_INVALID_UNWIND_TARGET
;
546 er2
.ExceptionFlags
= EXCEPTION_NONCONTINUABLE
;
548 RtlRaiseException(&er2
);
553 Stack
= ERHead
+ sizeof(EXCEPTION_REGISTRATION
);
554 if ( (KPCR
->StackBase
<= (PVOID
)ERHead
) // Make sure that ERHead
555 && (KPCR
->StackLimit
>= (PVOID
)Stack
) // is in range, and a multiple
556 && (0 == ((ULONG_PTR
)ERHead
& 3)) ) // of 4 (i.e., sane)
561 PEXCEPTION_REGISTRATION NewERHead
;
562 PEXCEPTION_REGISTRATION pCurrExceptReg
;
563 EXCEPTION_DISPOSITION ReturnValue
;
565 DPRINT("Executing handler at 0x%X for unwind\n", ERHead
->handler
);
567 ReturnValue
= RtlpExecuteHandlerForUnwind(
574 DPRINT("Handler at 0x%X returned 0x%X\n", ERHead
->handler
, ReturnValue
);
576 if (ReturnValue
!= ExceptionContinueSearch
)
578 if (ReturnValue
!= ExceptionCollidedUnwind
)
580 DPRINT("Bad return value\n");
582 er2
.ExceptionRecord
= pExceptRec
;
583 er2
.NumberParameters
= 0;
584 er2
.ExceptionCode
= STATUS_INVALID_DISPOSITION
;
585 er2
.ExceptionFlags
= EXCEPTION_NONCONTINUABLE
;
587 RtlRaiseException(&er2
);
592 pCurrExceptReg
= ERHead
;
593 ERHead
= ERHead
->prev
;
595 DPRINT("New ERHead is 0x%X\n", ERHead
);
597 DPRINT("Setting exception registration at 0x%X as current\n",
598 RegistrationFrame
->prev
);
600 // Unlink the exception handler
601 KeGetCurrentKPCR()->ExceptionList
= RegistrationFrame
->prev
;
603 else // The stack looks goofy! Raise an exception to bail out
605 DPRINT("Bad stack\n");
607 er2
.ExceptionRecord
= pExceptRec
;
608 er2
.NumberParameters
= 0;
609 er2
.ExceptionCode
= STATUS_BAD_STACK
;
610 er2
.ExceptionFlags
= EXCEPTION_NONCONTINUABLE
;
612 RtlRaiseException(&er2
);
616 // If we get here, we reached the end of the EXCEPTION_REGISTRATION list.
617 // This shouldn't happen normally.
619 DPRINT("Ran out of exception registrations. RegistrationFrame is (0x%X)\n",
622 if ((ULONG_PTR
)RegistrationFrame
== -1)
623 NtContinue(&Context
, FALSE
);
625 NtRaiseException(pExceptRec
, &Context
, 0);