6653ee7b9ab10857708d67cd9d85e77f60d484c1
[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.18 2002/02/09 18:41:24 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 /* PCR->KeExceptionDispatchCount++; */
204
205 if (Context == NULL)
206 {
207 TContext.ContextFlags = CONTEXT_FULL;
208 if (PreviousMode == UserMode)
209 {
210 TContext.ContextFlags = TContext.ContextFlags | CONTEXT_DEBUGGER;
211 }
212
213 KeTrapFrameToContext(Tf, &TContext);
214
215 Context = &TContext;
216 }
217 #if 0
218 if (ExceptionRecord->ExceptionCode == STATUS_BREAKPOINT)
219 {
220 Context->Eip--;
221 }
222 #endif
223 if (PreviousMode == UserMode)
224 {
225 if (SearchFrames)
226 {
227 PULONG Stack;
228 ULONG CDest;
229
230 /* FIXME: Give the kernel debugger a chance */
231
232 /* FIXME: Forward exception to user mode debugger */
233
234 /* FIXME: Check user mode stack for enough space */
235
236
237 /*
238 * Let usermode try and handle the exception
239 */
240 Tf->Esp = Tf->Esp -
241 (12 + sizeof(EXCEPTION_RECORD) + sizeof(CONTEXT));
242 Stack = (PULONG)Tf->Esp;
243 CDest = 3 + (ROUND_UP(sizeof(EXCEPTION_RECORD), 4) / 4);
244 /* Return address */
245 Stack[0] = 0;
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));
252
253 Tf->Eip = (ULONG)LdrpGetSystemDllExceptionDispatcher();
254 return;
255 }
256
257 /* FIXME: Forward the exception to the debugger */
258
259 /* FIXME: Forward the exception to the process exception port */
260
261 /* Terminate the offending thread */
262 ZwTerminateThread(NtCurrentThread(), ExceptionRecord->ExceptionCode);
263
264 /* If that fails then bugcheck */
265 DbgPrint("Could not terminate thread\n");
266 KeBugCheck(KMODE_EXCEPTION_NOT_HANDLED);
267 }
268 else
269 {
270 KD_CONTINUE_TYPE Action;
271
272 /* PreviousMode == KernelMode */
273
274 if (!KdDebuggerEnabled || KdDebugType != GdbDebug)
275 {
276 KeBugCheck (KMODE_EXCEPTION_NOT_HANDLED);
277 }
278
279 Action = KdEnterDebuggerException (ExceptionRecord, Context, Tf);
280 if (Action != kdHandleException)
281 {
282 Value = RtlpDispatchException (ExceptionRecord, Context);
283
284 DPRINT("RtlpDispatchException() returned with 0x%X\n", Value);
285 /* If RtlpDispatchException() does not handle the exception then bugcheck */
286 if (Value != ExceptionContinueExecution)
287 {
288 KeBugCheck (KMODE_EXCEPTION_NOT_HANDLED);
289 }
290 }
291 else
292 {
293 KeContextToTrapFrame (Context, KeGetCurrentThread()->TrapFrame);
294 }
295 }
296 }
297
298 VOID STDCALL
299 ExRaiseAccessViolation (VOID)
300 {
301 ExRaiseStatus (STATUS_ACCESS_VIOLATION);
302 }
303
304 VOID STDCALL
305 ExRaiseDatatypeMisalignment (VOID)
306 {
307 ExRaiseStatus (STATUS_DATATYPE_MISALIGNMENT);
308 }
309
310 VOID STDCALL
311 ExRaiseStatus (IN NTSTATUS Status)
312 {
313 EXCEPTION_RECORD ExceptionRecord;
314
315 DPRINT("ExRaiseStatus(%x)\n", Status);
316
317 ExceptionRecord.ExceptionRecord = NULL;
318 ExceptionRecord.NumberParameters = 0;
319 ExceptionRecord.ExceptionCode = Status;
320 ExceptionRecord.ExceptionFlags = 0;
321
322 RtlRaiseException(&ExceptionRecord);
323 }
324
325
326 NTSTATUS STDCALL
327 NtRaiseException (IN PEXCEPTION_RECORD ExceptionRecord,
328 IN PCONTEXT Context,
329 IN BOOLEAN SearchFrames)
330 {
331 KiDispatchException(ExceptionRecord,
332 Context,
333 PsGetCurrentThread()->Tcb.TrapFrame,
334 ExGetPreviousMode(),
335 SearchFrames);
336 return(STATUS_SUCCESS);
337 }
338
339
340 VOID STDCALL
341 RtlRaiseException(PEXCEPTION_RECORD ExceptionRecord)
342 {
343 ZwRaiseException(ExceptionRecord, NULL, TRUE);
344 }
345
346
347 inline
348 EXCEPTION_DISPOSITION
349 RtlpExecuteHandler(
350 PEXCEPTION_RECORD ExceptionRecord,
351 PEXCEPTION_REGISTRATION ExceptionRegistration,
352 PCONTEXT Context,
353 PVOID DispatcherContext,
354 PEXCEPTION_HANDLER Handler,
355 PEXCEPTION_HANDLER RawHandler)
356 {
357 EXCEPTION_DISPOSITION Value;
358
359 // Set up an EXCEPTION_REGISTRATION
360 __asm__ ("pushl %0;pushl %%fs:0;movl %%esp,%%fs:0;" : : "g" (RawHandler));
361
362 // Invoke the exception callback function
363 Value = Handler(
364 ExceptionRecord,
365 ExceptionRegistration,
366 Context,
367 DispatcherContext);
368
369 // Remove the minimal EXCEPTION_REGISTRATION frame
370 //__asm__ ("movl %fs:0,%esp; popl %fs:0");
371
372 __asm__ ("movl (%%esp),%%eax;movl %%eax,%%fs:0;addl $8,%%esp;" : : : "%eax");
373
374 return Value;
375 }
376
377
378 EXCEPTION_DISPOSITION
379 RtlpExceptionHandler(
380 PEXCEPTION_RECORD ExceptionRecord,
381 PEXCEPTION_REGISTRATION ExceptionRegistration,
382 PCONTEXT Context,
383 PVOID DispatcherContext)
384 {
385 // If unwind flag set, return DISPOSITION_CONTINUE_SEARCH, else
386 // assign DispatcherContext context and return DISPOSITION_NESTED_EXCEPTION
387
388 if (ExceptionRecord->ExceptionFlags & EXCEPTION_UNWINDING)
389 {
390 DPRINT("RtlpExceptionHandler(). Returning ExceptionContinueSearch\n");
391 return ExceptionContinueSearch;
392 }
393 else
394 {
395 DPRINT("RtlpExceptionHandler(). Returning ExceptionNestedException\n");
396 *(PEXCEPTION_REGISTRATION*)DispatcherContext = ExceptionRegistration->prev;
397 return ExceptionNestedException;
398 }
399 }
400
401
402 EXCEPTION_DISPOSITION
403 RtlpUnwindHandler(
404 PEXCEPTION_RECORD ExceptionRecord,
405 PEXCEPTION_REGISTRATION ExceptionRegistration,
406 PCONTEXT Context,
407 PVOID DispatcherContext)
408 {
409 // If unwind flag set, return DISPOSITION_CONTINUE_SEARCH, else
410 // assign DispatcherContext and return DISPOSITION_COLLIDED_UNWIND
411
412 if (ExceptionRecord->ExceptionFlags & EXCEPTION_UNWINDING)
413 {
414 DPRINT("RtlpUnwindHandler(). Returning ExceptionContinueSearch\n");
415 return ExceptionContinueSearch;
416 }
417 else
418 {
419 DPRINT("RtlpUnwindHandler(). Returning ExceptionCollidedUnwind\n");
420 *(PEXCEPTION_REGISTRATION*)DispatcherContext = ExceptionRegistration->prev;
421 return ExceptionCollidedUnwind;
422 }
423 }
424
425
426 EXCEPTION_DISPOSITION
427 RtlpExecuteHandlerForException(
428 PEXCEPTION_RECORD ExceptionRecord,
429 PEXCEPTION_REGISTRATION ExceptionRegistration,
430 PCONTEXT Context,
431 PVOID DispatcherContext,
432 PEXCEPTION_HANDLER Handler)
433 {
434 return RtlpExecuteHandler(
435 ExceptionRecord,
436 ExceptionRegistration,
437 Context,
438 DispatcherContext,
439 Handler,
440 RtlpExceptionHandler);
441 }
442
443
444 EXCEPTION_DISPOSITION
445 RtlpExecuteHandlerForUnwind(
446 PEXCEPTION_RECORD ExceptionRecord,
447 PEXCEPTION_REGISTRATION ExceptionRegistration,
448 PCONTEXT Context,
449 PVOID DispatcherContext,
450 PEXCEPTION_HANDLER Handler)
451 {
452 return RtlpExecuteHandler(
453 ExceptionRecord,
454 ExceptionRegistration,
455 Context,
456 DispatcherContext,
457 Handler,
458 RtlpUnwindHandler);
459 }
460
461
462 VOID STDCALL
463 RtlUnwind(
464 PEXCEPTION_REGISTRATION RegistrationFrame,
465 PVOID ReturnAddress,
466 PEXCEPTION_RECORD ExceptionRecord,
467 DWORD EaxValue)
468 {
469 PEXCEPTION_REGISTRATION ERHead;
470 PEXCEPTION_RECORD pExceptRec;
471 EXCEPTION_RECORD TempER;
472 CONTEXT Context;
473 //PVOID Stack;
474 PKTHREAD Thread;
475
476 DPRINT("RtlUnwind() called. RegistrationFrame 0x%X\n", RegistrationFrame);
477 #ifndef NDEBUG
478 RtlpDumpExceptionRegistrations();
479 #endif /* NDEBUG */
480 Thread = KeGetCurrentThread();
481
482 ERHead = Thread->TrapFrame->ExceptionList;
483
484 if (ExceptionRecord == NULL) // The normal case
485 {
486 pExceptRec = &TempER;
487
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;
494 }
495
496 if (RegistrationFrame)
497 pExceptRec->ExceptionFlags |= EXCEPTION_UNWINDING;
498 else
499 pExceptRec->ExceptionFlags |= (EXCEPTION_UNWINDING|EXCEPTION_EXIT_UNWIND);
500
501 Context.ContextFlags =
502 (CONTEXT_i386 | CONTEXT_CONTROL | CONTEXT_INTEGER | CONTEXT_SEGMENTS);
503
504 KeTrapFrameToContext(Thread->TrapFrame, &Context);
505
506 Context.Esp += 0x10;
507 Context.Eax = EaxValue;
508
509 // Begin traversing the list of EXCEPTION_REGISTRATION
510 while ((ULONG_PTR)ERHead != -1)
511 {
512 EXCEPTION_RECORD er2;
513
514 DPRINT("ERHead 0x%X\n", ERHead);
515
516 if (ERHead == RegistrationFrame)
517 {
518 DPRINT("Continueing execution\n");
519 NtContinue(&Context, FALSE);
520 return;
521 }
522 else
523 {
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))
527 {
528 DPRINT("The exception frame is bad\n");
529
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;
535
536 RtlRaiseException(&er2);
537 }
538 }
539
540 #if 0
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)
545 {
546 #else
547 if (1) {
548 #endif
549 PEXCEPTION_REGISTRATION NewERHead;
550 PEXCEPTION_REGISTRATION pCurrExceptReg;
551 EXCEPTION_DISPOSITION ReturnValue;
552
553 DPRINT("Executing handler at 0x%X for unwind\n", ERHead->handler);
554
555 ReturnValue = RtlpExecuteHandlerForUnwind(
556 pExceptRec,
557 ERHead,
558 &Context,
559 &NewERHead,
560 ERHead->handler);
561
562 DPRINT("Handler at 0x%X returned 0x%X\n", ERHead->handler, ReturnValue);
563
564 if (ReturnValue != ExceptionContinueSearch)
565 {
566 if (ReturnValue != ExceptionCollidedUnwind)
567 {
568 DPRINT("Bad return value\n");
569
570 er2.ExceptionRecord = pExceptRec;
571 er2.NumberParameters = 0;
572 er2.ExceptionCode = STATUS_INVALID_DISPOSITION;
573 er2.ExceptionFlags = EXCEPTION_NONCONTINUABLE;
574
575 RtlRaiseException(&er2);
576 } else
577 ERHead = NewERHead;
578 }
579
580 pCurrExceptReg = ERHead;
581 ERHead = ERHead->prev;
582
583 DPRINT("New ERHead is 0x%X\n", ERHead);
584
585 DPRINT("Setting exception registration at 0x%X as current\n",
586 RegistrationFrame->prev);
587
588 // Unlink the exception handler
589 KeGetCurrentKPCR()->ExceptionList = RegistrationFrame->prev;
590 }
591 else // The stack looks goofy! Raise an exception to bail out
592 {
593 DPRINT("Bad stack\n");
594
595 er2.ExceptionRecord = pExceptRec;
596 er2.NumberParameters = 0;
597 er2.ExceptionCode = STATUS_BAD_STACK;
598 er2.ExceptionFlags = EXCEPTION_NONCONTINUABLE;
599
600 RtlRaiseException(&er2);
601 }
602 }
603
604 // If we get here, we reached the end of the EXCEPTION_REGISTRATION list.
605 // This shouldn't happen normally.
606
607 DPRINT("Ran out of exception registrations. RegistrationFrame is (0x%X)\n",
608 RegistrationFrame);
609
610 if ((ULONG_PTR)RegistrationFrame == -1)
611 NtContinue(&Context, FALSE);
612 else
613 NtRaiseException(pExceptRec, &Context, 0);
614 }
615
616 /* EOF */