summaryrefslogtreecommitdiffstats
path: root/private/ntos/rtl/ppc/vunwind.c
diff options
context:
space:
mode:
Diffstat (limited to 'private/ntos/rtl/ppc/vunwind.c')
-rw-r--r--private/ntos/rtl/ppc/vunwind.c1056
1 files changed, 1056 insertions, 0 deletions
diff --git a/private/ntos/rtl/ppc/vunwind.c b/private/ntos/rtl/ppc/vunwind.c
new file mode 100644
index 000000000..148910e2a
--- /dev/null
+++ b/private/ntos/rtl/ppc/vunwind.c
@@ -0,0 +1,1056 @@
+/*++
+
+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;
+
+ break;
+
+ case OP_INTENT (X19_OP, UnwindR12):
+ case OP_INTENT (X19_OP, UnwindReverse):
+ case OP_INTENT (X19_OP, UnwindReverseR12):
+ //
+ // 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