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) 1990 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) 11-Sep-1990 00017 00018 Environment: 00019 00020 Any mode. 00021 00022 Revision History: 00023 00024 --*/ 00025 00026 #include "ntrtlp.h" 00027 00028 // 00029 // Define local macros. 00030 // 00031 // Raise noncontinuable exception with associated exception record. 00032 // 00033 00034 #define RAISE_EXCEPTION(Status, ExceptionRecordt) { \ 00035 EXCEPTION_RECORD ExceptionRecordn; \ 00036 \ 00037 ExceptionRecordn.ExceptionCode = Status; \ 00038 ExceptionRecordn.ExceptionFlags = EXCEPTION_NONCONTINUABLE; \ 00039 ExceptionRecordn.ExceptionRecord = ExceptionRecordt; \ 00040 ExceptionRecordn.NumberParameters = 0; \ 00041 RtlRaiseException(&ExceptionRecordn); \ 00042 } 00043 00044 // 00045 // Define stack register and zero register numbers. 00046 // 00047 00048 #define RA 0x1f // integer register 31 00049 #define SP 0x1d // integer register 29 00050 #define ZERO 0x0 // integer register 0 00051 00052 // 00053 // Define saved register masks. 00054 // 00055 00056 #define SAVED_FLOATING_MASK 0xfff00000 // saved floating registers 00057 #define SAVED_INTEGER_MASK 0xf3ffff02 // saved integer registers 00058 00059 // 00060 // Define private function prototypes. 00061 // 00062 00063 VOID 00064 RtlpRestoreContext ( 00065 IN PCONTEXT Context, 00066 IN PEXCEPTION_RECORD ExceptionRecord OPTIONAL 00067 ); 00068 00069 VOID 00070 RtlpRaiseException ( 00071 IN PEXCEPTION_RECORD ExceptionRecord 00072 ); 00073 00074 VOID 00075 RtlpRaiseStatus ( 00076 IN NTSTATUS Status 00077 ); 00078 00079 ULONG 00080 RtlpVirtualUnwind ( 00081 IN ULONG ControlPc, 00082 IN PRUNTIME_FUNCTION FunctionEntry, 00083 IN PCONTEXT ContextRecord, 00084 OUT PBOOLEAN InFunction, 00085 OUT PULONG EstablisherFrame, 00086 IN OUT PKNONVOLATILE_CONTEXT_POINTERS ContextPointers OPTIONAL 00087 ); 00088 00089 ULONG 00090 RtlpVirtualUnwind32 ( 00091 IN ULONG ControlPc, 00092 IN PRUNTIME_FUNCTION FunctionEntry, 00093 IN OUT PCONTEXT ContextRecord, 00094 OUT PBOOLEAN InFunction, 00095 OUT PULONG EstablisherFrame, 00096 IN OUT PKNONVOLATILE_CONTEXT_POINTERS ContextPointers OPTIONAL 00097 ); 00098 00099 00100 BOOLEAN 00101 RtlDispatchException ( 00102 IN PEXCEPTION_RECORD ExceptionRecord, 00103 IN PCONTEXT ContextRecord 00104 ) 00105 00106 /*++ 00107 00108 Routine Description: 00109 00110 This function attempts to dispatch an exception to a frame based 00111 handler by searching backwards through the stack based call frames. 00112 The search begins with the frame specified in the context record and 00113 continues backward until either a handler is found that handles the 00114 exception, the stack is found to be invalid (i.e., out of limits or 00115 unaligned), or the end of the call hierarchy is reached. 00116 00117 As each frame is encounter, the PC where control left the corresponding 00118 function is determined and used to lookup exception handler information 00119 in the runtime function table built by the linker. If the respective 00120 routine has an exception handler, then the handler is called. If the 00121 handler does not handle the exception, then the prologue of the routine 00122 is executed backwards to "unwind" the effect of the prologue and then 00123 the next frame is examined. 00124 00125 Arguments: 00126 00127 ExceptionRecord - Supplies a pointer to an exception record. 00128 00129 ContextRecord - Supplies a pointer to a context record. 00130 00131 Return Value: 00132 00133 If the exception is handled by one of the frame based handlers, then 00134 a value of TRUE is returned. Otherwise a value of FALSE is returned. 00135 00136 --*/ 00137 00138 { 00139 00140 CONTEXT ContextRecord1; 00141 ULONG ControlPc; 00142 DISPATCHER_CONTEXT DispatcherContext; 00143 EXCEPTION_DISPOSITION Disposition; 00144 ULONG EstablisherFrame; 00145 ULONG ExceptionFlags; 00146 PRUNTIME_FUNCTION FunctionEntry; 00147 ULONG Index; 00148 BOOLEAN InFunction; 00149 ULONG HighLimit; 00150 ULONG LowLimit; 00151 ULONG NestedFrame; 00152 ULONG NextPc; 00153 00154 // 00155 // Get current stack limits, copy the context record, get the initial 00156 // PC value, capture the exception flags, and set the nested exception 00157 // frame pointer. 00158 // 00159 00160 RtlpGetStackLimits(&LowLimit, &HighLimit); 00161 RtlMoveMemory(&ContextRecord1, ContextRecord, sizeof(CONTEXT)); 00162 ControlPc = ContextRecord1.Fir; 00163 ExceptionFlags = ExceptionRecord->ExceptionFlags & EXCEPTION_NONCONTINUABLE; 00164 NestedFrame = 0; 00165 00166 // 00167 // Start with the frame specified by the context record and search 00168 // backwards through the call frame hierarchy attempting to find an 00169 // exception handler that will handle the exception. 00170 // 00171 00172 do { 00173 00174 // 00175 // Lookup the function table entry using the point at which control 00176 // left the procedure. 00177 // 00178 00179 FunctionEntry = RtlLookupFunctionEntry(ControlPc); 00180 00181 // 00182 // If there is a function table entry for the routine, then virtually 00183 // unwind to the caller of the current routine to obtain the virtual 00184 // frame pointer of the establisher and check if there is an exception 00185 // handler for the frame. 00186 // 00187 00188 if (FunctionEntry != NULL) { 00189 NextPc = RtlVirtualUnwind(ControlPc | 1, 00190 FunctionEntry, 00191 &ContextRecord1, 00192 &InFunction, 00193 &EstablisherFrame, 00194 NULL); 00195 00196 // 00197 // If the virtual frame pointer is not within the specified stack 00198 // limits or the virtual frame pointer is unaligned, then set the 00199 // stack invalid flag in the exception record and return exception 00200 // not handled. Otherwise, check if the current routine has an 00201 // exception handler. 00202 // 00203 00204 if ((EstablisherFrame < LowLimit) || (EstablisherFrame > HighLimit) || 00205 ((EstablisherFrame & 0x7) != 0)) { 00206 ExceptionFlags |= EXCEPTION_STACK_INVALID; 00207 break; 00208 00209 } else if ((FunctionEntry->ExceptionHandler != NULL) && InFunction) { 00210 00211 // 00212 // The frame has an exception handler. The handler must be 00213 // executed by calling another routine that is written in 00214 // assembler. This is required because up level addressing 00215 // of the handler information is required when a nested 00216 // exception is encountered. 00217 // 00218 00219 DispatcherContext.ControlPc = ControlPc; 00220 DispatcherContext.FunctionEntry = FunctionEntry; 00221 DispatcherContext.EstablisherFrame = EstablisherFrame; 00222 DispatcherContext.ContextRecord = ContextRecord; 00223 ExceptionRecord->ExceptionFlags = ExceptionFlags; 00224 00225 // 00226 // If requested log exception. 00227 // 00228 00229 if (NtGlobalFlag & FLG_ENABLE_EXCEPTION_LOGGING) { 00230 Index = RtlpLogExceptionHandler(ExceptionRecord, 00231 ContextRecord, 00232 ControlPc, 00233 FunctionEntry, 00234 sizeof(RUNTIME_FUNCTION)); 00235 } 00236 00237 Disposition = 00238 RtlpExecuteHandlerForException(ExceptionRecord, 00239 EstablisherFrame, 00240 ContextRecord, 00241 &DispatcherContext, 00242 FunctionEntry->ExceptionHandler); 00243 00244 if (NtGlobalFlag & FLG_ENABLE_EXCEPTION_LOGGING) { 00245 RtlpLogLastExceptionDisposition(Index, Disposition); 00246 } 00247 00248 ExceptionFlags |= 00249 (ExceptionRecord->ExceptionFlags & EXCEPTION_NONCONTINUABLE); 00250 00251 // 00252 // If the current scan is within a nested context and the frame 00253 // just examined is the end of the nested region, then clear 00254 // the nested context frame and the nested exception flag in 00255 // the exception flags. 00256 // 00257 00258 if (NestedFrame == EstablisherFrame) { 00259 ExceptionFlags &= (~EXCEPTION_NESTED_CALL); 00260 NestedFrame = 0; 00261 } 00262 00263 // 00264 // Case on the handler disposition. 00265 // 00266 00267 switch (Disposition) { 00268 00269 // 00270 // The disposition is to continue execution. 00271 // 00272 // If the exception is not continuable, then raise the 00273 // exception STATUS_NONCONTINUABLE_EXCEPTION. Otherwise 00274 // return exception handled. 00275 // 00276 00277 case ExceptionContinueExecution : 00278 if ((ExceptionFlags & EXCEPTION_NONCONTINUABLE) != 0) { 00279 RAISE_EXCEPTION(STATUS_NONCONTINUABLE_EXCEPTION, ExceptionRecord); 00280 00281 } else { 00282 return TRUE; 00283 } 00284 00285 // 00286 // The disposition is to continue the search. 00287 // 00288 // Get next frame address and continue the search. 00289 // 00290 00291 case ExceptionContinueSearch : 00292 break; 00293 00294 // 00295 // The disposition is nested exception. 00296 // 00297 // Set the nested context frame to the establisher frame 00298 // address and set the nested exception flag in the 00299 // exception flags. 00300 // 00301 00302 case ExceptionNestedException : 00303 ExceptionFlags |= EXCEPTION_NESTED_CALL; 00304 if (DispatcherContext.EstablisherFrame > NestedFrame) { 00305 NestedFrame = DispatcherContext.EstablisherFrame; 00306 } 00307 00308 break; 00309 00310 // 00311 // All other disposition values are invalid. 00312 // 00313 // Raise invalid disposition exception. 00314 // 00315 00316 default : 00317 RAISE_EXCEPTION(STATUS_INVALID_DISPOSITION, ExceptionRecord); 00318 } 00319 } 00320 00321 } else { 00322 00323 // 00324 // Set point at which control left the previous routine. 00325 // 00326 00327 NextPc = (ULONG)(ContextRecord1.XIntRa - 4); 00328 00329 // 00330 // If the next control PC is the same as the old control PC, then 00331 // the function table is not correctly formed. 00332 // 00333 00334 if (NextPc == ControlPc) { 00335 break; 00336 } 00337 } 00338 00339 // 00340 // Set point at which control left the previous routine. 00341 // 00342 00343 ControlPc = NextPc; 00344 } while ((ULONG)ContextRecord1.XIntSp < HighLimit); 00345 00346 // 00347 // Set final exception flags and return exception not handled. 00348 // 00349 00350 ExceptionRecord->ExceptionFlags = ExceptionFlags; 00351 return FALSE; 00352 } 00353 00354 PRUNTIME_FUNCTION 00355 RtlLookupFunctionEntry ( 00356 IN ULONG ControlPc 00357 ) 00358 00359 /*++ 00360 00361 Routine Description: 00362 00363 This function searches the currently active function tables for an entry 00364 that corresponds to the specified PC value. 00365 00366 Arguments: 00367 00368 ControlPc - Supplies the address of an instruction within the specified 00369 function. 00370 00371 Return Value: 00372 00373 If there is no entry in the function table for the specified PC, then 00374 NULL is returned. Otherwise, the address of the function table entry 00375 that corresponds to the specified PC is returned. 00376 00377 --*/ 00378 00379 { 00380 00381 PRUNTIME_FUNCTION FunctionEntry; 00382 PRUNTIME_FUNCTION FunctionTable; 00383 ULONG SizeOfExceptionTable; 00384 LONG High; 00385 PVOID ImageBase; 00386 LONG Low; 00387 LONG Middle; 00388 USHORT i; 00389 00390 // 00391 // Search for the image that includes the specified PC value. 00392 // 00393 00394 ImageBase = RtlPcToFileHeader((PVOID)ControlPc, &ImageBase); 00395 00396 // 00397 // If an image is found that includes the specified PC, then locate the 00398 // function table for the image. 00399 // 00400 00401 if (ImageBase != NULL) { 00402 FunctionTable = (PRUNTIME_FUNCTION)RtlImageDirectoryEntryToData( 00403 ImageBase, TRUE, IMAGE_DIRECTORY_ENTRY_EXCEPTION, 00404 &SizeOfExceptionTable); 00405 00406 // 00407 // If a function table is located, then search the function table 00408 // for a function table entry for the specified PC. 00409 // 00410 00411 if (FunctionTable != NULL) { 00412 00413 // 00414 // Initialize search indicies. 00415 // 00416 00417 Low = 0; 00418 High = (SizeOfExceptionTable / sizeof(RUNTIME_FUNCTION)) - 1; 00419 00420 // 00421 // Perform binary search on the function table for a function table 00422 // entry that subsumes the specified PC. 00423 // 00424 00425 while (High >= Low) { 00426 00427 // 00428 // Compute next probe index and test entry. If the specified PC 00429 // is greater than of equal to the beginning address and less 00430 // than the ending address of the function table entry, then 00431 // return the address of the function table entry. Otherwise, 00432 // continue the search. 00433 // 00434 00435 Middle = (Low + High) >> 1; 00436 FunctionEntry = &FunctionTable[Middle]; 00437 if (ControlPc < FunctionEntry->BeginAddress) { 00438 High = Middle - 1; 00439 00440 } else if (ControlPc >= FunctionEntry->EndAddress) { 00441 Low = Middle + 1; 00442 00443 } else { 00444 00445 // 00446 // The capability exists for more than one function entry 00447 // to map to the same function. This permits a function to 00448 // have discontiguous code segments described by separate 00449 // function table entries. If the ending prologue address 00450 // is not within the limits of the begining and ending 00451 // address of the function able entry, then the prologue 00452 // ending address is the address of a function table entry 00453 // that accurately describes the ending prologue address. 00454 // 00455 00456 if ((FunctionEntry->PrologEndAddress < FunctionEntry->BeginAddress) || 00457 (FunctionEntry->PrologEndAddress > FunctionEntry->EndAddress)) { 00458 FunctionEntry = (PRUNTIME_FUNCTION)FunctionEntry->PrologEndAddress; 00459 } 00460 00461 return FunctionEntry; 00462 } 00463 } 00464 } 00465 } 00466 00467 // 00468 // A function table entry for the specified PC was not found. 00469 // 00470 00471 return NULL; 00472 } 00473 00474 VOID 00475 RtlRaiseException ( 00476 IN PEXCEPTION_RECORD ExceptionRecord 00477 ) 00478 00479 /*++ 00480 00481 Routine Description: 00482 00483 This function raises a software exception by building a context record 00484 and calling the raise exception system service. 00485 00486 N.B. This routine is a shell routine that simply calls another routine 00487 to do the real work. The reason this is done is to avoid a problem 00488 in try/finally scopes where the last statement in the scope is a 00489 call to raise an exception. 00490 00491 Arguments: 00492 00493 ExceptionRecord - Supplies a pointer to an exception record. 00494 00495 Return Value: 00496 00497 None. 00498 00499 --*/ 00500 00501 { 00502 00503 RtlpRaiseException(ExceptionRecord); 00504 return; 00505 } 00506 00507 VOID 00508 RtlpRaiseException ( 00509 IN PEXCEPTION_RECORD ExceptionRecord 00510 ) 00511 00512 /*++ 00513 00514 Routine Description: 00515 00516 This function raises a software exception by building a context record 00517 and calling the raise exception system service. 00518 00519 Arguments: 00520 00521 ExceptionRecord - Supplies a pointer to an exception record. 00522 00523 Return Value: 00524 00525 None. 00526 00527 --*/ 00528 00529 { 00530 00531 ULONG ControlPc; 00532 CONTEXT ContextRecord; 00533 ULONG EstablisherFrame; 00534 PRUNTIME_FUNCTION FunctionEntry; 00535 BOOLEAN InFunction; 00536 ULONG NextPc; 00537 NTSTATUS Status; 00538 00539 // 00540 // Capture the current context, virtually unwind to the caller of this 00541 // routine, set the fault instruction address to that of the caller, and 00542 // call the raise exception system service. 00543 // 00544 00545 RtlCaptureContext(&ContextRecord); 00546 ControlPc = (ULONG)(ContextRecord.XIntRa - 4); 00547 FunctionEntry = RtlLookupFunctionEntry(ControlPc); 00548 NextPc = RtlVirtualUnwind(ControlPc | 1, 00549 FunctionEntry, 00550 &ContextRecord, 00551 &InFunction, 00552 &EstablisherFrame, 00553 NULL); 00554 00555 ContextRecord.Fir = NextPc + 4; 00556 ExceptionRecord->ExceptionAddress = (PVOID)ContextRecord.Fir; 00557 Status = ZwRaiseException(ExceptionRecord, &ContextRecord, TRUE); 00558 00559 // 00560 // There should never be a return from this system service unless 00561 // there is a problem with the argument list itself. Raise another 00562 // exception specifying the status value returned. 00563 // 00564 00565 RtlRaiseStatus(Status); 00566 return; 00567 } 00568 00569 VOID 00570 RtlRaiseStatus ( 00571 IN NTSTATUS Status 00572 ) 00573 00574 /*++ 00575 00576 Routine Description: 00577 00578 This function raises an exception with the specified status value. The 00579 exception is marked as noncontinuable with no parameters. 00580 00581 N.B. This routine is a shell routine that simply calls another routine 00582 to do the real work. The reason this is done is to avoid a problem 00583 in try/finally scopes where the last statement in the scope is a 00584 call to raise an exception. 00585 00586 Arguments: 00587 00588 Status - Supplies the status value to be used as the exception code 00589 for the exception that is to be raised. 00590 00591 Return Value: 00592 00593 None. 00594 00595 --*/ 00596 00597 { 00598 00599 RtlpRaiseStatus(Status); 00600 return; 00601 } 00602 00603 VOID 00604 RtlpRaiseStatus ( 00605 IN NTSTATUS Status 00606 ) 00607 00608 /*++ 00609 00610 Routine Description: 00611 00612 This function raises an exception with the specified status value. The 00613 exception is marked as noncontinuable with no parameters. 00614 00615 Arguments: 00616 00617 Status - Supplies the status value to be used as the exception code 00618 for the exception that is to be raised. 00619 00620 Return Value: 00621 00622 None. 00623 00624 --*/ 00625 00626 { 00627 00628 ULONG ControlPc; 00629 CONTEXT ContextRecord; 00630 ULONG EstablisherFrame; 00631 EXCEPTION_RECORD ExceptionRecord; 00632 PRUNTIME_FUNCTION FunctionEntry; 00633 BOOLEAN InFunction; 00634 ULONG NextPc; 00635 00636 // 00637 // Construct an exception record. 00638 // 00639 00640 ExceptionRecord.ExceptionCode = Status; 00641 ExceptionRecord.ExceptionRecord = (PEXCEPTION_RECORD)NULL; 00642 ExceptionRecord.NumberParameters = 0; 00643 ExceptionRecord.ExceptionFlags = EXCEPTION_NONCONTINUABLE; 00644 00645 // 00646 // Capture the current context, virtually unwind to the caller of this 00647 // routine, set the fault instruction address to that of the caller, and 00648 // call the raise exception system service. 00649 // 00650 00651 RtlCaptureContext(&ContextRecord); 00652 ControlPc = (ULONG)(ContextRecord.XIntRa - 4); 00653 FunctionEntry = RtlLookupFunctionEntry(ControlPc); 00654 NextPc = RtlVirtualUnwind(ControlPc | 1, 00655 FunctionEntry, 00656 &ContextRecord, 00657 &InFunction, 00658 &EstablisherFrame, 00659 NULL); 00660 00661 ContextRecord.Fir = NextPc + 4; 00662 ExceptionRecord.ExceptionAddress = (PVOID)ContextRecord.Fir; 00663 Status = ZwRaiseException(&ExceptionRecord, &ContextRecord, TRUE); 00664 00665 // 00666 // There should never be a return from this system service unless 00667 // there is a problem with the argument list itself. Raise another 00668 // exception specifying the status value returned. 00669 // 00670 00671 RtlRaiseStatus(Status); 00672 return; 00673 } 00674 00675 VOID 00676 RtlUnwind ( 00677 IN PVOID TargetFrame OPTIONAL, 00678 IN PVOID TargetIp OPTIONAL, 00679 IN PEXCEPTION_RECORD ExceptionRecord OPTIONAL, 00680 IN PVOID ReturnValue 00681 ) 00682 00683 /*++ 00684 00685 Routine Description: 00686 00687 This function initiates an unwind of procedure call frames. The machine 00688 state at the time of the call to unwind is captured in a context record 00689 and the unwinding flag is set in the exception flags of the exception 00690 record. If the TargetFrame parameter is not specified, then the exit unwind 00691 flag is also set in the exception flags of the exception record. A backward 00692 scan through the procedure call frames is then performed to find the target 00693 of the unwind operation. 00694 00695 As each frame is encounter, the PC where control left the corresponding 00696 function is determined and used to lookup exception handler information 00697 in the runtime function table built by the linker. If the respective 00698 routine has an exception handler, then the handler is called. 00699 00700 N.B. This routine is provided for backward compatibility with release 1. 00701 00702 Arguments: 00703 00704 TargetFrame - Supplies an optional pointer to the call frame that is the 00705 target of the unwind. If this parameter is not specified, then an exit 00706 unwind is performed. 00707 00708 TargetIp - Supplies an optional instruction address that specifies the 00709 continuation address of the unwind. This address is ignored if the 00710 target frame parameter is not specified. 00711 00712 ExceptionRecord - Supplies an optional pointer to an exception record. 00713 00714 ReturnValue - Supplies a value that is to be placed in the integer 00715 function return register just before continuing execution. 00716 00717 Return Value: 00718 00719 None. 00720 00721 --*/ 00722 00723 { 00724 00725 CONTEXT ContextRecord; 00726 00727 // 00728 // Call real unwind routine specifying a context record as an 00729 // extra argument. 00730 // 00731 00732 RtlUnwind2(TargetFrame, 00733 TargetIp, 00734 ExceptionRecord, 00735 ReturnValue, 00736 &ContextRecord); 00737 00738 return; 00739 } 00740 00741 VOID 00742 RtlUnwind2 ( 00743 IN PVOID TargetFrame OPTIONAL, 00744 IN PVOID TargetIp OPTIONAL, 00745 IN PEXCEPTION_RECORD ExceptionRecord OPTIONAL, 00746 IN PVOID ReturnValue, 00747 IN PCONTEXT ContextRecord 00748 ) 00749 00750 /*++ 00751 00752 Routine Description: 00753 00754 This function initiates an unwind of procedure call frames. The machine 00755 state at the time of the call to unwind is captured in a context record 00756 and the unwinding flag is set in the exception flags of the exception 00757 record. If the TargetFrame parameter is not specified, then the exit unwind 00758 flag is also set in the exception flags of the exception record. A backward 00759 scan through the procedure call frames is then performed to find the target 00760 of the unwind operation. 00761 00762 As each frame is encounter, the PC where control left the corresponding 00763 function is determined and used to lookup exception handler information 00764 in the runtime function table built by the linker. If the respective 00765 routine has an exception handler, then the handler is called. 00766 00767 Arguments: 00768 00769 TargetFrame - Supplies an optional pointer to the call frame that is the 00770 target of the unwind. If this parameter is not specified, then an exit 00771 unwind is performed. 00772 00773 TargetIp - Supplies an optional instruction address that specifies the 00774 continuation address of the unwind. This address is ignored if the 00775 target frame parameter is not specified. 00776 00777 ExceptionRecord - Supplies an optional pointer to an exception record. 00778 00779 ReturnValue - Supplies a value that is to be placed in the integer 00780 function return register just before continuing execution. 00781 00782 ContextRecord - Supplies a pointer to a context record that can be used 00783 to store context druing the unwind operation. 00784 00785 Return Value: 00786 00787 None. 00788 00789 --*/ 00790 00791 { 00792 00793 ULONG ControlPc; 00794 DISPATCHER_CONTEXT DispatcherContext; 00795 EXCEPTION_DISPOSITION Disposition; 00796 ULONG EstablisherFrame; 00797 ULONG ExceptionFlags; 00798 EXCEPTION_RECORD ExceptionRecord1; 00799 PRUNTIME_FUNCTION FunctionEntry; 00800 BOOLEAN InFunction; 00801 ULONG HighLimit; 00802 ULONG LowLimit; 00803 ULONG NextPc; 00804 00805 // 00806 // Get current stack limits, capture the current context, virtually 00807 // unwind to the caller of this routine, get the initial PC value, and 00808 // set the unwind target address. 00809 // 00810 00811 RtlpGetStackLimits(&LowLimit, &HighLimit); 00812 RtlCaptureContext(ContextRecord); 00813 ControlPc = (ULONG)(ContextRecord->XIntRa - 4); 00814 FunctionEntry = RtlLookupFunctionEntry(ControlPc); 00815 NextPc = RtlVirtualUnwind(ControlPc | 1, 00816 FunctionEntry, 00817 ContextRecord, 00818 &InFunction, 00819 &EstablisherFrame, 00820 NULL); 00821 00822 ControlPc = NextPc; 00823 ContextRecord->Fir = (ULONG)TargetIp; 00824 00825 // 00826 // If an exception record is not specified, then build a local exception 00827 // record for use in calling exception handlers during the unwind operation. 00828 // 00829 00830 if (ARGUMENT_PRESENT(ExceptionRecord) == FALSE) { 00831 ExceptionRecord = &ExceptionRecord1; 00832 ExceptionRecord1.ExceptionCode = STATUS_UNWIND; 00833 ExceptionRecord1.ExceptionRecord = NULL; 00834 ExceptionRecord1.ExceptionAddress = (PVOID)ControlPc; 00835 ExceptionRecord1.NumberParameters = 0; 00836 } 00837 00838 // 00839 // If the target frame of the unwind is specified, then a normal unwind 00840 // is being performed. Otherwise, an exit unwind is being performed. 00841 // 00842 00843 ExceptionFlags = EXCEPTION_UNWINDING; 00844 if (ARGUMENT_PRESENT(TargetFrame) == FALSE) { 00845 ExceptionRecord->ExceptionFlags |= EXCEPTION_EXIT_UNWIND; 00846 } 00847 00848 // 00849 // Scan backward through the call frame hierarchy and call exception 00850 // handlers until the target frame of the unwind is reached. 00851 // 00852 00853 do { 00854 00855 // 00856 // Lookup the function table entry using the point at which control 00857 // left the procedure. 00858 // 00859 00860 FunctionEntry = RtlLookupFunctionEntry(ControlPc); 00861 00862 // 00863 // If there is a function table entry for the routine, then virtually 00864 // unwind to the caller of the routine to obtain the virtual frame 00865 // pointer of the establisher, but don't update the context record. 00866 // 00867 00868 if (FunctionEntry != NULL) { 00869 NextPc = RtlpVirtualUnwind(ControlPc, 00870 FunctionEntry, 00871 ContextRecord, 00872 &InFunction, 00873 &EstablisherFrame, 00874 NULL); 00875 00876 // 00877 // If the virtual frame pointer is not within the specified stack 00878 // limits, the virtual frame pointer is unaligned, or the target 00879 // frame is below the virtual frame and an exit unwind is not being 00880 // performed, then raise the exception STATUS_BAD_STACK. Otherwise, 00881 // check to determine if the current routine has an exception 00882 // handler. 00883 // 00884 00885 if ((EstablisherFrame < LowLimit) || (EstablisherFrame > HighLimit) || 00886 ((ARGUMENT_PRESENT(TargetFrame) != FALSE) && 00887 ((ULONG)TargetFrame < EstablisherFrame)) || 00888 ((EstablisherFrame & 0x7) != 0)) { 00889 RAISE_EXCEPTION(STATUS_BAD_STACK, ExceptionRecord); 00890 00891 } else if ((FunctionEntry->ExceptionHandler != NULL) && InFunction) { 00892 00893 // 00894 // The frame has an exception handler. 00895 // 00896 // The control PC, establisher frame pointer, the address 00897 // of the function table entry, and the address of the 00898 // context record are all stored in the dispatcher context. 00899 // This information is used by the unwind linkage routine 00900 // and can be used by the exception handler itself. 00901 // 00902 // A linkage routine written in assembler is used to actually 00903 // call the actual exception handler. This is required by the 00904 // exception handler that is associated with the linkage 00905 // routine so it can have access to two sets of dispatcher 00906 // context when it is called. 00907 // 00908 00909 DispatcherContext.ControlPc = ControlPc; 00910 DispatcherContext.FunctionEntry = FunctionEntry; 00911 DispatcherContext.EstablisherFrame = EstablisherFrame; 00912 DispatcherContext.ContextRecord = ContextRecord; 00913 00914 // 00915 // Call the exception handler. 00916 // 00917 00918 do { 00919 00920 // 00921 // If the establisher frame is the target of the unwind 00922 // operation, then set the target unwind flag. 00923 // 00924 00925 if ((ULONG)TargetFrame == EstablisherFrame) { 00926 ExceptionFlags |= EXCEPTION_TARGET_UNWIND; 00927 } 00928 00929 ExceptionRecord->ExceptionFlags = ExceptionFlags; 00930 00931 // 00932 // Set the specified return value in case the exception 00933 // handler directly continues execution. 00934 // 00935 00936 ContextRecord->XIntV0 = (LONG)ReturnValue; 00937 Disposition = 00938 RtlpExecuteHandlerForUnwind(ExceptionRecord, 00939 EstablisherFrame, 00940 ContextRecord, 00941 &DispatcherContext, 00942 FunctionEntry->ExceptionHandler); 00943 00944 // 00945 // Clear target unwind and collided unwind flags. 00946 // 00947 00948 ExceptionFlags &= ~(EXCEPTION_COLLIDED_UNWIND | 00949 EXCEPTION_TARGET_UNWIND); 00950 00951 // 00952 // Case on the handler disposition. 00953 // 00954 00955 switch (Disposition) { 00956 00957 // 00958 // The disposition is to continue the search. 00959 // 00960 // If the target frame has not been reached, then 00961 // virtually unwind to the caller of the current 00962 // routine, update the context record, and continue 00963 // the search for a handler. 00964 // 00965 00966 case ExceptionContinueSearch : 00967 if (EstablisherFrame != (ULONG)TargetFrame) { 00968 NextPc = RtlVirtualUnwind(ControlPc | 1, 00969 FunctionEntry, 00970 ContextRecord, 00971 &InFunction, 00972 &EstablisherFrame, 00973 NULL); 00974 } 00975 00976 break; 00977 00978 // 00979 // The disposition is collided unwind. 00980 // 00981 // Set the target of the current unwind to the context 00982 // record of the previous unwind, and reexecute the 00983 // exception handler from the collided frame with the 00984 // collided unwind flag set in the exception record. 00985 // 00986 00987 case ExceptionCollidedUnwind : 00988 ControlPc = DispatcherContext.ControlPc; 00989 FunctionEntry = DispatcherContext.FunctionEntry; 00990 ContextRecord = DispatcherContext.ContextRecord; 00991 ContextRecord->Fir = (ULONG)TargetIp; 00992 ExceptionFlags |= EXCEPTION_COLLIDED_UNWIND; 00993 EstablisherFrame = DispatcherContext.EstablisherFrame; 00994 break; 00995 00996 // 00997 // All other disposition values are invalid. 00998 // 00999 // Raise invalid disposition exception. 01000 // 01001 01002 default : 01003 RAISE_EXCEPTION(STATUS_INVALID_DISPOSITION, ExceptionRecord); 01004 } 01005 01006 } while ((ExceptionFlags & EXCEPTION_COLLIDED_UNWIND) != 0); 01007 01008 } else { 01009 01010 // 01011 // If the target frame has not been reached, then virtually unwind to the 01012 // caller of the current routine and update the context record. 01013 // 01014 01015 if (EstablisherFrame != (ULONG)TargetFrame) { 01016 NextPc = RtlVirtualUnwind(ControlPc | 1, 01017 FunctionEntry, 01018 ContextRecord, 01019 &InFunction, 01020 &EstablisherFrame, 01021 NULL); 01022 } 01023 } 01024 01025 } else { 01026 01027 // 01028 // Set point at which control left the previous routine. 01029 // 01030 01031 NextPc = (ULONG)(ContextRecord->XIntRa - 4); 01032 01033 // 01034 // If the next control PC is the same as the old control PC, then 01035 // the function table is not correctly formed. 01036 // 01037 01038 if (NextPc == ControlPc) { 01039 RtlRaiseStatus(STATUS_BAD_FUNCTION_TABLE); 01040 } 01041 } 01042 01043 // 01044 // Set point at which control left the previous routine. 01045 // 01046 // N.B. Make sure the address is in the delay slot of the jal 01047 // to prevent the boundary condition of the return address 01048 // being at the front of a try body. 01049 // 01050 01051 ControlPc = NextPc; 01052 01053 } while ((EstablisherFrame < HighLimit) && 01054 (EstablisherFrame != (ULONG)TargetFrame)); 01055 01056 // 01057 // If the establisher stack pointer is equal to the target frame 01058 // pointer, then continue execution. Otherwise, an exit unwind was 01059 // performed or the target of the unwind did not exist and the 01060 // debugger and subsystem are given a second chance to handle the 01061 // unwind. 01062 // 01063 01064 if (EstablisherFrame == (ULONG)TargetFrame) { 01065 ContextRecord->XIntV0 = (LONG)ReturnValue; 01066 RtlpRestoreContext(ContextRecord, ExceptionRecord); 01067 01068 } else { 01069 ZwRaiseException(ExceptionRecord, ContextRecord, FALSE); 01070 } 01071 } 01072 01073 ULONG 01074 RtlVirtualUnwind ( 01075 IN ULONG ControlPc, 01076 IN PRUNTIME_FUNCTION FunctionEntry, 01077 IN OUT PCONTEXT ContextRecord, 01078 OUT PBOOLEAN InFunction, 01079 OUT PULONG EstablisherFrame, 01080 IN OUT PKNONVOLATILE_CONTEXT_POINTERS ContextPointers OPTIONAL 01081 ) 01082 01083 /*++ 01084 01085 Routine Description: 01086 01087 This function virtually unwinds the specfified function by executing its 01088 prologue code backwards. 01089 01090 If the function is a leaf function, then the address where control left 01091 the previous frame is obtained from the context record. If the function 01092 is a nested function, but not an exception or interrupt frame, then the 01093 prologue code is executed backwards and the address where control left 01094 the previous frame is obtained from the updated context record. 01095 01096 Otherwise, an exception or interrupt entry to the system is being unwound 01097 and a specially coded prologue restores the return address twice. Once 01098 from the fault instruction address and once from the saved return address 01099 register. The first restore is returned as the function value and the 01100 second restore is place in the updated context record. 01101 01102 If a context pointers record is specified, then the address where each 01103 nonvolatile registers is restored from is recorded in the appropriate 01104 element of the context pointers record. 01105 01106 N.B. This routine handles 64-bit context records. 01107 01108 Arguments: 01109 01110 ControlPc - Supplies the address where control left the specified 01111 function. 01112 01113 N.B. The low order bit of this argument is used to denote the 01114 context record type. If the low order bit is clear, then 01115 the context record contains 32-bit information. Otherwise, 01116 it contains 64-bit information. 01117 01118 FunctionEntry - Supplies the address of the function table entry for the 01119 specified function. 01120 01121 ContextRecord - Supplies the address of a context record. 01122 01123 InFunction - Supplies a pointer to a variable that receives whether the 01124 control PC is within the current function. 01125 01126 EstablisherFrame - Supplies a pointer to a variable that receives the 01127 the establisher frame pointer value. 01128 01129 ContextPointers - Supplies an optional pointer to a context pointers 01130 record. 01131 01132 Return Value: 01133 01134 The address where control left the previous frame is returned as the 01135 function value. 01136 01137 --*/ 01138 01139 { 01140 01141 ULONG Address; 01142 ULONG DecrementOffset; 01143 ULONG DecrementRegister; 01144 PULONG FloatingRegister; 01145 ULONG Function; 01146 MIPS_INSTRUCTION Instruction; 01147 PULONGLONG IntegerRegister; 01148 ULONG NextPc; 01149 LONG Offset; 01150 ULONG Opcode; 01151 ULONG Rd; 01152 BOOLEAN RestoredRa; 01153 BOOLEAN RestoredSp; 01154 ULONG Rs; 01155 ULONG Rt; 01156 01157 // 01158 // If the low order bit of the control PC is clear, then the context 01159 // record format is 32-bit. Otherwise, the context record format is 01160 // 64-bits. 01161 // 01162 01163 if ((ControlPc & 1) == 0) { 01164 return RtlpVirtualUnwind32(ControlPc, 01165 FunctionEntry, 01166 ContextRecord, 01167 InFunction, 01168 EstablisherFrame, 01169 ContextPointers); 01170 01171 } else { 01172 01173 // 01174 // Set the base address of the integer and floating register arrays. 01175 // 01176 01177 FloatingRegister = &ContextRecord->FltF0; 01178 IntegerRegister = &ContextRecord->XIntZero; 01179 01180 // 01181 // If the instruction at the point where control left the specified 01182 // function is a return, then any saved registers have been restored 01183 // with the possible exception of the stack pointer and the control 01184 // PC is not considered to be in the function (i.e., an epilogue). 01185 // 01186 01187 ControlPc &= ~1; 01188 if (*((PULONG)ControlPc) == JUMP_RA) { 01189 *InFunction = FALSE; 01190 Instruction.Long = *((PULONG)ControlPc + 1); 01191 Opcode = Instruction.i_format.Opcode; 01192 Offset = Instruction.i_format.Simmediate; 01193 Rd = Instruction.r_format.Rd; 01194 Rs = Instruction.i_format.Rs; 01195 Rt = Instruction.i_format.Rt; 01196 Function = Instruction.r_format.Function; 01197 01198 // 01199 // If the opcode is an add immediate unsigned op and both the 01200 // source and destination registers are SP, then add the signed 01201 // offset value to SP. Otherwise, if the opcode is a special op, 01202 // the operation is an add unsigned, and the source and destination 01203 // registers are both SP, then add the register specified by Rd to 01204 // SP. 01205 // 01206 01207 if ((Opcode == ADDIU_OP) && (Rt == SP) && (Rs == SP)) { 01208 IntegerRegister[SP] += Offset; 01209 01210 } else if ((Opcode == SPEC_OP) && (Function == ADDU_OP) && 01211 (Rd == SP) && (Rs == SP)) { 01212 IntegerRegister[SP] += IntegerRegister[Rt]; 01213 } 01214 01215 *EstablisherFrame = (ULONG)ContextRecord->XIntSp; 01216 return (ULONG)ContextRecord->XIntRa; 01217 } 01218 01219 // 01220 // If the address where control left the specified function is outside 01221 // the limits of the prologue, then the control PC is considered to be 01222 // within the function and the control address is set to the end of 01223 // the prologue. Otherwise, the control PC is not considered to be 01224 // within the function (i.e., it is within the prologue). 01225 // 01226 01227 if ((ControlPc < FunctionEntry->BeginAddress) || 01228 (ControlPc >= FunctionEntry->PrologEndAddress)) { 01229 *InFunction = TRUE; 01230 ControlPc = FunctionEntry->PrologEndAddress; 01231 01232 } else { 01233 *InFunction = FALSE; 01234 } 01235 01236 // 01237 // Scan backward through the prologue and reload callee registers that 01238 // were stored. 01239 // 01240 01241 DecrementRegister = 0; 01242 *EstablisherFrame = (ULONG)ContextRecord->XIntSp; 01243 NextPc = (ULONG)(ContextRecord->XIntRa - 4); 01244 RestoredRa = FALSE; 01245 RestoredSp = FALSE; 01246 while (ControlPc > FunctionEntry->BeginAddress) { 01247 01248 // 01249 // Get instruction value, decode fields, case of opcode value, and 01250 // reverse store operations. 01251 // 01252 01253 ControlPc -= 4; 01254 Instruction.Long = *((PULONG)ControlPc); 01255 Opcode = Instruction.i_format.Opcode; 01256 Offset = Instruction.i_format.Simmediate; 01257 Rd = Instruction.r_format.Rd; 01258 Rs = Instruction.i_format.Rs; 01259 Rt = Instruction.i_format.Rt; 01260 Address = (ULONG)(Offset + IntegerRegister[Rs]); 01261 if (Opcode == SW_OP) { 01262 01263 // 01264 // Store word. 01265 // 01266 // If the base register is SP and the source register is an 01267 // integer register, then reload the register value. 01268 // 01269 01270 if (Rs == SP) { 01271 IntegerRegister[Rt] = *((PLONG)Address); 01272 01273 // 01274 // If the destination register is RA and this is the first 01275 // time that RA is being restored, then set the address of 01276 // where control left the previous frame. Otherwise, this 01277 // is an interrupt or exception and the return PC should be 01278 // biased by 4. Otherwise, if the destination register is 01279 // SP and this is the first time that SP is being restored, 01280 // then set the establisher frame pointer. 01281 // 01282 01283 if (Rt == RA) { 01284 if (RestoredRa == FALSE) { 01285 NextPc = (ULONG)(ContextRecord->XIntRa - 4); 01286 RestoredRa = TRUE; 01287 01288 } else { 01289 NextPc += 4; 01290 } 01291 01292 } else if (Rt == SP) { 01293 if (RestoredSp == FALSE) { 01294 *EstablisherFrame = (ULONG)ContextRecord->XIntSp; 01295 RestoredSp = TRUE; 01296 } 01297 } 01298 01299 // 01300 // If a context pointer record is specified, then record 01301 // the address where the destination register contents 01302 // are stored. 01303 // 01304 01305 if (ARGUMENT_PRESENT(ContextPointers)) { 01306 ContextPointers->XIntegerContext[Rt] = (PULONGLONG)Address; 01307 } 01308 } 01309 01310 } else if (Opcode == SD_OP) { 01311 01312 // 01313 // Store double. 01314 // 01315 // If the base register is SP and the source register is an 01316 // integer register, then reload the register value. 01317 // 01318 01319 if (Rs == SP) { 01320 IntegerRegister[Rt] = *((PULONGLONG)Address); 01321 01322 // 01323 // If the destination register is RA and this is the first 01324 // time that RA is being restored, then set the address of 01325 // where control left the previous frame. Otherwise, this 01326 // is an interrupt or exception and the return PC should be 01327 // biased by 4. Otherwise, if the destination register is 01328 // SP and this is the first time that SP is being restored, 01329 // then set the establisher frame pointer. 01330 // 01331 01332 if (Rt == RA) { 01333 if (RestoredRa == FALSE) { 01334 NextPc = (ULONG)(ContextRecord->XIntRa - 4); 01335 RestoredRa = TRUE; 01336 01337 } else { 01338 NextPc += 4; 01339 } 01340 01341 } else if (Rt == SP) { 01342 if (RestoredSp == FALSE) { 01343 *EstablisherFrame = (ULONG)ContextRecord->XIntSp; 01344 RestoredSp = TRUE; 01345 } 01346 } 01347 01348 // 01349 // If a context pointer record is specified, then record 01350 // the address where the destination register contents 01351 // are stored. 01352 // 01353 // N.B. The low order bit of the address is set to indicate 01354 // a store double operation. 01355 // 01356 01357 if (ARGUMENT_PRESENT(ContextPointers)) { 01358 ContextPointers->XIntegerContext[Rt] = (PLONGLONG)((ULONG)Address | 1); 01359 } 01360 } 01361 01362 } else if (Opcode == SWC1_OP) { 01363 01364 // 01365 // Store word coprocessor 1. 01366 // 01367 // If the base register is SP and the source register is a 01368 // floating register, then reload the register value. 01369 // 01370 01371 if (Rs == SP) { 01372 FloatingRegister[Rt] = *((PULONG)Address); 01373 01374 // 01375 // If a context pointer record is specified, then record 01376 // the address where the destination register contents 01377 // are stored. 01378 // 01379 01380 if (ARGUMENT_PRESENT(ContextPointers)) { 01381 ContextPointers->FloatingContext[Rt] = (PULONG)Address; 01382 } 01383 } 01384 01385 } else if (Opcode == SDC1_OP) { 01386 01387 // 01388 // Store double coprocessor 1. 01389 // 01390 // If the base register is SP and the source register is a 01391 // floating register, then reload the register and the next 01392 // register values. 01393 // 01394 01395 if (Rs == SP) { 01396 FloatingRegister[Rt] = *((PULONG)Address); 01397 FloatingRegister[Rt + 1] = *((PULONG)(Address + 4)); 01398 01399 // 01400 // If a context pointer record is specified, then record 01401 // the address where the destination registers contents 01402 // are stored. 01403 // 01404 01405 if (ARGUMENT_PRESENT(ContextPointers)) { 01406 ContextPointers->FloatingContext[Rt] = (PULONG)Address; 01407 ContextPointers->FloatingContext[Rt + 1] = (PULONG)(Address + 4); 01408 } 01409 } 01410 01411 } else if (Opcode == ADDIU_OP) { 01412 01413 // 01414 // Add immediate unsigned. 01415 // 01416 // If both the source and destination registers are SP, then 01417 // a standard stack allocation was performed and the signed 01418 // displacement value should be subtracted from SP. Otherwise, 01419 // if the destination register is the decrement register and 01420 // the source register is zero, then add the decrement value 01421 // to SP. 01422 // 01423 01424 if ((Rs == SP) && (Rt == SP)) { 01425 IntegerRegister[SP] -= Offset; 01426 if (RestoredSp == FALSE) { 01427 *EstablisherFrame = (ULONG)ContextRecord->XIntSp; 01428 RestoredSp = TRUE; 01429 } 01430 01431 } else if ((Rt == DecrementRegister) && (Rs == ZERO)) { 01432 IntegerRegister[SP] += Offset; 01433 if (RestoredSp == FALSE) { 01434 *EstablisherFrame = (ULONG)ContextRecord->XIntSp; 01435 RestoredSp = TRUE; 01436 } 01437 } 01438 01439 } else if (Opcode == ORI_OP) { 01440 01441 // 01442 // Or immediate. 01443 // 01444 // If both the destination and source registers are the decrement 01445 // register, then save the decrement value. Otherwise, if the 01446 // destination register is the decrement register and the source 01447 // register is zero, then add the decrement value to SP. 01448 // 01449 01450 if ((Rs == DecrementRegister) && (Rt == DecrementRegister)) { 01451 DecrementOffset = (Offset & 0xffff); 01452 01453 } else if ((Rt == DecrementRegister) && (Rs == ZERO)) { 01454 IntegerRegister[SP] += (Offset & 0xffff); 01455 if (RestoredSp == FALSE) { 01456 *EstablisherFrame = (ULONG)ContextRecord->XIntSp; 01457 RestoredSp = TRUE; 01458 } 01459 } 01460 01461 } else if (Opcode == SPEC_OP) { 01462 01463 // 01464 // Special operation. 01465 // 01466 // The real opcode is in the function field of special opcode 01467 // instructions. 01468 // 01469 01470 Function = Instruction.r_format.Function; 01471 if ((Function == ADDU_OP) || (Function == OR_OP)) { 01472 01473 // 01474 // Add unsigned or an or operation. 01475 // 01476 // If one of the source registers is ZERO, then the 01477 // operation is a move operation and the destination 01478 // register should be moved to the appropriate source 01479 // register. 01480 // 01481 01482 if (Rt == ZERO) { 01483 IntegerRegister[Rs] = IntegerRegister[Rd]; 01484 01485 // 01486 // If the destination register is RA and this is the 01487 // first time that RA is being restored, then set the 01488 // address of where control left the previous frame. 01489 // Otherwise, this an interrupt or exception and the 01490 // return PC should be biased by 4. 01491 // 01492 01493 if (Rs == RA) { 01494 if (RestoredRa == FALSE) { 01495 NextPc = (ULONG)(ContextRecord->XIntRa - 4); 01496 RestoredRa = TRUE; 01497 01498 } else { 01499 NextPc += 4; 01500 } 01501 } 01502 01503 } else if (Rs == ZERO) { 01504 IntegerRegister[Rt] = IntegerRegister[Rd]; 01505 01506 // 01507 // If the destination register is RA and this is the 01508 // first time that RA is being restored, then set the 01509 // address of where control left the previous frame. 01510 // Otherwise, this an interrupt or exception and the 01511 // return PC should be biased by 4. 01512 // 01513 01514 if (Rt == RA) { 01515 if (RestoredRa == FALSE) { 01516 NextPc = (ULONG)(ContextRecord->XIntRa - 4); 01517 RestoredRa = TRUE; 01518 01519 } else { 01520 NextPc += 4; 01521 } 01522 } 01523 } 01524 01525 } else if (Function == SUBU_OP) { 01526 01527 // 01528 // Subtract unsigned. 01529 // 01530 // If the destination register is SP and the source register 01531 // is SP, then a stack allocation greater than 32kb has been 01532 // performed and source register number of the decrement must 01533 // be saved for later use. 01534 // 01535 01536 if ((Rd == SP) && (Rs == SP)) { 01537 DecrementRegister = Rt; 01538 } 01539 } 01540 01541 } else if (Opcode == LUI_OP) { 01542 01543 // 01544 // Load upper immediate. 01545 // 01546 // If the destination register is the decrement register, then 01547 // compute the decrement value, add it from SP, and clear the 01548 // decrement register number. 01549 // 01550 01551 if (Rt == DecrementRegister) { 01552 DecrementRegister = 0; 01553 IntegerRegister[SP] += (LONG)(DecrementOffset + (Offset << 16)); 01554 if (RestoredSp == FALSE) { 01555 *EstablisherFrame = (ULONG)(ContextRecord->XIntSp); 01556 RestoredSp = TRUE; 01557 } 01558 } 01559 } 01560 } 01561 01562 // 01563 // Make sure that integer register zero is really zero. 01564 // 01565 01566 ContextRecord->XIntZero = 0; 01567 return NextPc; 01568 } 01569 } 01570 01571 ULONG 01572 RtlpVirtualUnwind32 ( 01573 IN ULONG ControlPc, 01574 IN PRUNTIME_FUNCTION FunctionEntry, 01575 IN OUT PCONTEXT ContextRecord, 01576 OUT PBOOLEAN InFunction, 01577 OUT PULONG EstablisherFrame, 01578 IN OUT PKNONVOLATILE_CONTEXT_POINTERS ContextPointers OPTIONAL 01579 ) 01580 01581 /*++ 01582 01583 Routine Description: 01584 01585 This function virtually unwinds the specfified function by executing its 01586 prologue code backwards. 01587 01588 If the function is a leaf function, then the address where control left 01589 the previous frame is obtained from the context record. If the function 01590 is a nested function, but not an exception or interrupt frame, then the 01591 prologue code is executed backwards and the address where control left 01592 the previous frame is obtained from the updated context record. 01593 01594 Otherwise, an exception or interrupt entry to the system is being unwound 01595 and a specially coded prologue restores the return address twice. Once 01596 from the fault instruction address and once from the saved return address 01597 register. The first restore is returned as the function value and the 01598 second restore is place in the updated context record. 01599 01600 If a context pointers record is specified, then the address where each 01601 nonvolatile registers is restored from is recorded in the appropriate 01602 element of the context pointers record. 01603 01604 N.B. This routine handles 32-bit context records. 01605 01606 Arguments: 01607 01608 ControlPc - Supplies the address where control left the specified 01609 function. 01610 01611 FunctionEntry - Supplies the address of the function table entry for the 01612 specified function. 01613 01614 ContextRecord - Supplies the address of a context record. 01615 01616 InFunction - Supplies a pointer to a variable that receives whether the 01617 control PC is within the current function. 01618 01619 EstablisherFrame - Supplies a pointer to a variable that receives the 01620 the establisher frame pointer value. 01621 01622 ContextPointers - Supplies an optional pointer to a context pointers 01623 record. 01624 01625 Return Value: 01626 01627 The address where control left the previous frame is returned as the 01628 function value. 01629 01630 --*/ 01631 01632 { 01633 01634 ULONG Address; 01635 ULONG DecrementOffset; 01636 ULONG DecrementRegister; 01637 PULONG FloatingRegister; 01638 ULONG Function; 01639 MIPS_INSTRUCTION Instruction; 01640 PULONG IntegerRegister; 01641 ULONG NextPc; 01642 LONG Offset; 01643 ULONG Opcode; 01644 ULONG Rd; 01645 BOOLEAN RestoredRa; 01646 BOOLEAN RestoredSp; 01647 ULONG Rs; 01648 ULONG Rt; 01649 01650 // 01651 // Set the base address of the integer and floating register arrays. 01652 // 01653 01654 FloatingRegister = &ContextRecord->FltF0; 01655 IntegerRegister = &ContextRecord->IntZero; 01656 01657 // 01658 // If the instruction at the point where control left the specified 01659 // function is a return, then any saved registers have been restored 01660 // with the possible exception of the stack pointer and the control 01661 // PC is not considered to be in the function (i.e., an epilogue). 01662 // 01663 01664 if (*((PULONG)ControlPc) == JUMP_RA) { 01665 *InFunction = FALSE; 01666 Instruction.Long = *((PULONG)ControlPc + 1); 01667 Opcode = Instruction.i_format.Opcode; 01668 Offset = Instruction.i_format.Simmediate; 01669 Rd = Instruction.r_format.Rd; 01670 Rs = Instruction.i_format.Rs; 01671 Rt = Instruction.i_format.Rt; 01672 Function = Instruction.r_format.Function; 01673 01674 // 01675 // If the opcode is an add immediate unsigned op and both the source 01676 // and destination registers are SP, then add the signed offset value 01677 // to SP. Otherwise, if the opcode is a special op, the operation is 01678 // an add unsigned, and the source and destination registers are both 01679 // SP, then add the register specified by Rd to SP. 01680 // 01681 01682 if ((Opcode == ADDIU_OP) && (Rt == SP) && (Rs == SP)) { 01683 IntegerRegister[SP] += Offset; 01684 01685 } else if ((Opcode == SPEC_OP) && (Function == ADDU_OP) && 01686 (Rd == SP) && (Rs == SP)) { 01687 IntegerRegister[SP] += IntegerRegister[Rt]; 01688 } 01689 01690 *EstablisherFrame = ContextRecord->IntSp; 01691 return ContextRecord->IntRa; 01692 } 01693 01694 // 01695 // If the address where control left the specified function is outside 01696 // the limits of the prologue, then the control PC is considered to be 01697 // within the function and the control address is set to the end of 01698 // the prologue. Otherwise, the control PC is not considered to be 01699 // within the function (i.e., it is within the prologue). 01700 // 01701 01702 if ((ControlPc < FunctionEntry->BeginAddress) || 01703 (ControlPc >= FunctionEntry->PrologEndAddress)) { 01704 *InFunction = TRUE; 01705 ControlPc = FunctionEntry->PrologEndAddress; 01706 01707 } else { 01708 *InFunction = FALSE; 01709 } 01710 01711 // 01712 // Scan backward through the prologue and reload callee registers that 01713 // were stored. 01714 // 01715 01716 DecrementRegister = 0; 01717 *EstablisherFrame = ContextRecord->IntSp; 01718 NextPc = ContextRecord->IntRa - 4; 01719 RestoredRa = FALSE; 01720 RestoredSp = FALSE; 01721 while (ControlPc > FunctionEntry->BeginAddress) { 01722 01723 // 01724 // Get instruction value, decode fields, case of opcode value, and 01725 // reverse store operations. 01726 // 01727 01728 ControlPc -= 4; 01729 Instruction.Long = *((PULONG)ControlPc); 01730 Opcode = Instruction.i_format.Opcode; 01731 Offset = Instruction.i_format.Simmediate; 01732 Rd = Instruction.r_format.Rd; 01733 Rs = Instruction.i_format.Rs; 01734 Rt = Instruction.i_format.Rt; 01735 Address = Offset + IntegerRegister[Rs]; 01736 if (Opcode == SW_OP) { 01737 01738 // 01739 // Store word. 01740 // 01741 // If the base register is SP and the source register is an 01742 // integer register, then reload the register value. 01743 // 01744 01745 if (Rs == SP) { 01746 IntegerRegister[Rt] = *((PULONG)Address); 01747 01748 // 01749 // If the destination register is RA and this is the first 01750 // time that RA is being restored, then set the address of 01751 // where control left the previous frame. Otherwise, this 01752 // is an interrupt or exception and the return PC should be 01753 // biased by 4. Otherwise, if the destination register is 01754 // SP and this is the first time that SP is being restored, 01755 // then set the establisher frame pointer. 01756 // 01757 01758 if (Rt == RA) { 01759 if (RestoredRa == FALSE) { 01760 NextPc = ContextRecord->IntRa - 4; 01761 RestoredRa = TRUE; 01762 01763 } else { 01764 NextPc += 4; 01765 } 01766 01767 } else if (Rt == SP) { 01768 if (RestoredSp == FALSE) { 01769 *EstablisherFrame = ContextRecord->IntSp; 01770 RestoredSp = TRUE; 01771 } 01772 } 01773 01774 // 01775 // If a context pointer record is specified, then record 01776 // the address where the destination register contents 01777 // are stored. 01778 // 01779 01780 if (ARGUMENT_PRESENT(ContextPointers)) { 01781 ContextPointers->XIntegerContext[Rt] = (PULONGLONG)Address; 01782 } 01783 } 01784 01785 } else if (Opcode == SWC1_OP) { 01786 01787 // 01788 // Store word coprocessor 1. 01789 // 01790 // If the base register is SP and the source register is a 01791 // floating register, then reload the register value. 01792 // 01793 01794 if (Rs == SP) { 01795 FloatingRegister[Rt] = *((PULONG)Address); 01796 01797 // 01798 // If a context pointer record is specified, then record 01799 // the address where the destination register contents 01800 // are stored. 01801 // 01802 01803 if (ARGUMENT_PRESENT(ContextPointers)) { 01804 ContextPointers->FloatingContext[Rt] = (PULONG)Address; 01805 } 01806 } 01807 01808 } else if (Opcode == SDC1_OP) { 01809 01810 // 01811 // Store double coprocessor 1. 01812 // 01813 // If the base register is SP and the source register is a 01814 // floating register, then reload the register and the next 01815 // register values. 01816 // 01817 01818 if (Rs == SP) { 01819 FloatingRegister[Rt] = *((PULONG)Address); 01820 FloatingRegister[Rt + 1] = *((PULONG)(Address + 4)); 01821 01822 // 01823 // If a context pointer record is specified, then record 01824 // the address where the destination registers contents 01825 // are stored. 01826 // 01827 01828 if (ARGUMENT_PRESENT(ContextPointers)) { 01829 ContextPointers->FloatingContext[Rt] = (PULONG)Address; 01830 ContextPointers->FloatingContext[Rt + 1] = (PULONG)(Address + 4); 01831 } 01832 } 01833 01834 } else if (Opcode == ADDIU_OP) { 01835 01836 // 01837 // Add immediate unsigned. 01838 // 01839 // If both the source and destination registers are SP, then 01840 // a standard stack allocation was performed and the signed 01841 // displacement value should be subtracted from SP. Otherwise, 01842 // if the destination register is the decrement register and 01843 // the source register is zero, then add the decrement value 01844 // to SP. 01845 // 01846 01847 if ((Rs == SP) && (Rt == SP)) { 01848 IntegerRegister[SP] -= Offset; 01849 if (RestoredSp == FALSE) { 01850 *EstablisherFrame = ContextRecord->IntSp; 01851 RestoredSp = TRUE; 01852 } 01853 01854 } else if ((Rt == DecrementRegister) && (Rs == ZERO)) { 01855 IntegerRegister[SP] += Offset; 01856 if (RestoredSp == FALSE) { 01857 *EstablisherFrame = ContextRecord->IntSp; 01858 RestoredSp = TRUE; 01859 } 01860 } 01861 01862 } else if (Opcode == ORI_OP) { 01863 01864 // 01865 // Or immediate. 01866 // 01867 // If both the destination and source registers are the decrement 01868 // register, then save the decrement value. Otherwise, if the 01869 // destination register is the decrement register and the source 01870 // register is zero, then add the decrement value to SP. 01871 // 01872 01873 if ((Rs == DecrementRegister) && (Rt == DecrementRegister)) { 01874 DecrementOffset = (Offset & 0xffff); 01875 01876 } else if ((Rt == DecrementRegister) && (Rs == ZERO)) { 01877 IntegerRegister[SP] += (Offset & 0xffff); 01878 if (RestoredSp == FALSE) { 01879 *EstablisherFrame = ContextRecord->IntSp; 01880 RestoredSp = TRUE; 01881 } 01882 } 01883 01884 } else if (Opcode == SPEC_OP) { 01885 01886 // 01887 // Special operation. 01888 // 01889 // The real opcode is in the function field of special opcode 01890 // instructions. 01891 // 01892 01893 Function = Instruction.r_format.Function; 01894 if ((Function == ADDU_OP) || (Function == OR_OP)) { 01895 01896 // 01897 // Add unsigned or an or operation. 01898 // 01899 // If one of the source registers is ZERO, then the 01900 // operation is a move operation and the destination 01901 // register should be moved to the appropriate source 01902 // register. 01903 // 01904 01905 if (Rt == ZERO) { 01906 IntegerRegister[Rs] = IntegerRegister[Rd]; 01907 01908 // 01909 // If the destination register is RA and this is the 01910 // first time that RA is being restored, then set the 01911 // address of where control left the previous frame. 01912 // Otherwise, this an interrupt or exception and the 01913 // return PC should be biased by 4. 01914 // 01915 01916 if (Rs == RA) { 01917 if (RestoredRa == FALSE) { 01918 NextPc = ContextRecord->IntRa - 4; 01919 RestoredRa = TRUE; 01920 01921 } else { 01922 NextPc += 4; 01923 } 01924 } 01925 01926 } else if (Rs == ZERO) { 01927 IntegerRegister[Rt] = IntegerRegister[Rd]; 01928 01929 // 01930 // If the destination register is RA and this is the 01931 // first time that RA is being restored, then set the 01932 // address of where control left the previous frame. 01933 // Otherwise, this an interrupt or exception and the 01934 // return PC should be biased by 4. 01935 // 01936 01937 if (Rt == RA) { 01938 if (RestoredRa == FALSE) { 01939 NextPc = ContextRecord->IntRa - 4; 01940 RestoredRa = TRUE; 01941 01942 } else { 01943 NextPc += 4; 01944 } 01945 } 01946 } 01947 01948 } else if (Function == SUBU_OP) { 01949 01950 // 01951 // Subtract unsigned. 01952 // 01953 // If the destination register is SP and the source register 01954 // is SP, then a stack allocation greater than 32kb has been 01955 // performed and source register number of the decrement must 01956 // be saved for later use. 01957 // 01958 01959 if ((Rd == SP) && (Rs == SP)) { 01960 DecrementRegister = Rt; 01961 } 01962 } 01963 01964 } else if (Opcode == LUI_OP) { 01965 01966 // 01967 // Load upper immediate. 01968 // 01969 // If the destination register is the decrement register, then 01970 // compute the decrement value, add it from SP, and clear the 01971 // decrement register number. 01972 // 01973 01974 if (Rt == DecrementRegister) { 01975 DecrementRegister = 0; 01976 IntegerRegister[SP] += (DecrementOffset + (Offset << 16)); 01977 if (RestoredSp == FALSE) { 01978 *EstablisherFrame = ContextRecord->IntSp; 01979 RestoredSp = TRUE; 01980 } 01981 } 01982 } 01983 } 01984 01985 // 01986 // Make sure that integer register zero is really zero. 01987 // 01988 01989 ContextRecord->IntZero = 0; 01990 return NextPc; 01991 } 01992 01993 ULONG 01994 RtlpVirtualUnwind ( 01995 IN ULONG ControlPc, 01996 IN PRUNTIME_FUNCTION FunctionEntry, 01997 IN PCONTEXT ContextRecord, 01998 OUT PBOOLEAN InFunction, 01999 OUT PULONG EstablisherFrame, 02000 IN OUT PKNONVOLATILE_CONTEXT_POINTERS ContextPointers OPTIONAL 02001 ) 02002 02003 /*++ 02004 02005 Routine Description: 02006 02007 This function virtually unwinds the specfified function by executing its 02008 prologue code backwards. 02009 02010 If the function is a leaf function, then the address where control left 02011 the previous frame is obtained from the context record. If the function 02012 is a nested function, but not an exception or interrupt frame, then the 02013 prologue code is executed backwards and the address where control left 02014 the previous frame is obtained from the updated context record. 02015 02016 Otherwise, an exception or interrupt entry to the system is being unwound 02017 and a specially coded prologue restores the return address twice. Once 02018 from the fault instruction address and once from the saved return address 02019 register. The first restore is returned as the function value and the 02020 second restore is place in the updated context record. 02021 02022 If a context pointers record is specified, then the address where each 02023 nonvolatile registers is restored from is recorded in the appropriate 02024 element of the context pointers record. 02025 02026 N.B. This function copies the specified context record and only computes 02027 the establisher frame and whether control is actually in a function. 02028 02029 Arguments: 02030 02031 ControlPc - Supplies the address where control left the specified 02032 function. 02033 02034 FunctionEntry - Supplies the address of the function table entry for the 02035 specified function. 02036 02037 ContextRecord - Supplies the address of a context record. 02038 02039 InFunction - Supplies a pointer to a variable that receives whether the 02040 control PC is within the current function. 02041 02042 EstablisherFrame - Supplies a pointer to a variable that receives the 02043 the establisher frame pointer value. 02044 02045 ContextPointers - Supplies an optional pointer to a context pointers 02046 record. 02047 02048 Return Value: 02049 02050 The address where control left the previous frame is returned as the 02051 function value. 02052 02053 --*/ 02054 02055 { 02056 02057 CONTEXT LocalContext; 02058 02059 // 02060 // Copy the context record so updates will not be reflected in the 02061 // original copy and then virtually unwind to the caller of the 02062 // specified control point. 02063 // 02064 02065 RtlMoveMemory((PVOID)&LocalContext, ContextRecord, sizeof(CONTEXT)); 02066 return RtlVirtualUnwind(ControlPc | 1, 02067 FunctionEntry, 02068 &LocalContext, 02069 InFunction, 02070 EstablisherFrame, 02071 ContextPointers); 02072 }

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