📄 shunwind.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:
// shunwind.c
//
// Abstract:
// This module implements the unwinding of procedure call frames for
// exception processing on Hitachi SH-3 & SH-4.
//
//------------------------------------------------------------------------------
#include "kernel.h"
#include "shxinst.h"
#define sp 0xf /* register 15 */
// The saved registers are the permanent general registers (ie. those
// that get restored in the epilog)
#define SAVED_INTEGER_MASK 0x0000ff00 /* saved integer registers */
#define IS_REGISTER_SAVED(Register) ((SAVED_INTEGER_MASK >> Register) & 1L)
#if SH4
#define SAVED_FLOATING_MASK 0x0000f000 /* saved floating pt registers */
#define IS_FLOAT_REGISTER_SAVED(Register) \
((SAVED_FLOATING_MASK >> Register) & 1L)
#define FR_MASK 0x00200000 /* masks FR bit in fpscr */
#define SZ_MASK 0x00100000 /* masks SZ bit in fpscr */
#define PR_MASK 0x00080000 /* masks PR bit in fpscr */
#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.
//
// 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.
//
// 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.
//
// 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 ContextRecord,
OUT PBOOLEAN InFunction,
OUT PULONG EstablisherFrame,
IN PPROCESS pprc
)
{
ULONG Address;
ULONG TemporaryValue;
ULONG TemporaryRegister;
SH3IW Instruction;
USHORT instrProlog;
PULONG IntegerRegister;
#if SH4
PULONG FloatingRegister;
#endif
ULONG NextPc;
ULONG Offset;
ULONG Rm;
ULONG Rn;
LONG PendingValue;
BOOL PendingAdd;
ULONG PendingSource;
ULONG PendingTarget;
ULONG pcOfst = ControlPc - (ULONG) ZeroPtr (ControlPc);
ULONG BeginAddress = FunctionEntry->BeginAddress;
ULONG PrologEndAddress = FunctionEntry->PrologEndAddress;
IntegerRegister = &ContextRecord->R0;
NextPc = ContextRecord->PR - 2;
*InFunction = FALSE;
// map slot 0 address to the process
if (BeginAddress < (1 << VA_SECTION)) {
BeginAddress += pcOfst;
PrologEndAddress += pcOfst;
}
#if SH4
// We assume FR bit always 0 in prolog.
FloatingRegister = ContextRecord->FRegs;
#endif
// If we are at the end of the function, and the delay slot instruction
// is one of the following, then just execute the instruction...
// MOV Rm,sp
// ADD Rm,sp
// ADD #imm,sp
// MOV.L @sp+,Rn
// For SH4:
// FSCHG
// FMOV.S @sp+,FRn
// There is no need reverse execute the prolog, because the epilog has
// already taken care of restoring all the other permanent registers.
instrProlog = *(PUSHORT)ControlPc;
if (instrProlog == RTS) {
ControlPc += 2;
Instruction = *(PSH3IW)ControlPc;
// TRAPA #1
// Could a user insert a breakpoint in the delay slot of a function return?
// And would we support such a bold move? Yes and Yes.
if ((Instruction.instr5.opcode == 0xc3) &&
(Instruction.instr5.imm_disp == 0x01)) {
(*KDSanitize)((BYTE*)&Instruction, (void*)ControlPc, sizeof(Instruction), FALSE);
}
// MOV Rm,sp
if ((Instruction.instr3.opcode == 0x6) &&
(Instruction.instr3.ext_disp == 0x3)) {
Rm = Instruction.instr3.regM;
Rn = Instruction.instr3.regN;
if (Rn == sp)
IntegerRegister[sp] = IntegerRegister[Rm];
}
// ADD Rm,sp
else if ((Instruction.instr3.opcode == 0x3) &&
(Instruction.instr3.ext_disp == 0xc)) {
Rm = Instruction.instr3.regM;
Rn = Instruction.instr3.regN;
if (Rn == sp)
IntegerRegister[sp] += IntegerRegister[Rm];
}
// ADD #imm,sp
//
// Note: the offset added to sp will always be positive...
else if (Instruction.instr2.opcode == 0x7) {
Rn = Instruction.instr2.reg;
Offset = Instruction.instr2.imm_disp;
if (Rn == sp)
IntegerRegister[sp] += Offset;
}
// MOV.L @sp+,Rn
else if ((Instruction.instr3.opcode == 0x6) &&
(Instruction.instr3.ext_disp == 0x6)) {
Rm = Instruction.instr3.regM;
Rn = Instruction.instr3.regN;
if ((Rm == sp) && IS_REGISTER_SAVED(Rn)) {
IntegerRegister[Rn] = *(PULONG)IntegerRegister[sp];
IntegerRegister[sp] += 4;
}
}
#if SH4
// FMOV.S @sp+, FRn
else if ((Instruction.instr3.opcode == 0xf) &&
(Instruction.instr3.ext_disp == 0x9) &&
(Instruction.instr3.regM == sp)) {
Rn = Instruction.instr3.regN;
if (IS_FLOAT_REGISTER_SAVED(Rn)) {
FloatingRegister[Rn] = *(PULONG)IntegerRegister[sp];
IntegerRegister[sp] += 4;
}
}
// FSCHG
else if (Instruction.instr6.opcode == 0xf3fd) {
ContextRecord->Fpscr ^= SZ_MASK; // toggle the SZ bit
}
#endif
*EstablisherFrame = ContextRecord->R15;
return NextPc;
}
#if SH4
// Always expect FR and PR bits to be 0 throughout the prolog
ContextRecord->Fpscr &= ~(FR_MASK | PR_MASK);
#endif
// 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., it is within the prologue).
if ((ControlPc < BeginAddress) ||
(ControlPc >= PrologEndAddress)) {
*InFunction = TRUE;
ControlPc = PrologEndAddress;
#if SH4
// Always expect SZ bit to be 0 at end of prolog
ContextRecord->Fpscr &= ~(SZ_MASK);
#endif
}
// Scan backward through the prologue and reload callee registers that
// were stored.
//
// Cannot initialize holding registers to 0 because that is a valid
// register value...
PendingSource = (ULONG)-1;
TemporaryRegister = (ULONG)-1;
while (ControlPc > BeginAddress) {
ControlPc -= 2;
Instruction = *(PSH3IW)ControlPc;
// TRAPA #1
// Replace any breakpoints in the prolog with the instructions they replaced
if ((Instruction.instr5.opcode == 0xc3) &&
(Instruction.instr5.imm_disp == 0x01)) {
(*KDSanitize)((BYTE*)&Instruction, (void*)ControlPc, sizeof(Instruction), FALSE);
}
// MOV #imm,Rn
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -