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
|