Windows NT 4.0 source code leak
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

/*++
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);
}