windows-nt/Source/XPSP1/NT/sdktools/debuggers/ntsd64/vdm.cpp
2020-09-26 16:20:57 +08:00

715 lines
24 KiB
C++

//----------------------------------------------------------------------------
//
// VDM debugging support.
//
// Copyright (C) Microsoft Corporation, 1997-2001.
//
//----------------------------------------------------------------------------
#include "ntsdp.hpp"
BOOL fVDMInitDone;
BOOL fVDMActive;
VDMPROCESSEXCEPTIONPROC pfnVDMProcessException;
VDMGETTHREADSELECTORENTRYPROC pfnVDMGetThreadSelectorEntry;
VDMGETPOINTERPROC pfnVDMGetPointer;
VDMGETCONTEXTPROC pfnVDMGetContext;
VDMSETCONTEXTPROC pfnVDMSetContext;
VDMGETSELECTORMODULEPROC pfnVDMGetSelectorModule;
#define SEGTYPE_AVAILABLE 0
#define SEGTYPE_V86 1
#define SEGTYPE_PROT 2
#define MAXSEGENTRY 1024
SEGENTRY segtable[MAXSEGENTRY];
VOID VDMRegCmd(
LPSTR achInput,
X86_NT5_CONTEXT *pvc
)
{
DWORD dwVal;
if ( _stricmp(achInput,"rp") == 0 )
{
g_X86Machine.OutputAll(g_X86Machine.m_AllMask,
DEBUG_OUTPUT_NORMAL);
return;
}
if (achInput[1] != 'e') {
dprintf("VDM R: can only operate on 32-bit registers\n");
return;
}
if (strlen(achInput) < 6) {
dprintf("VDM R: Missing value\n");
return;
}
dwVal = strtoul(&achInput[5], NULL, 16);
if ( _strnicmp(&achInput[2],"ax", 2) == 0 ) {
pvc->Eax = dwVal;
} else if ( _strnicmp(&achInput[2],"bx", 2) == 0 ) {
pvc->Ebx = dwVal;
} else if ( _strnicmp(&achInput[2],"cx", 2) == 0 ) {
pvc->Ecx = dwVal;
} else if ( _strnicmp(&achInput[2],"dx", 2) == 0 ) {
pvc->Edx = dwVal;
} else if ( _strnicmp(&achInput[2],"si", 2) == 0 ) {
pvc->Esi = dwVal;
} else if ( _strnicmp(&achInput[2],"di", 2) == 0 ) {
pvc->Edi = dwVal;
} else if ( _strnicmp(&achInput[2],"ip", 2) == 0 ) {
pvc->Eip = dwVal;
} else if ( _strnicmp(&achInput[2],"sp", 2) == 0 ) {
pvc->Esp = dwVal;
} else if ( _strnicmp(&achInput[2],"bp", 2) == 0 ) {
pvc->Ebp = dwVal;
} else if ( _strnicmp(&achInput[2],"fl", 2) == 0 ) {
pvc->EFlags = dwVal;
} else {
dprintf("Invalid register\n");
return;
}
dprintf("%.8X will be flushed to '%3.3s'. Use 'rp' to display pending values\n",
dwVal,
&achInput[1]);
}
void
DebugEvent64To(LPDEBUG_EVENT64 Event64,
LPDEBUG_EVENT Event)
{
Event->dwDebugEventCode = Event64->dwDebugEventCode;
Event->dwProcessId = Event64->dwProcessId;
Event->dwThreadId = Event64->dwThreadId;
switch(Event64->dwDebugEventCode)
{
case EXCEPTION_DEBUG_EVENT:
ExceptionRecord64To(&Event64->u.Exception.ExceptionRecord,
&Event->u.Exception.ExceptionRecord);
Event->u.Exception.dwFirstChance =
Event64->u.Exception.dwFirstChance;
break;
case CREATE_THREAD_DEBUG_EVENT:
Event->u.CreateThread.hThread =
(PVOID)(ULONG_PTR)(Event64->u.CreateThread.hThread);
Event->u.CreateThread.lpThreadLocalBase =
(PVOID)(ULONG_PTR)(Event64->u.CreateThread.lpThreadLocalBase);
Event->u.CreateThread.lpStartAddress =
(LPTHREAD_START_ROUTINE)(ULONG_PTR)(Event64->u.CreateThread.lpStartAddress);
break;
case CREATE_PROCESS_DEBUG_EVENT:
Event->u.CreateProcessInfo.hFile =
(PVOID)(ULONG_PTR)(Event64->u.CreateProcessInfo.hFile);
Event->u.CreateProcessInfo.hProcess =
(PVOID)(ULONG_PTR)(Event64->u.CreateProcessInfo.hProcess);
Event->u.CreateProcessInfo.hThread =
(PVOID)(ULONG_PTR)(Event64->u.CreateProcessInfo.hThread);
Event->u.CreateProcessInfo.lpBaseOfImage =
(PVOID)(ULONG_PTR)(Event64->u.CreateProcessInfo.lpBaseOfImage);
Event->u.CreateProcessInfo.dwDebugInfoFileOffset =
Event64->u.CreateProcessInfo.dwDebugInfoFileOffset;
Event->u.CreateProcessInfo.nDebugInfoSize =
Event64->u.CreateProcessInfo.nDebugInfoSize;
Event->u.CreateProcessInfo.lpThreadLocalBase =
(PVOID)(ULONG_PTR)(Event64->u.CreateProcessInfo.lpThreadLocalBase);
Event->u.CreateProcessInfo.lpStartAddress =
(LPTHREAD_START_ROUTINE)(ULONG_PTR)(Event64->u.CreateProcessInfo.lpStartAddress);
Event->u.CreateProcessInfo.lpImageName =
(PVOID)(ULONG_PTR)(Event64->u.CreateProcessInfo.lpImageName);
Event->u.CreateProcessInfo.fUnicode =
Event64->u.CreateProcessInfo.fUnicode;
break;
case EXIT_THREAD_DEBUG_EVENT:
Event->u.ExitThread.dwExitCode =
Event64->u.ExitThread.dwExitCode;
break;
case EXIT_PROCESS_DEBUG_EVENT:
Event->u.ExitProcess.dwExitCode =
Event64->u.ExitProcess.dwExitCode;
break;
case LOAD_DLL_DEBUG_EVENT:
Event->u.LoadDll.hFile =
(PVOID)(ULONG_PTR)(Event64->u.LoadDll.hFile);
Event->u.LoadDll.lpBaseOfDll =
(PVOID)(ULONG_PTR)(Event64->u.LoadDll.lpBaseOfDll);
Event->u.LoadDll.dwDebugInfoFileOffset =
Event64->u.LoadDll.dwDebugInfoFileOffset;
Event->u.LoadDll.nDebugInfoSize =
Event64->u.LoadDll.nDebugInfoSize;
Event->u.LoadDll.lpImageName =
(PVOID)(ULONG_PTR)(Event64->u.LoadDll.lpImageName);
Event->u.LoadDll.fUnicode =
Event64->u.LoadDll.fUnicode;
break;
case UNLOAD_DLL_DEBUG_EVENT:
Event->u.UnloadDll.lpBaseOfDll =
(PVOID)(ULONG_PTR)(Event64->u.UnloadDll.lpBaseOfDll);
break;
case OUTPUT_DEBUG_STRING_EVENT:
Event->u.DebugString.lpDebugStringData =
(LPSTR)(ULONG_PTR)(Event64->u.DebugString.lpDebugStringData);
Event->u.DebugString.fUnicode =
Event64->u.DebugString.fUnicode;
Event->u.DebugString.nDebugStringLength =
Event64->u.DebugString.nDebugStringLength;
break;
case RIP_EVENT:
Event->u.RipInfo.dwError =
Event64->u.RipInfo.dwError;
Event->u.RipInfo.dwType =
Event64->u.RipInfo.dwType;
break;
}
}
ULONG
VDMEvent(DEBUG_EVENT64* pDebugEvent)
/*
returns - 0, if exception not handled
STATUS_VDM_EVENT, if exception handled
otherwise, the return value is the translated event status,
for example STATUS_BREAKPOINT
*/
{
LPSTR Str;
LPSTR pFileName;
BOOL b;
ULONG lpNumberOfBytesRead;
UINT_PTR address;
PULONG_PTR lpdw;
int segslot;
int mode;
BOOL fData;
WORD selector;
WORD segment;
WORD newselect;
BOOL fStop;
DWORD ImgLen;
ULONG ulRet;
BOOL fNeedSegTableEdit;
BOOL fNeedInteractive;
BOOL fVerbose;
CHAR achInput[_MAX_PATH];
PSTR pTemp;
BOOL fProcess;
SEGMENT_NOTE se;
IMAGE_NOTE im;
BOOL bProtectMode;
WORD EventFlags;
ulRet = VDMEVENT_HANDLED;
if (!fVDMInitDone) {
fVDMInitDone = TRUE;
HINSTANCE hmodVDM = NULL;
const char* c_szVDMFailed = NULL;
fVDMActive = (
(hmodVDM = LoadLibrary("VDMDBG.DLL")) &&
(pfnVDMProcessException = (VDMPROCESSEXCEPTIONPROC)
GetProcAddress(hmodVDM, c_szVDMFailed = "VDMProcessException")
) &&
(pfnVDMGetPointer = (VDMGETPOINTERPROC)
GetProcAddress(hmodVDM, c_szVDMFailed = "VDMGetPointer")
) &&
(pfnVDMGetThreadSelectorEntry = (VDMGETTHREADSELECTORENTRYPROC)
GetProcAddress(hmodVDM, c_szVDMFailed = "VDMGetThreadSelectorEntry")
) &&
(pfnVDMGetContext = (VDMGETCONTEXTPROC)
GetProcAddress(hmodVDM, c_szVDMFailed = "VDMGetContext")
) &&
(pfnVDMSetContext = (VDMSETCONTEXTPROC)
GetProcAddress( hmodVDM, c_szVDMFailed = "VDMSetContext")
) &&
(pfnVDMGetSelectorModule = (VDMGETSELECTORMODULEPROC)
GetProcAddress( hmodVDM, c_szVDMFailed = "VDMGetSelectorModule")
)
); // fVDMActive
if (!fVDMActive) { // display error on first time...
if (!hmodVDM) {
dprintf("LoadLibrary(VDMDBG.DLL) failed\n");
}
else if (c_szVDMFailed && *c_szVDMFailed) { // is valid printable string
dprintf("%s can not be found in VDMDBG.DLL\n", c_szVDMFailed);
}
else {
dprintf("Unknown failure while initializing VDMDBG.DLL\n");
} // iff
} // if
} // if
if (!fVDMActive) return VDMEVENT_NOT_HANDLED;
DEBUG_EVENT Event;
DebugEvent64To(pDebugEvent, &Event);
lpdw = &(Event.u.Exception.ExceptionRecord.ExceptionInformation[0]);
fProcess = (*pfnVDMProcessException)(&Event);
fNeedSegTableEdit = FALSE;
fNeedInteractive = FALSE;
fVerbose = FALSE;
mode = LOWORD(lpdw[0]);
EventFlags = HIWORD(lpdw[0]);
bProtectMode = (BOOL) (EventFlags & VDMEVENT_PE);
// Has the caller explicitly asked for interaction?
if (EventFlags & VDMEVENT_NEEDS_INTERACTIVE) {
fNeedInteractive = TRUE;
}
if (EventFlags & VDMEVENT_VERBOSE) {
fVerbose = TRUE;
}
switch( mode ) {
case DBG_SEGLOAD:
case DBG_SEGMOVE:
case DBG_SEGFREE:
case DBG_MODLOAD:
case DBG_MODFREE:
address = lpdw[2];
b = g_Target->ReadVirtual(EXTEND64(address),
&se, sizeof(se),
&lpNumberOfBytesRead ) == S_OK;
if ( !b || lpNumberOfBytesRead != sizeof(se) ) {
return( VDMEVENT_NOT_HANDLED );
}
break;
case DBG_DLLSTART:
case DBG_DLLSTOP:
case DBG_TASKSTART:
case DBG_TASKSTOP:
address = lpdw[2];
b = g_Target->ReadVirtual(EXTEND64(address),
&im, sizeof(im),
&lpNumberOfBytesRead ) == S_OK;
if ( !b || lpNumberOfBytesRead != sizeof(im) ) {
return( VDMEVENT_NOT_HANDLED );
}
break;
}
switch( mode ) {
default:
ulRet = VDMEVENT_NOT_HANDLED;
break;
case DBG_SEGLOAD:
fNeedSegTableEdit = TRUE;
selector = se.Selector1;
segment = se.Segment;
fData = (BOOL)se.Type;
segslot = 0;
while ( segslot < MAXSEGENTRY ) {
if ( segtable[segslot].type != SEGTYPE_AVAILABLE ) {
if ( _stricmp(segtable[segslot].path_name, se.FileName) == 0 ) {
break;
}
}
segslot++;
}
if ( segslot == MAXSEGENTRY ) {
if ( strlen(se.FileName) != 0 ) {
dprintf("Loading [%s]\n", se.FileName );
}
}
if (fVerbose) {
dprintf("VDM SegLoad: %s(%d) %s => %x\n",
se.FileName,
segment,
fData ? "Data" : "Code",
selector);
}
break;
case DBG_SEGMOVE:
fNeedSegTableEdit = TRUE;
segment = se.Segment;
selector = se.Selector1;
newselect = se.Selector2;
if ( newselect == 0 ) {
mode = DBG_SEGFREE;
} else if (segment > 1) {
//
// real mode module is getting split up into different
// segments, so create a new segtable entry
//
segslot = 0;
while ( segslot < MAXSEGENTRY ) {
if (( segtable[segslot].type != SEGTYPE_AVAILABLE ) &&
( segtable[segslot].selector == selector )) {
mode = DBG_MODLOAD;
segment--; //make it zero-based
selector = newselect;
pFileName = segtable[segslot].path_name;
// Don't have the image length here so
// just choose one.
ImgLen = segtable[segslot].ImgLen;
break;
}
segslot++;
}
}
if (fVerbose) {
dprintf("VDM SegMove: (%d) %x => %x\n",
segment, selector, newselect);
}
break;
case DBG_SEGFREE:
fNeedSegTableEdit = TRUE;
selector = se.Selector1;
if (fVerbose) {
dprintf("VDM SegFree: %x\n",selector);
}
break;
case DBG_MODFREE:
fNeedSegTableEdit = TRUE;
if ( strlen(se.FileName) != 0 ) {
dprintf("Freeing [%s]\n", se.FileName );
} else if (fVerbose) {
dprintf("VDM ModFree: unknown module\n");
}
break;
case DBG_MODLOAD:
fNeedSegTableEdit = TRUE;
selector = se.Selector1;
ImgLen = se.Length;
segment = 0;
pFileName = se.FileName;
segslot = 0;
while ( segslot < MAXSEGENTRY ) {
if ( segtable[segslot].type != SEGTYPE_AVAILABLE ) {
if ( _stricmp(segtable[segslot].path_name, se.FileName) == 0 ) {
break;
}
}
segslot++;
}
if ( segslot == MAXSEGENTRY ) {
if ( strlen(se.FileName) != 0 ) {
dprintf("Loading [%s]\n", se.FileName );
}
}
if (fVerbose) {
dprintf("VDM ModLoad: %s => %x, len=%x\n",
se.FileName,
selector,
ImgLen);
}
break;
case DBG_SINGLESTEP:
if (g_TargetMachineType == IMAGE_FILE_MACHINE_I386) {
fNeedInteractive = FALSE;
ulRet = STATUS_SINGLE_STEP;
} else {
fNeedInteractive = TRUE;
}
break;
case DBG_BREAK:
if (g_TargetMachineType == IMAGE_FILE_MACHINE_I386) {
fNeedInteractive = FALSE;
ulRet = STATUS_BREAKPOINT;
} else {
fNeedInteractive = TRUE;
}
break;
case DBG_GPFAULT:
dprintf(" GP Fault in VDM\n");
if (g_TargetMachineType == IMAGE_FILE_MACHINE_I386) {
fNeedInteractive = FALSE;
ulRet = STATUS_ACCESS_VIOLATION;
} else {
fNeedInteractive = TRUE;
}
break;
case DBG_GPFAULT2:
dprintf("GP Fault in VDM\n");
dprintf("!!! second chance !!!\n");
fNeedInteractive = TRUE;
break;
case DBG_INSTRFAULT:
dprintf("invalid opcode fault in VDM\n");
fNeedInteractive = TRUE;
break;
case DBG_DIVOVERFLOW:
dprintf("divide overflow in VDM\n");
fNeedInteractive = TRUE;
break;
case DBG_STACKFAULT:
dprintf("stack fault in VDM\n");
if (g_TargetMachineType == IMAGE_FILE_MACHINE_I386) {
fNeedInteractive = FALSE;
ulRet = STATUS_ACCESS_VIOLATION;
} else {
fNeedInteractive = TRUE;
}
break;
case DBG_TASKSTART:
if ( fNeedInteractive || fVerbose ) {
dprintf("VDM Start Task <%s:%s>\n",
im.Module,
im.FileName );
}
break;
case DBG_DLLSTART:
if ( fNeedInteractive || fVerbose ) {
dprintf("VDM Start Dll <%s:%s>\n", im.Module, im.FileName );
}
break;
case DBG_TASKSTOP:
fNeedInteractive = FALSE;
break;
case DBG_DLLSTOP:
fNeedInteractive = FALSE;
break;
}
/*
** Temporary code to emulate a 16-bit debugger. Eventually I will make
** NTSD understand these events and call ProcessStateChange to allow
** real 16-bit debugging and other activities on the other 32-bit threads.
** -BobDay
*/
if ( fNeedInteractive ) {
char text[MAX_DISASM_LEN];
char path[128];
UINT cSeg;
ADDR addr;
X86_NT5_CONTEXT vc;
g_X86Machine.m_Context.X86Nt5Context.ContextFlags = VDMCONTEXT_FULL;
(*pfnVDMGetContext)(g_EventProcess->Handle, OS_HANDLE(g_EventThread->Handle),
(LPVDMCONTEXT)&g_X86Machine.m_Context);
g_X86Machine.m_Context.X86Nt5Context.EFlags &= ~V86FLAGS_TRACE;
vc = g_X86Machine.m_Context.X86Nt5Context;
// Dump a simulated context
g_X86Machine.OutputAll(g_X86Machine.m_AllMask,
DEBUG_OUTPUT_PROMPT_REGISTERS);
b = (*pfnVDMGetSelectorModule)(g_EventProcess->Handle, OS_HANDLE(g_EventThread->Handle),
(WORD)g_X86Machine.m_Context.X86Nt5Context.SegCs, &cSeg, text, 128, path, 128 );
if ( b ) {
dprintf("%s:%d!%04x:\n", text, cSeg, (WORD)g_X86Machine.m_Context.X86Nt5Context.Eip );
}
addr.seg = (WORD)g_X86Machine.m_Context.X86Nt5Context.SegCs;
addr.off = g_X86Machine.m_Context.X86Nt5Context.Eip;
if ( !bProtectMode ) {
addr.type = ADDR_V86 | FLAT_COMPUTED;
addr.flat = (*pfnVDMGetPointer)(
g_EventProcess->Handle,
OS_HANDLE(g_EventThread->Handle),
addr.seg,
(ULONG)addr.off,
FALSE
);
} else {
addr.type = ADDR_16 | FLAT_COMPUTED;
addr.flat = (*pfnVDMGetPointer)(
g_EventProcess->Handle,
OS_HANDLE(g_EventThread->Handle),
addr.seg,
(ULONG)addr.off,
TRUE
);
}
if ( Flat(addr) == 0 ) {
dprintf("Unable to disassemble failing code\n");
} else {
g_X86Machine.Disassemble( &addr, text, TRUE );
dprintf("%s", text );
}
AddExtensionDll("vdmexts", TRUE, NULL);
while ( TRUE ) {
GetInput("VDM>", achInput, sizeof(achInput));
if ( _stricmp(achInput,"gh") == 0 || _stricmp(achInput,"g") == 0 ) {
ulRet = VDMEVENT_HANDLED;
break;
}
if ( _stricmp(achInput,"gn") == 0 ) {
ulRet = VDMEVENT_NOT_HANDLED;
break;
}
if ( _stricmp(achInput, "t") == 0 ) {
ulRet = VDMEVENT_HANDLED;
vc.EFlags |= V86FLAGS_TRACE;
break;
}
if ((achInput[0] == 'r') && (_stricmp(achInput, "r") != 0)) {
VDMRegCmd(achInput, &vc);
g_X86Machine.m_Context.X86Nt5Context = vc;
continue;
}
if (achInput[0] == '!') {
fnBangCmd(NULL, &achInput[1], &pTemp, FALSE);
continue;
}
if (achInput[0] == '.') {
g_CurCmd = &achInput[1];
g_CommandStart = g_CurCmd;
ProcessCommandsAndCatch(NULL);
continue;
}
if ( _stricmp(achInput,"?") == 0 ) {
dprintf("\n---------- NTVDM Monitor Help ------------\n\n");
dprintf("g - Go\n");
dprintf("gh - Go : Exception handled\n");
dprintf("gn - Go : Exception not handled\n");
dprintf("help - Display extension help\n");
dprintf("r<reg> <val> - reg=[eax|ebx|ecx|edx|eip|esp|ebp|efl]\n");
dprintf("rp - display pending register set\n");
dprintf("t - Trace 1 instruction\n");
dprintf("!<cmd> - Execute extension command\n");
dprintf(".<cmd> - Execute native NTSD command\n");
dprintf("\nAnything else is interpreted as a VDMEXTS extension command\n\n");
continue;
}
fnBangCmd(NULL, achInput, &pTemp, FALSE);
}
g_X86Machine.m_Context.X86Nt5Context = vc;
(*pfnVDMSetContext)(g_EventProcess->Handle, OS_HANDLE(g_EventThread->Handle),
(LPVDMCONTEXT)&g_X86Machine.m_Context);
}
/*
** End of temporary code
*/
if ( fNeedSegTableEdit ) {
segslot = 0;
fStop = FALSE;
while ( segslot < MAXSEGENTRY ) {
switch( mode ) {
case DBG_SEGLOAD:
if ( segtable[segslot].type == SEGTYPE_AVAILABLE ) {
segtable[segslot].segment = segment;
segtable[segslot].selector = selector;
// This notification message is used only by wow in prot
// It could be determined from the current mode to be
// correct
segtable[segslot].type = SEGTYPE_PROT;
Str = (PSTR)calloc(1,strlen(se.FileName)+1);
if ( !Str ) {
return( VDMEVENT_NOT_HANDLED );
}
strcpy( Str, se.FileName );
segtable[segslot].path_name = Str;
segtable[segslot].ImgLen = 0;
fStop = TRUE;
}
break;
case DBG_SEGMOVE:
if (( segtable[segslot].type != SEGTYPE_AVAILABLE ) &&
( segtable[segslot].selector == selector )) {
segtable[segslot].selector = newselect;
fStop = TRUE;
}
break;
case DBG_SEGFREE:
if ( segtable[segslot].selector == selector ) {
fStop = TRUE;
segtable[segslot].type = SEGTYPE_AVAILABLE;
free(segtable[segslot].path_name);
segtable[segslot].path_name = NULL;
}
break;
case DBG_MODFREE:
if ( segtable[segslot].type != SEGTYPE_AVAILABLE ) {
if ( _stricmp(segtable[segslot].path_name,se.FileName) == 0 ) {
segtable[segslot].type = SEGTYPE_AVAILABLE;
free(segtable[segslot].path_name);
segtable[segslot].path_name = NULL;
}
}
break;
case DBG_MODLOAD:
if ( segtable[segslot].type == SEGTYPE_AVAILABLE ) {
segtable[segslot].segment = segment;
segtable[segslot].selector = selector;
// This notification message is used only by v86 dos
// It could be determined from the current mode to be
// correct
segtable[segslot].type = SEGTYPE_V86;
Str = (PSTR)calloc(1,strlen(pFileName)+1);
if ( !Str ) {
return( VDMEVENT_NOT_HANDLED );
}
strcpy( Str, pFileName );
segtable[segslot].path_name = Str;
segtable[segslot].ImgLen = ImgLen;
fStop = TRUE;
}
break;
}
if ( fStop ) {
break;
}
segslot++;
}
if ( segslot == MAXSEGENTRY ) {
if ( mode == DBG_SEGLOAD ) {
dprintf("Warning - adding selector %04X for segment %d, segtable full\n",
selector, segment );
}
}
}
pDebugEvent->u.Exception.ExceptionRecord.ExceptionCode = ulRet;
return( ulRet );
}