📄 unwind.c
字号:
//
// Copyright (c) Microsoft Corporation. All rights reserved.
//
//
// This source code is licensed under Microsoft Shared Source License
// Version 1.0 for Windows CE.
// For a copy of the license visit http://go.microsoft.com/fwlink/?LinkId=3223.
//
/*++
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 );
//------------------------------------------------------------------------------
//
// 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
//
//------------------------------------------------------------------------------
BOOL
ReadMemory(
void *src,
void *dest,
int length
)
{
__try {
memcpy(dest, src, length);
return TRUE;
} __except (1) {
}
return FALSE;
}
//------------------------------------------------------------------------------
//
// Routine Description:
// The function reads the user process's memory for the unwinder. The
// access is protected via try/except. The returned memory is "santized",
// all debugger inserted breakpoints are restored to the original instruction.
//
// 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
//
//------------------------------------------------------------------------------
BOOL
ReadMemorySanitized(
void *src,
void *dest,
int length
)
{
if (ReadMemory(src, dest, length)) {
(*KDSanitize)((BYTE *)dest, src, length, FALSE);
return TRUE;
}
return FALSE;
}
//------------------------------------------------------------------------------
//
// 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.
//
//------------------------------------------------------------------------------
ULONG
RtlVirtualUnwind(
IN ULONG ControlPc,
IN PRUNTIME_FUNCTION FunctionEntry,
IN OUT PCONTEXT Context,
OUT PBOOLEAN InFunction,
OUT PULONG EstablisherFrame,
IN PPROCESS pprc
)
{
//
// This function is simply a passthrough which dispatches to the correct
// unwinder for the processor mode at the current unwind point.
//
if (ControlPc & 0x01) { // Thumb Mode
return ThumbVirtualUnwind(ControlPc,
FunctionEntry,
Context,
InFunction,
EstablisherFrame
);
}
return ArmVirtualUnwind(ControlPc,
FunctionEntry,
Context,
InFunction,
EstablisherFrame
);
}
//------------------------------------------------------------------------------
//
// Routine Description:
//
// This is the ARM specific implementation of RtlVirtualUnwind
//
// Arguments:
//
// See RtlVirtualUnwind
//
// Return Value:
//
// See RtlVirtualUnwind
//
//------------------------------------------------------------------------------
ULONG
ArmVirtualUnwind(
IN ULONG ControlPc,
IN PRUNTIME_FUNCTION FunctionEntry,
IN OUT PCONTEXT Context,
OUT PBOOLEAN InFunction,
OUT PULONG EstablisherFrame
)
{
ULONG Address;
LONG cb;
DWORD NextPc;
BOOLEAN HaveExceptionPc;
BOOLEAN ExecutingEpilog;
LONG i,j;
ARMI instr, instr2;
ARMI Prolog[15];
PULONG Register;
ULONG pcOfst = ControlPc - (ULONG) ZeroPtr (ControlPc);
ULONG BeginAddress = FunctionEntry->BeginAddress;
ULONG PrologEndAddress = FunctionEntry->PrologEndAddress;
// 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.
DEBUGMSG(ZONE_SEH, (TEXT("Unwind: Pc=%8.8x Begin=%8.8x PrologEnd=%8.8x End=%8.8x\r\n"),
ControlPc, BeginAddress, PrologEndAddress,
FunctionEntry->EndAddress));
Register = &Context->R0;
*InFunction = FALSE;
if (PrologEndAddress == BeginAddress) {
// No prolog.
*EstablisherFrame = Register[13];
return Register[14] - 4;
}
// map slot 0 address to the process
if (BeginAddress < (1 << VA_SECTION)) {
BeginAddress += pcOfst;
PrologEndAddress += pcOfst;
}
// 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.
if (ControlPc >= PrologEndAddress) {
DWORD EpilogPc = ControlPc;
DEBUGMSG(ZONE_SEH, (TEXT("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 (!ReadMemorySanitized((PVOID)EpilogPc, &instr, 4))
return ControlPc;
ExecutingEpilog = CheckConditionCodes(Register[16], instr.instruction);
while (ExecutingEpilog) {
if (!ReadMemorySanitized((PVOID)EpilogPc, &instr, 4))
return ControlPc;
DEBUGMSG(ZONE_SEH, (TEXT("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) {
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -