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.20 2002/05/08 17:05:32 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
) || (!(KdDebugState
& KD_DEBUG_GDB
)))
276 /* FIXME: Get ExceptionNr and CR2 */
277 KeBugCheckWithTf (KMODE_EXCEPTION_NOT_HANDLED
, 0, 0, 0, 0, Tf
);
280 Action
= KdEnterDebuggerException (ExceptionRecord
, Context
, Tf
);
281 if (Action
!= kdHandleException
)
283 Value
= RtlpDispatchException (ExceptionRecord
, Context
);
285 DPRINT("RtlpDispatchException() returned with 0x%X\n", Value
);
287 * If RtlpDispatchException() does not handle the exception then
290 if (Value
!= ExceptionContinueExecution
)
292 KeBugCheck (KMODE_EXCEPTION_NOT_HANDLED
);
297 KeContextToTrapFrame (Context
, KeGetCurrentThread()->TrapFrame
);
303 ExRaiseAccessViolation (VOID
)
305 ExRaiseStatus (STATUS_ACCESS_VIOLATION
);
309 ExRaiseDatatypeMisalignment (VOID
)
311 ExRaiseStatus (STATUS_DATATYPE_MISALIGNMENT
);
315 ExRaiseStatus (IN NTSTATUS Status
)
317 EXCEPTION_RECORD ExceptionRecord
;
319 DPRINT("ExRaiseStatus(%x)\n", Status
);
321 ExceptionRecord
.ExceptionRecord
= NULL
;
322 ExceptionRecord
.NumberParameters
= 0;
323 ExceptionRecord
.ExceptionCode
= Status
;
324 ExceptionRecord
.ExceptionFlags
= 0;
326 RtlRaiseException(&ExceptionRecord
);
331 NtRaiseException (IN PEXCEPTION_RECORD ExceptionRecord
,
333 IN BOOLEAN SearchFrames
)
335 KiDispatchException(ExceptionRecord
,
337 PsGetCurrentThread()->Tcb
.TrapFrame
,
340 return(STATUS_SUCCESS
);
345 RtlRaiseException(PEXCEPTION_RECORD ExceptionRecord
)
347 ZwRaiseException(ExceptionRecord
, NULL
, TRUE
);
352 EXCEPTION_DISPOSITION
354 PEXCEPTION_RECORD ExceptionRecord
,
355 PEXCEPTION_REGISTRATION ExceptionRegistration
,
357 PVOID DispatcherContext
,
358 PEXCEPTION_HANDLER Handler
,
359 PEXCEPTION_HANDLER RawHandler
)
361 EXCEPTION_DISPOSITION Value
;
363 // Set up an EXCEPTION_REGISTRATION
364 __asm__ ("pushl %0;pushl %%fs:0;movl %%esp,%%fs:0;" : : "g" (RawHandler
));
366 // Invoke the exception callback function
369 ExceptionRegistration
,
373 // Remove the minimal EXCEPTION_REGISTRATION frame
374 //__asm__ ("movl %fs:0,%esp; popl %fs:0");
376 __asm__ ("movl (%%esp),%%eax;movl %%eax,%%fs:0;addl $8,%%esp;" : : : "%eax");
382 EXCEPTION_DISPOSITION
383 RtlpExceptionHandler(
384 PEXCEPTION_RECORD ExceptionRecord
,
385 PEXCEPTION_REGISTRATION ExceptionRegistration
,
387 PVOID DispatcherContext
)
389 // If unwind flag set, return DISPOSITION_CONTINUE_SEARCH, else
390 // assign DispatcherContext context and return DISPOSITION_NESTED_EXCEPTION
392 if (ExceptionRecord
->ExceptionFlags
& EXCEPTION_UNWINDING
)
394 DPRINT("RtlpExceptionHandler(). Returning ExceptionContinueSearch\n");
395 return ExceptionContinueSearch
;
399 DPRINT("RtlpExceptionHandler(). Returning ExceptionNestedException\n");
400 *(PEXCEPTION_REGISTRATION
*)DispatcherContext
= ExceptionRegistration
->prev
;
401 return ExceptionNestedException
;
406 EXCEPTION_DISPOSITION
408 PEXCEPTION_RECORD ExceptionRecord
,
409 PEXCEPTION_REGISTRATION ExceptionRegistration
,
411 PVOID DispatcherContext
)
413 // If unwind flag set, return DISPOSITION_CONTINUE_SEARCH, else
414 // assign DispatcherContext and return DISPOSITION_COLLIDED_UNWIND
416 if (ExceptionRecord
->ExceptionFlags
& EXCEPTION_UNWINDING
)
418 DPRINT("RtlpUnwindHandler(). Returning ExceptionContinueSearch\n");
419 return ExceptionContinueSearch
;
423 DPRINT("RtlpUnwindHandler(). Returning ExceptionCollidedUnwind\n");
424 *(PEXCEPTION_REGISTRATION
*)DispatcherContext
= ExceptionRegistration
->prev
;
425 return ExceptionCollidedUnwind
;
430 EXCEPTION_DISPOSITION
431 RtlpExecuteHandlerForException(
432 PEXCEPTION_RECORD ExceptionRecord
,
433 PEXCEPTION_REGISTRATION ExceptionRegistration
,
435 PVOID DispatcherContext
,
436 PEXCEPTION_HANDLER Handler
)
438 return RtlpExecuteHandler(
440 ExceptionRegistration
,
444 RtlpExceptionHandler
);
448 EXCEPTION_DISPOSITION
449 RtlpExecuteHandlerForUnwind(
450 PEXCEPTION_RECORD ExceptionRecord
,
451 PEXCEPTION_REGISTRATION ExceptionRegistration
,
453 PVOID DispatcherContext
,
454 PEXCEPTION_HANDLER Handler
)
456 return RtlpExecuteHandler(
458 ExceptionRegistration
,
468 PEXCEPTION_REGISTRATION RegistrationFrame
,
470 PEXCEPTION_RECORD ExceptionRecord
,
473 PEXCEPTION_REGISTRATION ERHead
;
474 PEXCEPTION_RECORD pExceptRec
;
475 EXCEPTION_RECORD TempER
;
480 DPRINT("RtlUnwind() called. RegistrationFrame 0x%X\n", RegistrationFrame
);
482 RtlpDumpExceptionRegistrations();
484 Thread
= KeGetCurrentThread();
486 ERHead
= Thread
->TrapFrame
->ExceptionList
;
488 if (ExceptionRecord
== NULL
) // The normal case
490 pExceptRec
= &TempER
;
492 pExceptRec
->ExceptionFlags
= 0;
493 pExceptRec
->ExceptionCode
= STATUS_UNWIND
;
494 pExceptRec
->ExceptionRecord
= NULL
;
495 // FIXME: Find out if NT retrieves the return address from the stack instead
496 pExceptRec
->ExceptionAddress
= ReturnAddress
;
497 //pExceptRec->ExceptionInformation[0] = 0;
500 if (RegistrationFrame
)
501 pExceptRec
->ExceptionFlags
|= EXCEPTION_UNWINDING
;
503 pExceptRec
->ExceptionFlags
|= (EXCEPTION_UNWINDING
|EXCEPTION_EXIT_UNWIND
);
505 Context
.ContextFlags
=
506 (CONTEXT_i386
| CONTEXT_CONTROL
| CONTEXT_INTEGER
| CONTEXT_SEGMENTS
);
508 KeTrapFrameToContext(Thread
->TrapFrame
, &Context
);
511 Context
.Eax
= EaxValue
;
513 // Begin traversing the list of EXCEPTION_REGISTRATION
514 while ((ULONG_PTR
)ERHead
!= -1)
516 EXCEPTION_RECORD er2
;
518 DPRINT("ERHead 0x%X\n", ERHead
);
520 if (ERHead
== RegistrationFrame
)
522 DPRINT("Continueing execution\n");
523 NtContinue(&Context
, FALSE
);
528 // If there's an exception frame, but it's lower on the stack
529 // then the head of the exception list, something's wrong!
530 if (RegistrationFrame
&& (RegistrationFrame
<= ERHead
))
532 DPRINT("The exception frame is bad\n");
534 // Generate an exception to bail out
535 er2
.ExceptionRecord
= pExceptRec
;
536 er2
.NumberParameters
= 0;
537 er2
.ExceptionCode
= STATUS_INVALID_UNWIND_TARGET
;
538 er2
.ExceptionFlags
= EXCEPTION_NONCONTINUABLE
;
540 RtlRaiseException(&er2
);
545 Stack
= ERHead
+ sizeof(EXCEPTION_REGISTRATION
);
546 if ( (KPCR
->StackBase
<= (PVOID
)ERHead
) // Make sure that ERHead
547 && (KPCR
->StackLimit
>= (PVOID
)Stack
) // is in range, and a multiple
548 && (0 == ((ULONG_PTR
)ERHead
& 3)) ) // of 4 (i.e., sane)
553 PEXCEPTION_REGISTRATION NewERHead
;
554 PEXCEPTION_REGISTRATION pCurrExceptReg
;
555 EXCEPTION_DISPOSITION ReturnValue
;
557 DPRINT("Executing handler at 0x%X for unwind\n", ERHead
->handler
);
559 ReturnValue
= RtlpExecuteHandlerForUnwind(
566 DPRINT("Handler at 0x%X returned 0x%X\n", ERHead
->handler
, ReturnValue
);
568 if (ReturnValue
!= ExceptionContinueSearch
)
570 if (ReturnValue
!= ExceptionCollidedUnwind
)
572 DPRINT("Bad return value\n");
574 er2
.ExceptionRecord
= pExceptRec
;
575 er2
.NumberParameters
= 0;
576 er2
.ExceptionCode
= STATUS_INVALID_DISPOSITION
;
577 er2
.ExceptionFlags
= EXCEPTION_NONCONTINUABLE
;
579 RtlRaiseException(&er2
);
584 pCurrExceptReg
= ERHead
;
585 ERHead
= ERHead
->prev
;
587 DPRINT("New ERHead is 0x%X\n", ERHead
);
589 DPRINT("Setting exception registration at 0x%X as current\n",
590 RegistrationFrame
->prev
);
592 // Unlink the exception handler
593 KeGetCurrentKPCR()->ExceptionList
= RegistrationFrame
->prev
;
595 else // The stack looks goofy! Raise an exception to bail out
597 DPRINT("Bad stack\n");
599 er2
.ExceptionRecord
= pExceptRec
;
600 er2
.NumberParameters
= 0;
601 er2
.ExceptionCode
= STATUS_BAD_STACK
;
602 er2
.ExceptionFlags
= EXCEPTION_NONCONTINUABLE
;
604 RtlRaiseException(&er2
);
608 // If we get here, we reached the end of the EXCEPTION_REGISTRATION list.
609 // This shouldn't happen normally.
611 DPRINT("Ran out of exception registrations. RegistrationFrame is (0x%X)\n",
614 if ((ULONG_PTR
)RegistrationFrame
== -1)
615 NtContinue(&Context
, FALSE
);
617 NtRaiseException(pExceptRec
, &Context
, 0);