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.
680 lines
18 KiB
680 lines
18 KiB
/*++
|
|
* Main module of DBG DLL.
|
|
*
|
|
* Copyright (c) 1997, Microsoft Corporation
|
|
*
|
|
* This code runs in the debuggee's process.
|
|
*
|
|
--*/
|
|
#include <precomp.h>
|
|
#pragma hdrstop
|
|
|
|
|
|
BOOL InVdmDebugger = FALSE;
|
|
BOOL bWantsNtsdPrompt = FALSE;
|
|
|
|
ULONG ulTHHOOK = 0L; // Address registered from 16-bit world
|
|
LPVOID lpRemoteAddress = NULL; // Address registered from WOW32
|
|
DWORD lpRemoteBlock = 0; // Address registered from WOW32
|
|
BOOL f386;
|
|
DWORD VdmDbgEFLAGS;
|
|
|
|
WORD DbgWowhExeHead = 0;
|
|
WORD DbgWowhGlobalHeap = 0;
|
|
PDWORD lpVdmState = NULL;
|
|
PBYTE lpNtCpuInfo = NULL;
|
|
|
|
UCHAR DbgTimerMode = VDMTI_TIMER_TICK;
|
|
BOOL DbgTimerInitialized = FALSE;
|
|
VOID VdmDbgAttach(VOID);
|
|
|
|
|
|
BOOLEAN
|
|
DbgDllInitialize(
|
|
IN PVOID DllHandle,
|
|
IN ULONG Reason,
|
|
IN PCONTEXT Context OPTIONAL
|
|
)
|
|
/*
|
|
* DBGDllInitialize - DBG Initialiazation routine.
|
|
*
|
|
*/
|
|
|
|
{
|
|
HANDLE MyDebugPort;
|
|
DWORD st;
|
|
BOOL fAlreadyDebugged;
|
|
|
|
UNREFERENCED_PARAMETER(Context);
|
|
|
|
switch ( Reason ) {
|
|
|
|
case DLL_PROCESS_ATTACH:
|
|
|
|
#ifdef i386
|
|
// X86 Only, get pointer to Register Context Block
|
|
px86 = getIntelRegistersPointer();
|
|
#endif
|
|
|
|
st = NtQueryInformationProcess(
|
|
NtCurrentProcess(),
|
|
ProcessDebugPort,
|
|
(PVOID)&MyDebugPort,
|
|
sizeof(MyDebugPort),
|
|
NULL );
|
|
if ( NT_SUCCESS(st) ) {
|
|
fDebugged = (MyDebugPort != NULL);
|
|
} else {
|
|
fDebugged = FALSE;
|
|
}
|
|
|
|
if (fDebugged) {
|
|
DbgAttach();
|
|
}
|
|
break;
|
|
|
|
case DLL_THREAD_ATTACH:
|
|
//
|
|
// See if the debugger is attaching. If it is, then turn on the
|
|
// default of breaking into the debugger.
|
|
//
|
|
fAlreadyDebugged = fDebugged;
|
|
|
|
st = NtQueryInformationProcess(
|
|
NtCurrentProcess(),
|
|
ProcessDebugPort,
|
|
(PVOID)&MyDebugPort,
|
|
sizeof(MyDebugPort),
|
|
NULL );
|
|
if ( NT_SUCCESS(st) ) {
|
|
fDebugged = (MyDebugPort != NULL);
|
|
} else {
|
|
fDebugged = FALSE;
|
|
}
|
|
|
|
if (fDebugged && !fAlreadyDebugged) {
|
|
DbgAttach();
|
|
}
|
|
break;
|
|
|
|
case DLL_THREAD_DETACH:
|
|
break;
|
|
|
|
case DLL_PROCESS_DETACH:
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
return( TRUE );
|
|
}
|
|
|
|
void
|
|
DbgAttach(
|
|
void
|
|
)
|
|
/*
|
|
* DbgAttach
|
|
*
|
|
* Called to initiate the communication with a debugger
|
|
*
|
|
*/
|
|
{
|
|
//
|
|
// Send DBG_ATTACH notification
|
|
//
|
|
DbgGetContext();
|
|
SendVDMEvent(DBG_ATTACH);
|
|
|
|
//
|
|
// This call tells ntvdm to resync with us
|
|
//
|
|
VdmDbgAttach();
|
|
}
|
|
|
|
BOOL
|
|
WINAPI
|
|
xxxDbgInit(
|
|
PVOID pState,
|
|
ULONG InitialVdmDbgFlags,
|
|
PVOID pCpuInfo
|
|
)
|
|
/*
|
|
* DbgInit
|
|
*
|
|
* Called once ntvdm has completed it's initialization. Now it
|
|
* is possible to compute for example the IntelMemoryBase.
|
|
*
|
|
*/
|
|
{
|
|
|
|
VdmDbgTraceFlags = InitialVdmDbgFlags;
|
|
lpVdmState = pState;
|
|
IntelMemoryBase = (ULONG)VdmMapFlat(0, 0, VDM_V86);
|
|
lpNtCpuInfo = pCpuInfo;
|
|
|
|
//
|
|
// turn on default debugging bits in ntvdmstate
|
|
//
|
|
*lpVdmState |= VDM_BREAK_DEBUGGER;
|
|
#if DBG
|
|
*lpVdmState |= VDM_TRACE_HISTORY;
|
|
#endif
|
|
|
|
if (fDebugged) {
|
|
DbgGetContext();
|
|
SendVDMEvent(DBG_INIT);
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
BOOL
|
|
WINAPI
|
|
xxxDbgIsDebuggee(
|
|
void
|
|
)
|
|
/*
|
|
* DbgIsDebuggee
|
|
*
|
|
* Determines if we are being debugged
|
|
*
|
|
* Entry: void
|
|
*
|
|
* Exit: BOOL bRet - TRUE we are being debugged
|
|
*
|
|
*/
|
|
{
|
|
return fDebugged;
|
|
}
|
|
|
|
BOOL
|
|
SendVDMEvent(
|
|
WORD wEventType
|
|
)
|
|
|
|
/* SendVDMEvent
|
|
*
|
|
* Sends a VDM event notification to the debugger
|
|
*
|
|
* Entry:
|
|
*
|
|
* Exit: BOOL bRet
|
|
* Returns TRUE if exception was handled, FALSE otherwise
|
|
*
|
|
*/
|
|
{
|
|
BOOL fResult;
|
|
|
|
EventParams[0] = MAKELONG( wEventType, EventFlags );
|
|
EventParams[3] = (DWORD)&viInfo;
|
|
|
|
InVdmDebugger = TRUE;
|
|
|
|
do {
|
|
|
|
bWantsNtsdPrompt = FALSE;
|
|
|
|
|
|
// Slimyness to determine whether the exception was handled or not.
|
|
|
|
try {
|
|
RaiseException( STATUS_VDM_EVENT,
|
|
0,
|
|
4,
|
|
EventParams );
|
|
fResult = TRUE;
|
|
} except(EXCEPTION_EXECUTE_HANDLER) {
|
|
fResult = FALSE;
|
|
}
|
|
|
|
//
|
|
// bWantsNtsdPrompt may be changed by vdmexts
|
|
//
|
|
if (bWantsNtsdPrompt) {
|
|
DbgBreakPoint();
|
|
}
|
|
|
|
} while (bWantsNtsdPrompt);
|
|
|
|
|
|
InVdmDebugger = FALSE;
|
|
FlushVdmBreakPoints();
|
|
|
|
return( fResult );
|
|
}
|
|
|
|
|
|
VOID
|
|
DbgGetContext(
|
|
VOID
|
|
)
|
|
|
|
/* DbgGetContext() - Get the VDM's current context
|
|
*
|
|
* Most of the routines that send VDMEvents need to have a context record
|
|
* associated with them. This routine is a quick way to get most of the
|
|
* general registers. Redundant work is being done because AX for example
|
|
* is often on the stack and as such must really be pulled from the frame.
|
|
* Hopefully this is OK because it is fast.
|
|
*/
|
|
|
|
{
|
|
//
|
|
// Everything defaults to 0.
|
|
//
|
|
RtlFillMemory( &vcContext, sizeof(VDMCONTEXT), (UCHAR)0 );
|
|
RtlFillMemory( &viInfo, sizeof(VDMINTERNALINFO), (UCHAR)0 );
|
|
|
|
//
|
|
// Fill in the internal info structure
|
|
//
|
|
viInfo.dwLdtBase = (DWORD)ExpLdt;
|
|
viInfo.dwIntelBase = IntelMemoryBase;
|
|
viInfo.wKernelSeg = HIWORD(ulTHHOOK);
|
|
viInfo.dwOffsetTHHOOK = (DWORD)(LOWORD(ulTHHOOK));
|
|
viInfo.vdmContext = &vcContext;
|
|
viInfo.lpRemoteAddress = lpRemoteAddress;
|
|
viInfo.lpRemoteBlock = lpRemoteBlock;
|
|
viInfo.f386 = f386;
|
|
viInfo.lpNtvdmState = lpVdmState;
|
|
viInfo.lpVdmDbgFlags = &VdmDbgTraceFlags;
|
|
viInfo.lpNtCpuInfo = lpNtCpuInfo;
|
|
viInfo.lpVdmBreakPoints= &VdmBreakPoints;
|
|
|
|
EventFlags = 0;
|
|
if(NtCurrentTeb()->Vdm) {
|
|
//
|
|
// Fill in the context structure
|
|
//
|
|
vcContext.SegEs = (ULONG)getES();
|
|
vcContext.SegDs = (ULONG)getDS();
|
|
vcContext.SegCs = (ULONG)getCS();
|
|
vcContext.SegSs = (ULONG)getSS();
|
|
vcContext.SegFs = (ULONG)getFS();
|
|
vcContext.SegGs = (ULONG)getGS();
|
|
|
|
vcContext.EFlags = getEFLAGS();
|
|
VdmDbgEFLAGS = vcContext.EFlags; // save for vdmexts
|
|
|
|
vcContext.Edi = getEDI();
|
|
vcContext.Esi = getESI();
|
|
vcContext.Ebx = getEBX();
|
|
vcContext.Edx = getEDX();
|
|
vcContext.Ecx = getECX();
|
|
vcContext.Eax = getEAX();
|
|
|
|
vcContext.Ebp = getEBP();
|
|
vcContext.Eip = getEIP();
|
|
vcContext.Esp = getESP();
|
|
|
|
//
|
|
// Put in special flags in event field
|
|
//
|
|
|
|
if (!(getMSW() & MSW_PE) || (getEFLAGS() & V86FLAGS_V86)) {
|
|
EventFlags |= VDMEVENT_V86; // emulator is in real or v86 mode
|
|
} else {
|
|
EventFlags |= VDMEVENT_PE;
|
|
|
|
if ((getMSW() & MSW_PE) && !SEGMENT_IS_BIG(vcContext.SegCs)) {
|
|
EventFlags |= VDMEVENT_PM16; // emulator is in real or v86 mode
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void
|
|
WINAPI
|
|
xxxDbgDispatch()
|
|
{
|
|
UNALIGNED WORD *stack;
|
|
WORD mode;
|
|
WORD selector;
|
|
WORD segment;
|
|
WORD new_selector;
|
|
BOOL fBPRelease;
|
|
BOOL fData;
|
|
LPSTR lpModuleName;
|
|
LPSTR lpPathName;
|
|
UCHAR fPE;
|
|
PFFRAME16 pFFrame;
|
|
PTFRAME16 pTFrame;
|
|
PNDFRAME16 pNDFrame;
|
|
PSTFRAME16 pSTFrame;
|
|
WORD wFrame;
|
|
|
|
fPE = ISPESET;
|
|
|
|
stack = (UNALIGNED WORD *)Sim32GetVDMPointer(
|
|
((ULONG)getSS() << 16) + (ULONG)getSP(),
|
|
MAX_DBG_FRAME, fPE );
|
|
|
|
mode = *stack++;
|
|
|
|
//
|
|
// If there is no debugger, then only handle DBG_WOWINIT
|
|
//
|
|
if (!fDebugged && (mode != DBG_WOWINIT)) {
|
|
return;
|
|
}
|
|
|
|
switch( mode ) {
|
|
case DBG_SEGLOAD:
|
|
selector = *stack++;
|
|
segment = *stack++;
|
|
lpModuleName = (LPSTR)Sim32GetVDMPointer(
|
|
(ULONG)*stack + ((ULONG)(*(stack+1)) << 16),
|
|
0, fPE );
|
|
stack += 2;
|
|
lpPathName = (LPSTR)Sim32GetVDMPointer(
|
|
(ULONG)*stack + ((ULONG)(*(stack+1)) << 16),
|
|
0, fPE );
|
|
if ( lpPathName == NULL ) {
|
|
lpPathName = "";
|
|
}
|
|
|
|
stack += 2;
|
|
fData = (BOOL)(*stack++);
|
|
SegmentLoad( lpModuleName, lpPathName, selector, segment, fData );
|
|
break;
|
|
|
|
case DBG_SEGMOVE:
|
|
selector = *stack++;
|
|
new_selector = *stack++;
|
|
SegmentMove( selector, new_selector );
|
|
break;
|
|
|
|
case DBG_SEGFREE:
|
|
fBPRelease = (BOOL)*stack++;
|
|
selector = *stack++;
|
|
SegmentFree( selector, fBPRelease );
|
|
break;
|
|
|
|
case DBG_MODFREE:
|
|
lpModuleName = (LPSTR)Sim32GetVDMPointer(
|
|
(ULONG)*stack + ((ULONG)(*(stack+1)) << 16),
|
|
0, fPE );
|
|
stack += 2;
|
|
lpPathName = (LPSTR)Sim32GetVDMPointer(
|
|
(ULONG)*stack + ((ULONG)(*(stack+1)) << 16),
|
|
0, fPE );
|
|
if ( lpPathName == NULL ) {
|
|
lpPathName = "";
|
|
}
|
|
ModuleFree( lpModuleName, lpPathName );
|
|
break;
|
|
|
|
case DBG_GPFAULT2:
|
|
wFrame = getBP() - (WORD)(FIELD_OFFSET(FFRAME16,wBP));
|
|
|
|
pFFrame = (PFFRAME16)Sim32GetVDMPointer(
|
|
((ULONG)getSS() << 16) + (ULONG)wFrame,
|
|
MAX_DBG_FRAME, fPE );
|
|
|
|
|
|
fData = DbgGPFault2( pFFrame );
|
|
|
|
setAX((WORD)fData);
|
|
break;
|
|
|
|
case DBG_DIVOVERFLOW:
|
|
pTFrame = (PTFRAME16)Sim32GetVDMPointer(
|
|
(ULONG)((ULONG)getSS() << 16) + (ULONG)getSP(),
|
|
MAX_DBG_FRAME, fPE );
|
|
|
|
|
|
fData = DbgDivOverflow2( pTFrame );
|
|
|
|
setAX((WORD)fData);
|
|
break;
|
|
|
|
case DBG_DLLSTART:
|
|
pNDFrame = (PNDFRAME16)Sim32GetVDMPointer(
|
|
(ULONG)((ULONG)getSS() << 16) + (ULONG)getSP(),
|
|
MAX_DBG_FRAME, fPE );
|
|
|
|
|
|
fData = DbgDllStart( pNDFrame );
|
|
|
|
setAX((WORD)fData);
|
|
break;
|
|
|
|
case DBG_TASKSTOP:
|
|
pSTFrame = (PSTFRAME16)Sim32GetVDMPointer(
|
|
(ULONG)((ULONG)getSS() << 16) + (ULONG)getSP(),
|
|
MAX_DBG_FRAME, fPE );
|
|
|
|
fData = DbgTaskStop( pSTFrame );
|
|
break;
|
|
|
|
case DBG_ATTACH:
|
|
break;
|
|
|
|
case DBG_TOOLHELP:
|
|
ulTHHOOK = (ULONG)*stack + ((ULONG)(*(stack+1)) << 16);
|
|
stack += 2;
|
|
f386 = (BOOL)*stack;
|
|
break;
|
|
|
|
case DBG_WOWINIT:
|
|
//
|
|
// Pass in some data from KRNL386 so that VDMEXTS can get a
|
|
// hold of them.
|
|
//
|
|
DbgWowhExeHead = getDX();
|
|
DbgWowhGlobalHeap = getCX();
|
|
break;
|
|
|
|
default:
|
|
setAX(0); // Event not handled
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
VOID
|
|
xxxDbgNotifyRemoteThreadAddress(
|
|
LPVOID lpAddress,
|
|
DWORD lpBlock
|
|
) {
|
|
lpRemoteAddress = lpAddress;
|
|
lpRemoteBlock = lpBlock;
|
|
}
|
|
|
|
VOID
|
|
xxxDbgNotifyDebugged(
|
|
BOOL fNewDebugged
|
|
) {
|
|
fDebugged = fNewDebugged;
|
|
}
|
|
|
|
|
|
|
|
VOID
|
|
RestoreVDMContext(
|
|
VDMCONTEXT *vcContext
|
|
)
|
|
{
|
|
|
|
setEAX(vcContext->Eax);
|
|
setEBX(vcContext->Ebx);
|
|
setECX(vcContext->Ecx);
|
|
setEDX(vcContext->Edx);
|
|
setEIP(vcContext->Eip);
|
|
setESP(vcContext->Esp);
|
|
setEBP(vcContext->Ebp);
|
|
setEFLAGS(vcContext->EFlags);
|
|
|
|
}
|
|
|
|
ULONG
|
|
DbgEventTime(
|
|
PVDM_TRACEINFO pVdmTraceInfo
|
|
)
|
|
{
|
|
ULONG TickCount = 0;
|
|
USHORT id;
|
|
ULONG CurTick;
|
|
LARGE_INTEGER CurTime;
|
|
LARGE_INTEGER DiffTime;
|
|
#ifdef _X86_
|
|
ULONG CurHigh, CurLow;
|
|
#endif
|
|
|
|
if (!DbgTimerInitialized) {
|
|
|
|
#ifdef _X86_
|
|
ULONG CpuidHigh;
|
|
_asm pushad
|
|
_asm mov eax, 1
|
|
_asm _emit 0fh
|
|
_asm _emit 0a2h // CPUID
|
|
_asm mov CpuidHigh, edx
|
|
_asm popad
|
|
if (CpuidHigh & 0x10) {
|
|
// cpu has time stamp counter
|
|
DbgTimerMode = VDMTI_TIMER_PENTIUM;
|
|
}
|
|
#endif
|
|
|
|
|
|
switch(DbgTimerMode & VDMTI_TIMER_MODE) {
|
|
|
|
case VDMTI_TIMER_TICK:
|
|
pVdmTraceInfo->TimeStamp.LowPart = GetTickCount();
|
|
DbgTimerInitialized = TRUE;
|
|
break;
|
|
|
|
#ifdef _X86_
|
|
case VDMTI_TIMER_PENTIUM:
|
|
_asm push eax
|
|
_asm push edx
|
|
_asm _emit 0fh
|
|
_asm _emit 031h // RDTSC
|
|
_asm mov CurLow, eax
|
|
_asm mov CurHigh, edx
|
|
_asm pop edx
|
|
_asm pop eax
|
|
|
|
pVdmTraceInfo->TimeStamp.LowPart = CurLow;
|
|
pVdmTraceInfo->TimeStamp.HighPart = CurHigh;
|
|
DbgTimerInitialized = TRUE;
|
|
break;
|
|
#endif
|
|
}
|
|
}
|
|
|
|
|
|
if (DbgTimerInitialized) {
|
|
|
|
pVdmTraceInfo->Flags = (pVdmTraceInfo->Flags & ~VDMTI_TIMER_MODE) +
|
|
(DbgTimerMode & VDMTI_TIMER_MODE);
|
|
|
|
switch(pVdmTraceInfo->Flags & VDMTI_TIMER_MODE) {
|
|
|
|
case VDMTI_TIMER_TICK:
|
|
CurTick = GetTickCount();
|
|
|
|
if (CurTick > pVdmTraceInfo->TimeStamp.LowPart) {
|
|
TickCount = CurTick - pVdmTraceInfo->TimeStamp.LowPart;
|
|
pVdmTraceInfo->TimeStamp.LowPart = CurTick;
|
|
} else {
|
|
TickCount = 0;
|
|
}
|
|
|
|
break;
|
|
|
|
#ifdef _X86_
|
|
case VDMTI_TIMER_PENTIUM: {
|
|
_asm push eax
|
|
_asm push edx
|
|
_asm _emit 0fh
|
|
_asm _emit 031h // RDTSC
|
|
_asm mov CurLow, eax
|
|
_asm mov CurHigh, edx
|
|
_asm pop edx
|
|
_asm pop eax
|
|
CurTime.LowPart = CurLow;
|
|
CurTime.HighPart = CurHigh;
|
|
|
|
DiffTime.QuadPart = CurTime.QuadPart - pVdmTraceInfo->TimeStamp.QuadPart;
|
|
pVdmTraceInfo->TimeStamp.QuadPart = CurTime.QuadPart;
|
|
TickCount = DiffTime.LowPart;
|
|
|
|
//if (DiffTime.HighPart) {
|
|
// TickCount = 0xffffffff;
|
|
//}
|
|
break;
|
|
}
|
|
#endif
|
|
}
|
|
}
|
|
|
|
return (TickCount);
|
|
}
|
|
|
|
#define NUM_VDM_TRACE_PAGES 8
|
|
|
|
VOID
|
|
WINAPI
|
|
xxxDbgTraceEvent(
|
|
PVDM_TRACEINFO pVdmTraceInfo,
|
|
USHORT Type,
|
|
USHORT wData,
|
|
ULONG lData
|
|
)
|
|
{
|
|
PVDM_TRACEENTRY pEntry;
|
|
|
|
if (!(*(ULONG *)(IntelMemoryBase+FIXED_NTVDMSTATE_LINEAR) & VDM_TRACE_HISTORY)) {
|
|
return;
|
|
}
|
|
|
|
if (!pVdmTraceInfo->pTraceTable) {
|
|
pVdmTraceInfo->pTraceTable = (PVDM_TRACEENTRY) VirtualAlloc(NULL,
|
|
4096*NUM_VDM_TRACE_PAGES,
|
|
MEM_COMMIT,
|
|
PAGE_READWRITE);
|
|
|
|
if (!pVdmTraceInfo->pTraceTable) {
|
|
// couldn't allocate memory
|
|
return;
|
|
}
|
|
|
|
pVdmTraceInfo->CurrentEntry = 0;
|
|
pVdmTraceInfo->NumPages = NUM_VDM_TRACE_PAGES;
|
|
pVdmTraceInfo->Flags = 0;
|
|
}
|
|
|
|
pEntry = &pVdmTraceInfo->pTraceTable[pVdmTraceInfo->CurrentEntry];
|
|
|
|
pEntry->Type = Type;
|
|
pEntry->wData = wData;
|
|
pEntry->lData = lData;
|
|
pEntry->Time = DbgEventTime(pVdmTraceInfo);
|
|
|
|
pEntry->eax = getEAX();
|
|
pEntry->ebx = getEBX();
|
|
pEntry->ecx = getECX();
|
|
pEntry->edx = getEDX();
|
|
pEntry->esi = getESI();
|
|
pEntry->edi = getEDI();
|
|
pEntry->ebp = getEBP();
|
|
pEntry->esp = getESP();
|
|
pEntry->eip = getEIP();
|
|
pEntry->eflags = getEFLAGS();
|
|
|
|
pEntry->cs = getCS();
|
|
pEntry->ds = getDS();
|
|
pEntry->es = getES();
|
|
pEntry->fs = getFS();
|
|
pEntry->gs = getGS();
|
|
pEntry->ss = getSS();
|
|
|
|
if (++pVdmTraceInfo->CurrentEntry >=
|
|
(pVdmTraceInfo->NumPages*4096/sizeof(VDM_TRACEENTRY))) {
|
|
pVdmTraceInfo->CurrentEntry = 0;
|
|
}
|
|
|
|
}
|