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) 1993 IBM Corporation and 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 for PowerPC. 00013 00014 Author: 00015 00016 Rick Simpson 16-Aug-1993 00017 00018 based on MIPS version by David N. Cutler (davec) 11-Sep-1990 00019 00020 Environment: 00021 00022 Any mode. 00023 00024 Revision History: 00025 00026 Tom Wood (twood) 19-Aug-1994 00027 Update to use RtlVirtualUnwind even when there isn't a function table 00028 entry. Add stack limit parameters to RtlVirtualUnwind. 00029 00030 Changed RtlLookupFunctionEntry to deal with the indirect entries. 00031 00032 --*/ 00033 00034 #include "ntrtlp.h" 00035 00036 // 00037 // Define local macros. 00038 // 00039 00040 #ifndef ROS_DEBUG 00041 #ifndef READ_ULONG 00042 #define READ_ULONG(addr,dest) dest = (*((PULONG)(addr))) 00043 #define READ_DOUBLE(addr,dest) dest = (*((PDOUBLE)(addr))) 00044 #endif 00045 00046 #include "vunwind.c" 00047 #endif // ROS_DEBUG 00048 00049 // 00050 // Define local macros. 00051 // 00052 // Raise noncontinuable exception with associated exception record. 00053 // 00054 00055 #define RAISE_EXCEPTION(Status, ExceptionRecordt) { \ 00056 EXCEPTION_RECORD ExceptionRecordn; \ 00057 \ 00058 ExceptionRecordn.ExceptionCode = Status; \ 00059 ExceptionRecordn.ExceptionFlags = EXCEPTION_NONCONTINUABLE; \ 00060 ExceptionRecordn.ExceptionRecord = ExceptionRecordt; \ 00061 ExceptionRecordn.NumberParameters = 0; \ 00062 RtlRaiseException(&ExceptionRecordn); \ 00063 } 00064 00065 // 00066 // Define private function prototypes. 00067 // 00068 00069 VOID 00070 RtlpRestoreContext ( 00071 IN PCONTEXT Context, 00072 IN PEXCEPTION_RECORD ExceptionRecord OPTIONAL 00073 ); 00074 00075 VOID 00076 RtlpRaiseException ( 00077 IN PEXCEPTION_RECORD ExceptionRecord 00078 ); 00079 00080 VOID 00081 RtlpRaiseStatus ( 00082 IN NTSTATUS Status 00083 ); 00084 00085 ULONG 00086 RtlpVirtualUnwind ( 00087 IN ULONG ControlPc, 00088 IN PRUNTIME_FUNCTION FunctionEntry, 00089 IN PCONTEXT ContextRecord, 00090 OUT PBOOLEAN InFunction, 00091 OUT PULONG EstablisherFrame, 00092 IN OUT PKNONVOLATILE_CONTEXT_POINTERS ContextPointers OPTIONAL, 00093 IN ULONG LowStackLimit, 00094 IN ULONG HighStackLimit 00095 ); 00096 00097 BOOLEAN 00098 RtlDispatchException ( 00099 IN PEXCEPTION_RECORD ExceptionRecord, 00100 IN PCONTEXT ContextRecord 00101 ) 00102 00103 /*++ 00104 00105 Routine Description: 00106 00107 This function attempts to dispatch an exception to a frame based 00108 handler by searching backwards through the stack based call frames. 00109 The search begins with the frame specified in the context record and 00110 continues backward until either a handler is found that handles the 00111 exception, the stack is found to be invalid (i.e., out of limits or 00112 unaligned), or the end of the call hierarchy is reached. 00113 00114 As each frame is encountered, the PC where control left the corresponding 00115 function is determined and used to lookup exception handler information 00116 in the runtime function table built by the linker. If the respective 00117 routine has an exception handler, then the handler is called. If the 00118 handler does not handle the exception, then the prologue of the routine 00119 is executed backwards to "unwind" the effect of the prologue and then 00120 the next frame is examined. 00121 00122 Arguments: 00123 00124 ExceptionRecord - Supplies a pointer to an exception record. 00125 00126 ContextRecord - Supplies a pointer to a context record. 00127 00128 Return Value: 00129 00130 If the exception is handled by one of the frame based handlers, then 00131 a value of TRUE is returned. Otherwise a value of FALSE is returned. 00132 00133 --*/ 00134 00135 { 00136 00137 CONTEXT ContextRecord1; 00138 ULONG ControlPc; 00139 DISPATCHER_CONTEXT DispatcherContext; 00140 EXCEPTION_DISPOSITION Disposition; 00141 ULONG EstablisherFrame; 00142 ULONG ExceptionFlags; 00143 PRUNTIME_FUNCTION FunctionEntry; 00144 BOOLEAN InFunction; 00145 ULONG HighLimit; 00146 ULONG LowLimit; 00147 ULONG NestedFrame; 00148 ULONG NextPc; 00149 00150 // 00151 // Get current stack limits, copy the context record, get the initial 00152 // PC value, capture the exception flags, and set the nested exception 00153 // frame pointer. 00154 // 00155 00156 RtlpGetStackLimits(&LowLimit, &HighLimit); 00157 RtlMoveMemory(&ContextRecord1, ContextRecord, sizeof(CONTEXT)); 00158 ControlPc = ContextRecord1.Iar; 00159 ExceptionFlags = ExceptionRecord->ExceptionFlags & EXCEPTION_NONCONTINUABLE; 00160 NestedFrame = 0; 00161 00162 // 00163 // Start with the frame specified by the context record and search 00164 // backwards through the call frame hierarchy attempting to find an 00165 // exception handler that will handle the exception. 00166 // 00167 00168 do { 00169 00170 // 00171 // Lookup the function table entry using the point at which control 00172 // left the procedure. 00173 // 00174 00175 FunctionEntry = RtlLookupFunctionEntry(ControlPc); 00176 00177 // 00178 // Virtually unwind to the caller of the current routine to obtain 00179 // the virtual frame pointer of the establisher and the next PC. 00180 // 00181 00182 NextPc = RtlVirtualUnwind(ControlPc, 00183 FunctionEntry, 00184 &ContextRecord1, 00185 &InFunction, 00186 &EstablisherFrame, 00187 NULL, 00188 LowLimit, 00189 HighLimit); 00190 00191 // 00192 // If there is a function table entry for the routine, check if 00193 // there is an exception handler for the frame. 00194 // 00195 00196 if (FunctionEntry != NULL) { 00197 // 00198 // If the virtual frame pointer is not within the specified stack 00199 // limits or the virtual frame pointer is unaligned, then set the 00200 // stack invalid flag in the exception record and return exception 00201 // not handled. Otherwise, check if the current routine has an 00202 // exception handler. 00203 // 00204 00205 if ((EstablisherFrame < LowLimit) || (EstablisherFrame > HighLimit) || 00206 ((EstablisherFrame & 0x7) != 0)) { 00207 ExceptionFlags |= EXCEPTION_STACK_INVALID; 00208 break; 00209 00210 } else if ((FunctionEntry->ExceptionHandler != NULL) && InFunction) { 00211 ULONG Index; 00212 00213 // 00214 // The frame has an exception handler. The handler must be 00215 // executed by calling another routine that is written in 00216 // assembler. This is required because up level addressing 00217 // of the handler information is required when a nested 00218 // exception is encountered. 00219 // 00220 00221 DispatcherContext.ControlPc = ControlPc; 00222 DispatcherContext.FunctionEntry = FunctionEntry; 00223 DispatcherContext.EstablisherFrame = EstablisherFrame; 00224 DispatcherContext.ContextRecord = ContextRecord; 00225 ExceptionRecord->ExceptionFlags = ExceptionFlags; 00226 00227 if (NtGlobalFlag & FLG_ENABLE_EXCEPTION_LOGGING) { 00228 Index = RtlpLogExceptionHandler( 00229 ExceptionRecord, 00230 ContextRecord, 00231 ControlPc, 00232 FunctionEntry, 00233 sizeof(RUNTIME_FUNCTION)); 00234 } 00235 00236 Disposition = 00237 RtlpExecuteHandlerForException(ExceptionRecord, 00238 EstablisherFrame, 00239 ContextRecord, 00240 &DispatcherContext, 00241 FunctionEntry->ExceptionHandler); 00242 00243 if (NtGlobalFlag & FLG_ENABLE_EXCEPTION_LOGGING) { 00244 RtlpLogLastExceptionDisposition(Index, Disposition); 00245 } 00246 00247 ExceptionFlags |= 00248 (ExceptionRecord->ExceptionFlags & EXCEPTION_NONCONTINUABLE); 00249 00250 // 00251 // If the current scan is within a nested context and the frame 00252 // just examined is the end of the nested region, then clear 00253 // the nested context frame and the nested exception flag in 00254 // the exception flags. 00255 // 00256 00257 if (NestedFrame == EstablisherFrame) { 00258 ExceptionFlags &= (~EXCEPTION_NESTED_CALL); 00259 NestedFrame = 0; 00260 } 00261 00262 // 00263 // Case on the handler disposition. 00264 // 00265 00266 switch (Disposition) { 00267 00268 // 00269 // The disposition is to continue execution. 00270 // 00271 // If the exception is not continuable, then raise the 00272 // exception STATUS_NONCONTINUABLE_EXCEPTION. Otherwise 00273 // return exception handled. 00274 // 00275 00276 case ExceptionContinueExecution : 00277 if ((ExceptionFlags & EXCEPTION_NONCONTINUABLE) != 0) { 00278 RAISE_EXCEPTION(STATUS_NONCONTINUABLE_EXCEPTION, ExceptionRecord); 00279 00280 } else { 00281 return TRUE; 00282 } 00283 00284 // 00285 // The disposition is to continue the search. 00286 // 00287 // Get next frame address and continue the search. 00288 // 00289 00290 case ExceptionContinueSearch : 00291 break; 00292 00293 // 00294 // The disposition is nested exception. 00295 // 00296 // Set the nested context frame to the establisher frame 00297 // address and set the nested exception flag in the 00298 // exception flags. 00299 // 00300 00301 case ExceptionNestedException : 00302 ExceptionFlags |= EXCEPTION_NESTED_CALL; 00303 if (DispatcherContext.EstablisherFrame > NestedFrame) { 00304 NestedFrame = DispatcherContext.EstablisherFrame; 00305 } 00306 00307 break; 00308 00309 // 00310 // All other disposition values are invalid. 00311 // 00312 // Raise invalid disposition exception. 00313 // 00314 00315 default : 00316 RAISE_EXCEPTION(STATUS_INVALID_DISPOSITION, ExceptionRecord); 00317 } 00318 } 00319 00320 } else { 00321 00322 // 00323 // If the next control PC is the same as the old control PC, then 00324 // the function table is not correctly formed. 00325 // 00326 00327 if (NextPc == ControlPc) { 00328 break; 00329 } 00330 } 00331 00332 // 00333 // Set point at which control left the previous routine. 00334 // 00335 00336 ControlPc = NextPc; 00337 } while (ContextRecord1.Gpr1 < HighLimit); 00338 00339 // 00340 // Set final exception flags and return exception not handled. 00341 // 00342 00343 ExceptionRecord->ExceptionFlags = ExceptionFlags; 00344 return FALSE; 00345 } 00346 00347 PRUNTIME_FUNCTION 00348 RtlLookupFunctionEntry ( 00349 IN ULONG ControlPc 00350 ) 00351 00352 /*++ 00353 00354 Routine Description: 00355 00356 This function searches the currently active function tables for an entry 00357 that corresponds to the specified PC value. 00358 00359 Arguments: 00360 00361 ControlPc - Supplies the address of an instruction within the specified 00362 function. 00363 00364 Return Value: 00365 00366 If there is no entry in the function table for the specified PC, then 00367 NULL is returned. Otherwise, the address of the function table entry 00368 that corresponds to the specified PC is returned. 00369 00370 --*/ 00371 00372 { 00373 00374 PRUNTIME_FUNCTION FunctionEntry; 00375 PRUNTIME_FUNCTION FunctionTable; 00376 ULONG SizeOfExceptionTable; 00377 LONG High; 00378 PVOID ImageBase; 00379 LONG Low; 00380 LONG Middle; 00381 00382 // 00383 // Search for the image that includes the specified PC value. 00384 // 00385 00386 ImageBase = RtlPcToFileHeader((PVOID)ControlPc, &ImageBase); 00387 00388 // 00389 // If an image is found that includes the specified PC, then locate the 00390 // function table for the image. 00391 // 00392 00393 if (ImageBase != NULL) { 00394 FunctionTable = (PRUNTIME_FUNCTION)RtlImageDirectoryEntryToData( 00395 ImageBase, TRUE, IMAGE_DIRECTORY_ENTRY_EXCEPTION, 00396 &SizeOfExceptionTable); 00397 00398 // 00399 // If a function table is located, then search the function table 00400 // for a function table entry for the specified PC. 00401 // 00402 00403 if (FunctionTable != NULL) { 00404 00405 // 00406 // Initialize search indicies. 00407 // 00408 00409 Low = 0; 00410 High = (SizeOfExceptionTable / sizeof(RUNTIME_FUNCTION)) - 1; 00411 00412 // 00413 // Perform binary search on the function table for a function table 00414 // entry that subsumes the specified PC. 00415 // 00416 00417 while (High >= Low) { 00418 00419 // 00420 // Compute next probe index and test entry. If the specified PC 00421 // is greater than of equal to the beginning address and less 00422 // than the ending address of the function table entry, then 00423 // return the address of the function table entry. Otherwise, 00424 // continue the search. 00425 // 00426 00427 Middle = (Low + High) >> 1; 00428 FunctionEntry = &FunctionTable[Middle]; 00429 if (ControlPc < FunctionEntry->BeginAddress) { 00430 High = Middle - 1; 00431 00432 } else if (ControlPc >= FunctionEntry->EndAddress) { 00433 Low = Middle + 1; 00434 00435 } else { 00436 00437 // 00438 // The capability exists for more than one function entry 00439 // to map to the same function. This permits a function to 00440 // have (within reason) discontiguous code segment(s). If 00441 // PrologEndAddress is out of range, it is re-interpreted 00442 // as a pointer to the primary function table entry for 00443 // that function. The out of range test takes into account 00444 // the redundant encoding of millicode and glue code. 00445 // 00446 00447 if (((FunctionEntry->PrologEndAddress < FunctionEntry->BeginAddress) || 00448 (FunctionEntry->PrologEndAddress > FunctionEntry->EndAddress)) && 00449 (FunctionEntry->PrologEndAddress & 3) == 0) { 00450 FunctionEntry = (PRUNTIME_FUNCTION)FunctionEntry->PrologEndAddress; 00451 } 00452 return FunctionEntry; 00453 } 00454 } 00455 } 00456 } 00457 00458 // 00459 // A function table entry for the specified PC was not found. 00460 // 00461 00462 return NULL; 00463 } 00464 00465 VOID 00466 RtlRaiseException ( 00467 IN PEXCEPTION_RECORD ExceptionRecord 00468 ) 00469 00470 /*++ 00471 00472 Routine Description: 00473 00474 This function raises a software exception by building a context record 00475 and calling the raise exception system service. 00476 00477 N.B. This routine is a shell routine that simply calls another routine 00478 to do the real work. The reason this is done is to avoid a problem 00479 in try/finally scopes where the last statement in the scope is a 00480 call to raise an exception. 00481 00482 Arguments: 00483 00484 ExceptionRecord - Supplies a pointer to an exception record. 00485 00486 Return Value: 00487 00488 None. 00489 00490 --*/ 00491 00492 { 00493 00494 RtlpRaiseException(ExceptionRecord); 00495 return; 00496 } 00497 00498 VOID 00499 RtlpRaiseException ( 00500 IN PEXCEPTION_RECORD ExceptionRecord 00501 ) 00502 00503 /*++ 00504 00505 Routine Description: 00506 00507 This function raises a software exception by building a context record 00508 and calling the raise exception system service. 00509 00510 Arguments: 00511 00512 ExceptionRecord - Supplies a pointer to an exception record. 00513 00514 Return Value: 00515 00516 None. 00517 00518 --*/ 00519 00520 { 00521 00522 ULONG ControlPc; 00523 CONTEXT ContextRecord; 00524 ULONG EstablisherFrame; 00525 PRUNTIME_FUNCTION FunctionEntry; 00526 BOOLEAN InFunction; 00527 ULONG NextPc; 00528 NTSTATUS Status; 00529 00530 // 00531 // Capture the current context, virtually unwind to the caller of this 00532 // routine, set the fault instruction address to that of the caller, and 00533 // call the raise exception system service. 00534 // 00535 00536 RtlCaptureContext(&ContextRecord); 00537 ControlPc = ContextRecord.Lr - 4; 00538 FunctionEntry = RtlLookupFunctionEntry(ControlPc); 00539 NextPc = RtlVirtualUnwind(ControlPc, 00540 FunctionEntry, 00541 &ContextRecord, 00542 &InFunction, 00543 &EstablisherFrame, 00544 NULL, 00545 0, 00546 0xffffffff); 00547 00548 ContextRecord.Iar = NextPc + 4; 00549 ExceptionRecord->ExceptionAddress = (PVOID)ContextRecord.Iar; 00550 Status = ZwRaiseException(ExceptionRecord, &ContextRecord, TRUE); 00551 00552 // 00553 // There should never be a return from this system service unless 00554 // there is a problem with the argument list itself. Raise another 00555 // exception specifying the status value returned. 00556 // 00557 00558 RtlRaiseStatus(Status); 00559 return; 00560 } 00561 00562 VOID 00563 RtlRaiseStatus ( 00564 IN NTSTATUS Status 00565 ) 00566 00567 /*++ 00568 00569 Routine Description: 00570 00571 This function raises an exception with the specified status value. The 00572 exception is marked as noncontinuable with no parameters. 00573 00574 N.B. This routine is a shell routine that simply calls another routine 00575 to do the real work. The reason this is done is to avoid a problem 00576 in try/finally scopes where the last statement in the scope is a 00577 call to raise an exception. 00578 00579 Arguments: 00580 00581 Status - Supplies the status value to be used as the exception code 00582 for the exception that is to be raised. 00583 00584 Return Value: 00585 00586 None. 00587 00588 --*/ 00589 00590 { 00591 00592 RtlpRaiseStatus(Status); 00593 return; 00594 } 00595 00596 VOID 00597 RtlpRaiseStatus ( 00598 IN NTSTATUS Status 00599 ) 00600 00601 /*++ 00602 00603 Routine Description: 00604 00605 This function raises an exception with the specified status value. The 00606 exception is marked as noncontinuable with no parameters. 00607 00608 Arguments: 00609 00610 Status - Supplies the status value to be used as the exception code 00611 for the exception that is to be raised. 00612 00613 Return Value: 00614 00615 None. 00616 00617 --*/ 00618 00619 { 00620 00621 ULONG ControlPc; 00622 CONTEXT ContextRecord; 00623 ULONG EstablisherFrame; 00624 EXCEPTION_RECORD ExceptionRecord; 00625 PRUNTIME_FUNCTION FunctionEntry; 00626 BOOLEAN InFunction; 00627 ULONG NextPc; 00628 00629 // 00630 // Construct an exception record. 00631 // 00632 00633 ExceptionRecord.ExceptionCode = Status; 00634 ExceptionRecord.ExceptionRecord = (PEXCEPTION_RECORD)NULL; 00635 ExceptionRecord.NumberParameters = 0; 00636 ExceptionRecord.ExceptionFlags = EXCEPTION_NONCONTINUABLE; 00637 00638 // 00639 // Capture the current context, virtually unwind to the caller of this 00640 // routine, set the fault instruction address to that of the caller, and 00641 // call the raise exception system service. 00642 // 00643 00644 RtlCaptureContext(&ContextRecord); 00645 ControlPc = ContextRecord.Lr - 4; 00646 FunctionEntry = RtlLookupFunctionEntry(ControlPc); 00647 NextPc = RtlVirtualUnwind(ControlPc, 00648 FunctionEntry, 00649 &ContextRecord, 00650 &InFunction, 00651 &EstablisherFrame, 00652 NULL, 00653 0, 00654 0xffffffff); 00655 00656 ContextRecord.Iar = NextPc + 4; 00657 ExceptionRecord.ExceptionAddress = (PVOID)ContextRecord.Iar; 00658 Status = ZwRaiseException(&ExceptionRecord, &ContextRecord, TRUE); 00659 00660 // 00661 // There should never be a return from this system service unless 00662 // there is a problem with the argument list itself. Raise another 00663 // exception specifying the status value returned. 00664 // 00665 00666 RtlRaiseStatus(Status); 00667 return; 00668 } 00669 00670 VOID 00671 RtlUnwind ( 00672 IN PVOID TargetFrame OPTIONAL, 00673 IN PVOID TargetIp OPTIONAL, 00674 IN PEXCEPTION_RECORD ExceptionRecord OPTIONAL, 00675 IN PVOID ReturnValue 00676 ) 00677 00678 /*++ 00679 00680 Routine Description: 00681 00682 This function initiates an unwind of procedure call frames. The machine 00683 state at the time of the call to unwind is captured in a context record 00684 and the unwinding flag is set in the exception flags of the exception 00685 record. If the TargetFrame parameter is not specified, then the exit unwind 00686 flag is also set in the exception flags of the exception record. A backward 00687 scan through the procedure call frames is then performed to find the target 00688 of the unwind operation. 00689 00690 As each frame is encounter, the PC where control left the corresponding 00691 function is determined and used to lookup exception handler information 00692 in the runtime function table built by the linker. If the respective 00693 routine has an exception handler, then the handler is called. 00694 00695 N.B. This routine is provided for backward compatibility with release 1. 00696 00697 Arguments: 00698 00699 TargetFrame - Supplies an optional pointer to the call frame that is the 00700 target of the unwind. If this parameter is not specified, then an exit 00701 unwind is performed. 00702 00703 TargetIp - Supplies an optional instruction address that specifies the 00704 continuation address of the unwind. This address is ignored if the 00705 target frame parameter is not specified. 00706 00707 ExceptionRecord - Supplies an optional pointer to an exception record. 00708 00709 ReturnValue - Supplies a value that is to be placed in the integer 00710 function return register just before continuing execution. 00711 00712 Return Value: 00713 00714 None. 00715 00716 --*/ 00717 00718 { 00719 00720 CONTEXT ContextRecord; 00721 00722 // 00723 // Call real unwind routine specifying a context record as an 00724 // extra argument. 00725 // 00726 00727 RtlUnwind2(TargetFrame, 00728 TargetIp, 00729 ExceptionRecord, 00730 ReturnValue, 00731 &ContextRecord); 00732 00733 return; 00734 } 00735 00736 VOID 00737 RtlUnwind2 ( 00738 IN PVOID TargetFrame OPTIONAL, 00739 IN PVOID TargetIp OPTIONAL, 00740 IN PEXCEPTION_RECORD ExceptionRecord OPTIONAL, 00741 IN PVOID ReturnValue, 00742 IN PCONTEXT ContextRecord 00743 ) 00744 00745 /*++ 00746 00747 Routine Description: 00748 00749 This function initiates an unwind of procedure call frames. The machine 00750 state at the time of the call to unwind is captured in a context record 00751 and the unwinding flag is set in the exception flags of the exception 00752 record. If the TargetFrame parameter is not specified, then the exit unwind 00753 flag is also set in the exception flags of the exception record. A backward 00754 scan through the procedure call frames is then performed to find the target 00755 of the unwind operation. 00756 00757 As each frame is encounter, the PC where control left the corresponding 00758 function is determined and used to lookup exception handler information 00759 in the runtime function table built by the linker. If the respective 00760 routine has an exception handler, then the handler is called. 00761 00762 Arguments: 00763 00764 TargetFrame - Supplies an optional pointer to the call frame that is the 00765 target of the unwind. If this parameter is not specified, then an exit 00766 unwind is performed. 00767 00768 TargetIp - Supplies an optional instruction address that specifies the 00769 continuation address of the unwind. This address is ignored if the 00770 target frame parameter is not specified. 00771 00772 ExceptionRecord - Supplies an optional pointer to an exception record. 00773 00774 ReturnValue - Supplies a value that is to be placed in the integer 00775 function return register just before continuing execution. 00776 00777 ContextRecord - Supplies a pointer to a context record that can be used 00778 to store context druing the unwind operation. 00779 00780 Return Value: 00781 00782 None. 00783 00784 --*/ 00785 00786 { 00787 00788 ULONG ControlPc; 00789 DISPATCHER_CONTEXT DispatcherContext; 00790 EXCEPTION_DISPOSITION Disposition; 00791 ULONG EstablisherFrame; 00792 ULONG ExceptionFlags; 00793 EXCEPTION_RECORD ExceptionRecord1; 00794 PRUNTIME_FUNCTION FunctionEntry; 00795 BOOLEAN InFunction; 00796 ULONG HighLimit; 00797 ULONG LowLimit; 00798 ULONG NextPc; 00799 00800 // 00801 // Get current stack limits, capture the current context, virtually 00802 // unwind to the caller of this routine, get the initial PC value, and 00803 // set the unwind target address. 00804 // 00805 00806 RtlpGetStackLimits(&LowLimit, &HighLimit); 00807 RtlCaptureContext(ContextRecord); 00808 ControlPc = ContextRecord->Lr - 4; 00809 FunctionEntry = RtlLookupFunctionEntry(ControlPc); 00810 NextPc = RtlVirtualUnwind(ControlPc, 00811 FunctionEntry, 00812 ContextRecord, 00813 &InFunction, 00814 &EstablisherFrame, 00815 NULL, 00816 0, 00817 0xffffffff); 00818 00819 ControlPc = NextPc; 00820 ContextRecord->Iar = (ULONG)TargetIp; 00821 00822 // 00823 // If an exception record is not specified, then build a local exception 00824 // record for use in calling exception handlers during the unwind operation. 00825 // 00826 00827 if (ARGUMENT_PRESENT(ExceptionRecord) == FALSE) { 00828 ExceptionRecord = &ExceptionRecord1; 00829 ExceptionRecord1.ExceptionCode = STATUS_UNWIND; 00830 ExceptionRecord1.ExceptionRecord = NULL; 00831 ExceptionRecord1.ExceptionAddress = (PVOID)ControlPc; 00832 ExceptionRecord1.NumberParameters = 0; 00833 } 00834 00835 // 00836 // If the target frame of the unwind is specified, then a normal unwind 00837 // is being performed. Otherwise, an exit unwind is being performed. 00838 // 00839 00840 ExceptionFlags = EXCEPTION_UNWINDING; 00841 if (ARGUMENT_PRESENT(TargetFrame) == FALSE) { 00842 ExceptionRecord->ExceptionFlags |= EXCEPTION_EXIT_UNWIND; 00843 } 00844 00845 // 00846 // Scan backward through the call frame hierarchy and call exception 00847 // handlers until the target frame of the unwind is reached. 00848 // 00849 00850 do { 00851 00852 // 00853 // Lookup the function table entry using the point at which control 00854 // left the procedure. 00855 // 00856 00857 FunctionEntry = RtlLookupFunctionEntry(ControlPc); 00858 00859 // 00860 // If there is a function table entry for the routine, then virtually 00861 // unwind to the caller of the routine to obtain the virtual frame 00862 // pointer of the establisher, but don't update the context record. 00863 // 00864 00865 if (FunctionEntry != NULL) { 00866 NextPc = RtlpVirtualUnwind(ControlPc, 00867 FunctionEntry, 00868 ContextRecord, 00869 &InFunction, 00870 &EstablisherFrame, 00871 NULL, 00872 LowLimit, 00873 HighLimit); 00874 00875 // 00876 // If the virtual frame pointer is not within the specified stack 00877 // limits, the virtual frame pointer is unaligned, or the target 00878 // frame is below the virtual frame and an exit unwind is not being 00879 // performed, then raise the exception STATUS_BAD_STACK. Otherwise, 00880 // check to determine if the current routine has an exception 00881 // handler. 00882 // 00883 00884 if ((EstablisherFrame < LowLimit) || (EstablisherFrame > HighLimit) || 00885 ((ARGUMENT_PRESENT(TargetFrame) != FALSE) && 00886 ((ULONG)TargetFrame < EstablisherFrame)) || 00887 ((EstablisherFrame & 0x7) != 0)) { 00888 RAISE_EXCEPTION(STATUS_BAD_STACK, ExceptionRecord); 00889 00890 } else if ((FunctionEntry->ExceptionHandler != NULL) && InFunction) { 00891 00892 // 00893 // The frame has an exception handler. 00894 // 00895 // The control PC, establisher frame pointer, the address 00896 // of the function table entry, and the address of the 00897 // context record are all stored in the dispatcher context. 00898 // This information is used by the unwind linkage routine 00899 // and can be used by the exception handler itself. 00900 // 00901 // A linkage routine written in assembler is used to actually 00902 // call the actual exception handler. This is required by the 00903 // exception handler that is associated with the linkage 00904 // routine so it can have access to two sets of dispatcher 00905 // context when it is called. 00906 // 00907 00908 DispatcherContext.ControlPc = ControlPc; 00909 DispatcherContext.FunctionEntry = FunctionEntry; 00910 DispatcherContext.EstablisherFrame = EstablisherFrame; 00911 DispatcherContext.ContextRecord = ContextRecord; 00912 00913 // 00914 // Call the exception handler. 00915 // 00916 00917 do { 00918 00919 // 00920 // If the establisher frame is the target of the unwind 00921 // operation, then set the target unwind flag. 00922 // 00923 00924 if ((ULONG)TargetFrame == EstablisherFrame) { 00925 ExceptionFlags |= EXCEPTION_TARGET_UNWIND; 00926 } 00927 00928 ExceptionRecord->ExceptionFlags = ExceptionFlags; 00929 00930 // 00931 // Set the specified return value in case the exception 00932 // handler directly continues execution. 00933 // 00934 00935 ContextRecord->Gpr3 = (ULONG)ReturnValue; 00936 Disposition = 00937 RtlpExecuteHandlerForUnwind(ExceptionRecord, 00938 EstablisherFrame, 00939 ContextRecord, 00940 &DispatcherContext, 00941 FunctionEntry->ExceptionHandler); 00942 00943 // 00944 // Clear target unwind and collided unwind flags. 00945 // 00946 00947 ExceptionFlags &= ~(EXCEPTION_COLLIDED_UNWIND | 00948 EXCEPTION_TARGET_UNWIND); 00949 00950 // 00951 // Case on the handler disposition. 00952 // 00953 00954 switch (Disposition) { 00955 00956 // 00957 // The disposition is to continue the search. 00958 // 00959 // If the target frame has not been reached, then 00960 // virtually unwind to the caller of the current 00961 // routine, update the context record, and continue 00962 // the search for a handler. 00963 // 00964 00965 case ExceptionContinueSearch : 00966 if (EstablisherFrame != (ULONG)TargetFrame) { 00967 NextPc = RtlVirtualUnwind(ControlPc, 00968 FunctionEntry, 00969 ContextRecord, 00970 &InFunction, 00971 &EstablisherFrame, 00972 NULL, 00973 0, 00974 0xffffffff); 00975 } 00976 00977 break; 00978 00979 // 00980 // The disposition is collided unwind. 00981 // 00982 // Set the target of the current unwind to the context 00983 // record of the previous unwind, and reexecute the 00984 // exception handler from the collided frame with the 00985 // collided unwind flag set in the exception record. 00986 // 00987 00988 case ExceptionCollidedUnwind : 00989 ControlPc = DispatcherContext.ControlPc; 00990 FunctionEntry = DispatcherContext.FunctionEntry; 00991 ContextRecord = DispatcherContext.ContextRecord; 00992 ContextRecord->Iar = (ULONG)TargetIp; 00993 ExceptionFlags |= EXCEPTION_COLLIDED_UNWIND; 00994 EstablisherFrame = DispatcherContext.EstablisherFrame; 00995 break; 00996 00997 // 00998 // All other disposition values are invalid. 00999 // 01000 // Raise invalid disposition exception. 01001 // 01002 01003 default : 01004 RAISE_EXCEPTION(STATUS_INVALID_DISPOSITION, ExceptionRecord); 01005 } 01006 01007 } while ((ExceptionFlags & EXCEPTION_COLLIDED_UNWIND) != 0); 01008 01009 } else { 01010 01011 // 01012 // If the target frame has not been reached, then virtually unwind to the 01013 // caller of the current routine and update the context record. 01014 // 01015 01016 if (EstablisherFrame != (ULONG)TargetFrame) { 01017 NextPc = RtlVirtualUnwind(ControlPc, 01018 FunctionEntry, 01019 ContextRecord, 01020 &InFunction, 01021 &EstablisherFrame, 01022 NULL, 01023 0, 01024 0xffffffff); 01025 } 01026 } 01027 01028 } else { 01029 01030 // 01031 // Set point at which control left the previous routine. 01032 // 01033 NextPc = RtlVirtualUnwind(ControlPc, 01034 FunctionEntry, 01035 ContextRecord, 01036 &InFunction, 01037 &EstablisherFrame, 01038 NULL, 01039 0, 01040 0xffffffff); 01041 01042 // 01043 // If the next control PC is the same as the old control PC, then 01044 // the function table is not correctly formed. 01045 // 01046 01047 if (NextPc == ControlPc) { 01048 RtlRaiseStatus(STATUS_BAD_FUNCTION_TABLE); 01049 } 01050 } 01051 01052 // 01053 // Set point at which control left the previous routine. 01054 // 01055 // N.B. Make sure the address is in the delay slot of the jal 01056 // to prevent the boundary condition of the return address 01057 // being at the front of a try body. 01058 // 01059 01060 ControlPc = NextPc; 01061 01062 } while ((EstablisherFrame < HighLimit) && 01063 (EstablisherFrame != (ULONG)TargetFrame)); 01064 01065 // 01066 // If the establisher stack pointer is equal to the target frame 01067 // pointer, then continue execution. Otherwise, an exit unwind was 01068 // performed or the target of the unwind did not exist and the 01069 // debugger and subsystem are given a second chance to handle the 01070 // unwind. 01071 // 01072 01073 if (EstablisherFrame == (ULONG)TargetFrame) { 01074 // 01075 // Virtually unwind the target frame to recover the value of r.2. 01076 // We must take care to not unwind a glue sequence that may have 01077 // been used to reach the target frame. This is done by giving 01078 // stack limit values that will regard any stack pointer as bad. 01079 // 01080 CONTEXT TocContext; 01081 RtlMoveMemory((PVOID)&TocContext, ContextRecord, sizeof(CONTEXT)); 01082 ControlPc = ContextRecord->Iar; 01083 FunctionEntry = RtlLookupFunctionEntry(ControlPc); 01084 NextPc = RtlVirtualUnwind(ControlPc, 01085 FunctionEntry, 01086 &TocContext, 01087 &InFunction, 01088 &EstablisherFrame, 01089 NULL, 01090 0xffffffff, 01091 0); 01092 ContextRecord->Gpr2 = TocContext.Gpr2; 01093 ContextRecord->Gpr3 = (ULONG)ReturnValue; 01094 RtlpRestoreContext(ContextRecord, ExceptionRecord); 01095 01096 } else { 01097 ZwRaiseException(ExceptionRecord, ContextRecord, FALSE); 01098 } 01099 } 01100 01101 ULONG 01102 RtlpVirtualUnwind ( 01103 IN ULONG ControlPc, 01104 IN PRUNTIME_FUNCTION FunctionEntry, 01105 IN PCONTEXT ContextRecord, 01106 OUT PBOOLEAN InFunction, 01107 OUT PULONG EstablisherFrame, 01108 IN OUT PKNONVOLATILE_CONTEXT_POINTERS ContextPointers OPTIONAL, 01109 IN ULONG LowStackLimit, 01110 IN ULONG HighStackLimit 01111 ) 01112 01113 /*++ 01114 01115 Routine Description: 01116 01117 This function virtually unwinds the specfified function by executing its 01118 prologue code backwards. 01119 01120 If the function is a leaf function, then the address where control left 01121 the previous frame is obtained from the context record. If the function 01122 is a nested function, but not an exception or interrupt frame, then the 01123 prologue code is executed backwards and the address where control left 01124 the previous frame is obtained from the updated context record. 01125 01126 If the function is register save millicode, it is treated as a leaf 01127 function. If the function is register restore millicode, the remaining 01128 body is executed forwards and the address where control left the 01129 previous frame is obtained from the final blr instruction. 01130 01131 If the function was called via glue code and is not that glue code, 01132 the prologe of the glue code is executed backwards in addition to the 01133 above actions. 01134 01135 Otherwise, an exception or interrupt entry to the system is being 01136 unwound and a specially coded prologue restores the return address 01137 twice. Once from the fault instruction address and once from the saved 01138 return address register. The first restore is returned as the function 01139 value and the second restore is place in the updated context record. 01140 01141 If a context pointers record is specified, then the address where each 01142 nonvolatile registers is restored from is recorded in the appropriate 01143 element of the context pointers record. 01144 01145 N.B. This function copies the specified context record and only computes 01146 the establisher frame and whether control is actually in a function. 01147 01148 Arguments: 01149 01150 ControlPc - Supplies the address where control left the specified 01151 function. 01152 01153 FunctionEntry - Supplies the address of the function table entry for the 01154 specified function or NULL if the function is a leaf function. 01155 01156 ContextRecord - Supplies the address of a context record. 01157 01158 InFunction - Supplies a pointer to a variable that receives whether the 01159 control PC is within the current function. 01160 01161 EstablisherFrame - Supplies a pointer to a variable that receives the 01162 the establisher frame pointer value. 01163 01164 ContextPointers - Supplies an optional pointer to a context pointers 01165 record. 01166 01167 LowStackLimit, HighStackLimit - Range of valid values for the stack 01168 pointer. This indicates whether it is valid to examine NextPc. 01169 01170 Return Value: 01171 01172 The address where control left the previous frame is returned as the 01173 function value. 01174 01175 --*/ 01176 01177 { 01178 01179 CONTEXT LocalContext; 01180 01181 // 01182 // Copy the context record so updates will not be reflected in the 01183 // original copy and then virtually unwind to the caller of the 01184 // specified control point. 01185 // 01186 01187 RtlMoveMemory((PVOID)&LocalContext, ContextRecord, sizeof(CONTEXT)); 01188 return RtlVirtualUnwind(ControlPc, 01189 FunctionEntry, 01190 &LocalContext, 01191 InFunction, 01192 EstablisherFrame, 01193 ContextPointers, 01194 LowStackLimit, 01195 HighStackLimit); 01196 }

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