4064 lines
107 KiB
C
4064 lines
107 KiB
C
#include "precomp.h"
|
|
#pragma hdrstop
|
|
|
|
#include <wdbgexts.h>
|
|
|
|
#include "dmsql.h"
|
|
#include "fbrdbg.h"
|
|
#include "fiber.h"
|
|
|
|
HANDLE hEventContinue;
|
|
extern LPDM_MSG LpDmMsg;
|
|
extern SYSTEM_INFO SystemInfo;
|
|
extern WT_STRUCT WtStruct; // .. for wt
|
|
extern DEBUG_ACTIVE_STRUCT DebugActiveStruct; // ... for DebugActiveProcess()
|
|
extern RELOAD_STRUCT ReloadStruct; // for !reload, usermode
|
|
|
|
extern PKILLSTRUCT KillQueue;
|
|
extern CRITICAL_SECTION csKillQueue;
|
|
extern CRITICAL_SECTION csProcessDebugEvent;
|
|
|
|
extern HTHDX thdList;
|
|
extern HPRCX prcList;
|
|
extern CRITICAL_SECTION csThreadProcList;
|
|
|
|
extern BOOL fSmartRangeStep;
|
|
extern HANDLE hEventNoDebuggee;
|
|
extern HANDLE hEventRemoteQuit;
|
|
extern char nameBuffer[];
|
|
extern DEBUG_EVENT64 falseSSEvent;
|
|
|
|
extern BOOL fDisconnected;
|
|
extern BOOL fUseRoot;
|
|
extern BOOL FLoading16;
|
|
extern BOOL FDMRemote;
|
|
extern BOOL fUseRealName;
|
|
|
|
|
|
BOOL
|
|
DbgWriteMemory(
|
|
HPRCX hprc,
|
|
ULONG64 qwOffset,
|
|
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;
|
|
LPVOID lpOffset = (LPVOID)qwOffset;
|
|
|
|
assert(hprc->rwHand != (HANDLE)-1);
|
|
|
|
// For now we will not modify fibers
|
|
/*if ((hprc->pFbrCntx) && (hprc->fUseFbrs)) {
|
|
return FALSE;
|
|
}*/
|
|
|
|
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, ULONG64 qwOffset, 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
|
|
--*/
|
|
{
|
|
DWORD cbr;
|
|
LPVOID lpOffset = (LPVOID)qwOffset;
|
|
|
|
assert(Is64PtrSE(qwOffset));
|
|
|
|
if (CrashDump) {
|
|
cbr = DmpReadMemory(qwOffset, lpb, cb);
|
|
if (lpRead) {
|
|
*lpRead = cbr;
|
|
}
|
|
return (cbr > 0) || (cbr == cb);
|
|
}
|
|
|
|
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 (~(DWORD_PTR)(PAGE_SIZE-1))
|
|
|
|
DWORD firstsize;
|
|
DWORD dwRead;
|
|
|
|
firstsize = (DWORD)((((DWORD_PTR)lpOffset + PAGE_SIZE) & PAGE_MASK) - (DWORD_PTR)lpOffset);
|
|
if (cb < firstsize) {
|
|
firstsize = cb;
|
|
}
|
|
|
|
DPRINT(0, ("ReadMemory @%p %i bytes failed. Error:%i Now trying reading %i bytes\n", lpOffset, cb, e, firstsize));
|
|
|
|
// read from the first page. If the first read fails,
|
|
// fail the whole thing.
|
|
|
|
|
|
if (!ReadProcessMemory(hprc->rwHand, lpOffset, lpb, firstsize, lpRead)) {
|
|
DPRINT(0, ("ReadMemory @%p %i bytes failed. Error:%i \n", lpOffset, firstsize, GetLastError()));
|
|
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)
|
|
{
|
|
if (CrashDump) {
|
|
return DmpGetContext(hthd->tid - 1, lpcontext);
|
|
} else if (hthd->fWowEvent) {
|
|
return WOWGetThreadContext(hthd, lpcontext);
|
|
} else {
|
|
if ((hthd->hprc->pFbrCntx) && (hthd->hprc->fUseFbrs)) {
|
|
DWORD lpRead;
|
|
DbgReadMemory(hthd->hprc,
|
|
(ULONG64)hthd->hprc->pFbrCntx,
|
|
lpcontext,
|
|
sizeof(CONTEXT),
|
|
&lpRead);
|
|
if (lpRead == sizeof(CONTEXT)) {
|
|
return TRUE;
|
|
} else {
|
|
return FALSE;
|
|
}
|
|
}
|
|
#if defined(TARGET_IA64)
|
|
{
|
|
|
|
// v-vadimp - on IA64 CONTEXT has to be 16 byte aligned, while
|
|
// what we may get here is a member of HTHDX (or whatever else)
|
|
// where it may not be (compiler bug?), so make a local CONTEXT
|
|
// var which will be aligned by the compiler, and do the call
|
|
// with it
|
|
|
|
CONTEXT Context = {0};
|
|
BOOL i;
|
|
Context.ContextFlags = lpcontext->ContextFlags;
|
|
DPRINT(1, ("GetThreadContext:Thread %p\n", hthd->rwHand));
|
|
i = GetThreadContext(hthd->rwHand, &Context);
|
|
DPRINT(1, ("GetThreadContext:%i-%i\n", i, GetLastError()));
|
|
memcpy((LPVOID)lpcontext, (LPVOID)&Context, sizeof(CONTEXT));
|
|
DPRINT(1, ("Returned context:IIP:%016I64x IPSR:%016I64x Sp:%016I64x\n",
|
|
lpcontext->StIIP,
|
|
lpcontext->StIPSR,
|
|
lpcontext->IntSp));
|
|
return i;
|
|
}
|
|
#endif
|
|
return GetThreadContext(hthd->rwHand, lpcontext);
|
|
}
|
|
}
|
|
|
|
BOOL DbgSetThreadContext(HTHDX hthd, PCONTEXT lpcontext)
|
|
{
|
|
assert(!CrashDump);
|
|
|
|
if (CrashDump) {
|
|
return FALSE;
|
|
} else if (hthd->fWowEvent) {
|
|
return WOWSetThreadContext(hthd, lpcontext);
|
|
} else {
|
|
#if defined (TARGET_IA64)
|
|
{ // see comment in DbgGetThreadContext
|
|
CONTEXT Context = {0};
|
|
BOOL i;
|
|
memcpy(&Context, lpcontext, sizeof(CONTEXT));
|
|
i = SetThreadContext(hthd->rwHand, &Context);
|
|
DPRINT(1, ("Setting context:IIP:%016I64x IPSR:%016I64x Sp:%016I64x:%i:%i\n",
|
|
Context.StIIP,
|
|
Context.StIPSR,
|
|
Context.IntSp,
|
|
i,
|
|
GetLastError()));
|
|
return i;
|
|
}
|
|
#endif
|
|
return SetThreadContext(hthd->rwHand, lpcontext);
|
|
}
|
|
}
|
|
|
|
BOOL WriteBreakPoint(IN PBREAKPOINT Breakpoint)
|
|
{
|
|
DWORD cb;
|
|
BP_UNIT opcode = BP_OPCODE;
|
|
#if defined(TARGET_IA64)
|
|
BOOL r;
|
|
BP_UNIT Content;
|
|
r = AddrReadMemory(Breakpoint->hprc, Breakpoint->hthd, &Breakpoint->addr, &Content, BP_SIZE, &cb);
|
|
|
|
switch (GetAddrOff(Breakpoint->addr) & 0xf) {
|
|
case 0:
|
|
Content = (Content & ~(INST_SLOT0_MASK)) | (opcode << 5);
|
|
break;
|
|
case 4:
|
|
Content = (Content & ~(INST_SLOT1_MASK)) | (opcode << 14);
|
|
break;
|
|
case 8:
|
|
Content = (Content & ~(INST_SLOT2_MASK)) | (opcode << 23);
|
|
break;
|
|
default:
|
|
assert(!"Bad bundle slot - breakpoint not written");
|
|
}
|
|
|
|
DPRINT(0, ("Writing BP @ %I64x; Content:%I64x\n",
|
|
GetAddrOff(Breakpoint->addr),
|
|
(ULONG64)Content
|
|
));
|
|
|
|
r = AddrWriteMemory(Breakpoint->hprc, Breakpoint->hthd, &Breakpoint->addr, &Content, BP_SIZE, &cb);
|
|
|
|
// read in intruction template if current instruction is slot 2.
|
|
// check for two-slot MOVL instruction. Reject request if attempt to
|
|
// set break in slot 2 of MLI template.
|
|
if ((GetAddrOff(Breakpoint->addr) & 0xf) != 0) {
|
|
ADDR BundleAddr = Breakpoint->addr;
|
|
GetAddrOff(BundleAddr) &= ~0xf;
|
|
r = AddrReadMemory(Breakpoint->hprc, Breakpoint->hthd, &BundleAddr, &Content, BP_SIZE, &cb);
|
|
if (((Content & INST_TEMPL_MASK) >> 1) == 0x2) {
|
|
if ((GetAddrOff(Breakpoint->addr) & 0xf) == 4) {
|
|
// if template= type 2 MLI, change to type 0
|
|
Content &= ~((INST_TEMPL_MASK >> 1) << 1);
|
|
Breakpoint->flags |= BREAKPOINT_IA64_MOVL;
|
|
r = AddrWriteMemory(Breakpoint->hprc, Breakpoint->hthd, &BundleAddr, &Content, BP_SIZE, &cb);
|
|
} else {
|
|
// set breakpoint at slot 2 of MOVL is illegal
|
|
assert(!"Attempting to set a BP on the second slot of MOVL");
|
|
return 0;
|
|
}
|
|
}
|
|
}
|
|
#else
|
|
BOOL r = AddrWriteMemory(Breakpoint->hprc,
|
|
Breakpoint->hthd,
|
|
&Breakpoint->addr,
|
|
&opcode,
|
|
BP_SIZE,
|
|
&cb);
|
|
#endif
|
|
return (r && (cb == BP_SIZE));
|
|
}
|
|
|
|
BOOL RestoreBreakPoint(IN PBREAKPOINT Breakpoint)
|
|
{
|
|
DWORD cb;
|
|
BOOL r;
|
|
|
|
assert(Breakpoint->bpType == bptpExec || Breakpoint->bpType == bptpMessage);
|
|
|
|
#if defined(TARGET_IA64)
|
|
{
|
|
BP_UNIT Content;
|
|
ADDR BundleAddr;
|
|
DWORD k;
|
|
|
|
// Read in memory since adjancent instructions in the same bundle may have
|
|
// been modified after we save them. Restore only the content of the slot which has
|
|
// the break instruction inserted.
|
|
|
|
DPRINT(0, ("Restoring BP @ %p\n", GetAddrOff(Breakpoint->addr)));
|
|
if (!AddrReadMemory(Breakpoint->hprc, Breakpoint->hthd, &Breakpoint->addr, (LPBYTE)&Content, BP_SIZE, &cb)) {
|
|
return FALSE;
|
|
}
|
|
|
|
switch (GetAddrOff(Breakpoint->addr) & 0xf) {
|
|
case 0:
|
|
Content = (Content & ~(INST_SLOT0_MASK)) | (Breakpoint->instr1 & INST_SLOT0_MASK);
|
|
break;
|
|
case 4:
|
|
Content = (Content & ~(INST_SLOT1_MASK)) | (Breakpoint->instr1 & INST_SLOT1_MASK);
|
|
break;
|
|
case 8:
|
|
Content = (Content & ~(INST_SLOT2_MASK)) | (Breakpoint->instr1 & INST_SLOT2_MASK);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
if (!(r = AddrWriteMemory(Breakpoint->hprc, Breakpoint->hthd, &Breakpoint->addr, (LPBYTE)&Content, BP_SIZE, &cb))) {
|
|
return FALSE;
|
|
}
|
|
|
|
// restore template to MLI if displaced instruction was MOVL
|
|
if (Breakpoint->flags & BREAKPOINT_IA64_MOVL) {
|
|
BundleAddr.addr.off = GetAddrOff(Breakpoint->addr) & ~(0xf);
|
|
if (!(AddrReadMemory(Breakpoint->hprc, Breakpoint->hthd, &BundleAddr, (LPBYTE)&Content, BP_SIZE, &k))) {
|
|
return FALSE;
|
|
}
|
|
Content &= ~((INST_TEMPL_MASK >> 1) << 1); // set template to MLI
|
|
Content |= 0x4;
|
|
if (!(AddrWriteMemory(Breakpoint->hprc, Breakpoint->hthd, &BundleAddr, (LPBYTE)&Content, BP_SIZE, &k))) {
|
|
return FALSE;
|
|
}
|
|
}
|
|
}
|
|
#else
|
|
r = AddrWriteMemory(Breakpoint->hprc,
|
|
Breakpoint->hthd,
|
|
&Breakpoint->addr,
|
|
&Breakpoint->instr1,
|
|
BP_SIZE,
|
|
&cb);
|
|
#endif
|
|
return (r && (cb == BP_SIZE));
|
|
}
|
|
|
|
|
|
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_ALPHA:
|
|
p->Level = SystemInfo.wProcessorLevel;
|
|
p->Type = mptdaxp;
|
|
p->Endian = endLittle;
|
|
p->Level = 21064; // BUGBUG - why?
|
|
break;
|
|
case PROCESSOR_ARCHITECTURE_ALPHA64:
|
|
p->Level = SystemInfo.wProcessorLevel;
|
|
p->Type = mptdaxp;
|
|
p->Endian = endLittle;
|
|
break;
|
|
case PROCESSOR_ARCHITECTURE_IA64:
|
|
p->Level = SystemInfo.wProcessorLevel;
|
|
p->Type = mptia64;
|
|
p->Endian = endLittle;
|
|
break;
|
|
default:
|
|
assert(!"Unknown target machine");
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
typedef struct
|
|
{
|
|
PID pidWanted;
|
|
HWND hFound;
|
|
} FWINDOWSTRUCT;
|
|
|
|
static BOOL CALLBACK EnumAllWindows(HWND hwnd, LPARAM lParam)
|
|
{
|
|
PID pid;
|
|
FWINDOWSTRUCT * pfWindow = (FWINDOWSTRUCT *)lParam;
|
|
|
|
// what we want is windows *without* an owner, hence !GetWindow...
|
|
// and visible windows
|
|
// and those owned by the debuggee
|
|
|
|
if (
|
|
!GetWindow(hwnd, GW_OWNER) &&
|
|
IsWindowVisible(hwnd) &&
|
|
(GetWindowThreadProcessId(hwnd, &pid), (pid == pfWindow->pidWanted))
|
|
) {
|
|
pfWindow->hFound = hwnd;
|
|
return FALSE; // found it so don't enum any more
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
// find the window associated with the debuggee
|
|
// Original code used GetWindow( GW_HWNDFIRST / GW_HWNDNEXT ) but this would
|
|
// often, for no reason, fail to give us the debuggee's window. New version uses
|
|
// EnumWindows to find it, which seems much more reliable
|
|
|
|
HWND HwndFromPid(PID pid)
|
|
{
|
|
FWINDOWSTRUCT fWindow;
|
|
fWindow.pidWanted = pid;
|
|
fWindow.hFound = NULL;
|
|
|
|
EnumWindows(EnumAllWindows, (LPARAM)&fWindow);
|
|
return fWindow.hFound;
|
|
}
|
|
|
|
|
|
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
|
|
--*/
|
|
{
|
|
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);
|
|
|
|
return;
|
|
} /* QueueContinueDebugEvent() */
|
|
|
|
|
|
BOOL DequeueContinueDebugEvents(VOID)
|
|
/*++
|
|
Routine Description:
|
|
Remove any pending continues from the queue and execute them. Updates the
|
|
threads' context if necessary.
|
|
|
|
NOTE: the lpcq->tid parameter MUST BE the thread that the debug event
|
|
occured on.
|
|
|
|
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) {
|
|
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() */
|
|
|
|
|
|
VOID
|
|
AddQueue(
|
|
DWORD dwType,
|
|
DWORD dwProcessId,
|
|
DWORD dwThreadId,
|
|
DWORD64 dwData,
|
|
DWORD dwLen
|
|
)
|
|
{
|
|
switch (dwType) {
|
|
case QT_CONTINUE_DEBUG_EVENT:
|
|
case QT_TRACE_DEBUG_EVENT:
|
|
if (!CrashDump) {
|
|
QueueContinueDebugEvent(dwProcessId, dwThreadId, (DWORD)dwData);
|
|
}
|
|
break;
|
|
case QT_RELOAD_MODULES:
|
|
if (!ReloadStruct.Flag) {
|
|
ReloadStruct.Hthd = HTHDXFromPIDTID(dwProcessId, dwThreadId);
|
|
ReloadStruct.String = malloc(dwLen);
|
|
_tcscpy(ReloadStruct.String, (PUCHAR)dwData);
|
|
ReloadStruct.Flag = 1;
|
|
}
|
|
break;
|
|
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);
|
|
}
|
|
}
|
|
|
|
|
|
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
|
|
)
|
|
{
|
|
return DequeueContinueDebugEvents();
|
|
}
|
|
|
|
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
|
|
--*/
|
|
{
|
|
XOSD xosd;
|
|
OFFSET offRgTls;
|
|
DWORD iTls;
|
|
LPADDR lpaddr = (LPADDR)LpDmMsg->rgb;
|
|
OFFSET offResult;
|
|
DWORD cb;
|
|
int iDll;
|
|
OFFSET offDll = *(OFFSET *)lpdbb->rgbVar;
|
|
|
|
assert(Is64PtrSE(offDll));
|
|
|
|
/*
|
|
* Read number 1. Get the pointer to the Thread Local Storage array.
|
|
*/
|
|
if ((DbgReadMemory(hprcx, hthdx->offTeb + 0x2c, &offRgTls, sizeof(OFFSET), &cb) == 0) || (cb != sizeof(OFFSET))) {
|
|
err:
|
|
xosd = xosdUnknown;
|
|
Reply(0, &xosd, lpdbb->hpid);
|
|
return;
|
|
}
|
|
|
|
assert(Is64PtrSE(offRgTls));
|
|
|
|
/*
|
|
* Read number 2. Get the TLS index for this dll
|
|
*/
|
|
|
|
for (iDll = 0; iDll < hprcx->cDllList; iDll += 1) {
|
|
if (hprcx->rgDllList[iDll].fValidDll) {
|
|
assert(Is64PtrSE(hprcx->rgDllList[iDll].offBaseOfImage));
|
|
if (hprcx->rgDllList[iDll].offBaseOfImage == offDll) {
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (iDll == hprcx->cDllList) {
|
|
goto err;
|
|
}
|
|
|
|
if (!DbgReadMemory(hprcx,
|
|
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, offRgTls + iTls * sizeof(OFFSET),
|
|
&offResult, sizeof(OFFSET), &cb) == 0) ||
|
|
(cb != sizeof(OFFSET))) {
|
|
goto err;
|
|
}
|
|
|
|
assert(Is64PtrSE(offResult));
|
|
|
|
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
|
|
--*/
|
|
{
|
|
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
|
|
|
|
xosd = xosdInvalidParameter;
|
|
|
|
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)) {
|
|
xosd = xosdBadProcess;
|
|
}
|
|
|
|
addr = *(LPADDR)(lpdbb->rgbVar);
|
|
|
|
if (!ADDR_IS_FLAT(addr)) {
|
|
fRet = TranslateAddress(hprc, 0, &addr, TRUE);
|
|
assert(fRet);
|
|
if (!fRet) {
|
|
xosd = xosdBadAddress;
|
|
goto reply;
|
|
}
|
|
}
|
|
|
|
{
|
|
MEMORY_BASIC_INFORMATION mbi;
|
|
LPMEMINFO lpmi;
|
|
|
|
dwSize = VirtualQueryEx(hprc->rwHand, (LPVOID)addr.addr.off, &mbi, sizeof(MEMORY_BASIC_INFORMATION));
|
|
if (dwSize != sizeof(MEMORY_BASIC_INFORMATION)) {
|
|
xosd = xosdUnknown;
|
|
goto reply;
|
|
}
|
|
|
|
// the structure layouts are different, so we have to copy by fields, even on 64 bit machines.
|
|
lpmi = (LPMEMINFO)LpDmMsg->rgb;
|
|
ZeroMemory(lpmi, sizeof(*lpmi));
|
|
|
|
// These should never have to be sign extended, but let's be safe.
|
|
lpmi->addr.addr.off = SEPtrTo64(mbi.BaseAddress);
|
|
lpmi->addrAllocBase.addr.off = SEPtrTo64(mbi.AllocationBase);
|
|
lpmi->dwAllocationProtect = mbi.AllocationProtect;
|
|
lpmi->uRegionSize = mbi.RegionSize;
|
|
lpmi->dwState = mbi.State;
|
|
lpmi->dwProtect = mbi.Protect;
|
|
lpmi->dwType = mbi.Protect;
|
|
}
|
|
|
|
reply:
|
|
LpDmMsg->xosdRet = xosd;
|
|
Reply(sizeof(MEMINFO), LpDmMsg, lpdbb->hpid);
|
|
} /* ProcessVirtualQueryCmd */
|
|
|
|
|
|
VOID ProcessGetDmInfoCmd(HPRCX hprc, LPDBB lpdbb, DWORD cb)
|
|
{
|
|
LPDMINFO lpi = (LPDMINFO)LpDmMsg->rgb;
|
|
|
|
LpDmMsg->xosdRet = xosdNone;
|
|
|
|
lpi->mAsync = asyncRun | asyncMem | asyncStop | asyncBP | asyncKill | asyncWP | asyncSpawn;
|
|
lpi->fHasThreads = 1;
|
|
lpi->fReturnStep = 0;
|
|
//lpi->fRemote = ???
|
|
#ifdef TARGET_i386
|
|
lpi->fAlwaysFlat = 0;
|
|
#else
|
|
lpi->fAlwaysFlat = 1;
|
|
#endif
|
|
lpi->fHasReload = 1;
|
|
lpi->fNonLocalGoto = 1;
|
|
lpi->fKernelMode = 0;
|
|
|
|
lpi->cbSpecialRegs = 0;
|
|
lpi->MajorVersion = 0;
|
|
lpi->MinorVersion = 0;
|
|
|
|
lpi->Breakpoints = bptsExec | bptsDataC | bptsDataW | bptsDataR | bptsDataExec;
|
|
|
|
GetMachineType(&lpi->Processor);
|
|
|
|
lpi->fDMInfoCacheable = TRUE;
|
|
|
|
|
|
// hack so that TL can call tlfGetVersion before
|
|
// reply buffer is initialized.
|
|
|
|
if (cb >= (FIELD_OFFSET(DBB, rgbVar) + sizeof(DMINFO))) {
|
|
memcpy(lpdbb->rgbVar, lpi, sizeof(DMINFO));
|
|
}
|
|
|
|
Reply(sizeof(DMINFO), LpDmMsg, lpdbb->hpid);
|
|
} /* ProcessGetDMInfoCmd */
|
|
|
|
|
|
VOID ActionFunctionCallComplete(LPDEBUG_EVENT64 pde, HTHDX hthd, DWORDLONG unused, DWORDLONG lparam)
|
|
/*++
|
|
Routine Description:
|
|
This is the action function which is always called when a debuggee
|
|
function call which was initiated by CallFunction completes.
|
|
|
|
This retrieves the return value, if any, restores the thread context,
|
|
and calls the function provided to CallFunction.
|
|
--*/
|
|
{
|
|
PCALLSTRUCT pcs = (PCALLSTRUCT)lparam;
|
|
DWORDLONG qw = pcs->HasReturnValue ? GetFunctionResult(pcs) : 0;
|
|
ACVECTOR Action = pcs->Action;
|
|
DWORDLONG lparam1 = pcs->lparam;
|
|
|
|
RemoveBP(pcs->pbp);
|
|
|
|
hthd->context = pcs->context;
|
|
hthd->fContextDirty = TRUE;
|
|
hthd->pcs = NULL;
|
|
free(pcs);
|
|
|
|
if (Action) {
|
|
(*Action)(pde, hthd, qw, lparam1);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
BOOL
|
|
WINAPIV
|
|
CallFunction(
|
|
HTHDX hthd,
|
|
ACVECTOR Action,
|
|
LPARAM lparam,
|
|
BOOL HasReturnValue,
|
|
DWORDLONG Function,
|
|
int cArgs,
|
|
...
|
|
)
|
|
/*++
|
|
Routine Description:
|
|
Call a function in the debuggee. When the function returns, the action
|
|
function will be called with the return value.
|
|
Arguments:
|
|
hthd - Supplies the thread that will be used
|
|
Action - Supplies the DM's completion function
|
|
lparam -
|
|
HasReturnValue - Supplies flag for whether to expect a result
|
|
Function - Supplies the address of the function to call
|
|
cArgs - Supplies the number of args to the function
|
|
... - Supplies arguments. There must be cArgs arguments.
|
|
Return Value:
|
|
TRUE if call was initiated, FALSE if it failed
|
|
--*/
|
|
{
|
|
PCALLSTRUCT pcs;
|
|
ADDR addr;
|
|
va_list vargs;
|
|
|
|
|
|
// Don't try to do this with a 16 bit thread.
|
|
|
|
|
|
assert(!hthd->fWowEvent);
|
|
|
|
|
|
// can't nest 'em
|
|
|
|
|
|
assert(!hthd->pcs);
|
|
|
|
|
|
pcs = malloc(sizeof(*pcs));
|
|
assert(pcs || !"malloc failed in CallFunction");
|
|
if (!pcs) {
|
|
return 0;
|
|
}
|
|
|
|
|
|
// Remember the current context
|
|
|
|
hthd->pcs = pcs;
|
|
pcs->context = hthd->context;
|
|
|
|
|
|
// Remember the action function, and what do do when
|
|
// function returns.
|
|
|
|
pcs->Action = Action;
|
|
pcs->HasReturnValue = HasReturnValue;
|
|
pcs->lparam = lparam;
|
|
|
|
|
|
|
|
// set a BP on the current PC, and register a persistent
|
|
// expected event to catch it later.
|
|
|
|
|
|
AddrInit(&addr, 0, 0, PC(hthd), TRUE, TRUE, FALSE, FALSE);
|
|
pcs->pbp = SetBP(hthd->hprc, hthd, bptpExec, bpnsStop, &addr, (HPID)INVALID);
|
|
|
|
|
|
// don't try to step off of BP.
|
|
|
|
pcs->atBP = hthd->atBP;
|
|
hthd->atBP = NULL;
|
|
|
|
RegisterExpectedEvent(
|
|
hthd->hprc,
|
|
hthd,
|
|
BREAKPOINT_DEBUG_EVENT,
|
|
(UINT_PTR)pcs->pbp,
|
|
NULL,
|
|
ActionFunctionCallComplete,
|
|
TRUE,
|
|
(UINT_PTR)pcs);
|
|
|
|
|
|
// do machine dependent part
|
|
|
|
// GetCurrentThread() returns a token which means "this thread"
|
|
// regardless of context.
|
|
|
|
|
|
va_start(vargs, cArgs);
|
|
vCallFunctionHelper(hthd, Function, cArgs, vargs);
|
|
va_end(vargs);
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
|
|
VOID
|
|
ActionResumeThread(
|
|
DEBUG_EVENT64 * pde,
|
|
HTHDX hthd,
|
|
DWORDLONG unused,
|
|
DWORDLONG lparam
|
|
)
|
|
{
|
|
|
|
// This thread just fell out of SuspendThread.
|
|
// The context has been restored; just continue.
|
|
|
|
|
|
ContinueThread(hthd);
|
|
}
|
|
|
|
|
|
|
|
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.
|
|
|
|
Arguments:
|
|
|
|
hthd - Supplies thread
|
|
|
|
Return Value:
|
|
|
|
TRUE if the thread will be suspended, FALSE if not.
|
|
|
|
--*/
|
|
{
|
|
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) {
|
|
|
|
if (sizeof(hthd->hprc->dwKernel32Base) == sizeof(DWORD64)) {
|
|
assert(Is64PtrSE(hthd->hprc->dwKernel32Base));
|
|
}
|
|
|
|
DPRINT(1, ("can't suspend thread %p: Kernel32 not loaded\n", 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)((UINT_PTR)lpSuspendThread - (UINT_PTR)hdll
|
|
+ hthd->hprc->dwKernel32Base);
|
|
|
|
|
|
// GetCurrentThread() returns a token which means "this thread"
|
|
// regardless of context.
|
|
|
|
|
|
return CallFunction(hthd,
|
|
ActionResumeThread,
|
|
0,
|
|
TRUE,
|
|
(UINT_PTR)lpSuspendThread,
|
|
1,
|
|
GetCurrentThread());
|
|
}
|
|
|
|
|
|
VOID GetActiveFibers(HPRCX hprc, LPVOID * buf)
|
|
{
|
|
HTHDX hthd;
|
|
TEB teb;
|
|
int ct = 0;
|
|
int cbr;
|
|
|
|
// Determine the fiber loaded on each thread
|
|
// Don't display that fiber
|
|
for (hthd = hprc->hthdChild; hthd; hthd = hthd->nextSibling) {
|
|
DbgReadMemory(hprc, hthd->offTeb, (LPVOID)&teb, sizeof(teb), &cbr);
|
|
DbgReadMemory(hprc, (UINT_PTR)teb.NtTib.Self, (LPVOID)&teb, sizeof(teb), &cbr);
|
|
buf[ct++] = teb.NtTib.FiberData;
|
|
}
|
|
}
|
|
|
|
|
|
VOID
|
|
LocalProcessSystemServiceCmd(
|
|
HPRCX hprc,
|
|
HTHDX hthd,
|
|
LPDBB lpdbb
|
|
)
|
|
/*++
|
|
Routine Description:
|
|
This function is called in response to a SystemService command from
|
|
the shell. It is used as a catch all to get and set strange information
|
|
which is not covered elsewhere. The set of SystemServices is OS and implemenation dependent.
|
|
Arguments:
|
|
hprc - Supplies a process handle
|
|
hthd - Supplies a thread handle
|
|
lpdbb - Supplies the command information packet
|
|
Return Value:
|
|
None.
|
|
--*/
|
|
{
|
|
LPSSS lpsss = (LPSSS)lpdbb->rgbVar;
|
|
|
|
switch (lpsss->ssvc) {
|
|
case ssvcGetProcessHandle:
|
|
LpDmMsg->xosdRet = xosdNone;
|
|
*((HANDLE *)LpDmMsg->rgb) = hprc->rwHand;
|
|
Reply(sizeof(HANDLE), LpDmMsg, lpdbb->hpid);
|
|
return;
|
|
case ssvcGetThreadHandle:
|
|
LpDmMsg->xosdRet = xosdNone;
|
|
*((HANDLE *)LpDmMsg->rgb) = hthd->rwHand;
|
|
Reply(sizeof(HANDLE), LpDmMsg, lpdbb->hpid);
|
|
return;
|
|
case ssvcOleRpc:
|
|
{
|
|
BOOL fStartOrpcDebugging = lpsss->rgbData[0];
|
|
|
|
switch (hprc->OrpcDebugging) {
|
|
case ORPC_NOT_DEBUGGING:
|
|
if (fStartOrpcDebugging) {
|
|
hprc->OrpcDebugging = ORPC_START_DEBUGGING;
|
|
}
|
|
|
|
// ignore stop debugging request when we arent debugging
|
|
break;
|
|
case ORPC_DEBUGGING:
|
|
if (!fStartOrpcDebugging)
|
|
hprc->OrpcDebugging = ORPC_STOP_DEBUGGING;
|
|
|
|
// ignore start debugging request when we arent debugging
|
|
break;
|
|
case ORPC_STOP_DEBUGGING:
|
|
if (fStartOrpcDebugging)
|
|
hprc->OrpcDebugging = ORPC_DEBUGGING;
|
|
|
|
// ignore redundant stop debugging request
|
|
break;
|
|
case ORPC_START_DEBUGGING:
|
|
if (!fStartOrpcDebugging)
|
|
hprc->OrpcDebugging = ORPC_NOT_DEBUGGING;
|
|
|
|
// ignore redundant start debugging requst
|
|
break;
|
|
default:
|
|
assert(FALSE);
|
|
}
|
|
}
|
|
|
|
LpDmMsg->xosdRet = xosdNone;
|
|
Reply(0, LpDmMsg, lpdbb->hpid);
|
|
break;
|
|
|
|
#if SQLDBG
|
|
case ssvcSqlDebug:
|
|
LpDmMsg->xosdRet = DMSqlSystemService(hprc, lpsss->rgbData);
|
|
Reply(0, LpDmMsg, lpdbb->hpid);
|
|
break;
|
|
#endif
|
|
case ssvcFiberDebug:
|
|
{
|
|
HFBRX hfbr = hprc->FbrLst;
|
|
OFBRS ofbrs = *((OFBRS *)lpsss->rgbData);
|
|
DWORD iAfbrs;
|
|
LPVOID * Actvfbrs;
|
|
int cb = 0;
|
|
iAfbrs = NumberOfThreadsInProcess(hthd->hprc);
|
|
Actvfbrs = malloc(iAfbrs * sizeof(LPVOID));
|
|
GetActiveFibers(hthd->hprc, Actvfbrs);
|
|
|
|
if (ofbrs.op == OFBR_SET_FBRCNTX) {
|
|
hprc->pFbrCntx = ofbrs.FbrCntx;
|
|
} else if (ofbrs.op == OFBR_ENABLE_FBRS) {
|
|
hprc->fUseFbrs = TRUE;
|
|
} else if (ofbrs.op == OFBR_DISABLE_FBRS) {
|
|
hprc->fUseFbrs = FALSE;
|
|
} else if (ofbrs.op == OFBR_QUERY_LIST_SIZE) {
|
|
cb = sizeof(int);
|
|
//count size of the list of fibers
|
|
while (hfbr) {
|
|
DWORD i;
|
|
BOOL fskip;//skip fibers loaded in threads
|
|
for (fskip = FALSE, i = 0; i < iAfbrs; i++) {
|
|
if (Actvfbrs[i] == hfbr->fbrstrt)
|
|
fskip = TRUE;
|
|
}
|
|
if (!fskip) {
|
|
cb += 4;
|
|
}
|
|
hfbr = hfbr->next;
|
|
}
|
|
//put byte count at the beginning
|
|
memcpy(lpsss->rgbData + sizeof(int), &cb, sizeof(int));
|
|
cb = 2 * sizeof(int);
|
|
memcpy(lpsss->rgbData, &cb, sizeof(int));
|
|
lpsss->cbReturned = cb;
|
|
} else if (ofbrs.op == OFBR_GET_LIST) {
|
|
cb = sizeof(int);
|
|
while (hfbr) {
|
|
BOOL fskip;//skip fibers loaded in threads
|
|
DWORD i;
|
|
for (fskip = FALSE, i = 0; i < iAfbrs; i++) {
|
|
if (Actvfbrs[i] == hfbr->fbrstrt)
|
|
fskip = TRUE;
|
|
}
|
|
if (!fskip) {
|
|
memcpy(lpsss->rgbData + cb, &(hfbr->fbrcntx), 4);
|
|
cb += 4;
|
|
}
|
|
hfbr = hfbr->next;
|
|
}
|
|
//put byte count at the beginning
|
|
memcpy(lpsss->rgbData, &cb, sizeof(int));
|
|
lpsss->cbReturned = cb;
|
|
}
|
|
|
|
LpDmMsg->xosdRet = xosdNone;
|
|
memcpy(LpDmMsg->rgb, lpsss->rgbData, cb);
|
|
Reply(cb, LpDmMsg, lpdbb->hpid);
|
|
free(Actvfbrs);
|
|
}
|
|
break;
|
|
|
|
default:
|
|
LpDmMsg->xosdRet = xosdUnsupported;
|
|
Reply(0, LpDmMsg, lpdbb->hpid);
|
|
return;
|
|
}
|
|
} /* LocalProcessSystemServiceCmd() */
|
|
|
|
|
|
VOID
|
|
ProcessIoctlGenericCmd(
|
|
HPRCX hprc,
|
|
HTHDX hthd,
|
|
LPDBB lpdbb
|
|
)
|
|
{
|
|
LPSSS lpsss = (LPSSS)lpdbb->rgbVar;
|
|
|
|
PIOCTLGENERIC InputPig = (PIOCTLGENERIC)lpsss->rgbData;
|
|
PVOID InputBuffer = InputPig->data;
|
|
DWORD InputType = InputPig->ioctlSubType;
|
|
|
|
DWORD ReplyXosd;
|
|
DWORD ReplyLength;
|
|
PVOID ReplyBuffer;
|
|
PIOCTLGENERIC ReplyPig = (PIOCTLGENERIC)LpDmMsg->rgb;
|
|
|
|
ADDR addr;
|
|
PDETOSAVE pDeToSave;
|
|
PGET_TEB_ADDRESS Gta;
|
|
|
|
*ReplyPig = *InputPig;
|
|
ReplyBuffer = ReplyPig->data;
|
|
ReplyLength = 0;
|
|
|
|
ReplyXosd = xosdNone;
|
|
|
|
switch (InputPig->ioctlSubType) {
|
|
case IG_DEBUG_EVENT:
|
|
pDeToSave = GetMostRecentDebugEvent(hthd->hprc->pid, hthd->tid);
|
|
|
|
if (!pDeToSave) {
|
|
memset(ReplyBuffer, 0, sizeof(DEBUG_EVENT));
|
|
// No error; just nothing to return
|
|
ReplyXosd = xosdQueueEmpty;
|
|
} else {
|
|
memcpy(ReplyBuffer, &pDeToSave->de, sizeof(DEBUG_EVENT));
|
|
ReplyXosd = xosdNone;
|
|
}
|
|
ReplyLength = sizeof(DEBUG_EVENT);
|
|
break;
|
|
|
|
case IG_TRANSLATE_ADDRESS:
|
|
memcpy(&addr, InputBuffer, sizeof(addr));
|
|
if (TranslateAddress(hprc, hthd, &addr, TRUE)) {
|
|
memcpy(ReplyBuffer, &addr, sizeof(addr));
|
|
ReplyLength = sizeof(addr);
|
|
ReplyXosd = xosdNone;
|
|
} else {
|
|
ReplyXosd = xosdUnknown;
|
|
}
|
|
break;
|
|
|
|
case IG_WATCH_TIME:
|
|
WtRangeStep(hthd);
|
|
break;
|
|
|
|
case IG_WATCH_TIME_STOP:
|
|
case IG_WATCH_TIME_RECALL:
|
|
case IG_WATCH_TIME_PROCS:
|
|
WtStruct.fWt = TRUE;
|
|
WtStruct.dwType = InputType;
|
|
WtStruct.hthd = hthd;
|
|
break;
|
|
|
|
case IG_GET_TEB_ADDRESS:
|
|
|
|
Gta = (PGET_TEB_ADDRESS)ReplyBuffer;
|
|
|
|
if (hthd->offTeb == 0) {
|
|
ReplyXosd = xosdUnknown;
|
|
} else {
|
|
Gta->Address = hthd->offTeb;
|
|
ReplyLength = sizeof(*Gta);
|
|
ReplyXosd = xosdNone;
|
|
}
|
|
|
|
break;
|
|
|
|
case IG_TASK_LIST:
|
|
{
|
|
DWORD TasksReturned;
|
|
DWORD MaxTasks;
|
|
|
|
if (CrashDump) {
|
|
ReplyXosd = xosdUnknown;
|
|
} else {
|
|
// get max parameter
|
|
MaxTasks = ((PTASK_LIST)InputBuffer)->dwProcessId;
|
|
|
|
// copy header to return buffer
|
|
GetTaskList((PTASK_LIST)ReplyBuffer, MaxTasks, &TasksReturned);
|
|
ReplyLength = sizeof(TASK_LIST) * TasksReturned;
|
|
ReplyXosd = xosdNone;
|
|
}
|
|
}
|
|
break;
|
|
case IG_RELOAD:
|
|
AddQueue(QT_RELOAD_MODULES, hthd->hprc->pid, hthd->tid, (UINT_PTR)InputPig->data, _tcslen((LPSTR)InputPig->data) + 1);
|
|
ReplyLength = 0;
|
|
break;
|
|
case IG_GET_OS_VERSION:
|
|
{
|
|
PDBG_GET_OS_VERSION posv = (PDBG_GET_OS_VERSION)ReplyPig->data;
|
|
|
|
memset(posv, 0, sizeof(*posv));
|
|
|
|
posv->osi.dwOSVersionInfoSize = sizeof(posv->osi);
|
|
GetVersionEx(&posv->osi);
|
|
GetSystemInfo(&posv->si);
|
|
|
|
if (VER_PLATFORM_WIN32_NT == posv->osi.dwPlatformId) {
|
|
HKEY hkey = NULL;
|
|
TCHAR sz[20] = {0};
|
|
DWORD dwType;
|
|
DWORD dwSize = sizeof(sz);
|
|
|
|
if (ERROR_SUCCESS == RegOpenKeyEx(HKEY_LOCAL_MACHINE, "Software\\Microsoft\\Windows NT\\CurrentVersion", 0, KEY_READ, &hkey)) {
|
|
if (ERROR_SUCCESS == RegQueryValueEx(hkey, "CurrentType", NULL, &dwType, (PUCHAR)sz, &dwSize)) {
|
|
if (*sz) {
|
|
_tcslwr(sz);
|
|
posv->fChecked = (NULL != _tcsstr(sz, "checked"));
|
|
}
|
|
}
|
|
|
|
RegCloseKey(hkey);
|
|
}
|
|
}
|
|
ReplyLength = sizeof(*posv);
|
|
}
|
|
break;
|
|
case IG_GET_PROCESS_PARAMETERS:
|
|
{
|
|
TEB Teb;
|
|
PEB Peb;
|
|
PRTL_USER_PROCESS_PARAMETERS64 upp = (PRTL_USER_PROCESS_PARAMETERS64)ReplyPig->data;
|
|
RTL_USER_PROCESS_PARAMETERS32 upp32Tmp;
|
|
DWORD dw;
|
|
|
|
ReplyXosd = xosdUnknown;
|
|
|
|
|
|
// get the peb and find the RTL_USER_PROCESS_PARAMETERS
|
|
|
|
|
|
if (IsChicago()) {
|
|
break;
|
|
}
|
|
|
|
if (!hprc->PebAddress) {
|
|
// grab the first thread and read its teb
|
|
if (!hthd) {
|
|
hthd = hprc->hthdChild;
|
|
}
|
|
|
|
if (hthd && hthd->offTeb && DbgReadMemory(hprc, hthd->offTeb, &Teb, sizeof(Teb), &dw)) {
|
|
hprc->PebAddress = (UOFFSET)Teb.ProcessEnvironmentBlock;
|
|
} else {
|
|
break;
|
|
}
|
|
}
|
|
|
|
// read the peb
|
|
if (!DbgReadMemory(hprc, hprc->PebAddress, &Peb, sizeof(Peb), &dw)) {
|
|
break;
|
|
}
|
|
|
|
// copy some info
|
|
|
|
#ifdef _WIN64
|
|
if (!DbgReadMemory(hprc, (ULONG64)Peb.ProcessParameters, upp, sizeof(RTL_USER_PROCESS_PARAMETERS), &dw)) {
|
|
|
|
break;
|
|
}
|
|
#else
|
|
if (!DbgReadMemory(hprc, (DWORDLONG)Peb.ProcessParameters, &upp32Tmp, sizeof(RTL_USER_PROCESS_PARAMETERS32), &dw)) {
|
|
break;
|
|
}
|
|
|
|
UserProcessParameters32To64(&upp32Tmp, upp);
|
|
#endif
|
|
|
|
ReplyLength = sizeof(RTL_USER_PROCESS_PARAMETERS64);
|
|
ReplyXosd = xosdNone;
|
|
}
|
|
break;
|
|
default:
|
|
ReplyXosd = xosdUnknown;
|
|
break;
|
|
}
|
|
|
|
ReplyPig->length = ReplyLength;
|
|
LpDmMsg->xosdRet = ReplyXosd;
|
|
Reply(ReplyLength + sizeof(IOCTLGENERIC), LpDmMsg, lpdbb->hpid);
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
VOID
|
|
ProcessSSVCCustomCmd(
|
|
HPRCX hprc,
|
|
HTHDX hthd,
|
|
LPDBB lpdbb
|
|
)
|
|
{
|
|
LPSSS lpsss = (LPSSS)lpdbb->rgbVar;
|
|
LPTSTR p = (LPTSTR)lpsss->rgbData;
|
|
|
|
|
|
LpDmMsg->xosdRet = xosdUnsupported;
|
|
|
|
|
|
// parse the command
|
|
|
|
while (*p && !_istspace(*p)) {
|
|
p = _tcsinc(p);
|
|
}
|
|
if (*p) {
|
|
*p = _T('\0');
|
|
p = _tcsinc(p);
|
|
}
|
|
|
|
|
|
// this is what the code should look like:
|
|
|
|
// at this point the 'p' variable points to any arguments
|
|
// to the dot command
|
|
|
|
// if (_tcsicmp( lpsss->rgbData, "dot-command" ) == 0) {
|
|
// > do your thing <-
|
|
// LpDmMsg->xosdRet = xosdNone;
|
|
// }
|
|
|
|
if (!_ftcsicmp((LPTSTR)lpsss->rgbData, _T("FastStep"))) {
|
|
fSmartRangeStep = TRUE;
|
|
LpDmMsg->xosdRet = xosdNone;
|
|
} else if (!_ftcsicmp((LPTSTR)lpsss->rgbData, _T("SlowStep"))) {
|
|
fSmartRangeStep = FALSE;
|
|
LpDmMsg->xosdRet = xosdNone;
|
|
}
|
|
|
|
|
|
// send back our response
|
|
|
|
Reply(0, LpDmMsg, lpdbb->hpid);
|
|
} /* ProcessSSVCCustomCmd() */
|
|
|
|
|
|
|
|
|
|
DWORD WINAPI
|
|
DoTerminate(
|
|
LPVOID lpv
|
|
)
|
|
{
|
|
HPRCX hprcx = (HPRCX)lpv;
|
|
|
|
if (CrashDump) {
|
|
ProcessUnloadCmd(hprcx, NULL, NULL);
|
|
return 0;
|
|
}
|
|
|
|
#ifdef TARGET_i386
|
|
ClearAllDebugRegisters(hprcx); // just in case Win95 gets confused
|
|
#endif
|
|
|
|
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_EVENT64 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) {
|
|
hthd->fExceptionHandled = TRUE;
|
|
ContinueThread(hthd);
|
|
}
|
|
}
|
|
|
|
ProcessUnloadCmd(hprc, NULL, NULL);
|
|
|
|
} else {
|
|
|
|
RemoveAllHprcBP(hprc);
|
|
|
|
|
|
// 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;
|
|
|
|
// setting the priority to TIME_CRITICAL causes Win95 to hang
|
|
// DS96:6580 so we are less aggressive
|
|
if (!IsChicago()) {
|
|
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) {
|
|
hthd->fExceptionHandled = TRUE;
|
|
ContinueThread(hthd);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
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;
|
|
}
|
|
|
|
|
|
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;
|
|
DEBUG_EVENT64 de;
|
|
|
|
DEBUG_PRINT("ProcessAsyncGoCmd called.\n\r");
|
|
|
|
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) {
|
|
|
|
xosd = xosdBadThread;
|
|
|
|
} 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, 0);
|
|
FreeHthdx(hthd);
|
|
|
|
hprc->pstate &= ~ps_deadThread;
|
|
for (hthd = hprc->hthdChild; hthd; hthd = hthd->nextSibling) {
|
|
if (hthd->tstate & ts_dead) {
|
|
hprc->pstate |= ps_deadThread;
|
|
}
|
|
}
|
|
|
|
}
|
|
}
|
|
}
|
|
|
|
Reply(0, &xosd, lpdbb->hpid);
|
|
return(xosd);
|
|
}
|
|
|
|
|
|
void
|
|
ActionAsyncStop(
|
|
DEBUG_EVENT64 * pde,
|
|
HTHDX hthd,
|
|
DWORDLONG unused,
|
|
DWORDLONG lparam
|
|
)
|
|
/*++
|
|
|
|
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.
|
|
|
|
--*/
|
|
|
|
{
|
|
BPR bpr;
|
|
HPRCX hprc = hthd->hprc;
|
|
BREAKPOINT * pbpT;
|
|
BREAKPOINT * pbp;
|
|
BREAKPOINT * pbpStop = (PBREAKPOINT)lparam;
|
|
|
|
|
|
// remove all expected events we set up for this
|
|
// (which also removes their breakpoints).
|
|
// Just removing the bps causes heap corruption,
|
|
// as ConsumeAllProcessEvents will delete the outstanding EEs
|
|
// and their BPs, which we don't want to do twice.
|
|
// Note: don't delete this expected event as the caller does that
|
|
|
|
|
|
for (pbp = BPNextHprcPbp(hprc, NULL); pbp != NULL; pbp = pbpT) {
|
|
pbpT = BPNextHprcPbp(hprc, pbp);
|
|
|
|
if ((pbp != pbpStop) && (pbp->id == (HPID)ASYNC_STOP_BP)) {
|
|
// get the expected event off the queue, free it, and free the bp
|
|
EXPECTED_EVENT * ee = PeeIsEventExpected(NULL, BREAKPOINT_DEBUG_EVENT, (UINT_PTR)pbp, TRUE);
|
|
if (ee) {
|
|
MHFree(ee);
|
|
}
|
|
RemoveBP(pbp);
|
|
}
|
|
}
|
|
|
|
|
|
// We no longer need to have this breakpoint set (our EE was not freed above)
|
|
|
|
|
|
RemoveBP(pbpStop);
|
|
|
|
|
|
// Setup a return packet which says we hit an async stop breakpoint
|
|
|
|
|
|
#ifdef TARGET_i386
|
|
bpr.segCS = (SEGMENT)hthd->context.SegCs;
|
|
bpr.segSS = (SEGMENT)hthd->context.SegSs;
|
|
bpr.offEBP = (UOFFSET)hthd->context.Ebp;
|
|
#endif
|
|
|
|
bpr.offEIP = PC(hthd);
|
|
|
|
bpr.fFlat = hthd->fAddrIsFlat;
|
|
bpr.fOff32 = hthd->fAddrOff32;
|
|
bpr.fReal = hthd->fAddrIsReal;
|
|
|
|
DMSendDebugPacket(dbcAsyncStop,
|
|
hprc->hpid,
|
|
hthd->htid,
|
|
sizeof(BPR),
|
|
&bpr
|
|
);
|
|
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.
|
|
|
|
--*/
|
|
|
|
{
|
|
CONTEXT regs;
|
|
BREAKPOINT * pbp;
|
|
ADDR addr;
|
|
BOOL fSetFocus = *(BOOL *)lpdbb->rgbVar;
|
|
|
|
|
|
// If we are debugging a dump file, don't
|
|
// bother trying to stop it. Just reply to it as
|
|
// if it were sucessful.
|
|
|
|
if (CrashDump) {
|
|
LpDmMsg->xosdRet = xosdNone;
|
|
Reply(0, LpDmMsg, lpdbb->hpid);
|
|
return;
|
|
}
|
|
|
|
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) {
|
|
DbgGetThreadContext(hthd, ®s);
|
|
|
|
AddrInit(&addr, 0, 0, cPC(®s), TRUE, TRUE, FALSE, FALSE);
|
|
pbp = SetBP(hprc, hthd, bptpExec, bpnsStop, &addr, (HPID)ASYNC_STOP_BP);
|
|
|
|
if (!pbp) {
|
|
assert(IsChicago()); // UNDONE: deal with error
|
|
continue;
|
|
}
|
|
|
|
RegisterExpectedEvent(hthd->hprc,
|
|
hthd,
|
|
BREAKPOINT_DEBUG_EVENT,
|
|
(UINT_PTR)pbp,
|
|
DONT_NOTIFY,
|
|
ActionAsyncStop,
|
|
FALSE,
|
|
(UINT_PTR)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
|
|
}
|
|
|
|
|
|
// post a dummy message so we will see the BP
|
|
|
|
PostThreadMessage(hthd->tid, WM_NULL, 0, 0);
|
|
}
|
|
|
|
LpDmMsg->xosdRet = xosdNone;
|
|
Reply(0, LpDmMsg, lpdbb->hpid);
|
|
return;
|
|
} /* ProcessAsyncStopCmd() */
|
|
|
|
|
|
VOID
|
|
ProcessDebugActiveCmd(
|
|
HPRCX hprc,
|
|
HTHDX hthd,
|
|
LPDBB lpdbb
|
|
)
|
|
{
|
|
LPDAP lpdap = ((LPDAP)(lpdbb->rgbVar));
|
|
|
|
Unreferenced(hprc);
|
|
Unreferenced(hthd);
|
|
|
|
if (FDMRemote && !fDisconnected) {
|
|
|
|
SetEvent(hEventRemoteQuit);
|
|
|
|
} else if (!StartDmPollThread()) {
|
|
|
|
|
|
// CreateThread() failed; fail and send a dbcError.
|
|
|
|
LpDmMsg->xosdRet = xosdUnknown;
|
|
Reply(0, LpDmMsg, lpdbb->hpid);
|
|
|
|
|
|
|
|
// Wait for previous attach to complete or fail:
|
|
|
|
} 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 {
|
|
|
|
*nameBuffer = 0;
|
|
|
|
ResetEvent(DebugActiveStruct.hEventReady);
|
|
ResetEvent(DebugActiveStruct.hEventApiDone);
|
|
|
|
DebugActiveStruct.dwProcessId = lpdap->dwProcessId;
|
|
DebugActiveStruct.hEventGo = lpdap->hEventGo;
|
|
|
|
|
|
// Open a waitable handle to the process. The poll thread
|
|
// will use this to determine whether the target process has
|
|
// exited before the debugger was connected.
|
|
|
|
if (lpdap->dwProcessId != 0 &&
|
|
lpdap->dwProcessId != (UINT)-1) {
|
|
|
|
DebugActiveStruct.hProcess = OpenProcess(STANDARD_RIGHTS_REQUIRED | SYNCHRONIZE,
|
|
FALSE,
|
|
lpdap->dwProcessId
|
|
);
|
|
} else {
|
|
DebugActiveStruct.hProcess = INVALID_HANDLE_VALUE;
|
|
}
|
|
|
|
|
|
// ** Do this last!! **
|
|
|
|
DebugActiveStruct.fAttach = TRUE;
|
|
|
|
// **
|
|
|
|
|
|
|
|
// Wait for poll thread to complete the API call:
|
|
|
|
|
|
WaitForSingleObject(DebugActiveStruct.hEventApiDone, INFINITE);
|
|
|
|
if (DebugActiveStruct.fReturn != 0) {
|
|
|
|
LpDmMsg->xosdRet = xosdNone;
|
|
|
|
// the poll thread will reply when creating the "root" process.
|
|
// we reply when this is really a reattach.
|
|
|
|
if (!fUseRoot || lpdap->dwProcessId == 0) {
|
|
Reply(0, LpDmMsg, lpdbb->hpid);
|
|
}
|
|
|
|
} else {
|
|
|
|
// DebugActiveProcess failed
|
|
|
|
if (DebugActiveStruct.hProcess != INVALID_HANDLE_VALUE) {
|
|
CloseHandle(DebugActiveStruct.hProcess);
|
|
}
|
|
SetEvent(DebugActiveStruct.hEventReady);
|
|
LpDmMsg->xosdRet = xosdUnknown;
|
|
Reply(0, LpDmMsg, lpdbb->hpid);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
// Non local goto support
|
|
|
|
/** ACTIONNLGDISPATCH
|
|
|
|
* PURPOSE:
|
|
|
|
* PARAMETERS:
|
|
|
|
* RETURN:
|
|
|
|
* DESCRIPTION:
|
|
|
|
*/
|
|
|
|
VOID
|
|
ActionNLGDispatch(
|
|
DEBUG_EVENT64 * pde,
|
|
HTHDX hthd,
|
|
DWORDLONG unused,
|
|
DWORDLONG lparam
|
|
)
|
|
{
|
|
ADDR addrPC, addrReturn;
|
|
HNLG hnlg;
|
|
LPNLG lpnlg;
|
|
NLG_DESTINATION nlgDest;
|
|
XOSD xosd;
|
|
BOOL fStop = FALSE;
|
|
DWORD cb;
|
|
BREAKPOINT * bp = (BREAKPOINT *)lparam;
|
|
METHOD * ContinueSSMethod;
|
|
|
|
assert(bp);
|
|
|
|
AddrFromHthdx(&addrPC, hthd);
|
|
|
|
hnlg = CheckNLG(hthd->hprc, hthd, NLG_DISPATCH, &addrPC);
|
|
assert(hnlg);
|
|
ClearBPFlag(hthd);
|
|
|
|
RestoreInstrBP(hthd, bp);
|
|
|
|
lpnlg = LLLock(hnlg);
|
|
|
|
addrReturn = lpnlg->addrNLGReturn;
|
|
|
|
xosd = AddrReadMemory(
|
|
hthd->hprc,
|
|
hthd,
|
|
&lpnlg->addrNLGDestination,
|
|
&nlgDest,
|
|
sizeof(NLG_DESTINATION),
|
|
&cb
|
|
);
|
|
|
|
LLUnlock(hnlg);
|
|
|
|
if ((nlgDest.dwSig == NLG_SIG) &&
|
|
(nlgDest.dwCode != NLG_DESTRUCTOR_ENTER)
|
|
) {
|
|
BREAKPOINT * bp;
|
|
HPID hpid = hthd->hprc->hpid;
|
|
CANSTEP CanStep;
|
|
ADDR addrSP;
|
|
|
|
GetAddrOff(addrSP) = GetSPFromNLGDest(hthd, &nlgDest);
|
|
|
|
DPRINT(2, ("NLG Dispatch old SP=%I64x, new SP=%I64x",
|
|
GetAddrOff(hthd->addrStack),
|
|
GetAddrOff(addrSP)
|
|
));
|
|
if (GetAddrOff(hthd->addrStack) < GetAddrOff(addrSP)) {
|
|
|
|
ConsumeAllProcessEvents(hthd->hprc, FALSE);
|
|
fStop = TRUE;
|
|
|
|
/*
|
|
** Set breakpoint at the destination, which we'll hit after it's done
|
|
*/
|
|
|
|
SetAddrOff(&addrPC, nlgDest.uoffDestination);
|
|
|
|
#ifndef TARGET_i386
|
|
GetCanStep(hthd->hprc->hpid, hthd->htid, &addrPC, &CanStep);
|
|
|
|
switch (CanStep.Flags) {
|
|
|
|
case CANSTEP_YES:
|
|
GetAddrOff(addrPC) += CanStep.PrologOffset;
|
|
break;
|
|
|
|
default:
|
|
fStop = FALSE;
|
|
// assert(FALSE);
|
|
break;
|
|
}
|
|
#endif
|
|
|
|
if (fStop) {
|
|
bp = SetBP(hthd->hprc, hthd, bptpExec, bpnsStop, &addrPC, (HPID)INVALID);
|
|
|
|
RegisterExpectedEvent(
|
|
hthd->hprc,
|
|
hthd,
|
|
BREAKPOINT_DEBUG_EVENT,
|
|
(UINT_PTR)bp,
|
|
DONT_NOTIFY,
|
|
ActionNLGDestination,
|
|
FALSE,
|
|
(UINT_PTR)bp
|
|
);
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Keep ourselves registered. Then Consume will remove this BP
|
|
*/
|
|
RegisterExpectedEvent(
|
|
hthd->hprc,
|
|
hthd,
|
|
BREAKPOINT_DEBUG_EVENT,
|
|
(UINT_PTR)lparam,
|
|
DONT_NOTIFY,
|
|
ActionNLGDispatch,
|
|
FALSE,
|
|
lparam
|
|
);
|
|
|
|
ContinueSSMethod = (METHOD *)MHAlloc(sizeof(METHOD));
|
|
assert(ContinueSSMethod);
|
|
ContinueSSMethod->notifyFunction = MethodContinueSS;
|
|
ContinueSSMethod->lparam = (UINT_PTR)ContinueSSMethod;
|
|
ContinueSSMethod->lparam2 = (LPVOID)lparam;
|
|
SingleStep(hthd, ContinueSSMethod, FALSE, FALSE);
|
|
|
|
} // ActionNLGDispatch
|
|
|
|
/** PROCESSDMFNONLOCALGOTO
|
|
|
|
* PURPOSE:
|
|
|
|
* PARAMETERS:
|
|
|
|
* RETURN:
|
|
|
|
* DESCRIPTION:
|
|
|
|
* This is called in response to a dmfNonLocalGoto command from the EM.
|
|
|
|
*/
|
|
|
|
VOID
|
|
ProcessNonLocalGoto(
|
|
HPRCX hprc,
|
|
HTHDX hthd,
|
|
LPDBB lpdbb
|
|
)
|
|
{
|
|
LPNLG lpnlg = (LPNLG)lpdbb->rgbVar;
|
|
HNLG hnlg;
|
|
XOSD xosd = xosdNone;
|
|
|
|
if (lpnlg->fEnable) {
|
|
hnlg = LLCreate(hprc->llnlg);
|
|
|
|
if (!hnlg) {
|
|
/*
|
|
** REVIEW: Memory Failure
|
|
*/
|
|
assert(FALSE);
|
|
} else {
|
|
LPNLG lpnlgT;
|
|
BREAKPOINT * bp;
|
|
LLAdd(hprc->llnlg, hnlg);
|
|
lpnlgT = LLLock(hnlg);
|
|
|
|
*lpnlgT = *lpnlg;
|
|
|
|
emiAddr(lpnlgT->addrNLGDispatch) = 0;
|
|
emiAddr(lpnlgT->addrNLGReturn) = 0;
|
|
emiAddr(lpnlgT->addrNLGReturn2) = 0;
|
|
emiAddr(lpnlgT->addrNLGDestination) = 0;
|
|
|
|
LLUnlock(hnlg);
|
|
}
|
|
} else {
|
|
hnlg = LLFind(hprc->llnlg, NULL, &lpnlg->hemi, (LONG)nfiHEMI);
|
|
if (!hnlg) {
|
|
/*
|
|
** We better have it otherwise the EM shouldn't be telling us
|
|
** about to remove it.
|
|
*/
|
|
|
|
// well, like it or not, this assertion happens constantly.
|
|
|
|
//assert ( FALSE );
|
|
} else {
|
|
LPNLG lpnlgT = LLLock(hnlg);
|
|
|
|
BREAKPOINT * bp = FindBP(hprc, hthd, bptpExec, bpnsStop, &lpnlgT->addrNLGDispatch, TRUE);
|
|
EXPECTED_EVENT * ee = PeeIsEventExpected(NULL, BREAKPOINT_DEBUG_EVENT, (UINT_PTR)bp, TRUE);
|
|
if (ee) {
|
|
ConsumeSpecifiedEvent(ee);
|
|
}
|
|
|
|
LLUnlock(hnlg);
|
|
|
|
LLDelete(hprc->llnlg, hnlg);
|
|
}
|
|
|
|
}
|
|
|
|
Reply(0, &xosd, lpdbb->hpid);
|
|
} // ProcessDmfNonLocalGoto
|
|
|
|
|
|
INT
|
|
WINAPI
|
|
NLGComp(
|
|
LPNLG lpnlg,
|
|
LPV lpvKey,
|
|
LONG lParam
|
|
)
|
|
{
|
|
NFI nfi = (NFI)lParam;
|
|
|
|
switch (nfi) {
|
|
case nfiHEMI:
|
|
if (lpnlg->hemi == *(LPHEMI)lpvKey) {
|
|
return fCmpEQ;
|
|
} else {
|
|
return fCmpLT;
|
|
}
|
|
break;
|
|
|
|
default:
|
|
// Should not reach here
|
|
assert(FALSE);
|
|
return fCmpGT;
|
|
break;
|
|
}
|
|
|
|
} // NLGComp
|
|
/** CHECKNLG
|
|
|
|
* PURPOSE:
|
|
|
|
* PARAMETERS:
|
|
|
|
* RETURN:
|
|
|
|
* DESCRIPTION:
|
|
|
|
*/
|
|
|
|
HNLG
|
|
CheckNLG(
|
|
HPRCX hprc,
|
|
HTHDX hthd,
|
|
NLG_LOCATION nlgLoc,
|
|
LPADDR lpaddrPC
|
|
)
|
|
{
|
|
HNLG hnlg = hnlgNull;
|
|
HNLG hnlgRet = hnlgNull;
|
|
|
|
while (!hnlgRet && (hnlg = LLNext(hprc->llnlg, hnlg))) {
|
|
LPNLG lpnlg = LLLock(hnlg);
|
|
LPADDR lpaddr;
|
|
LPADDR lpaddr2 = NULL;
|
|
|
|
switch (nlgLoc) {
|
|
case NLG_DISPATCH:
|
|
lpaddr = &lpnlg->addrNLGDispatch;
|
|
break;
|
|
|
|
case NLG_RETURN:
|
|
lpaddr = &lpnlg->addrNLGReturn;
|
|
lpaddr2 = &lpnlg->addrNLGReturn2;
|
|
break;
|
|
|
|
default:
|
|
assert(FALSE);
|
|
}
|
|
|
|
if (FAddrsEq(*lpaddr, *lpaddrPC)) {
|
|
hnlgRet = hnlg;
|
|
} else if ((lpaddr2 != NULL) && FAddrsEq(*lpaddr2, *lpaddrPC)) {
|
|
hnlgRet = hnlg;
|
|
}
|
|
LLUnlock(hnlg);
|
|
}
|
|
|
|
return (hnlgRet);
|
|
|
|
} // CheckNLG
|
|
|
|
/** ACTIONNLGDESTINATION
|
|
|
|
* PURPOSE:
|
|
|
|
* PARAMETERS:
|
|
|
|
* RETURN:
|
|
|
|
* DESCRIPTION:
|
|
|
|
*/
|
|
|
|
VOID
|
|
ActionNLGDestination(
|
|
DEBUG_EVENT64 * pde,
|
|
HTHDX hthd,
|
|
DWORDLONG unused,
|
|
DWORDLONG lparam
|
|
)
|
|
{
|
|
BREAKPOINT * bp = (BREAKPOINT *)lparam;
|
|
|
|
assert(bp);
|
|
RemoveBP(bp);
|
|
hthd->fReturning = FALSE;
|
|
ConsumeAllThreadEvents(hthd, FALSE);
|
|
|
|
NotifyEM(&falseSSEvent, hthd, 0, 0);
|
|
} // ActionNLGDestination
|
|
|
|
VOID
|
|
ProcessFiberEvent(
|
|
DEBUG_EVENT64 * pde,
|
|
HTHDX hthd
|
|
)
|
|
{
|
|
HPRCX hprc = hthd->hprc;
|
|
HFBRX hfbr;
|
|
EXCEPTION_DEBUG_INFO64 * dbginfo = (EXCEPTION_DEBUG_INFO64 *)&(pde->u);
|
|
EFBR efbr = (EFBR)dbginfo->ExceptionRecord.ExceptionInformation[0];
|
|
|
|
// There are only two cases - Create Fiber and Delete Fiber
|
|
|
|
switch (efbr) {
|
|
case ecreate_fiber:
|
|
|
|
hfbr = (HFBRX)MHAlloc(sizeof(HFBRXSTRUCT));
|
|
memset(hfbr, 0, sizeof(*hfbr));
|
|
|
|
// Add to the process's list of fibers
|
|
hfbr->next = hprc->FbrLst;
|
|
hprc->FbrLst = hfbr;
|
|
|
|
// Grab the start and context pointers for the fiber
|
|
|
|
hfbr->fbrstrt = (LPVOID)dbginfo->ExceptionRecord.ExceptionInformation[1];
|
|
hfbr->fbrcntx = (LPVOID)dbginfo->ExceptionRecord.ExceptionInformation[2];
|
|
|
|
// Have the debuggee continue after the exception
|
|
hthd->fExceptionHandled = TRUE;
|
|
ContinueThread(hthd);
|
|
break;
|
|
case edelete_fiber:
|
|
{
|
|
HFBRX prevhfbr;
|
|
LPVOID fbrstrt = (LPVOID)dbginfo->ExceptionRecord.ExceptionInformation[1];
|
|
LPVOID fbrcntx = (LPVOID)dbginfo->ExceptionRecord.ExceptionInformation[2];
|
|
|
|
hfbr = hprc->FbrLst;
|
|
if ((hfbr->fbrstrt == fbrstrt) &&
|
|
(hfbr->fbrcntx == fbrcntx)) {
|
|
hprc->FbrLst = hfbr->next;
|
|
MHFree(hfbr);
|
|
} else {
|
|
prevhfbr = hfbr;
|
|
hfbr = hfbr->next;
|
|
while (hfbr) {
|
|
if ((hfbr->fbrstrt == fbrstrt) &&
|
|
(hfbr->fbrcntx == fbrcntx)) {
|
|
prevhfbr->next = hfbr->next;
|
|
MHFree(hfbr);
|
|
break;
|
|
} else {
|
|
prevhfbr = hfbr;
|
|
hfbr = hfbr->next;
|
|
}
|
|
}
|
|
}
|
|
// Have the debuggee continue after the exception
|
|
hthd->fExceptionHandled = TRUE;
|
|
ContinueThread(hthd);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
VOID
|
|
RemoveFiberList(
|
|
HPRCX hprc
|
|
)
|
|
{
|
|
HFBRX hfbr = hprc->FbrLst;
|
|
HFBRX next = NULL;
|
|
while (hfbr) {
|
|
next = hfbr->next;
|
|
MHFree(hfbr);
|
|
hfbr = next;
|
|
}
|
|
hprc->FbrLst = NULL;
|
|
}
|
|
|
|
|
|
void
|
|
ContinueThreadEx(
|
|
HTHDX hthd,
|
|
DWORD ContinueStatus,
|
|
DWORD EventType,
|
|
TSTATEX NewState
|
|
)
|
|
{
|
|
|
|
// If NewState is ts_running, do the "usual magic" as a special
|
|
// case. If it is anything else, set the flag to NewState
|
|
|
|
if (NewState == ts_running) {
|
|
hthd->tstate &= ~(ts_stopped | ts_first | ts_second);
|
|
hthd->tstate |= ts_running;
|
|
} else {
|
|
hthd->tstate = NewState;
|
|
}
|
|
|
|
hthd->fExceptionHandled = FALSE;
|
|
|
|
AddQueue(EventType,
|
|
hthd->hprc->pid,
|
|
hthd->tid,
|
|
ContinueStatus,
|
|
0);
|
|
}
|
|
|
|
|
|
void
|
|
ContinueProcess(
|
|
HPRCX hprc
|
|
)
|
|
{
|
|
HTHDX hthd;
|
|
for (hthd = hprc->hthdChild; hthd; hthd = hthd->nextSibling) {
|
|
if (hthd->tstate & ts_stopped) {
|
|
ContinueThread(hthd);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
void
|
|
ContinueThread(
|
|
HTHDX hthd
|
|
)
|
|
{
|
|
DWORD ContinueStatus = DBG_CONTINUE;
|
|
if ((hthd->tstate & (ts_first | ts_second)) &&
|
|
!hthd->fExceptionHandled) {
|
|
ContinueStatus = (DWORD)DBG_EXCEPTION_NOT_HANDLED;
|
|
}
|
|
ContinueThreadEx(hthd,
|
|
ContinueStatus,
|
|
QT_CONTINUE_DEBUG_EVENT,
|
|
ts_running
|
|
);
|
|
}
|
|
|
|
|
|
|
|
|
|
// Helper functions for LoadDll.
|
|
|
|
|
|
|
|
|
|
PTCHAR
|
|
CopyFileNameFromDBGFileName(
|
|
PTCHAR Dest,
|
|
PTCHAR Src
|
|
)
|
|
{
|
|
TCHAR rgchPath[_MAX_PATH];
|
|
TCHAR rgchBase[_MAX_FNAME];
|
|
TCHAR rgchExt[_MAX_EXT];
|
|
|
|
_tsplitpath(Src, NULL, rgchPath, rgchBase, rgchExt);
|
|
|
|
if (_tcsicmp(rgchExt, _T(".DBG")) != 0) {
|
|
if (Dest != Src) {
|
|
_tcscpy(Dest, Src);
|
|
}
|
|
} else if (rgchPath[0] && rgchPath[1] &&
|
|
_tcschr(rgchPath, '\\') == &rgchPath[_tcslen(rgchPath) - 1]) {
|
|
rgchPath[_tcslen(rgchPath) - 1] = 0;
|
|
_tcscpy(Dest, rgchBase);
|
|
_tcscat(Dest, _T("."));
|
|
_tcscat(Dest, rgchPath);
|
|
} else {
|
|
_tcscpy(Dest, rgchBase);
|
|
_tcscat(Dest, _T(".exe"));
|
|
}
|
|
|
|
return Dest;
|
|
}
|
|
|
|
|
|
// MagicModuleId is a magic module ID number for when we have to make
|
|
// up a name for a module.
|
|
|
|
|
|
MagicModuleId = 0;
|
|
|
|
|
|
BOOL
|
|
GetModnameFromImage(
|
|
PIMAGE_NT_HEADERS pNtHdr,
|
|
PIMAGE_SECTION_HEADER pSH,
|
|
LOAD_DLL_DEBUG_INFO64 * pldd,
|
|
LPTSTR lpName,
|
|
int cbName
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine attempts to get the name of the exe as placed
|
|
in the debug section section by the linker.
|
|
|
|
Arguments:
|
|
|
|
pNtHdr - Supplies pointer to NT headers in image PE header
|
|
|
|
pSH - Supplies pointer to section headers
|
|
|
|
pldd - Supplies the info structure from the debug event
|
|
|
|
lpName - Returns the exe name
|
|
|
|
cbName - Supplies the size of the buffer at lpName
|
|
|
|
Return Value:
|
|
|
|
TRUE if a name was found, FALSE if not.
|
|
The exe name is returned as an ANSI string in lpName.
|
|
|
|
--*/
|
|
{
|
|
/*
|
|
* See if the exe name is in the image
|
|
*/
|
|
PIMAGE_OPTIONAL_HEADER pOptHdr = &pNtHdr->OptionalHeader;
|
|
PIMAGE_DEBUG_DIRECTORY pDebugDir;
|
|
IMAGE_DEBUG_DIRECTORY DebugDir;
|
|
PIMAGE_DEBUG_MISC pMisc;
|
|
PIMAGE_DEBUG_MISC pT;
|
|
DWORD rva;
|
|
int nDebugDirs;
|
|
int i;
|
|
int l;
|
|
BOOL rVal = FALSE;
|
|
|
|
nDebugDirs = pOptHdr->DataDirectory[IMAGE_DIRECTORY_ENTRY_DEBUG].Size /
|
|
sizeof(IMAGE_DEBUG_DIRECTORY);
|
|
|
|
if (!nDebugDirs) {
|
|
return FALSE;
|
|
}
|
|
|
|
rva = pOptHdr->DataDirectory[IMAGE_DIRECTORY_ENTRY_DEBUG].VirtualAddress;
|
|
|
|
for (i = 0; i < pNtHdr->FileHeader.NumberOfSections; i++) {
|
|
if (rva >= pSH[i].VirtualAddress
|
|
&& rva < pSH[i].VirtualAddress + pSH[i].SizeOfRawData) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (i >= pNtHdr->FileHeader.NumberOfSections) {
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
// this is a pointer in the debuggee image:
|
|
|
|
if (pldd->hFile == 0) {
|
|
pDebugDir = (PIMAGE_DEBUG_DIRECTORY)
|
|
((rva - pSH[i].VirtualAddress) + pSH[i].VirtualAddress);
|
|
} else {
|
|
pDebugDir = (PIMAGE_DEBUG_DIRECTORY)
|
|
(rva - pSH[i].VirtualAddress + pSH[i].PointerToRawData);
|
|
}
|
|
|
|
for (i = 0; i < nDebugDirs; i++) {
|
|
|
|
SetReadPointer((ULONG)(ULONG_PTR)(&pDebugDir[i]), FILE_BEGIN);
|
|
DoRead((LPV)&DebugDir, sizeof(DebugDir));
|
|
|
|
if (DebugDir.Type == IMAGE_DEBUG_TYPE_MISC) {
|
|
|
|
l = DebugDir.SizeOfData;
|
|
pMisc = pT = MHAlloc(l);
|
|
|
|
if (pldd->hFile == 0) {
|
|
SetReadPointer((ULONG)DebugDir.AddressOfRawData, FILE_BEGIN);
|
|
} else {
|
|
SetReadPointer((ULONG)DebugDir.PointerToRawData, FILE_BEGIN);
|
|
}
|
|
|
|
DoRead((LPV)pMisc, l);
|
|
|
|
while (l > 0) {
|
|
if (pMisc->DataType != IMAGE_DEBUG_MISC_EXENAME) {
|
|
l -= pMisc->Length;
|
|
if (l > (int)DebugDir.SizeOfData) {
|
|
l = 0; // Avoid AV on bad exe
|
|
break;
|
|
}
|
|
pMisc = (PIMAGE_DEBUG_MISC)
|
|
(((LPSTR)pMisc) + pMisc->Length);
|
|
} else {
|
|
|
|
PVOID pExeName;
|
|
|
|
pExeName = (PVOID)&pMisc->Data[0];
|
|
|
|
#if !defined(_UNICODE)
|
|
if (!pMisc->Unicode) {
|
|
_tcscpy(lpName, (LPSTR)pExeName);
|
|
rVal = TRUE;
|
|
} else {
|
|
WideCharToMultiByte(CP_ACP,
|
|
0,
|
|
(LPWSTR)pExeName,
|
|
-1,
|
|
lpName,
|
|
cbName,
|
|
NULL,
|
|
NULL);
|
|
rVal = TRUE;
|
|
}
|
|
#else
|
|
if (pMisc->Unicode) {
|
|
wcscpy(lpName, (LPTSTR)pExeName);
|
|
rVal = TRUE;
|
|
} else {
|
|
MultiByteToWideChar(CP_ACP,
|
|
0,
|
|
(LPTSTR)pExeName,
|
|
-1,
|
|
lpName,
|
|
cbName);
|
|
rVal = TRUE;
|
|
}
|
|
#endif
|
|
CopyFileNameFromDBGFileName(lpName, lpName);
|
|
|
|
break;
|
|
}
|
|
}
|
|
|
|
MHFree(pT);
|
|
|
|
break;
|
|
|
|
}
|
|
}
|
|
|
|
return rVal;
|
|
}
|
|
|
|
|
|
BOOL
|
|
GetModnameFromExportTable(
|
|
PIMAGE_NT_HEADERS pNtHdr,
|
|
PIMAGE_SECTION_HEADER pSH,
|
|
LOAD_DLL_DEBUG_INFO64 * pldd,
|
|
LPTSTR lpName,
|
|
int cbName
|
|
)
|
|
/*++
|
|
|
|
Routine Descriotion:
|
|
|
|
This routine attempts to invent an exe name for a DLL
|
|
from the module name found in the export table. This
|
|
will fail if there is no export table, so it is not
|
|
usually useful for EXEs.
|
|
|
|
Arguments:
|
|
|
|
pNtHdr - Supplies pointer to NT header in image PE header
|
|
|
|
pSH - Supplies pointer to section header table
|
|
|
|
pldd - Supplies ptr to info record from debug event
|
|
|
|
lpName - Returns name when successful
|
|
|
|
cbName - Supplies size of buffer at lpName
|
|
|
|
Return Value:
|
|
|
|
TRUE if successful and name is copied to lpName, FALSE
|
|
if not successful.
|
|
|
|
--*/
|
|
{
|
|
IMAGE_EXPORT_DIRECTORY expDir;
|
|
ULONG ExportVA;
|
|
ULONG oExports;
|
|
int iobj;
|
|
int cobj;
|
|
|
|
/*
|
|
* Find object which has the same RVA as the
|
|
* export table.
|
|
*/
|
|
|
|
cobj = pNtHdr->FileHeader.NumberOfSections;
|
|
|
|
ExportVA = pNtHdr->
|
|
OptionalHeader.
|
|
DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].
|
|
VirtualAddress;
|
|
|
|
if (!ExportVA) {
|
|
return FALSE;
|
|
}
|
|
|
|
for (iobj = 0; iobj < cobj; iobj++) {
|
|
if (pSH[iobj].VirtualAddress == ExportVA) {
|
|
oExports = pSH[iobj].PointerToRawData;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (iobj >= cobj) {
|
|
return FALSE;
|
|
}
|
|
|
|
if ((SetReadPointer(oExports, FILE_BEGIN) == -1L)
|
|
|| !DoRead(&expDir, sizeof(expDir))) {
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
SetReadPointer(oExports + (ULONG)expDir.Name - ExportVA,
|
|
FILE_BEGIN);
|
|
|
|
_ftcscpy(lpName, _T("#:\\"));
|
|
|
|
if (!DoRead(lpName + 3, cbName - 3)) {
|
|
// It's a DLL, but we can't get the name...
|
|
_stprintf(lpName + 3, _T("DLL%02d.DLL"), ++MagicModuleId);
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
typedef DWORD(WINAPI * LPFNGETNAME) (
|
|
HANDLE hProcess,
|
|
HMODULE hModule,
|
|
LPSTR lpFilename,
|
|
DWORD nSize
|
|
);
|
|
|
|
|
|
BOOL GetModNameUsingPsApi(HTHDX hthd, LOAD_DLL_DEBUG_INFO64 * pldd, LPTSTR lpName, int cbName)
|
|
/*++
|
|
Routine Description:
|
|
This routine attempts to get the fullpathname for a DLL by calling an entry point in psapi.dll.
|
|
This will fail on Win95
|
|
Arguments:
|
|
hthd - Ptr to the current thread structure.
|
|
pldd - Supplies ptr to info record from debug event
|
|
lpName - Returns name when successful
|
|
cbName - Supplies size of buffer at lpName
|
|
Return Value:
|
|
TRUE if successful and name is copied to lpName, FALSE if not successful.
|
|
--*/
|
|
{
|
|
HANDLE hModule = NULL;
|
|
BOOL fRet = FALSE;
|
|
|
|
assert(Is64PtrSE(pldd->lpBaseOfDll));
|
|
|
|
if (IsChicago()) {
|
|
return FALSE;
|
|
}
|
|
|
|
if ((hModule = LoadLibrary("psapi.dll")) != NULL) {
|
|
LPFNGETNAME ProcAddr = (LPFNGETNAME)GetProcAddress(hModule, "GetModuleFileNameExA");
|
|
|
|
if ((*ProcAddr) (hthd->hprc->rwHand,
|
|
(HMODULE)pldd->lpBaseOfDll, /* Same as hModule */
|
|
lpName,
|
|
cbName)
|
|
) {
|
|
fRet = TRUE;
|
|
}
|
|
|
|
FreeLibrary(hModule);
|
|
}
|
|
|
|
return fRet;
|
|
}
|
|
|
|
|
|
|
|
#ifdef INTERNAL
|
|
void
|
|
DeferIt(
|
|
HTHDX hthd,
|
|
DEBUG_EVENT64 * pde
|
|
)
|
|
{
|
|
PDLL_DEFER_LIST pddl;
|
|
PDLL_DEFER_LIST * ppddl;
|
|
|
|
pddl = MHAlloc(sizeof(DLL_DEFER_LIST));
|
|
pddl->next = NULL;
|
|
pddl->LoadDll = pde->u.LoadDll;
|
|
for (ppddl = &hthd->hprc->pDllDeferList; *ppddl; ) {
|
|
ppddl = &((*ppddl)->next);
|
|
}
|
|
*ppddl = pddl;
|
|
}
|
|
#endif // INTERNAL
|
|
|
|
/** FFilesIdentical
|
|
|
|
* PURPOSE:
|
|
* Determine whether a filename and a file handle refer to the same file.
|
|
|
|
* INPUT:
|
|
* szFilename: a filename
|
|
* hFile: a file handle
|
|
|
|
* OUTPUT:
|
|
* Returns TRUE if these refer to the same file.
|
|
*/
|
|
|
|
BOOL
|
|
FFilesIdentical(
|
|
LPTSTR szFileName,
|
|
HANDLE hFile
|
|
)
|
|
{
|
|
HANDLE hFile2;
|
|
BY_HANDLE_FILE_INFORMATION bhfi1, bhfi2;
|
|
BOOL fIdentical = FALSE;
|
|
|
|
hFile2 = CreateFile(szFileName, GENERIC_READ,
|
|
FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING,
|
|
FILE_ATTRIBUTE_NORMAL, 0);
|
|
|
|
if (hFile2 != INVALID_HANDLE_VALUE) {
|
|
if (GetFileInformationByHandle(hFile, &bhfi1) &&
|
|
GetFileInformationByHandle(hFile2, &bhfi2)) {
|
|
if (bhfi1.dwVolumeSerialNumber == bhfi2.dwVolumeSerialNumber &&
|
|
bhfi1.nFileIndexHigh == bhfi2.nFileIndexHigh &&
|
|
bhfi1.nFileIndexLow == bhfi2.nFileIndexLow) {
|
|
fIdentical = TRUE;
|
|
}
|
|
}
|
|
|
|
VERIFY(CloseHandle(hFile2));
|
|
}
|
|
|
|
return fIdentical;
|
|
}
|
|
|
|
/** FINDMODULE
|
|
|
|
* PURPOSE:
|
|
* Find a module (DLL or EXE) on the path.
|
|
|
|
* INPUT:
|
|
* szModName Buffer with the name of a module, e.g. "FOO.EXE",
|
|
* "BAR.DLL", etc. May have mixed case.
|
|
* cchModName Length of buffer szModName.
|
|
|
|
* OUTPUT:
|
|
* szModName Replaced with a more specific (but not necessarily
|
|
* canonical) path to the module, e.g.
|
|
* "C:\NT\SYSTEM\FOO.EXE", "MYDIR\BAR.DLL", if the
|
|
* module can be found. If it can't be found or
|
|
* there isn't enough space in the buffer, the buffer
|
|
* is left unchanged.
|
|
|
|
*/
|
|
|
|
BOOL
|
|
FindModule(
|
|
LPTSTR szModName,
|
|
UINT cchModName
|
|
)
|
|
{
|
|
LPTSTR pSlash;
|
|
LPTSTR szFullPath = (LPTSTR)_alloca(cchModName * sizeof(TCHAR));
|
|
|
|
/*
|
|
** We call SearchPath which is a convenient way to
|
|
** find a file along the path that Windows uses to load a DLL.
|
|
** REVIEW: BUG: In order to find the DLL correctly, the call to
|
|
** SearchPath must be made with the Current Directory set to the
|
|
** Current Directory of the process that has just loaded the DLL.
|
|
*/
|
|
DWORD result = SearchPath(NULL, szModName, NULL, cchModName, szFullPath, &pSlash);
|
|
if ((result != 0) && (result != cchModName)) {
|
|
_tcscpy(szModName, szFullPath);
|
|
return TRUE;
|
|
}
|
|
|
|
return(FALSE);
|
|
}
|
|
|
|
/** GetNTDebugModuleFileNameLastChance
|
|
|
|
* PURPOSE:
|
|
* Do the best job we can of getting a filename for an EXE or DLL
|
|
* which has just been loaded. There is no good way to do this under
|
|
* NT, so we kludge around a lot. For EXEs, we saved the name of
|
|
* the EXE we started before starting it. For DLLs, we look in the
|
|
* export table of the DLL for its name, which may or may not be
|
|
* correct.
|
|
|
|
* INPUT:
|
|
* hProcess Handle to the process in question (either the
|
|
* process which has just started, or the process
|
|
* for which a DLL has just been loaded)
|
|
* hFile Handle to disk file for the EXE or DLL
|
|
* lpBaseOfImage Address in hProcess's address space of the
|
|
* beginning of the newly loaded EXE or DLL
|
|
* cchModName Size of szModName
|
|
|
|
* OUTPUT:
|
|
* szModName Buffer where module name (possibly with path)
|
|
* is written
|
|
|
|
*/
|
|
|
|
BOOL
|
|
FGetNTFileName(
|
|
HTHDX hthd,
|
|
PDLLLOAD_ITEM pdll,
|
|
HANDLE hFile,
|
|
LPTSTR szModName,
|
|
UINT cchModName
|
|
)
|
|
{
|
|
BOOL fRet = FindModule(szModName, cchModName);
|
|
|
|
/* If this isn't the right filename, keep looking ... */
|
|
if (FFilesIdentical(szModName, hFile)) {
|
|
fRet = TRUE;
|
|
} else {
|
|
/* Try to determine the filename from the handle, by whatever
|
|
** means available
|
|
*/
|
|
#if 1
|
|
fRet = FALSE;
|
|
#else
|
|
if (!FTrojanGetDebugModuleFileName(hthd, pdll, szModName, cchModName)) {
|
|
/* We shouldn't get here! We've got to do everything
|
|
** we possibly can to get the filename, because if we don't
|
|
** get the filename, we'll have major problems later on.
|
|
** If we ever hit the assertion, we've just got to come up
|
|
** with some other ways to try to determine the filename.
|
|
*/
|
|
assert(FALSE);
|
|
} else {
|
|
fRet = TRUE;
|
|
}
|
|
#endif
|
|
}
|
|
return(fRet);
|
|
}
|
|
|
|
|
|
/** FIXCASE
|
|
|
|
* PURPOSE:
|
|
* Fix the upper/lower case of a filename so that it matches what
|
|
* is on disk. If the disk in question supports mixed-case
|
|
* filenames, we change the name we have to match the case of the
|
|
* name on disk; otherwise, we set the name to lower case (because
|
|
* that's prettier than upper case).
|
|
|
|
* INPUT:
|
|
* szFilename Name of file.
|
|
|
|
* OUTPUT:
|
|
* szFilename Upper/lower case changed.
|
|
|
|
*/
|
|
|
|
VOID
|
|
FixCase(
|
|
LPTSTR szFilename
|
|
)
|
|
{
|
|
|
|
#ifdef WIN32
|
|
|
|
TCHAR rgchDrive[4]; /* "X:\" */
|
|
LPTSTR szDrive;
|
|
DWORD dwFlags;
|
|
WIN32_FIND_DATA wfd;
|
|
LPTSTR pch;
|
|
LPTSTR pchStart;
|
|
TCHAR ch;
|
|
HANDLE hSearch;
|
|
|
|
if (szFilename[0] && szFilename[1] == _T(':')) {
|
|
_stprintf(rgchDrive, _T("%c:\\"), szFilename[0]);
|
|
szDrive = rgchDrive;
|
|
} else {
|
|
szDrive = NULL;
|
|
}
|
|
|
|
if (GetVolumeInformation(szDrive, NULL, 0, NULL, NULL, &dwFlags, NULL, 0)
|
|
&&
|
|
(dwFlags & FS_CASE_IS_PRESERVED)
|
|
) {
|
|
|
|
/*
|
|
** For each filename component, check what case it has on disk.
|
|
*/
|
|
pch = szFilename;
|
|
|
|
if (pch[0] && pch[1] == _T(':')) { /* path has drive letter? */
|
|
*pch = (TCHAR)_totupper(*pch); /* upper case drive letter */
|
|
pch += 2;
|
|
}
|
|
|
|
if (*pch == _T('/') || *pch == _T('\\')) {
|
|
*pch = _T('\\');
|
|
++pch;
|
|
}
|
|
|
|
while (*pch) {
|
|
|
|
pchStart = pch;
|
|
|
|
while (*pch && *pch != _T('\\') && *pch != _T('/')) {
|
|
pch = _tcsinc(pch);
|
|
}
|
|
|
|
ch = *pch;
|
|
*pch = _T('\0');
|
|
|
|
/*
|
|
** Find this filename component
|
|
*/
|
|
hSearch = FindFirstFile(szFilename, &wfd);
|
|
|
|
/*
|
|
** If the search failed, or if it returned a name of a
|
|
** different length than the one we asked for (e.g. we
|
|
** asked for "FOO." and it gave us "FOO"), we'll give
|
|
** up and convert the rest of the name to lower case
|
|
*/
|
|
if (hSearch == INVALID_HANDLE_VALUE ||
|
|
_ftcslen(pchStart) != _ftcslen(wfd.cFileName)) {
|
|
*pch = ch;
|
|
_ftcslwr(pchStart);
|
|
return;
|
|
}
|
|
|
|
/*
|
|
** Copy the correct case into our filename
|
|
*/
|
|
_tcscpy(pchStart, wfd.cFileName);
|
|
|
|
/*
|
|
** Close the search
|
|
*/
|
|
assert(!FindNextFile(hSearch, &wfd));
|
|
VERIFY(FindClose(hSearch));
|
|
|
|
/*
|
|
** Restore the slash or NULL
|
|
*/
|
|
*pch = ch;
|
|
|
|
/*
|
|
** If we're on a separator, move to next filename component
|
|
*/
|
|
if (*pch) {
|
|
*pch = _T('\\');
|
|
pch++;
|
|
}
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
#endif
|
|
|
|
/*
|
|
** Convert to lower case, with backslashes
|
|
*/
|
|
|
|
_ftcslwr(szFilename);
|
|
|
|
for (; *szFilename; szFilename = _tcsinc(szFilename)) {
|
|
if (*szFilename == _T('/')) {
|
|
*szFilename = _T('\\');
|
|
}
|
|
}
|
|
}
|
|
|
|
void
|
|
FixFilename(
|
|
TCHAR * szTempFilename,
|
|
const TCHAR * szInFilename
|
|
)
|
|
/*++
|
|
|
|
/** FIXFILENAME
|
|
|
|
* PURPOSE:
|
|
* Fix the upper/lower case of a filename and so that it matches what
|
|
* is on disk. Also if we have a 8.3 name for a long filename
|
|
* convert the whole path to its long filename equivalen.
|
|
* If the disk in question supports mixed-case
|
|
* filenames, we change the name we have to match the case of the
|
|
* name on disk; otherwise, we set the name to lower case (because
|
|
* that's prettier than upper case).
|
|
|
|
* INPUT:
|
|
* szInFilename Name of file. .
|
|
|
|
* OUTPUT:
|
|
* szTempFilename Upper/lower case changed, long filename variant.
|
|
* Assumes size is big enough to hold long
|
|
* filename
|
|
|
|
* Note: Win95 has an API to do this (called via an int 31h or somesuch),
|
|
* but NT has no such API. Why? To make everyone go through this pain I guess.
|
|
|
|
*/
|
|
|
|
{
|
|
CHAR rgchDrive[4]; /* "X:\" */
|
|
CHAR * szDrive;
|
|
DWORD dwFlags;
|
|
WIN32_FIND_DATA wfd;
|
|
CHAR * pch;
|
|
CHAR * pchTemp;
|
|
CHAR * pchStart;
|
|
CHAR ch;
|
|
HANDLE hSearch;
|
|
CHAR szFilename[512];
|
|
|
|
_tcscpy(szFilename, szInFilename); // make local copy
|
|
|
|
if (szFilename[0] && szFilename[1] == ':') {
|
|
sprintf(rgchDrive, "%c:\\", szFilename[0]);
|
|
szDrive = rgchDrive;
|
|
} else {
|
|
szDrive = NULL;
|
|
}
|
|
|
|
if (!GetVolumeInformation(szDrive, NULL, 0, NULL, NULL, &dwFlags, NULL, 0)
|
|
||
|
|
!(dwFlags & FS_CASE_IS_PRESERVED)
|
|
) {
|
|
|
|
_tcscpy(szTempFilename, szFilename); // just copy it if not case sensitive
|
|
|
|
} else {
|
|
|
|
|
|
/*
|
|
** For each filename component, check what case it has on disk.
|
|
*/
|
|
pch = szFilename;
|
|
pchTemp = szTempFilename;
|
|
|
|
if (pch[0] && pch[1] == ':') { /* path has drive letter? */
|
|
_tcsncpy(pchTemp, pch, 2);
|
|
|
|
/* upper case drive letter */
|
|
*pchTemp = (char)_totupper((*pchTemp)); /* upper case drive letter */
|
|
pch += 2;
|
|
pchTemp += 2;
|
|
}
|
|
|
|
if (*pch == '/' || *pch == '\\') {
|
|
*pch = *pchTemp = '\\';
|
|
++pchTemp;
|
|
++pch;
|
|
}
|
|
|
|
while (*pch) {
|
|
size_t iLen;
|
|
|
|
pchStart = pch;
|
|
|
|
while (*pch && *pch != '\\' && *pch != '/') {
|
|
pch = _tcsinc(pch);
|
|
}
|
|
|
|
ch = *pch;
|
|
*pch = '\0';
|
|
|
|
/*
|
|
** Find this filename component
|
|
*/
|
|
hSearch = FindFirstFile(szFilename, &wfd);
|
|
|
|
/*
|
|
** If the search failed, we'll give
|
|
** up and convert the rest of the name to lower case
|
|
*/
|
|
if (hSearch == INVALID_HANDLE_VALUE) {
|
|
*pch = ch;
|
|
CharLower(pchStart);
|
|
// Copy over the rest of the filename to the temporary buffer.
|
|
// this will now have the best we can do about converting
|
|
// this filename.
|
|
_tcscpy(pchTemp, pchStart);
|
|
|
|
_tcscpy(szFilename, szTempFilename);
|
|
|
|
return;
|
|
}
|
|
|
|
/*
|
|
** Copy the correct name into the temp filename,
|
|
*/
|
|
iLen = _tcslen(wfd.cFileName);
|
|
_tcsncpy(pchTemp, wfd.cFileName, iLen);
|
|
pchTemp += iLen;
|
|
|
|
/*
|
|
** Close the search
|
|
*/
|
|
assert(!FindNextFile(hSearch, &wfd));
|
|
FindClose(hSearch);
|
|
|
|
/*
|
|
** Restore the slash or NULL
|
|
*/
|
|
*pch = ch;
|
|
|
|
/*
|
|
** If we're on a separator, move to next filename component
|
|
*/
|
|
if (*pch) {
|
|
*pchTemp = *pch = '\\';
|
|
pch++; pchTemp++;
|
|
}
|
|
}
|
|
|
|
*pchTemp = '\0';
|
|
}
|
|
}
|
|
|
|
|
|
BOOL
|
|
LoadDll(
|
|
DEBUG_EVENT64 * de,
|
|
HTHDX hthd,
|
|
LPWORD lpcbPacket,
|
|
LPBYTE * lplpbPacket,
|
|
BOOL fThreadIsStopped
|
|
)
|
|
/*++
|
|
Routine Description:
|
|
This routine is used to load the signification information about
|
|
a PE exe file. This information consists of the name of the exe
|
|
just loaded (hopefully this will be provided later by the OS) and
|
|
a description of the sections in the exe file.
|
|
Arguments:
|
|
de - Supplies a pointer to the current debug event
|
|
hthd - Supplies a pointer to the current thread structure
|
|
lpcbPacket - Returns the count of bytes in the created packet
|
|
lplpbPacket - Returns the pointer to the created packet
|
|
Return Value:
|
|
True on success and FALSE on failure
|
|
--*/
|
|
{
|
|
LOAD_DLL_DEBUG_INFO64 * ldd = &de->u.LoadDll;
|
|
LPMODULELOAD lpmdl;
|
|
TCHAR szModName[512];
|
|
TCHAR szAnsiName[512];
|
|
DWORD cbObject;
|
|
ULONG64 offset;
|
|
DWORD lenSz, lenTable;
|
|
DWORD cobj, iobj;
|
|
DWORD isecTLS;
|
|
IMAGE_DOS_HEADER dosHdr;
|
|
IMAGE_NT_HEADERS ntHdr;
|
|
IMAGE_SECTION_HEADER * rgSecHdr = NULL;
|
|
HANDLE hFile;
|
|
int iDll;
|
|
HPRCX hprc = hthd->hprc;
|
|
DWORD cb;
|
|
TCHAR rgch[512];
|
|
LPVOID lpv;
|
|
LPTSTR lpsz;
|
|
ADDR addr;
|
|
BOOL fTlsPresent;
|
|
OFFSET off;
|
|
TCHAR fname[_MAX_FNAME];
|
|
TCHAR ext[_MAX_EXT];
|
|
|
|
|
|
if (hprc->pstate & (ps_killed | ps_dead)) {
|
|
// Process is dead, don't bother doing anything.
|
|
return FALSE;
|
|
}
|
|
|
|
assert(Is64PtrSE(ldd->lpBaseOfDll));
|
|
|
|
// Remember this dll in the process dll list.
|
|
|
|
|
|
// run through the list and see if there is already an entry for this address
|
|
assert(Is64PtrSE(ldd->lpBaseOfDll));
|
|
for (iDll = 0; iDll < hprc->cDllList; iDll += 1) {
|
|
if (hprc->rgDllList[iDll].fValidDll) {
|
|
assert(Is64PtrSE(hprc->rgDllList[iDll].offBaseOfImage));
|
|
if (hprc->rgDllList[iDll].offBaseOfImage == ldd->lpBaseOfDll) {
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (iDll == hprc->cDllList) {
|
|
// didn't find it; find an empty record
|
|
for (iDll = 0; iDll < hprc->cDllList; iDll += 1) {
|
|
if (!hprc->rgDllList[iDll].fValidDll) {
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (iDll == hprc->cDllList) {
|
|
// the dll list needs to be expanded
|
|
hprc->cDllList += 10;
|
|
if (!hprc->rgDllList) {
|
|
hprc->rgDllList = (PDLLLOAD_ITEM)MHAlloc(sizeof(DLLLOAD_ITEM) * 10);
|
|
memset(hprc->rgDllList, 0, sizeof(DLLLOAD_ITEM) * 10);
|
|
} else {
|
|
hprc->rgDllList = MHRealloc(hprc->rgDllList, hprc->cDllList * sizeof(DLLLOAD_ITEM));
|
|
memset(&hprc->rgDllList[hprc->cDllList - 10], 0, 10 * sizeof(DLLLOAD_ITEM));
|
|
}
|
|
} else if (hprc->rgDllList[iDll].offBaseOfImage != ldd->lpBaseOfDll) {
|
|
memset(&hprc->rgDllList[iDll], 0, sizeof(DLLLOAD_ITEM));// if this is an empty entry, make sure it is cleared.
|
|
}
|
|
|
|
|
|
// stick the demarcator at the start of the string so that we
|
|
// don't have to move the string over later.
|
|
|
|
|
|
*szModName = CMODULEDEMARCATOR;
|
|
|
|
|
|
// Process the DOS header. It is currently regarded as mandatory
|
|
|
|
// This has to be read before attempting to resolve the
|
|
// name of a module on NT. If the name resolution is aborted,
|
|
// the memory allocated for the IMAGE_SECTION_HEADER must be
|
|
// freed.
|
|
|
|
if (ldd->hFile == 0) {
|
|
assert(Is64PtrSE(ldd->lpBaseOfDll));
|
|
SetPointerToMemory(hprc, ldd->lpBaseOfDll);
|
|
} else {
|
|
SetPointerToFile(ldd->hFile);
|
|
}
|
|
|
|
SetReadPointer(0, FILE_BEGIN);
|
|
|
|
if (DoRead(&dosHdr, sizeof(dosHdr)) == FALSE) {
|
|
DPRINT(1, (_T("ReadFile got error %u\r\n"), GetLastError()));
|
|
return FALSE;
|
|
}
|
|
|
|
// Read in the PE header record
|
|
if ((dosHdr.e_magic != IMAGE_DOS_SIGNATURE) || (SetReadPointer(dosHdr.e_lfanew, FILE_BEGIN) == -1L)) {
|
|
return FALSE;
|
|
}
|
|
|
|
if (!DoRead(&ntHdr, sizeof(ntHdr))) {
|
|
return FALSE;
|
|
}
|
|
|
|
// test whether we have a TLS directory
|
|
fTlsPresent = !!ntHdr.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_TLS].Size;
|
|
if (sizeof(ntHdr.OptionalHeader) != ntHdr.FileHeader.SizeOfOptionalHeader) {
|
|
SetReadPointer(ntHdr.FileHeader.SizeOfOptionalHeader - sizeof(ntHdr.OptionalHeader), FILE_CURRENT);
|
|
}
|
|
|
|
// Save off the count of objects in the dll/exe file
|
|
cobj = ntHdr.FileHeader.NumberOfSections;
|
|
|
|
// Save away the offset in the file where the object table
|
|
// starts. We will need this later to get information about
|
|
// each of the objects.
|
|
rgSecHdr = (IMAGE_SECTION_HEADER *)MHAlloc(cobj * sizeof(IMAGE_SECTION_HEADER));
|
|
if (!DoRead(rgSecHdr, cobj * sizeof(IMAGE_SECTION_HEADER))) {
|
|
assert(FALSE);
|
|
MHFree(rgSecHdr);
|
|
return FALSE;
|
|
}
|
|
|
|
// if the dll record is marked valid, it already matches the dll
|
|
if (hprc->rgDllList[iDll].fValidDll) {
|
|
// in this case we are re-doing a mod load for a dll
|
|
// that is part of a process that is being reconnected
|
|
assert(hprc->rgDllList[iDll].szDllName != NULL);
|
|
_tcscpy(szModName + 1, hprc->rgDllList[iDll].szDllName);
|
|
} else {
|
|
if (CrashDump) {
|
|
CopyFileNameFromDBGFileName(szModName + 1, (PVOID)ldd->lpImageName);
|
|
if ((hprc->rgDllList[iDll].szDllName = MHAlloc(_tcslen(szModName + 1) + 1)) != NULL) {
|
|
_tcscpy(hprc->rgDllList[iDll].szDllName, szModName + 1);
|
|
} else {
|
|
return FALSE;
|
|
}
|
|
|
|
// Normal case: image name is in the debug event
|
|
} else if (((PVOID)ldd->lpImageName != NULL)
|
|
&& DbgReadMemory(hprc, ldd->lpImageName, &lpv, sizeof(lpv), (int *)&cb)
|
|
&& (cb == sizeof(lpv))
|
|
&& (lpv != NULL)
|
|
&& DbgReadMemory(hprc, SEPtrTo64(lpv), rgch, sizeof(rgch), (int *)&cb)) {
|
|
// we're happy...
|
|
#if !defined(_UNICODE)
|
|
if (!ldd->fUnicode) {
|
|
FixFilename(szModName + 1, rgch);
|
|
} else {
|
|
WideCharToMultiByte(CP_ACP,
|
|
0,
|
|
(LPWSTR)rgch,
|
|
-1,
|
|
szAnsiName,
|
|
_tsizeof(szAnsiName),
|
|
NULL,
|
|
NULL);
|
|
FixFilename(szModName + 1, szAnsiName);
|
|
}
|
|
if ((hprc->rgDllList[iDll].szDllName = MHAlloc(_tcslen(szModName + 1) + 1)) != NULL) {
|
|
_tcscpy(hprc->rgDllList[iDll].szDllName, szModName + 1);
|
|
} else {
|
|
return FALSE;
|
|
}
|
|
#else
|
|
if (ldd->fUnicode) {
|
|
FixFilename(szModName + 1, rgch);
|
|
} else {
|
|
MultiByteToWideChar(CP_ACP, 0, (LPTSTR)rgch, -1, szAnsiName, _tsizeof(szAnsiName));
|
|
FixFilename(szModName + 1, szAnsiName);
|
|
}
|
|
if ((hprc->rgDllList[iDll].szDllName = MHAlloc((_tcslen(szModName + 1) + 1)) * sizeof(TCHAR)) != NULL) {
|
|
_tcscpy(hprc->rgDllList[iDll].szDllName, szModName + 1);
|
|
} else {
|
|
return FALSE;
|
|
}
|
|
#endif
|
|
|
|
}
|
|
|
|
// If *nameBuffer != 0 then we know we are really
|
|
// dealing with the root exe and we can steal the
|
|
// name from there.
|
|
else if (*nameBuffer && !FLoading16) {
|
|
if (FDMRemote) {
|
|
_tsplitpath(nameBuffer, NULL, NULL, fname, ext);
|
|
sprintf(szModName + 1, _T("#:\\%s%s"), fname, ext);
|
|
} else {
|
|
_tcscpy(szModName + 1, nameBuffer); // do NOT FixFilename this one
|
|
}
|
|
|
|
if ((hprc->rgDllList[iDll].szDllName = MHAlloc(_tcslen(szModName + 1) + 1)) != NULL) {
|
|
_tcscpy(hprc->rgDllList[iDll].szDllName, szModName + 1);
|
|
} else {
|
|
return FALSE;
|
|
}
|
|
|
|
// Try to use PSAPI stuff to get the image name
|
|
} else if (GetModNameUsingPsApi(hthd, ldd, rgch, _tsizeof(rgch))) {
|
|
// cool...
|
|
FixFilename(szModName + 1, rgch);
|
|
if ((hprc->rgDllList[iDll].szDllName = MHAlloc(_tcslen(szModName + 1) + 1)) != NULL) {
|
|
_tcscpy(hprc->rgDllList[iDll].szDllName, szModName + 1);
|
|
} else {
|
|
return FALSE;
|
|
}
|
|
|
|
// Look for a debug misc resord in the image
|
|
} else if (GetModnameFromImage(&ntHdr, rgSecHdr, ldd, rgch, _tsizeof(rgch))) {
|
|
// joyful...
|
|
lpsz = _ftcsrchr(rgch, _T('\\'));
|
|
if (!lpsz) {
|
|
lpsz = _ftcsrchr(rgch, _T(':'));
|
|
}
|
|
if (lpsz) {
|
|
lpsz = _ftcsinc(lpsz);
|
|
} else {
|
|
lpsz = rgch;
|
|
}
|
|
|
|
#if defined(DOLPHIN)
|
|
if (FGetNTFileName(hthd, &hprc->rgDllList[iDll], ldd->hFile, rgch, _tsizeof(rgch))) {
|
|
_ftcscpy(szModName + 1, rgch);
|
|
} else
|
|
#endif
|
|
{
|
|
_ftcscpy(szModName + 1, _T("#:\\"));
|
|
_ftcscpy(szModName + 4, lpsz);
|
|
}
|
|
|
|
if ((hprc->rgDllList[iDll].szDllName = MHAlloc(_tcslen(lpsz) + 1)) != NULL) {
|
|
_tcscpy(hprc->rgDllList[iDll].szDllName, lpsz);
|
|
} else {
|
|
return FALSE;
|
|
}
|
|
|
|
// If it is a dll, it probably exports something, so it has a module name.
|
|
} else if (GetModnameFromExportTable(&ntHdr, rgSecHdr, ldd, rgch, _tsizeof(rgch))) {
|
|
// serene...
|
|
_ftcscpy(szModName + 1, rgch);
|
|
if ((hprc->rgDllList[iDll].szDllName = MHAlloc(_tcslen(rgch) + 1)) != NULL) {
|
|
_tcscpy(hprc->rgDllList[iDll].szDllName, rgch);
|
|
} else {
|
|
return FALSE;
|
|
}
|
|
|
|
// if it isn't available, make something up.
|
|
} else {
|
|
// hopeless...
|
|
#if defined(DOLPHIN)
|
|
if (!LoadString(hInstance, IDS_UnknownExe, rgch, _tsizeof(rgch))) {
|
|
assert(FALSE);
|
|
}
|
|
if (FGetNTFileName(hthd, &hprc->rgDllList[iDll], ldd->hFile, rgch, _tsizeof(rgch))) {
|
|
_ftcscpy(szModName + 1, rgch);
|
|
} else
|
|
#endif
|
|
sprintf(szModName + 1, _T("#:\\APP%02d.EXE"), ++MagicModuleId);
|
|
if ((hprc->rgDllList[iDll].szDllName = MHAlloc(_tcslen(szModName + 1) + 1)) != NULL) {
|
|
_tcscpy(hprc->rgDllList[iDll].szDllName, szModName + 1);
|
|
} else {
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
if (!FLoading16) {
|
|
*nameBuffer = 0;
|
|
}
|
|
}
|
|
|
|
// for remote case, kill the drive letter to
|
|
// prevent finding same exe on wrong platform,
|
|
// except when user gave path to exe.
|
|
|
|
if (fUseRealName) {
|
|
fUseRealName = FALSE;
|
|
}
|
|
lenSz = _ftcslen(szModName);
|
|
DPRINT(10, (_T("** LoadDll %s base=%I64x\n"), szModName, ldd->lpBaseOfDll));
|
|
|
|
szModName[lenSz] = 0;
|
|
|
|
lpsz = _ftcsrchr(szModName, _T('\\'));
|
|
if (!lpsz) {
|
|
lpsz = _ftcsrchr(szModName, _T(':'));
|
|
}
|
|
if (lpsz) {
|
|
lpsz = _ftcsinc(lpsz);
|
|
} else {
|
|
lpsz = szModName;
|
|
}
|
|
|
|
if (_ftcsicmp(lpsz, _T("kernel32.dll")) == 0) {
|
|
hprc->dwKernel32Base = (UINT_PTR)ldd->lpBaseOfDll;
|
|
}
|
|
|
|
assert(Is64PtrSE(hprc->rgDllList[iDll].offBaseOfImage));
|
|
assert(Is64PtrSE(ldd->lpBaseOfDll));
|
|
|
|
if (hprc->rgDllList[iDll].offBaseOfImage != ldd->lpBaseOfDll) {
|
|
// new dll to add to the list
|
|
hprc->rgDllList[iDll].fValidDll = TRUE;
|
|
hprc->rgDllList[iDll].offBaseOfImage = (OFFSET)ldd->lpBaseOfDll;
|
|
hprc->rgDllList[iDll].cbImage = ntHdr.OptionalHeader.SizeOfImage;
|
|
}
|
|
|
|
// Find address of OLE RPC tracing export (if any)
|
|
|
|
// If hFile is NULL, we cannot do any RPC debugging. This may happen
|
|
// in certain platforms. So, check for this.
|
|
|
|
if (ldd->hFile != NULL) {
|
|
FGetExport(&hprc->rgDllList[iDll],
|
|
(HFILE)(DWORD_PTR)ldd->hFile,
|
|
_T("DllDebugObjectRPCHook"),
|
|
&hprc->rgDllList[iDll].lpvOleRpc);
|
|
} else {
|
|
hprc->rgDllList[iDll].lpvOleRpc = NULL;
|
|
}
|
|
|
|
DMSqlLoadDll(hprc, ldd, iDll);
|
|
|
|
szModName[lenSz] = CMODULEDEMARCATOR;
|
|
|
|
if (/* FDMRemote */ TRUE) {
|
|
if (ldd->hFile != 0 && ldd->hFile != (HANDLE)-1) {
|
|
CloseHandle(ldd->hFile); // don't need this anymore
|
|
}
|
|
hFile = (HANDLE)-1; // remote: can't send file handle across wire
|
|
} else {
|
|
if (ldd->hFile == 0) {
|
|
hFile = (HANDLE)-1;
|
|
} else {
|
|
hFile = ldd->hFile; // local: let SH use our handle
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Make up a record to send back from the name.
|
|
* Additionally send back:
|
|
* The file handle (if local)
|
|
* The load base of the dll
|
|
* The time and date stamp of the exe
|
|
* The checksum of the file
|
|
*/
|
|
assert(Is64PtrSE(ldd->lpBaseOfDll));
|
|
sprintf(szModName + lenSz + 1, _T("0x%08lX%c0x%08lX%c0x%016I64X%c0x%016I64X%c%08lX%c"),
|
|
ntHdr.FileHeader.TimeDateStamp, CMODULEDEMARCATOR,
|
|
ntHdr.OptionalHeader.CheckSum, CMODULEDEMARCATOR,
|
|
SEPtrTo64(hFile), CMODULEDEMARCATOR,
|
|
ldd->lpBaseOfDll, CMODULEDEMARCATOR,
|
|
ntHdr.OptionalHeader.SizeOfImage, CMODULEDEMARCATOR
|
|
);
|
|
lenSz = _ftcslen(szModName);
|
|
/*
|
|
* Allocate the packet which will be sent across to the EM.
|
|
* The packet will consist of:
|
|
* The MODULELOAD structure sizeof(MODULELOAD) +
|
|
* The section description array cobj*sizeof(OBJD) +
|
|
* The name of the DLL lenSz+1
|
|
*/
|
|
|
|
lenTable = (cobj * sizeof(OBJD));
|
|
*lpcbPacket = (WORD)(sizeof(MODULELOAD) + lenTable + (lenSz + 1) * sizeof(TCHAR));
|
|
*lplpbPacket = (LPBYTE)(lpmdl = (LPMODULELOAD)MHAlloc(*lpcbPacket));
|
|
lpmdl->lpBaseOfDll = ldd->lpBaseOfDll;
|
|
lpmdl->cobj = cobj;
|
|
lpmdl->mte = (WORD)-1;
|
|
|
|
#ifdef TARGET_i386
|
|
lpmdl->CSSel = (unsigned short)hthd->context.SegCs;
|
|
lpmdl->DSSel = (unsigned short)hthd->context.SegDs;
|
|
#else
|
|
lpmdl->CSSel = lpmdl->DSSel = 0;
|
|
#endif // i386
|
|
|
|
/*
|
|
* Set up the descriptors for each of the section headers
|
|
* so that the EM can map between section numbers and flat addresses.
|
|
*/
|
|
|
|
lpmdl->uoffDataBase = 0;
|
|
for (iobj = 0; iobj < cobj; iobj++) {
|
|
OLESEG oleseg;
|
|
|
|
offset = rgSecHdr[iobj].VirtualAddress + ldd->lpBaseOfDll;
|
|
cbObject = rgSecHdr[iobj].Misc.VirtualSize;
|
|
if (cbObject == 0) {
|
|
cbObject = rgSecHdr[iobj].SizeOfRawData;
|
|
}
|
|
|
|
lpmdl->rgobjd[iobj].offset = (UOFFSET)offset;
|
|
lpmdl->rgobjd[iobj].cb = cbObject;
|
|
lpmdl->rgobjd[iobj].wPad = 1;
|
|
|
|
#if defined(TARGET_i386)
|
|
if (IMAGE_SCN_CNT_CODE & rgSecHdr[iobj].Characteristics) {
|
|
lpmdl->rgobjd[iobj].wSel = (WORD)hthd->context.SegCs;
|
|
} else {
|
|
lpmdl->rgobjd[iobj].wSel = (WORD)hthd->context.SegDs;
|
|
}
|
|
#else
|
|
lpmdl->rgobjd[iobj].wSel = 0;
|
|
#endif // TARGET_i386
|
|
|
|
if (!_fmemcmp(rgSecHdr[iobj].Name, ".data\0\0", IMAGE_SIZEOF_SHORT_NAME)) {
|
|
if (lpmdl->uoffDataBase == 0) {
|
|
lpmdl->uoffDataBase = offset;
|
|
}
|
|
}
|
|
/*
|
|
* If the section is one of the special OLE segments, we
|
|
* keep track of the address ranges in the OLERG structure.
|
|
*/
|
|
if ((oleseg = GetOleSegType(rgSecHdr[iobj].Name)) != olenone) {
|
|
OLERG FAR * lpolerg;
|
|
DWORD i;
|
|
|
|
hprc->rgDllList[iDll].fContainsOle = TRUE;
|
|
|
|
/* expand (or create) our buffer */
|
|
++hprc->colerg;
|
|
if (hprc->rgolerg) {
|
|
hprc->rgolerg = MHRealloc(hprc->rgolerg,
|
|
hprc->colerg * sizeof(OLERG));
|
|
} else {
|
|
hprc->rgolerg = MHAlloc(hprc->colerg * sizeof(OLERG));
|
|
}
|
|
|
|
/* find place to insert new OLE range */
|
|
for (i = 0; i < hprc->colerg - 1; ++i) {
|
|
if (offset < hprc->rgolerg[i].uoffMin) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* insert an OLERG */
|
|
memmove(&hprc->rgolerg[i + 1],
|
|
&hprc->rgolerg[i],
|
|
sizeof(OLERG) * (hprc->colerg - i - 1));
|
|
|
|
/* insert new OLE range */
|
|
lpolerg = &hprc->rgolerg[i];
|
|
lpolerg->uoffMin = offset;
|
|
lpolerg->uoffMax = offset + cbObject;
|
|
lpolerg->segType = oleseg;
|
|
}
|
|
if (fTlsPresent && !_fmemcmp(rgSecHdr[iobj].Name, ".tls\0\0\0", IMAGE_SIZEOF_SHORT_NAME)) {
|
|
isecTLS = iobj + 1;
|
|
}
|
|
}
|
|
|
|
lpmdl->fRealMode = FALSE;
|
|
lpmdl->fFlatMode = TRUE;
|
|
lpmdl->fOffset32 = TRUE;
|
|
lpmdl->dwSizeOfDll = ntHdr.OptionalHeader.SizeOfImage;
|
|
lpmdl->uoffiTls = 0;
|
|
lpmdl->isecTLS = 0;
|
|
lpmdl->iTls = 0; // cache it later on demand if uoffiTls != 0
|
|
lpmdl->fThreadIsStopped = fThreadIsStopped;
|
|
|
|
/*
|
|
* Copy the name of the dll to the end of the packet.
|
|
*/
|
|
_fmemcpy(((BYTE *)&lpmdl->rgobjd) + lenTable, szModName, lenSz + 1);
|
|
|
|
/*
|
|
* Locate the TLS section if one exists. If so then get the
|
|
* pointer to the TLS index
|
|
|
|
* Structure at the address is:
|
|
* VA lpRawData
|
|
* ULONG cbRawData
|
|
* VA lpIndex
|
|
* VA lpCallBacks
|
|
*/
|
|
|
|
if (ntHdr.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_TLS].VirtualAddress != 0) {
|
|
if ((DbgReadMemory(hprc,
|
|
ntHdr.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_TLS].VirtualAddress + ldd->lpBaseOfDll + 8,
|
|
&off,
|
|
sizeof(OFFSET),
|
|
&cb) == 0) ||
|
|
(cb != sizeof(OFFSET))) {
|
|
assert(FALSE);
|
|
}
|
|
|
|
hprc->rgDllList[iDll].offTlsIndex = off;
|
|
lpmdl->uoffiTls = off;
|
|
lpmdl->isecTLS = isecTLS;
|
|
}
|
|
|
|
/*
|
|
* free up the memory used for holding the section headers
|
|
*/
|
|
MHFree(rgSecHdr);
|
|
|
|
if (fDisconnected) {
|
|
// this will prevent the dm from sending a message up to
|
|
// the shell. the dm's data structures are setup just fine
|
|
// so that when the debugger re-connects we can deliver the
|
|
// mod loads correctly.
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
} /* LoadDll() */
|
|
|
|
|
|
VOID
|
|
UnloadAllModules(
|
|
HPRCX hprc,
|
|
HTHDX hthd,
|
|
BOOL AlwaysNotify,
|
|
BOOL ReallyDestroy
|
|
)
|
|
{
|
|
DEBUG_EVENT64 de;
|
|
DWORD i;
|
|
|
|
for (i = 0; i < (DWORD)hprc->cDllList; i++) {
|
|
if (hprc->rgDllList[i].fValidDll) {
|
|
|
|
assert(Is64PtrSE(hprc->rgDllList[i].offBaseOfImage));
|
|
|
|
if (!hprc->rgDllList[i].fWow) {
|
|
de.dwDebugEventCode = UNLOAD_DLL_DEBUG_EVENT;
|
|
de.dwProcessId = hprc->pid;
|
|
de.dwThreadId = hthd ? hthd->tid : 0;
|
|
de.u.UnloadDll.lpBaseOfDll = hprc->rgDllList[i].offBaseOfImage;
|
|
if (AlwaysNotify || !IsChicago()) {
|
|
NotifyEM(&de, hthd, 0, 0);
|
|
}
|
|
if (ReallyDestroy) {
|
|
DestroyDllLoadItem(&hprc->rgDllList[i]);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
VOID
|
|
ReloadUsermodeModules(
|
|
HTHDX hthd,
|
|
PTCHAR String
|
|
)
|
|
{
|
|
int i;
|
|
DEBUG_EVENT64 de;
|
|
HPRCX hprc = hthd->hprc;
|
|
LPRTP rtp;
|
|
TCHAR szTmpDllName[_MAX_PATH * 2];
|
|
|
|
|
|
// All or nothing... Ignore the String arg for now.
|
|
|
|
|
|
|
|
|
|
// First unload everything:
|
|
|
|
|
|
UnloadAllModules(hthd->hprc, hthd, TRUE, FALSE);
|
|
|
|
|
|
// reload it all
|
|
|
|
|
|
for (i = 0; i < hprc->cDllList; i++) {
|
|
if (hprc->rgDllList[i].fValidDll) {
|
|
LPBYTE lpbPacket;
|
|
WORD cbPacket;
|
|
|
|
assert(Is64PtrSE(hprc->rgDllList[i].offBaseOfImage));
|
|
|
|
de.dwDebugEventCode = LOAD_DLL_DEBUG_EVENT;
|
|
de.dwProcessId = hprc->pid;
|
|
de.dwThreadId = hthd->tid;
|
|
|
|
|
|
// We have closed the file already...
|
|
|
|
de.u.LoadDll.hFile = NULL;
|
|
|
|
de.u.LoadDll.lpBaseOfDll = hprc->rgDllList[i].offBaseOfImage;
|
|
de.u.LoadDll.fUnicode = FALSE;
|
|
|
|
// Copy the name to a tmp before we free it in the
|
|
// 'DestroyDllLoadItem' function.
|
|
|
|
if (!hprc->rgDllList[i].szDllName) {
|
|
de.u.LoadDll.lpImageName = 0;
|
|
} else {
|
|
_tcsncpy(szTmpDllName, hprc->rgDllList[i].szDllName, sizeof(szTmpDllName) / sizeof(TCHAR));
|
|
szTmpDllName[sizeof(szTmpDllName) / sizeof(TCHAR) - 1] = 0;
|
|
de.u.LoadDll.lpImageName = (UINT_PTR)szTmpDllName;
|
|
}
|
|
|
|
// We destroy the entry to make room. By doing it here, we make sure
|
|
// we can always load the max amount instead of the max amount-1.
|
|
|
|
DestroyDllLoadItem(&hprc->rgDllList[i]);
|
|
|
|
if (LoadDll(&de, hthd, &cbPacket, &lpbPacket, FALSE) || (cbPacket == 0)) {
|
|
NotifyEM(&de, hthd, cbPacket, (UINT_PTR)lpbPacket);
|
|
}
|
|
}
|
|
}
|
|
|
|
// tell the shell that the !reload is finished
|
|
rtp = (LPRTP)MHAlloc(FIELD_OFFSET(RTP, rgbVar) + sizeof(DWORD));
|
|
rtp->hpid = hthd->hprc->hpid;
|
|
rtp->htid = hthd->htid;
|
|
rtp->dbc = dbcServiceDone;
|
|
rtp->cb = sizeof(DWORD);
|
|
*(LPDWORD)rtp->rgbVar = 1;
|
|
DmTlFunc(tlfDebugPacket, rtp->hpid, FIELD_OFFSET(RTP, rgbVar) + rtp->cb, (LONG_PTR)rtp);
|
|
MHFree(rtp);
|
|
}
|
|
|
|
|
|
BOOL DMWaitForDebugEvent(LPDEBUG_EVENT64 de64, DWORD timeout)
|
|
{
|
|
#if defined (_WIN64)
|
|
return WaitForDebugEvent((LPDEBUG_EVENT)de64, timeout);
|
|
#else
|
|
DEBUG_EVENT de;
|
|
BOOL rval;
|
|
|
|
rval = WaitForDebugEvent(&de, timeout);
|
|
if (rval) {
|
|
DebugEvent32To64((LPDEBUG_EVENT32)&de, de64);
|
|
}
|
|
|
|
return rval;
|
|
#endif
|
|
} |