test: exdsptch.c Source File

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 }