1052 lines
35 KiB
C
1052 lines
35 KiB
C
|
/*++
|
|||
|
|
|||
|
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
|