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