mirror of https://github.com/lianthony/NT4.0
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
333 lines
11 KiB
333 lines
11 KiB
/*++
|
|
|
|
Copyright (c) 1992 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
machine.c
|
|
|
|
Abstract:
|
|
|
|
This file contains machine specific code to support the callmon program
|
|
|
|
Author:
|
|
|
|
Wesley Witt (wesw) 2-May-1995
|
|
|
|
Revision History:
|
|
|
|
--*/
|
|
|
|
#include "callmonp.h"
|
|
#include "mipsinst.h"
|
|
|
|
enum {
|
|
REGF0, REGF1, REGF2, REGF3, REGF4, REGF5, REGF6, REGF7,
|
|
REGF8, REGF9, REGF10, REGF11, REGF12, REGF13, REGF14, REGF15,
|
|
REGF16, REGF17, REGF18, REGF19, REGF20, REGF21, REGF22, REGF23,
|
|
REGF24, REGF25, REGF26, REGF27, REGF28, REGF29, REGF30, REGF31,
|
|
|
|
REGZERO, REGAT, REGV0, REGV1, REGA0, REGA1, REGA2, REGA3,
|
|
REGT0, REGT1, REGT2, REGT3, REGT4, REGT5, REGT6, REGT7,
|
|
REGS0, REGS1, REGS2, REGS3, REGS4, REGS5, REGS6, REGS7,
|
|
REGT8, REGT9, REGK0, REGK1, REGGP, REGSP, REGS8, REGRA,
|
|
|
|
REGLO, REGHI, REGFSR, REGFIR, REGPSR,
|
|
|
|
FLAGCU,
|
|
FLAGCU3, FLAGCU2, FLAGCU1, FLAGCU0,
|
|
FLAGIMSK,
|
|
FLAGINT5, FLAGINT4, FLAGINT3, FLAGINT2, FLAGINT1, FLAGINT0,
|
|
FLAGSW1, FLAGSW0,
|
|
FLAGKUO, FLAGIEO, FLAGKUP, FLAGIEP, FLAGKUC, FLAGIEC,
|
|
FLAGKSU, FLAGERL, FLAGEXL, FLAGIE,
|
|
|
|
FLAGFPC,
|
|
|
|
PREGEA, PREGEXP, PREGRA, PREGP,
|
|
PREGU0, PREGU1, PREGU2, PREGU3, PREGU4,
|
|
PREGU5, PREGU6, PREGU7, PREGU8, PREGU9
|
|
};
|
|
|
|
#define FLTBASE REGF0
|
|
#define REGBASE REGZERO
|
|
#define FLAGBASE FLAGCU
|
|
#define PREGBASE PREGEA
|
|
|
|
|
|
|
|
struct SubReg {
|
|
ULONG regindex;
|
|
ULONG shift;
|
|
ULONG mask;
|
|
};
|
|
|
|
struct SubReg subregname[] = {
|
|
{ REGPSR, 28, 0xf }, // CU mask
|
|
{ REGPSR, 31, 1 }, // CU3 flag
|
|
{ REGPSR, 30, 1 }, // CU2 flag
|
|
{ REGPSR, 29, 1 }, // CU1 flag
|
|
{ REGPSR, 28, 1 }, // CU0 flag
|
|
{ REGPSR, 8, 0xff }, // IMSK mask
|
|
{ REGPSR, 15, 1 }, // INT5 - int 5 enable
|
|
{ REGPSR, 14, 1 }, // INT4 - int 4 enable
|
|
{ REGPSR, 13, 1 }, // INT3 - int 3 enable
|
|
{ REGPSR, 12, 1 }, // INT2 - int 2 enable
|
|
{ REGPSR, 11, 1 }, // INT1 - int 1 enable
|
|
{ REGPSR, 10, 1 }, // INT0 - int 0 enable
|
|
{ REGPSR, 9, 1 }, // SW1 - software int 1 enable
|
|
{ REGPSR, 8, 1 }, // SW0 - software int 0 enable
|
|
|
|
// R3000-specific status bits
|
|
|
|
{ REGPSR, 5, 1 }, // KUO
|
|
{ REGPSR, 4, 1 }, // IEO
|
|
{ REGPSR, 3, 1 }, // KUP
|
|
{ REGPSR, 2, 1 }, // IEP
|
|
{ REGPSR, 1, 1 }, // KUC
|
|
{ REGPSR, 0, 1 }, // IEC
|
|
|
|
// R4000-specific status bits
|
|
|
|
{ REGPSR, 3, 2 }, // KSU
|
|
{ REGPSR, 2, 1 }, // ERL
|
|
{ REGPSR, 1, 1 }, // EXL
|
|
{ REGPSR, 0, 1 }, // IE
|
|
|
|
{ REGFSR, 23, 1 } // FPC - floating point condition
|
|
};
|
|
|
|
DWORD InstructionBuffer = BREAK_OP;
|
|
PVOID BreakpointInstruction = (PVOID)&InstructionBuffer;
|
|
ULONG SizeofBreakpointInstruction = sizeof( InstructionBuffer );
|
|
BREAKPOINT_INFO StepBreakpoint;
|
|
BOOLEAN StepActive = FALSE;
|
|
PTHREAD_INFO StepThread = NULL;
|
|
CONTEXT RegisterContext;
|
|
|
|
|
|
ULONG
|
|
GetRegValue(ULONG regnum)
|
|
{
|
|
return *(&RegisterContext.FltF0 + regnum);
|
|
}
|
|
|
|
ULONG
|
|
GetRegFlagValue (ULONG regnum)
|
|
{
|
|
ULONG value;
|
|
|
|
if (regnum < FLAGBASE || regnum >= PREGBASE) {
|
|
value = GetRegValue(regnum);
|
|
} else {
|
|
regnum -= FLAGBASE;
|
|
value = GetRegValue(subregname[regnum].regindex);
|
|
value = (value >> subregname[regnum].shift) & subregname[regnum].mask;
|
|
}
|
|
return value;
|
|
}
|
|
|
|
DWORD
|
|
GetNextFir(
|
|
PPROCESS_INFO Process,
|
|
ULONG FirAddr
|
|
)
|
|
{
|
|
MIPS_INSTRUCTION disinstr;
|
|
ULONG returnvalue;
|
|
ULONG opcode;
|
|
|
|
|
|
//
|
|
// Get current instruction
|
|
//
|
|
if (!ReadProcessMemory(
|
|
Process->Handle,
|
|
(PVOID)FirAddr,
|
|
&disinstr,
|
|
sizeof(disinstr),
|
|
NULL )) {
|
|
return 0;
|
|
}
|
|
|
|
opcode = disinstr.j_format.Opcode;
|
|
returnvalue = FirAddr + sizeof(ULONG) * 2; // assume delay slot
|
|
|
|
if (disinstr.Long == 0x0000000c) {
|
|
// stepping over a syscall instruction must set the breakpoint
|
|
// at the caller's return address, not the inst after the syscall
|
|
returnvalue = GetRegValue(REGRA);
|
|
}
|
|
else
|
|
if (opcode == 0x00L // SPECIAL
|
|
&& (disinstr.r_format.Function & ~0x01L) == 0x08L) {
|
|
// jr/jalr only
|
|
if (disinstr.r_format.Function == 0x08L /*|| !fStep*/) // jr or trace
|
|
returnvalue = GetRegValue(disinstr.r_format.Rs + REGBASE);
|
|
}
|
|
else if (opcode == 0x01L) {
|
|
// For BCOND opcode, RT values 0x00 - 0x03, 0x10 - 0x13
|
|
// are defined as conditional jumps. A 16-bit relative
|
|
// offset is taken if:
|
|
//
|
|
// (RT is even and (RS) < 0 (0x00 = BLTZ, 0x02 = BLTZL,
|
|
// 0x10 = BLTZAL, 0x12 = BLTZALL)
|
|
// OR
|
|
// RT is odd and (RS) >= 0 (0x01 = BGEZ, 0x03 = BGEZL
|
|
// 0x11 = BGEZAL, 0x13 = BGEZALL))
|
|
// AND
|
|
// (RT is 0x00 to 0x03 (BLTZ BGEZ BLTZL BGEZL non-linking)
|
|
// OR
|
|
// fStep is FALSE (linking and not stepping over))
|
|
//
|
|
if (((disinstr.i_format.Rt & ~0x13) == 0x00) &&
|
|
(((LONG)GetRegValue(disinstr.i_format.Rs + REGBASE) >= 0) ==
|
|
(BOOLEAN)(disinstr.i_format.Rt & 0x01)) &&
|
|
(((disinstr.i_format.Rt & 0x10) == 0x00) /*|| !fStep*/))
|
|
returnvalue = ((LONG)(SHORT)disinstr.i_format.Simmediate << 2)
|
|
+ FirAddr + sizeof(ULONG);
|
|
}
|
|
else if ((opcode & ~0x01L) == 0x02) {
|
|
// J and JAL opcodes (0x02 and 0x03). Target is
|
|
// 26-bit absolute offset using high four bits of the
|
|
// instruction location. Return target if J opcode or
|
|
// not stepping over JAL.
|
|
//
|
|
if (opcode == 0x02 /*|| !fStep*/)
|
|
returnvalue = (disinstr.j_format.Target << 2)
|
|
+ (FirAddr & 0xf0000000);
|
|
}
|
|
else if ((opcode & ~0x11L) == 0x04) {
|
|
// BEQ, BNE, BEQL, BNEL opcodes (0x04, 0x05, 0x14, 0x15).
|
|
// Target is 16-bit relative offset to next instruction.
|
|
// Return target if (BEQ or BEQL) and (RS) == (RT),
|
|
// or (BNE or BNEL) and (RS) != (RT).
|
|
//
|
|
if ((BOOLEAN)(opcode & 0x01) ==
|
|
(BOOLEAN)(GetRegValue(disinstr.i_format.Rs + REGBASE)
|
|
!= GetRegValue(disinstr.i_format.Rt + REGBASE)))
|
|
returnvalue = ((long)(short)disinstr.i_format.Simmediate << 2)
|
|
+ FirAddr + sizeof(ULONG);
|
|
}
|
|
else if ((opcode & ~0x11L) == 0x06) {
|
|
// BLEZ, BGTZ, BLEZL, BGTZL opcodes (0x06, 0x07, 0x16, 0x17).
|
|
// Target is 16-bit relative offset to next instruction.
|
|
// Return target if (BLEZ or BLEZL) and (RS) <= 0,
|
|
// or (BGTZ or BGTZL) and (RS) > 0.
|
|
//
|
|
if ((BOOLEAN)(opcode & 0x01) ==
|
|
(BOOLEAN)((long)GetRegValue(disinstr.i_format.Rs
|
|
+ REGBASE) > 0))
|
|
returnvalue = ((long)(short)disinstr.i_format.Simmediate << 2)
|
|
+ FirAddr + sizeof(ULONG);
|
|
}
|
|
else if (opcode == 0x11L
|
|
&& (disinstr.i_format.Rs & ~0x04L) == 0x08L
|
|
&& (disinstr.i_format.Rt & ~0x03L) == 0x00L) {
|
|
// COP1 opcode (0x11) with (RS) == 0x08 or (RS) == 0x0c and
|
|
// (RT) == 0x00 to 0x03, producing BC1F, BC1T, BC1FL, BC1TL
|
|
// instructions. Return target if (BC1F or BC1FL) and floating
|
|
// point condition is FALSE or if (BC1T or BC1TL) and condition TRUE.
|
|
//
|
|
if ((disinstr.i_format.Rt & 0x01) == GetRegFlagValue(FLAGFPC))
|
|
returnvalue = ((long)(short)disinstr.i_format.Simmediate << 2)
|
|
+ FirAddr + sizeof(ULONG);
|
|
}
|
|
else if ((opcode == 0x38) /*&& (fStep)*/) {
|
|
//
|
|
// stepping over an SC instruction, so skip the immediately following
|
|
// BEQ instruction. The SC will fail because we are tracing over it,
|
|
// the branch will be taken, and we will run through the LL/SC again
|
|
// until the SC succeeds. Then the branch won't be taken, and we
|
|
// will hit our breakpoint.
|
|
//
|
|
|
|
returnvalue += sizeof(ULONG); // skip BEQ and BEQ's delay slot
|
|
}
|
|
else
|
|
returnvalue -= sizeof(ULONG); // remove delay slot
|
|
|
|
return returnvalue;
|
|
}
|
|
|
|
BOOLEAN
|
|
SkipOverHardcodedBreakpoint(
|
|
PPROCESS_INFO Process,
|
|
PTHREAD_INFO Thread,
|
|
PVOID BreakpointAddress
|
|
)
|
|
{
|
|
ULONG Instruction;
|
|
|
|
RegisterContext.ContextFlags = CONTEXT_FULL;
|
|
if (!GetThreadContext( Thread->Handle, &RegisterContext )) {
|
|
fprintf(stderr, "CALLMON: Failed to get context for thread %x %d\n", Thread->Id, GetLastError() );
|
|
return FALSE;
|
|
}
|
|
|
|
RegisterContext.Fir = (ULONG)((PCHAR)BreakpointAddress + 4);
|
|
|
|
if (!SetThreadContext( Thread->Handle, &RegisterContext )) {
|
|
fprintf(stderr, "CALLMON: Failed to set context for thread %x %d\n", Thread->Id, GetLastError() );
|
|
return FALSE;
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
BOOLEAN
|
|
BeginSingleStepBreakpoint(
|
|
PPROCESS_INFO Process,
|
|
PTHREAD_INFO Thread
|
|
)
|
|
{
|
|
DWORD NextFir;
|
|
|
|
RegisterContext.ContextFlags = CONTEXT_FULL;
|
|
if (!GetThreadContext( Thread->Handle, &RegisterContext )) {
|
|
fprintf(stderr, "CALLMON: Failed to get context for thread %x %d\n", Thread->Id, GetLastError() );
|
|
return FALSE;
|
|
}
|
|
if (!StepActive) {
|
|
NextFir = GetNextFir(Process, RegisterContext.Fir);
|
|
if (NextFir == 0) {
|
|
return(FALSE);
|
|
}
|
|
ZeroMemory(&StepBreakpoint, sizeof(StepBreakpoint));
|
|
StepBreakpoint.Address = (PVOID)NextFir;
|
|
StepBreakpoint.SavedInstructionValid = FALSE;
|
|
if (InstallBreakpoint(Process, &StepBreakpoint)) {
|
|
StepActive = TRUE;
|
|
StepThread = Thread;
|
|
return(TRUE);
|
|
} else {
|
|
return(FALSE);
|
|
}
|
|
}
|
|
return(FALSE);
|
|
}
|
|
|
|
|
|
BOOLEAN
|
|
EndSingleStepBreakpoint(
|
|
PPROCESS_INFO Process,
|
|
PTHREAD_INFO Thread
|
|
)
|
|
{
|
|
BOOLEAN Status;
|
|
|
|
RegisterContext.ContextFlags = CONTEXT_FULL;
|
|
if (!GetThreadContext( Thread->Handle, &RegisterContext )) {
|
|
fprintf(stderr, "CALLMON: Failed to get context for thread %x %d\n", Thread->Id, GetLastError() );
|
|
return FALSE;
|
|
}
|
|
if ((PVOID)RegisterContext.Fir == StepBreakpoint.Address) {
|
|
if (RemoveBreakpoint(Process, &StepBreakpoint)) {
|
|
ZeroMemory(&StepBreakpoint, sizeof(StepBreakpoint));
|
|
StepActive = FALSE;
|
|
StepThread = NULL;
|
|
return(TRUE);
|
|
}
|
|
}
|
|
return(FALSE);
|
|
}
|