Main Page | Class Hierarchy | Class List | File List | Class Members | File Members

exdsptch.c

Go to the documentation of this file.
00001 /*++ 00002 00003 Copyright (c) 1989 Microsoft Corporation 00004 00005 Module Name: 00006 00007 exdsptch.c 00008 00009 Abstract: 00010 00011 This module implements the dispatching of exception and the unwinding of 00012 procedure call frames. 00013 00014 Author: 00015 00016 David N. Cutler (davec) 13-Aug-1989 00017 00018 Environment: 00019 00020 Any mode. 00021 00022 Revision History: 00023 00024 10 april 90 bryanwi 00025 00026 Port to the 386. 00027 00028 --*/ 00029 00030 #include "ntrtlp.h" 00031 00032 00033 // 00034 // Dispatcher context structure definition. 00035 // 00036 00037 typedef struct _DISPATCHER_CONTEXT { 00038 PEXCEPTION_REGISTRATION_RECORD RegistrationPointer; 00039 } DISPATCHER_CONTEXT; 00040 00041 // 00042 // Execute handler for exception function prototype. 00043 // 00044 00045 EXCEPTION_DISPOSITION 00046 RtlpExecuteHandlerForException ( 00047 IN PEXCEPTION_RECORD ExceptionRecord, 00048 IN PVOID EstablisherFrame, 00049 IN OUT PCONTEXT ContextRecord, 00050 IN OUT PVOID DispatcherContext, 00051 IN PEXCEPTION_ROUTINE ExceptionRoutine 00052 ); 00053 00054 // 00055 // Execute handler for unwind function prototype. 00056 // 00057 00058 EXCEPTION_DISPOSITION 00059 RtlpExecuteHandlerForUnwind ( 00060 IN PEXCEPTION_RECORD ExceptionRecord, 00061 IN PVOID EstablisherFrame, 00062 IN OUT PCONTEXT ContextRecord, 00063 IN OUT PVOID DispatcherContext, 00064 IN PEXCEPTION_ROUTINE ExceptionRoutine 00065 ); 00066 00067 00068 00069 BOOLEAN 00070 RtlDispatchException ( 00071 IN PEXCEPTION_RECORD ExceptionRecord, 00072 IN PCONTEXT ContextRecord 00073 ) 00074 00075 /*++ 00076 00077 Routine Description: 00078 00079 This function attempts to dispatch an exception to a call frame based 00080 handler by searching backwards through the stack based call frames. The 00081 search begins with the frame specified in the context record and continues 00082 backward until either a handler is found that handles the exception, the 00083 stack is found to be invalid (i.e., out of limits or unaligned), or the end 00084 of the call hierarchy is reached. 00085 00086 Arguments: 00087 00088 ExceptionRecord - Supplies a pointer to an exception record. 00089 00090 ContextRecord - Supplies a pointer to a context record. 00091 00092 Return Value: 00093 00094 If the exception is handled by one of the frame based handlers, then 00095 a value of TRUE is returned. Otherwise a value of FALSE is returned. 00096 00097 --*/ 00098 00099 { 00100 00101 DISPATCHER_CONTEXT DispatcherContext; 00102 EXCEPTION_DISPOSITION Disposition; 00103 PEXCEPTION_REGISTRATION_RECORD RegistrationPointer; 00104 PEXCEPTION_REGISTRATION_RECORD NestedRegistration; 00105 ULONG HighAddress; 00106 ULONG HighLimit; 00107 ULONG LowLimit; 00108 EXCEPTION_RECORD ExceptionRecord1; 00109 00110 #if !defined(WX86_i386) 00111 00112 ULONG Index; 00113 00114 #endif 00115 00116 // 00117 // Get current stack limits. 00118 // 00119 00120 RtlpGetStackLimits(&LowLimit, &HighLimit); 00121 00122 // 00123 // Start with the frame specified by the context record and search 00124 // backwards through the call frame hierarchy attempting to find an 00125 // exception handler that will handler the exception. 00126 // 00127 00128 RegistrationPointer = RtlpGetRegistrationHead(); 00129 NestedRegistration = 0; 00130 00131 while (RegistrationPointer != EXCEPTION_CHAIN_END) { 00132 00133 // 00134 // If the call frame is not within the specified stack limits or the 00135 // call frame is unaligned, then set the stack invalid flag in the 00136 // exception record and return FALSE. Else check to determine if the 00137 // frame has an exception handler. 00138 // 00139 00140 HighAddress = (ULONG)RegistrationPointer + 00141 sizeof(EXCEPTION_REGISTRATION_RECORD); 00142 00143 if ( ((ULONG)RegistrationPointer < LowLimit) || 00144 (HighAddress > HighLimit) || 00145 (((ULONG)RegistrationPointer & 0x3) != 0) ) { 00146 00147 #if defined(NTOS_KERNEL_RUNTIME) 00148 00149 // 00150 // Allow for the possibility that the problem occured on the 00151 // DPC stack. 00152 // 00153 00154 ULONG TestAddress = (ULONG)RegistrationPointer; 00155 00156 if (((TestAddress & 0x3) == 0) && 00157 KeGetCurrentIrql() >= DISPATCH_LEVEL) { 00158 00159 PKPRCB Prcb = KeGetCurrentPrcb(); 00160 ULONG DpcStack = (ULONG)Prcb->DpcStack; 00161 00162 if ((Prcb->DpcRoutineActive) && 00163 (HighAddress <= DpcStack) && 00164 (TestAddress >= DpcStack - KERNEL_STACK_SIZE)) { 00165 00166 // 00167 // This error occured on the DPC stack, switch 00168 // stack limits to the DPC stack and restart 00169 // the loop. 00170 // 00171 00172 HighLimit = DpcStack; 00173 LowLimit = DpcStack - KERNEL_STACK_SIZE; 00174 continue; 00175 } 00176 } 00177 00178 #endif 00179 00180 ExceptionRecord->ExceptionFlags |= EXCEPTION_STACK_INVALID; 00181 return FALSE; 00182 } 00183 00184 #if !defined(WX86_i386) 00185 00186 // 00187 // The handler must be executed by calling another routine 00188 // that is written in assembler. This is required because 00189 // up level addressing of the handler information is required 00190 // when a nested exception is encountered. 00191 // 00192 00193 if (NtGlobalFlag & FLG_ENABLE_EXCEPTION_LOGGING) { 00194 Index = RtlpLogExceptionHandler( 00195 ExceptionRecord, 00196 ContextRecord, 00197 0, 00198 (PULONG)RegistrationPointer, 00199 4 * sizeof(ULONG)); 00200 // can't use sizeof(EXCEPTION_REGISTRATION_RECORD 00201 // because we need the 2 dwords above it. 00202 } 00203 #endif 00204 00205 Disposition = RtlpExecuteHandlerForException( 00206 ExceptionRecord, 00207 (PVOID)RegistrationPointer, 00208 ContextRecord, 00209 (PVOID)&DispatcherContext, 00210 (PEXCEPTION_ROUTINE)RegistrationPointer->Handler); 00211 00212 #if !defined(WX86_i386) 00213 if (NtGlobalFlag & FLG_ENABLE_EXCEPTION_LOGGING) { 00214 RtlpLogLastExceptionDisposition(Index, Disposition); 00215 } 00216 #endif 00217 00218 00219 // 00220 // If the current scan is within a nested context and the frame 00221 // just examined is the end of the context region, then clear 00222 // the nested context frame and the nested exception in the 00223 // exception flags. 00224 // 00225 00226 if (NestedRegistration == RegistrationPointer) { 00227 ExceptionRecord->ExceptionFlags &= (~EXCEPTION_NESTED_CALL); 00228 NestedRegistration = 0; 00229 } 00230 00231 // 00232 // Case on the handler disposition. 00233 // 00234 00235 switch (Disposition) { 00236 00237 // 00238 // The disposition is to continue execution. If the 00239 // exception is not continuable, then raise the exception 00240 // STATUS_NONCONTINUABLE_EXCEPTION. Otherwise return 00241 // TRUE. 00242 // 00243 00244 case ExceptionContinueExecution : 00245 if ((ExceptionRecord->ExceptionFlags & 00246 EXCEPTION_NONCONTINUABLE) != 0) { 00247 ExceptionRecord1.ExceptionCode = 00248 STATUS_NONCONTINUABLE_EXCEPTION; 00249 ExceptionRecord1.ExceptionFlags = EXCEPTION_NONCONTINUABLE; 00250 ExceptionRecord1.ExceptionRecord = ExceptionRecord; 00251 ExceptionRecord1.NumberParameters = 0; 00252 RtlRaiseException(&ExceptionRecord1); 00253 } else { 00254 return TRUE; 00255 } 00256 00257 // 00258 // The disposition is to continue the search. Get next 00259 // frame address and continue the search. 00260 // 00261 00262 case ExceptionContinueSearch : 00263 break; 00264 00265 // 00266 // The disposition is nested exception. Set the nested 00267 // context frame to the establisher frame address and set 00268 // nested exception in the exception flags. 00269 // 00270 00271 case ExceptionNestedException : 00272 ExceptionRecord->ExceptionFlags |= EXCEPTION_NESTED_CALL; 00273 if (DispatcherContext.RegistrationPointer > NestedRegistration) { 00274 NestedRegistration = DispatcherContext.RegistrationPointer; 00275 } 00276 break; 00277 00278 // 00279 // All other disposition values are invalid. Raise 00280 // invalid disposition exception. 00281 // 00282 00283 default : 00284 ExceptionRecord1.ExceptionCode = STATUS_INVALID_DISPOSITION; 00285 ExceptionRecord1.ExceptionFlags = EXCEPTION_NONCONTINUABLE; 00286 ExceptionRecord1.ExceptionRecord = ExceptionRecord; 00287 ExceptionRecord1.NumberParameters = 0; 00288 RtlRaiseException(&ExceptionRecord1); 00289 break; 00290 } 00291 00292 // 00293 // If chain goes in wrong direction or loops, report an 00294 // invalid exception stack, otherwise go on to the next one. 00295 // 00296 00297 RegistrationPointer = RegistrationPointer->Next; 00298 } 00299 return FALSE; 00300 } 00301 00302 VOID 00303 RtlUnwind ( 00304 IN PVOID TargetFrame OPTIONAL, 00305 IN PVOID TargetIp OPTIONAL, 00306 IN PEXCEPTION_RECORD ExceptionRecord OPTIONAL, 00307 IN PVOID ReturnValue 00308 ) 00309 00310 /*++ 00311 00312 Routine Description: 00313 00314 This function initiates an unwind of procedure call frames. The machine 00315 state at the time of the call to unwind is captured in a context record 00316 and the unwinding flag is set in the exception flags of the exception 00317 record. If the TargetFrame parameter is not specified, then the exit unwind 00318 flag is also set in the exception flags of the exception record. A backward 00319 walk through the procedure call frames is then performed to find the target 00320 of the unwind operation. 00321 00322 N.B. The captured context passed to unwinding handlers will not be 00323 a completely accurate context set for the 386. This is because 00324 there isn't a standard stack frame in which registers are stored. 00325 00326 Only the integer registers are affected. The segement and 00327 control registers (ebp, esp) will have correct values for 00328 the flat 32 bit environment. 00329 00330 N.B. If you change the number of arguments, make sure you change the 00331 adjustment of ESP after the call to RtlpCaptureContext (for 00332 STDCALL calling convention) 00333 00334 Arguments: 00335 00336 TargetFrame - Supplies an optional pointer to the call frame that is the 00337 target of the unwind. If this parameter is not specified, then an exit 00338 unwind is performed. 00339 00340 TargetIp - Supplies an optional instruction address that specifies the 00341 continuation address of the unwind. This address is ignored if the 00342 target frame parameter is not specified. 00343 00344 ExceptionRecord - Supplies an optional pointer to an exception record. 00345 00346 ReturnValue - Supplies a value that is to be placed in the integer 00347 function return register just before continuing execution. 00348 00349 Return Value: 00350 00351 None. 00352 00353 --*/ 00354 00355 { 00356 PCONTEXT ContextRecord; 00357 CONTEXT ContextRecord1; 00358 DISPATCHER_CONTEXT DispatcherContext; 00359 EXCEPTION_DISPOSITION Disposition; 00360 PEXCEPTION_REGISTRATION_RECORD RegistrationPointer; 00361 PEXCEPTION_REGISTRATION_RECORD PriorPointer; 00362 ULONG HighAddress; 00363 ULONG HighLimit; 00364 ULONG LowLimit; 00365 EXCEPTION_RECORD ExceptionRecord1; 00366 EXCEPTION_RECORD ExceptionRecord2; 00367 00368 // 00369 // Get current stack limits. 00370 // 00371 00372 RtlpGetStackLimits(&LowLimit, &HighLimit); 00373 00374 // 00375 // If an exception record is not specified, then build a local exception 00376 // record for use in calling exception handlers during the unwind operation. 00377 // 00378 00379 if (ARGUMENT_PRESENT(ExceptionRecord) == FALSE) { 00380 ExceptionRecord = &ExceptionRecord1; 00381 ExceptionRecord1.ExceptionCode = STATUS_UNWIND; 00382 ExceptionRecord1.ExceptionFlags = 0; 00383 ExceptionRecord1.ExceptionRecord = NULL; 00384 ExceptionRecord1.ExceptionAddress = RtlpGetReturnAddress(); 00385 ExceptionRecord1.NumberParameters = 0; 00386 } 00387 00388 // 00389 // If the target frame of the unwind is specified, then set EXCEPTION_UNWINDING 00390 // flag in the exception flags. Otherwise set both EXCEPTION_EXIT_UNWIND and 00391 // EXCEPTION_UNWINDING flags in the exception flags. 00392 // 00393 00394 if (ARGUMENT_PRESENT(TargetFrame) == TRUE) { 00395 ExceptionRecord->ExceptionFlags |= EXCEPTION_UNWINDING; 00396 } else { 00397 ExceptionRecord->ExceptionFlags |= (EXCEPTION_UNWINDING | 00398 EXCEPTION_EXIT_UNWIND); 00399 } 00400 00401 // 00402 // Capture the context. 00403 // 00404 00405 ContextRecord = &ContextRecord1; 00406 ContextRecord1.ContextFlags = CONTEXT_INTEGER | CONTEXT_CONTROL | CONTEXT_SEGMENTS; 00407 RtlpCaptureContext(ContextRecord); 00408 00409 #ifdef STD_CALL 00410 // 00411 // Adjust captured context to pop our arguments off the stack 00412 // 00413 ContextRecord->Esp += sizeof(TargetFrame) + 00414 sizeof(TargetIp) + 00415 sizeof(ExceptionRecord) + 00416 sizeof(ReturnValue); 00417 #endif 00418 ContextRecord->Eax = (ULONG)ReturnValue; 00419 00420 // 00421 // Scan backward through the call frame hierarchy, calling exception 00422 // handlers as they are encountered, until the target frame of the unwind 00423 // is reached. 00424 // 00425 00426 RegistrationPointer = RtlpGetRegistrationHead(); 00427 while (RegistrationPointer != EXCEPTION_CHAIN_END) { 00428 00429 // 00430 // If this is the target of the unwind, then continue execution 00431 // by calling the continue system service. 00432 // 00433 00434 if ((ULONG)RegistrationPointer == (ULONG)TargetFrame) { 00435 ZwContinue(ContextRecord, FALSE); 00436 00437 // 00438 // If the target frame is lower in the stack than the current frame, 00439 // then raise STATUS_INVALID_UNWIND exception. 00440 // 00441 00442 } else if ( (ARGUMENT_PRESENT(TargetFrame) == TRUE) && 00443 ((ULONG)TargetFrame < (ULONG)RegistrationPointer) ) { 00444 ExceptionRecord2.ExceptionCode = STATUS_INVALID_UNWIND_TARGET; 00445 ExceptionRecord2.ExceptionFlags = EXCEPTION_NONCONTINUABLE; 00446 ExceptionRecord2.ExceptionRecord = ExceptionRecord; 00447 ExceptionRecord2.NumberParameters = 0; 00448 RtlRaiseException(&ExceptionRecord2); 00449 } 00450 00451 // 00452 // If the call frame is not within the specified stack limits or the 00453 // call frame is unaligned, then raise the exception STATUS_BAD_STACK. 00454 // Else restore the state from the specified frame to the context 00455 // record. 00456 // 00457 00458 HighAddress = (ULONG)RegistrationPointer + 00459 sizeof(EXCEPTION_REGISTRATION_RECORD); 00460 00461 if ( ((ULONG)RegistrationPointer < LowLimit) || 00462 (HighAddress > HighLimit) || 00463 (((ULONG)RegistrationPointer & 0x3) != 0) ) { 00464 00465 #if defined(NTOS_KERNEL_RUNTIME) 00466 00467 // 00468 // Allow for the possibility that the problem occured on the 00469 // DPC stack. 00470 // 00471 00472 ULONG TestAddress = (ULONG)RegistrationPointer; 00473 00474 if (((TestAddress & 0x3) == 0) && 00475 KeGetCurrentIrql() >= DISPATCH_LEVEL) { 00476 00477 PKPRCB Prcb = KeGetCurrentPrcb(); 00478 ULONG DpcStack = (ULONG)Prcb->DpcStack; 00479 00480 if ((Prcb->DpcRoutineActive) && 00481 (HighAddress <= DpcStack) && 00482 (TestAddress >= DpcStack - KERNEL_STACK_SIZE)) { 00483 00484 // 00485 // This error occured on the DPC stack, switch 00486 // stack limits to the DPC stack and restart 00487 // the loop. 00488 // 00489 00490 HighLimit = DpcStack; 00491 LowLimit = DpcStack - KERNEL_STACK_SIZE; 00492 continue; 00493 } 00494 } 00495 00496 #endif 00497 00498 ExceptionRecord2.ExceptionCode = STATUS_BAD_STACK; 00499 ExceptionRecord2.ExceptionFlags = EXCEPTION_NONCONTINUABLE; 00500 ExceptionRecord2.ExceptionRecord = ExceptionRecord; 00501 ExceptionRecord2.NumberParameters = 0; 00502 RtlRaiseException(&ExceptionRecord2); 00503 } else { 00504 00505 // 00506 // The handler must be executed by calling another routine 00507 // that is written in assembler. This is required because 00508 // up level addressing of the handler information is required 00509 // when a collided unwind is encountered. 00510 // 00511 00512 Disposition = RtlpExecuteHandlerForUnwind( 00513 ExceptionRecord, 00514 (PVOID)RegistrationPointer, 00515 ContextRecord, 00516 (PVOID)&DispatcherContext, 00517 RegistrationPointer->Handler); 00518 00519 // 00520 // Case on the handler disposition. 00521 // 00522 00523 switch (Disposition) { 00524 00525 // 00526 // The disposition is to continue the search. Get next 00527 // frame address and continue the search. 00528 // 00529 00530 case ExceptionContinueSearch : 00531 break; 00532 00533 // 00534 // The disposition is colided unwind. Maximize the target 00535 // of the unwind and change the context record pointer. 00536 // 00537 00538 case ExceptionCollidedUnwind : 00539 00540 // 00541 // Pick up the registration pointer that was active at 00542 // the time of the unwind, and simply continue. 00543 // 00544 00545 RegistrationPointer = DispatcherContext.RegistrationPointer; 00546 break; 00547 00548 00549 // 00550 // All other disposition values are invalid. Raise 00551 // invalid disposition exception. 00552 // 00553 00554 default : 00555 ExceptionRecord2.ExceptionCode = STATUS_INVALID_DISPOSITION; 00556 ExceptionRecord2.ExceptionFlags = EXCEPTION_NONCONTINUABLE; 00557 ExceptionRecord2.ExceptionRecord = ExceptionRecord; 00558 ExceptionRecord2.NumberParameters = 0; 00559 RtlRaiseException(&ExceptionRecord2); 00560 break; 00561 } 00562 00563 // 00564 // Step to next registration record 00565 // 00566 00567 PriorPointer = RegistrationPointer; 00568 RegistrationPointer = RegistrationPointer->Next; 00569 00570 // 00571 // Unlink the unwind handler, since it's been called. 00572 // 00573 00574 RtlpUnlinkHandler(PriorPointer); 00575 00576 // 00577 // If chain goes in wrong direction or loops, raise an 00578 // exception. 00579 // 00580 00581 } 00582 } 00583 00584 if (TargetFrame == EXCEPTION_CHAIN_END) { 00585 00586 // 00587 // Caller simply wants to unwind all exception records. 00588 // This differs from an exit_unwind in that no "exit" is desired. 00589 // Do a normal continue, since we've effectively found the 00590 // "target" the caller wanted. 00591 // 00592 00593 ZwContinue(ContextRecord, FALSE); 00594 00595 } else { 00596 00597 // 00598 // Either (1) a real exit unwind was performed, or (2) the 00599 // specified TargetFrame is not present in the exception handler 00600 // list. In either case, give debugger and subsystem a chance 00601 // to see the unwind. 00602 // 00603 00604 ZwRaiseException(ExceptionRecord, ContextRecord, FALSE); 00605 00606 } 00607 return; 00608 }

Generated on Sat May 15 19:39:57 2004 for test by doxygen 1.3.7