/*++ Copyright (c) 1993 IBM Corporation and Microsoft Corporation Module Name: vunwind.c Abstract: This module contains the instruction classifying and virtual unwinding routines for structured exception handling on PowerPC. Virtual Unwind was moved to this file from exdsptch.c so that it can be used directly in the kernel debugger. WARNING! The kernel debugger and windbg need to be modified if the number and type of parameters for READ_ULONG and READ_DOUBLE are modified. Use CAUTION if you add include statements Author: Rick Simpson 16-Aug-1993 based on MIPS version by David N. Cutler (davec) 11-Sep-1990 Environment: Any mode. Revision History: Tom Wood (twood) 1-Nov-1993 Add back changes to RtlVirtualUnwind made when the MIPS version was ported. Tom Wood (twood) 1-Feb-1994 Change to using a function table entry for register save/restore millicode. Add forward execution of register restore millicode. Add the ITERATOR abstraction. Peter Johnston (plj@vnet.ibm.com) 14-Feb-1994 Added InstrGetOut classification to allow a simulated return from dummy prologues such as those in system exception handling. Tom Wood (twood) 28-Feb-1994 Added the WINDBG interface. Tom Wood (twood) 8-Jun-1994 Added the _IMAGEHLP_SOURCE_ interface. Tom Wood (twood) 8-Jun-1994 Removed the WINDBG interface. Updated the _IMAGEHLP_SOURCE_ interface to deal with the fact that ExceptionHandler and HandlerData cannot be relied upon. Also, the FunctionEntry value may be a static buffer returned by RtlLookupFunctionEntry (i.e. FunctionTableAccess). The copy of vunwind.c in ntos/rtl/ppc is older and should be replaced with this version. Tom Wood (twood) 9-Aug-1994 Added support for the new glue code sequences. Added InstrGlue and InstrTocRestore. The former replaces InstrGetOut. RtlVirtualUnwind is now required to be called when there is no function table entry. --*/ typedef DOUBLE *PDOUBLE; #ifdef ROS_DEBUG #include "ntrtlp.h" #define READ_ULONG(addr,dest) dest = (*((PULONG)(addr))) #define READ_DOUBLE(addr,dest) dest = (*((PDOUBLE)(addr))) #endif #ifdef _IMAGEHLP_SOURCE_ #define FUNCTION_ENTRY_IS_IMAGE_STYLE #define NOT_IMAGEHLP(E) #else #define NOT_IMAGEHLP(E) E #endif #ifdef KERNEL_DEBUGGER #define FUNCTION_ENTRY_IS_IMAGE_STYLE #define RtlVirtualUnwind VirtualUnwind #endif // // The `ClassifyInstruction' function returns an enum that identifies // the type of processing needed for an instruction. // typedef enum _INSTR_CLASS { InstrIgnore, // Do not process InstrMFLR, // Move from Link Register InstrMFCR, // Move from Condition Register InstrSTW, // Store word InstrSTWU, // Store word with update InstrSTWUr12, // Store word with update during UnwindR12 InstrSTFD, // Store float double InstrMR, // Move register InstrMRr12, // Move register during UnwindR12 InstrMRfwd, // Move register during UnwindForward InstrADDIr12, // Add immediate during UnwindR12 InstrADDIfwd, // Add immediate during UnwindForward InstrSaveCode, // Branch and link to GPR or FPR saving millicode InstrRestoreCode, // Branch to GPR or FPR saving millicode InstrGlue, // Branch or Branch and link to glue code InstrBLR, // Branch to Link Register InstrTOCRestore, // Load that restores the TOC [after a call to glue] InstrSetEstablisher // Special instruction used to set establisher frame } INSTR_CLASS; // // If `ClassifyInstruction' returns `InstrSaveCode' or `InstrRestoreCode', // the following information is completed. // typedef struct _MILLICODE_INFO { ULONG TargetPc; // Millicode entry point PRUNTIME_FUNCTION FunctionEntry; // Millicode function table entry } MILLICODE_INFO, *PMILLICODE_INFO; // // `ClassifyInstruction' interprets the instruction based on the intent. // typedef enum _UNWIND_INTENT { UnwindForward, // Performing a forward execution UnwindR12, // Performing a reverse execution to get r.12 UnwindReverse, // Performing a reverse execution UnwindReverseR12 // Performing a reverse execution allowing r.12 } UNWIND_INTENT; // // The simulated execution by `RtlVirtualUnwind' is controlled by this // data type. // typedef struct _ITERATOR { ULONG BeginPc; // Address of first instruction to simulate ULONG EndPc; // Address after the last instruction to simulate LONG Increment; // Simulation direction UNWIND_INTENT Intent; // Simulation intent } ITERATOR, *PITERATOR; #define GPR1 1 // GPR 1 in an RA, RB, RT, etc. field #define GPR2 2 // GPR 2 in an RA, RB, RT, etc. field #define GPR12 12 // GPR 12 in an RA, RB, RT, etc. field #define LINKREG 0x100 // Link Reg in a MFSPR instruction #define COUNTREG 0x120 // Count Reg in a MFSPR instruction // // TryReadUlong attempts to read memory from a possibly unsafe address. // It is in a seperate routine to avoid optimization deficiencies caused // by use of try/except. // #ifndef _IMAGEHLP_SOURCE_ static NTSTATUS TryReadUlong(IN ULONG NextPc, OUT PULONG Value) { try { READ_ULONG (NextPc, *Value); } except (EXCEPTION_EXECUTE_HANDLER) { return GetExceptionCode(); } return STATUS_SUCCESS; } #endif static INSTR_CLASS ClassifyInstruction (PPC_INSTRUCTION *I, UNWIND_INTENT Intent, #ifdef _IMAGEHLP_SOURCE_ HANDLE hProcess, PREAD_PROCESS_MEMORY_ROUTINE ReadMemory, PFUNCTION_TABLE_ACCESS_ROUTINE FunctionTableAccess, #endif ULONG Pc, PMILLICODE_INFO Info) /*++ Routine description: This function inspects the instruction identified by the "Pc" argument and determines what sort of processing is needed in order to simulate its execution. Some instructions can be safely ignored altogether, in which case "InstrIgnore" is returned. For others, a value is returned indicating what kind of instruction was found. The interpreation depends on the value of "Intent". Arguments: I - Address of a struct containing the instruction to be examined. Intent - Type of unwinding being performed. Pc - Address of the instruction, used for computing relative branch addresses. Info - Address to store a description of the register save/restore millicode. Return value: One of the enum values defined above is returned. --*/ { // Unique value combining an opcode and an UNWIND_INTENT value. #define OP_INTENT(OP,INTENT) ((OP) << 2 | (INTENT)) #ifdef _IMAGEHLP_SOURCE_ DWORD ImagehlpCb = 0; #endif switch (OP_INTENT (I->Primary_Op, Intent)) { // // Store word: recognize "stw r.n, disp(r.1)". Allow a base of // r.12 if we have computed its value. // case OP_INTENT (STW_OP, UnwindReverseR12): if (I->Dform_RA == GPR12) return InstrSTW; // fall thru case OP_INTENT (STW_OP, UnwindReverse): if (I->Dform_RA == GPR1) return InstrSTW; break; // // Load word: recognize "lwz r.n, disp(r.x)" in epilogue millicode. // case OP_INTENT (LWZ_OP, UnwindForward): return InstrSTW; // // Load word: recognize "lwz r.toc, disp(r.1)" as TOC restore // instruction. // case OP_INTENT (LWZ_OP, UnwindReverse): if (I->Dform_RA == GPR1 && I->Dform_RS == GPR2) return InstrTOCRestore; // // Store word with update: recognize "stwu r.1, r.1, disp" // case OP_INTENT (STWU_OP, UnwindReverse): case OP_INTENT (STWU_OP, UnwindReverseR12): case OP_INTENT (STWU_OP, UnwindR12): if (I->Dform_RS == GPR1 && I->Dform_RA == GPR1) return (Intent == UnwindR12 ? InstrSTWUr12 : InstrSTWU); break; // // Store float double: recognize "stfd f.n, disp(r.1)". Allow a // base of r.12 if we have computed its value. // case OP_INTENT (STFD_OP, UnwindReverseR12): if (I->Dform_RA == GPR12) return InstrSTFD; // fall thru case OP_INTENT (STFD_OP, UnwindReverse): if (I->Dform_RA == GPR1) return InstrSTFD; break; // // Load float double: recognize "lfd f.n, disp(r.x)" // case OP_INTENT (LFD_OP, UnwindForward): return InstrSTFD; // // Add immediate: recognize "addi r.12, r.1, delta" // case OP_INTENT (ADDI_OP, UnwindR12): if (I->Dform_RS == GPR12 && I->Dform_RA == GPR1) return InstrADDIr12; break; case OP_INTENT (ADDI_OP, UnwindForward): return InstrADDIfwd; // // Branch (long form): recognize "bl[a] saveregs"and "b[a] restregs" // case OP_INTENT (B_OP, UnwindReverse): // // Compute branch target address, allowing for branch-relative // and branch-absolute. // Pc = ((LONG)(I->Iform_LI) << 2) + (I->Iform_AA ? 0 : Pc); // // Quickly distinguish "bl subroutine" from "bl[a] saveregs". // "mtlr" is not a valid instruction in register save millicode and // is usually the first instruction in "bl subroutine". // if (I->Iform_LK) { PPC_INSTRUCTION TempI; READ_ULONG (Pc, TempI.Long); if (TempI.Primary_Op == X31_OP && TempI.Xform_XO == MFSPR_OP && TempI.XFXform_spr == LINKREG) { break; } } // // Determine whether the target address is part of a register // save or register restore sequence or is a direct branch out // by checking it's function table entry. // if ((Info->FunctionEntry = (PRUNTIME_FUNCTION)RtlLookupFunctionEntry(Pc)) != NULL #ifndef FUNCTION_ENTRY_IS_IMAGE_STYLE && Info->FunctionEntry->ExceptionHandler == 0 #endif ) { Info->TargetPc = Pc; switch ( #ifdef FUNCTION_ENTRY_IS_IMAGE_STYLE Info->FunctionEntry->BeginAddress - Info->FunctionEntry->PrologEndAddress #else (ULONG)Info->FunctionEntry->HandlerData #endif ) { case 1: if (I->Iform_LK) return InstrSaveCode; break; case 2: if (!I->Iform_LK) return InstrRestoreCode; break; #ifdef FUNCTION_ENTRY_IS_IMAGE_STYLE default: if ((Info->FunctionEntry->PrologEndAddress & 3) == 1) #else case 3: #endif return InstrGlue; break; } } break; // unrecognized entry point // // Extended ops -- primary opcode 19 // case OP_INTENT (X19_OP, UnwindForward): // // BLR: recognized "bclr 20,0". // if (I->Long == RETURN_INSTR) return InstrBLR; // // RFI: this instruction is used in special kernel fake prologues // to indicate that the establisher frame address should be // updated using the current value of sp. // if (I->Xform_XO == RFI_OP) { return InstrSetEstablisher; } break; // // Extended ops -- primary opcode 31 // case OP_INTENT (X31_OP, UnwindForward): case OP_INTENT (X31_OP, UnwindR12): case OP_INTENT (X31_OP, UnwindReverse): case OP_INTENT (X31_OP, UnwindReverseR12): switch (OP_INTENT (I->Xform_XO, Intent)) { // // OR register: recognize "or r.x, r.y, r.y" as move-reg // case OP_INTENT (OR_OP, UnwindR12): if (I->Xform_RS == I->Xform_RB && I->Xform_RA == GPR12 && I->Xform_RB == GPR1) return InstrMRr12; break; case OP_INTENT (OR_OP, UnwindReverse): case OP_INTENT (OR_OP, UnwindReverseR12): if (I->Xform_RS == I->Xform_RB && I->Xform_RB != GPR1) return InstrMR; break; case OP_INTENT (OR_OP, UnwindForward): if (I->Xform_RS == I->Xform_RB) return InstrMRfwd; break; // // Store word with update indexed: recognize "stwux r.1, r.1, r.x" // case OP_INTENT (STWUX_OP, UnwindReverse): case OP_INTENT (STWUX_OP, UnwindReverseR12): case OP_INTENT (STWUX_OP, UnwindR12): if (I->Xform_RS == GPR1 && I->Xform_RA == GPR1) return (Intent == UnwindR12 ? InstrSTWUr12 : InstrSTWU); break; // // Move to/from special-purpose reg: recognize "mflr", "mtlr" // case OP_INTENT (MFSPR_OP, UnwindReverse): case OP_INTENT (MTSPR_OP, UnwindForward): if (I->XFXform_spr == LINKREG) return InstrMFLR; break; // // Move from Condition Register: "mfcr r.x" // case OP_INTENT (MFCR_OP, UnwindReverse): case OP_INTENT (MFCR_OP, UnwindReverseR12): return InstrMFCR; // // Move to Condition Register: "mtcrf 255,r.x" // case OP_INTENT (MTCRF_OP, UnwindForward): if (I->XFXform_FXM == 255) return InstrMFCR; break; default: // unrecognized break; } default: // unrecognized break; } // // Instruction not recognized; just ignore it and carry on // return InstrIgnore; #undef OP_INTENT } #ifdef _IMAGEHLP_SOURCE_ static #endif ULONG RtlVirtualUnwind ( #ifdef _IMAGEHLP_SOURCE_ HANDLE hProcess, DWORD ControlPc, PRUNTIME_FUNCTION FunctionEntry, PCONTEXT ContextRecord, PREAD_PROCESS_MEMORY_ROUTINE ReadMemory, PFUNCTION_TABLE_ACCESS_ROUTINE FunctionTableAccess #define ContextPointers ((PKNONVOLATILE_CONTEXT_POINTERS)0) #else IN ULONG ControlPc, IN PRUNTIME_FUNCTION FunctionEntry, IN OUT PCONTEXT ContextRecord, OUT PBOOLEAN InFunction, OUT PULONG EstablisherFrame, IN OUT PKNONVOLATILE_CONTEXT_POINTERS ContextPointers OPTIONAL, IN ULONG LowStackLimit, IN ULONG HighStackLimit #endif ) /*++ Routine Description: This function virtually unwinds the specfified function by executing its prologue code backwards. If the function is a leaf function, then the address where control left the previous frame is obtained from the context record. If the function is a nested function, but not an exception or interrupt frame, then the prologue code is executed backwards and the address where control left the previous frame is obtained from the updated context record. If the function is register save millicode, it is treated as a leaf function. If the function is register restore millicode, the remaining body is executed forwards and the address where control left the previous frame is obtained from the final blr instruction. If the function was called via glue code and is not that glue code, the prologe of the glue code is executed backwards in addition to the above actions. Otherwise, an exception or interrupt entry to the system is being unwound and a specially coded prologue restores the return address twice. Once from the fault instruction address and once from the saved return address register. The first restore is returned as the function value and the second restore is place in the updated context record. If a context pointers record is specified, then the address where each nonvolatile registers is restored from is recorded in the appropriate element of the context pointers record. Arguments: ControlPc - Supplies the address where control left the specified function. FunctionEntry - Supplies the address of the function table entry for the specified function or NULL if the function is a leaf function. ContextRecord - Supplies the address of a context record. InFunction - Supplies a pointer to a variable that receives whether the control PC is within the current function. EstablisherFrame - Supplies a pointer to a variable that receives the the establisher frame pointer value. ContextPointers - Supplies an optional pointer to a context pointers record. LowStackLimit, HighStackLimit - Range of valid values for the stack pointer. This indicates whether it is valid to examine NextPc. Return Value: The address where control left the previous frame is returned as the function value. --*/ { ITERATOR Iterator[8]; PITERATOR Piterator; ULONG Address; PDOUBLE FloatingRegister; PPC_INSTRUCTION I; PULONG IntegerRegister; ULONG NextPc, Pc; BOOLEAN RestoredLr = FALSE; BOOLEAN RestoredSp = FALSE; BOOLEAN ComputedSp = FALSE; ULONG Rt; MILLICODE_INFO Info; INSTR_CLASS InstrClass; #ifdef _IMAGEHLP_SOURCE_ DWORD ImagehlpCb = 0; RUNTIME_FUNCTION SavedFunctionEntry; #else ULONG EstablisherFrameValue; #endif // // Set the base address of the integer and floating register arrays. // FloatingRegister = &ContextRecord->Fpr0; IntegerRegister = &ContextRecord->Gpr0; // // If the function is a leaf function, perform the default unwinding // action and check to see if the function was called via glue. // if (FunctionEntry == NULL) { // // Set point at which control left the previous routine. // NextPc = ContextRecord->Lr - 4; // // If the next control PC is the same as the old control PC, then // the function table is not correctly formed. // if (NextPc == ControlPc) return NextPc; goto CheckForGlue; } #ifdef _IMAGEHLP_SOURCE_ else { SavedFunctionEntry = *FunctionEntry; FunctionEntry = &SavedFunctionEntry; } #endif // // Set initial values for EstablisherFrame and Offset. // (this may need more careful planning IBMPLJ). // NOT_IMAGEHLP (*EstablisherFrame = EstablisherFrameValue = ContextRecord->Gpr1); READ_ULONG (ControlPc, I.Long); if (I.Long == RETURN_INSTR) { // // If the instruction at the point where control left the specified // function is a return, then any saved registers have been restored // and the control PC is not considered to be in the function // (i.e., in an epilogue). // NOT_IMAGEHLP(*InFunction = FALSE); NextPc = ContextRecord->Lr; goto CheckForGlue; } InstrClass = ClassifyInstruction(&I, UnwindReverse, #ifdef _IMAGEHLP_SOURCE_ hProcess, ReadMemory, FunctionTableAccess, #endif ControlPc, &Info); if (InstrClass == InstrRestoreCode) { // // If the instruction at the point where control left the // specified function is a branch to register restore // millicode, the state is restored by simulating the // execution of the restore millicode. The control PC is in // an epilogue. // Iterator[0].BeginPc = Info.TargetPc; Iterator[0].EndPc = Info.FunctionEntry->EndAddress; Iterator[0].Increment = 4; Iterator[0].Intent = UnwindForward; NOT_IMAGEHLP(*InFunction = FALSE); } else if ( #ifdef FUNCTION_ENTRY_IS_IMAGE_STYLE (FunctionEntry->BeginAddress - FunctionEntry->PrologEndAddress) == 2 #else FunctionEntry->ExceptionHandler == 0 && (ULONG)FunctionEntry->HandlerData == 2 #endif ) { // // If the address is in register restore millicode, the state // is restored by completing the execution of the restore // millicode. The control PC is in an epilogue. // Iterator[0].BeginPc = ControlPc; Iterator[0].EndPc = FunctionEntry->EndAddress; Iterator[0].Increment = 4; Iterator[0].Intent = UnwindForward; NOT_IMAGEHLP(*InFunction = FALSE); } else { // // If the address where control left the specified function is a // TOC restore instruction and the previous instruction is a call // via glue instruction, we must forward execute the TOC restore // instruction. // if (InstrClass == InstrTOCRestore) { PPC_INSTRUCTION Iprev; READ_ULONG (ControlPc - 4, Iprev.Long); if (ClassifyInstruction (&Iprev, UnwindReverse, #ifdef _IMAGEHLP_SOURCE_ hProcess, ReadMemory, FunctionTableAccess, #endif ControlPc - 4, &Info) == InstrGlue) { // // Forward execute the TOC restore. We assume (reasonably) // that the next instruction is covered by the same function // table entry and it isn't one of the above special cases // (InstrRestoreCode or InstrBLR). // ControlPc += 4; Address = IntegerRegister[I.Dform_RA] + I.Dform_D; Rt = I.Dform_RT; READ_ULONG (Address, IntegerRegister[Rt]); if (ARGUMENT_PRESENT (ContextPointers)) ContextPointers->IntegerContext[Rt] = (PULONG) Address; } } // // If the address where control left the specified function is // outside the limits of the prologue, then the control PC is // considered to be within the function and the control // address is set to the end of the prologue. Otherwise, the // control PC is not considered to be within the function // (i.e., in the prologue). // Iterator[0].EndPc = FunctionEntry->BeginAddress - 4; Iterator[0].Increment = -4; Iterator[0].Intent = UnwindReverse; if ((ControlPc < FunctionEntry->BeginAddress) || (ControlPc >= (FunctionEntry->PrologEndAddress & ~3))) { NOT_IMAGEHLP(*InFunction = TRUE); Iterator[0].BeginPc = ((FunctionEntry->PrologEndAddress & ~3) - 4); } else { NOT_IMAGEHLP(*InFunction = FALSE); Iterator[0].BeginPc = ControlPc - 4; } } // // Scan through the given instructions and reload callee registers // as indicated. // NextPc = ContextRecord->Lr - 4; UnwindGlue: for (Piterator = Iterator; Piterator >= Iterator; Piterator--) { for (Pc = Piterator->BeginPc; Pc != Piterator->EndPc; Pc += Piterator->Increment) { READ_ULONG (Pc, I.Long); Address = IntegerRegister[I.Dform_RA] + I.Dform_D; Rt = I.Dform_RT; switch (ClassifyInstruction (&I, Piterator->Intent, #ifdef _IMAGEHLP_SOURCE_ hProcess, ReadMemory, FunctionTableAccess, #endif Pc, &Info)) { // // Move from Link Register (save LR in a GPR) // // In the usual case, the link register gets set by a call // instruction so the PC value should point to the // instruction that sets the link register. In an interrupt // or exception frame, the link register and PC value are // independent. By convention, fake prologues for these // frames store the link register twice: once to the link // register location, once to the faulting PC location. // // If this is the first time that RA is being restored, // then set the address of where control left the previous // frame. Otherwise, this is an interrupt or exception and // the return PC should be biased by 4 and the link register // value should be updated. // case InstrMFLR: ContextRecord->Lr = IntegerRegister[Rt]; if ( RestoredLr == FALSE ) { NextPc = ContextRecord->Lr - 4; RestoredLr = TRUE; } else { NextPc += 4; } continue; // Next PC // // Branch to Link Register (forward execution). // case InstrBLR: NextPc = ContextRecord->Lr - 4; break; // Terminate simulation--start next iterator. // // Move from Condition Register (save CR in a GPR) // case InstrMFCR: ContextRecord->Cr = IntegerRegister[Rt]; continue; // Next PC // // Store word (save a GPR) // case InstrSTW: // // Even though a stw r.sp, xxxx in general is an invalid // proloque instruction there are places in the kernel // fake prologues (KiExceptionExit) where we must use this, // so handle it. // READ_ULONG (Address, IntegerRegister[Rt]); if (ARGUMENT_PRESENT (ContextPointers)) ContextPointers->IntegerContext[Rt] = (PULONG) Address; continue; // Next PC // // Store word with update, Store word with update indexed // (buy stack frame, updating stack pointer and link // cell in storage) // case InstrSTWU: Address = IntegerRegister[GPR1]; READ_ULONG(Address,IntegerRegister[GPR1]); if (RestoredSp == FALSE) { NOT_IMAGEHLP (*EstablisherFrame = EstablisherFrameValue = ContextRecord->Gpr1); RestoredSp = TRUE; } if (ARGUMENT_PRESENT (ContextPointers)) ContextPointers->IntegerContext[Rt] = (PULONG) Address; continue; // Next PC // // Store floating point double (save an FPR) // case InstrSTFD: READ_DOUBLE (Address, FloatingRegister[Rt]); if (ARGUMENT_PRESENT (ContextPointers)) ContextPointers->FloatingContext[Rt] = (PDOUBLE) Address; continue; // Next PC // // Move register. Certain forms are ignored based on the intent. // case InstrMR: IntegerRegister[I.Xform_RA] = IntegerRegister[Rt]; continue; // Next PC case InstrMRfwd: IntegerRegister[Rt] = IntegerRegister[I.Xform_RA]; continue; // Next PC case InstrMRr12: IntegerRegister[Rt] = IntegerRegister[I.Xform_RA]; break; // Terminate search--start next iterator. // // Add immediate. Certain forms are ignored based on the intent. // case InstrADDIfwd: IntegerRegister[Rt] = Address; continue; // Next PC case InstrADDIr12: if (!ComputedSp) { // No intervening instruction changes r.1, so compute // addi r.12,r.1,N instead of addi r.12,r.12,N. IntegerRegister[Rt] = IntegerRegister[GPR1]; } IntegerRegister[Rt] += I.Dform_SI; break; // Terminate search--start next iterator. // // Store with update while searching for the value of r.12 // case InstrSTWUr12: ComputedSp = TRUE; Address = IntegerRegister[GPR1]; READ_ULONG(Address,IntegerRegister[GPR12]); continue; // Next PC // // A call to a register save millicode. // case InstrSaveCode: // // Push an iterator to incorporate the actions of the // millicode. // Piterator++; Piterator->BeginPc = Info.FunctionEntry->EndAddress - 4; Piterator->EndPc = Info.TargetPc - 4; Piterator->Increment = -4; Piterator->Intent = UnwindReverseR12; // // Push an iterator to determine the current value of r.12 // Piterator++; Piterator->BeginPc = Pc - 4; Piterator->EndPc = Piterator[-2].EndPc; Piterator->Increment = -4; Piterator->Intent = UnwindR12; ComputedSp = FALSE; // // Update the start of the original iterator so it can later // resume where it left off. // Piterator[-2].BeginPc = Pc - 4; Piterator++; break; // Start the next iterator. // // A branch was encountered in the prologue to a routine // identified as glue code. This should only happen from // fake prologues such as those in the system exception // handler. // // We handle it by pushing an iterator to incorporate // the actions of the glue code prologue. // case InstrGlue: // // Check that we don't nest too deeply. Verify that // we can push this iterator and the iterators for a // glue sequence. There's no need to make this check // elsewhere because B_OP is only recognized during // UnwindReverse. Returing zero is the only error action // we have. // if (Piterator - Iterator + 4 > sizeof (Iterator) / sizeof (Iterator[0])) return 0; // // Push an iterator to incorporate the actions of the glue // code's prologue. Check that we don't nest too deeply. // Verify that we can push this iterator and the iterators // for a glue sequence. // Piterator++; Piterator->BeginPc = (Info.FunctionEntry->PrologEndAddress & ~3) - 4; Piterator->EndPc = Info.FunctionEntry->BeginAddress - 4; Piterator->Increment = -4; Piterator->Intent = UnwindReverse; // // Update the start of the original iterator so it can later // resume where it left off. // Piterator[-1].BeginPc = Pc - 4; Piterator++; break; // Start the next iterator. // // Special "set establisher" instruction (rfi). // // Kernel fake prologues that can't use stwu (KiExceptionExit, // KiAlternateExit) use an rfi instruction to tell the // unwinder to update the establisher frame pointer using // the current value of sp. // case InstrSetEstablisher: NOT_IMAGEHLP (*EstablisherFrame = EstablisherFrameValue = ContextRecord->Gpr1); continue; // Next PC // // None of the above. Just ignore the instruction. It // is presumed to be non-prologue code that has been // merged into the prologue for scheduling purposes. It // may also be improper code in a register save/restore // millicode routine or unimportant code when // determining the value of r.12. // case InstrIgnore: default: continue; // Next PC } break; // Start the next iterator. } // end foreach Pc } // end foreach Iterator CheckForGlue: // // Check that we aren't at the end of the call chain. We now require // that the link register at program start-up be zero. Unfortunately, // this isn't always true. Also verify that the stack pointer remains // valid. // if (NextPc == 0 || NextPc + 4 == 0 #ifdef _IMAGEHLP_SOURCE_ || NextPc == 1 #else || EstablisherFrameValue < LowStackLimit || EstablisherFrameValue > HighStackLimit || (EstablisherFrameValue & 0x7) != 0 #endif ) return NextPc; // // Is the instruction at NextPc an branch? // #ifdef _IMAGEHLP_SOURCE_ READ_ULONG (NextPc, I.Long); #else if ( !NT_SUCCESS(TryReadUlong(NextPc, &I.Long)) ) { return NextPc; } #endif if (I.Primary_Op != B_OP) return NextPc; // // Compute branch target address, allowing for branch-relative // and branch-absolute. // Pc = ((LONG)(I.Iform_LI) << 2) + (I.Iform_AA ? 0 : NextPc); // // If the branch target is contained in this function table entry, // either this function is glue or it wasn't called via glue. This // is the usual case. // if (FunctionEntry != NULL && Pc >= FunctionEntry->BeginAddress && Pc < FunctionEntry->EndAddress) return NextPc; // // Allow for a stub glue in the thunk and a common ptrgl function // where the stub glue does not have a function table entry. // if ((FunctionEntry = (PRUNTIME_FUNCTION)RtlLookupFunctionEntry(Pc)) != NULL) { // // The target instruction must be "lwz r.x,disp(r.2)". // READ_ULONG (Pc, I.Long); if (I.Primary_Op == LWZ_OP && I.Dform_RA == GPR2) { // // The next instruction must be "b glue-code". // READ_ULONG (Pc + 4, I.Long); if (I.Primary_Op == B_OP && I.Iform_LK) { // // Compute branch target address, allowing for // branch-relative and branch-absolute. // Pc = ((LONG)(I.Iform_LI) << 2) + (I.Iform_AA ? 0 : Pc + 4); FunctionEntry = (PRUNTIME_FUNCTION)RtlLookupFunctionEntry(Pc); } } } // // Determine whether the branch target is glue code. // if (!(FunctionEntry != NULL #ifndef FUNCTION_ENTRY_IS_IMAGE_STYLE && FunctionEntry->ExceptionHandler == 0 && (ULONG)FunctionEntry->HandlerData == 3 #else && (FunctionEntry->BeginAddress < FunctionEntry->PrologEndAddress) && (FunctionEntry->PrologEndAddress & 3) == 1 #endif )) return NextPc; // // Unwind the glue code prologue. We won't loop because // next time through, the branch target will be contained in // the function table entry. // #ifdef _IMAGEHLP_SOURCE_ SavedFunctionEntry = *FunctionEntry; FunctionEntry = &SavedFunctionEntry; #endif Iterator[0].EndPc = FunctionEntry->BeginAddress - 4; Iterator[0].Increment = -4; Iterator[0].Intent = UnwindReverse; Iterator[0].BeginPc = ((FunctionEntry->PrologEndAddress & ~3) - 4); goto UnwindGlue; } #undef NOT_IMAGEHLP