Windows2000/private/windbg64/debugger/dm/alpmach.c
2020-09-30 17:12:32 +02:00

925 lines
21 KiB
C

/*++
Copyright (c) 1992 Microsoft Corporation
Module Name:
alpmach.c
Abstract:
This file contains the ALPHA specific code for dealing with
the process of stepping a single instruction. This includes
determination of the next offset to be stopped at and if the
instruction is all call type instruction.
Author:
Miche Baker-Harvey (miche) - stole the appropriate parts
from ntsd for the alpha version.
Environment:
Win32 - User
Notes:
There are equivalent INTEL and MIPS files.
--*/
#include "precomp.h"
#pragma hdrstop
extern INT nVerbose;
extern char rgchDebug[];
extern LPDM_MSG LpDmMsg;
extern CRITICAL_SECTION csContinueQueue;
#define FALSE 0
#define TRUE 1
#define STATIC static
ALPHA_INSTRUCTION disinstr;
ULONG findTypeFromOpcode(ULONG);
BOOL
IsRet(
HTHDX hthd,
LPADDR addr
)
{
DWORD instr;
DWORD cBytes;
if (!AddrReadMemory( hthd->hprc, hthd, addr, &instr, 4, &cBytes )) {
return FALSE;
}
return (instr == 0x6bfa8001);
}
void
IsCall (
HTHDX hthd,
LPADDR lpaddr,
LPINT lpf,
BOOL fStepOver
)
/*++
Routine Description:
IsCall
Arguments:
hthd - Supplies the handle to the thread
lpaddr - Supplies the address to be check for a call instruction
lpf - Returns class of instruction:
INSTR_IS_CALL
INSTR_BREAKPOINT
INSTR_SOFT_INTERRUPT
INSTR_TRACE_BIT
fStepOver - Supplies step over vs step into flag
Side Effects:
The value of lpaddr is set to point to the returned-to
instruction when the instruction is type INSTR_IS_CALL.
Return Value:
lpf - see Arguments.
--*/
{
DWORD opcode;
DWORD function;
ADDR firaddr = *lpaddr;
DWORD length;
ALPHA_INSTRUCTION disinstr;
BOOL r;
if (hthd->fIsCallDone) {
*lpaddr = hthd->addrIsCall;
*lpf = hthd->iInstrIsCall;
return;
}
/*
* Assume that this is not a call instruction
*/
*lpf = INSTR_TRACE_BIT;
/*
* Read in the dword which contains the instruction under
* inspection.
*/
r = AddrReadMemory(hthd->hprc,
hthd,
&firaddr,
&disinstr.Long,
sizeof(DWORD),
&length );
if (!r || length != sizeof(DWORD)) {
DPRINT(1, ("AddrReadMemory in IsCall FAILED"));
return;
}
/*
* Assume that this is a jump instruction and get the opcode.
* This is the top 6 bits of the instruction dword.
*/
opcode = disinstr.Jump.Opcode;
/*
* Jump and Branch to Subroutine are CALLs
*/
if (opcode == JMP_OP) {
function = disinstr.Jump.Function;
if (function == JSR_FUNC) {
*lpf = INSTR_IS_CALL;
}
}
if (opcode == BSR_OP) {
*lpf = INSTR_IS_CALL;
}
/*
* There are several PAL breakpoint operations,
* and a couple operations redirect control
*/
if (opcode == CALLPAL_OP) {
function = disinstr.Pal.Function;
switch(function) {
case (BPT_FUNC):
case (CALLKD_FUNC):
case (KBPT_FUNC):
*lpf = INSTR_BREAKPOINT;
break;
case (CALLSYS_FUNC):
case (GENTRAP_FUNC):
*lpf = INSTR_IS_CALL;
break;
default:
break;
}
}
DPRINT(1, ("(IsCall?) FIR=%08I64x Type=%s\n", firaddr,
*lpf==INSTR_IS_CALL ? "CALL":
(*lpf==INSTR_BREAKPOINT ? "BREAKPOINT":
(*lpf==INSTR_SOFT_INTERRUPT ? "INTERRUPT":
"NORMAL"))));
if (*lpf==INSTR_IS_CALL) {
lpaddr->addr.off += sizeof (disinstr);
hthd->addrIsCall = *lpaddr;
}
hthd->iInstrIsCall = *lpf;
return;
} /* IsCall() */
#if 1
ULONG64
AlpGetNextOffset (
HTHDX hthd,
BOOL fStep
)
/*++
Routine Description:
From a limited disassembly of the instruction pointed
by the FIR register, compute the offset of the next
instruction for either a trace or step operation.
trace -> the next instruction to execute
step -> the instruction in the next memory location or the
next instruction executed due to a branch (step
over subroutine calls).
Arguments:
hthd - Supplies the handle to the thread to get the next offset for
fStep - Supplies TRUE for STEP offset and FALSE for trace offset
Returns:
step or trace offset if input is TRUE or FALSE, respectively
Version Information:
This copy of GetNextOffset is from ntsd\alpha\ntdis.c@v16 (11/14/92)
12-may-97 kentf modified to make LDx_L .. STx_C sequences atomic.
--*/
{
ULONG64 returnvalue;
ULONG opcode;
ULONG64 updatedpc;
ULONG64 branchTarget;
BOOL r;
int i;
#define MAX_LOCKED_SEQUENCE 40
LONGLONG Rav;
LONGLONG Fav;
ULONGLONG Rbv;
DWORD length;
PULONGLONG RegArray = &hthd->context.IntV0;
ADDR firaddr;
ALPHA_INSTRUCTION disinstr;
AddrFromHthdx(&firaddr, hthd);
// relative addressing updates PC first
// Assume next sequential instruction is next offset
updatedpc = firaddr.addr.off + sizeof(ULONG);
r = AddrReadMemory(hthd->hprc,
hthd,
&firaddr,
&disinstr.Long,
sizeof(DWORD),
&length );
if (!r || length != sizeof(DWORD)) {
DPRINT(1, ("GetNextOffset: failed AddrReadMemory %08I64x\n", firaddr.addr.off));
assert(FALSE);
return 4;
}
opcode = disinstr.Memory.Opcode;
returnvalue = updatedpc;
switch(findTypeFromOpcode(opcode)) {
case ALPHA_CALLPAL:
if (disinstr.Pal.Function == CALLSYS_FUNC) {
return RegArray[RA_REG];
}
break;
case ALPHA_JUMP:
switch(disinstr.Jump.Function) {
case JSR_FUNC:
case JSR_CO_FUNC:
if (fStep) {
// Step over the subroutine call;
return returnvalue;
}
// fall through
case JMP_FUNC:
case RET_FUNC:
Rbv = RegArray[disinstr.Memory.Rb];
return (Rbv & (~3));
break;
}
break;
case ALPHA_BRANCH:
branchTarget = (updatedpc + (disinstr.Branch.BranchDisp * 4));
Rav = (LONGLONG)RegArray[disinstr.Memory.Ra];
DPRINT(1, ("Rav %08I64x returnValue %08I64x branchTarget %08I64x\n",
Rav, returnvalue, branchTarget));
switch(opcode) {
case BR_OP: return(branchTarget); break;
case BSR_OP: if (!fStep) return(branchTarget); break;
case BEQ_OP: if (Rav == 0) return(branchTarget); break;
case BLT_OP: if (Rav < 0) return(branchTarget); break;
case BLE_OP: if (Rav <= 0) return(branchTarget); break;
case BNE_OP: if (Rav != 0) return(branchTarget); break;
case BGE_OP: if (Rav >= 0) return(branchTarget); break;
case BGT_OP: if (Rav > 0) return(branchTarget); break;
case BLBC_OP: if ((Rav & 0x1) == 0) return(branchTarget); break;
case BLBS_OP: if ((Rav & 0x1) == 1) return(branchTarget); break;
};
return returnvalue;
break;
case ALPHA_FP_BRANCH:
branchTarget = (updatedpc + (disinstr.Branch.BranchDisp * 4));
RegArray = &hthd->context.FltF0;
Fav = (LONGLONG)RegArray[disinstr.Branch.Ra];
if (Fav == 0x8000000000000000) {
Fav = 0;
}
DPRINT(1, ("Fav %08I64x returnValue %08I64x branchTarget %08I64x\n",
Fav, returnvalue, branchTarget));
switch(opcode) {
case FBEQ_OP: if (Fav == 0) return (branchTarget); break;
case FBLT_OP: if (Fav < 0) return (branchTarget); break;
case FBNE_OP: if (Fav != 0) return (branchTarget); break;
case FBLE_OP: if (Fav <= 0) return (branchTarget); break;
case FBGE_OP: if (Fav >= 0) return (branchTarget); break;
case FBGT_OP: if (Fav > 0) return (branchTarget); break;
};
return returnvalue;
break;
case ALPHA_MEMORY:
if (opcode == LDL_L_OP || opcode == LDQ_L_OP) {
// BUGBUG kentf This is not good enough. It will fail if a branch is
// BUGBUG kentf taken before the STx_C.
firaddr.addr.off = updatedpc;
for (i = 0; i < MAX_LOCKED_SEQUENCE; i++) {
r = AddrReadMemory(hthd->hprc,
hthd,
&firaddr,
&disinstr.Long,
sizeof(DWORD),
&length );
if (!r || length != sizeof(DWORD)) {
DPRINT(1, ("GetNextOffset: failed AddrReadMemory %08I64x\n", firaddr.addr.off));
assert(FALSE);
return 4;
}
opcode = disinstr.Memory.Opcode;
if (opcode == STL_C_OP || opcode == STQ_C_OP) {
return firaddr.addr.off + sizeof(ULONG);
}
firaddr.addr.off += sizeof(ULONG);
}
DPRINT(1, ("GetNextOffset: did not find STx_C for LDx_L at %08I64x\n", updatedpc-4));
}
break;
}
return returnvalue;
}
#endif // 1
XOSD
SetupFunctionCall(
LPEXECUTE_OBJECT_DM lpeo,
LPEXECUTE_STRUCT lpes
)
{
/*
* Can only execute functions on the current stopped thread. Therefore
* assert that the current thread is stopped.
*/
assert(lpeo->hthd->tstate & ts_stopped);
if (!(lpeo->hthd->tstate & ts_stopped)) {
return xosdBadThread;
}
/*
* Now get the current stack offset.
*/
lpeo->addrStack.addr.off = lpeo->hthd->context.IntSp;
/*
* Now place the return address correctly
*/
lpeo->hthd->context.Fir = lpeo->hthd->context.IntRa =
lpeo->addrStart.addr.off;
/*
* Set the instruction pointer to the starting addresses
* and write the context back out
*/
lpeo->hthd->context.Fir = lpeo->addrStart.addr.off;
lpeo->hthd->fContextDirty = TRUE;
return xosdNone;
}
BOOL
CompareStacks(
LPEXECUTE_OBJECT_DM lpeo
)
/*++
Routine Description:
This routine is used to determine if the stack pointers are correct
for terminating function evaluation.
Arguments:
lpeo - Supplies the pointer to the DM Execute Object description
Return Value:
TRUE if the evaluation is to be terminated and FALSE otherwise
--*/
{
if (lpeo->addrStack.addr.off <= lpeo->hthd->context.IntSp) {
return TRUE;
}
return FALSE;
} /* CompareStacks() */
BOOL
ProcessFrameStackWalkNextCmd(
HPRCX hprc,
HTHDX hthd,
PCONTEXT context,
LPVOID pctxPtrs
)
{
return FALSE;
} // ProcessFrameStackWalkNextCmd
#if 0
#if DBG
ULONG RtlDebugFlags = 0;
#define RTL_DBG_VIRTUAL_UNWIND 1
#define RTL_DBG_VIRTUAL_UNWIND_DETAIL 2
// Define an array of symbolic names for the integer registers.
PCHAR RtlpIntegerRegisterNames[32] = {
"v0", "t0", "t1", "t2", "t3", "t4", "t5", "t6", // 0 - 7
"t7", "s0", "s1", "s2", "s3", "s4", "s5", "fp", // 8 - 15
"a0", "a1", "a2", "a3", "a4", "a5", "t8", "t9", // 16 - 23
"t10", "t11", "ra", "t12", "at", "gp", "sp", "zero", // 24 - 31
};
// This function disassembles the instruction at the given address. It is
// only used for debugging and recognizes only those few instructions that
// are relevant during reverse execution of the prologue by virtual unwind.
VOID
_RtlpDebugDisassemble (
IN ULONG64 ControlPc,
IN PCONTEXT ContextRecord
)
{
UCHAR Comments[50];
PULONGLONG FloatingRegister;
ULONG Function;
ULONG Hint;
ULONG Literal8;
ALPHA_INSTRUCTION Instruction;
PULONGLONG IntegerRegister;
LONG Offset16;
UCHAR Operands[20];
ULONG Opcode;
PCHAR OpName;
ULONG Ra;
ULONG Rb;
ULONG Rc;
PCHAR RaName;
PCHAR RbName;
PCHAR RcName;
if (RtlDebugFlags & RTL_DBG_VIRTUAL_UNWIND_DETAIL) {
Instruction.Long = *((PULONG)ControlPc);
Hint = Instruction.Jump.Hint;
Literal8 = Instruction.OpLit.Literal;
Offset16 = Instruction.Memory.MemDisp;
Opcode = Instruction.Memory.Opcode;
Ra = Instruction.OpReg.Ra;
RaName = RtlpIntegerRegisterNames[Ra];
Rb = Instruction.OpReg.Rb;
RbName = RtlpIntegerRegisterNames[Rb];
Rc = Instruction.OpReg.Rc;
RcName = RtlpIntegerRegisterNames[Rc];
IntegerRegister = &ContextRecord->IntV0;
FloatingRegister = &ContextRecord->FltF0;
OpName = NULL;
switch (Opcode) {
case JMP_OP :
if (Instruction.Jump.Function == RET_FUNC) {
OpName = "ret";
sprintf(Operands, "%s, (%s), %04lx", RaName, RbName, Hint);
sprintf(Comments, "%s = %I64x", RbName, IntegerRegister[Rb]);
}
break;
case LDAH_OP :
case LDA_OP :
case STQ_OP :
if (Opcode == LDA_OP) {
OpName = "lda";
} else if (Opcode == LDAH_OP) {
OpName = "ldah";
} else if (Opcode == STQ_OP) {
OpName = "stq";
}
sprintf(Operands, "%s, $%d(%s)", RaName, Offset16, RbName);
sprintf(Comments, "%s = %I64x", RaName, IntegerRegister[Ra]);
break;
case ARITH_OP :
case BIT_OP :
Function = Instruction.OpReg.Function;
if ((Opcode == ARITH_OP) && (Function == ADDQ_FUNC)) {
OpName = "addq";
} else if ((Opcode == ARITH_OP) && (Function == SUBQ_FUNC)) {
OpName = "subq";
} else if ((Opcode == BIT_OP) && (Function == BIS_FUNC)) {
OpName = "bis";
} else {
break;
}
if (Instruction.OpReg.RbvType == RBV_REGISTER_FORMAT) {
sprintf(Operands, "%s, %s, %s", RaName, RbName, RcName);
} else {
sprintf(Operands, "%s, $%d, %s", RaName, Literal8, RcName);
}
sprintf(Comments, "%s = %I64x", RcName, IntegerRegister[Rc]);
break;
case FPOP_OP :
if (Instruction.FpOp.Function == CPYS_FUNC) {
OpName = "cpys";
sprintf(Operands, "f%d, f%d, f%d", Ra, Rb, Rc);
sprintf(Comments, "f%d = %I64x", Rc, FloatingRegister[Rc]);
}
break;
case STT_OP :
OpName = "stt";
sprintf(Operands, "f%d, $%d(%s)", Ra, Offset16, RbName);
sprintf(Comments, "f%d = %I64x", Ra, FloatingRegister[Ra]);
break;
}
if (OpName == NULL) {
OpName = "???";
sprintf(Operands, "...");
sprintf(Comments, "Unknown to virtual unwind.");
}
DEBUG_PRINT_5(" %08I64x: %08lx %-5s %-16s // %s\n",
ControlPc, Instruction.Long, OpName, Operands, Comments);
}
return;
}
#define _RtlpFoundTrapFrame(NextPc) \
if (RtlDebugFlags & RTL_DBG_VIRTUAL_UNWIND) { \
DEBUG_PRINT_1(" *** Looks like a trap frame (fake prologue), Fir = %I64x\n", \
NextPc); \
}
#else
#define _RtlpDebugDisassemble(ControlPc, ContextRecord)
#define _RtlpFoundTrapFrame(NextPc)
#endif
// MBH - this value is redefined in windbg common code to be IntSp.
#define SP_REG 30
DWORD
BranchUnassemble(
void *Memory,
ADDR *Addr,
BOOL *IsBranch,
BOOL *TargetKnown,
BOOL *IsCall,
BOOL *IsTable,
ADDR *Target
)
{
ULONG OpCode;
ALPHA_INSTRUCTION *Instr;
UOFFSET TargetOffset;
UNREFERENCED_PARAMETER( Addr );
assert( Memory );
assert( IsBranch );
assert( TargetKnown );
assert( IsCall );
assert( Target );
*IsBranch = FALSE;
*IsTable = FALSE;
TargetOffset = 0;
Instr = (ALPHA_INSTRUCTION *)Memory;
OpCode = Instr->Jump.Opcode;
switch ( OpCode ) {
case JMP_OP:
switch ( Instr->Jump.Function ) {
case JMP_FUNC:
case RET_FUNC:
*IsBranch = TRUE;
*IsCall = FALSE;
*TargetKnown = FALSE;
break;
case JSR_FUNC:
case JSR_CO_FUNC:
*IsBranch = TRUE;
*IsCall = TRUE;
*TargetKnown = FALSE;
break;
}
break;
case CALLPAL_OP:
switch( Instr->Pal.Function ) {
case CALLSYS_FUNC:
case GENTRAP_FUNC:
*IsBranch = TRUE;
*IsCall = TRUE;
*TargetKnown = FALSE;
break;
}
break;
case BSR_OP:
*IsBranch = TRUE;
*IsCall = TRUE;
*TargetKnown = TRUE;
TargetOffset = (GetAddrOff(*Addr) + 4) + (Instr->Branch.BranchDisp << 2);
break;
case BR_OP:
case FBEQ_OP:
case FBLT_OP:
case FBLE_OP:
case FBNE_OP:
case FBGE_OP:
case FBGT_OP:
case BLBC_OP:
case BEQ_OP:
case BLT_OP:
case BLE_OP:
case BLBS_OP:
case BNE_OP:
case BGE_OP:
case BGT_OP:
*IsBranch = TRUE;
*IsCall = FALSE;
*TargetKnown = TRUE;
TargetOffset = (GetAddrOff(*Addr) + 4) + (Instr->Branch.BranchDisp << 2);
break;
default:
break;
}
AddrInit( Target, 0, 0, TargetOffset, TRUE, TRUE, FALSE, FALSE );
return sizeof( DWORD );
}
#endif // If 0
#ifndef KERNEL
UOFFSET
GetSPFromNLGDest(
HTHDX hthd,
LPNLG_DESTINATION pNlgDest
)
{
UOFFSET dwRet;
switch (pNlgDest->dwCode) {
case NLG_CATCH_ENTER: // Catch handler
case NLG_EXCEPT_ENTER: // Exception handler
case NLG_FILTER_ENTER: // Exception filter
case NLG_CATCH_LEAVE: // Return from Catch
case NLG_FINALLY_ENTER: // Termination handlers
case NLG_DESTRUCTOR_ENTER: // -GX handler
case NLG_LONGJMPEX: // Exception safe longjmp
dwRet = pNlgDest->uoffFramePointer;
break;
case NLG_LONGJMP:
dwRet = pNlgDest->uoffFramePointer+1;
break;
default:
dwRet = STACK_POINTER(hthd)+1; // emulate Virtual FP?
break;
}
return dwRet;
}
PVOID
InfoExceptionDuringStep(
HTHDX hthd
)
{
Unreferenced(hthd);
// Information that needs to be propagated from the step location
// to the action handler when an exception occurs is passed in the
// void * returned by this function. In case of Alpha no information
// needs to be passed currently.
return NULL;
}
DWORDLONG
GetFunctionResult(
PCALLSTRUCT pcs
)
{
return pcs->context.IntV0;
}
VOID
vCallFunctionHelper(
HTHDX hthd,
DWORD64 lpFunction,
int cArgs,
va_list vargs
)
{
int i;
assert(Is64PtrSE(lpFunction));
for (i = 0; i < cArgs; i++) {
(&hthd->context.IntA0)[i] = va_arg(vargs, DWORD);
}
hthd->context.IntRa = PC(hthd);
Set_PC(hthd, lpFunction);
hthd->fContextDirty = TRUE;
}
BOOL
GetWndProcMessage(
HTHDX hthd,
UINT* pmsg
)
/*++
Routine Description:
This function is used to get the current Windows message (WM_CREATE, etc)
when execution has been stopped at a wndproc.
Return Value:
False on failure; True otherwise.
--*/
{
*pmsg = (UINT)hthd->context.IntA1;
return TRUE;
}
#endif // !KERNEL
BOOL
DecodeSingleStepEvent(
HTHDX hthd,
DEBUG_EVENT64 *de,
PDWORD eventCode,
PDWORD_PTR subClass
)
/*++
Routine Description:
Arguments:
hthd - Supplies thread that has a single step exception pending
de - Supplies the DEBUG_EVENT64 structure for the exception
eventCode - Returns the remapped debug event id
subClass - Returns the remapped subClass id
Return Value:
TRUE if event was a real single step or was successfully mapped
to a breakpoint. FALSE if a register breakpoint occurred which was
not expected.
--*/
{
return FALSE;
}
ULONGLONG
GetRegValue(
PCONTEXT regs,
int cvindex
)
{
if (cvindex < CV_ALPHA_IntV0+32) {
return (&regs->IntV0)[cvindex - CV_ALPHA_IntV0];
}
switch (cvindex) {
case CV_ALPHA_Fir:
return regs->Fir;
case CV_ALPHA_Psr:
return regs->Psr;
default:
assert(!"GetRegValue called with unrecognized index");
return (ULONGLONG)0 - 1;
}
}