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.23 2002/09/07 15:12:56 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 *****************************************************************/
32 #include <internal/debug.h>
35 /* FUNCTIONS ****************************************************************/
38 RtlpExecuteHandlerForException(
39 PEXCEPTION_RECORD ExceptionRecord
,
40 PEXCEPTION_REGISTRATION ExceptionRegistration
,
42 PVOID DispatcherContext
,
43 PEXCEPTION_HANDLER Handler
);
48 VOID
RtlpDumpExceptionRegistrations(VOID
)
50 PEXCEPTION_REGISTRATION Current
;
53 DbgPrint("Dumping exception registrations:\n");
55 Thread
= KeGetCurrentThread();
58 assert(Thread
->TrapFrame
);
60 Current
= Thread
->TrapFrame
->ExceptionList
;
62 if ((ULONG_PTR
)Current
!= -1)
64 while ((ULONG_PTR
)Current
!= -1)
66 DbgPrint(" (0x%08X) HANDLER (0x%08X)\n", Current
, Current
->handler
);
67 Current
= Current
->prev
;
69 DbgPrint(" End-Of-List\n");
71 DbgPrint(" No exception registrations exists.\n");
78 RtlpDispatchException(
79 PEXCEPTION_RECORD ExceptionRecord
,
82 PEXCEPTION_REGISTRATION RegistrationFrame
;
83 DWORD DispatcherContext
;
88 DPRINT("RtlpDispatchException() called\n");
90 RtlpDumpExceptionRegistrations();
92 Thread
= KeGetCurrentThread();
94 DPRINT("Thread is 0x%X\n", Thread
);
96 KPCR
= KeGetCurrentKPCR();
98 RegistrationFrame
= Thread
->TrapFrame
->ExceptionList
;
100 DPRINT("RegistrationFrame is 0x%X\n", RegistrationFrame
);
102 while ((ULONG_PTR
)RegistrationFrame
!= -1)
104 EXCEPTION_RECORD ExceptionRecord2
;
106 //PVOID RegistrationFrameEnd = (PVOID)RegistrationFrame + 8;
108 // Make sure the registration frame is located within the stack
110 DPRINT("Error checking\n");
112 if (Thread
->KernelStack
> RegistrationFrameEnd
)
114 ExceptionRecord
->ExceptionFlags
|= EH_STACK_INVALID
;
115 return ExceptionDismiss
;
118 if (Thread
->StackLimit
< RegistrationFrameEnd
)
120 ExceptionRecord
->ExceptionFlags
|= EH_STACK_INVALID
;
121 return ExceptionDismiss
;
124 // Make sure stack is DWORD aligned
125 if ((ULONG_PTR
)RegistrationFrame
& 3)
127 ExceptionRecord
->ExceptionFlags
|= EH_STACK_INVALID
;
128 return ExceptionDismiss
;
132 DPRINT("Calling handler at 0x%X\n", RegistrationFrame
->handler
);
134 ReturnValue
= RtlpExecuteHandlerForException(
139 RegistrationFrame
->handler
);
141 if (RegistrationFrame
== NULL
)
143 ExceptionRecord
->ExceptionFlags
&= ~EH_NESTED_CALL
; // Turn off flag
146 if (ReturnValue
== ExceptionContinueExecution
)
148 /* Copy the changed context back to the trap frame and return */
149 NtContinue(Context
, FALSE
);
150 return ExceptionContinueExecution
;
152 else if (ReturnValue
== ExceptionDismiss
)
154 if (ExceptionRecord
->ExceptionFlags
& EH_NONCONTINUABLE
)
156 ExceptionRecord2
.ExceptionRecord
= ExceptionRecord
;
157 ExceptionRecord2
.ExceptionCode
= STATUS_NONCONTINUABLE_EXCEPTION
;
158 ExceptionRecord2
.ExceptionFlags
= EH_NONCONTINUABLE
;
159 ExceptionRecord2
.NumberParameters
= 0;
160 RtlRaiseException(&ExceptionRecord2
);
162 /* Else continue search */
164 else if (ReturnValue
== ExceptionNestedException
)
166 ExceptionRecord
->ExceptionFlags
|= EH_EXIT_UNWIND
;
167 if (DispatcherContext
> Temp
)
168 Temp
= DispatcherContext
;
170 else if (ReturnValue
== ExceptionCollidedUnwind
)
172 ExceptionRecord2
.ExceptionRecord
= ExceptionRecord
;
173 ExceptionRecord2
.ExceptionCode
= STATUS_INVALID_DISPOSITION
;
174 ExceptionRecord2
.ExceptionFlags
= EH_NONCONTINUABLE
;
175 ExceptionRecord2
.NumberParameters
= 0;
176 RtlRaiseException(&ExceptionRecord2
);
179 RegistrationFrame
= RegistrationFrame
->prev
; // Go to previous frame
182 /* No exception handler will handle this exception */
184 return ExceptionDismiss
;
189 KiDispatchException(PEXCEPTION_RECORD ExceptionRecord
,
192 KPROCESSOR_MODE PreviousMode
,
193 BOOLEAN SearchFrames
)
195 EXCEPTION_DISPOSITION Value
;
198 DPRINT("KiDispatchException() called\n");
200 /* PCR->KeExceptionDispatchCount++; */
204 TContext
.ContextFlags
= CONTEXT_FULL
;
205 if (PreviousMode
== UserMode
)
207 TContext
.ContextFlags
= TContext
.ContextFlags
| CONTEXT_DEBUGGER
;
210 KeTrapFrameToContext(Tf
, &TContext
);
215 if (ExceptionRecord
->ExceptionCode
== STATUS_BREAKPOINT
)
220 if (PreviousMode
== UserMode
)
227 /* FIXME: Give the kernel debugger a chance */
229 /* FIXME: Forward exception to user mode debugger */
231 /* FIXME: Check user mode stack for enough space */
235 * Let usermode try and handle the exception
238 (12 + sizeof(EXCEPTION_RECORD
) + sizeof(CONTEXT
));
239 Stack
= (PULONG
)Tf
->Esp
;
240 CDest
= 3 + (ROUND_UP(sizeof(EXCEPTION_RECORD
), 4) / 4);
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
));
250 Tf
->Eip
= (ULONG
)LdrpGetSystemDllExceptionDispatcher();
254 /* FIXME: Forward the exception to the debugger */
256 /* FIXME: Forward the exception to the process exception port */
258 /* Terminate the offending thread */
259 ZwTerminateThread(NtCurrentThread(), ExceptionRecord
->ExceptionCode
);
261 /* If that fails then bugcheck */
262 DbgPrint("Could not terminate thread\n");
263 KeBugCheck(KMODE_EXCEPTION_NOT_HANDLED
);
267 KD_CONTINUE_TYPE Action
;
269 /* PreviousMode == KernelMode */
271 if (KdDebuggerEnabled
&& KdDebugState
& KD_DEBUG_GDB
)
273 Action
= KdEnterDebuggerException (ExceptionRecord
, Context
, Tf
);
276 else if (KdDebuggerEnabled
&& KdDebugState
& KD_DEBUG_KDB
)
278 Action
= KdbEnterDebuggerException (ExceptionRecord
, Context
, Tf
);
283 KeBugCheckWithTf (KMODE_EXCEPTION_NOT_HANDLED
, 0, 0, 0, 0, Tf
);
285 if (Action
!= kdHandleException
)
287 Value
= RtlpDispatchException (ExceptionRecord
, Context
);
289 DPRINT("RtlpDispatchException() returned with 0x%X\n", Value
);
291 * If RtlpDispatchException() does not handle the exception then
294 if (Value
!= ExceptionContinueExecution
)
296 KeBugCheck (KMODE_EXCEPTION_NOT_HANDLED
);
301 KeContextToTrapFrame (Context
, KeGetCurrentThread()->TrapFrame
);
307 ExRaiseAccessViolation (VOID
)
309 ExRaiseStatus (STATUS_ACCESS_VIOLATION
);
313 ExRaiseDatatypeMisalignment (VOID
)
315 ExRaiseStatus (STATUS_DATATYPE_MISALIGNMENT
);
319 ExRaiseStatus (IN NTSTATUS Status
)
321 EXCEPTION_RECORD ExceptionRecord
;
323 DPRINT("ExRaiseStatus(%x)\n", Status
);
325 ExceptionRecord
.ExceptionRecord
= NULL
;
326 ExceptionRecord
.NumberParameters
= 0;
327 ExceptionRecord
.ExceptionCode
= Status
;
328 ExceptionRecord
.ExceptionFlags
= 0;
330 RtlRaiseException(&ExceptionRecord
);
335 NtRaiseException (IN PEXCEPTION_RECORD ExceptionRecord
,
337 IN BOOLEAN SearchFrames
)
339 KiDispatchException(ExceptionRecord
,
341 PsGetCurrentThread()->Tcb
.TrapFrame
,
344 return(STATUS_SUCCESS
);
349 RtlRaiseException(PEXCEPTION_RECORD ExceptionRecord
)
351 ZwRaiseException(ExceptionRecord
, NULL
, TRUE
);
356 EXCEPTION_DISPOSITION
358 PEXCEPTION_RECORD ExceptionRecord
,
359 PEXCEPTION_REGISTRATION ExceptionRegistration
,
361 PVOID DispatcherContext
,
362 PEXCEPTION_HANDLER Handler
,
363 PEXCEPTION_HANDLER RawHandler
)
365 EXCEPTION_DISPOSITION Value
;
367 // Set up an EXCEPTION_REGISTRATION
368 __asm__ ("pushl %0;pushl %%fs:0;movl %%esp,%%fs:0;" : : "g" (RawHandler
));
370 // Invoke the exception callback function
373 ExceptionRegistration
,
377 // Remove the minimal EXCEPTION_REGISTRATION frame
378 //__asm__ ("movl %fs:0,%esp; popl %fs:0");
380 __asm__ ("movl (%%esp),%%eax;movl %%eax,%%fs:0;addl $8,%%esp;" : : : "%eax");
386 EXCEPTION_DISPOSITION
387 RtlpExceptionHandler(
388 PEXCEPTION_RECORD ExceptionRecord
,
389 PEXCEPTION_REGISTRATION ExceptionRegistration
,
391 PVOID DispatcherContext
)
393 // If unwind flag set, return DISPOSITION_CONTINUE_SEARCH, else
394 // assign DispatcherContext context and return DISPOSITION_NESTED_EXCEPTION
396 if (ExceptionRecord
->ExceptionFlags
& EH_UNWINDING
)
398 DPRINT("RtlpExceptionHandler(). Returning ExceptionContinueSearch\n");
399 return ExceptionContinueSearch
;
403 DPRINT("RtlpExceptionHandler(). Returning ExceptionNestedException\n");
404 *(PEXCEPTION_REGISTRATION
*)DispatcherContext
= ExceptionRegistration
->prev
;
405 return ExceptionNestedException
;
410 EXCEPTION_DISPOSITION
412 PEXCEPTION_RECORD ExceptionRecord
,
413 PEXCEPTION_REGISTRATION ExceptionRegistration
,
415 PVOID DispatcherContext
)
417 // If unwind flag set, return DISPOSITION_CONTINUE_SEARCH, else
418 // assign DispatcherContext and return DISPOSITION_COLLIDED_UNWIND
420 if (ExceptionRecord
->ExceptionFlags
& EH_UNWINDING
)
422 DPRINT("RtlpUnwindHandler(). Returning ExceptionContinueSearch\n");
423 return ExceptionContinueSearch
;
427 DPRINT("RtlpUnwindHandler(). Returning ExceptionCollidedUnwind\n");
428 *(PEXCEPTION_REGISTRATION
*)DispatcherContext
= ExceptionRegistration
->prev
;
429 return ExceptionCollidedUnwind
;
434 EXCEPTION_DISPOSITION
435 RtlpExecuteHandlerForException(
436 PEXCEPTION_RECORD ExceptionRecord
,
437 PEXCEPTION_REGISTRATION ExceptionRegistration
,
439 PVOID DispatcherContext
,
440 PEXCEPTION_HANDLER Handler
)
442 return RtlpExecuteHandler(
444 ExceptionRegistration
,
448 (PEXCEPTION_HANDLER
)RtlpExceptionHandler
);
452 EXCEPTION_DISPOSITION
453 RtlpExecuteHandlerForUnwind(
454 PEXCEPTION_RECORD ExceptionRecord
,
455 PEXCEPTION_REGISTRATION ExceptionRegistration
,
457 PVOID DispatcherContext
,
458 PEXCEPTION_HANDLER Handler
)
460 return RtlpExecuteHandler(
462 ExceptionRegistration
,
466 (PEXCEPTION_HANDLER
)RtlpUnwindHandler
);
472 PEXCEPTION_REGISTRATION RegistrationFrame
,
474 PEXCEPTION_RECORD ExceptionRecord
,
477 PEXCEPTION_REGISTRATION ERHead
;
478 PEXCEPTION_RECORD pExceptRec
;
479 EXCEPTION_RECORD TempER
;
484 DPRINT("RtlUnwind() called. RegistrationFrame 0x%X\n", RegistrationFrame
);
486 RtlpDumpExceptionRegistrations();
488 Thread
= KeGetCurrentThread();
490 ERHead
= Thread
->TrapFrame
->ExceptionList
;
492 if (ExceptionRecord
== NULL
) // The normal case
494 pExceptRec
= &TempER
;
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;
504 if (RegistrationFrame
)
505 pExceptRec
->ExceptionFlags
|= EH_UNWINDING
;
507 pExceptRec
->ExceptionFlags
|= (EH_UNWINDING
|EH_EXIT_UNWIND
);
509 Context
.ContextFlags
=
510 (CONTEXT_i386
| CONTEXT_CONTROL
| CONTEXT_INTEGER
| CONTEXT_SEGMENTS
);
512 KeTrapFrameToContext(Thread
->TrapFrame
, &Context
);
515 Context
.Eax
= EaxValue
;
517 // Begin traversing the list of EH_REGISTRATION
518 while ((ULONG_PTR
)ERHead
!= -1)
520 EXCEPTION_RECORD er2
;
522 DPRINT("ERHead 0x%X\n", ERHead
);
524 if (ERHead
== RegistrationFrame
)
526 DPRINT("Continueing execution\n");
527 NtContinue(&Context
, FALSE
);
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
))
536 DPRINT("The exception frame is bad\n");
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
;
544 RtlRaiseException(&er2
);
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)
557 PEXCEPTION_REGISTRATION NewERHead
;
558 PEXCEPTION_REGISTRATION pCurrExceptReg
;
559 EXCEPTION_DISPOSITION ReturnValue
;
561 DPRINT("Executing handler at 0x%X for unwind\n", ERHead
->handler
);
563 ReturnValue
= RtlpExecuteHandlerForUnwind(
570 DPRINT("Handler at 0x%X returned 0x%X\n", ERHead
->handler
, ReturnValue
);
572 if (ReturnValue
!= ExceptionContinueSearch
)
574 if (ReturnValue
!= ExceptionCollidedUnwind
)
576 DPRINT("Bad return value\n");
578 er2
.ExceptionRecord
= pExceptRec
;
579 er2
.NumberParameters
= 0;
580 er2
.ExceptionCode
= STATUS_INVALID_DISPOSITION
;
581 er2
.ExceptionFlags
= EH_NONCONTINUABLE
;
583 RtlRaiseException(&er2
);
588 pCurrExceptReg
= ERHead
;
589 ERHead
= ERHead
->prev
;
591 DPRINT("New ERHead is 0x%X\n", ERHead
);
593 DPRINT("Setting exception registration at 0x%X as current\n",
594 RegistrationFrame
->prev
);
596 // Unlink the exception handler
597 KeGetCurrentKPCR()->Tib
.ExceptionList
= RegistrationFrame
->prev
;
599 else // The stack looks goofy! Raise an exception to bail out
601 DPRINT("Bad stack\n");
603 er2
.ExceptionRecord
= pExceptRec
;
604 er2
.NumberParameters
= 0;
605 er2
.ExceptionCode
= STATUS_BAD_STACK
;
606 er2
.ExceptionFlags
= EH_NONCONTINUABLE
;
608 RtlRaiseException(&er2
);
612 // If we get here, we reached the end of the EXCEPTION_REGISTRATION list.
613 // This shouldn't happen normally.
615 DPRINT("Ran out of exception registrations. RegistrationFrame is (0x%X)\n",
618 if ((ULONG_PTR
)RegistrationFrame
== -1)
619 NtContinue(&Context
, FALSE
);
621 NtRaiseException(pExceptRec
, &Context
, 0);