diff options
author | Adam <you@example.com> | 2020-05-17 05:51:50 +0200 |
---|---|---|
committer | Adam <you@example.com> | 2020-05-17 05:51:50 +0200 |
commit | e611b132f9b8abe35b362e5870b74bce94a1e58e (patch) | |
tree | a5781d2ec0e085eeca33cf350cf878f2efea6fe5 /private/windbg/newdm/user | |
download | NT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.tar NT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.tar.gz NT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.tar.bz2 NT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.tar.lz NT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.tar.xz NT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.tar.zst NT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.zip |
Diffstat (limited to 'private/windbg/newdm/user')
-rw-r--r-- | private/windbg/newdm/user/dm.def | 7 | ||||
-rw-r--r-- | private/windbg/newdm/user/dm.rc | 14 | ||||
-rw-r--r-- | private/windbg/newdm/user/makefile | 6 | ||||
-rw-r--r-- | private/windbg/newdm/user/makefile.inc | 23 | ||||
-rw-r--r-- | private/windbg/newdm/user/precomp.h | 81 | ||||
-rw-r--r-- | private/windbg/newdm/user/process.c | 335 | ||||
-rw-r--r-- | private/windbg/newdm/user/sources | 89 | ||||
-rw-r--r-- | private/windbg/newdm/user/userapi.c | 2041 | ||||
-rw-r--r-- | private/windbg/newdm/user/wow.c | 1175 |
9 files changed, 3771 insertions, 0 deletions
diff --git a/private/windbg/newdm/user/dm.def b/private/windbg/newdm/user/dm.def new file mode 100644 index 000000000..a46fad329 --- /dev/null +++ b/private/windbg/newdm/user/dm.def @@ -0,0 +1,7 @@ +LIBRARY dm + +EXPORTS + DmDllInit PRIVATE + DMInit PRIVATE + DMFunc PRIVATE + DBGVersionCheck PRIVATE diff --git a/private/windbg/newdm/user/dm.rc b/private/windbg/newdm/user/dm.rc new file mode 100644 index 000000000..8e2f96f6a --- /dev/null +++ b/private/windbg/newdm/user/dm.rc @@ -0,0 +1,14 @@ +// +// Adapted from OAK\BIN\VERRC.TPL +// +#include <windows.h> +#include <ntverp.h> + +#define VER_FILETYPE VFT_DLL +#define VER_FILESUBTYPE VFT2_UNKNOWN +#define VER_FILEDESCRIPTION_STR "Debuggee Module for WinDbg" +#define VER_INTERNALNAME_STR "dm.dll\0" +#define VER_ORIGINALFILENAME_STR "dm.dll\0" + +#include <common.ver> +
\ No newline at end of file diff --git a/private/windbg/newdm/user/makefile b/private/windbg/newdm/user/makefile new file mode 100644 index 000000000..6ee4f43fa --- /dev/null +++ b/private/windbg/newdm/user/makefile @@ -0,0 +1,6 @@ +# +# DO NOT EDIT THIS FILE!!! Edit .\sources. if you want to add a new source +# file to this component. This file merely indirects to the real make file +# that is shared by all the components of NT OS/2 +# +!INCLUDE $(NTMAKEENV)\makefile.def diff --git a/private/windbg/newdm/user/makefile.inc b/private/windbg/newdm/user/makefile.inc new file mode 100644 index 000000000..e2cfd4e35 --- /dev/null +++ b/private/windbg/newdm/user/makefile.inc @@ -0,0 +1,23 @@ +!IF 0 + +Copyright (c) 1989 Microsoft Corporation + +Module Name: + + makefile.inc + +Abstract: + + This file tells nmake the correct dependency relationships + for objects created from c files in this directory which are + wrappers for c files in another directory. + + +Author: + + Kent Forschmiedt + + +!ENDIF + +obj\i386\d3dm.obj: ..\..\em\p_i386\d3.c diff --git a/private/windbg/newdm/user/precomp.h b/private/windbg/newdm/user/precomp.h new file mode 100644 index 000000000..7fc930840 --- /dev/null +++ b/private/windbg/newdm/user/precomp.h @@ -0,0 +1,81 @@ +/*-- + +Copyright (c) 1992 Microsoft Corporation + +Module Name: + + precomp.h + +Abstract: + + Header file that is pre-compiled into a .pch file + +Author: + + Wesley Witt (wesw) 21-Sep-1993 + +Environment: + + Win32, User Mode + +--*/ + +#include <nt.h> +#include <ntrtl.h> +#include <nturtl.h> +#include <ntdbg.h> +#include <windows.h> +#include <stdlib.h> +#include <stdlib.h> +#include <stdio.h> +#include <memory.h> +#include <process.h> +#include <string.h> +#include <malloc.h> + +#if defined(TARGET_i386) && !defined(WIN32S) +#include <vdmdbg.h> +#endif + +#if defined(TARGET_ALPHA) +#include <alphaops.h> +#include "ctxptrs.h" +#endif + +#if defined(TARGET_MIPS) +#include <mipsinst.h> +#endif + +#if defined(TARGET_PPC) +#include <ppcinst.h> +#endif + +#ifdef OSDEBUG4 + +#include "od.h" +#include "odp.h" +#include "emdm.h" +#include "win32dm.h" + +#else // OSDEBUG4 + +#include "defs.h" +#include "types.h" +#include "cvtypes.hxx" +#include "od.h" +#include "emdm.h" +#include "ll.h" +#include "tl.h" +#include "shapi.hxx" + +#endif // OSDEBUG4 + +#include "heap.h" + +#include "dm.h" +#include "list.h" +#include "bp.h" +#include "funccall.h" +#include "debug.h" +#include "dbgver.h" + diff --git a/private/windbg/newdm/user/process.c b/private/windbg/newdm/user/process.c new file mode 100644 index 000000000..9c010eee9 --- /dev/null +++ b/private/windbg/newdm/user/process.c @@ -0,0 +1,335 @@ +/*++ + +Copyright (c) 1993 Microsoft Corporation + +Module Name: + + process.c + +Abstract: + + This code provides access to the task list. + +Author: + + Wesley Witt (wesw) 16-June-1993 + +Environment: + + User Mode + +--*/ + +#include "precomp.h" +#pragma hdrstop + + +#ifndef WIN32S + +#include <winperf.h> + + + +// +// defines +// +#define INITIAL_SIZE 51200 +#define EXTEND_SIZE 25600 +#define REGKEY_PERF "software\\microsoft\\windows nt\\currentversion\\perflib" +#define REGSUBKEY_COUNTERS "Counters" +#define PROCESS_COUNTER "process" +#define PROCESSID_COUNTER "id process" +#define UNKNOWN_TASK "unknown" + + + +VOID +GetTaskList( + PTASK_LIST pTask, + DWORD dwNumTasks + ) + +/*++ + +Routine Description: + + Provides an API for getting a list of tasks running at the time of the + API call. This function uses the registry performance data to get the + task list and is therefor straight WIN32 calls that anyone can call. + +Arguments: + + ldwNumTasks - pointer to a dword that will be set to the + number of tasks returned. + +Return Value: + + PTASK_LIST - pointer to an array of TASK_LIST records. + +--*/ + +{ + DWORD rc; + HKEY hKeyNames = NULL; + DWORD dwType; + DWORD dwSize; + LPBYTE buf = NULL; + CHAR szSubKey[1024]; + LANGID lid; + LPSTR p; + LPSTR p2; + PPERF_DATA_BLOCK pPerf; + PPERF_OBJECT_TYPE pObj; + PPERF_INSTANCE_DEFINITION pInst; + PPERF_COUNTER_BLOCK pCounter; + PPERF_COUNTER_DEFINITION pCounterDef; + DWORD i; + DWORD dwProcessIdTitle; + DWORD dwProcessIdCounter; + CHAR szProcessName[MAX_PATH]; + DWORD dwLimit = dwNumTasks - 1; + DWORD ThisPid = GetCurrentProcessId(); + + + + // + // Look for the list of counters. Always use the neutral + // English version, regardless of the local language. We + // are looking for some particular keys, and we are always + // going to do our looking in English. We are not going + // to show the user the counter names, so there is no need + // to go find the corresponding name in the local language. + // + lid = MAKELANGID( LANG_ENGLISH, SUBLANG_NEUTRAL ); + sprintf( szSubKey, "%s\\%03x", REGKEY_PERF, lid ); + rc = RegOpenKeyEx( HKEY_LOCAL_MACHINE, + szSubKey, + 0, + KEY_READ, + &hKeyNames + ); + if (rc != ERROR_SUCCESS) { + goto exit; + } + + // + // get the buffer size for the counter names + // + rc = RegQueryValueEx( hKeyNames, + REGSUBKEY_COUNTERS, + NULL, + &dwType, + NULL, + &dwSize + ); + + if (rc != ERROR_SUCCESS) { + goto exit; + } + + // + // allocate the counter names buffer + // + buf = (LPBYTE) malloc( dwSize ); + if (buf == NULL) { + goto exit; + } + memset( buf, 0, dwSize ); + + // + // read the counter names from the registry + // + rc = RegQueryValueEx( hKeyNames, + REGSUBKEY_COUNTERS, + NULL, + &dwType, + buf, + &dwSize + ); + + if (rc != ERROR_SUCCESS) { + goto exit; + } + + // + // now loop thru the counter names looking for the following counters: + // + // 1. "Process" process name + // 2. "ID Process" process id + // + // the buffer contains multiple null terminated strings and then + // finally null terminated at the end. the strings are in pairs of + // counter number and counter name. + // + + p = buf; + while (*p) { + if (_stricmp(p, PROCESS_COUNTER) == 0) { + // + // look backwards for the counter number + // + for( p2=p-2; isdigit(*p2); p2--) ; + strcpy( szSubKey, p2+1 ); + } + else + if (_stricmp(p, PROCESSID_COUNTER) == 0) { + // + // look backwards for the counter number + // + for( p2=p-2; isdigit(*p2); p2--) ; + dwProcessIdTitle = atol( p2+1 ); + } + // + // next string + // + p += (strlen(p) + 1); + } + + // + // free the counter names buffer + // + free( buf ); + + + // + // allocate the initial buffer for the performance data + // + dwSize = INITIAL_SIZE; + buf = malloc( dwSize ); + if (buf == NULL) { + goto exit; + } + memset( buf, 0, dwSize ); + + + while (TRUE) { + + rc = RegQueryValueEx( HKEY_PERFORMANCE_DATA, + szSubKey, + NULL, + &dwType, + buf, + &dwSize + ); + + pPerf = (PPERF_DATA_BLOCK) buf; + + // + // check for success and valid perf data block signature + // + if ((rc == ERROR_SUCCESS) && + (dwSize > 0) && + (pPerf)->Signature[0] == (WCHAR)'P' && + (pPerf)->Signature[1] == (WCHAR)'E' && + (pPerf)->Signature[2] == (WCHAR)'R' && + (pPerf)->Signature[3] == (WCHAR)'F' ) { + break; + } + + // + // if buffer is not big enough, reallocate and try again + // + if (rc == ERROR_MORE_DATA) { + dwSize += EXTEND_SIZE; + buf = realloc( buf, dwSize ); + memset( buf, 0, dwSize ); + } + else { + goto exit; + } + } + + // + // set the perf_object_type pointer + // + pObj = (PPERF_OBJECT_TYPE) ((DWORD)pPerf + pPerf->HeaderLength); + + // + // loop thru the performance counter definition records looking + // for the process id counter and then save its offset + // + pCounterDef = (PPERF_COUNTER_DEFINITION) ((DWORD)pObj + pObj->HeaderLength); + for (i=0; i<(DWORD)pObj->NumCounters; i++) { + if (pCounterDef->CounterNameTitleIndex == dwProcessIdTitle) { + dwProcessIdCounter = pCounterDef->CounterOffset; + break; + } + pCounterDef++; + } + + dwNumTasks = min( dwLimit, (DWORD)pObj->NumInstances ); + + pInst = (PPERF_INSTANCE_DEFINITION) ((DWORD)pObj + pObj->DefinitionLength); + + // + // loop thru the performance instance data extracting each process name + // and process id + // + for (i=0; i<dwNumTasks; i++) { + // + // pointer to the process name + // + p = (LPSTR) ((DWORD)pInst + pInst->NameOffset); + + // + // convert it to ascii + // + rc = WideCharToMultiByte( CP_ACP, + 0, + (LPCWSTR)p, + -1, + szProcessName, + sizeof(szProcessName), + NULL, + NULL + ); + + if (!rc) { + // + // if we cant convert the string then use a bogus value + // + strcpy( pTask->ProcessName, UNKNOWN_TASK ); + } + + if (strlen(szProcessName)+4 <= sizeof(pTask->ProcessName)) { + strcpy( pTask->ProcessName, szProcessName ); + strcat( pTask->ProcessName, ".exe" ); + } + + // + // get the process id + // + pCounter = (PPERF_COUNTER_BLOCK) ((DWORD)pInst + pInst->ByteLength); + pTask->dwProcessId = *((LPDWORD) ((DWORD)pCounter + dwProcessIdCounter)); + if (pTask->dwProcessId == 0) { + pTask->dwProcessId = (DWORD)-2; + } + if (_stricmp(szProcessName,"csrss")==0) { + pTask->dwProcessId = (DWORD)-1; + } + + // + // next process + // + if (pTask->dwProcessId == ThisPid) { + ZeroMemory( pTask, sizeof(*pTask) ); + } else { + pTask++; + } + pInst = (PPERF_INSTANCE_DEFINITION) ((DWORD)pCounter + pCounter->ByteLength); + } + +exit: + if (buf) { + free( buf ); + } + + if (hKeyNames) { + RegCloseKey( hKeyNames ); + RegCloseKey( HKEY_PERFORMANCE_DATA ); + } + + return; +} + +#endif diff --git a/private/windbg/newdm/user/sources b/private/windbg/newdm/user/sources new file mode 100644 index 000000000..d3634544f --- /dev/null +++ b/private/windbg/newdm/user/sources @@ -0,0 +1,89 @@ +!IF 0 + +Copyright (c) 1989 Microsoft Corporation + +Module Name: + + sources. + +Abstract: + + This file specifies the target component being built and the list of + sources files needed to build that component. Also specifies optional + compiler switches and libraries that are unique for the component being + built. + + +Author: + + Jim Schaad (jimsch) + +!ENDIF + +MAJORCOMP=windbg +MINORCOMP=dm + +TARGETNAME=dm +TARGETPATH=obj +TARGETTYPE=DYNLINK + +DLLENTRY=DllInit + +SOURCES= \ + ..\bp.c \ + ..\dmx32.c \ + ..\event.c \ + ..\funccall.c \ + ..\procem.c \ + ..\step.c \ + ..\util.c \ + ..\walk.c \ + process.c \ + userapi.c \ + wow.c \ + dm.rc + +i386_SOURCES= \ + ..\i386mach.c \ + ..\i386d3dm.c + +mips_SOURCES= \ + ..\mipsmach.c + +ppc_SOURCES= \ + ..\ppcmach.c + +alpha_SOURCES= \ + ..\alpmach.c + +UMTYPE=windows + +!include ..\..\common.src + +MIPS_FLAGS=-DNO_TRACE_BIT -DTARGET_MIPS +386_FLAGS=-DWOW -DTARGET_i386 +ALPHA_FLAGS=-DNO_TRACE_BIT -DTARGET_ALPHA +PPC_FLAGS=-DNO_TRACE_BIT -DTARGET_PPC + +C_DEFINES = $(C_DEFINES) -DDWORDLONG=ULONGLONG + +INCLUDES=..;..\..\osdebug\include;..\..\osdebug + +CONDITIONAL_INCLUDES=odp.h win32dm.h + +TARGETLIBS= \ + $(BASEDIR)\public\sdk\lib\*\kernel32.lib \ + $(BASEDIR)\public\sdk\lib\*\user32.lib \ + $(BASEDIR)\public\sdk\lib\*\vdmdbg.lib \ + $(BASEDIR)\public\sdk\lib\*\advapi32.lib \ + $(BASEDIR)\public\sdk\lib\*\crashlib.lib \ +!IF $(ALPHA) + ..\..\em\p_alpha\obj\*\emalp.lib \ +!ENDIF + $(MORE_LIBS) + +NTTARGETFILES= + +PRECOMPILED_INCLUDE=precomp.h +PRECOMPILED_PCH=precomp.pch +PRECOMPILED_OBJ=precomp.obj diff --git a/private/windbg/newdm/user/userapi.c b/private/windbg/newdm/user/userapi.c new file mode 100644 index 000000000..da84896e6 --- /dev/null +++ b/private/windbg/newdm/user/userapi.c @@ -0,0 +1,2041 @@ +#include "precomp.h" +#pragma hdrstop + +#ifdef WIN32S +extern BOOL fProcessingDebugEvent; +extern BOOL fWaitForDebugEvent; +extern DWORD tidExit; +extern BOOL fExitProcessDebugEvent; // set true when the last debug event +#endif + +HANDLE hEventContinue; +extern LPDM_MSG LpDmMsg; +extern SYSTEM_INFO SystemInfo; +extern WT_STRUCT WtStruct; // .. for wt +extern DEBUG_ACTIVE_STRUCT DebugActiveStruct; // ... for DebugActiveProcess() + +extern PKILLSTRUCT KillQueue; +extern CRITICAL_SECTION csKillQueue; + +extern HTHDX thdList; +extern HPRCX prcList; +extern CRITICAL_SECTION csThreadProcList; + +extern BOOL fSmartRangeStep; +extern HANDLE hEventNoDebuggee; +extern HANDLE hEventRemoteQuit; +extern BOOL fDisconnected; +extern BOOL fUseRoot; +extern char nameBuffer[]; + +#if defined(TARGET_MIPS) +MIPSCONTEXTSIZE MipsContextSize; +#endif + + + +BOOL +DbgWriteMemory( + HPRCX hprc, + LPVOID lpOffset, + LPBYTE lpb, + DWORD cb, + LPDWORD pcbWritten + ) +/*++ + +Routine Description: + + Write to a flat address in a process. + +Arguments: + + hprc - Supplies the handle to the process + + lpOffset - Supplies address of data in process + + lpb - Supplies a pointer to the bytes to be written + + cb - Supplies the count of bytes to be written + + pcbWritten - Returns the number of bytes actually written + +Return Value: + + TRUE if successful and FALSE otherwise + +--*/ + +{ + BOOL fRet; + + assert(hprc->rwHand != (HANDLE)-1); + + if (hprc->rwHand == (HANDLE)-1) { + return FALSE; + } + + fRet = WriteProcessMemory(hprc->rwHand, lpOffset, lpb, cb, pcbWritten); + +#if defined(JLS_RWBP) && DBG + { + DWORD cbT; + LPBYTE lpbT = malloc(cb); + + assert( fRet ); + assert( *pcbWritten == cb ); + fRet = ReadProcessMemory(hprc->rwHand, lpOffset, lpbT, cb, &cbT); + assert(fRet); + assert( cb == cbT); + assert(memcmp(lpbT, lpb) == 0); + free lpbT; + } +#endif + + return fRet; +} + + +BOOL +DbgReadMemory( + HPRCX hprc, + LPVOID lpOffset, + LPVOID lpb, + DWORD cb, + LPDWORD lpRead + ) +/*++ + +Routine Description: + + Read from a flat address in a process. + +Arguments: + + hprc - Supplies the handle to the process + + lpOffset - Supplies address of data in process + + lpb - Supplies a pointer to the local buffer + + cb - Supplies the count of bytes to read + + lpRead - Returns the number of bytes actually read + +Return Value: + + TRUE if successful and FALSE otherwise + +--*/ + +{ +#ifndef WIN32S + DWORD cbr; + if (CrashDump) { + cbr = DmpReadMemory( (PVOID)lpOffset, lpb, cb ); + if (lpRead) { + *lpRead = cbr; + } + return (cbr > 0) || (cbr == cb); + } +#endif + + assert(hprc->rwHand != (HANDLE)-1); + + if (hprc->rwHand == (HANDLE)-1) { + return FALSE; + } + + if (ReadProcessMemory(hprc->rwHand, lpOffset, lpb, cb, lpRead)) { + + return TRUE; + + } else { + +#if DBG + int e = GetLastError(); +#endif + // + // Reads across page boundaries will not work if the + // second page is not accessible. + // +#define PAGE_SIZE (SystemInfo.dwPageSize) +#define PAGE_MASK (~(PAGE_SIZE-1)) + + DWORD firstsize; + DWORD dwRead; + + firstsize = (((DWORD)lpOffset + PAGE_SIZE) & PAGE_MASK) - (DWORD)lpOffset; + if (cb < firstsize) { + firstsize = cb; + } + + // + // read from the first page. If the first read fails, + // fail the whole thing. + // + + if (!ReadProcessMemory(hprc->rwHand, lpOffset, lpb, firstsize, + lpRead)) { + return FALSE; + } + + // + // read intermediate complete pages. + // if any of these reads fail, succeed with a short read. + // + + assert(*lpRead == firstsize); + cb -= firstsize; + lpb = (LPVOID)((LPBYTE)lpb + firstsize); + lpOffset = (LPVOID)((LPBYTE)lpOffset + firstsize); + + while (cb >= PAGE_SIZE) { + + if (!ReadProcessMemory(hprc->rwHand, lpOffset, lpb, PAGE_SIZE, + &dwRead)) { + return TRUE; + } else { + assert(dwRead == PAGE_SIZE); + lpb = (LPVOID)((LPBYTE)lpb + PAGE_SIZE); + lpOffset = (LPVOID)((LPBYTE)lpOffset + PAGE_SIZE); + *lpRead += dwRead; + cb -= PAGE_SIZE; + } + } + + if (cb > 0) { + if (ReadProcessMemory(hprc->rwHand, lpOffset, lpb, cb, &dwRead)) { + assert(dwRead == cb); + *lpRead += dwRead; + } + } + + return TRUE; + } + +} + +BOOL +DbgGetThreadContext( + HTHDX hthd, + PCONTEXT lpcontext + ) +{ +#ifndef WIN32S + if (CrashDump) { + return DmpGetContext(hthd->tid-1, lpcontext); + } else if (hthd->fWowEvent) { + return WOWGetThreadContext(hthd, lpcontext); + } else +#endif + { +#ifndef TARGET_MIPS + return GetThreadContext(hthd->rwHand, lpcontext); +#else + BOOL rc; + DWORD Flags = lpcontext->ContextFlags; + + if (MipsContextSize == Ctx32Bit) { + lpcontext->ContextFlags = ((lpcontext->ContextFlags & ~CONTEXT_EXTENDED_INTEGER) | CONTEXT_INTEGER); + } + rc = GetThreadContext(hthd->rwHand, lpcontext); + if (rc) { + if ((Flags & CONTEXT_EXTENDED_INTEGER) == CONTEXT_EXTENDED_INTEGER) { + CoerceContext32To64(lpcontext); + } else if ((Flags & CONTEXT_INTEGER) == CONTEXT_INTEGER) { + CoerceContext64To32(lpcontext); + } + } + return rc; +#endif + } +} + +BOOL +DbgSetThreadContext( + HTHDX hthd, + PCONTEXT lpcontext + ) +{ + assert(!CrashDump); + +#ifndef WIN32S + if (CrashDump) { + return FALSE; + } else if (hthd->fWowEvent) { + return WOWSetThreadContext(hthd, lpcontext); + } else +#endif + { +#ifdef TARGET_MIPS + CONTEXT ctx = *lpcontext; + lpcontext = &ctx; + if (MipsContextSize == Ctx32Bit) { + CoerceContext64To32(lpcontext); + } else { + CoerceContext32To64(lpcontext); + } +#endif + return SetThreadContext(hthd->rwHand, lpcontext); + } +} + +BOOL +WriteBreakPoint( + IN PBREAKPOINT Breakpoint + ) +{ + DWORD cb; + BP_UNIT opcode = BP_OPCODE; + BOOL r = AddrWriteMemory(Breakpoint->hprc, + Breakpoint->hthd, + &Breakpoint->addr, + &opcode, + BP_SIZE, + &cb); + return (r && (cb == BP_SIZE)); +} + +BOOL +RestoreBreakPoint( + IN PBREAKPOINT Breakpoint + ) +{ + DWORD cb; + BOOL r; + + assert(Breakpoint->bpType == bptpExec); + + r = AddrWriteMemory(Breakpoint->hprc, + Breakpoint->hthd, + &Breakpoint->addr, + &Breakpoint->instr1, + BP_SIZE, + &cb); + return (r && (cb == BP_SIZE)); +} + + + +/****************************************************************************/ +/****************************************************************************/ +#ifndef PROCESSOR_MIPS_R10000 +#define PROCESSOR_MIPS_R10000 10000 +#endif + + +VOID +GetMachineType( + LPPROCESSOR p + ) +{ + // Look Ma, no ifdefs!! + + SYSTEM_INFO SystemInfo; + + GetSystemInfo(&SystemInfo); + switch (SystemInfo.wProcessorArchitecture) { + + case PROCESSOR_ARCHITECTURE_INTEL: + p->Level = SystemInfo.wProcessorLevel; + p->Type = mptix86; + p->Endian = endLittle; + break; + + case PROCESSOR_ARCHITECTURE_MIPS: + p->Level = SystemInfo.wProcessorLevel * 1000; + p->Type = mptmips; + p->Endian = endLittle; + break; + + case PROCESSOR_ARCHITECTURE_ALPHA: + p->Level = SystemInfo.wProcessorLevel; + p->Type = mptdaxp; + p->Endian = endLittle; + p->Level = 21064; // BUGBUG - why? + break; + + case PROCESSOR_ARCHITECTURE_PPC: + p->Level = SystemInfo.wProcessorLevel + 600; // BUGBUG - 603+ + p->Type = mptmppc; + p->Endian = endLittle; + break; + + default: + assert(!"Unknown target machine"); + break; + } +} + + + +HWND +HwndFromPid ( + PID pid + ) +{ + + HWND hwnd = GetForegroundWindow(); + HWND hwndNext; + + DPRINT(4, ( "*HwndFromPid, pid = 0x%lx\n", pid ) ); + + for (hwndNext = GetWindow ( hwnd, GW_HWNDFIRST ); + hwndNext; + hwndNext = GetWindow ( hwndNext, GW_HWNDNEXT )) { + + // what we want is windows *without* an owner, hence !GetWindow... + if ( !GetWindow ( hwndNext, GW_OWNER ) && + IsWindowVisible ( hwndNext ) ) { + PID pidT; + + GetWindowThreadProcessId ( hwndNext, &pidT ); + DPRINT(4, ("\thwnd 0x%08lx owned by process 0x%lx, ", + hwndNext, pidT ) ); +#if DBG + { + char szWindowText[256]; + if ( GetWindowText(hwndNext, szWindowText, + sizeof(szWindowText)) ) { + DPRINT(4, ("title = \"%s\"\n", szWindowText) ); + } else { + DPRINT(4, ("title = \"<none>\"\n") ); + } + } +#endif + if ( pid == pidT ) { + // found a match, return the hwnd + break; + } + } // if ( !GetWindow... + hwndNext = GetWindow ( hwndNext, GW_HWNDNEXT ); + } // while ( hwndNext ) + + return hwndNext; +} + +VOID +DmSetFocus ( + HPRCX phprc + ) +{ + PID pidGer; // debugger pid + PID pidCurFore; // owner of foreground window + HWND hwndCurFore; // current foreground window + HWND phprc_hwndProcess; + HWND hwndT; + + + // decide if we are the foreground app currently + pidGer = GetCurrentProcessId(); // debugger pid + hwndCurFore = GetForegroundWindow(); + if ( hwndCurFore && + GetWindowThreadProcessId ( hwndCurFore, &pidCurFore ) ) { + if ( pidCurFore != pidGer ) { + // foreground is not debugger, bail out + return; + } + } + + phprc_hwndProcess = HwndFromPid ( phprc->pid ); + if ( !phprc_hwndProcess ) { + // no window attached to pid; bail out + return; + } + + // continuing with valid hwnd's and we have foreground window + assert ( phprc_hwndProcess ); + + // now, get the last active window in that group! + hwndT = GetLastActivePopup ( phprc_hwndProcess ); + + // NOTE: taskman has a check at this point for state disabled... + // don't know if I should do it either... + SetForegroundWindow ( hwndT ); +} + + + +/****************************************************************************/ +// +// ContinueDebugEvent() queue. +// We can only have one debug event pending per process, but there may be +// one event pending for each process we are debugging. +// +// There are 200 entries in a static sized queue. If there are ever more +// than 200 debug events pending, AND windbg actually handles them all in +// less than 1/5 second, we will be in trouble. Until then, sleep soundly. +// +/****************************************************************************/ +typedef struct tagCQUEUE { + struct tagCQUEUE *next; + DWORD pid; + DWORD tid; + DWORD dwContinueStatus; +} CQUEUE, *LPCQUEUE; + +static LPCQUEUE lpcqFirst; +static LPCQUEUE lpcqLast; +static LPCQUEUE lpcqFree; +static CQUEUE cqueue[200]; +static CRITICAL_SECTION csContinueQueue; + +static BOOL DequeueContinueDebugEvents( void ); + + +/***************************************************************************/ +/***************************************************************************/ + + + +VOID +QueueContinueDebugEvent( + DWORD dwProcessId, + DWORD dwThreadId, + DWORD dwContinueStatus + ) + +/*++ + +Routine Description: + + Queue a debug event continue for later execution. + +Arguments: + + dwProcessId = pid to continue + + dwThreadId = tid to continue + + dwContinueStatus - Supplies the continue status code + +Return Value: + + None. + +--*/ + +{ +#ifdef WIN32S + + HTHDX hthd; + + if (dwContinueStatus != DBG_TERMINATE_PROCESS) { + hthd = HTHDXFromPIDTID(dwProcessId, dwThreadId); + if (hthd) { + if (hthd->fContextDirty) { + /* + * Set the child's context + */ + DPRINT(1, ("Context is dirty\n")); + + DbgSetThreadContext(hthd, &hthd->context); + + hthd->fContextDirty = FALSE; + } + } + } + + DPRINT(1, ("ContinueDebugEvent(PID:%x, TID:%x, Stat:%u)\r\n", + dwProcessId, dwThreadId, dwContinueStatus)); + + ContinueDebugEvent(dwProcessId, dwThreadId, dwContinueStatus); + fProcessingDebugEvent = FALSE; + fWaitForDebugEvent = FALSE; + +#else + + LPCQUEUE lpcq; + + EnterCriticalSection(&csContinueQueue); + + lpcq = lpcqFree; + assert(lpcq); + + lpcqFree = lpcq->next; + + lpcq->next = NULL; + if (lpcqLast) { + lpcqLast->next = lpcq; + } + lpcqLast = lpcq; + + if (!lpcqFirst) { + lpcqFirst = lpcq; + } + + lpcq->pid = dwProcessId; + lpcq->tid = dwThreadId; + lpcq->dwContinueStatus = dwContinueStatus; + + LeaveCriticalSection(&csContinueQueue); + +#endif + + return; +} /* QueueContinueDebugEvent() */ + + +#ifndef WIN32S +BOOL +DequeueContinueDebugEvents( + VOID + ) +/*++ + +Routine Description: + + Remove any pending continues from the queue and execute them. + +Arguments: + + none + +Return Value: + + TRUE if one or more events were continued. + FALSE if none were continued. + +--*/ +{ + LPCQUEUE lpcq; + BOOL fDid = FALSE; + HTHDX hthd; + + EnterCriticalSection(&csContinueQueue); + + while ( lpcq=lpcqFirst ) { + + hthd = HTHDXFromPIDTID(lpcq->pid, lpcq->tid); + if (hthd) { + if (hthd->fContextDirty) { + /* + * Set the child's context + */ + + DbgSetThreadContext(hthd, &hthd->context); + + hthd->fContextDirty = FALSE; + } + hthd->fWowEvent = FALSE; + } + + ContinueDebugEvent(lpcq->pid, lpcq->tid, lpcq->dwContinueStatus); + + lpcqFirst = lpcq->next; + if (lpcqFirst == NULL) { + lpcqLast = NULL; + } + + lpcq->next = lpcqFree; + lpcqFree = lpcq; + + fDid = TRUE; + } + + LeaveCriticalSection(&csContinueQueue); + return fDid; +} /* DequeueContinueDebugEvents() */ +#endif // !WIN32S + + +VOID +AddQueue( + DWORD dwType, + DWORD dwProcessId, + DWORD dwThreadId, + DWORD dwData, + DWORD dwLen + ) + +/*++ + +Routine Description: + + + +Arguments: + + dwType + + dwProcessId + + dwThreadId + + dwData + + dwLen + +Return Value: + + none + +--*/ + +{ + switch (dwType) { + case QT_CONTINUE_DEBUG_EVENT: + case QT_TRACE_DEBUG_EVENT: + if (CrashDump) { + break; + } + + QueueContinueDebugEvent(dwProcessId, dwThreadId, dwData); + break; + + + case QT_RELOAD_MODULES: + case QT_REBOOT: + case QT_CRASH: + case QT_RESYNC: + assert(!"Unsupported usermode QType in AddQueue."); + break; + + case QT_DEBUGSTRING: + assert(!"Is this a bad idea?"); + DMPrintShellMsg( "%s", (LPSTR)dwData ); + free((LPSTR)dwData); + break; + + } + + if (dwType == QT_CONTINUE_DEBUG_EVENT) { + SetEvent( hEventContinue ); + } + + return; +} + +BOOL +DequeueAllEvents( + BOOL fForce, // force a dequeue even if the dm isn't initialized + BOOL fConsume // delete all events from the queue with no action + ) +{ +#ifdef WIN32S + return TRUE; +#else + return DequeueContinueDebugEvents(); +#endif +} + +VOID +InitEventQueue( + VOID + ) +{ + int n; + int i; + + InitializeCriticalSection(&csContinueQueue); + + n = sizeof(cqueue) / sizeof(CQUEUE); + for (i = 0; i < n-1; i++) { + cqueue[i].next = &cqueue[i+1]; + } + cqueue[n-1].next = NULL; + lpcqFree = &cqueue[0]; + lpcqFirst = NULL; + lpcqLast = NULL; +} + + +VOID +ProcessQueryTlsBaseCmd( + HPRCX hprcx, + HTHDX hthdx, + LPDBB lpdbb + ) + +/*++ + +Routine Description: + + This function is called in response to an EM request to get the base + of the thread local storage for a given thread and DLL. + +Arguments: + + hprcx - Supplies a process handle + hthdx - Supplies a thread handle + lpdbb - Supplies the command information packet + +Return Value: + + None. + +--*/ + +{ + XOSD xosd; + OFFSET offRgTls; + DWORD iTls; + LPADDR lpaddr = (LPADDR) LpDmMsg->rgb; + OFFSET offResult; + DWORD cb; + int iDll; + OFFSET offDll = * (OFFSET *) lpdbb->rgbVar; + + /* + * Read number 1. Get the pointer to the Thread Local Storage array. + */ + + + if ((DbgReadMemory(hprcx, (char *) hthdx->offTeb+0x2c, + &offRgTls, sizeof(OFFSET), &cb) == 0) || + (cb != sizeof(OFFSET))) { + err: + xosd = xosdUnknown; + Reply(0, &xosd, lpdbb->hpid); + return; + } + + /* + * Read number 2. Get the TLS index for this dll + */ + + for (iDll=0; iDll<hprcx->cDllList; iDll+=1 ) { + if (hprcx->rgDllList[iDll].fValidDll && + (hprcx->rgDllList[iDll].offBaseOfImage == offDll)) { + break; + } + } + + if (iDll == hprcx->cDllList) { + goto err; + } + + if (!DbgReadMemory(hprcx, + (char *) hprcx->rgDllList[iDll].offTlsIndex, + &iTls, + sizeof(iTls), + &cb) || + (cb != sizeof(iTls))) { + goto err; + } + + + /* + * Read number 3. Get the actual TLS base pointer + */ + + if ((DbgReadMemory(hprcx, (char *)offRgTls+iTls*sizeof(OFFSET), + &offResult, sizeof(OFFSET), &cb) == 0) || + (cb != sizeof(OFFSET))) { + goto err; + } + + memset(lpaddr, 0, sizeof(ADDR)); + + lpaddr->addr.off = offResult; +#ifdef TARGET_i386 + lpaddr->addr.seg = (SEGMENT) hthdx->context.SegDs; +#else + lpaddr->addr.seg = 0; +#endif + ADDR_IS_FLAT(*lpaddr) = TRUE; + + LpDmMsg->xosdRet = xosdNone; + Reply( sizeof(ADDR), LpDmMsg, lpdbb->hpid ); + return; +} /* ProcessQueryTlsBaseCmd() */ + + +VOID +ProcessQuerySelectorCmd( + HPRCX hprcx, + HTHDX hthdx, + LPDBB lpdbb + ) +/*++ + +Routine Description: + + This command is sent from the EM to fill in an LDT_ENTRY structure + for a given selector. + +Arguments: + + hprcx - Supplies the handle to the process + + hthdx - Supplies the handle to the thread and is optional + + lpdbb - Supplies the pointer to the full query packet + +Return Value: + + None. + +--*/ + +{ + XOSD xosd; + +#if defined( TARGET_i386 ) + SEGMENT seg; + + seg = *((SEGMENT *) lpdbb->rgbVar); + + if (hthdx == hthdxNull) { + hthdx = hprcx->hthdChild; + } + + if ((hthdx != NULL) && + (GetThreadSelectorEntry(hthdx->rwHand, seg, (LDT_ENTRY *) LpDmMsg->rgb))) { + LpDmMsg->xosdRet = xosdNone; + Reply( sizeof(LDT_ENTRY), LpDmMsg, lpdbb->hpid); + return; + } +#endif + +#ifdef OSDEBUG4 + xosd = xosdInvalidParameter; +#else + xosd = xosdInvalidSelector; +#endif + + Reply( sizeof(xosd), &xosd, lpdbb->hpid); + + return; +} /* ProcessQuerySelectorCmd */ + + +VOID +ProcessVirtualQueryCmd( + HPRCX hprc, + LPDBB lpdbb + ) +{ + XOSD xosd = xosdNone; + ADDR addr; + BOOL fRet; + DWORD dwSize; + + if (!hprc->rwHand || hprc->rwHand == (HANDLE)(-1)) { +#ifdef OSDEBUG4 + xosd = xosdBadProcess; +#else + xosd = xosdInvalidProc; +#endif + } + + addr = *(LPADDR)(lpdbb->rgbVar); + + if (!ADDR_IS_FLAT(addr)) { + fRet = TranslateAddress(hprc, 0, &addr, TRUE); + assert(fRet); + if (!fRet) { + xosd = xosdBadAddress; + goto reply; + } + } + + dwSize = VirtualQueryEx(hprc->rwHand, + (LPCVOID)addr.addr.off, + (PMEMORY_BASIC_INFORMATION)LpDmMsg->rgb, + sizeof(MEMORY_BASIC_INFORMATION)); + + if (dwSize != sizeof(MEMORY_BASIC_INFORMATION)) { + xosd = xosdUnknown; + goto reply; + } + + reply: + + LpDmMsg->xosdRet = xosd; + Reply( sizeof(MEMORY_BASIC_INFORMATION), LpDmMsg, lpdbb->hpid ); + + return; +} /* ProcessVirtualQueryCmd */ + +VOID +ProcessGetDmInfoCmd( + HPRCX hprc, + LPDBB lpdbb, + DWORD cb + ) +{ + LPDMINFO lpi = (LPDMINFO)LpDmMsg->rgb; + + LpDmMsg->xosdRet = xosdNone; + + lpi->fAsync = 1; +#ifdef WIN32S + lpi->fHasThreads = 0; +#else + lpi->fHasThreads = 1; +#endif + lpi->fReturnStep = 0; + //lpi->fRemote = ??? + lpi->fAsyncStop = 1; + lpi->fAlwaysFlat = 0; + lpi->fHasReload = 0; + + lpi->cbSpecialRegs = 0; + lpi->MajorVersion = 0; + lpi->MinorVersion = 0; + + lpi->Breakpoints = bptsExec | + bptsDataC | + bptsDataW | + bptsDataR | + bptsDataExec; + + GetMachineType(&lpi->Processor); + + // + // hack so that TL can call tlfGetVersion before + // reply buffer is initialized. + // + if ( cb >= (sizeof(DBB) + sizeof(DMINFO)) ) { + memcpy(lpdbb->rgbVar, lpi, sizeof(DMINFO)); + } + + Reply( sizeof(DMINFO), LpDmMsg, lpdbb->hpid ); +} /* ProcessGetDMInfoCmd */ + + + +ActionResumeThread( + DEBUG_EVENT * pde, + HTHDX hthd, + DWORD unused, + PSUSPENDSTRUCT pss + ) +{ + // + // This thread just hit a breakpoint after falling out of + // SuspendThread. Clear the BP, put the original context + // back and continue. + // + + RemoveBP( pss->pbp ); + + hthd->context = pss->context; + hthd->fContextDirty = TRUE; + hthd->pss = NULL; + + free(pss); + + AddQueue( QT_CONTINUE_DEBUG_EVENT, + hthd->hprc->pid, + hthd->tid, + DBG_CONTINUE, + 0); + return 0; +} + + +BOOL +MakeThreadSuspendItself( + HTHDX hthd + ) +/*++ + +Routine Description: + + Set up the thread to call SuspendThread. This relies on kernel32 + being present in the debuggee, and the current implementation gives + up if the thread is in a 16 bit context. + + The cpu dependent part of this is MakeThreadSuspendItselfHelper, + in mach.c. + +Arguments: + + hthd - Supplies thread + +Return Value: + + TRUE if the thread will be suspended, FALSE if not. + +--*/ +{ + PSUSPENDSTRUCT pss; + ADDR addr; + HANDLE hdll; + FARPROC lpSuspendThread; + + // + // the only time this should fail is when the debuggee + // does not use kernel32, which is rare. + // + + if (!hthd->hprc->dwKernel32Base) { + DPRINT(1, ("can't suspend thread %x: Kernel32 not loaded\n", + (DWORD)hthd)); + DMPrintShellMsg("*** Unable to suspend thread.\n"); + return 0; + } + + // + // Oh, yeah... don't try to do this with a 16 bit thread, either. + // maybe someday... + // + + if (hthd->fWowEvent) { + DMPrintShellMsg("*** Can't leave 16 bit thread suspended.\n"); + return 0; + } + + // + // find the address of SuspendThread + // + + hdll = GetModuleHandle("KERNEL32"); + assert(hdll || !"kernel32 not found in DM!!!"); + if (!hdll) { + return 0; + } + + lpSuspendThread = GetProcAddress(hdll, "SuspendThread"); + assert(lpSuspendThread || !"SuspendThread not found in kernel32!!!"); + if (!lpSuspendThread) { + return 0; + } + + // + // this is probably unneccessary, because I think kernel32 + // may not be relocated. + // + lpSuspendThread = (FARPROC)((DWORD)lpSuspendThread - (DWORD)hdll + + hthd->hprc->dwKernel32Base); + + pss = malloc(sizeof(*pss)); + assert(pss || !"malloc failed in MakeThreadSuspendItself"); + if (!pss) { + return 0; + } + + // + // Remember the current context + // + hthd->pss = pss; + pss->context = hthd->context; + + // + // set a BP on the current PC, and register a persistent + // expected event to catch it later. + // + + AddrInit(&addr, 0, 0, (DWORD) PC(hthd), TRUE, TRUE, FALSE, FALSE); + pss->pbp = SetBP( hthd->hprc, hthd, bptpExec, bpnsStop, &addr, (HPID) INVALID); + + // + // don't try to step off of BP. + // + pss->atBP = hthd->atBP; + hthd->atBP = NULL; + + RegisterExpectedEvent( + hthd->hprc, + hthd, + BREAKPOINT_DEBUG_EVENT, + (DWORD)pss->pbp, + NULL, + (ACVECTOR)ActionResumeThread, + TRUE, + pss); + + // + // do machine dependent part + // + MakeThreadSuspendItselfHelper(hthd, lpSuspendThread); + + return TRUE; +} + + + +VOID +ProcessIoctlGenericCmd( + HPRCX hprc, + HTHDX hthd, + LPDBB lpdbb + ) +{ + LPIOL lpiol = (LPIOL)lpdbb->rgbVar; + PIOCTLGENERIC pig = (PIOCTLGENERIC)lpiol->rgbVar; + DWORD len; + ADDR addr; + + + switch( pig->ioctlSubType ) { + case IG_TRANSLATE_ADDRESS: + memcpy( &addr, pig->data, sizeof(addr) ); + if (TranslateAddress( hprc, hthd, &addr, TRUE )) { + memcpy( pig->data, &addr, sizeof(addr) ); + len = sizeof(IOCTLGENERIC) + pig->length; + memcpy( LpDmMsg->rgb, pig, len ); + LpDmMsg->xosdRet = xosdNone; + Reply( sizeof(IOCTLGENERIC)+pig->length, LpDmMsg, lpdbb->hpid ); + } else { + LpDmMsg->xosdRet = xosdUnknown; + Reply( 0, LpDmMsg, lpdbb->hpid ); + } + break; + + case IG_WATCH_TIME: + WtRangeStep( hthd ); + LpDmMsg->xosdRet = xosdNone; + Reply( 0, LpDmMsg, lpdbb->hpid ); + break; + + case IG_WATCH_TIME_STOP: + WtStruct.fWt = TRUE; + WtStruct.dwType = pig->ioctlSubType; + WtStruct.hthd = hthd; + LpDmMsg->xosdRet = xosdNone; + Reply( 0, LpDmMsg, lpdbb->hpid ); + break; + + case IG_WATCH_TIME_RECALL: + WtStruct.fWt = TRUE; + WtStruct.dwType = pig->ioctlSubType; + WtStruct.hthd = hthd; + LpDmMsg->xosdRet = xosdNone; + Reply( 0, LpDmMsg, lpdbb->hpid ); + break; + + case IG_WATCH_TIME_PROCS: + WtStruct.fWt = TRUE; + WtStruct.dwType = pig->ioctlSubType; + WtStruct.hthd = hthd; + LpDmMsg->xosdRet = xosdNone; + Reply( 0, LpDmMsg, lpdbb->hpid ); + break; + + case IG_THREAD_INFO: +#ifdef WIN32S + LpDmMsg->xosdRet = xosdUnknown; + Reply( 0, LpDmMsg, lpdbb->hpid ); +#else + { + typedef NTSTATUS (* QTHREAD)(HANDLE,THREADINFOCLASS,PVOID,ULONG,PULONG); + + NTSTATUS Status; + THREAD_BASIC_INFORMATION ThreadBasicInfo; + QTHREAD Qthread; + + Qthread = (QTHREAD)GetProcAddress( GetModuleHandle( "ntdll.dll" ), "NtQueryInformationThread" ); + if (!Qthread) { + LpDmMsg->xosdRet = xosdUnknown; + Reply( 0, LpDmMsg, lpdbb->hpid ); + break; + } + + Status = Qthread( hthd->rwHand, + ThreadBasicInformation, + &ThreadBasicInfo, + sizeof(ThreadBasicInfo), + NULL + ); + if (!NT_SUCCESS(Status)) { + LpDmMsg->xosdRet = xosdUnknown; + Reply( 0, LpDmMsg, lpdbb->hpid ); + } + + *(LPDWORD)pig->data = (DWORD)ThreadBasicInfo.TebBaseAddress; + + len = sizeof(IOCTLGENERIC) + pig->length; + memcpy( LpDmMsg->rgb, pig, len ); + + LpDmMsg->xosdRet = xosdNone; + Reply( len, LpDmMsg, lpdbb->hpid ); + } +#endif // WIN32S + break; + + case IG_TASK_LIST: +#ifdef WIN32S + LpDmMsg->xosdRet = xosdUnknown; + Reply( 0, LpDmMsg, lpdbb->hpid ); +#else + { + PTASK_LIST pTaskList = (PTASK_LIST)pig->data; + GetTaskList( pTaskList, pTaskList->dwProcessId ); + len = sizeof(IOCTLGENERIC) + pig->length; + memcpy( LpDmMsg->rgb, pig, len ); + LpDmMsg->xosdRet = xosdNone; + Reply( sizeof(IOCTLGENERIC)+pig->length, LpDmMsg, lpdbb->hpid ); + } +#endif + break; + + default: + LpDmMsg->xosdRet = xosdUnknown; + Reply( 0, LpDmMsg, lpdbb->hpid ); + break; + } + + return; +} + + +VOID +ProcessIoctlCustomCmd( + HPRCX hprc, + HTHDX hthd, + LPDBB lpdbb + ) +{ + LPIOL lpiol = (LPIOL)lpdbb->rgbVar; + LPSTR p = lpiol->rgbVar; + + + LpDmMsg->xosdRet = xosdUnsupported; + + // + // parse the command + // + while (*p && !isspace(*p++)); + if (*p) { + *(p-1) = '\0'; + } + + // + // we don't have any custom dot command here yet + // when we do this is what the code should look like: + // + // at this point the 'p' variable points to any arguments + // to the dot command + // + // if (_stricmp( lpiol->rgbVar, "dot-command" ) == 0) { + // -----> do your thing <------ + // LpDmMsg->xosdRet = xosdNone; + // } + // +#if 0 + if ( !_stricmp(lpiol->rgbVar, "FastStep") ) { + fSmartRangeStep = TRUE; + LpDmMsg->xosdRet = xosdNone; + } else if ( !_stricmp(lpiol->rgbVar, "SlowStep") ) { + fSmartRangeStep = FALSE; + LpDmMsg->xosdRet = xosdNone; + } +#else + LpDmMsg->xosdRet = xosdNone; +#endif + + // + // send back our response + // + Reply(0, LpDmMsg, lpdbb->hpid); +} /* ProcessIoctlCustomCmd() */ + + +#ifndef WIN32S + +DWORD WINAPI +DoTerminate( + LPVOID lpv + ) +{ + HPRCX hprcx = (HPRCX)lpv; + + if (CrashDump) { + ProcessUnloadCmd(hprcx, NULL, NULL); + return 0; + } + TerminateProcess(hprcx->rwHand, 1); + + // + // now that TerminateThread has completed, put priority + // back before calling out of DM + // + + SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_NORMAL); + + WaitForSingleObject(hprcx->hExitEvent, INFINITE); + + ProcessUnloadCmd(hprcx, NULL, NULL); + + return 0; +} + +VOID +CompleteTerminateProcessCmd( + VOID + ) +{ + DEBUG_EVENT devent, *de=&devent; + HANDLE hThread; + DWORD dwTid; + BREAKPOINT *pbpT; + BREAKPOINT *pbp; + PKILLSTRUCT pk; + HPRCX hprc; + HTHDX hthd; + + DEBUG_PRINT("CompleteTerminateProcessCmd"); + + EnterCriticalSection(&csKillQueue); + + pk = KillQueue; + if (pk) { + KillQueue = pk->next; + } + + LeaveCriticalSection(&csKillQueue); + + assert(pk); + if (!pk) { + return; + } + + hprc = pk->hprc; + free(pk); + + ConsumeAllProcessEvents(hprc, TRUE); + + /* + * see if process is already dead + */ + + if ((hprc->pstate & ps_dead) || (hprc->rwHand == (HANDLE)INVALID)) { + + // + // Queue a continue if any thread is stopped + // + + for (hthd = hprc->hthdChild; hthd; hthd = hthd->nextSibling) { + if (hthd->tstate & ts_stopped) { + AddQueue( QT_CONTINUE_DEBUG_EVENT, + hthd->hprc->pid, + hthd->tid, + DBG_CONTINUE, + 0); + hthd->tstate &= ~ts_stopped; + hthd->tstate |= ts_running; + } + } + + ProcessUnloadCmd(hprc, NULL, NULL); + + } else { + + for (pbp = BPNextHprcPbp(hprc, NULL); pbp; pbp = pbpT) { + pbpT = BPNextHprcPbp(hprc, pbp); + RemoveBP(pbp); + } + + // + // Start another thread to kill the thing. This thread needs to + // continue any threads which are stopped. The new thread will then + // wait until this one (the poll thread) has handled all of the + // events, and then send destruction notifications to the shell. + // + + hThread = CreateThread(NULL, + 4096, + DoTerminate, + (LPVOID)hprc, + 0, + &dwTid); + assert(hThread); + if ( !hThread ) { + return; + } + + // + // Yield so DoTerminate can do its thing before we start posting + // ContinueDebugEvents, so we minimize the time that the app + // runs before it is terminated. + // + + hprc->pstate |= ps_killed; + SetThreadPriority(hThread, THREAD_PRIORITY_TIME_CRITICAL); + Sleep(0); + + CloseHandle(hThread); + + // + // Queue a continue if any thread is stopped + // + + for (hthd = hprc->hthdChild; hthd; hthd = hthd->nextSibling) { + if (hthd->tstate & ts_stopped) { + AddQueue( QT_CONTINUE_DEBUG_EVENT, + hthd->hprc->pid, + hthd->tid, + DBG_CONTINUE, + 0); + hthd->tstate &= ~ts_stopped; + hthd->tstate |= ts_running; + } + } + + } + +} + + +DWORD +ProcessTerminateProcessCmd( + HPRCX hprc, + HTHDX hthd, + LPDBB lpdbb + ) +{ + PKILLSTRUCT pk; + + if (!hprc) { + return FALSE; + } + + Unreferenced( lpdbb ); + + pk = (PKILLSTRUCT)malloc(sizeof(KILLSTRUCT)); + pk->hprc = hprc; + + EnterCriticalSection(&csKillQueue); + + pk->next = KillQueue; + KillQueue = pk; + + LeaveCriticalSection(&csKillQueue); + + return TRUE; +} +#endif + +#ifdef WIN32S + +DWORD +ProcessTerminateProcessCmd( + HPRCX hprc, + HTHDX hthd, + LPDBB lpdbb + ) +{ + DWORD rval; + + if (!hprc) { + return FALSE; + } + + Unreferenced( lpdbb ); + + DEBUG_PRINT_2("ProcessTerminateProcessCmd called hprc=0x%x, hthd=0x%x.\n\r", + hprc, hthd); + + // Win32s doesn't support TerminateProcess(), but does give us a special + // ContinueDebugEvent flag. If we are stopped at a debug event, we can + // Continue with this flag to terminate the child app. + + DEBUG_PRINT("ConsumeAllProcessEvents\r\n"); + + ConsumeAllProcessEvents(hprc, TRUE); + + DEBUG_PRINT("Check process state\r\n"); + + if ((hprc->pstate & ps_dead) || hprc->rwHand == (HANDLE)INVALID) { + DEBUG_PRINT("Process already dead\r\n"); + if (fExitProcessDebugEvent) { + // we saved tidExit when we got the EXIT_PROCESS_DEBUG_EVENT + AddQueue( QT_CONTINUE_DEBUG_EVENT, + hprc->pid, + tidExit, + DBG_CONTINUE, + 0); + } + rval = FALSE; // already dead + } + + if (fProcessingDebugEvent) { + DEBUG_PRINT_1("Continue with %s\r\n", + (fExitProcessDebugEvent ? "DBG_CONTINUE" : "DBG_TERMINATE_PROCESS")); + + for (hthd = hprc->hthdChild; hthd; hthd = hthd->nextSibling) { + if (hthd->tstate & ts_stopped) { + AddQueue( QT_CONTINUE_DEBUG_EVENT, + hthd->hprc->pid, + hthd->tid, + fExitProcessDebugEvent? + DBG_CONTINUE : DBG_TERMINATE_PROCESS, + 0); + hthd->tstate &= ~ts_stopped; + hthd->tstate |= ts_running; + } + } + + // mark this process as killed + DEBUG_PRINT("Mark process as killed\r\n"); + hprc->pstate |= ps_killed; + rval = TRUE; // killed it. + } else { + DEBUG_PRINT("Can't terminate process right now\r\n"); + // can't continue debug event right now, so can't terminate. + rval = FALSE; + } + + return rval; +} +#endif + + +VOID +ProcessAllProgFreeCmd( + HPRCX hprcXX, + HTHDX hthd, + LPDBB lpdbb + ) +{ + HPRCX hprc; + + Unreferenced(hprcXX); + Unreferenced(hthd); + + for (;;) { + + EnterCriticalSection(&csThreadProcList); + for (hprc = prcList; hprc; hprc = hprc->next) { + if (hprc->pstate != (ps_root | ps_destroyed)) { + break; + } + } + LeaveCriticalSection(&csThreadProcList); + + if (hprc) { + ProcessTerminateProcessCmd(hprc, hthd, lpdbb); + ProcessUnloadCmd(hprc, hthd, lpdbb); + } else { + break; + } + + } + + WaitForSingleObject(hEventNoDebuggee, INFINITE); +} + + +DWORD +ProcessAsyncGoCmd( + HPRCX hprc, + HTHDX hthd, + LPDBB lpdbb + ) +{ + XOSD xosd = xosdNone; +#ifndef WIN32S + DEBUG_EVENT de; +#endif + + DEBUG_PRINT("ProcessAsyncGoCmd called.\n\r"); + +#ifdef WIN32S + xosd = xosdUnsupported; // can't resume thread in win32s +#else + +#ifdef WIN32 + if ((hthd->tstate & ts_frozen)) { + if (hthd->tstate & ts_stopped) { + // + // if at a debug event, it won't really be suspended, + // so just clear the flag. + // + hthd->tstate &= ~ts_frozen; + + } else if (ResumeThread(hthd->rwHand) == -1L ) { + +#ifdef OSDEBUG4 + xosd = xosdBadThread; +#else + xosd = xosdInvalidThread; +#endif + + } else { + + hthd->tstate &= ~ts_frozen; + + /* + * deal with dead, frozen, continued thread: + */ + if ((hthd->tstate & ts_dead) && !(hthd->tstate & ts_stopped)) { + + de.dwDebugEventCode = DESTROY_THREAD_DEBUG_EVENT; + de.dwProcessId = hprc->pid; + de.dwThreadId = hthd->tid; + NotifyEM(&de, hthd, 0, NULL); + FreeHthdx(hthd); + + hprc->pstate &= ~ps_deadThread; + for (hthd = hprc->hthdChild; hthd; hthd = hthd->nextSibling) { + if (hthd->tstate & ts_dead) { + hprc->pstate |= ps_deadThread; + } + } + + } + } + } +#endif // WIN32 +#endif // !WIN32S + + Reply(0, &xosd, lpdbb->hpid); + return(xosd); +} + + +void +ActionAsyncStop( + DEBUG_EVENT * pde, + HTHDX hthd, + DWORD unused, + BREAKPOINT * pbp + ) +/*++ + +Routine Description: + + This routine is called if a breakpoint is hit which is part of a + Async Stop request. When hit is needs to do the following: clean + out any expected events on the current thread, clean out all breakpoints + which are setup for doing the current async stop. + +Arguments: + + pde - Supplies a pointer to the debug event which just occured + + hthd - Supplies a pointer to the thread for the debug event + + pbp - Supplies a pointer to the breakpoint for the ASYNC stop + +Return Value: + + None. + +--*/ + +{ + union { + RTP rtp; + char rgb[sizeof(RTP) + sizeof(BPR)]; + } rtpbuf; + RTP * prtp = &rtpbuf.rtp; + BPR * pbpr = (BPR *) prtp->rgbVar; + HPRCX hprc = hthd->hprc; + BREAKPOINT * pbpT; + + /* + * We no longer need to have this breakpoint set. + */ + + RemoveBP( pbp ); + + /* + * Remove any other breakpoints in this process which are for + * async stop commands + */ + + for (pbp = BPNextHprcPbp(hprc, NULL); pbp != NULL; pbp = pbpT) { + + pbpT = BPNextHprcPbp(hprc, pbp); + + if (pbp->id == (HPID)ASYNC_STOP_BP) { + RemoveBP( pbp ); + } + } + + /* + * Setup a return packet which says we hit an async stop breakpoint + */ + + prtp->hpid = hprc->hpid; + prtp->htid = hthd->htid; + prtp->dbc = dbcAsyncStop; + prtp->cb = sizeof(BPR); + +#ifdef TARGET_i386 + pbpr->segCS = (SEGMENT) hthd->context.SegCs; + pbpr->segSS = (SEGMENT) hthd->context.SegSs; + pbpr->offEBP = (UOFFSET) hthd->context.Ebp; +#endif + + pbpr->offEIP = (DWORD) PC(hthd); + + DmTlFunc(tlfDebugPacket, prtp->hpid, sizeof(rtpbuf), (LONG)&rtpbuf); + + return; +} /* ActionAsyncStop() */ + + + +VOID +ProcessAsyncStopCmd( + HPRCX hprc, + HTHDX hthd, + LPDBB lpdbb + ) + +/*++ + +Routine Description: + + This function is called in response to a asynchronous stop request. + In order to do this we will set breakpoints the current PC for + every thread in the system and wait for the fireworks to start. + +Arguments: + + hprc - Supplies a process handle + + hthd - Supplies a thread handle + + lpdbb - Supplies the command information packet + +Return Value: + + None. + +--*/ + +{ +#ifdef WIN32S + + /* + * Win32s doesn't support async stop this way. The user should + * press the debugger hot key at the debuggee console to generate + * an async stop. This may change if BoazF gives us a private API + * to generate the async stop exception. + */ + DEBUG_PRINT("\r\nProcessAsyncStopCmd\r\n"); + + LpDmMsg->xosdRet = xosdUnsupported; + Reply(0, LpDmMsg, lpdbb->hpid); + return; + +#else + CONTEXT regs; + BREAKPOINT * pbp; + ADDR addr; + BOOL fSetFocus = * ( BOOL *) lpdbb->rgbVar; + + regs.ContextFlags = CONTEXT_CONTROL; + + + /* + * Step 1. Enumerate through the threads and freeze them all. + */ + + for (hthd = hprc->hthdChild; hthd != NULL; hthd = hthd->nextSibling) { + if (SuspendThread(hthd->rwHand) == -1L) { + ; // Internal error; + } + } + + /* + * Step 2. Place a breakpoint on every PC address + */ + + for (hthd = hprc->hthdChild; hthd != NULL; hthd = hthd->nextSibling) { +#ifndef WIN32S + if (CrashDump) { + DmpGetContext( hthd->tid-1, ®s ); + } else +#endif + { + GetThreadContext( hthd->rwHand, ®s ); + } + + AddrInit(&addr, 0, 0, (DWORD)cPC(®s), TRUE, TRUE, FALSE, FALSE); + pbp = SetBP(hprc, hthd, bptpExec, bpnsStop, &addr, (HPID) ASYNC_STOP_BP); + + RegisterExpectedEvent(hthd->hprc, + hthd, + BREAKPOINT_DEBUG_EVENT, + (DWORD)pbp, + DONT_NOTIFY, + (ACVECTOR)ActionAsyncStop, + FALSE, + pbp); + } + + /* + * Step 3. Unfreeze all threads + */ + + if (fSetFocus) { + DmSetFocus(hprc); + } + for (hthd = hprc->hthdChild; hthd != NULL; hthd = hthd->nextSibling) { + if (ResumeThread(hthd->rwHand) == -1) { + ; // Internal error + } + } + + LpDmMsg->xosdRet = xosdNone; + Reply(0, LpDmMsg, lpdbb->hpid); + return; +#endif +} /* ProcessAsyncStopCmd() */ + + +VOID +ProcessDebugActiveCmd( + HPRCX hprc, + HTHDX hthd, + LPDBB lpdbb + ) +{ +#ifdef WIN32S + + Unreferenced(hprc); + Unreferenced(hthd); + + LpDmMsg->xosdRet = xosdUnsupported; // can't attatch in win32s + *((DWORD *)LpDmMsg->rgb) = ERROR_NOT_SUPPORTED; + Reply(sizeof(DWORD), LpDmMsg, lpdbb->hpid); + +#else + +#ifdef OSDEBUG4 + + LPDAP lpdap = ((LPDAP)(lpdbb->rgbVar)); + + Unreferenced(hprc); + Unreferenced(hthd); + + if (fDisconnected) { + + SetEvent( hEventRemoteQuit ); + + } else if (!StartDmPollThread()) { + + // + // CreateThread() failed; fail and send a dbcError. + // + LpDmMsg->xosdRet = xosdUnknown; + Reply(0, LpDmMsg, lpdbb->hpid); + + } else if (WaitForSingleObject(DebugActiveStruct.hEventReady, INFINITE) + != 0) { + // + // the wait failed. why? are there cases where we + // should restart the wait? + // + LpDmMsg->xosdRet = xosdUnknown; + Reply(0, LpDmMsg, lpdbb->hpid); + + } else { + + ResetEvent(DebugActiveStruct.hEventReady); + ResetEvent(DebugActiveStruct.hEventApiDone); + + DebugActiveStruct.dwProcessId = lpdap->dwProcessId; + DebugActiveStruct.hEventGo = lpdap->hEventGo; + DebugActiveStruct.fAttach = TRUE; + + *nameBuffer = 0; + + // wait for it... + + if (WaitForSingleObject(DebugActiveStruct.hEventApiDone, INFINITE) == 0 + && DebugActiveStruct.fReturn != 0) { + + LpDmMsg->xosdRet = xosdNone; + // + // the poll thread will reply when creating the "root" process. + // + if (!fUseRoot) { + Reply(0, LpDmMsg, lpdbb->hpid); + } + + } else { + LpDmMsg->xosdRet = xosdUnknown; + Reply(0, LpDmMsg, lpdbb->hpid); + } + + } + + +#else // OSDEBUG4 + + LPDBG_ACTIVE_STRUCT lpdba = ((LPDBG_ACTIVE_STRUCT)(lpdbb->rgbVar)); + + Unreferenced(hprc); + Unreferenced(hthd); + + if (fDisconnected) { + SetEvent( hEventRemoteQuit ); + } else if (!StartDmPollThread()) { + + LpDmMsg->xosdRet = xosdUnknown; + // Last error is from CreateThread(); + *((DWORD *)LpDmMsg->rgb) = GetLastError(); + Reply(0, LpDmMsg, lpdbb->hpid); + + // wait for attach struct to be available + } else if (WaitForSingleObject(DebugActiveStruct.hEventReady, INFINITE) + != 0) { + LpDmMsg->xosdRet = xosdUnknown; + *((DWORD *)LpDmMsg->rgb) = GetLastError(); + Reply(0, LpDmMsg, lpdbb->hpid); + + } else { + + ResetEvent(DebugActiveStruct.hEventReady); + ResetEvent(DebugActiveStruct.hEventApiDone); + + DebugActiveStruct.dwProcessId = lpdba->dwProcessId; + DebugActiveStruct.hEventGo = lpdba->hEventGo; + DebugActiveStruct.fAttach = TRUE; + + *nameBuffer = 0; + + // wait for it... + + if (WaitForSingleObject(DebugActiveStruct.hEventApiDone, INFINITE) == 0 + && DebugActiveStruct.fReturn != 0) { + + LpDmMsg->xosdRet = xosdNone; + // + // the poll thread will reply when creating the "root" process. + // + if (!fUseRoot) { + Reply(0, LpDmMsg, lpdbb->hpid); + } + + } else { + + DebugActiveStruct.dwProcessId = 0; + DebugActiveStruct.hEventGo = NULL; + LpDmMsg->xosdRet = xosdUnknown; + *((DWORD *)LpDmMsg->rgb) = DebugActiveStruct.dwError; + Reply(0, LpDmMsg, lpdbb->hpid); + } + } + +#endif // OSDEBUG4 + + SetEvent(DebugActiveStruct.hEventReady); + +#endif // WIN32S +} + + +VOID +ProcessRemoteQuit( + VOID + ) +{ + HPRCX hprc; + BREAKPOINT *pbp; + BREAKPOINT *pbpT; + + + EnterCriticalSection(&csThreadProcList); + + for(hprc=prcList->next; hprc; hprc=hprc->next) { + for (pbp = BPNextHprcPbp(hprc, NULL); pbp; pbp = pbpT) { + pbpT = BPNextHprcPbp(hprc, pbp); + RemoveBP(pbp); + } + } + + LeaveCriticalSection(&csThreadProcList); + + fDisconnected = TRUE; + ResetEvent( hEventRemoteQuit ); +} diff --git a/private/windbg/newdm/user/wow.c b/private/windbg/newdm/user/wow.c new file mode 100644 index 000000000..e536bc792 --- /dev/null +++ b/private/windbg/newdm/user/wow.c @@ -0,0 +1,1175 @@ +/*++ + +Copyright (c) 1992 Microsoft Corporation + +Module Name: + + WOW.C + +Abstract: + + This file contains the code which is responsable for dealing with + the WOW debug structures and debug events. + +Author: + + James L Schaad (jimsch) 05-11-92 + +Environment: + + User Mode WIN32 + +--*/ + + +#include "precomp.h" +#pragma hdrstop + + + + +/************************* GLOBAL VARIABLES ******************************/ + +extern char abEMReplyBuf[]; +extern DDVECTOR RgfnFuncEventDispatch[]; +extern DDVECTOR DebugDispatchTable[]; +extern char nameBuffer[]; + +/********************** Local Prototypes *********************************/ + +int LookupDllName(HPRCX hprcx, char * sz); +int InsertDllName(HPRCX hprcx, char * sz); +void RemoveDllName(HPRCX hprcx, int idx); + +static DEBUG_EVENT DeWow; + + +/************************** FUNCTIONS ************************************/ + + +void +ProcessEntryPointEvent( + DEBUG_EVENT * pde, + HTHDX hthdx + ) +/*++ + +Routine Description: + + This handles task start events after the first one. + +Arguments: + + +Return Value: + + +--*/ +{ +#if defined(i386) && !defined(WIN32S) + IMAGE_NOTE in; + LPBYTE lpb; + DWORD cbRead; + int b; + char szName0[_MAX_FNAME + _MAX_EXT]; + char szName1[_MAX_FNAME + _MAX_EXT]; + char szExt [_MAX_EXT]; + + + lpb = (LPBYTE)pde-> + u.Exception.ExceptionRecord.ExceptionInformation[2]; + b = DbgReadMemory(hthdx->hprc, + lpb, + &in, + sizeof(in), + &cbRead); + + if ((b == 0) || (cbRead != sizeof(in))) { + b = GetLastError(); + } else { + _splitpath(in.FileName, NULL, NULL, szName0, szExt); + strcat(szName0, szExt); + _splitpath(nameBuffer, NULL, NULL, szName1, szExt); + strcat(szName1, szExt); + + if (_strcmpi(szName0, szName1) == 0) { + *nameBuffer = 0; + hthdx->hprc->pstate &= ~ps_preEntry; + hthdx->tstate |= ts_stopped; + NotifyEM(pde, hthdx, 0, (LPVOID)ENTRY_BP); + return; + } + } + + AddQueue( QT_CONTINUE_DEBUG_EVENT, + hthdx->hprc->pid, + hthdx->tid, + DBG_CONTINUE, + 0); + hthdx->tstate &= ~(ts_stopped|ts_first|ts_second); + hthdx->tstate |= ts_running; + return; +#endif +} + + + +void +ProcessSegmentLoadEvent( + DEBUG_EVENT * pde, + HTHDX hthdx + ) + +/*++ + +Routine Description: + + This function takes care of dealing with segment load events from + the wow system. These come in as exceptions and are translated + to segment load events in ProcessDebugEvent. + +Arguments: + + pde - Supplies a pointer to the modified debug event + hthdx - Supplies the handle to the thread of the debug event + +Return Value: + + None. + +--*/ + +{ +#if defined(i386) && !defined(WIN32S) + + LPDWORD lpdw = &pde->u.Exception.ExceptionRecord.ExceptionInformation[0]; + int mode = LOWORD( lpdw[0] ); + int cb; + int cbRead; + int b; + char rgch[512]; + char * lpb; + LPMODULELOAD lpmdl; + RTP rtp; + LPRTP lprtp; + WORD packetType = tlfDebugPacket; + HEMI hemi; + HPRCX hprcx = hthdx->hprc; + int idx; + SEGMENT_NOTE sn; + ADDR addr; + EXPECTED_EVENT * pee; + DWORD eventCode; + DWORD subClass; + LDT_ENTRY ldt; + BREAKPOINT *bp; + + +#ifdef i386 + DeWow = *pde; + + rtp.hpid = hprcx->hpid; + rtp.htid = hthdx->htid; + + hthdx->fWowEvent = TRUE; + + VDMProcessException(pde); + + switch ( mode ) { + /* + * SEG LOAD: + * + * LOWORD(lpdw[0]) --- DBG_SEGLOAD + * HIWORD(lpdw[0]) --- Unused + * LOWORD(lpdw[1]) --- Unused + * HIWORD(lpdw[1]) --- Unused + * lpdw[2] --- pointer to SEGMENT_NOTE structure + * lpdw[3] --- Reserved + */ + + case DBG_SEGLOAD: + lpb = (char *) lpdw[2]; + b = DbgReadMemory(hprcx, lpb, &sn, sizeof(sn), &cbRead); + + if ((b == 0) || (cbRead != sizeof(sn))) { + b = GetLastError(); + AddQueue( QT_CONTINUE_DEBUG_EVENT, + hthdx->hprc->pid, + hthdx->tid, + DBG_CONTINUE, + 0); + hthdx->tstate &= ~(ts_stopped|ts_first|ts_second); + hthdx->tstate |= ts_running; + return; + } + + if (sn.FileName[0] == 0) { + AddQueue( QT_CONTINUE_DEBUG_EVENT, + hthdx->hprc->pid, + hthdx->tid, + DBG_CONTINUE, + 0); + hthdx->tstate &= ~(ts_stopped|ts_first|ts_second); + hthdx->tstate |= ts_running; + return; + } + cb = strlen(sn.FileName)+1; + + idx = LookupDllName(hprcx, sn.FileName); + + if ( idx != -1 ) { + if (hprcx->rgDllList[idx].fReal) { + // + // Changing from real to protected mode. We don't + // support this, so we'll throw away what we have + // and start from scratch. + // + lprtp = (LPRTP) rgch; + *lprtp = rtp; + lprtp->dbc = dbceModFree16; + lprtp->cb = sizeof(WORD); + *((WORD *)(&lprtp->rgbVar[0])) = idx; + + DmTlFunc(tlfDebugPacket, rtp.hpid, lprtp->cb + sizeof(RTP), + (LONG)(LPV) lprtp); + + RemoveDllName( hprcx, idx ); + idx = -1; + } + } + + if (idx == -1) { + + cb = cb + sizeof(MODULELOAD) + (sn.Segment+1)*sizeof(OBJD); + lprtp = (LPRTP) malloc(cb + sizeof(RTP)); + memset(lprtp, 0, cb + sizeof(RTP)); + + *lprtp = rtp; + lprtp->dbc = dbcModLoad; + lprtp->cb = cb; + + lpmdl = (LPMODULELOAD) &(lprtp->rgbVar[0]); + lpmdl->cobj = sn.Segment+1; + + lpmdl->rgobjd[sn.Segment].wSel = sn.Selector1; + lpmdl->rgobjd[sn.Segment].cb = (DWORD) -1; + lpmdl->rgobjd[sn.Segment].wPad = 1; + + lpmdl->mte = InsertDllName(hprcx, sn.FileName); + strcpy((char *) &lpmdl->rgobjd[lpmdl->cobj], sn.FileName); + + lpmdl->fRealMode = FALSE; + lpmdl->fFlatMode = FALSE; + lpmdl->fOffset32 = FALSE; + + DmTlFunc(tlfRequest, rtp.hpid, lprtp->cb + sizeof(RTP), + (LONG)(LPV) lprtp); + + hemi = *((HEMI *) abEMReplyBuf); + + free(lprtp); + AddQueue( QT_CONTINUE_DEBUG_EVENT, + hthdx->hprc->pid, + hthdx->tid, + DBG_CONTINUE, + 0); + hthdx->tstate &= ~(ts_stopped|ts_first|ts_second); + hthdx->tstate |= ts_running; + + } else { + LPSLI lpsli; + + lprtp = (LPRTP) malloc( sizeof(RTP) + sizeof(SLI)); + *lprtp = rtp; + lprtp->dbc = dbceSegLoad; + lprtp->cb = sizeof(SLI); + lpsli = (LPSLI) &lprtp->rgbVar[0]; + lpsli->wSelector = sn.Selector1; + lpsli->wSegNo = sn.Segment; + lpsli->mte = idx; + + DmTlFunc(tlfDebugPacket, rtp.hpid, lprtp->cb + sizeof(RTP), + (LONG)(LPV) lprtp); + + free( lprtp ); + } + + break; + + /* + * SEGMOVE: + * + * This event will be triggered if a selector number + * is to be changed. + * + * LOWORD( lpdw[0] ) - SEGMOVE + * LOWORD( lpdw[1] ) - old selector number + * HIWORD( lpdw[1] ) - new selector number + */ + + case DBG_SEGMOVE: + + lpb = (char *) lpdw[2]; + b = DbgReadMemory(hprcx, lpb, &sn, sizeof(sn), &cbRead); + + if ((b == 0) || (cbRead != sizeof(sn))) { + b = GetLastError(); + AddQueue( QT_CONTINUE_DEBUG_EVENT, + hthdx->hprc->pid, + hthdx->tid, + DBG_CONTINUE, + 0); + hthdx->tstate &= ~(ts_stopped|ts_first|ts_second); + hthdx->tstate |= ts_running; + return; + } + + if (sn.FileName[0] == 0) { + AddQueue( QT_CONTINUE_DEBUG_EVENT, + hthdx->hprc->pid, + hthdx->tid, + DBG_CONTINUE, + 0); + hthdx->tstate &= ~(ts_stopped|ts_first|ts_second); + hthdx->tstate |= ts_running; + return; + } + cb = strlen(sn.FileName)+1; + + idx = LookupDllName(hprcx, sn.FileName); + + if ( idx != -1 ) { + LPSLI lpsli; + + lprtp = (LPRTP) malloc( sizeof(RTP) + sizeof(SLI)); + *lprtp = rtp; + lprtp->dbc = dbceSegMove; + lprtp->cb = sizeof(SLI); + lpsli = (LPSLI) &lprtp->rgbVar[0]; + assert( sn.Selector1 == 0 ); + lpsli->wSelector = sn.Selector2; + lpsli->wSegNo = sn.Segment; + lpsli->mte = idx; + + DmTlFunc(tlfDebugPacket, rtp.hpid, lprtp->cb + sizeof(RTP), + (LONG)(LPV) lprtp); + + free( lprtp ); + } + + AddQueue( QT_CONTINUE_DEBUG_EVENT, + hthdx->hprc->pid, + hthdx->tid, + DBG_CONTINUE, + 0); + hthdx->tstate &= ~(ts_stopped|ts_first|ts_second); + hthdx->tstate |= ts_running; + + break; + + /* + * SEGFREE: + * + * This event is triggered if a selector is freed + * + * LOWORD( lpdw[0] ) - SEGFREE + * HIWORD( lpdw[0] ) - fBPRelease + * LOWORD( lpdw[1] ) - selector to be freed + */ + + case DBG_SEGFREE: + + AddQueue( QT_CONTINUE_DEBUG_EVENT, + hthdx->hprc->pid, + hthdx->tid, + DBG_CONTINUE, + 0); + hthdx->tstate &= ~(ts_stopped|ts_first|ts_second); + hthdx->tstate |= ts_running; + + break; + + /* + * MODLOAD: + * + * This event is triggered when a new DLL is loaded + * + * LOWORD( lpdw[0] ) - MODLOAD + * HIWORD( lpdw[0] ) - length of module name + * HIWORD( lpdw[1] ) - selector + * lpdw[2] - address of module name + * lpdw[3] - image length + * + */ + + case DBG_MODLOAD: + + lpb = (char *) lpdw[2]; + b = DbgReadMemory(hprcx, lpb, &sn, sizeof(sn), &cbRead); + + if ((b == 0) || (cbRead != sizeof(sn))) { + b = GetLastError(); + AddQueue( QT_CONTINUE_DEBUG_EVENT, + hthdx->hprc->pid, + hthdx->tid, + DBG_CONTINUE, + 0); + hthdx->tstate &= ~(ts_stopped|ts_first|ts_second); + hthdx->tstate |= ts_running; + return; + } + + if (sn.FileName[0] == 0) { + AddQueue( QT_CONTINUE_DEBUG_EVENT, + hthdx->hprc->pid, + hthdx->tid, + DBG_CONTINUE, + 0); + hthdx->tstate &= ~(ts_stopped|ts_first|ts_second); + hthdx->tstate |= ts_running; + return; + } + + cb = strlen(sn.FileName)+1; + idx = LookupDllName(hprcx, sn.FileName); + + if (idx == -1) { + cb = cb + sizeof(MODULELOAD); + lprtp = (LPRTP) malloc(cb + sizeof(RTP)); + memset(lprtp, 0, cb + sizeof(RTP)); + + *lprtp = rtp; + lprtp->dbc = dbcModLoad; + lprtp->cb = cb; + + lpmdl = (LPMODULELOAD) &(lprtp->rgbVar[0]); + lpmdl->cobj = 0; + + lpmdl->mte = InsertDllName(hprcx, sn.FileName); + + idx = LookupDllName(hprcx, sn.FileName); + + if ( idx != -1 ) { + hprcx->rgDllList[idx].fReal = TRUE; + } + + strcpy((char *) &lpmdl->rgobjd[lpmdl->cobj], sn.FileName); + + lpmdl->StartingSegment = sn.Segment; + + lpmdl->fRealMode = TRUE; + lpmdl->fFlatMode = FALSE; + lpmdl->fOffset32 = FALSE; + + DmTlFunc(tlfRequest, rtp.hpid, lprtp->cb + sizeof(RTP), + (LONG)(LPV) lprtp); + + hemi = *((HEMI *) abEMReplyBuf); + + free(lprtp); + } + + AddQueue( QT_CONTINUE_DEBUG_EVENT, + hthdx->hprc->pid, + hthdx->tid, + DBG_CONTINUE, + 0); + hthdx->tstate &= ~(ts_stopped|ts_first|ts_second); + hthdx->tstate |= ts_running; + + break; + + /* + * MODFREE: + * + * This event is triggered when a DLL is unloaded + * + * LOWORD( lpdw[0] ) - MODFREE + */ + + case DBG_MODFREE: + lpb = (char *) lpdw[2]; + b = DbgReadMemory(hprcx, lpb, &sn, sizeof(sn), &cbRead); + + if ((b == 0) || (cbRead != sizeof(sn))) { + b = GetLastError(); + AddQueue( QT_CONTINUE_DEBUG_EVENT, + hthdx->hprc->pid, + hthdx->tid, + DBG_CONTINUE, + 0); + hthdx->tstate &= ~(ts_stopped|ts_first|ts_second); + hthdx->tstate |= ts_running; + return; + } + + if (sn.FileName[0] == 0) { + AddQueue( QT_CONTINUE_DEBUG_EVENT, + hthdx->hprc->pid, + hthdx->tid, + DBG_CONTINUE, + 0); + hthdx->tstate &= ~(ts_stopped|ts_first|ts_second); + hthdx->tstate |= ts_running; + return; + } + cb = strlen(sn.FileName)+1; + + idx = LookupDllName(hprcx, sn.FileName); + + if (idx != -1) { + lprtp = (LPRTP) rgch; + *lprtp = rtp; + lprtp->dbc = dbceModFree16; + lprtp->cb = sizeof(WORD); + *((WORD *)(&lprtp->rgbVar[0])) = idx; + + DmTlFunc(tlfDebugPacket, rtp.hpid, lprtp->cb + sizeof(RTP), + (LONG)(LPV) lprtp); + + RemoveDllName( hprcx, idx ); + } + + AddQueue( QT_CONTINUE_DEBUG_EVENT, + hthdx->hprc->pid, + hthdx->tid, + DBG_CONTINUE, + 0); + hthdx->tstate &= ~(ts_stopped|ts_first|ts_second); + hthdx->tstate |= ts_running; + + break; + + /* + * Int 01h break; + */ + + case DBG_SINGLESTEP: + hthdx->context.ContextFlags = VDMCONTEXT_FULL; + VDMGetThreadContext(pde, &hthdx->context); + + eventCode = EXCEPTION_DEBUG_EVENT; + pde->dwDebugEventCode = EXCEPTION_DEBUG_EVENT; + pde->u.Exception.ExceptionRecord.ExceptionCode = subClass = + (DWORD)EXCEPTION_SINGLE_STEP; + + /* + * They are not clearing the trace bit + */ + + hthdx->context.EFlags &= ~TF_BIT_MASK; + hthdx->fContextDirty = TRUE; + + AddrInit(&addr, 0, (SEGMENT) hthdx->context.SegCs, hthdx->context.Eip, + FALSE, FALSE, FALSE, FALSE); + + bp = FindBP(hthdx->hprc, hthdx, bptpExec, (BPNS)-1, &addr, FALSE); + + if ( bp ) { + SetBPFlag( hthdx, bp ); + } + + goto dispatch; + + case DBG_TASKSTART: + hthdx->context.ContextFlags = VDMCONTEXT_FULL; + VDMGetThreadContext(pde, &hthdx->context); + + eventCode = + pde->dwDebugEventCode = ENTRYPOINT_DEBUG_EVENT; + + goto dispatch; + + case DBG_TASKSTOP: + case DBG_DLLSTART: + case DBG_DLLSTOP: + case DBG_ATTACH: + AddQueue( QT_CONTINUE_DEBUG_EVENT, + hthdx->hprc->pid, + hthdx->tid, + DBG_CONTINUE, + 0); + hthdx->tstate &= ~(ts_stopped|ts_first|ts_second); + hthdx->tstate |= ts_running; + break; + + + /* + * Int 03h break + * + * LOWORD(lpdw[0]) --- BREAKPOINT + * HIWORD(lpdw[0]) --- Protect Mode + */ + + case DBG_BREAK: + hthdx->context.ContextFlags = VDMCONTEXT_FULL; + VDMGetThreadContext(pde, &hthdx->context); + + PC(hthdx) -= 1; + hthdx->fContextDirty = TRUE; + + eventCode = pde->dwDebugEventCode = BREAKPOINT_DEBUG_EVENT; + + // NOTENOTE --- jimsch -- assuming only 0xcc not 0xcd 0x3 breakpoints + + AddrInit(&addr, 0, (SEGMENT) hthdx->context.SegCs, hthdx->context.Eip, + FALSE, FALSE, FALSE, FALSE); + + bp = FindBP(hthdx->hprc, hthdx, bptpExec, (BPNS)-1, &addr, FALSE); + + if ( bp && bp->isStep ) { + + eventCode = EXCEPTION_DEBUG_EVENT; + pde->dwDebugEventCode = EXCEPTION_DEBUG_EVENT; + + pde->u.Exception.ExceptionRecord.ExceptionCode = + subClass = (DWORD)EXCEPTION_SINGLE_STEP; + RemoveBP(bp); + + } else { + + if ( bp ) { + SetBPFlag( hthdx, bp ); + } + + pde->u.Exception.ExceptionRecord.ExceptionCode = + subClass = (DWORD)bp; + } + + pde->u.Exception.ExceptionRecord.ExceptionAddress = (LPVOID) PC(hthdx); + + dispatch: + hthdx->fAddrIsReal = FALSE; + hthdx->fAddrIsFlat = FALSE; + + if (VDMGetThreadSelectorEntry(pde, + hthdx->rwHand, + (WORD) hthdx->context.SegCs, + &ldt)) { + if (ldt.HighWord.Bits.Default_Big) { + hthdx->fAddrOff32 = TRUE; + } else { + hthdx->fAddrOff32 = FALSE; + } + } else { + hthdx->fAddrOff32 = FALSE; + } + + /* + * Check if this debug event was expected + */ + + pee = PeeIsEventExpected(hthdx, eventCode, subClass); + + /* + * If it wasn't, run the standard handler with + * notifications going to the execution model + */ + + assert((0 < eventCode) && (eventCode < MAX_EVENT_CODE)); + + if (pee == NULL) { + if ((hthdx != NULL) && (hthdx->tstate & ts_funceval)) { + RgfnFuncEventDispatch[eventCode-EXCEPTION_DEBUG_EVENT](pde, hthdx); + } else { + DebugDispatchTable[eventCode-EXCEPTION_DEBUG_EVENT](pde,hthdx); + } + return; + } + + + /* + * If it was expected then call the action + * function if one was specified + */ + + if (pee->action) { + (pee->action)(pde, hthdx, 0, pee->lparam); + } + + /* + * And call the notifier if one was specified + */ + + if (pee->notifier) { + METHOD *nm = pee->notifier; + (nm->notifyFunction)(pde, hthdx, 0, nm->lparam); + } + + free(pee); + break; + +#if 0 // why is this here?? + case DBG_DIVOVERFLOW: + pde->dwDebugEventCode = 3; + goto fault_occured; + + case DBG_INSTRFAULT: + pde->dwDebugEventCode = 1; + goto fault_occured; +#endif + + case DBG_DIVOVERFLOW: + case DBG_INSTRFAULT: + case DBG_GPFAULT: + pde->dwDebugEventCode = EXCEPTION_DEBUG_EVENT; + +#if 0 // why is this here?? + fault_occured: +#endif + + hthdx->context.ContextFlags = VDMCONTEXT_FULL; + VDMGetThreadContext(pde, &hthdx->context); + pde->u.Exception.ExceptionRecord.ExceptionCode = 13; + + hthdx->fAddrIsReal = FALSE; + hthdx->fAddrIsFlat = FALSE; + + if (VDMGetThreadSelectorEntry(pde, hthdx->rwHand, + (WORD) hthdx->context.SegCs, &ldt)) { + if (ldt.HighWord.Bits.Default_Big) { + hthdx->fAddrOff32 = TRUE; + } else { + hthdx->fAddrOff32 = FALSE; + } + } else { + hthdx->fAddrOff32 = FALSE; + } + + ProcessExceptionEvent(pde, hthdx); + break; + + default: + AddQueue( QT_CONTINUE_DEBUG_EVENT, + hthdx->hprc->pid, + hthdx->tid, + DBG_CONTINUE, + 0); + hthdx->tstate &= ~(ts_stopped|ts_first|ts_second); + hthdx->tstate |= ts_running; + break; + } +#endif // i386 +#endif // i386 && !Win32S + return; +} /* ProcessSegmentLoadEvent() */ + + +#if defined(i386) && !defined(WIN32S) + +int +LookupDllName( + HPRCX hprcx, + char * sz + ) + +/*++ + +Routine Description: + + This routine will snoop through the structures of an exe and determine + if we have previously seen this dll name for this process. + +Arguments: + + hprcx - Supplies a handle to a process + sz - Supplies a pointer to a DLL file name + . + +Return Value: + + The index in the array if present. Else -1 + +--*/ + +{ + int i; + + /* + * If there is no list then the item is not on the list + */ + + if (hprcx->rgDllList == NULL) { + return -1; + } + + /* + * Check each item in the list + */ + + for (i=0; i<hprcx->cDllList; i++) { + if ((hprcx->rgDllList[i].fValidDll) && + (hprcx->rgDllList[i].szDllName != NULL) && + (strcmp(hprcx->rgDllList[i].szDllName, sz) == 0)) { + return i; + } + } + return -1; +} /* LookupDllName() */ + + +int +InsertDllName( + HPRCX hprcx, + char * sz + ) + +/*++ + +Routine Description: + + This routine will take a dll name and an mte and put then into the + list of dlls for the current process. + +Arguments: + + hprcx - Supplies a handle to a process + sz - Supplies a pointer to a DLL file name + +Return Value: + + The index in the array where it was placed. + +--*/ + +{ + int i; + int cb; + int cItem; + + /* + * If there is no list then make a list + */ + + if (hprcx->rgDllList == NULL) { + hprcx->rgDllList = (PDLLLOAD_ITEM) malloc(sizeof(DLLLOAD_ITEM) * 10); + memset(hprcx->rgDllList, 0, sizeof(DLLLOAD_ITEM)*10); + } + + /* + * Check each item in the list + */ + + for (i=0; i<hprcx->cDllList; i++) { + if ((hprcx->rgDllList[i].fValidDll == FALSE) && + (hprcx->rgDllList[i].szDllName == NULL)) { + hprcx->rgDllList[i].szDllName = _strdup(sz); + hprcx->rgDllList[i].fValidDll = TRUE; + hprcx->rgDllList[i].fWow = TRUE; + return i; + } + } + + cItem = hprcx->cDllList; + if (i == cItem) { + cb = i * sizeof(DLLLOAD_ITEM); + cItem += 10; + hprcx->rgDllList = (PDLLLOAD_ITEM) realloc(hprcx->rgDllList, cItem*sizeof(DLLLOAD_ITEM)); + memset(cb + (char *) hprcx->rgDllList, 0, 10*sizeof(DLLLOAD_ITEM)); + } + + + hprcx->rgDllList[i].szDllName = _strdup(sz); + hprcx->rgDllList[i].fValidDll = TRUE; + hprcx->rgDllList[i].fWow = TRUE; + hprcx->cDllList += 10; + + return i; +} /* InsertDllName() */ + + + +void +RemoveDllName( + HPRCX hprcx, + int idx + ) + +/*++ + +Routine Description: + + This routine will remove a dll from the internal list of known dlls. + +Arguments: + + hprcx - Supplies the handle to the current process + idx - supplies the index of the dll to be removed + +Return Value: + + None. + +--*/ + +{ + assert( hprcx->rgDllList != NULL ); + + free(hprcx->rgDllList[idx].szDllName); + hprcx->rgDllList[idx].szDllName = NULL; + hprcx->rgDllList[idx].fValidDll = FALSE; + hprcx->rgDllList[idx].fReal = FALSE; + hprcx->rgDllList[idx].fWow = FALSE; + + return; +} /* RemoveDllName() */ + + +#endif // i386 && !WIN32S + +/*++ + +Routine Description: + + Callback for VDMEnumProcessWOW + +Arguments: + + See PROCESSENUMPROC in vdmdbg.h + +Return Value: + + TRUE + +--*/ +BOOL WINAPI EnumCallBack( + DWORD dwProcessId, + DWORD dwAttributes, + LPARAM lpUserDefined + ) +{ + UNREFERENCED_PARAMETER( dwProcessId ); + UNREFERENCED_PARAMETER( dwAttributes ); + UNREFERENCED_PARAMETER( lpUserDefined ); + + return TRUE; +} + + + +BOOL +IsWOWPresent( + VOID + ) + +/*++ + +Routine Description: + + Determines if WOW is running + +Arguments: + + None + +Return Value: + + TRUE if WOW is running, FALSE otherwise + +--*/ + +{ +#if defined(i386) && !defined(WIN32S) + return VDMEnumProcessWOW( (PROCESSENUMPROC)EnumCallBack, 0 ); +#else + return FALSE; +#endif +} + + + +BOOL +TranslateAddress( + HPRCX hprc, + HTHDX hthd, + LPADDR lpaddr, + BOOL f16ToFlat + ) + +/*++ + +Routine Description: + + This function is used to preform address translations from the segmented + to the flat world and back again. + +Arguments: + + hprc - Supplies the handle to the current process + hthd - Supplies the handle to the thread for the address + lpaddr - Supplies the address to be translated + f16ToFlat - Supplies the direction the translation is to be made + +Return Value: + + TRUE on success and FALSE on failure + +--*/ + +{ +#if defined(i386) + LDT_ENTRY ldt; + ULONG ul; + +#ifdef WIN32S + ADDR_IS_FLAT(*lpaddr) = TRUE; + ADDR_IS_REAL(*lpaddr) = FALSE; + return TRUE; +#endif + + /* + * Step 0. If the address has already been mapped flat then return + */ + + if (ADDR_IS_FLAT(*lpaddr)) { + return TRUE; + } + + /* + * Step 1. Is to find a stopped thread. This is mainly for WOW support + * where the lack of a stopped thread is a serious thing, + * we can not do anything smartly with wow if this is the + * case. We will currently only search for a single + * stopped thread cause the blasted operating system won't + * let us have more than one. + */ + + if (hthd == 0) { + for (hthd = hprc->hthdChild; hthd != hthdxNull; hthd = hthd->next) { + if (hthd->tstate & ts_stopped) { + break; + } + } + + if (hthd == 0) { + hthd = hprc->hthdChild; + } + } + + /* + * Must have a thread + */ + + if (hthd == NULL) { + return FALSE; + } + + /* + * Step 2. Depending on if the last event was WOW we need to + * either go in with a WOW remap or do the standard + * non-wow remap + */ + + if (hthd->fAddrIsFlat) { + if (ADDR_IS_REAL( *lpaddr )) { + ul = GetAddrSeg(*lpaddr) * 16 + GetAddrOff(*lpaddr); + lpaddr->addr.off = ul; + } else if (GetThreadSelectorEntry(hthd->rwHand, GetAddrSeg(*lpaddr), + &ldt)) { + ul = (ldt.HighWord.Bytes.BaseHi << 24) | + (ldt.HighWord.Bytes.BaseMid << 16) | + (ldt.BaseLow); + lpaddr->addr.off += ul; + } else { + /* + * Unrecognized selector + */ + return FALSE; + } + } else { +#if defined(i386) && !defined(WIN32S) + lpaddr->addr.off = VDMGetPointer(hprc->rwHand, hthd->rwHand, + GetAddrSeg(*lpaddr), + GetAddrOff(*lpaddr), + !lpaddr->mode.fReal); +#else // defined(i386) && !defined(WIN32S) + assert(FALSE); + return FALSE; +#endif // defined(i386) && !defined(WIN32S) + } +#endif + ADDR_IS_FLAT(*lpaddr) = TRUE; + ADDR_IS_REAL(*lpaddr) = FALSE; + return TRUE; +} /* TranslateAddress() */ + + +BOOL +WOWGetThreadContext( + HTHDX hthdx, + LPCONTEXT lpcxt + ) + +/*++ + +Routine Description: + + This routine is called to g the context of a WOW thread. We have + a current assumption that we will only have one WOW event at a time. + +Arguments: + + hthdx - supplies the handle to the thread to change the context of + lpcxt - supplies the new context. + +Return Value: + + TRUE on success and FALSE on failure + +--*/ + +{ +#if defined(i386) && !defined(WIN32S) + assert(hthdx->fWowEvent); + + if (hthdx->tid != DeWow.dwThreadId) { + assert(FALSE); + return FALSE; + } + + return VDMGetThreadContext(&DeWow, lpcxt); +#else + return FALSE; +#endif +} /* WOWGetThreadContext() */ + + + +BOOL +WOWSetThreadContext( + HTHDX hthdx, + LPCONTEXT lpcxt + ) + +/*++ + +Routine Description: + + This routine is called to set the context of a WOW thread. We have + a current assumption that we will only have one WOW event at a time. + +Arguments: + + hthdx - supplies the handle to the thread to change the context of + lpcxt - supplies the new context. + +Return Value: + + TRUE on success and FALSE on failure + +--*/ + +{ +#if defined(i386) && !defined(WIN32S) + assert(hthdx->fWowEvent); + + if (hthdx->tid != DeWow.dwThreadId) { + assert(FALSE); + return FALSE; + } + + return VDMSetThreadContext(&DeWow, lpcxt); +#else + return FALSE; +#endif +} /* WOWSetThreadContext() */ |