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.18 2002/02/09 18:41:24 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");
203 /* PCR->KeExceptionDispatchCount++; */
207 TContext
.ContextFlags
= CONTEXT_FULL
;
208 if (PreviousMode
== UserMode
)
210 TContext
.ContextFlags
= TContext
.ContextFlags
| CONTEXT_DEBUGGER
;
213 KeTrapFrameToContext(Tf
, &TContext
);
218 if (ExceptionRecord
->ExceptionCode
== STATUS_BREAKPOINT
)
223 if (PreviousMode
== UserMode
)
230 /* FIXME: Give the kernel debugger a chance */
232 /* FIXME: Forward exception to user mode debugger */
234 /* FIXME: Check user mode stack for enough space */
238 * Let usermode try and handle the exception
241 (12 + sizeof(EXCEPTION_RECORD
) + sizeof(CONTEXT
));
242 Stack
= (PULONG
)Tf
->Esp
;
243 CDest
= 3 + (ROUND_UP(sizeof(EXCEPTION_RECORD
), 4) / 4);
246 /* Pointer to EXCEPTION_RECORD structure */
247 Stack
[1] = (ULONG
)&Stack
[3];
248 /* Pointer to CONTEXT structure */
249 Stack
[2] = (ULONG
)&Stack
[CDest
];
250 memcpy(&Stack
[3], ExceptionRecord
, sizeof(EXCEPTION_RECORD
));
251 memcpy(&Stack
[CDest
], Context
, sizeof(CONTEXT
));
253 Tf
->Eip
= (ULONG
)LdrpGetSystemDllExceptionDispatcher();
257 /* FIXME: Forward the exception to the debugger */
259 /* FIXME: Forward the exception to the process exception port */
261 /* Terminate the offending thread */
262 ZwTerminateThread(NtCurrentThread(), ExceptionRecord
->ExceptionCode
);
264 /* If that fails then bugcheck */
265 DbgPrint("Could not terminate thread\n");
266 KeBugCheck(KMODE_EXCEPTION_NOT_HANDLED
);
270 KD_CONTINUE_TYPE Action
;
272 /* PreviousMode == KernelMode */
274 if (!KdDebuggerEnabled
|| KdDebugType
!= GdbDebug
)
276 KeBugCheck (KMODE_EXCEPTION_NOT_HANDLED
);
279 Action
= KdEnterDebuggerException (ExceptionRecord
, Context
, Tf
);
280 if (Action
!= kdHandleException
)
282 Value
= RtlpDispatchException (ExceptionRecord
, Context
);
284 DPRINT("RtlpDispatchException() returned with 0x%X\n", Value
);
285 /* If RtlpDispatchException() does not handle the exception then bugcheck */
286 if (Value
!= ExceptionContinueExecution
)
288 KeBugCheck (KMODE_EXCEPTION_NOT_HANDLED
);
293 KeContextToTrapFrame (Context
, KeGetCurrentThread()->TrapFrame
);
299 ExRaiseAccessViolation (VOID
)
301 ExRaiseStatus (STATUS_ACCESS_VIOLATION
);
305 ExRaiseDatatypeMisalignment (VOID
)
307 ExRaiseStatus (STATUS_DATATYPE_MISALIGNMENT
);
311 ExRaiseStatus (IN NTSTATUS Status
)
313 EXCEPTION_RECORD ExceptionRecord
;
315 DPRINT("ExRaiseStatus(%x)\n", Status
);
317 ExceptionRecord
.ExceptionRecord
= NULL
;
318 ExceptionRecord
.NumberParameters
= 0;
319 ExceptionRecord
.ExceptionCode
= Status
;
320 ExceptionRecord
.ExceptionFlags
= 0;
322 RtlRaiseException(&ExceptionRecord
);
327 NtRaiseException (IN PEXCEPTION_RECORD ExceptionRecord
,
329 IN BOOLEAN SearchFrames
)
331 KiDispatchException(ExceptionRecord
,
333 PsGetCurrentThread()->Tcb
.TrapFrame
,
336 return(STATUS_SUCCESS
);
341 RtlRaiseException(PEXCEPTION_RECORD ExceptionRecord
)
343 ZwRaiseException(ExceptionRecord
, NULL
, TRUE
);
348 EXCEPTION_DISPOSITION
350 PEXCEPTION_RECORD ExceptionRecord
,
351 PEXCEPTION_REGISTRATION ExceptionRegistration
,
353 PVOID DispatcherContext
,
354 PEXCEPTION_HANDLER Handler
,
355 PEXCEPTION_HANDLER RawHandler
)
357 EXCEPTION_DISPOSITION Value
;
359 // Set up an EXCEPTION_REGISTRATION
360 __asm__ ("pushl %0;pushl %%fs:0;movl %%esp,%%fs:0;" : : "g" (RawHandler
));
362 // Invoke the exception callback function
365 ExceptionRegistration
,
369 // Remove the minimal EXCEPTION_REGISTRATION frame
370 //__asm__ ("movl %fs:0,%esp; popl %fs:0");
372 __asm__ ("movl (%%esp),%%eax;movl %%eax,%%fs:0;addl $8,%%esp;" : : : "%eax");
378 EXCEPTION_DISPOSITION
379 RtlpExceptionHandler(
380 PEXCEPTION_RECORD ExceptionRecord
,
381 PEXCEPTION_REGISTRATION ExceptionRegistration
,
383 PVOID DispatcherContext
)
385 // If unwind flag set, return DISPOSITION_CONTINUE_SEARCH, else
386 // assign DispatcherContext context and return DISPOSITION_NESTED_EXCEPTION
388 if (ExceptionRecord
->ExceptionFlags
& EXCEPTION_UNWINDING
)
390 DPRINT("RtlpExceptionHandler(). Returning ExceptionContinueSearch\n");
391 return ExceptionContinueSearch
;
395 DPRINT("RtlpExceptionHandler(). Returning ExceptionNestedException\n");
396 *(PEXCEPTION_REGISTRATION
*)DispatcherContext
= ExceptionRegistration
->prev
;
397 return ExceptionNestedException
;
402 EXCEPTION_DISPOSITION
404 PEXCEPTION_RECORD ExceptionRecord
,
405 PEXCEPTION_REGISTRATION ExceptionRegistration
,
407 PVOID DispatcherContext
)
409 // If unwind flag set, return DISPOSITION_CONTINUE_SEARCH, else
410 // assign DispatcherContext and return DISPOSITION_COLLIDED_UNWIND
412 if (ExceptionRecord
->ExceptionFlags
& EXCEPTION_UNWINDING
)
414 DPRINT("RtlpUnwindHandler(). Returning ExceptionContinueSearch\n");
415 return ExceptionContinueSearch
;
419 DPRINT("RtlpUnwindHandler(). Returning ExceptionCollidedUnwind\n");
420 *(PEXCEPTION_REGISTRATION
*)DispatcherContext
= ExceptionRegistration
->prev
;
421 return ExceptionCollidedUnwind
;
426 EXCEPTION_DISPOSITION
427 RtlpExecuteHandlerForException(
428 PEXCEPTION_RECORD ExceptionRecord
,
429 PEXCEPTION_REGISTRATION ExceptionRegistration
,
431 PVOID DispatcherContext
,
432 PEXCEPTION_HANDLER Handler
)
434 return RtlpExecuteHandler(
436 ExceptionRegistration
,
440 RtlpExceptionHandler
);
444 EXCEPTION_DISPOSITION
445 RtlpExecuteHandlerForUnwind(
446 PEXCEPTION_RECORD ExceptionRecord
,
447 PEXCEPTION_REGISTRATION ExceptionRegistration
,
449 PVOID DispatcherContext
,
450 PEXCEPTION_HANDLER Handler
)
452 return RtlpExecuteHandler(
454 ExceptionRegistration
,
464 PEXCEPTION_REGISTRATION RegistrationFrame
,
466 PEXCEPTION_RECORD ExceptionRecord
,
469 PEXCEPTION_REGISTRATION ERHead
;
470 PEXCEPTION_RECORD pExceptRec
;
471 EXCEPTION_RECORD TempER
;
476 DPRINT("RtlUnwind() called. RegistrationFrame 0x%X\n", RegistrationFrame
);
478 RtlpDumpExceptionRegistrations();
480 Thread
= KeGetCurrentThread();
482 ERHead
= Thread
->TrapFrame
->ExceptionList
;
484 if (ExceptionRecord
== NULL
) // The normal case
486 pExceptRec
= &TempER
;
488 pExceptRec
->ExceptionFlags
= 0;
489 pExceptRec
->ExceptionCode
= STATUS_UNWIND
;
490 pExceptRec
->ExceptionRecord
= NULL
;
491 // FIXME: Find out if NT retrieves the return address from the stack instead
492 pExceptRec
->ExceptionAddress
= ReturnAddress
;
493 //pExceptRec->ExceptionInformation[0] = 0;
496 if (RegistrationFrame
)
497 pExceptRec
->ExceptionFlags
|= EXCEPTION_UNWINDING
;
499 pExceptRec
->ExceptionFlags
|= (EXCEPTION_UNWINDING
|EXCEPTION_EXIT_UNWIND
);
501 Context
.ContextFlags
=
502 (CONTEXT_i386
| CONTEXT_CONTROL
| CONTEXT_INTEGER
| CONTEXT_SEGMENTS
);
504 KeTrapFrameToContext(Thread
->TrapFrame
, &Context
);
507 Context
.Eax
= EaxValue
;
509 // Begin traversing the list of EXCEPTION_REGISTRATION
510 while ((ULONG_PTR
)ERHead
!= -1)
512 EXCEPTION_RECORD er2
;
514 DPRINT("ERHead 0x%X\n", ERHead
);
516 if (ERHead
== RegistrationFrame
)
518 DPRINT("Continueing execution\n");
519 NtContinue(&Context
, FALSE
);
524 // If there's an exception frame, but it's lower on the stack
525 // then the head of the exception list, something's wrong!
526 if (RegistrationFrame
&& (RegistrationFrame
<= ERHead
))
528 DPRINT("The exception frame is bad\n");
530 // Generate an exception to bail out
531 er2
.ExceptionRecord
= pExceptRec
;
532 er2
.NumberParameters
= 0;
533 er2
.ExceptionCode
= STATUS_INVALID_UNWIND_TARGET
;
534 er2
.ExceptionFlags
= EXCEPTION_NONCONTINUABLE
;
536 RtlRaiseException(&er2
);
541 Stack
= ERHead
+ sizeof(EXCEPTION_REGISTRATION
);
542 if ( (KPCR
->StackBase
<= (PVOID
)ERHead
) // Make sure that ERHead
543 && (KPCR
->StackLimit
>= (PVOID
)Stack
) // is in range, and a multiple
544 && (0 == ((ULONG_PTR
)ERHead
& 3)) ) // of 4 (i.e., sane)
549 PEXCEPTION_REGISTRATION NewERHead
;
550 PEXCEPTION_REGISTRATION pCurrExceptReg
;
551 EXCEPTION_DISPOSITION ReturnValue
;
553 DPRINT("Executing handler at 0x%X for unwind\n", ERHead
->handler
);
555 ReturnValue
= RtlpExecuteHandlerForUnwind(
562 DPRINT("Handler at 0x%X returned 0x%X\n", ERHead
->handler
, ReturnValue
);
564 if (ReturnValue
!= ExceptionContinueSearch
)
566 if (ReturnValue
!= ExceptionCollidedUnwind
)
568 DPRINT("Bad return value\n");
570 er2
.ExceptionRecord
= pExceptRec
;
571 er2
.NumberParameters
= 0;
572 er2
.ExceptionCode
= STATUS_INVALID_DISPOSITION
;
573 er2
.ExceptionFlags
= EXCEPTION_NONCONTINUABLE
;
575 RtlRaiseException(&er2
);
580 pCurrExceptReg
= ERHead
;
581 ERHead
= ERHead
->prev
;
583 DPRINT("New ERHead is 0x%X\n", ERHead
);
585 DPRINT("Setting exception registration at 0x%X as current\n",
586 RegistrationFrame
->prev
);
588 // Unlink the exception handler
589 KeGetCurrentKPCR()->ExceptionList
= RegistrationFrame
->prev
;
591 else // The stack looks goofy! Raise an exception to bail out
593 DPRINT("Bad stack\n");
595 er2
.ExceptionRecord
= pExceptRec
;
596 er2
.NumberParameters
= 0;
597 er2
.ExceptionCode
= STATUS_BAD_STACK
;
598 er2
.ExceptionFlags
= EXCEPTION_NONCONTINUABLE
;
600 RtlRaiseException(&er2
);
604 // If we get here, we reached the end of the EXCEPTION_REGISTRATION list.
605 // This shouldn't happen normally.
607 DPRINT("Ran out of exception registrations. RegistrationFrame is (0x%X)\n",
610 if ((ULONG_PTR
)RegistrationFrame
== -1)
611 NtContinue(&Context
, FALSE
);
613 NtRaiseException(pExceptRec
, &Context
, 0);