mirror of https://github.com/lianthony/NT4.0
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.
2041 lines
46 KiB
2041 lines
46 KiB
#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 );
|
|
}
|