📄 unwind.c
字号:
/*++
Copyright (c) 1997-2000 Microsoft Corporation. All rights reserved.
Module Name:
unwind.c
Abstract:
This module implements the unwinding of procedure call frames for
exception processing.
--*/
#include "kernel.h"
#include "arminst.h"
#include <stdlib.h>
extern
ULONG
ThumbVirtualUnwind(
IN ULONG ControlPc,
IN PRUNTIME_FUNCTION FunctionEntry,
IN OUT PCONTEXT Context,
OUT PBOOLEAN InFunction,
OUT PULONG EstablisherFrame
);
//
// Prototypes for local functions:
//
BOOL CheckConditionCodes(ULONG CPSR, DWORD instr);
BOOL UnwindLoadMultiple(ARMI instr, PULONG Register);
void UnwindDataProcess(ARMI instr, PULONG Register);
BOOL UnwindLoadI(ARMI instr, PULONG Register);
ULONG ArmVirtualUnwind( IN ULONG ControlPc,
IN PRUNTIME_FUNCTION FunctionEntry,
IN OUT PCONTEXT Context,
OUT PBOOLEAN InFunction,
OUT PULONG EstablisherFrame );
BOOL ReadMemory(
void *src,
void *dest,
int length
)
/*++
Routine Description:
The function reads the user process's memory for the unwinder. The
access is protected via try/except.
Arguments:
src - ptr to source data
dest - ptr to destination buffer
length - number of bytes to copy
Return Value:
TRUE - if able to access memory OK
FALSE - if exception while accessing user memory
--*/
{
__try {
memcpy(dest, src, length);
return TRUE;
} __except (1) {
}
return FALSE;
}
ULONG
RtlVirtualUnwind(
IN ULONG ControlPc,
IN PRUNTIME_FUNCTION FunctionEntry,
IN OUT PCONTEXT Context,
OUT PBOOLEAN InFunction,
OUT PULONG EstablisherFrame)
/*++
Routine Description:
This function virtually unwinds the specfified function by executing its
prologue code backwards (or its epilog forward).
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.
Otherwise, an exception or interrupt entry to the system is being unwound
and a special instruction in the prologue indicates the fault instruction
address.
Arguments:
ControlPc - Supplies the address where control left the specified
function.
FunctionEntry - Supplies the address of the function table entry for the
specified 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.
Return Value:
The address where control left the previous frame is returned as the
function value.
--*/
{
//
// This function is simply a passthrough which dispatches to the correct
// unwinder for the processor mode at the current unwind point.
//
#if defined(THUMBSUPPORT)
if ( ControlPc & 0x01 ){ // Thumb Mode
return( ThumbVirtualUnwind( ControlPc,
FunctionEntry,
Context,
InFunction,
EstablisherFrame
) );
}
#endif
return( ArmVirtualUnwind( ControlPc,
FunctionEntry,
Context,
InFunction,
EstablisherFrame
) );
}
ULONG
ArmVirtualUnwind(
IN ULONG ControlPc,
IN PRUNTIME_FUNCTION FunctionEntry,
IN OUT PCONTEXT Context,
OUT PBOOLEAN InFunction,
OUT PULONG EstablisherFrame)
/*++
Routine Description:
This is the ARM specific implementation of RtlVirtualUnwind
Arguments:
See RtlVirtualUnwind
Return Value:
See RtlVirtualUnwind
--*/
{
ULONG Address;
LONG cb;
DWORD EpilogPc;
DWORD NextPc;
BOOLEAN HaveExceptionPc;
BOOLEAN ExecutingEpilog;
LONG i,j;
ARMI instr, instr2;
ARMI Prolog[10]; // The prolog will never be more than 10 instructions
PULONG Register;
// NOTE: When unwinding the call stack, we assume that all instructions
// in the prolog have the condition code "Always execute." This is not
// necessarily true for the epilog.
#if 0
NKDbgPrintfW(L"Unwind: Pc=%8.8x Begin=%8.8x PrologEnd=%8.8x End=%8.8x\r\n",
ControlPc, FunctionEntry->BeginAddress, FunctionEntry->PrologEndAddress,
FunctionEntry->EndAddress);
#endif
Register = &Context->R0;
*InFunction = FALSE;
if (FunctionEntry->PrologEndAddress-FunctionEntry->BeginAddress == 0) {
// No prolog.
*EstablisherFrame = Register[13];
return Register[14] - 4;
}
// First check to see if we are in the epilog. If so, forward execution
// through the end of the epilog is required. Epilogs are composed of the
// following:
//
// An ldmdb which uses the frame pointer, R11, as the base and updates
// the PC.
//
// -or-
//
// A stack unlink (ADD R13, x) if necessary, followed by an ldmia which
// updates the PC or a single mov instruction to copy the link register
// to the PC
//
// -or-
//
// An ldmia which updates the link register, followed by a regular
// branch instruction. (This is an optimization when the last instruction
// before a return is a call.)
//
// A routine may also have an empty epilog. (The last instruction is to
// branch to another routine, and it doesn't modify any permanent
// registers.) But, in this case, we would also have an empty prolog.
//
// If we are in an epilog, and the condition codes dictate that the
// instructions should not be executed, treat this as not an epilog at all.
EpilogPc = ControlPc;
if (EpilogPc >= FunctionEntry->PrologEndAddress) {
///NKDbgPrintfW(L"Checking epilog @%8.8x\r\n", EpilogPc);
// Check the condition code of the first instruction. If it won't be
// executed, don't bother checking what type of instruction it is.
if (!ReadMemory((PVOID)EpilogPc, &instr, 4))
return ControlPc;
ExecutingEpilog = CheckConditionCodes(Register[16], instr.instruction);
while (ExecutingEpilog) {
if (!ReadMemory((PVOID)EpilogPc, &instr, 4))
return ControlPc;
///NKDbgPrintfW(L"executing epilog @%8.8x\r\n", EpilogPc);
// Test for these instructions:
// ADD R13, X - stack unlink
//
// MOV PC, LR - return
//
// LDMIA or LDMDB including PC - update registers and return
// (SP) (FP)
//
// LDMIA including LR, followed by a branch
// Update registers and branch. (In our case, return.)
//
// A branch preceded by an LDMIA including LR
// Copy the LR to the PC to get the call stack
if ((instr.instruction & ADD_SP_MASK) == ADD_SP_INSTR) {
UnwindDataProcess(instr, Register);
} else if ((instr.instruction & MOV_PC_LR_MASK) == MOV_PC_LR) {
instr.dataproc.rd = 14; // don't damage Pc value for Unwinder
UnwindDataProcess(instr, Register);
*EstablisherFrame = Register[13];
return Register[14] - 4;
} else if ((instr.instruction & LDM_PC_MASK) == LDM_PC_INSTR) {
ulong SavePc = Register[15];
if (!UnwindLoadMultiple(instr, Register))
return ControlPc;
ControlPc = Register[15];
Register[15] = SavePc;
*EstablisherFrame = Register[13];
return ControlPc - 4;
} else if ((instr.instruction & LDM_LR_MASK) == LDM_LR_INSTR) {
if (!ReadMemory((PVOID)(EpilogPc+4), &instr2, 4))
return ControlPc;
if ( (instr2.instruction & B_BL_MASK) == B_INSTR ||
(instr2.instruction & BX_MASK) == BX_INSTR ) {
if (!UnwindLoadMultiple(instr, Register))
return ControlPc;
*EstablisherFrame = Register[13];
return Register[14] - 4;
} else
ExecutingEpilog = FALSE;
} else
ExecutingEpilog = FALSE;
EpilogPc += 4;
}
}
// We were not in the epilog. Load in the prolog, and reverse execute it.
cb = FunctionEntry->PrologEndAddress - FunctionEntry->BeginAddress;
if (cb > sizeof(Prolog)) {
ASSERT(FALSE); // The prolog should never be more than 10 instructions
return ControlPc;
}
//
// Thumb assembly functions which switch to ARM via "bx pc" will have
// the low bit of the BeginAddress set. Align the BeginAddress
//
Address = FunctionEntry->BeginAddress & ~0x03;
//NKDbgPrintfW(L"Reading Prolog @%8.8x\r\n", Address);
if (!ReadMemory((PVOID)Address, (PVOID)Prolog, cb))
return ControlPc;
// Check to see if we're already in the prolog.
if (ControlPc < FunctionEntry->PrologEndAddress)
cb = ControlPc - FunctionEntry->BeginAddress;
else
*InFunction = TRUE;
*EstablisherFrame = Register[13];
HaveExceptionPc = FALSE;
// Reverse execute starting with the last instruction in the prolog
// that has been executed.
for (i = cb/4 - 1 ; i >= 0 ; --i) {
// Test for these instructions:
//
// SUB SP, X - stack allocation
//
// MOV X, SP - save stack value
//
// STMDB R13, {PC} - special instruction in fake prolog
//
// STMDB - save incoming registers
//
// MOV R11, X or
// ADD R11, X or
// SUB R11, X - FP (frame pointer) setup
//
if ((Prolog[i].instruction & MOV_SP_MASK) == MOV_SP_INSTR) {
Register[13] = Register[Prolog[i].dataproc.rd];
} else if ((Prolog[i].instruction & SUB_SP_MASK) == SUB_SP_INSTR) {
// This is a stack link. Unlink the stack.
///NKDbgPrintfW(L"%d: stack link instr.\r\n", i);
if (Prolog[i].dataproc.bits != 0x1 && Prolog[i].dpshi.rm == 0xc) {
// Look for an LDR instruction above this one.
j = i - 1;
while (j >= 0 && (Prolog[j].instruction & LDR_MASK) != LDR_PC_INSTR)
j--;
if (j == 0) {
ASSERT(FALSE); // This should never happen
return ControlPc;
}
// Get the address of the ldr instruction + 8 + the offset
Address = j*4 + FunctionEntry->BeginAddress + Prolog[j].ldr.offset + 8;
// R12 is the value at that location.
if (!ReadMemory((PVOID)Address, (PVOID)&Register[12], 4))
return ControlPc;
}
// Change the subtract to an add, and execute the instruction
Prolog[i].dataproc.opcode = OP_ADD;
UnwindDataProcess(Prolog[i], Register);
} else if ((Prolog[i].instruction & STM_PC_MASK) == STM_PC_INSTR) {
//
// STMDB R13!, { PC }
//
// This is an instruction that will not appear in a real prolog
// (or probably anywhere else). It is used as a marker in a fake
// prolog as described below.
//
// 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, the fake prolog for these frames (see
// CaptureContext in armtrap.s) contains the STMDB instruction
// shown above to lead the unwinder to the faulting PC location.
//
if (!ReadMemory((PULONG)Register[13], &NextPc, 4))
return ControlPc;
Register[13] += 4;
HaveExceptionPc = TRUE;
}
else if ((Prolog[i].instruction & STM_MASK) == STM_INSTR) {
//
// Invert the Load, Increment and Before bits:
//
Prolog[i].instruction ^= 0x01900000;
if (!UnwindLoadMultiple(Prolog[i], Register))
return ControlPc;
} else if ((Prolog[i].instruction & STRI_LR_SPU_MASK)
== STRI_LR_SPU_INSTR) {
// Store of the link register that updates the stack as a base
// register must be reverse executed to restore LR and SP
// to their values on entry. This type of prolog is generated
// for finally funclets.
///NKDbgPrintfW(L"%d: STRI for mask=%4.4x.\r\n", i, Prolog[i].ldr.offset);
//
// Invert the Load, Increment and Before bits:
//
Prolog[i].instruction ^= 0x01900000;
if (!UnwindLoadI(Prolog[i], Register))
return ControlPc;
} else if ((Prolog[i].instruction & ARM_MRS_MASK) == ARM_MRS_INSTR) {
//
// An "MRS rd, cpsr" instruction exists in CaptureContext to
// preserve the Psr register when unwinding out of system calls.
//
Context->Psr = Register[Prolog[i].mrs.rd];
} else if ((Prolog[i].instruction & ADD_FP_MASK) == ADD_FP_INSTR
|| (Prolog[i].instruction & SUB_FP_MASK) == SUB_FP_INSTR
|| (Prolog[i].instruction & MOV_FP_MASK) == MOV_FP_INSTR) {
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -