Windows2000/private/windbg64/debugger/dm/dmx32.c
2020-09-30 17:12:32 +02:00

7139 lines
188 KiB
C

/*++
Copyright (c) 1993 Microsoft Corporation
Module Name:
dmx32.c
Author:
Wesley Witt (wesw) 15-Aug-1992
Environment:
NT 3.1
--*/
#include "precomp.h"
#pragma hdrstop
#include "simpldis.h"
#include "dmsql.h"
#include "fiber.h"
DBF *lpdbf;
#undef LOCAL
typedef enum {
Image_Unknown,
Image_16,
Image_32,
Image_Dump
} IMAGETYPE;
typedef IMAGETYPE *PIMAGETYPE;
enum {
System_DmpInvalid = -3, /* The DUMP can not be debugged */
System_DmpWrongPlatform = -2, /* The cross-platform debugging */
/* of DUMPs is not supported */
System_Invalid = -1, /* The exe can not be debugged */
System_Console = 1, /* The exe needs a console */
System_GUI = 0 /* The exe is a Windows exe */
};
int pCharMode(LPTSTR szAppName, PIMAGETYPE Image);
int nVerbose = MIN_VERBOSITY_LEVEL;
// std{in,out,err} for redirection of console apps.
// See ProcessDebuggeeRedirection()
static HANDLE rgh[3] = {0, 0, 0};
DMTLFUNCTYPE DmTlFunc = NULL;
BOOL FDMRemote = FALSE; // set true for remote debug
BOOL FUseOutputDebugString = FALSE;
BOOL FChicago = FALSE; // set true if running on Win95.
EXPECTED_EVENT masterEE = {0L,0L};
EXPECTED_EVENT *eeList = &masterEE;
static HTHDXSTRUCT masterTH = {0L,0L};
HTHDX thdList = &masterTH;
static HPRCXSTRUCT masterPR = {0L,0L};
HPRCX prcList = &masterPR;
// control access to thread and process lists:
CRITICAL_SECTION csThreadProcList;
CRITICAL_SECTION csEventList;
// control access to Walk list
CRITICAL_SECTION csWalk;
HPID hpidRoot = (HPID)INVALID; // this hpid is our hook to the native EM
BOOL fUseRoot; // next CREATE_PROCESS will use hpidRoot
DEBUG_EVENT64 falseSSEvent;
DEBUG_EVENT64 falseBPEvent;
DEBUG_EVENT64 FuncExitEvent;
METHOD EMNotifyMethod;
// Don't allow debug event processing during some shell operations
CRITICAL_SECTION csProcessDebugEvent;
// Event handles for synchronizing with the shell on proc/thread creates.
HANDLE hEventCreateProcess;
HANDLE hEventContinue;
// event handle for synchronizing connnect/reconnect with the tl
HANDLE hEventRemoteQuit;
HANDLE hEventNoDebuggee; // set when no debuggee is attached
int nWaitingForLdrBreakpoint = 0;
BOOL FLoading16 = FALSE;
BOOL fDisconnected = FALSE;
#ifdef DEBUG_API_INTERLOCK
static int ApiLockCount = 0;
static PCSTR pszLastFileToLock = NULL;
static int nLastFileToLock = 0;
static PCSTR pszLastFileToUnlock = NULL;
static int nLastFileToUnlock = 0;
VOID
TakeApiLockFunc(
PCSTR pszFile,
int nLine
)
{
if (ApiLockCount
&& (ULONG_PTR)GetCurrentThreadId() != (ULONG_PTR)csApiInterlock.OwningThread ) {
DPRINT(-2, ( (" ** WARN: Waiting on another thread %s %d\n"), pszFile, nLine) );
//assert(!"API lock released when not owned");
}
EnterCriticalSection(&csApiInterlock);
if (ApiLockCount++ > 1) {
DPRINT(-2, ( (" ** ERROR: Locked twice. TakeApiLock %s %d\n"), pszFile, nLine) );
assert(!"API locked twice");
}
DPRINT(-1, ( (" ** TakeApiLock %s %d\n"), pszFile, nLine) );
pszLastFileToLock = pszFile;
nLastFileToLock = nLine;
}
BOOL
TryApiLockFunc(
PCSTR pszFile,
int nLine
)
{
BOOL b = TryEnterCriticalSection(&csApiInterlock);
if (b) {
++ApiLockCount;
DPRINT(-1, ( ("TryApiLock %s %d\n"), pszFile, nLine) );
pszLastFileToLock = pszFile;
nLastFileToLock = nLine;
}
if (ApiLockCount > 1) {
DPRINT(-2, ( (" ** ERROR: Locked twice. TryApiLock %s %d\n"), pszFile, nLine) );
assert(!"API locked twice");
}
return b;
}
VOID
ReleaseApiLockFunc(
PCSTR pszFile,
int nLine
)
{
DPRINT(-1, ( (" ** ReleaseApiLock %s %d\n"), pszFile, nLine) )
if ((ULONG_PTR)GetCurrentThreadId() != (ULONG_PTR)csApiInterlock.OwningThread ) {
DPRINT(-2, ( (" ** ERROR: Not owner. ReleaseApiLock %s %d\n"), pszFile, nLine) );
assert(!"API lock released when not owned");
}
if (ApiLockCount < 1) {
DPRINT(-2, ( (" ** ERROR: Count is 0. ReleaseApiLock %s %d\n"), pszFile, nLine) );
assert(!"API Lock released when count is 0");
} else {
--ApiLockCount;
LeaveCriticalSection(&csApiInterlock);
}
pszLastFileToUnlock = pszFile;
nLastFileToUnlock = nLine;
}
#endif // DEBUG_API_INTERLOCK
#ifndef KERNEL
// There may be multiple pending debug events.
LIST_ENTRY DmListOfPendingDebugEvents;
LIST_ENTRY DmFreeListOfDebugEvents;
PDETOSAVE GetNextFreeDebugEvent();
// crash dump stuff
BOOL CrashDump;
PCONTEXT CrashContext;
PEXCEPTION_RECORD CrashException;
PUSERMODE_CRASHDUMP_HEADER CrashDumpHeader;
ULONG64 KiPcrBaseAddress;
ULONG64 KiProcessors;
HANDLE hDmPollThread = 0; // Handle for event loop thread.
BOOL fDmPollQuit = FALSE; // tell poll thread to exit NOW
SYSTEM_INFO SystemInfo;
OSVERSIONINFO OsVersionInfo;
WT_STRUCT WtStruct; // .. for wt
VOID
Cleanup(
VOID
);
DWORD WINAPI
CallDmPoll(
LPVOID lpv
);
VOID
CrashDumpThread(
LPVOID lpv
);
void
Close3Handles(
HANDLE *rgh
);
XOSD
ProcessDebuggeeRedirection(
LPTSTR lszCommandLine,
STARTUPINFO FAR * psi
);
BOOL
SetDebugPrivilege(
void
);
#endif // !KERNEL
#if defined(TARGET_IA64) || defined(TARGET_AXP64) //while we don't have a good crashlib, etc.
BOOL
DmpGetThread(
IN ULONG Processor,
OUT PCRASH_THREAD Thread
) { return FALSE; }
BOOL
DmpDetectVersionParameters(
CRASHDUMP_VERSION_INFO* VersionInfo
) { return FALSE; }
BOOL
DmpInitialize (
IN LPSTR FileName,
OUT PCONTEXT *Context,
OUT PEXCEPTION_RECORD *Exception,
OUT PVOID *DmpHeader
) { return FALSE; }
DWORD
DmpReadMemory (
IN ULONG64 BaseAddress,
IN PVOID Buffer,
IN ULONG Size
) { return FALSE; }
BOOL
DmpGetContext(
IN ULONG Processor,
OUT PVOID Context
) { return FALSE; }
#define xosdDumpInvalidFile xosdUnsupported
#define xosdDumpWrongPlatform xosdUnsupported
#endif
BOOL DmpIsItAUserModeFile(VOID) { return FALSE; }
BOOL DmpUserModeTestHeader(VOID) { return FALSE; }
#if defined(TARGET_IA64) // AXP64 has disasm
VOID CleanupDisassembler(VOID) { };
#endif
#ifdef KERNEL
extern BOOL fCrashDump;
KDOPTIONS KdOptions[] = {
_T("BaudRate"), KDO_BAUDRATE, KDT_DWORD, 9600,
_T("Port"), KDO_PORT, KDT_DWORD, 2,
_T("Cache"), KDO_CACHE, KDT_DWORD, 8192,
_T("Verbose"), KDO_VERBOSE, KDT_DWORD, 0,
_T("InitialBp"), KDO_INITIALBP, KDT_DWORD, 0,
_T("Defer"), KDO_DEFER, KDT_DWORD, 0,
_T("UseModem"), KDO_USEMODEM, KDT_DWORD, 0,
_T("LogfileAppend"), KDO_LOGFILEAPPEND, KDT_DWORD, 0,
_T("GoExit"), KDO_GOEXIT, KDT_DWORD, 0,
_T("SymbolPath"), KDO_SYMBOLPATH, KDT_STRING, 0,
_T("LogfileName"), KDO_LOGFILENAME, KDT_STRING, 0,
_T("CrashDump"), KDO_CRASHDUMP, KDT_STRING, 0
};
VOID
GetKernelSymbolAddresses(
VOID
);
MODULEALIAS ModuleAlias[MAX_MODULEALIAS];
#endif // KERNEL
TCHAR nameBuffer[256];
// Reply buffers to and from em
char abEMReplyBuf[4096]; // Buffer for EM to reply to us in
char abDMReplyBuf[4096]; // Buffer for us to reply to EM requests in
LPDM_MSG LpDmMsg = (LPDM_MSG)abDMReplyBuf;
// To send a reply of the struct msMyStruct, do this:
// LpDmMsg->xosdRet = xosdMyReturnValue
// memcpy (LpDmMsg->rgb, &msMyStruct, sizeof (msMyStruct));
// Reply (sizeof (msMyStruct), LpDmMsg, hpid);
DDVECTOR DebugDispatchTable[] = {
ProcessExceptionEvent,
ProcessCreateThreadEvent,
ProcessCreateProcessEvent,
ProcessExitThreadEvent,
ProcessExitProcessEvent,
ProcessLoadDLLEvent,
ProcessUnloadDLLEvent,
ProcessOutputDebugStringEvent,
ProcessRipEvent,
ProcessBreakpointEvent,
NULL, /* CHECK_BREAKPOINT_DEBUG_EVENT */
ProcessSegmentLoadEvent, /* SEGMENT_LOAD_DEBUG_EVENT */
NULL, /* DESTROY_PROCESS_DEBUG_EVENT */
NULL, /* DESTROY_THREAD_DEBUG_EVENT */
NULL, /* ATTACH_DEADLOCK_DEBUG_EVENT */
NULL, /* ATTACH_EXITED_DEBUG_EVENT */
ProcessEntryPointEvent, /* ENTRYPOINT_DEBUG_EVENT */
NULL, /* LOAD_COMPLETE_DEBUG_EVENT */
NULL, /* INPUT_DEBUG_STRING_EVENT */
NULL, /* MESSAGE_DEBUG_EVENT */
NULL, /* MESSAGE_SEND_DEBUG_EVENT */
NULL, /* FUNC_EXIT_EVENT */
#ifdef KERNEL
NULL,
NULL,
#else
ProcessOleEvent, /* OLE_DEBUG_EVENT */
ProcessFiberEvent,
#endif
NULL, /* GENERIC_DEBUG_EVENT */
#ifdef KERNEL
NULL,
#else
ProcessBogusSSEvent, /* BOGUS_WIN95_SINGLESTEP_EVENT */
#endif
NULL /* MAX_EVENT_CODE */
};
/*
* This array contains the set of default actions to be taken for
* all debug events if the thread has the "In Function Evaluation"
* bit set.
*/
DDVECTOR RgfnFuncEventDispatch[] = {
EvntException,
NULL, /* This can never happen */
NULL, /* This can never happen */
ProcessExitThreadEvent,
EvntExitProcess,
ProcessLoadDLLEvent, /* Use normal processing */
ProcessUnloadDLLEvent, /* Use normal processing */
ProcessOutputDebugStringEvent, /* Use normal processing */
NULL,
EvntBreakpoint, /* Breakpoint processor */
NULL,
ProcessSegmentLoadEvent, /* WOW event */
NULL, /* DESTROY_PROCESS_DEBUG_EVENT */
NULL, /* DESTROY_THREAD_DEBUG_EVENT */
NULL, /* ATTACH_DEADLOCK_DEBUG_EVENT */
NULL, /* ATTACH_EXITED_DEBUG_EVENT */
ProcessEntryPointEvent, /* ENTRYPOINT_DEBUG_EVENT */
NULL, /* LOAD_COMPLETE_DEBUG_EVENT */
NULL, /* INPUT_DEBUG_STRING_EVENT */
NULL, /* MESSAGE_DEBUG_EVENT */
NULL, /* MESSAGE_SEND_DEBUG_EVENT */
NULL, /* FUNC_EXIT_EVENT */
NULL, /* OLE_DEBUG_EVENT */
NULL, /* FIBER_DEBUG_EVENT */
NULL, /* GENERIC_DEBUG_EVENT */
#ifdef KERNEL
NULL, /* BOGUS_WIN95_SINGLESTEP_EVENT */
#else
ProcessBogusSSEvent, /* BOGUS_WIN95_SINGLESTEP_EVENT */
#endif
NULL /* MAX_EVENT_CODE */
};
void UNREFERENCED_PARAMETERS(LPVOID lpv,...)
{
lpv=NULL;
}
SPAWN_STRUCT SpawnStruct; // packet for causing CreateProcess()
DEBUG_ACTIVE_STRUCT DebugActiveStruct; // ... for DebugActiveProcess()
#ifndef KERNEL
RELOAD_STRUCT ReloadStruct; // for !reload, usermode
#endif
PKILLSTRUCT KillQueue;
CRITICAL_SECTION csKillQueue;
BOOL IsExceptionIgnored(HPRCX, DWORD);
TCHAR SearchPathString[ 10000 ];
BOOL SearchPathSet;
BOOL fUseRealName = FALSE;
HINSTANCE hInstance; // The DM DLL's hInstance
LPTSTR
FmtMsg(
int msgid
)
{
void * lpb;
if (FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER |
FORMAT_MESSAGE_IGNORE_INSERTS |
FORMAT_MESSAGE_FROM_SYSTEM |
0, 0, msgid, 0, (LPTSTR)&lpb, 0, 0))
{
return (LPTSTR) lpb;
}
else
{
return NULL;
}
}
#include "resource.h"
TCHAR szErrArg[_MAX_PATH];
LPTSTR
LszFromErr(
DWORD dwErr
)
{
#define NTERR(name) ERROR_##name, IDS_##name,
static struct {
DWORD dwErr;
UINT idsErr;
} mpiszErr [] = {
NTERR(FILE_NOT_FOUND)
NTERR(PATH_NOT_FOUND)
NTERR(INVALID_HANDLE)
NTERR(INVALID_DRIVE)
NTERR(INVALID_PARAMETER)
NTERR(INVALID_NAME)
NTERR(BAD_PATHNAME)
NTERR(FILENAME_EXCED_RANGE)
NTERR(OUTOFMEMORY)
NTERR(NOT_ENOUGH_MEMORY)
NTERR(ACCESS_DENIED)
NTERR(SHARING_VIOLATION)
NTERR(OPEN_FAILED)
NTERR(BAD_FORMAT)
NTERR(CHILD_NOT_COMPLETE)
NTERR(INVALID_MODULETYPE)
NTERR(INVALID_EXE_SIGNATURE)
NTERR(EXE_MARKED_INVALID)
NTERR(BAD_EXE_FORMAT)
NTERR(DIRECTORY)
0, IDS_UNKNOWN_ERROR
};
static TCHAR rgchErr[256+_MAX_PATH]; // must be static!
static TCHAR rgchErrNum[256];
LPTSTR sz = rgchErr;
LPTSTR lszFmtErr = NULL;
int i;
if (!LoadString(hInstance, IDS_COULD_NOT_LOAD_DEBUGGEE,
sz, (int)(rgchErr + _tsizeof(rgchErr) - sz))) {
assert(FALSE);
}
sz += _tcslen(sz);
// If there's an argument to put in the string, put it here
if (szErrArg[0]) {
_tcscpy(sz, szErrArg);
_tcscat(sz, _T(": "));
sz += _tcslen(sz);
}
for (i = 0; mpiszErr[i].dwErr; ++i) {
if (mpiszErr[ i ].dwErr == dwErr) {
break;
}
}
// If we didn't find an error string in our list, call FormatMessage
// to get an error message from the operating system.
if (mpiszErr[i].dwErr == 0) {
lszFmtErr = FmtMsg(dwErr);
}
// If we got an error message from the operating system, display that,
// otherwise display our own message.
if (lszFmtErr) {
_tcsncpy(sz, lszFmtErr, (int)(rgchErr + _tsizeof(rgchErr) - sz));
rgchErr[_tsizeof(rgchErr)-1] = _T('\0');
}
else {
// Even if we got through the above loop without finding a match,
// we're okay, because the mpiszErr[] table ends with "unknown error".
if (!LoadString(hInstance, mpiszErr[i].idsErr,
sz, (int)(rgchErr+_tsizeof(rgchErr)-sz))) {
assert(FALSE);
}
}
sz += _tcslen(sz);
if (!LoadString(hInstance, IDS_NTError,
rgchErrNum, _tsizeof(rgchErrNum))) {
assert(FALSE);
}
sz += _stprintf(sz, rgchErrNum, dwErr);
return rgchErr;
}
void
SendNTError(
HPRCX hprc,
DWORD dwErr,
LPTSTR lszString
)
{
LPTSTR lszError;
if (lszString) {
lszError = lszString;
}
else {
lszError = (LPTSTR) LszFromErr(dwErr);
}
SendDBCError(hprc, dwErr, lszError);
}
void
SendDBCError(
HPRCX hprc,
DWORD dwErr,
LPTSTR lszString
)
{
typedef struct _LE {
XOSD xosd;
TCHAR rgchErr[];
} LE; // load error: passed with dbcError
LE FAR * ple;
LPRTP lprtp;
DWORD cbBuf;
LPCTSTR lszError;
if (lszString) {
lszError = lszString;
}
else {
lszError = ""; // Empty string.
}
cbBuf = FIELD_OFFSET(RTP, rgbVar) + sizeof(LE) + (_tcslen(lszError) + 1)*sizeof(TCHAR);
lprtp = (LPRTP) MHAlloc(cbBuf);
assert(lprtp);
ple = (LE *) (lprtp->rgbVar);
ple->xosd = (XOSD) dwErr;
_tcscpy(ple->rgchErr, lszError);
lprtp->dbc = dbcError;
if (hprc != NULL) {
lprtp->hpid= hprc->hpid;
} else {
lprtp->hpid = hpidRoot;
}
lprtp->htid = NULL;
lprtp->cb = cbBuf;
DmTlFunc(tlfDebugPacket, lprtp->hpid, lprtp->cb, (LPARAM) lprtp);
MHFree(lprtp);
}
BOOL
ResolveFile(
LPTSTR lpName,
LPTSTR lpFullName,
BOOL fUseRealName
)
{
DWORD dwAttr;
LPTSTR lpFilePart;
BOOL fOk;
if (fUseRealName) {
dwAttr = GetFileAttributes(lpName);
fOk = ((dwAttr != 0xffffffff)
&& ((dwAttr & FILE_ATTRIBUTE_DIRECTORY) == 0));
if (fOk) {
_ftcscpy(lpFullName, lpName);
}
} else {
fOk = SearchPath(SearchPathString,
lpName,
NULL,
MAX_PATH,
lpFullName,
&lpFilePart
);
if (!fOk) {
*lpFullName = 0;
}
}
return fOk;
}
#ifndef KERNEL
XOSD
Load(
HPRCX hprc,
LPCTSTR szAppName,
LPCTSTR szArg,
LPVOID pattrib,
LPVOID tattrib,
DWORD creationFlags,
BOOL fInheritHandles,
CONST LPCTSTR* environment,
LPCTSTR currentDirectory,
STARTUPINFO FAR * pStartupInfo,
LPPROCESS_INFORMATION lppi
)
/*++
Routine Description:
Arguments:
Return Value:
TRUE if the process was successfully created and FALSE otherwise.
--*/
{
XOSD xosd;
int type;
TCHAR ch, chTerm;
int fQuotedFileName;
int l;
IMAGETYPE Image;
LPTSTR lpch;
// Just spawning an exec, not debugging
BOOL fSpawnOrphan = !(creationFlags & DEBUG_PROCESS || creationFlags & DEBUG_ONLY_THIS_PROCESS);
static TCHAR szFullName[MAX_PATH];
static TCHAR szCommandLine[8192];
static TCHAR szCurrentDir[MAX_PATH]; // Directory to spawn the debuggee in.
Unreferenced( pattrib );
Unreferenced( tattrib );
Unreferenced( creationFlags );
/* NOTE: Might have to do the same sort of copying for
* szArg, pattrib, tattrib and
* startupInfo. Determine if this is necessary.
*/
// global flag to help with special handling of DOS/WOW apps.
FLoading16 = FALSE;
// Form the command line.
// First, we extract the program name and get its full path. Then
// we append the arguments.
if (szAppName[0] == _T('"')) {
// If the user specified a quoted name (ie: a Long File Name, perhaps?),
// terminate on the next quote.
chTerm = _T('"');
fQuotedFileName=TRUE;
szAppName++; // Advance past the quote.
} else {
// No Quote. Search for the first space.
chTerm = _T(' ');
fQuotedFileName=FALSE;
}
// Null terminate the command line
if ( (_ftcslen(szAppName) > 2 && szAppName[1] == _T(':'))
|| szAppName[0] == _T('\\')) {
_ftcscpy(szCommandLine, szAppName);
fUseRealName = TRUE;
} else if (_ftcschr(szAppName, _T('\\')) || !SearchPathSet) {
_ftcscpy(szCommandLine, _T(".\\") );
_ftcscat(szCommandLine, szAppName );
fUseRealName = TRUE;
} else {
if (!*SearchPathString) {
_ftcscpy(SearchPathString, _T(".;"));
l = 2;
l += GetSystemDirectory(SearchPathString+l,
_tsizeof(SearchPathString)-l);
SearchPathString[l++] = _T(';');
l += GetWindowsDirectory(SearchPathString+l,
_tsizeof(SearchPathString)-l);
SearchPathString[l++] = _T(';');
GetEnvironmentVariable(_T("PATH"),
SearchPathString+l,
_tsizeof(SearchPathString)-l);
}
_ftcscpy(szCommandLine, szAppName);
fUseRealName = FALSE;
}
if (fQuotedFileName) {
szAppName--;
}
// to be similar to the shell, we look for:
// .COM
// .EXE
// nothing
// since '.' is a valid filename character on many filesystems,
// we don't automatically assume that anything following a '.'
// is an "extension." If the extension is "COM" or "EXE", leave
// it alone; otherwise try the extensions.
lpch = _ftcschr(szCommandLine, _T('.'));
if (lpch &&
( lpch[1] == 0
|| _ftcsicmp(lpch, _T(".COM")) == 0
|| _ftcsicmp(lpch, _T(".EXE")) == 0)
) {
lpch = NULL;
} else {
lpch = szCommandLine + _ftcslen(szCommandLine);
}
*szFullName = 0;
if (lpch) {
_ftcscpy(lpch, _T(".COM"));
ResolveFile(szCommandLine, szFullName, fUseRealName);
}
if (!*szFullName && lpch) {
_ftcscpy(lpch, _T(".EXE"));
ResolveFile(szCommandLine, szFullName, fUseRealName);
}
if (!*szFullName) {
if (lpch) {
*lpch = 0;
}
ResolveFile(szCommandLine, szFullName, fUseRealName);
}
if (!*szFullName) {
return xosdFileNotFound;
}
// Let's do some error checking
type = pCharMode(szFullName, &Image);
if (System_DmpInvalid == type) {
return xosdDumpInvalidFile;
} else if (System_DmpWrongPlatform == type) {
return xosdDumpWrongPlatform;
} else if (System_Invalid == type) {
return xosdFileNotFound;
} else {
switch ( Image ) {
case Image_Unknown:
// treat as a com file
//return xosdBadFormat;
case Image_16:
FLoading16 = TRUE;
#if defined(TARGET_i386)
if ( (type == System_GUI) &&
!(creationFlags & CREATE_SEPARATE_WOW_VDM) &&
IsWOWPresent() )
{
// TODO need dbcError here
return xosdGeneral;
}
break;
#else
// TODO all platforms will suppport this
return xosdGeneral;
#endif
default:
break;
}
}
creationFlags |= (type?CREATE_NEW_CONSOLE:0);
// Make sure arg1 (the app's name) is enclosed in quotes
if ('"' != *szCommandLine) {
// Prepend a double quote
PTSTR psz = _ftcsdup(szCommandLine);
_ftcscpy(szCommandLine, "\"");
_ftcscat(szCommandLine, psz);
free(psz);
}
if ('"' != szCommandLine[_tcslen(szCommandLine)-1]) {
// Append a double quote
_ftcscat(szCommandLine, "\"");
}
// Add rest of arguments
_ftcscat(szCommandLine, " "); // Ensure a space just incase
_ftcscat(szCommandLine, szArg);
if (Image == Image_Dump) {
// must be a crash dump file
if (!StartCrashPollThread()) {
return xosdUnknown;
}
return xosdNone;
}
if (!fSpawnOrphan) {
if (!StartDmPollThread()) {
return xosdUnknown;
}
}
// Handle the current directory
if ( currentDirectory == NULL ) {
TCHAR szDir[_MAX_DIR];
// szCurrentDir is temporarily used to get the drive letter in it.
_tsplitpath(szFullName, szCurrentDir, szDir, NULL, NULL);
// After the _tcscat szCurrentDir will have the right value.
_tcscat(szCurrentDir, szDir);
} else {
_tcscpy(szCurrentDir, currentDirectory);
}
ResetEvent(SpawnStruct.hEventApiDone);
SpawnStruct.szAppName = szFullName;
xosd = ProcessDebuggeeRedirection (szCommandLine, pStartupInfo);
if (xosd != xosdNone) {
return xosd;
}
if (pStartupInfo -> dwFlags & STARTF_USESTDHANDLES && !fInheritHandles) {
// If we're redirecting the debuggee's STDIO, inheritHandles must be set.
assert (FALSE);
fInheritHandles = TRUE;
}
if (!_ftcslen (szCommandLine)) {
// In Win 95, if the Args are an empty string,
// the AppName is interpreted as with arguments. This fixes it.
SpawnStruct.szArgs = NULL;
} else {
// parse any IO redirection
SpawnStruct.szArgs = szCommandLine;
}
if (!_ftcslen(szCurrentDir)) {
SpawnStruct.pszCurrentDirectory = NULL;
} else {
SpawnStruct.pszCurrentDirectory = szCurrentDir;
}
SpawnStruct.fdwCreate = creationFlags;
SpawnStruct.si = *pStartupInfo;
SpawnStruct.fInheritHandles = fInheritHandles;
// The second argument to get command line should have both the exe name
// and the command line args. GetCommandLine & argv[] are based on the second
// argument and we need the exe name to appear there.
if (fSpawnOrphan) {
// If we're not debugging, it's because we're doing an Execute
// (aka SpawnOrphan), so just do it.
SpawnStruct.fReturn = CreateProcess(
SpawnStruct.szAppName,
SpawnStruct.szArgs,
NULL,
NULL,
SpawnStruct.fInheritHandles,
SpawnStruct.fdwCreate,
NULL,
SpawnStruct.pszCurrentDirectory,
&SpawnStruct.si,
lppi);
Close3Handles(rgh);
if (!SpawnStruct.fReturn) {
SpawnStruct.dwError = GetLastError();
}
} else {
// This is a semaphore! Set it last!
SpawnStruct.fSpawn = TRUE;
if (WaitForSingleObject( SpawnStruct.hEventApiDone, INFINITE ) != 0) {
SpawnStruct.fReturn = FALSE;
SpawnStruct.dwError = GetLastError();
}
}
if (SpawnStruct.fReturn) {
xosd = xosdNone;
} else {
DPRINT(1, (_T("Failed.\n")));
xosd = xosdGeneral;
// make a dbcError with SpawnStruct.dwError
SendNTError(hprc, SpawnStruct.dwError, NULL);
}
return xosd;
}
#endif // !KERNEL
HPRCX
InitProcess(
HPID hpid
)
/*++
Routine Description:
Arguments:
Return Value:
--*/
{
HPRCX hprc;
/*
* Create a process structure, place it
* at the head of the master list.
*/
hprc = (HPRCX)MHAlloc(sizeof(HPRCXSTRUCT));
memset(hprc, 0, sizeof(*hprc));
EnterCriticalSection(&csThreadProcList);
hprc->next = prcList->next;
prcList->next = hprc;
hprc->hpid = hpid;
hprc->exceptionList = NULL;
hprc->pid = (PID)-1; // Indicates prenatal process
hprc->pstate = 0;
hprc->cLdrBPWait = 0;
hprc->hExitEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
hprc->hEventCreateThread = CreateEvent(NULL, TRUE, TRUE, NULL);
hprc->f16bit = FALSE;
#ifndef KERNEL
hprc->pFbrCntx = NULL; //Ignore Fibers
hprc->FbrLst = NULL;
hprc->OrpcDebugging = ORPC_NOT_DEBUGGING;
hprc->dwKernel32Base = 0;
hprc->llnlg = LLInit(sizeof ( NLG ), llfNull, NULL, NLGComp );
#endif
InitExceptionList(hprc);
LeaveCriticalSection(&csThreadProcList);
return hprc;
}
void
ActionDebugNewReady(
DEBUG_EVENT64 * pde,
HTHDX hthd,
DWORDLONG unused,
ULONG64 lparam
)
/*++
Routine Description:
This function is called when a new child process is ready to run.
The process is in exactly the same state as in ActionAllDllsLoaded.
However, in this case the debugger is not waiting for a reply.
Arguments:
Return Value:
--*/
{
HPRCX hprc = (HPRCX)lparam;
XOSD xosd = xosdNone;
#if defined(INTERNAL)
LPBYTE lpbPacket;
WORD cbPacket;
PDLL_DEFER_LIST pddl;
PDLL_DEFER_LIST pddlT;
DEBUG_EVENT64 de;
#endif
DPRINT(5, (_T("Child finished loading\n")));
#ifdef TARGET_i386
hthd->fContextDirty = FALSE; // Undo the change made in ProcessDebugEvent
#endif
hprc->pstate &= ~ps_preStart; // Clear the loading state flag
hprc->pstate |= ps_preEntry; // next stage...
hthd->tstate |= ts_stopped; // Set that we have stopped on event
--nWaitingForLdrBreakpoint;
#if 0
SendDllLoads(hthd);
#endif
#if defined(INTERNAL)
hprc->fNameRequired = TRUE;
for (pddl = hprc->pDllDeferList; pddl; pddl = pddlT) {
pddlT = pddl->next;
de.dwDebugEventCode = LOAD_DLL_DEBUG_EVENT;
de.dwProcessId = pde->dwProcessId;
de.dwThreadId = pde->dwThreadId;
de.u.LoadDll = pddl->LoadDll;
if (LoadDll(&de, hthd, &cbPacket, &lpbPacket, FALSE) && (cbPacket != 0)) {
NotifyEM(&de, hthd, cbPacket, lpbPacket);
}
MHFree(pddl);
}
hprc->pDllDeferList = NULL;
#endif
/*
* Prepare to stop on thread entry point
*/
SetupEntryBP(hthd);
/*
* leave it stopped and notify the debugger.
*/
#if defined(TARGET_ALPHA) || defined(TARGET_AXP64) || defined(TARGET_IA64)
SetBPFlag(hthd, EMBEDDED_BP);
#endif
pde->dwDebugEventCode = LOAD_COMPLETE_DEBUG_EVENT;
NotifyEM(pde, hthd, 0, 0L);
return;
} /* ActionDebugNewReady() */
void
ActionDebugActiveReady(
DEBUG_EVENT64 * pde,
HTHDX hthd,
DWORDLONG unused,
ULONG64 lparam
)
/*++
Routine Description:
This function is called when a newly attached process is ready to run.
This process is not the same as the previous two. It is either running
or at an exception, and a thread has been created by DebugActiveProcess
for the sole purpose of hitting a breakpoint.
If we have an event handle, it needs to be signalled before the
breakpoint is continued.
Arguments:
Return Value:
--*/
{
HPRCX hprc = (HPRCX)lparam;
XOSD xosd = xosdNone;
DPRINT(5, (_T("Active process finished loading\n")));
#ifdef TARGET_i386
hthd->fContextDirty = FALSE; // Undo the change made in ProcessDebugEvent
#endif // i386
hprc->pstate &= ~ps_preStart;
hthd->tstate |= ts_stopped; // Set that we have stopped on event
--nWaitingForLdrBreakpoint;
#if 0
// BUGBUG kentf this is not wise. As implemented, this will run code
// in the crashed debuggee, calling LoadLibrary and other foolish things.
SendDllLoads(hthd);
#endif
/*
* If this is a crashed process, tell the OS
* to raise the exception.
* Tell the EM that we are finished loading;
* it will say GO, and we will catch the exception
* soon after.
*/
if (pde->dwProcessId == DebugActiveStruct.dwProcessId) {
if (DebugActiveStruct.hEventGo) {
SetEvent(DebugActiveStruct.hEventGo);
CloseHandle(DebugActiveStruct.hEventGo);
}
DebugActiveStruct.dwProcessId = 0;
DebugActiveStruct.hEventGo = 0;
if (DebugActiveStruct.hProcess != INVALID_HANDLE_VALUE) {
CloseHandle(DebugActiveStruct.hProcess);
DebugActiveStruct.hProcess = INVALID_HANDLE_VALUE;
}
SetEvent(DebugActiveStruct.hEventReady);
}
#if defined(TARGET_ALPHA) || defined(TARGET_AXP64) || defined(TARGET_IA64)
SetBPFlag(hthd, EMBEDDED_BP);
#endif
pde->dwDebugEventCode = LOAD_COMPLETE_DEBUG_EVENT;
NotifyEM(pde, hthd, 0, 0L);
return;
} /* ActionDebugActiveReady() */
void
ActionEntryPoint16(
DEBUG_EVENT64 * pde,
HTHDX hthdx,
DWORDLONG unused,
DWORDLONG lparam
)
/*++
Routine Description:
This is the registered event routine called when vdm
sends a DBG_TASKSTART notification.
Arguments:
Return Value:
None
--*/
{
hthdx->hprc->pstate &= ~ps_preEntry;
hthdx->tstate |= ts_stopped;
NotifyEM(pde, hthdx, 0, ENTRY_BP);
}
void
ActionEntryPoint(
DEBUG_EVENT64 * pde,
HTHDX hthd,
DWORDLONG unused,
DWORDLONG lparam
)
/*++
Routine Description:
This is the registered event routine called when the base
exe's entry point is executed. The action we take here
depends on whether we are debugging a 32 bit or 16 bit exe.
Arguments:
pde - Supplies debug event for breakpoint
hthd - Supplies descriptor for thread that hit BP
unused - unused
lparam - unused
Return Value:
None
--*/
{
PBREAKPOINT pbp;
pbp = AtBP(hthd);
assert(pbp);
RemoveBP(pbp);
// the main reason we are here.
ExprBPRestoreDebugRegs(hthd);
if (hthd->hprc->f16bit) {
// if this is a 16 bit exe, continue and watch for
// the task start event.
ContinueThread(hthd);
} else {
// if this is a 32 bit exe, stay stopped and notify the EM
hthd->hprc->pstate &= ~ps_preEntry;
hthd->tstate |= ts_stopped;
pde->dwDebugEventCode = ENTRYPOINT_DEBUG_EVENT;
NotifyEM(pde, hthd, 0, ENTRY_BP);
DMSqlStartup( hthd->hprc );
}
}
void
HandleDebugActiveDeadlock(
HPRCX hprc,
BOOL Exited
)
{
DEBUG_EVENT64 de;
HTHDX hthd;
// This timed out waiting for the loader
// breakpoint. Clear the prestart state,
// and tell the EM we are screwed up.
// The shell should then stop waiting for
// the loader BP.
hprc->pstate &= ~ps_preStart;
--nWaitingForLdrBreakpoint;
ConsumeAllProcessEvents(hprc, TRUE);
if (hprc->pid == DebugActiveStruct.dwProcessId) {
if (DebugActiveStruct.hEventGo) {
SetEvent(DebugActiveStruct.hEventGo);
CloseHandle(DebugActiveStruct.hEventGo);
}
DebugActiveStruct.dwProcessId = 0;
DebugActiveStruct.hEventGo = 0;
if (DebugActiveStruct.hProcess != INVALID_HANDLE_VALUE) {
CloseHandle(DebugActiveStruct.hProcess);
DebugActiveStruct.hProcess = INVALID_HANDLE_VALUE;
}
SetEvent(DebugActiveStruct.hEventReady);
}
de.dwDebugEventCode = Exited? ATTACH_EXITED_DEBUG_EVENT: ATTACH_DEADLOCK_DEBUG_EVENT;
de.dwProcessId = hprc->pid;
hthd = hprc->hthdChild;
if (hthd) {
de.dwThreadId = hthd->tid;
} else {
de.dwThreadId = 0;
}
NotifyEM(&de, hthd, 0, 0);
} /* HandleDebugActiveDeadlock() */
BOOL
SetupSingleStep(
HTHDX hthd,
BOOL DoContinue
)
/*++
Routine Description:
description-of-function.
Arguments:
hthd - Supplies The handle to the thread which is to
be single stepped
DoContinue - Supplies continuation flag
Return Value:
TRUE if successfly started step and FALSE otherwise
--*/
{
PBREAKPOINT pbp;
ADDR addr;
#if defined(NO_TRACE_FLAG)
/*
* Set a breakpoint at the next legal offset and mark the breakpoint
* as being for a single step.
*/
AddrInit(&addr, 0, 0, GetNextOffset(hthd, FALSE), TRUE, TRUE, FALSE, FALSE);
pbp = SetBP( hthd->hprc, hthd, bptpExec, bpnsStop, &addr, (HPID) INVALID);
if ( pbp != NULL ) {
pbp->isStep++;
}
/*
* Now issue the command to execute the child
*/
if ( DoContinue ) {
ContinueProcess (hthd->hprc);
}
#else // NO_TRACE_FLAG
#if defined(TARGET_i386)
#ifndef KERNEL
// Set the single step flag in the context and then start the
// thread running
// Modify the processor flags in the child's context
// If we are passing an exception on to the child we cannot set the
// trace flag, so do the non-trace version of this code.
if (!PassingExceptionForThisThread (hthd)) {
hthd->context.EFlags |= TF_BIT_MASK;
hthd->fContextDirty = TRUE;
} else {
int lpf = 0;
hthd->context.EFlags &= ~TF_BIT_MASK;
hthd->fContextDirty = TRUE;
// this sequence initializes addr to the addr after the current inst
AddrFromHthdx (&addr, hthd);
IsCall (hthd, &addr, &lpf, FALSE);
pbp = SetBP( hthd->hprc, hthd, bptpExec, bpnsStop, &addr, (HPID) INVALID);
if ( pbp != NULL ) {
pbp->isStep++;
}
}
#endif // KERNEL
#elif defined(TARGET_IA64)
#ifndef KERNEL
// Set the single step flag in the context and then start the
// thread running
// Modify the processor flags in the child's context
// If we are passing an exception on to the child we cannot set the
// trace flag, so do the non-trace version of this code.
if (!PassingExceptionForThisThread (hthd)) {
hthd->context.StIPSR |= TF_BIT_MASK;
hthd->fContextDirty = TRUE;
} else {
int lpf = 0;
hthd->context.StIPSR &= ~TF_BIT_MASK;
hthd->fContextDirty = TRUE;
// this sequence initializes addr to the addr after the current inst
AddrFromHthdx (&addr, hthd);
IsCall (hthd, &addr, &lpf, FALSE);
pbp = SetBP( hthd->hprc, hthd, bptpExec, bpnsStop, &addr, (HPID) INVALID);
if ( pbp != NULL ) {
pbp->isStep++;
}
}
#endif // KERNEL
#else // i386
#error "Need code for new CPU with trace bit"
#endif // i386
// Now issue the command to execute the child
if ( DoContinue ) {
ContinueThreadEx(hthd,
(DWORD)DBG_CONTINUE,
QT_TRACE_DEBUG_EVENT,
ts_running);
}
#endif // NO_TRACE_FLAG
return TRUE;
} /* SetupSingleStep() */
VOID
ActionReturnStep(
DEBUG_EVENT64 * pde,
HTHDX hthd,
DWORDLONG unused,
ULONG64 lparam
)
/*++
lpv - stack pointer at the top of the call
--*/
{
BREAKPOINT* pbp = AtBP(hthd);
assert(pbp);
if (STACK_POINTER(hthd) > lparam) {
RemoveBP(pbp);
pde->dwDebugEventCode = EXCEPTION_DEBUG_EVENT;
pde->u.Exception.ExceptionRecord.ExceptionCode = EXCEPTION_SINGLE_STEP;
//ExprBPStop(hthd->hprc, hthd);
NotifyEM(pde, hthd, 0, 0);
} else {
METHOD *ContinueSSMethod;
assert(pbp != EMBEDDED_BP);
assert(pbp != NULL);
ClearBPFlag(hthd);
RestoreInstrBP(hthd, pbp);
ContinueSSMethod = (METHOD*) MHAlloc(sizeof(METHOD));
ContinueSSMethod->notifyFunction = MethodContinueSS;
ContinueSSMethod->lparam = (ULONG64)ContinueSSMethod;
ContinueSSMethod->lparam2 = pbp;
/* Reenable */
RegisterExpectedEvent(hthd->hprc, hthd,
BREAKPOINT_DEBUG_EVENT,
(DWORD_PTR) pbp,
DONT_NOTIFY,
ActionReturnStep,
FALSE,
lparam
);
SingleStepEx(hthd, ContinueSSMethod, FALSE, FALSE, TRUE);
}
}
BOOL
SetupReturnStep(
HTHDX hthd,
BOOL DoContinue,
LPADDR lpaddr,
LPADDR addrStack
)
/*++
Routine Description:
description-of-function.
Arguments:
hthd - Supplies The handle to the thread which is to
be single stepped
DoContinue - Supplies continuation flag
lpaddr - Supplies the address to step to
addrStack - Supplies the address for SP.
Return Value:
TRUE if successfully started step and FALSE otherwise
--*/
{
BREAKPOINT * pbp;
/*
*/
pbp = SetBP( hthd->hprc, hthd, bptpExec, bpnsStop, lpaddr, (HPID)INVALID);
if ( !pbp ) {
// On Win95 we might not be able to set the bp because
// we might be in a callback and returning to system code.
assert(IsChicago());
if (IsInSystemDll(GetAddrOff(*lpaddr))) {
SendDBCErrorStep(hthd->hprc);
} else {
assert(FALSE); // Shouldn't happen any other time.
}
return FALSE;
}
if (addrStack) {
RegisterExpectedEvent(hthd->hprc, hthd,
BREAKPOINT_DEBUG_EVENT,
(DWORD_PTR)pbp,
DONT_NOTIFY,
ActionReturnStep,
FALSE,
GetAddrOff(*addrStack));
}
/*
* Now issue the command to execute the child
*/
if ( DoContinue ) {
ExprBPContinue( hthd->hprc, hthd );
ContinueThread (hthd);
}
return TRUE;
} /* SetupReturnStep() */
void
SetupEntryBP(
HTHDX hthd
)
/*++
Routine Description:
Set a breakpoint and make a persistent expected event for the
entry point of the first thread in a new process.
Arguments:
hthd - Supplies descriptor for thread to act on.
Return Value:
None
--*/
{
ADDR addr;
BREAKPOINT * bp;
AddrInit(&addr,
0,
0,
hthd->lpStartAddress,
TRUE,
TRUE,
FALSE,
FALSE);
#if defined(TARGET_IA64)
{
ULONGLONG ullPLabel[2];
DWORD cb;
DPRINT(1,(_T("Reading plabel at 0x%I64x-0x%I64x\n"),
hthd->lpStartAddress,
GetAddrOff(addr)));
if (!AddrReadMemory(hthd->hprc, hthd, &addr, &ullPLabel,sizeof(ullPLabel),&cb)) {
DPRINT(0,(_T("Could not read the plabel at 0x%I64x -- cb = %ld"),GetAddrOff(addr),cb));
} else {
DPRINT(1,("Dereferenced Entry BP from 0x%I64x (plabel?) to 0x%I64x(0x%I64x)\n",
GetAddrOff(addr),
(UOFFSET) ullPLabel[0],
(UOFFSET) ullPLabel[1]));
GetAddrOff(addr) = (UOFFSET) ullPLabel[0];
}
}
#endif
bp = SetBP(hthd->hprc, hthd, bptpExec, bpnsStop, &addr, (HPID)ENTRY_BP);
// register expected event
RegisterExpectedEvent(hthd->hprc,
hthd,
BREAKPOINT_DEBUG_EVENT,
(DWORD_PTR)bp,
DONT_NOTIFY,
ActionEntryPoint,
TRUE, // Persistent!
0
);
} /* SetupEntryBP() */
#ifdef KERNEL
VOID
RestoreKernelBreakpoints (
HTHDX hthd,
ULONG64 Offset
)
/*++
Routine Description:
Restores all breakpoints in our bp list that fall in the range of
offset -> offset+dbgkd_maxstream. This is necessary because the kd
stub in the target system clears all breakpoints in this range before
delivering an exception to the debugger.
Arguments:
hthd - handle to the current thread
Offset - beginning of the range, usually the current pc
Return Value:
None
--*/
{
BREAKPOINT *pbp;
DBGKD_WRITE_BREAKPOINT64 bps[MAX_KD_BPS];
DWORD i = 0;
#if defined(TARGET_IA64)
return; // IA64 version of KdpSetStateChange() will reinstall kernel breakpoints instead.
#endif
EnterCriticalSection(&csThreadProcList);
ZeroMemory( bps, sizeof(bps) );
for (pbp=bpList->next; pbp; pbp=pbp->next) {
if (GetAddrOff(pbp->addr) >= Offset &&
GetAddrOff(pbp->addr) < Offset+DBGKD_MAXSTREAM) {
if (i < MAX_KD_BPS) {
bps[i++].BreakPointAddress = GetAddrOff(pbp->addr);
}
}
}
if (i) {
WriteBreakPointEx( hthd, i, bps, 0 );
for (i=0,pbp=bpList->next; pbp; pbp=pbp->next) {
if (GetAddrOff(pbp->addr) == bps[i].BreakPointAddress) {
pbp->hBreakPoint = bps[i++].BreakPointHandle;
}
}
}
LeaveCriticalSection(&csThreadProcList);
}
#endif // KERNEL
#ifndef KERNEL
void
UpdateThreadContext(
HTHDX hthd
)
/*++
Routine Description:
This routine updates a thread's context, including real/32bit modes and
context dirty flags.
--*/
{
#if defined(TARGET_i386)
LDT_ENTRY ldtEntry;
#endif
hthd->context.ContextFlags = CONTEXT_FULL | CONTEXT_FLOATING_POINT;
DbgGetThreadContext( hthd, &hthd->context );
hthd->fContextDirty = FALSE;
hthd->fIsCallDone = FALSE;
#if !defined( TARGET_i386 )
hthd->fAddrIsReal = FALSE;
hthd->fAddrIsFlat = hthd->fAddrOff32 = TRUE;
#else // !i386
if (hthd->context.EFlags & V86FLAGS_V86) {
hthd->fAddrIsReal = TRUE;
hthd->fAddrIsFlat = FALSE;
hthd->fAddrOff32 = FALSE;
} else {
hthd->fAddrIsReal = FALSE;
if (PcSegOfHthdx(hthd) == hthd->hprc->segCode) {
hthd->fAddrIsFlat = hthd->fAddrOff32 = TRUE;
} else {
hthd->fAddrIsFlat = FALSE;
if (!GetThreadSelectorEntry(hthd->rwHand,
PcSegOfHthdx(hthd),
&ldtEntry)) {
hthd->fAddrOff32 = FALSE;
} else if (!ldtEntry.HighWord.Bits.Default_Big) {
hthd->fAddrOff32 = FALSE;
} else {
hthd->fAddrOff32 = TRUE;
}
}
}
#endif // !i386
}
void
ProcessDebugEvent(
DEBUG_EVENT64 * de
)
/*++
Routine Description:
This routine is called whenever a debug event notification comes from
the operating system.
Arguments:
de - Supplies a pointer to the debug event which just occured
Return Value:
None.
--*/
{
EXPECTED_EVENT * ee;
DWORD eventCode = de->dwDebugEventCode;
DWORD_PTR subClass = 0L;
HTHDX hthd = NULL;
HPRCX hprc;
BREAKPOINT * bp;
ADDR addr;
BP_UNIT instr;
DWORD len;
BOOL fInstrIsBp;
DPRINT(3, (_T("Event Code == %x\n"), eventCode));
hprc = HPRCFromPID(de->dwProcessId);
/*
* While killing process, ignore everything
* except for exit events.
*/
if (hprc) {
hprc->cLdrBPWait = 0;
}
if ( hprc && (hprc->pstate & ps_killed) ) {
if (eventCode == EXCEPTION_DEBUG_EVENT) {
AddQueue( QT_CONTINUE_DEBUG_EVENT,
de->dwProcessId,
de->dwThreadId,
(DWORD)DBG_EXCEPTION_NOT_HANDLED,
0);
return;
} else if (eventCode != EXIT_THREAD_DEBUG_EVENT
&& eventCode != EXIT_PROCESS_DEBUG_EVENT ) {
AddQueue( QT_CONTINUE_DEBUG_EVENT,
de->dwProcessId,
de->dwThreadId,
(DWORD)DBG_EXCEPTION_NOT_HANDLED,
0);
return;
}
}
EnterCriticalSection(&csProcessDebugEvent);
if (eventCode == CREATE_THREAD_DEBUG_EVENT){
DPRINT(3, (_T("** NEW TID = (PID,TID)(%08lx, %08lx)\n"),
de->dwProcessId, de->dwThreadId));
} else {
/*
* Find our structure for this event's process
*/
DEBUG_PRINT(_T("Not Create Thread Debug Event\r\n"));
hthd = HTHDXFromPIDTID((PID)de->dwProcessId,(TID)de->dwThreadId);
// Update the context for all threads, not just the main one. We
// may be able to do this a little quicker by using the
// fContextDirty flag.
// First, update the main threads ExceptionRecord if necessary
if (hthd && eventCode == EXCEPTION_DEBUG_EVENT) {
hthd->ExceptionRecord = de->u.Exception.ExceptionRecord;
}
// Loop through all threads and update contexts
if (hthd && hprc) {
HTHDX hthdT;
// If for any reason this is set unset it.
#ifndef KERNEL
hprc->pFbrCntx = NULL;
#endif
for (hthdT = hprc->hthdChild; hthdT; hthdT = hthdT->nextSibling) {
UpdateThreadContext (hthdT);
}
} else if (hprc && (hprc->pstate & ps_killed)) {
/*
* this is an event for a thread that
* we never created:
*/
if (eventCode == EXIT_PROCESS_DEBUG_EVENT) {
/* Process exited on a thread we didn't pick up */
ProcessExitProcessEvent(de, NULL);
} else {
/* this is an exit thread for a thread we never picked up */
AddQueue( QT_CONTINUE_DEBUG_EVENT,
de->dwProcessId,
de->dwThreadId,
DBG_CONTINUE,
0);
}
goto done;
} else if (eventCode!=CREATE_PROCESS_DEBUG_EVENT) {
// This will happen sometimes when killing a process with
// ProcessTerminateProcessCmd and ProcessUnloadCmd.
AddQueue( QT_CONTINUE_DEBUG_EVENT,
de->dwProcessId,
de->dwThreadId,
DBG_CONTINUE,
0);
goto done;
}
}
// Mark the thread as having been stopped for some event.
if (hthd) {
hthd->tstate &= ~ts_running;
hthd->tstate |= ts_stopped;
}
/* If it is an exception event get the subclass */
if (eventCode == EXCEPTION_DEBUG_EVENT) {
subClass = de->u.Exception.ExceptionRecord.ExceptionCode;
DPRINT(1, (_T("Exception Event: subclass = %p "), subClass));
switch (subClass) {
case STATUS_SEGMENT_NOTIFICATION:
eventCode = de->dwDebugEventCode = SEGMENT_LOAD_DEBUG_EVENT;
break;
case EXCEPTION_SINGLE_STEP:
#if !defined(TARGET_i386) && !defined(TARGET_IA64)
assert(_T("!EXCEPTION_SINGLE_STEP on non-x86!"));
#endif
#if defined(TARGET_IA64)
// v-vadimp - looks like Merced doesn't reset PSR.ss (analog of
// EFLAG.tf) after each SS exception, we have to do it manually
hthd->fContextDirty = TRUE;
hthd->context.StIPSR &= ~TF_BIT_MASK;
#endif
AddrFromHthdx(&addr, hthd);
// This may be a single step or a hardware breakpoint.
// If it is a single step, leave it at that. If it is a
// hardware breakpoint, convert it to a BREAKPOINT_DEBUG_EVENT.
DecodeSingleStepEvent( hthd, de, &eventCode, &subClass );
break;
case EXCEPTION_BREAKPOINT:
/*
* Check if it is a BREAKPOINT exception:
* If it is, change the debug event to our pseudo-event,
* BREAKPOINT_DEBUG_EVENT (this is a pseudo-event because
* the API does not define such an event, and we are
* synthesizing not only the class of event but the
* subclass as well -- the subclass is set to the appropriate
* breakpoint structure)
*/
AddrFromHthdx(&addr, hthd);
#if defined(TARGET_i386)
// correct for machine overrun on a breakpoint
// On NT the offset value is overrun by 1 every time except
// for when doing post-mortem debugging. The value in the
// Debug Event structure is always correct though
addr.addr.off = de->u.Exception.ExceptionRecord.ExceptionAddress ;
#endif
// Determine the start of the breakpoint instruction
if ((AddrReadMemory(hprc, hthd, &addr, &instr, BP_SIZE, &len) == 0)
|| (len != BP_SIZE)) {
DPRINT(0, ("Memory read failed @ %I64x!!!\n",GetAddrOff(addr)));
assert(FALSE);
instr = 0;
}
#if defined(TARGET_ALPHA) || defined(TARGET_AXP64)
switch (instr) {
case CALLPAL_OP | CALLKD_FUNC:
case CALLPAL_OP | BPT_FUNC:
case CALLPAL_OP | KBPT_FUNC:
fInstrIsBp = TRUE;
break;
default:
fInstrIsBp = FALSE;
}
DPRINT(3, (_T("Looking for BP@%I64x\n"), addr.addr.off));
#elif defined(TARGET_i386)
/*
* It may have been a 0xcd 0x03 rather than a 0xcc
* (ie: check if it is a 1 or a 2 byte INT 3)
*/
fInstrIsBp = FALSE;
if (instr == BP_OPCODE) {
fInstrIsBp = TRUE;
} else if (instr == 0x3) { // 0xcd?
--addr.addr.off;
if (AddrReadMemory(hprc,
hthd,
&addr,
&instr,
1,
&len)
&& (len == 1)
&& (instr == 0xcd)) {
fInstrIsBp = TRUE;
} else {
++addr.addr.off;
}
}
Set_PC(hthd, addr.addr.off);
hthd->fContextDirty = TRUE;
DPRINT(3, (_T("Looking for BP@%I64x (instr=%I64x)\n"), addr.addr.off,
(ULONGLONG)instr));
#elif defined(TARGET_IA64)
{
BP_UNIT ia64_opcode;
// EM instruction slots are not byte-aligned. Break instruction
// (BP_OPCODE) must be shifted according to the instruction slot
// before comparison
switch (GetAddrOff(addr) & 0xf) {
case 0:
ia64_opcode = (instr & (INST_SLOT0_MASK)) >> 5;
break;
case 4:
ia64_opcode = (instr & (INST_SLOT1_MASK)) >> 14;
break;
case 8:
ia64_opcode = (instr & (INST_SLOT2_MASK)) >> 23;
break;
default:
assert(!"Bad BP Address");
ia64_opcode = 0;
break;
}
fInstrIsBp = (ia64_opcode == BP_OPCODE) || (instr == 0);
DPRINT(1, (_T("BP test: opcode:%I64x=BP_OPCODE:%x,BP?:%i @ address %I64x\n"),
ia64_opcode,
BP_OPCODE,
fInstrIsBp,
addr.addr.off));
}
#else
#error( "unknown processor type" )
#endif
/*
* Lookup the breakpoint in our (the dm) table
*/
bp = FindBP(hthd->hprc, hthd, bptpExec, (BPNS)-1, &addr, FALSE);
// We need to be able to find BPs for message BPs as well
if (bp == NULL) {
bp = FindBP (hthd->hprc,
hthd,
bptpMessage,
(BPNS) -1,
&addr,
FALSE);
}
SetBPFlag(hthd, bp?bp:EMBEDDED_BP);
if (!bp && !fInstrIsBp) {
// If the instruction is not a bp, and there is no record of
// the bp, this happened because the exception was already
// in the queue when we cleared the bp.
// We will just continue it.
DPRINT(1, (_T("Continuing false BP.\n")));
ContinueThread(hthd);
goto done;
}
// Q: What does it mean if we find the bp record, but the
// instruction is not a bp???
// A: It means that this thread hit the BP after another
// thread hit it, but before this thread could be suspended
// by the debug subsystem. The debugger cleared the BP
// temporarily in order that the other thread could run
// past it, and will put it back as soon as that thread
// hits the temp BP on the next instruction.
//assert(!bp || fInstrIsBp);
/*
* Reassign the event code to our pseudo-event code
*/
DPRINT(3, (_T("Reassigning event code!\n")));
/*
* For some machines there is not single instruction tracing
* on the chip. In this case we need to do it in software.
* Check to see if the breakpoint we just hit was there for
* doing single step emulation. If so then remap it to
* a single step exception.
*/
if (bp && bp->isStep){
de->u.Exception.ExceptionRecord.ExceptionCode = EXCEPTION_SINGLE_STEP;
subClass = EXCEPTION_SINGLE_STEP;
RemoveBP(bp);
break;
}
/*
* Reassign the subclass to point to the correct
* breakpoint structure and clear the exception code
*/
de->dwDebugEventCode = eventCode = BREAKPOINT_DEBUG_EVENT;
de->u.Exception.ExceptionRecord.ExceptionAddress = GetAddrOff(addr);
de->u.Exception.ExceptionRecord.ExceptionCode =
(DWORD)(DWORD_PTR)bp;
de->u.Exception.ExceptionRecord.NumberParameters =
(DWORD)((DWORD_PTR)bp>>32);
subClass = (DWORD_PTR)bp;
break;
case EXCEPTION_ORPC_DEBUG:
{
ORPC orpc;
// Is this really an OLE notification?
if ((orpc = OrpcFromPthd(hthd, de)) != orpcNil) {
if (orpc == orpcUnrecognized) {
// Unrecognized notification. Resume execution.
ContinueThread(hthd);
return;
}
/*
** Reassign the event code to our pseudo-event code
*/
de->dwDebugEventCode = eventCode = OLE_DEBUG_EVENT;
/*
** Reassign the exception code to the type of OLE
** event that occurred.
*/
de->u.Exception.ExceptionRecord.ExceptionCode = orpc;
subClass = orpc;
}
break;
}
case EXCEPTION_FIBER_DEBUG:
{
// If fibers are in use change to fiber event
if(hprc->fUseFbrs) {
de->dwDebugEventCode = eventCode = FIBER_DEBUG_EVENT;
}
break;
}
}
}
/*
* Check if this debug event was expected
*/
ee = PeeIsEventExpected(hthd, eventCode, subClass, TRUE);
/*
* If it wasn't, clear all consummable events
* and then run the standard handler with
* notifications going to the execution model
*/
assert((0 < eventCode) && (eventCode < MAX_EVENT_CODE));
if (!ee) {
if ((hthd != NULL) && (hthd->tstate & ts_funceval)) {
assert(RgfnFuncEventDispatch[eventCode-EXCEPTION_DEBUG_EVENT]);
RgfnFuncEventDispatch[eventCode-EXCEPTION_DEBUG_EVENT](de, hthd);
} else {
assert(DebugDispatchTable[eventCode-EXCEPTION_DEBUG_EVENT]);
DebugDispatchTable[eventCode-EXCEPTION_DEBUG_EVENT](de,hthd);
}
} else {
/*
* If it was expected then call the action
* function if one was specified
*/
if (ee->action) {
DPRINT(3,("Calling action @ %p\n",ee->action));
(ee->action)(de, hthd, 0, ee->lparam);
}
/*
* And call the notifier if one was specified
*/
if (ee->notifier) {
METHOD *nm = ee->notifier;
DPRINT(3,("Calling notifier @ %p\n",nm));
(nm->notifyFunction)(de, hthd, 0, nm->lparam);
}
MHFree(ee);
}
done:
LeaveCriticalSection(&csProcessDebugEvent);
return;
} /* ProcessDebugEvent() */
#else // KERNEL
void
ProcessDebugEvent(
DEBUG_EVENT64 *de,
DBGKD_WAIT_STATE_CHANGE64 * sc // Not used anymore
)
/*++
Routine Description:
This routine is called whenever a debug event notification comes from
the operating system.
Arguments:
de - Supplies a pointer to the debug event which just occured
Return Value:
None.
--*/
{
EXPECTED_EVENT * ee;
DWORD eventCode = de->dwDebugEventCode;
DWORD_PTR subClass = 0;
HTHDX hthd = NULL;
HPRCX hprc;
PBREAKPOINT bp;
ADDR addr;
DWORD cb;
BP_UNIT instr;
BOOL fInstrIsBp = FALSE;
DPRINT(3, (_T("Event Code == %x\n"), eventCode));
hprc = HPRCFromPID(de->dwProcessId);
/*
* While killing process, ignore everything
* except for exit events.
*/
if (hprc) {
hprc->cLdrBPWait = 0;
}
if ( hprc && (hprc->pstate & ps_killed) ) {
if (eventCode == EXCEPTION_DEBUG_EVENT) {
ContinueThreadEx(hthd,
(DWORD)DBG_EXCEPTION_NOT_HANDLED,
QT_CONTINUE_DEBUG_EVENT,
ts_running);
return;
} else if (eventCode != EXIT_THREAD_DEBUG_EVENT
&& eventCode != EXIT_PROCESS_DEBUG_EVENT ) {
AddQueue( QT_CONTINUE_DEBUG_EVENT,
de->dwProcessId,
de->dwThreadId,
(DWORD)DBG_EXCEPTION_NOT_HANDLED,
0);
return;
}
}
EnterCriticalSection(&csProcessDebugEvent);
if (eventCode == CREATE_THREAD_DEBUG_EVENT){
DPRINT(3, (_T("** NEW TID = (PID,TID)(%08lx, %08lx)\n"),
de->dwProcessId, de->dwThreadId));
} else {
/*
* Find our structure for this event's process
*/
hthd = HTHDXFromPIDTID((PID)de->dwProcessId,(TID)de->dwThreadId);
/*
* Update our context structure for this thread if we found one
* in our list. If we did not find a thread and this is
* not a create process debug event then return without
* processing the event as we are in big trouble.
*/
if (hthd) {
if (eventCode == EXCEPTION_DEBUG_EVENT) {
hthd->ExceptionRecord = de->u.Exception.ExceptionRecord;
}
hthd->context.ContextFlags = CONTEXT_FULL | CONTEXT_FLOATING_POINT;
DbgGetThreadContext(hthd,&hthd->context);
hthd->fContextDirty = FALSE;
hthd->fIsCallDone = FALSE;
hthd->fAddrIsReal = FALSE;
hthd->fAddrIsFlat = TRUE;
hthd->fAddrOff32 = TRUE;
} else
if (hprc && (hprc->pstate & ps_killed)) {
/*
* this is an event for a thread that
* we never created:
*/
if (eventCode == EXIT_PROCESS_DEBUG_EVENT) {
/* Process exited on a thread we didn't pick up */
ProcessExitProcessEvent(de, NULL);
} else {
/* this is an exit thread for a thread we never picked up */
AddQueue( QT_CONTINUE_DEBUG_EVENT,
de->dwProcessId,
de->dwThreadId,
DBG_CONTINUE,
0);
}
goto done;
} else if (eventCode!=CREATE_PROCESS_DEBUG_EVENT) {
assert(FALSE);
AddQueue( QT_CONTINUE_DEBUG_EVENT,
de->dwProcessId,
de->dwThreadId,
DBG_CONTINUE,
0);
goto done;
}
}
/*
* Mark the thread as having been stopped for some event.
*/
if (hthd) {
hthd->tstate &= ~ts_running;
hthd->tstate |= ts_stopped;
}
/* If it is an exception event get the subclass */
if (eventCode==EXCEPTION_DEBUG_EVENT){
subClass = de->u.Exception.ExceptionRecord.ExceptionCode;
DPRINT(1, (_T("Exception Event: subclass = %p "), subClass));
switch (subClass) {
case STATUS_SEGMENT_NOTIFICATION:
eventCode = de->dwDebugEventCode = SEGMENT_LOAD_DEBUG_EVENT;
break;
case EXCEPTION_SINGLE_STEP:
#if !defined(TARGET_i386)
assert(_T("!EXCEPTION_SINGLE_STEP on non-x86!"));
#endif
AddrFromHthdx(&addr, hthd);
RestoreKernelBreakpoints( hthd, GetAddrOff(addr) );
// This may be a single step or a hardware breakpoint.
// If it is a single step, leave it at that. If it is
// a hardware breakpoint, convert it to a BREAKPOINT_DEBUG_EVENT.
DecodeSingleStepEvent( hthd, de, &eventCode, &subClass );
break;
case EXCEPTION_BREAKPOINT:
/*
* Check if it is a BREAKPOINT exception:
* If it is, change the debug event to our pseudo-event,
* BREAKPOINT_DEBUG_EVENT (this is a pseudo-event because
* the API does not define such an event, and we are
* synthesizing not only the class of event but the
* subclass as well -- the subclass is set to the appropriate
* breakpoint structure)
*/
hthd->fDontStepOff = FALSE;
AddrFromHthdx(&addr, hthd);
/*
* Lookup the breakpoint in our (the dm) table
*/
bp = FindBP(hthd->hprc, hthd, bptpExec, (BPNS)-1, &addr, FALSE);
SetBPFlag(hthd, bp?bp:EMBEDDED_BP);
/*
* Reassign the event code to our pseudo-event code
*/
DPRINT(3, (_T("Reassigning event code!\n")));
/*
* For some machines there is not single instruction tracing
* on the chip. In this case we need to do it in software.
* Check to see if the breakpoint we just hit was there for
* doing single step emulation. If so then remap it to
* a single step exception.
*/
if (bp) {
if (bp->isStep){
de->u.Exception.ExceptionRecord.ExceptionCode = EXCEPTION_SINGLE_STEP;
subClass = EXCEPTION_SINGLE_STEP;
RemoveBP(bp);
RestoreKernelBreakpoints( hthd, GetAddrOff(addr) );
break;
} else {
RestoreKernelBreakpoints( hthd, GetAddrOff(addr) );
}
}
// Determine the start of the breakpoint instruction
// DO NOT use DbgReadMemory here! This is a non-cached read!
if (fCrashDump) {
cb = DmpReadMemory(GetAddrOff(addr), &instr, BP_SIZE);
if (cb != BP_SIZE) {
DPRINT(1, (_T("Memory read failed!!!\n")));
instr = 0;
}
} else {
if (DmKdReadVirtualMemoryNow(GetAddrOff(addr),&instr,BP_SIZE,&cb) || cb != BP_SIZE) {
DPRINT(1, (_T("Memory read failed!!!\n")));
instr = 0;
}
}
#if defined(TARGET_ALPHA) || defined(TARGET_AXP64)
switch (instr) {
case 0:
case CALLPAL_OP | CALLKD_FUNC:
case CALLPAL_OP | BPT_FUNC:
case CALLPAL_OP | KBPT_FUNC:
fInstrIsBp = TRUE;
break;
default:
addr.addr.off -= BP_SIZE;
// NON-CACHED READ!!!
if (fCrashDump) {
cb = DmpReadMemory(GetAddrOff(addr),&instr,BP_SIZE);
if (cb != BP_SIZE) {
DPRINT(1, (_T("Memory read failed!!!\n")));
instr = 0;
}
} else {
if (DmKdReadVirtualMemoryNow(GetAddrOff(addr),&instr,BP_SIZE,&cb) || cb != BP_SIZE) {
DPRINT(1, (_T("Memory read failed!!!\n")));
instr = 0;
}
}
switch (instr) {
case 0:
case CALLPAL_OP | CALLKD_FUNC:
case CALLPAL_OP | BPT_FUNC:
case CALLPAL_OP | KBPT_FUNC:
fInstrIsBp = TRUE;
hthd->fDontStepOff = TRUE;
break;
default:
fInstrIsBp = FALSE;
}
}
#elif defined(TARGET_i386)
/*
* It may have been a 0xcd 0x03 rather than a 0xcc
* (ie: check if it is a 1 or a 2 byte INT 3)
*/
fInstrIsBp = FALSE;
if (instr == BP_OPCODE || instr == 0) {
fInstrIsBp = TRUE;
} else
if (instr == 0x3) { // 0xcd?
--addr.addr.off;
if (fCrashDump) {
cb = DmpReadMemory(GetAddrOff(addr),&instr,BP_SIZE);
if (cb != BP_SIZE) {
DPRINT(1, (_T("Memory read failed!!!\n")));
instr = 0;
}
} else {
if (DmKdReadVirtualMemoryNow(GetAddrOff(addr),&instr,BP_SIZE,&cb) || cb != BP_SIZE) {
DPRINT(1, (_T("Memory read failed!!!\n")));
instr = 0;
}
}
if (cb == 1 && instr == 0xcd) {
--addr.addr.off;
fInstrIsBp = TRUE;
}
} else {
hthd->fDontStepOff = TRUE;
}
#elif defined(TARGET_IA64)
{
BP_UNIT ia64_opcode;
// EM instruction slots are not byte-aligned. Break instruction
// (BP_OPCODE) must be shifted according to the instruction slot
// before comparison
switch (GetAddrOff(addr) & 0xf) {
case 0:
ia64_opcode = (instr & (INST_SLOT0_MASK)) >> 5;
break;
case 4:
ia64_opcode = (instr & (INST_SLOT1_MASK)) >> 14;
break;
case 8:
ia64_opcode = (instr & (INST_SLOT2_MASK)) >> 23;
break;
default:
assert(!"Bad BP Address");
ia64_opcode = 0;
break;
}
fInstrIsBp = (ia64_opcode == BP_OPCODE) || (instr == 0);
}
#else
#error( "undefined processor type" );
#endif
if (!bp && !fInstrIsBp) {
DMPrintShellMsg( _T("Stopped at an unexpected exception: code=%08x addr=%08I64x\n"),
de->u.Exception.ExceptionRecord.ExceptionCode,
de->u.Exception.ExceptionRecord.ExceptionAddress
);
}
/*
* Reassign the subclass to point to the correct
* breakpoint structure and clear the exception code
*/
de->dwDebugEventCode = eventCode = BREAKPOINT_DEBUG_EVENT;
de->u.Exception.ExceptionRecord.ExceptionAddress = addr.addr.off;
de->u.Exception.ExceptionRecord.ExceptionCode = (DWORD)(DWORD_PTR)bp;
de->u.Exception.ExceptionRecord.NumberParameters = (DWORD)((DWORD_PTR)bp>>32);
subClass = (UINT_PTR)bp;
break;
}
}
/*
* Check if this debug event was expected
*/
ee = PeeIsEventExpected(hthd, eventCode, subClass, TRUE);
/*
* If it wasn't, clear all consumable events
* and then run the standard handler with
* notifications going to the execution model
*/
assert((0 < eventCode) && (eventCode < MAX_EVENT_CODE));
if (!ee) {
if ((hthd != NULL) && (hthd->tstate & ts_funceval)) {
assert(RgfnFuncEventDispatch[eventCode-EXCEPTION_DEBUG_EVENT]);
RgfnFuncEventDispatch[eventCode-EXCEPTION_DEBUG_EVENT](de, hthd);
} else {
assert(DebugDispatchTable[eventCode-EXCEPTION_DEBUG_EVENT]);
DebugDispatchTable[eventCode-EXCEPTION_DEBUG_EVENT](de,hthd);
}
} else {
/*
* If it was expected then call the action
* function if one was specified
*/
if (ee->action) {
(ee->action)(de, hthd, 0, ee->lparam);
}
/*
* And call the notifier if one was specified
*/
if (ee->notifier) {
METHOD *nm = ee->notifier;
(nm->notifyFunction)(de, hthd, 0, nm->lparam);
}
MHFree(ee);
}
done:
LeaveCriticalSection(&csProcessDebugEvent);
return;
} /* ProcessDebugEvent() */
#endif // KERNEL
/** ConsumeThreadEventsAndNotifyEM
** Description:
** Does exactly what it says it does
*/
void
ConsumeThreadEventsAndNotifyEM(
DEBUG_EVENT64* de,
HTHDX hthd,
DWORDLONG wParam,
DWORDLONG lparam
)
{
ConsumeAllThreadEvents(hthd, FALSE);
NotifyEM(de, hthd, wParam, lparam);
}
/** NotifyEM
** Synopsis:
** Entry:
** Returns:
** Description:
** Given a debug event from the OS send the correct information
** back to the debugger.
*/
void
NotifyEM(
DEBUG_EVENT64* de,
HTHDX hthd,
DWORDLONG wparam,
DWORDLONG lparam
)
/*++
Routine Description:
This is the interface for telling the EM about debug events.
In general, de describes an event which the debugger needs
to know about. In some cases, a reply is needed. In those
cases this routine handles the reply and does the appropriate
thing with the data from the reply.
Arguments:
de - Supplies debug event structure
hthd - Supplies thread that got the event
wparam - Supplies data specific to event
lparam - Supplies data specific to event
Return Value:
None
--*/
{
DWORD eventCode = de->dwDebugEventCode;
DWORD subClass;
RTP rtp;
RTP * lprtp;
WORD cbPacket=0;
LPBYTE lpbPacket;
LPVOID toFree=(LPVOID)0;
WORD packetType = tlfDebugPacket;
if (hthd) {
rtp.hpid = hthd->hprc->hpid;
rtp.htid = hthd->htid;
} else if (hpidRoot == (HPID)INVALID) {
return;
} else {
// cheat:
rtp.hpid = hpidRoot;
rtp.htid = NULL;
}
subClass = de->u.Exception.ExceptionRecord.ExceptionCode;
switch(eventCode){
case FUNC_EXIT_EVENT:
if (hthd->fDisplayReturnValues) {
cbPacket = sizeof(ADDR);
rtp.dbc = dbcExitedFunction;
lpbPacket = (LPBYTE) lparam;
assert(lpbPacket);
} else {
return;
}
break;
case EXCEPTION_DEBUG_EVENT:
if (subClass!=EXCEPTION_SINGLE_STEP){
PEXCEPTION_RECORD64 pexr=&de->u.Exception.ExceptionRecord;
DWORD cParam = pexr->NumberParameters;
DWORD nBytes = sizeof(EPR)+sizeof(ULONG64)*cParam;
LPEPR lpepr = MHAlloc(nBytes);
toFree = (LPVOID) lpepr;
cbPacket = (WORD) nBytes;
lpbPacket = (LPBYTE) lpepr;
#ifdef TARGET_i386
lpepr->bpr.segCS = (SEGMENT)hthd->context.SegCs;
lpepr->bpr.segSS = (SEGMENT)hthd->context.SegSs;
#endif
lpepr->bpr.offEBP = FRAME_POINTER(hthd);
lpepr->bpr.offESP = STACK_POINTER(hthd);
lpepr->bpr.offEIP = PC(hthd);
lpepr->bpr.fFlat = hthd->fAddrIsFlat;
lpepr->bpr.fOff32 = hthd->fAddrOff32;
lpepr->bpr.fReal = hthd->fAddrIsReal;
lpepr->efd = (EXCEPTION_FILTER_DEFAULT)lparam;
lpepr->dwFirstChance = de->u.Exception.dwFirstChance;
lpepr->ExceptionCode = pexr->ExceptionCode;
lpepr->ExceptionFlags = pexr->ExceptionFlags;
lpepr->NumberParameters = cParam;
for(;cParam;cParam--) {
lpepr->ExceptionInformation[cParam-1]=
pexr->ExceptionInformation[cParam-1];
}
rtp.dbc = dbcException;
break;
};
// Add 'foo returned' to Auto watch window
if (hthd->fReturning) {
hthd->fReturning = FALSE;
NotifyEM(&FuncExitEvent, hthd, 0, (ULONG64)&hthd->addrFrom );
}
// Fall through when subClass == EXCEPTION_SINGLE_STEP
case BREAKPOINT_DEBUG_EVENT:
case ENTRYPOINT_DEBUG_EVENT:
case CHECK_BREAKPOINT_DEBUG_EVENT:
case LOAD_COMPLETE_DEBUG_EVENT:
{
LPBPR lpbpr = MHAlloc ( sizeof ( BPR ) );
#ifdef TARGET_i386
UOFFSET bpAddr = PC(hthd)-1;
#else
UOFFSET bpAddr = PC(hthd);
#endif
toFree=lpbpr;
cbPacket = sizeof ( BPR );
lpbPacket = (LPBYTE) lpbpr;
#ifdef TARGET_i386
lpbpr->segCS = (SEGMENT)hthd->context.SegCs;
lpbpr->segSS = (SEGMENT)hthd->context.SegSs;
#endif
lpbpr->offEBP = FRAME_POINTER(hthd);
lpbpr->offESP = STACK_POINTER(hthd);
lpbpr->offEIP = PC(hthd);
lpbpr->fFlat = hthd->fAddrIsFlat;
lpbpr->fOff32 = hthd->fAddrOff32;
lpbpr->fReal = hthd->fAddrIsReal;
lpbpr->qwNotify = lparam;
if (eventCode==EXCEPTION_DEBUG_EVENT) {
rtp.dbc = dbcStep;
break;
} else { /* (the breakpoint case) */
// REVIEW: It would be cleaner to setup something which got
// called when the EE got consumed and turned this off. I
// don't think the DM has this notion except for preset bps.
// I think a general function for cleanup would be a good
// idea here.
if (eventCode != CHECK_BREAKPOINT_DEBUG_EVENT) {
hthd->fReturning = FALSE;
}
if (eventCode == ENTRYPOINT_DEBUG_EVENT) {
rtp.dbc = dbcEntryPoint;
} else if (eventCode == LOAD_COMPLETE_DEBUG_EVENT) {
rtp.dbc = dbcLoadComplete;
} else if (eventCode == CHECK_BREAKPOINT_DEBUG_EVENT) {
rtp.dbc = dbcCheckBpt;
packetType = tlfRequest;
} else {
rtp.dbc = dbcBpt;
}
/* NOTE: Ok try to follow this: If this was one
* of our breakpoints then we have already
* decremented the IP to point to the actual
* INT3 instruction (0xCC), this is so we
* can just replace the byte and continue
* execution from that point on.
* But if it was hard coded in by the user
* then we can't do anything but execute the
* NEXT instruction (because there is no
* instruction "under" this INT3 instruction)
* So the IP is left pointing at the NEXT
* instruction. But we don't want the EM
* think that we stopped at the instruction
* after the INT3, so we need to decrement
* offEIP so it's pointing at the hard-coded
* INT3. Got it?
* On an ENTRYPOINT_DEBUG_EVENT, the address is
* already right, and lparam is ENTRY_BP.
*/
if (!lparam) {
lpbpr->offEIP = (UOFFSET)de->
u.Exception.ExceptionRecord.ExceptionAddress;
}
}
}
break;
case CREATE_PROCESS_DEBUG_EVENT:
/*
* A Create Process event has occured. The following
* messages need to be sent back
* dbceAssignPID: Associate our handle with the debugger
* dbcModLoad: Inform the debugger of the module load for
* the main exe (this is done at the end of this routine)
*/
{
HPRCX hprc = (HPRCX)lparam;
/*
* Has the debugger requested this process?
* ie: has it already given us the HPID for this process?
*/
if (hprc->hpid != (HPID)INVALID){
lpbPacket = (LPBYTE)&(hprc->pid);
cbPacket = sizeof(hprc->pid);
/* Want the hprc for the child NOT the DM */
rtp.hpid = hprc->hpid;
#ifndef KERNEL
// hack for made-up image names:
{
extern int MagicModuleId;
MagicModuleId = 0;
}
#endif
rtp.dbc = dbceAssignPID;
}
/*
* The debugger doesn't know about this process yet,
* request an HPID for this new process.
*/
else {
LPNPP lpnpp = MHAlloc(cbPacket=sizeof(NPP));
toFree = lpnpp;
lpbPacket = (LPBYTE)lpnpp;
packetType = tlfRequest;
/*
* We must temporarily assign a valid HPID to this HPRC
* because OSDebug will try to de-reference it in the
* TL callback function
*/
rtp.hpid = hpidRoot;
lpnpp->pid = hprc->pid;
lpnpp->fReallyNew = TRUE;
rtp.dbc = dbcNewProc;
}
}
break;
case CREATE_THREAD_DEBUG_EVENT:
{
LPTCR lptcr = (LPTCR) MHAlloc(cbPacket=sizeof(TCR));
toFree = lpbPacket = (LPBYTE)lptcr;
packetType = tlfRequest;
lptcr->tid = hthd->tid;
lptcr->uoffTEB = (UOFFSET) hthd->offTeb;
rtp.hpid = hthd->hprc->hpid;
rtp.htid = hthd->htid;
rtp.dbc = dbcCreateThread;
}
break;
case EXIT_PROCESS_DEBUG_EVENT:
cbPacket = sizeof(DWORD);
lpbPacket = (LPBYTE) &(de->u.ExitProcess.dwExitCode);
hthd->hprc->pstate |= ps_exited;
rtp.hpid = hthd->hprc->hpid;
rtp.htid = hthd->htid;
rtp.dbc = dbcProcTerm;
break;
case EXIT_THREAD_DEBUG_EVENT:
cbPacket = sizeof(DWORD);
lpbPacket = (LPBYTE) &(de->u.ExitThread.dwExitCode);
hthd->tstate |= ts_dead; /* Mark thread as dead */
hthd->hprc->pstate |= ps_deadThread;
rtp.dbc = dbcThreadTerm;
break;
case DESTROY_PROCESS_DEBUG_EVENT:
DPRINT(3, (_T("DESTROY PROCESS\n")));
hthd->hprc->pstate |= ps_destroyed;
rtp.dbc = dbcDeleteProc;
break;
case DESTROY_THREAD_DEBUG_EVENT:
/*
* Check if already destroyed
*/
assert( (hthd->tstate & ts_destroyed) == 0 );
DPRINT(3, (_T("DESTROY THREAD\n")));
hthd->tstate |= ts_destroyed;
cbPacket = sizeof(DWORD);
// kentf exit code is bogus here
lpbPacket = (LPBYTE) &(de->u.ExitThread.dwExitCode);
rtp.dbc = dbcDeleteThread;
break;
case LOAD_DLL_DEBUG_EVENT:
rtp.dbc = dbcModLoad;
lpbPacket = (PVOID)lparam;
cbPacket = (USHORT)wparam;
//ValidateHeap();
toFree = (LPVOID)lpbPacket;
#ifdef KERNEL
// if we just loaded the kernel, get magic values
// If we are loading the kernel, this need to be synchronous so
// that we can load the magic values.
// But only for crash dumps.
// (we should be able to dispose of this by using KDDEBUGGER_DATA,
// but only for NT 5)
if (fCrashDump &&
_tcsicmp( (PTSTR) de->u.LoadDll.lpImageName, KERNEL_IMAGE_NAME ) == 0) {
packetType = tlfRequest;
}
#endif
break;
case UNLOAD_DLL_DEBUG_EVENT:
assert( Is64PtrSE(de->u.UnloadDll.lpBaseOfDll) );
cbPacket = sizeof(ULONG64);
lpbPacket = (LPBYTE) &(de->u.UnloadDll.lpBaseOfDll);
rtp.dbc = dbceModFree32;
break;
case OUTPUT_DEBUG_STRING_EVENT:
{
LPINFOAVAIL lpinf;
DWORD cbR;
rtp.dbc = dbcInfoAvail;
cbPacket = (WORD)(sizeof(INFOAVAIL) +
de->u.DebugString.nDebugStringLength + 1);
lpinf = (LPINFOAVAIL) lpbPacket = MHAlloc(cbPacket);
toFree = lpbPacket;
lpinf->fReply = FALSE;
lpinf->fUniCode = de->u.DebugString.fUnicode;
DbgReadMemory(hthd->hprc,
de->u.DebugString.lpDebugStringData,
&lpinf->buffer[0],
de->u.DebugString.nDebugStringLength,
&cbR);
lpinf->buffer[cbR] = 0;
}
break;
case INPUT_DEBUG_STRING_EVENT:
{
// BUGBUG - kcarlos - This code is not UNICODE safe. Should it be?
LPINFOAVAIL lpinf;
DWORD cbR;
packetType = tlfRequest;
rtp.dbc = dbcInfoReq;
cbPacket =
(WORD)(sizeof(INFOAVAIL) + de->u.DebugString.nDebugStringLength + 1);
lpinf = (LPINFOAVAIL) lpbPacket = MHAlloc(cbPacket);
toFree = lpbPacket;
lpinf->fReply = TRUE;
lpinf->fUniCode = de->u.DebugString.fUnicode;
memcpy(&lpinf->buffer[0],
(PVOID)de->u.DebugString.lpDebugStringData,
de->u.DebugString.nDebugStringLength
);
lpinf->buffer[ de->u.DebugString.nDebugStringLength ] = 0;
#if 0
// Protocol changed and someone forgot to update this???
DbgReadMemory(hthd->hprc,
de->u.DebugString.lpDebugStringData,
&lpinf->buffer[0],
de->u.DebugString.nDebugStringLength,
&cbR);
lpinf->buffer[ cbR ] = 0;
#endif
}
break;
case ATTACH_DEADLOCK_DEBUG_EVENT:
{
static XOSD xosd;
xosd = xosdAttachDeadlock;
cbPacket = sizeof(xosd);
lpbPacket = (LPBYTE) &xosd;
rtp.dbc = dbcError;
}
break;
case ATTACH_EXITED_DEBUG_EVENT:
{
static XOSD xosd;
xosd = xosdAttachDeadlock;//**
cbPacket = sizeof(xosd);
lpbPacket = (LPBYTE) &xosd;
rtp.dbc = dbcError;
}
break;
case MESSAGE_DEBUG_EVENT:
case MESSAGE_SEND_DEBUG_EVENT:
cbPacket = sizeof ( MSGI );
lpbPacket = (LPBYTE) lparam;
rtp.dbc = (eventCode == MESSAGE_DEBUG_EVENT) ? dbcMsgBpt :dbcSendMsgBpt;
break;
default:
DPRINT(1, (_T("Error, unknown event\n\r")));
hthd->fExceptionHandled = TRUE;
ContinueThread(hthd);
return;
}
DPRINT(3, (_T("Notify the debugger: dbc=%x, hpid=%x, htid=%x, cbpacket=%d "),
rtp.dbc, rtp.hpid, rtp.htid, cbPacket+FIELD_OFFSET(RTP, rgbVar)));
if (!(rtp.cb=cbPacket)) {
DmTlFunc(packetType, rtp.hpid, FIELD_OFFSET(RTP, rgbVar), (LPARAM) &rtp);
}
else {
lprtp = (LPRTP)MHAlloc(FIELD_OFFSET(RTP, rgbVar)+cbPacket);
_fmemcpy(lprtp, &rtp, FIELD_OFFSET(RTP, rgbVar));
_fmemcpy(lprtp->rgbVar, lpbPacket, cbPacket);
DmTlFunc(packetType, rtp.hpid, (FIELD_OFFSET(RTP, rgbVar)+cbPacket), (LPARAM) lprtp);
MHFree(lprtp);
}
if (toFree) {
MHFree(toFree);
}
DPRINT(3, (_T("\n")));
switch(eventCode){
case CREATE_THREAD_DEBUG_EVENT:
if (packetType == tlfRequest) {
hthd->htid = *((HTID *) abEMReplyBuf);
}
SetEvent(hthd->hprc->hEventCreateThread);
break;
case CREATE_PROCESS_DEBUG_EVENT:
if (packetType == tlfRequest) {
((HPRCX)lparam)->hpid = *((HPID *) abEMReplyBuf);
} else {
XOSD xosd = xosdNone;
DmTlFunc( tlfReply,
((HPRCX)lparam)->hpid,
sizeof(XOSD),
(LPARAM) &xosd);
}
SetEvent(hEventCreateProcess);
break;
case OUTPUT_DEBUG_STRING_EVENT:
// just here to synchronize
break;
case INPUT_DEBUG_STRING_EVENT:
{
de->u.DebugString.nDebugStringLength = _tcslen(abEMReplyBuf) + 1;
de->u.DebugString.lpDebugStringData = (DWORD64) MHAlloc(de->u.DebugString.nDebugStringLength);
_tcscpy((PVOID) de->u.DebugString.lpDebugStringData, abEMReplyBuf);
// Maybe this was intended to be a user mode reply????
// Well it sure doesn't work for kernel!
#if 0
DWORD cbR;
de->u.DebugString.nDebugStringLength = _ftcslen(abEMReplyBuf) + 1;
DbgWriteMemory(hthd->hprc,
de->u.DebugString.lpDebugStringData,
abEMReplyBuf,
de->u.DebugString.nDebugStringLength,
&cbR);
#endif
}
break;
default:
break;
}
//ValidateHeap();
return;
} /* NotifyEM() */
/** ACTIONTERMINATEPROCESS
* PURPOSE:
* INPUT:
* OUTPUT:
* EXCEPTIONS:
* IMPLEMENTATION:
*/
VOID
ActionTerminateProcess (
DEBUG_EVENT64 *pde,
HTHDX hthd,
DWORD unused,
ULONG64 lparam
)
{
ConsumeAllProcessEvents ( (HPRCX)lparam, TRUE );
hthd->fExceptionHandled = TRUE;
ContinueThread(hthd);
hthd->hprc->pstate |= ps_dead;
} /* ACTIONTERMINATEPROCESS */
/** ACTIONPROCESSLOADFAILED
* PURPOSE:
* When we issued the CreateProcess, that succeeded; but then we
* got an exception with the EXCEPTION_NONCONTINUABLE flag set,
* which means after letting the debuggee continue we would get
* a process-termination notification. (For example, if the
* system can't find a DLL that the process needs, it sends a
* STATUS_DLL_NOT_FOUND notification with this flag set.) So
* we've said ok, and now the system has sent us a procterm
* notification for it.
* INPUT:
* OUTPUT:
* EXCEPTIONS:
* IMPLEMENTATION:
*/
VOID
ActionProcessLoadFailed (
DEBUG_EVENT64 *pde,
HTHDX hthd,
DWORDLONG unused,
ULONG64 lparam
)
{
/* Call another action proc & do what it does */
ActionTerminateProcess(pde, hthd, 0, (ULONG64)hthd->hprc);
}
/** ACTIONNONCONTINUABLEEXCEPTION
* PURPOSE:
* When we issued the CreateProcess, that succeeded; but then we
* got an exception with the EXCEPTION_NONCONTINUABLE flag set,
* which means after letting the debuggee continue we would get
* a process-termination notification. (For example, if the
* system can't find a DLL that the process needs, it sends a
* STATUS_DLL_NOT_FOUND notification with this flag set.) So
* we've said ok, and now the system has sent us a last-chance
* notification for it.
* INPUT:
* OUTPUT:
* EXCEPTIONS:
* IMPLEMENTATION:
*/
VOID
ActionNoncontinuableException (
DEBUG_EVENT64 *pde,
HTHDX hthd,
DWORDLONG unused,
ULONG64 lparam
)
{
// [cuda#5673 7/8/93 mikemo] This is very strange -- it seems that
// in some situations NT has now sent us a SECOND first-chance
// notification, even though it already sent us one. I don't know
// why this is, but if this happens, I register another event expecting
// a last-chance notification, instead of expecting exit-process.
if (pde->u.Exception.dwFirstChance) {
RegisterExpectedEvent(hthd->hprc,
hthd,
EXCEPTION_DEBUG_EVENT,
(DWORD_PTR)pde->u.Exception.ExceptionRecord.ExceptionCode,
DONT_NOTIFY,
ActionNoncontinuableException,
FALSE,
0
);
} else {
RegisterExpectedEvent(hthd->hprc,
hthd,
EXIT_PROCESS_DEBUG_EVENT,
NO_SUBCLASS,
DONT_NOTIFY,
ActionProcessLoadFailed,
FALSE,
0);
}
ContinueThreadEx(hthd,
(DWORD)DBG_EXCEPTION_NOT_HANDLED,
QT_CONTINUE_DEBUG_EVENT,
ts_running);
}
void
ProcessExceptionEvent(
DEBUG_EVENT64* de,
HTHDX hthd
)
{
DWORD_PTR subclass = de->u.Exception.ExceptionRecord.ExceptionCode;
DWORD firstChance = de->u.Exception.dwFirstChance;
PBREAKPOINT bp=NULL;
EXCEPTION_FILTER_DEFAULT efd;
// If the thread is in a pre-start state we failed in the
// program loader, probably because a link couldn't be resolved.
if ( hthd->hprc->pstate & ps_preStart ) {
DPRINT(1, (_T("Exception during init\n")));
// since we will probably never see the expected BP,
// clear it out, and clear the prestart flag on the
// thread. If the exception is continuable, go ahead
// and deliver the exception to the shell as normal.
hthd->hprc->pstate &= ~ps_preStart;
hthd->tstate |= ts_stopped;
ConsumeAllProcessEvents(hthd->hprc, TRUE);
#if 0
if (!CrashDump &&
de->u.Exception.ExceptionRecord.ExceptionFlags & EXCEPTION_NONCONTINUABLE) {
// BUGBUG This case does not send an exception record to the shell; instead
// BUGBUG it aborts the process and sends an error message. This is probably
// BUGBUG undesirable behaviour in a real debugger.
DWORD dwRet;
LPTSTR lszError = NULL;
// The debugger is expecting either an OS-specific error code
// or an XOSD. We don't have an OS
// error code (all we have is an exception number). The debugger
// treats STATUS_DLL_NOT_FOUND as an error code, but doesn't
// recognize any other exception codes, so for other exception
// codes, just return xosdLoadChild.
if (subclass == STATUS_DLL_NOT_FOUND) {
TCHAR rgch[256]; // same as magic number in em\errors.c
if (!LoadString(hInstance, IDS_STATUS_DLL_NOT_FOUND, rgch, _tsizeof(rgch))) {
assert(FALSE);
} else {
lszError = rgch;
}
dwRet = xosdFileNotFound;
} else {
dwRet = xosdGeneral;
}
SendNTError(hthd->hprc, dwRet, lszError);
// Actually the CreateProcess succeeded, and then we
// received a notification with an exception; so we need
// to keep calling ContinueDebugEvent until the process
// is really dead.
RegisterExpectedEvent(hthd->hprc,
hthd,
EXCEPTION_DEBUG_EVENT,
subclass,
DONT_NOTIFY,
ActionNoncontinuableException,
FALSE,
0);
hthd->hprc->pstate |= ps_exited;
hthd->tstate &= ~ts_stopped;
AddQueue( QT_CONTINUE_DEBUG_EVENT,
hthd->hprc->pid,
hthd->tid,
(DWORD)DBG_EXCEPTION_NOT_HANDLED,
0);
return;
}
#endif // 0
}
switch (subclass) {
case EXCEPTION_SINGLE_STEP:
#if defined(DOLPHIN)
#if defined(NO_TRACE_BIT)
// BUGBUG kentf This next bit of code is a rather overcomplicated
// BUGBUG way of creating a deadlock. The same task could be
// BUGBUG accomplished with considerably less code.
// BUGBUG
// BUGBUG The problem which this intends to solve is as follows:
// BUGBUG When a thread is stepped using a code breakpoint, it
// BUGBUG may happen that another thread will execute the
// BUGBUG breakpoint. Since that thread is not intended to stop
// BUGBUG there, we must replace the instruction and allow that
// BUGBUG thread to continue. During the time that the "wrong"
// BUGBUG thread is executing the instruction, it may happen that
// BUGBUG the "right" thread will hit that instruction, and run
// BUGBUG through it, thereby missing the breakpoint.
// BUGBUG
// BUGBUG The following code attempts to solve the problem by
// BUGBUG suspending all of the threads in the process while
// BUGBUG stepping the "wrong" thread off of the breakpoint.
// BUGBUG
// BUGBUG This algorithm assumes that the "wrong" thread will be
// BUGBUG able to complete the instruction while all of the other
// BUGBUG threads are suspended. This assumption is easily
// BUGBUG disproven. For example, try this on a call to
// BUGBUG WaitForSingleObject.
// BUGBUG
// BUGBUG Secondly, this code assumes that the next debug event
// BUGBUG will be the exception from the "wrong" thread completing
// BUGBUG the new step. This is a absolutely not the case, and
// BUGBUG the code below will fail in ways much more disastrous
// BUGBUG than running through a step.
// BUGBUG
// BUGBUG
// BUGBUG Note 1: This particular condition cannot arise on
// BUGBUG a processor with a step flag, since the step flag
// BUGBUG cannot cause a single step exception on the
// BUGBUG wrong thread (except as a result of an OS bug [ref:win9x]).
{
/* This single step was intended for another thread */
HPRCX hprc = hthd->hprc;
HTHDX hthdCurr = NULL;
BREAKPOINT* tmpBP = NULL;
UOFF64 NextOffset;
ADDR addr;
BP_UNIT opcode = BP_OPCODE;
DWORD i;
AddrFromHthdx(&addr, hthd);
if (bp = AtBP(hthd)) {
RestoreInstrBP(hthd, bp);
}
/* Suspend all threads except this one */
for (hthdCurr = hprc->hthdChild; hthdCurr != NULL; hthdCurr = hthdCurr->nextSibling) {
if (hthdCurr != hthd) {
if (SuspendThread(hthdCurr->rwHand) == -1L) {
; // Internal error;
}
}
}
/* Set BP at next offset so we can get off this SS */
NextOffset = GetNextOffset( hthd, FALSE);
if (NextOffset != 0) {
ADDR tmpAddr;
AddrInit(&tmpAddr, 0, 0, NextOffset, TRUE, TRUE, FALSE, FALSE);
tmpBP = SetBP( hprc, hthd, bptpExec, bpnsStop, &tmpAddr, (HPID) INVALID );
assert(tmpBP);
} else {
assert(FALSE);
}
/* Get off SS */
VERIFY(ContinueDebugEvent(hprc->pid, hthd->tid, DBG_CONTINUE));
if (DMWaitForDebugEvent(de, INFINITE)) {
// this is certain to fire, and for the wrong process
assert(de->dwDebugEventCode == EXCEPTION_DEBUG_EVENT);
} else {
assert(FALSE);
}
hthd->context.ContextFlags = CONTEXT_FULL | CONTEXT_FLOATING_POINT;
VERIFY(DbgGetThreadContext(hthd, &hthd->context));
hthd->fContextDirty = FALSE;
hthd->fIsCallDone = FALSE;
RemoveBP(tmpBP);
/* Resume all threads except this one */
for (hthdCurr = hprc->hthdChild; hthdCurr != NULL; hthdCurr = hthdCurr->nextSibling) {
if (hthdCurr != hthd) {
if (ResumeThread(hthdCurr->rwHand) == -1L) {
assert(FALSE); // Internal error;
}
}
}
if (bp == NULL) {
bp = SetBP(hprc, hthd, bptpExec, bpnsStop, &addr, FALSE);
} else {
/* Put back SS */
AddrWriteMemory(hprc, hthd, &bp->addr, (LPBYTE)&opcode, BP_SIZE, &i);
bp->instances++;
}
bp->isStep++;
ContinueThread(hthd);
}
#endif // NO_TRACE_BIT
#endif // DOLPHIN
break;
default:
/*
* The user can define a set of exceptions for which we do not
* notify the shell on a first chance.
*/
if (!firstChance) {
DPRINT(3, (_T("2nd Chance Exception %p.\n"),subclass));
hthd->tstate |= ts_second;
efd = efdStop;
} else {
hthd->tstate |= ts_first;
efd = ExceptionAction(hthd->hprc, (ULONG)subclass);
switch (efd) {
case efdNotify:
DPRINT(3, (_T("Notifying EM of Exception %p.\n"),subclass));
NotifyEM(de, hthd, 0, efd);
// fall through to ignore case
#if defined (TARGET_i386) && !defined (KERNEL)
// If we are stepping (=> trace flag set) and we are attempting
// to step on a statement that raising an exception that gets
// caught somewhere else, then having the trace flag set
// prevents us from ending up where the NLG thing takes us.
// Turn it off & assume that we will get a second chance.
// This is wrong for disasm stepping and may be otherwise
// problematic.
hthd->context.EFlags &= ~TF_BIT_MASK;
hthd->fContextDirty = TRUE;
#endif
#if defined (TARGET_IA64) && !defined (KERNEL)
// If we are stepping (=> trace flag set) and we are attempting
// to step on a statement that raising an exception that gets
// caught somewhere else, then having the trace flag set
// prevents us from ending up where the NLG thing takes us.
// Turn it off & assume that we will get a second chance.
// This is wrong for disasm stepping and may be otherwise
// problematic.
hthd->context.StIPSR &= ~TF_BIT_MASK;
hthd->fContextDirty = TRUE;
#endif
case efdIgnore:
DPRINT(3, (_T("Ignoring Exception %p.\n"),subclass));
ContinueThreadEx(hthd,
DBG_EXCEPTION_NOT_HANDLED,
QT_CONTINUE_DEBUG_EVENT,
ts_running);
return;
case efdStop:
case efdCommand:
break;
}
}
// Cleanup ...
hthd->fReturning = FALSE;
ConsumeAllThreadEvents(hthd, FALSE);
//ExprBPStop(hthd->hprc, hthd);
NotifyEM(de, hthd, 0, efd);
break;
}
return;
} /* ProcessExceptionEvent() */
void
ProcessRipEvent(
DEBUG_EVENT64 * de,
HTHDX hthd
)
{
if (hthd) {
hthd->tstate |= ts_rip;
}
NotifyEM( de, hthd, 0, 0 );
} /* ProcessRipEvent() */
void
ProcessBreakpointEvent(
DEBUG_EVENT64 * pde,
HTHDX hthd
)
{
PBREAKPOINT pbp = (PBREAKPOINT) ((((DWORD_PTR)pde->u.Exception.ExceptionRecord.NumberParameters) << 32) | (DWORD)pde->u.Exception.ExceptionRecord.ExceptionCode);
#if 0 // This code doesn't do pointer extension properly
PBREAKPOINT pbp;
DWORD_PTR ibp = pde->u.Exception.ExceptionRecord.ExceptionCode;
ibp |= pde->u.Exception.ExceptionRecord.NumberParameters << 32;
pbp = (PBREAKPOINT)ibp;
#endif
pde->u.Exception.ExceptionRecord.ExceptionCode = 0;
pde->u.Exception.ExceptionRecord.NumberParameters = 0;
DPRINT(1, (_T("Hit a breakpoint -- ")));
if (!pbp) {
DPRINT(1, (_T("[Embedded BP]\n")));
// already set this in ProcessDebugEvent
//SetBPFlag(hthd, EMBEDDED_BP);
NotifyEM(pde, hthd, 0, (ULONG64)pbp);
} else if (!pbp->hthd || pbp->hthd == hthd) {
DPRINT(1, (_T("[One of our own BP's.]\n")));
if (pbp->bpType == bptpMessage) {
#ifdef KERNEL
assert(!"Message BP not allowed in Kernel mode");
NotifyEM(pde, hthd, 0, (ULONG64)pbp);
#else
MSGI msgi;
if (!GetWndProcMessage (hthd, &msgi.dwMessage)) {
assert (FALSE);
}
msgi.dwMask = 0; // mask is currently unused in the dm
msgi.addr = pbp->addr;
pde->dwDebugEventCode = MESSAGE_DEBUG_EVENT;
SetBPFlag (hthd, pbp);
ConsumeAllThreadEvents (hthd, FALSE);
NotifyEM (pde, hthd, 0, (ULONG64)&msgi);
#endif // KERNEL
} else if (CheckDataBP (hthd, pbp)) {
SetBPFlag(hthd, pbp);
if ((pbp->bpNotify == bpnsStop) ||
(pbp->bpNotify == bpnsCheck && CheckBpt(hthd, pbp))) {
ConsumeAllThreadEvents(hthd, FALSE);
NotifyEM(pde, hthd, 0, (ULONG64)pbp);
} else {
if (pbp->bpNotify == bpnsContinue) {
// NotifyEM(...);
}
ContinueFromBP(hthd, pbp);
}
} else {
ContinueFromBP(hthd, pbp);
}
} else {
DPRINT(1, (_T("[BP for another thread]\n")));
// data bp should never hit on the wrong thread
assert(pbp->hWalk == NULL);
ContinueFromBP(hthd, pbp);
}
}
VOID
ContinueFromBP(
HTHDX hthd,
PBREAKPOINT pbp
)
{
METHOD *ContinueSSMethod;
if (!AtBP(hthd)) {
ExprBPContinue( hthd->hprc, hthd );
ContinueThread(hthd);
} else {
ContinueSSMethod = (METHOD*)MHAlloc(sizeof(METHOD));
ContinueSSMethod->notifyFunction = MethodContinueSS;
ContinueSSMethod->lparam = (ULONG64)ContinueSSMethod;
ContinueSSMethod->lparam2 = pbp;
RestoreInstrBP(hthd, pbp);
SingleStep(hthd, ContinueSSMethod, FALSE, FALSE);
}
}
VOID
StuffHthdx(
DEBUG_EVENT64 *de,
HPRCX hprc,
HTHDX hthd
)
/*++
Routine Description:
Common code for CreateProcess and CreateThread events. Stuff
the hthd with default values, add the new structure to the process'
chain. Critical section csThreadProcList must be held when calling
this routine.
Arguments:
de - Supplies debug event
hprc - Supplies process structure
hthd - Supplies empty thread struct, returns full one.
Return Value:
--*/
{
hthd->nextGlobalThreadThisProbablyIsntTheOneYouWantedToUse = thdList->nextGlobalThreadThisProbablyIsntTheOneYouWantedToUse;
thdList->nextGlobalThreadThisProbablyIsntTheOneYouWantedToUse = hthd;
hthd->nextSibling = hprc->hthdChild;
hprc->hthdChild = (LPVOID)hthd;
hthd->hprc = hprc;
hthd->htid = 0;
hthd->atBP = (BREAKPOINT*)0;
hthd->tstate = ts_stopped;
hthd->fAddrIsReal = FALSE;
hthd->fAddrIsFlat = TRUE;
hthd->fAddrOff32 = TRUE;
hthd->fWowEvent = FALSE;
hthd->fStopOnNLG = FALSE;
hthd->fReturning = FALSE;
hthd->fDisplayReturnValues = TRUE;
#ifdef KERNEL
hthd->fContextStale = TRUE;
#endif
#ifndef KERNEL
hthd->pcs = NULL;
hthd->poleret = NULL;
#endif
InitializeListHead(&hthd->WalkList);
hthd->tid = de->dwThreadId;
if (de->dwDebugEventCode == CREATE_PROCESS_DEBUG_EVENT) {
hthd->rwHand = de->u.CreateProcessInfo.hThread;
hthd->offTeb = (OFFSET) de->u.CreateProcessInfo.lpThreadLocalBase;
hthd->lpStartAddress= de->u.CreateProcessInfo.lpStartAddress;
} else {
hthd->rwHand = de->u.CreateThread.hThread;
hthd->offTeb = (OFFSET) de->u.CreateThread.lpThreadLocalBase;
hthd->lpStartAddress= de->u.CreateThread.lpStartAddress;
}
}
void
ProcessCreateProcessEvent(
DEBUG_EVENT64 * pde,
HTHDX hthd
)
/*++
Routine Description:
This routine does the processing needed for a create process
event from the OS. We need to do the following:
- Set up our internal structures for the newly created thread
and process
- Get notifications back to the debugger
Arguments:
pde - Supplies pointer to the DEBUG_EVENT structure from the OS
hthd - Supplies thread descriptor that thread event occurred on
Return Value:
none
--*/
{
DEBUG_EVENT64 de2;
CREATE_PROCESS_DEBUG_INFO64 *pcpd = &pde->u.CreateProcessInfo;
HPRCX hprc;
LPBYTE lpbPacket;
WORD cbPacket;
DEBUG_PRINT(_T("ProcessCreateProcessEvent\r\n"));
ResetEvent(hEventCreateProcess);
hprc = InitProcess((HPID)INVALID);
// Stuff the process structure
if (fUseRoot) {
hprc->pstate |= ps_root;
hprc->hpid = hpidRoot;
fUseRoot = FALSE;
if (pcpd->lpStartAddress && FLoading16) {
hprc->f16bit = TRUE;
} else {
hprc->f16bit = FALSE;
}
}
hprc->pid = pde->dwProcessId;
hprc->pstate |= ps_preStart;
ResetEvent(hprc->hEventCreateThread);
// Create the first thread structure for this app
hthd = (HTHDX)MHAlloc(sizeof(HTHDXSTRUCT));
memset(hthd, 0, sizeof(*hthd));
EnterCriticalSection(&csThreadProcList);
StuffHthdx(pde, hprc, hthd);
LeaveCriticalSection(&csThreadProcList);
#ifndef KERNEL
#if defined(TARGET_i386)
hthd->context.ContextFlags = CONTEXT_FULL;
DbgGetThreadContext( hthd, &hthd->context );
hprc->segCode = (SEGMENT) hthd->context.SegCs;
#endif // i386
#endif // !KERNEL
#ifndef KERNEL
// try to create a handle with more permissions
if (!CrashDump) {
hprc->rwHand = OpenProcess(PROCESS_ALL_ACCESS, FALSE, pde->dwProcessId);
if (hprc->rwHand) {
hprc->CloseProcessHandle = TRUE;
} else {
hprc->rwHand = pcpd->hProcess;
}
}
#endif // KERNEL
/*
* There is going to be a breakpoint to announce that the
* process is loaded and runnable.
*/
nWaitingForLdrBreakpoint++;
if (pcpd->lpStartAddress == 0) {
// in an attach, the BP will be in another thread.
RegisterExpectedEvent( hprc,
(HTHDX)NULL,
BREAKPOINT_DEBUG_EVENT,
NO_SUBCLASS,
DONT_NOTIFY,
ActionDebugActiveReady,
FALSE,
(ULONG64)hprc);
#ifndef KERNEL
#if defined(INTERNAL)
// don't ever let it defer name resolutions
hprc->fNameRequired = TRUE;
#endif
#endif
} else {
// On a real start, the BP will be in the first thread.
RegisterExpectedEvent( hthd->hprc,
hthd,
BREAKPOINT_DEBUG_EVENT,
NO_SUBCLASS,
DONT_NOTIFY,
ActionDebugNewReady,
FALSE,
(ULONG64)hprc);
}
// Notify the EM of this newly created process.
// If not the root proc, an hpid will be created and added
// to the hprc by the em.
// We enter a crtitcal section to block the UI thread from
// querying the DM's prcList before it has been fully
// initialized.
EnterCriticalSection(&csThreadProcList);
NotifyEM(pde, hthd, 0, (ULONG64)hprc);
LeaveCriticalSection(&csThreadProcList);
#ifndef KERNEL
/*
* We also need to drop out a module load notification
* on this exe.
*/
assert( Is64PtrSE(pde->u.CreateProcessInfo.lpBaseOfImage) );
de2.dwDebugEventCode = LOAD_DLL_DEBUG_EVENT;
de2.dwProcessId = pde->dwProcessId;
de2.dwThreadId = pde->dwThreadId;
de2.u.LoadDll.hFile = pde->u.CreateProcessInfo.hFile;
de2.u.LoadDll.lpBaseOfDll = pde->u.CreateProcessInfo.lpBaseOfImage;
de2.u.LoadDll.lpImageName = pde->u.CreateProcessInfo.lpImageName;
de2.u.LoadDll.fUnicode = pde->u.CreateProcessInfo.fUnicode;
if (LoadDll(&de2, hthd, &cbPacket, &lpbPacket, FALSE) && (cbPacket != 0)) {
NotifyEM(&de2, hthd, cbPacket, (ULONG64)lpbPacket);
}
#endif // !KERNEL
/*
* Fake up a thread creation notification.
*/
pde->dwDebugEventCode = CREATE_THREAD_DEBUG_EVENT;
NotifyEM(pde, hthd, 0, (ULONG64)hprc);
/*
* Dont let the new process run: the shell will say Go()
* after receiving a CreateThread event.
*/
} /* ProcessCreateProcessEvent() */
void
ProcessCreateThreadEvent(
DEBUG_EVENT64 *pde,
HTHDX creatorHthd
)
{
CREATE_THREAD_DEBUG_INFO64 *ctd = &pde->u.CreateThread;
HTHDX hthd;
HPRCX hprc;
CONTEXT context;
#if defined(KERNEL) && defined(HAS_DEBUG_REGS)
KSPECIAL_REGISTERS ksr;
#endif // KERNEL && i386
Unreferenced(creatorHthd);
DPRINT(3, (_T("\n**CREATE THREAD EVENT\n")));
// Determine the hprc
hprc= HPRCFromPID(pde->dwProcessId);
ResetEvent(hprc->hEventCreateThread);
if (ctd->hThread == NULL) {
DPRINT(1, (_T("BAD HANDLE! BAD HANDLE!(%08lx)\n"), ctd->hThread));
AddQueue( QT_CONTINUE_DEBUG_EVENT,
pde->dwProcessId,
pde->dwThreadId,
DBG_CONTINUE,
0);
return;
}
if (!hprc) {
DPRINT(1, (_T("BAD PID! BAD PID!\n")));
AddQueue( QT_CONTINUE_DEBUG_EVENT,
pde->dwProcessId,
pde->dwThreadId,
DBG_CONTINUE,
0);
return;
}
// Create the thread structure
hthd = (HTHDX)MHAlloc(sizeof(HTHDXSTRUCT));
memset( hthd, 0, sizeof(*hthd));
// Stuff the structure
EnterCriticalSection(&csThreadProcList);
StuffHthdx(pde, hprc, hthd);
LeaveCriticalSection(&csThreadProcList);
// Let the expression breakpoint manager that a new thread
// has been created.
ExprBPCreateThread( hprc, hthd );
// initialize cache entries
context.ContextFlags = CONTEXT_FULL;
DbgGetThreadContext( hthd, &context );
#if defined(KERNEL) && defined(HAS_DEBUG_REGS)
GetExtendedContext( hthd, &ksr );
#endif // KERNEL && i386
// Notify the EM of this new thread
if (fDisconnected) {
ContinueThread(hthd);
} else {
NotifyEM(pde, hthd, 0, (ULONG64)hprc);
}
return;
}
void
ProcessExitProcessEvent(
DEBUG_EVENT64* pde,
HTHDX hthd
)
{
HPRCX hprc;
XOSD xosd;
HTHDX hthdT;
BREAKPOINT * pbp;
BREAKPOINT * pbpT;
DPRINT(3, (_T("ProcessExitProcessEvent\n")));
/*
* do all exit thread handling:
* If thread was created during/after the
* beginning of termination processing, we didn't
* pick it up, so don't try to destroy it.
*/
if (!hthd) {
hprc = HPRCFromPID(pde->dwProcessId);
} else {
hprc = hthd->hprc;
hthd->tstate |= ts_dead;
hthd->dwExitCode = pde->u.ExitProcess.dwExitCode;
}
#ifndef KERNEL
DEBUG_PRINT(_T("Try unloading modules.\r\n"));
UnloadAllModules( hprc, hthd? hthd:hprc->hthdChild, FALSE, TRUE );
DEBUG_PRINT(_T("Done with UnloadAllModules.\r\n"));
#endif
/* and exit process */
hprc->pstate |= ps_dead;
hprc->dwExitCode = pde->u.ExitProcess.dwExitCode;
ConsumeAllProcessEvents(hprc, TRUE);
/*
* Clean up BP records
*/
for (pbp = BPNextHprcPbp(hprc, NULL); pbp; pbp = pbpT) {
pbpT = BPNextHprcPbp(hprc, pbp);
RemoveBP(pbp);
}
#ifndef KERNEL
/*
* Clean up any Fibers that are left.
*/
RemoveFiberList(hprc);
#endif
/*
* If we haven't seen EXIT_THREAD events for any
* threads, we aren't going to, so consider them done.
*/
for (hthdT = hprc->hthdChild; hthdT; hthdT = hthdT->nextSibling) {
if ( !(hthdT->tstate & ts_dead) ) {
hthdT->tstate |= ts_dead;
hthdT->tstate &= ~ts_stopped;
}
}
/*
* If process hasn't initialized yet, we were expecting
* a breakpoint to notify us that all the DLLs are here.
* We didn't get that yet, so reply here.
*/
if (hprc->pstate & ps_preStart) {
xosd = xosdUnknown;
DmTlFunc( tlfReply, hprc->hpid, sizeof(XOSD), (LPARAM) &xosd);
}
if (!(hprc->pstate & ps_killed)) {
assert(hthd);
pde->dwDebugEventCode = EXIT_THREAD_DEBUG_EVENT;
pde->u.ExitThread.dwExitCode = hprc->dwExitCode;
NotifyEM(pde, hthd, 0, 0);
} else {
/*
* If ProcessTerminateProcessCmd() killed this,
* silently continue the event and release the semaphore.
* Don't notify the EM of anything; ProcessUnloadCmd()
* will do that for any undestroyed threads.
*/
AddQueue( QT_CONTINUE_DEBUG_EVENT,
pde->dwProcessId,
pde->dwThreadId,
DBG_CONTINUE,
0);
}
DMSqlTerminate(hprc);
SetEvent(hprc->hExitEvent);
} /* ProcessExitProcessEvent() */
void
ProcessExitThreadEvent(
DEBUG_EVENT64* pde,
HTHDX hthd
)
{
HPRCX hprc = hthd->hprc;
DPRINT(3, (_T("** ProcessExitThreadEvent, hthd == %p\n"), hthd));
hthd->tstate |= ts_dead;
if (hthd->tstate & ts_frozen) {
ResumeThread(hthd->rwHand);
hthd->tstate &= ~ts_frozen;
}
hthd->dwExitCode = pde->u.ExitThread.dwExitCode;
/*
* Free all events for this thread
*/
ConsumeAllThreadEvents(hthd, TRUE);
// Let the Expression Breakpoint manager know that this thread
// is gone.
ExprBPExitThread( hprc, hthd );
if (hprc->pstate & ps_killed) {
AddQueue( QT_CONTINUE_DEBUG_EVENT,
hthd->hprc->pid,
hthd->tid,
DBG_CONTINUE,
0);
} else if (fDisconnected) {
hthd->hprc->pstate |= ps_exited;
AddQueue( QT_CONTINUE_DEBUG_EVENT,
hthd->hprc->pid,
hthd->tid,
DBG_CONTINUE,
0);
} else {
NotifyEM(pde, hthd, 0, 0);
}
return;
} /* ProcessExitThreadEvent() */
void
ProcessLoadDLLEvent(
DEBUG_EVENT64* de,
HTHDX hthd
)
{
LPBYTE lpbPacket;
WORD cbPacket;
if (LoadDll(de, hthd, &cbPacket, &lpbPacket, TRUE) || (cbPacket == 0)) {
NotifyEM(de, hthd, cbPacket, (ULONG64)lpbPacket);
}
return;
} /* ProcessLoadDLLEvent() */
void
ProcessUnloadDLLEvent(
DEBUG_EVENT64* pde,
HTHDX hthd
)
{
int iDll;
HPRCX hprc = hthd->hprc;
DPRINT(10, (_T("** UnloadDll %I64x\n"), pde->u.UnloadDll.lpBaseOfDll));
assert( Is64PtrSE(pde->u.UnloadDll.lpBaseOfDll) );
for (iDll = 0; iDll < hprc->cDllList; iDll++) {
if (hprc->rgDllList[iDll].fValidDll) {
assert( Is64PtrSE(hprc->rgDllList[iDll].offBaseOfImage) );
if (hprc->rgDllList[iDll].offBaseOfImage ==
(OFFSET) pde->u.UnloadDll.lpBaseOfDll) {
break;
}
}
}
/*
* Make sure that we found a legal address. If not then assert
* and check for problems.
*/
#ifndef KERNEL
// this happens all the time in kernel mode
// when user mode modloads are enabled
assert( iDll != hprc->cDllList );
#endif
if (iDll != hprc->cDllList) {
if (!fDisconnected) {
NotifyEM(pde, hthd, 0, 0);
}
DestroyDllLoadItem( &hprc->rgDllList[iDll] );
}
ContinueThread(hthd);
return;
}
void
DestroyDllLoadItem(
PDLLLOAD_ITEM pDll
)
{
if (pDll->szDllName) {
MHFree(pDll->szDllName);
pDll->szDllName = NULL;
}
#ifdef KERNEL
if (pDll->sec) {
MHFree(pDll->sec);
pDll->sec = NULL;
}
#endif
pDll->offBaseOfImage = 0;
pDll->cbImage = 0;
pDll->fValidDll = FALSE;
return;
}
void ProcessOutputDebugStringEvent(DEBUG_EVENT64* de, HTHDX hthd)
/*++
Routine Description:
Handle an OutputDebugString from the debuggee
Arguments:
de - Supplies DEBUG_EVENT64 struct
hthd - Supplies thread descriptor for thread that generated the event
Return Value:
None
--*/
{
int cb = de->u.DebugString.nDebugStringLength;
#if DBG
DWORD cbR;
char rgch[256];
HANDLE rwHand;
cb = min(cb, 256);
rwHand = hthd->hprc->rwHand;
DbgReadMemory(hthd->hprc, de->u.DebugString.lpDebugStringData,
rgch, cb, &cbR);
rgch[cbR] = 0;
DPRINT(3, (_T("%s\n"), rgch));
#endif
NotifyEM(de, hthd, 0, 0);
ContinueThread(hthd);
return;
}
#ifndef KERNEL
void
ProcessBogusSSEvent(
DEBUG_EVENT64* de,
HTHDX hthd
)
/*++
Routine Description:
Handle a Bogus Win95 Single step event. Just continue execution
as if nothing happened.
Arguments:
de - Supplies DEBUG_EVENT64 struct
hthd - Supplies thread descriptor for thread
that generated the event
Return Value:
None
--*/
{
ContinueThread(hthd);
return;
}
#endif
void
Reply(
UINT length,
LPVOID lpbBuffer,
HPID hpid
)
/*++
Routine Description:
Send a reply packet to a tlfRequest from the EM
Arguments:
length - Supplies length of reply
lpbBuffer - Supplies reply data
hpid - Supplies handle to EM process descriptor
Return Value:
None
--*/
{
/*
* Add the size of the packet header to the length
*/
length += FIELD_OFFSET(DM_MSG, rgb);
DPRINT(5, (_T("Reply to EM [%d]\n"), length));
assert(length <= sizeof(abDMReplyBuf) || lpbBuffer != abDMReplyBuf);
if (DmTlFunc) { // IF there is a TL loaded, reply
DmTlFunc(tlfReply, hpid, length, (LPARAM) lpbBuffer);
}
return;
}
#ifdef KERNEL
#define DMAPILOCK() \
if (!TryApiLock()) {\
LpDmMsg->xosdRet = xosdTargetIsRunning;\
Reply( sizeof( XOSD ), LpDmMsg, lpdbb->hpid );\
break;\
}
#define DMAPIRELEASE() ReleaseApiLock()
#else
#define DMAPILOCK()
#define DMAPIRELEASE()
#endif
VOID FAR PASCAL
DMFunc(
DWORD cb,
LPDBB lpdbb
)
/*++
Routine Description:
This is the main entry point for the DM. This takes dmf
message packets from the debugger and handles them, usually
by dispatching to a worker function.
Arguments:
cb - supplies size of data packet
lpdbb - supplies pointer to packet
Return Value:
--*/
{
DMF dmf;
HPRCX hprc;
HTHDX hthd;
XOSD xosd = xosdNone;
dmf = (DMF) (lpdbb->dmf & 0xffff);
DEBUG_PRINT(_T("DMFunc\r\n"));
DPRINT(5, (_T("DmFunc [%2x] "), dmf));
hprc = HPRCFromHPID(lpdbb->hpid);
hthd = HTHDXFromHPIDHTID(lpdbb->hpid, lpdbb->htid);
switch ( dmf ) {
case dmfSelLim:
DPRINT(5, (_T("dmfSelLim\n")));
DMAPILOCK();
ProcessSelLimCmd(hprc, hthd, lpdbb);
DMAPIRELEASE();
break;
case dmfSetMulti:
DPRINT(5, (_T("dmfSetMulti\n")));
LpDmMsg->xosdRet = xosdNone;
Reply( 0, LpDmMsg, lpdbb->hpid );
break;
case dmfClearMulti:
DPRINT(5, (_T("dmfClearMulti\n")));
LpDmMsg->xosdRet = xosdNone;
Reply( 0, LpDmMsg, lpdbb->hpid );
break;
case dmfDebugger:
DPRINT(5, (_T("dmfDebugger\n")));
LpDmMsg->xosdRet = xosdNone;
Reply( 0, LpDmMsg, lpdbb->hpid );
break;
case dmfCreatePid:
DPRINT(5,(_T("dmfCreatePid\r\n")));
ProcessCreateProcessCmd(hprc, hthd, lpdbb);
break;
case dmfDestroyPid:
DPRINT(5, (_T("dmfDestroyPid\n")));
LpDmMsg->xosdRet = FreeProcess(hprc, TRUE);
Reply( 0, LpDmMsg, lpdbb->hpid);
break;
case dmfSpawnOrphan:
DPRINT(5, (_T("dmfSpawnOrphan\n")));
#ifdef KERNEL
assert(!"Spawn orphan not allowed in kernel mode");
LpDmMsg->xosdRet = xosdUnknown;
Reply( 0, LpDmMsg, lpdbb->hpid);
#else
ProcessSpawnOrphanCmd (hprc, hthd, lpdbb);
#endif
break;
case dmfProgLoad:
DPRINT(5, (_T("dmfProgLoad\n")));
ProcessProgLoadCmd(hprc, hthd, lpdbb);
break;
case dmfProgFree:
DPRINT(5, (_T("dmfProgFree\n")));
#ifndef KERNEL
ProcessTerminateProcessCmd(hprc, hthd, lpdbb);
#else // KERNEL
if (!hprc) {
LpDmMsg->xosdRet = xosdNone;
Reply( 0, LpDmMsg, lpdbb->hpid);
break;
}
if (KdOptions[KDO_GOEXIT].value) {
HTHDX hthdT;
PBREAKPOINT bp;
KdOptions[KDO_GOEXIT].value = 0;
for (hthdT = hprc->hthdChild; hthdT; hthdT = hthdT->nextSibling) {
if (hthdT->tstate & ts_stopped) {
if (bp = AtBP(hthdT)) {
if (!hthdT->fDontStepOff) {
IncrementIP(hthdT);
}
}
if (hthdT->fContextDirty) {
DbgSetThreadContext( hthdT, &hthdT->context );
hthdT->fContextDirty = FALSE;
}
KdOptions[KDO_GOEXIT].value = 1;
break;
}
}
}
DMAPILOCK();
ClearBps();
ProcessTerminateProcessCmd(hprc, hthd, lpdbb);
DMAPIRELEASE();
ProcessUnloadCmd(hprc, hthd, lpdbb);
if (KdOptions[KDO_GOEXIT].value) {
ContinueTargetSystem( DBG_CONTINUE, NULL );
// Hack: don't exit from here with lock held
DMAPIRELEASE();
}
#endif // KERNEL
LpDmMsg->xosdRet = xosdNone;
Reply( 0, LpDmMsg, lpdbb->hpid);
break;
case dmfBreakpoint:
DPRINT(5,(_T("dmfBreakpoint\r\n")));
DMAPILOCK();
ProcessBreakpointCmd(hprc, hthd, lpdbb);
DMAPIRELEASE();
break;
case dmfReadMem:
// This replies in the function
DPRINT(5, (_T("dmfReadMem\n")));
DMAPILOCK();
ProcessReadMemoryCmd(hprc, hthd, lpdbb);
DMAPIRELEASE();
break;
case dmfWriteMem:
DPRINT(5, (_T("dmfWriteMem\n")));
DMAPILOCK();
ProcessWriteMemoryCmd(hprc, hthd, lpdbb);
DMAPIRELEASE();
break;
case dmfReadReg:
DPRINT(5, (_T("dmfReadReg\n")));
DMAPILOCK();
ProcessGetContextCmd(hprc, hthd, lpdbb);
DMAPIRELEASE();
break;
case dmfWriteReg:
DPRINT(5, (_T("dmfWriteReg\n")));
DMAPILOCK();
ProcessSetContextCmd(hprc, hthd, lpdbb);
DMAPIRELEASE();
break;
#ifdef HAS_DEBUG_REGS
case dmfReadRegEx:
DPRINT(5, (_T("dmfReadRegEx\n")));
DMAPILOCK();
#ifdef KERNEL
ProcessGetExtendedContextCmd(hprc, hthd, lpdbb);
#else
ProcessGetDRegsCmd(hprc, hthd, lpdbb);
#endif
DMAPIRELEASE();
break;
case dmfWriteRegEx:
DPRINT(5, (_T("dmfWriteRegEx\n")));
DMAPILOCK();
#ifdef KERNEL
ProcessSetExtendedContextCmd(hprc, hthd, lpdbb);
#else
ProcessSetDRegsCmd(hprc, hthd, lpdbb);
#endif
DMAPIRELEASE();
break;
#else // HAS_DEBUG_REGS
case dmfReadRegEx:
case dmfWriteRegEx:
DPRINT(5,(_T("Read/WriteRegEx\r\n")));
assert(dmf != dmfReadRegEx && dmf != dmfWriteRegEx);
LpDmMsg->xosdRet = xosdUnknown;
Reply( 0, LpDmMsg, lpdbb->hpid );
break;
#endif // HAS_DEBUG_REGS
case dmfGo:
DPRINT(5, (_T("dmfGo\n")));
#if !defined(KERNEL)
//Turn off fiber debugging
hprc->pFbrCntx = NULL;
#endif
// We should not have to lock this, since it should
// be a nop if the target is running anyway.
ProcessContinueCmd(hprc, hthd, lpdbb);
break;
#if defined(KERNEL)
case dmfTerm:
DPRINT(5, (_T("dmfTerm\n")));
DMAPILOCK();
ProcessTerminateProcessCmd(hprc, hthd, lpdbb);
DMAPIRELEASE();
break;
#endif
case dmfStop:
DPRINT(5, (_T("dmfStop\n")));
ProcessAsyncStopCmd(hprc, hthd, lpdbb);
break;
case dmfFreeze:
DPRINT(5, (_T("dmfFreeze\n")));
ProcessFreezeThreadCmd(hprc, hthd, lpdbb);
break;
case dmfResume:
DPRINT(5, (_T("dmfResume\n")));
ProcessAsyncGoCmd(hprc, hthd, lpdbb);
break;
case dmfInit:
DPRINT(5, (_T("dmfInit\n")));
Reply( 0, &xosd, lpdbb->hpid);
break;
case dmfUnInit:
DPRINT(5, (_T("dmfUnInit\n")));
#ifdef KERNEL
DmPollTerminate();
#endif
Reply ( 1, LpDmMsg, lpdbb->hpid);
#ifndef KERNEL
Cleanup();
#endif
break;
case dmfGetDmInfo:
DPRINT(5,(_T("getDmInfo\r\n")));
ProcessGetDmInfoCmd(hprc, lpdbb, cb);
break;
case dmfSetupExecute:
DPRINT(5, (_T("dmfSetupExecute\n")));
DMAPILOCK();
ProcessSetupExecuteCmd(hprc, hthd, lpdbb);
DMAPIRELEASE();
break;
case dmfStartExecute:
DPRINT(5, (_T("dmfStartExecute\n")));
DMAPILOCK();
ProcessStartExecuteCmd(hprc, hthd, lpdbb);
DMAPIRELEASE();
break;
case dmfCleanUpExecute:
DPRINT(5, (_T("dmfCleanupExecute\n")));
DMAPILOCK();
ProcessCleanUpExecuteCmd(hprc, hthd, lpdbb);
DMAPIRELEASE();
break;
case dmfSystemService:
DPRINT(5, (_T("dmfSystemService\n")));
ProcessSystemServiceCmd( hprc, hthd, lpdbb );
break;
case dmfDebugActive:
DPRINT(5, (_T("dmfDebugActive\n")));
ProcessDebugActiveCmd( hprc, hthd, lpdbb);
break;
case dmfSetPath:
DPRINT(5, (_T("dmfSetPath\n")));
ProcessSetPathCmd( hprc, hthd, lpdbb );
break;
case dmfQueryTlsBase:
DPRINT(5, (_T("dmfQueryTlsBase\n")));
ProcessQueryTlsBaseCmd(hprc, hthd, lpdbb );
break;
case dmfQuerySelector:
DPRINT(5, (_T("dmfQuerySelector\n")));
DMAPILOCK();
ProcessQuerySelectorCmd(hprc, hthd, lpdbb );
DMAPIRELEASE();
break;
case dmfVirtualQuery:
DPRINT(5,(_T("VirtualQuery\r\n")));
//DMAPILOCK();
ProcessVirtualQueryCmd(hprc, lpdbb);
//DMAPIRELEASE();
break;
case dmfRemoteQuit:
DPRINT(5,(_T("RemoteQuit\r\n")));
ProcessRemoteQuit();
break;
#ifdef KERNEL
case dmfGetSections:
DPRINT(5,(_T("GetGetSections\r\n")));
DMAPILOCK();
ProcessGetSectionsCmd( hprc, hthd, lpdbb );
DMAPIRELEASE();
break;
#endif
case dmfSetExceptionState:
DPRINT(5,(_T("SetExceptionState\r\n")));
ProcessSetExceptionState(hprc, hthd, lpdbb);
break;
case dmfGetExceptionState:
DPRINT(5,(_T("GetExceptionState\r\n")));
ProcessGetExceptionState(hprc, hthd, lpdbb);
break;
case dmfSingleStep:
DPRINT(5,(_T("SingleStep\r\n")));
#if !defined(KERNEL)
//Turn off fiber debugging
hprc->pFbrCntx = NULL;
#endif
// again, this should not do anything unless the target is stopped,
// so don't lock it.
ProcessSingleStepCmd(hprc, hthd, lpdbb);
break;
case dmfRangeStep:
DPRINT(5,(_T("RangeStep\r\n")));
#if !defined(KERNEL)
//Turn off fiber debugging
hprc->pFbrCntx = NULL;
#endif
ProcessRangeStepCmd(hprc, hthd, lpdbb);
break;
case dmfReturnStep:
DPRINT(5,(_T("ReturnStep\r\n")));
ProcessReturnStepCmd(hprc, hthd, lpdbb);
break;
#ifndef KERNEL
case dmfNonLocalGoto:
DPRINT(5,(_T("NonLocalGoto\r\n")));
//Turn off fiber debugging
hprc->pFbrCntx = NULL;
ProcessNonLocalGoto(hprc, hthd, lpdbb);
break;
#endif
case dmfThreadStatus:
DPRINT(5,(_T("ThreadStatus\r\n")));
Reply( ProcessThreadStatCmd(hprc, hthd, lpdbb), LpDmMsg, lpdbb->hpid);
break;
case dmfProcessStatus:
DPRINT(5,(_T("ProcessStatus\r\n")));
if (hprc) {
LpDmMsg->xosdRet = xosdNone;
Reply( ProcessProcStatCmd(hprc, hthd, lpdbb), LpDmMsg, lpdbb->hpid);
} else {
LpDmMsg->xosdRet = xosdBadProcess;
Reply( 0, LpDmMsg, lpdbb->hpid);
}
break;
case dmfGetTimeStamp:
DPRINT(5,(_T("ProcessGetTimeStamp\r\n")));
Reply (ProcessGetTimeStamp (hprc, hthd, lpdbb), LpDmMsg, lpdbb->hpid);
break;
case dmfNewSymbolsLoaded:
#ifdef KERNEL
GetKernelSymbolAddresses();
#endif
Reply( 0, LpDmMsg, lpdbb->hpid);
break;
default:
DPRINT(5, (_T("DM: Unknown dmf == %d\n"), dmf));
assert(FALSE);
break;
}
return;
} /* DMFunc() */
#undef DMAPILOCK
#undef DMAPIRELEASE
#ifndef KERNEL
BOOL
StartDmPollThread(
void
)
/*++
Routine Description:
This creates the DM poll thread.
Arguments:
none
Return Value:
TRUE if the thread was successfully created or already
existed.
--*/
{
DWORD tid;
if (hDmPollThread) {
return TRUE;
}
hDmPollThread = CreateThread(0,0,CallDmPoll,0,0,&tid);
return hDmPollThread != 0;
}
BOOL
StartCrashPollThread(
void
)
/*++
Routine Description:
This creates the DM poll thread for a crash dump file.
Arguments:
none
Return Value:
TRUE if the thread was successfully created or already existed.
--*/
{
DWORD tid;
if (hDmPollThread) {
return TRUE;
}
hDmPollThread = CreateThread(0,0,(LPTHREAD_START_ROUTINE)CrashDumpThread,0,0,&tid);
return hDmPollThread != 0;
}
VOID
CrashDumpThread(
LPVOID lpv
)
{
HPRCX hprc;
HTHDX hthd;
HTHDX hthdNew;
LPBYTE lpbPacket;
WORD cbPacket;
PCRASH_MODULE CrashModule;
DEBUG_EVENT64 de;
DWORD i;
CHAR buf[32];
LPTSTR lpProgName = (LPTSTR)lpv;
DWORD DumpVer = CrashDumpHeader->MajorVersion;
CRASHDUMP_VERSION_INFO VersionInfo;
CrashDump = TRUE;
// fix up version bugs
DmpDetectVersionParameters( &VersionInfo );
// simulate a create process debug event
CrashModule = (PCRASH_MODULE)((PUCHAR)CrashDumpHeader+CrashDumpHeader->ModuleOffset);
de.dwDebugEventCode = CREATE_PROCESS_DEBUG_EVENT;
de.dwProcessId = 1;
de.dwThreadId = 1;
de.u.CreateProcessInfo.hFile = NULL;
de.u.CreateProcessInfo.hProcess = NULL;
de.u.CreateProcessInfo.hThread = NULL;
de.u.CreateProcessInfo.lpBaseOfImage = CrashModule->BaseOfImage;
de.u.CreateProcessInfo.dwDebugInfoFileOffset = 0;
de.u.CreateProcessInfo.nDebugInfoSize = 0;
de.u.CreateProcessInfo.lpStartAddress = 0;
de.u.CreateProcessInfo.lpThreadLocalBase = 0;
if (!CrashModule->ImageName[0]) {
de.u.CreateProcessInfo.lpImageName = SEPtrTo64("mod0.dll");
} else {
de.u.CreateProcessInfo.lpImageName = SEPtrTo64(CrashModule->ImageName);
}
_ftcscpy( nameBuffer, (PVOID)de.u.CreateProcessInfo.lpImageName);
de.u.CreateProcessInfo.fUnicode = FALSE;
ProcessDebugEvent(&de);
WaitForSingleObject( hEventCreateProcess, INFINITE );
// wait for the shell to say go to the thread
WaitForSingleObject( hEventContinue, INFINITE );
// mark the process as 'being connected' so that the continue debug
// events that are received from the shell are ignored
hprc = HPRCFromPID(1);
hthd = hprc->hthdChild;
hprc->pstate |= ps_connect;
// Get rid of the expected breakpoint
ConsumeAllProcessEvents(hprc, FALSE);
if (DumpVer >= 4) {
DmpGetThread( 0, &hthd->CrashThread );
hthd->offTeb = hthd->CrashThread.Teb;
} else {
ZeroMemory(&hthd->CrashThread, sizeof(CRASH_THREAD));
hthd->CrashThread.ThreadId = hthd->tid;
}
// generates the mod load events
for (i=1; i<CrashDumpHeader->ModuleCount; i++) {
// drwtsn32 puts a bogus value in the name length field
// when the string is empty:
int namelen = CrashModule->ImageName[0] ? CrashModule->ImageNameLength : 1;
CrashModule = (PCRASH_MODULE) ( (PUCHAR)CrashModule +
sizeof(CRASH_MODULE) +
namelen );
de.dwDebugEventCode = LOAD_DLL_DEBUG_EVENT;
de.dwProcessId = 1;
de.dwThreadId = 1;
de.u.LoadDll.hFile = NULL;
// Sign extend.....
if (sizeof(CrashModule->BaseOfImage) == sizeof(DWORD64)) {
de.u.LoadDll.lpBaseOfDll = CrashModule->BaseOfImage;
} else {
de.u.LoadDll.lpBaseOfDll = SE32To64(CrashModule->BaseOfImage);
}
if (!CrashModule->ImageName[0]) {
sprintf( buf, "mod%d.dll", i );
de.u.LoadDll.lpImageName = SEPtrTo64(buf);
} else {
de.u.LoadDll.lpImageName = SEPtrTo64(CrashModule->ImageName);
}
de.u.LoadDll.fUnicode = FALSE;
de.u.LoadDll.nDebugInfoSize = 0;
de.u.LoadDll.dwDebugInfoFileOffset = 0;
if (LoadDll(&de,hthd,&cbPacket,&lpbPacket, FALSE) && (cbPacket != 0)) {
NotifyEM(&de, hthd, cbPacket, (ULONG64)lpbPacket);
}
}
// generate a load complete debug event
ResetEvent( hEventContinue );
de.dwProcessId = hprc->pid;
de.dwThreadId = hprc->hthdChild->tid;
de.dwDebugEventCode = LOAD_COMPLETE_DEBUG_EVENT;
NotifyEM( &de, hthd, 0, 0L);
WaitForSingleObject( hEventContinue, INFINITE );
// create all of the threads
for (i=1; i<CrashDumpHeader->ThreadCount; i++) {
// generate a thread create event
ResetEvent( hprc->hEventCreateThread );
ResetEvent( hEventContinue );
de.dwDebugEventCode = CREATE_THREAD_DEBUG_EVENT;
de.dwProcessId = hprc->pid;
de.dwThreadId = i + 1;
de.u.CreateThread.hThread = (HANDLE) (i + 1);
de.u.CreateThread.lpThreadLocalBase = 0;
de.u.CreateThread.lpStartAddress = 0;
// Take critical section here so that it is still
// held after leaving ProcessDebugEvent
EnterCriticalSection(&csProcessDebugEvent);
ProcessDebugEvent(&de);
hthdNew = HTHDXFromPIDTID(1,(i+1));
DbgGetThreadContext( hthdNew, &hthdNew->context );
if (DumpVer >= 4) {
DmpGetThread( i, &hthdNew->CrashThread );
hthdNew->offTeb = hthdNew->CrashThread.Teb;
} else {
ZeroMemory(&hthdNew->CrashThread, sizeof(CRASH_THREAD));
hthdNew->CrashThread.ThreadId = hthdNew->tid;
}
LeaveCriticalSection(&csProcessDebugEvent);
WaitForSingleObject( hprc->hEventCreateThread, INFINITE );
// wait for the shell to continue the new thread
WaitForSingleObject( hEventContinue, INFINITE );
}
// Get the debug event for the crash
if (64 == VersionInfo.PointerSize) {
de = *(LPDEBUG_EVENT64)((PUCHAR)CrashDumpHeader+CrashDumpHeader->DebugEventOffset);
// convert the thread and process ids into the internal version...
de.dwProcessId = 1;
for (hthd = hprc->hthdChild; hthd; hthd = hthd->nextSibling) {
if (de.dwThreadId == hthd->CrashThread.ThreadId) {
de.dwThreadId = hthd->tid;
break;
}
}
} else {
// if this is an old crashdump file, try to find the crashed thread
de.dwDebugEventCode = EXCEPTION_DEBUG_EVENT;
// BUGBUG kentf no support yet for 64 bit crashdump
ExceptionRecord32To64((PEXCEPTION_RECORD32)CrashException, &de.u.Exception.ExceptionRecord);
for (hthd = hprc->hthdChild; hthd; hthd = hthd->nextSibling) {
if (PC(hthd) == de.u.Exception.ExceptionRecord.ExceptionAddress) {
de.dwThreadId = hthd->tid;
}
}
}
ProcessDebugEvent( &de );
while (!fDmPollQuit) {
// Handle kill commands
if (KillQueue) {
CompleteTerminateProcessCmd();
}
if (ReloadStruct.Flag) {
ReloadUsermodeModules(ReloadStruct.Hthd, ReloadStruct.String);
free((PVOID)ReloadStruct.String);
ReloadStruct.Flag = 0;
}
Sleep( 500 );
}
}
DWORD WINAPI CallDmPoll(LPVOID lpv)
/*++
Routine Description:
This is the debug event loop.
This routine creates or attaches to child process, and monitors them for debug events.
It serializes the dispatching of events from multiple process, and continues the events when the worker functions have finished processing the events.
Arguments:
lpv - Supplies an argument provided by CreateThread.
--*/
{
DEBUG_EVENT64 de;
PROCESS_INFORMATION pi;
int nprocs = 0;
UINT ErrMode;
Unreferenced( lpv );
DPRINT(5,(_T("CallDmPoll\r\n")));
// Crank up priority to improve performance, and improve our
// chances of winning races with the debuggee.
SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_HIGHEST);
fDmPollQuit = FALSE;
while (!fDmPollQuit) {
// Handle kill commands
if (KillQueue) {
CompleteTerminateProcessCmd();
goto doContinues;
}
// Handle spawn commands
if (SpawnStruct.fSpawn) {
SpawnStruct.fSpawn = FALSE;
ErrMode = SetErrorMode( 0 );
SpawnStruct.fReturn = CreateProcess( SpawnStruct.szAppName, SpawnStruct.szArgs, NULL, NULL, SpawnStruct.fInheritHandles, SpawnStruct.fdwCreate, NULL, SpawnStruct.pszCurrentDirectory, &SpawnStruct.si, &pi);
SetErrorMode( ErrMode );
if (SpawnStruct.fReturn == 0) {
SpawnStruct.dwError = GetLastError();
} else {
SpawnStruct.dwError = 0;
CloseHandle(pi.hThread);
CloseHandle(pi.hProcess);
Close3Handles(rgh);
}
SetEvent(SpawnStruct.hEventApiDone);
}
// Handle attach commands
if (DebugActiveStruct.fAttach) {
if (fDisconnected && DebugActiveStruct.dwProcessId == 0) {
SetEvent( hEventRemoteQuit );
SetEvent( DebugActiveStruct.hEventReady );
DebugActiveStruct.fReturn = 1;
DebugActiveStruct.dwError = 0;
} else {
DebugActiveStruct.fAttach = FALSE;
SetDebugPrivilege ();
DebugActiveStruct.fReturn = DebugActiveProcess(DebugActiveStruct.dwProcessId);
if (DebugActiveStruct.fReturn == 0) {
DebugActiveStruct.dwError = GetLastError();
} else {
DebugActiveStruct.dwError = 0;
}
}
SetEvent(DebugActiveStruct.hEventApiDone);
}
if (WtStruct.fWt) {
WtStruct.fWt = FALSE;
switch(WtStruct.dwType)
{
case IG_WATCH_TIME_STOP:
WtStruct.hthd->wtmode = 2;
break;
case IG_WATCH_TIME_RECALL:
break;
case IG_WATCH_TIME_PROCS:
break;
}
}
if (ReloadStruct.Flag) {
ReloadUsermodeModules(ReloadStruct.Hthd, ReloadStruct.String);
free((PVOID)ReloadStruct.String);
ReloadStruct.Flag = 0;
}
if (DMWaitForDebugEvent(&de, (DWORD) WAITFORDEBUG_MS)) {
#ifndef KERNEL
{
PDETOSAVE pDeToSave = GetNextFreeDebugEvent();
pDeToSave->de = de;
InsertTailList(&DmListOfPendingDebugEvents, &pDeToSave->List);
}
#endif // !KERNEL
if ( de.dwDebugEventCode == CREATE_PROCESS_DEBUG_EVENT ) {
assert(HPRCFromPID(de.dwProcessId) == NULL);
// post a dummy message to the app so it will unblock (W95)
if (IsChicago() && DebugActiveStruct.fReturn) {
PostThreadMessage (de.dwThreadId, WM_NULL, 0, 0);
}
if (nprocs == 0) {
ResetEvent(hEventNoDebuggee);
}
++nprocs;
} else if ( de.dwDebugEventCode == EXIT_PROCESS_DEBUG_EVENT) {
--nprocs;
}
if (fDisconnected) {
if (de.dwDebugEventCode == LOAD_DLL_DEBUG_EVENT ||
de.dwDebugEventCode == UNLOAD_DLL_DEBUG_EVENT ||
de.dwDebugEventCode == CREATE_THREAD_DEBUG_EVENT ||
de.dwDebugEventCode == EXIT_THREAD_DEBUG_EVENT ) {
// we can process these debug events very carefully
// while disconnected from the shell. the only requirement
// is that the dm doesn't call NotifyEM while disconnected.
} else
#if 0
if (de.dwDebugEventCode == EXCEPTION_DEBUG_EVENT) {
// if the exception is to be ignored, we can handle it here.
// if not, we have to wait for a reconnect.
} else
#endif
{
WaitForSingleObject( hEventRemoteQuit, INFINITE );
ResetEvent( hEventRemoteQuit );
fDisconnected = FALSE;
// this is a remote session reconnecting
ReConnectDebugger( &de, FALSE );
}
}
ProcessDebugEvent(&de);
} else if (WaitForSingleObject( hEventRemoteQuit, 0 ) == WAIT_OBJECT_0) {
// this is a remote session reconnecting
ResetEvent( hEventRemoteQuit );
ReConnectDebugger( NULL, FALSE );
} else if (nWaitingForLdrBreakpoint) {
// look for processes that are looking for a loader bp.
// See how long it has been since they got an event.
HPRCX hprc;
BOOL Exited = FALSE;
EnterCriticalSection(&csThreadProcList);
for (hprc = prcList->next; hprc; hprc = hprc->next) {
if (hprc->pstate & ps_preStart) {
if (++hprc->cLdrBPWait > LDRBP_MAXTICKS) {
// Signal a timeout for this one.
// just jump out of this loop - if
// another one is going to time out,
// it can do it on the next pass.
break;
// See if a pending attach has failed because the target process
// exited before the debugger got hooked up.
} else if (WaitForSingleObject(DebugActiveStruct.hEventReady, 0) != 0
&& hprc->pid == DebugActiveStruct.dwProcessId
&& DebugActiveStruct.hProcess != INVALID_HANDLE_VALUE
&& WaitForSingleObject(DebugActiveStruct.hProcess, 0) == 0) {
// An attached process exited before it really got attached
Exited = TRUE;
break;
}
}
}
LeaveCriticalSection(&csThreadProcList);
if (hprc) {
HandleDebugActiveDeadlock(hprc, Exited);
}
} else if (nprocs == 0) {
Sleep(WAITFORDEBUG_MS);
}
doContinues:
if (DequeueAllEvents(TRUE, FALSE) && nprocs <= 0) {
SetEvent(hEventNoDebuggee);
}
}
DEBUG_PRINT(_T("CallDmPoll Exit\r\n"));
return 0;
} /* CallDmPoll() */
BOOL SetDebugPrivilege(void)
/*++
Routine Description:
Enables SeDebugPrivilege if possible.
Return Value:
TRUE on success, FALSE on failure
--*/
{
HANDLE hToken;
LUID DebugValue;
TOKEN_PRIVILEGES tkp;
BOOL rVal = TRUE;
if (!OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &hToken)) {
return FALSE;
}
if (!LookupPrivilegeValue(NULL, SE_DEBUG_NAME, &DebugValue)) {
rVal = FALSE;
} else {
tkp.PrivilegeCount = 1;
tkp.Privileges[0].Luid = DebugValue;
tkp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
AdjustTokenPrivileges(hToken, FALSE, &tkp, sizeof(TOKEN_PRIVILEGES), NULL, NULL);
if (GetLastError() != ERROR_SUCCESS) {
rVal = FALSE;
}
}
CloseHandle(hToken);
return rVal;
}
#endif // !KERNEL
/* Dll Version */
#ifdef KERNEL
#ifdef DEBUGVER
DEBUG_VERSION('D','M',"NT Kernel Debugger Monitor")
#else
RELEASE_VERSION('D','M',"NT Kernel Debugger Monitor")
#endif
#else // KERNEL
#ifdef DEBUGVER
DEBUG_VERSION('D','M',"WIN32 Debugger Monitor")
#else
RELEASE_VERSION('D','M',"WIN32 Debugger Monitor")
#endif
#endif // KERNEL
DBGVERSIONCHECK()
int
WINAPI
DllMain(
HANDLE hModule,
DWORD dwReason,
DWORD dwReserved
)
/*++
Routine Description:
Entry point called by the loader during DLL initialization
and deinitialization. This creates and destroys some per-
instance objects.
Arguments:
hModule - Supplies base address of dll
dwReason - Supplies flags describing why we are here
dwReserved - depends on dwReason.
Return Value:
TRUE
--*/
{
Unreferenced(hModule);
Unreferenced(dwReserved);
#ifndef KERNEL
InitializeListHead(&DmListOfPendingDebugEvents);
InitializeListHead(&DmFreeListOfDebugEvents);
#endif // KERNEL
switch (dwReason)
{
case DLL_THREAD_ATTACH:
break;
case DLL_THREAD_DETACH:
break;
case DLL_PROCESS_DETACH:
CloseHandle(SpawnStruct.hEventApiDone);
CloseHandle(hEventCreateProcess);
CloseHandle(hEventRemoteQuit);
CloseHandle(hEventNoDebuggee);
CloseHandle(hEventContinue);
DeleteCriticalSection(&csProcessDebugEvent);
DeleteCriticalSection(&csThreadProcList);
DeleteCriticalSection(&csEventList);
DeleteCriticalSection(&csWalk);
#if !defined(KERNEL)
CloseHandle(DebugActiveStruct.hEventApiDone);
CloseHandle(DebugActiveStruct.hEventReady);
DeleteCriticalSection(&csKillQueue);
#endif
break;
case DLL_PROCESS_ATTACH:
InitializeCriticalSection(&csProcessDebugEvent);
InitializeCriticalSection(&csThreadProcList);
InitializeCriticalSection(&csEventList);
InitializeCriticalSection(&csWalk);
hEventCreateProcess = CreateEvent(NULL, TRUE, FALSE, NULL);
hEventRemoteQuit = CreateEvent(NULL, TRUE, FALSE, NULL);
hEventContinue = CreateEvent(NULL, TRUE, FALSE, NULL);
hEventNoDebuggee = CreateEvent(NULL, TRUE, FALSE, NULL);
SpawnStruct.hEventApiDone = CreateEvent(NULL, TRUE, FALSE, NULL);
#if defined(KERNEL)
InitializeCriticalSection(&csApiInterlock);
InitializeCriticalSection(&csSynchronizeTargetInterlock);
#endif // KERNEL
#if !defined(KERNEL)
InitializeCriticalSection(&csKillQueue);
DebugActiveStruct.hEventApiDone = CreateEvent(NULL, TRUE, TRUE, NULL);
DebugActiveStruct.hEventReady = CreateEvent(NULL, TRUE, TRUE, NULL);
/*
* These parameters are from SCOTTLU
*/
SetProcessShutdownParameters(0x3ff, 0);
// get helpful info
hInstance = hModule;
GetSystemInfo(&SystemInfo);
OsVersionInfo.dwOSVersionInfoSize = sizeof(OsVersionInfo);
GetVersionEx(&OsVersionInfo);
#if defined (TARGET_i386)
FChicago = ((GetVersion() & 0x80000000) && ((GetVersion() & 0xff)>3));
#endif // TARGET_i386
#endif // !KERNEL
break;
}
return TRUE;
}
BOOL PASCAL IsChicago(VOID)
/*++
Routine Description:
This routine allows the caller to determine if the DM is running on the Win95 family
Return Value:
TRUE if running on Win95 and FALSE othewise.
--*/
{
return FChicago;
}
BOOL
PASCAL
DmDllInit(
LPDBF lpb
)
/*++
Routine Description:
This routine allows the shell (debugger or remote stub)
to provide a service callback vector to the DM.
Arguments:
lpb - Supplies an array of functions for callbacks
Return Value:
TRUE if successfully initialized and FALSE othewise.
--*/
{
lpdbf = lpb;
return TRUE;
} /* DmDllInit() */
#ifdef KERNEL
void
ParseDmParams(
LPTSTR p
)
{
DWORD i;
CHAR szPath[MAX_PATH];
CHAR szStr[_MAX_PATH];
LPTSTR lpPathNext;
LPTSTR lpsz1;
LPTSTR lpsz2;
LPTSTR lpsz3;
for (i=0; i<MAX_MODULEALIAS; i++) {
if (!ModuleAlias[i].Special) {
ZeroMemory( &ModuleAlias[i], sizeof(MODULEALIAS) );
}
}
do {
p = _ftcstok( p, _T("=") );
if (p) {
for (i=0; i<MAXKDOPTIONS; i++) {
if (_ftcsicmp(KdOptions[i].keyword,p)==0) {
break;
}
}
if (i < MAXKDOPTIONS) {
p = _ftcstok( NULL, _T(" ") );
if (p) {
switch (KdOptions[i].typ) {
case KDT_DWORD:
KdOptions[i].value = atol( p );
break;
case KDT_STRING:
KdOptions[i].value = (UINT_PTR)_ftcsdup( p );
break;
}
p = p + (_ftcslen(p) + 1);
}
} else {
if (_ftcsicmp( p, _T("alias") ) == 0) {
p = _ftcstok( NULL, _T("#") );
if (p) {
for (i=0; i<MAX_MODULEALIAS; i++) {
if (ModuleAlias[i].ModuleName[0] == 0) {
break;
}
}
if (i < MAX_MODULEALIAS) {
_ftcscpy( ModuleAlias[i].ModuleName, p );
p = _ftcstok( NULL, _T(" ") );
if (p) {
_ftcscpy( ModuleAlias[i].Alias, p );
p = p + (_ftcslen(p) + 1);
}
} else {
p = _ftcstok( NULL, _T(" ") );
}
}
} else {
p = _ftcstok( NULL, _T(" ") );
}
}
}
} while(p && *p);
if (KdOptions[KDO_VERBOSE].value > 1) {
nVerbose = (BOOL)KdOptions[KDO_VERBOSE].value;
}
else {
nVerbose = MIN_VERBOSITY_LEVEL;
}
szPath[0] = 0;
lpPathNext = _ftcstok((LPTSTR)KdOptions[KDO_SYMBOLPATH].value, _T(";"));
while (lpPathNext) {
lpsz1 = szStr;
while ((lpsz2 = _ftcschr(lpPathNext, _T('%'))) != NULL) {
_ftcsncpy(lpsz1, lpPathNext, (size_t)(lpsz2 - lpPathNext));
lpsz1 += lpsz2 - lpPathNext;
lpsz2++;
lpPathNext = _ftcschr(lpsz2, _T('%'));
if (lpPathNext != NULL) {
*lpPathNext++ = 0;
lpsz3 = getenv(lpsz2);
if (lpsz3 != NULL) {
_ftcscpy(lpsz1, lpsz3);
lpsz1 += _ftcslen(lpsz3);
}
} else {
lpPathNext = _T("");
}
}
_ftcscpy(lpsz1, lpPathNext);
_ftcscat( szPath, szStr );
_ftcscat( szPath, _T(";") );
lpPathNext = _ftcstok(NULL, _T(";"));
}
if ( szPath[0] != 0 ) {
if (szPath[_ftcslen(szPath)-1] == _T(';')) {
szPath[_ftcslen(szPath)-1] = _T('\0');
}
_ftcscpy( (LPTSTR)KdOptions[KDO_SYMBOLPATH].value, szPath );
}
}
#endif // KERNEL
VOID ProcessRemoteQuit(VOID)
{
HPRCX hprc;
PBREAKPOINT pbp;
PBREAKPOINT pbpT;
EnterCriticalSection(&csThreadProcList);
for (hprc=prcList->next; hprc; hprc=hprc->next) {
for (pbp = BPNextHprcPbp(hprc, NULL); pbp; pbp = pbpT) {
pbpT = BPNextHprcPbp(hprc, pbp);
RemoveBP(pbp);
}
}
LeaveCriticalSection(&csThreadProcList);
fDisconnected = TRUE;
ResetEvent( hEventRemoteQuit );
}
XOSD FAR PASCAL
DMInit(
DMTLFUNCTYPE lpfnTl,
LPTSTR lpch
)
/*++
Routine Description:
This is the entry point called by the TL to initialize the
connection from DM to TL.
Arguments:
lpfnTl - Supplies entry point to TL
lpch - Supplies command line arg list
Return Value:
XOSD value: xosdNone for success, other values reflect reason
for failure to initialize properly.
--*/
{
int i, n;
XOSD xosd;
DEBUG_PRINT(_T("DMInit\r\n"));
if (lpfnTl != NULL) {
/*
** Parse out anything interesting from the command line args
*/
while (*lpch) {
while (isspace(*lpch)) {
lpch++;
}
if (*lpch != _T('/') && *lpch != _T('-')) {
break;
}
lpch++;
switch (*lpch++) {
case 0: // don't skip over end of string
--lpch;
default: // assert, continue is ok.
assert(FALSE);
break;
case 'v':
case 'V':
while (isspace(*lpch)) {
lpch++;
}
nVerbose = atoi(lpch);
while (isdigit(*lpch)) {
lpch++;
}
break;
case 'r':
case 'R':
FDMRemote = TRUE;
break;
case 'd':
case 'D':
FUseOutputDebugString = TRUE;
break;
}
}
#ifdef KERNEL
ParseDmParams( lpch );
#endif
/* Define a false single step event */
falseSSEvent.dwDebugEventCode = EXCEPTION_DEBUG_EVENT;
falseSSEvent.u.Exception.ExceptionRecord.ExceptionCode
= EXCEPTION_SINGLE_STEP;
falseBPEvent.dwDebugEventCode = BREAKPOINT_DEBUG_EVENT;
falseBPEvent.u.Exception.ExceptionRecord.ExceptionCode
= EXCEPTION_BREAKPOINT;
FuncExitEvent.dwDebugEventCode = FUNC_EXIT_EVENT;
FuncExitEvent.u.Exception.ExceptionRecord.ExceptionCode
= EXCEPTION_SINGLE_STEP;
/* Define the standard notification method */
EMNotifyMethod.notifyFunction = ConsumeThreadEventsAndNotifyEM;
EMNotifyMethod.lparam = 0;
SearchPathString[0] = _T('\0');
SearchPathSet = FALSE;
InitEventQueue();
// initialize data breakpoint handler
ExprBPInitialize();
SetDebugErrorLevel(SLE_WARNING);
/*
** Save the pointer to the Transport layer entry function
*/
DmTlFunc = lpfnTl;
/*
** Try and connect up to the other side of the link
*/
DmTlFunc( tlfSetBuffer, NULL, sizeof(abEMReplyBuf), (LPARAM) abEMReplyBuf );
if ((xosd = DmTlFunc( tlfConnect, NULL, 0, 0)) != xosdNone ) {
return(xosd);
}
DPRINT(10, (_T("DM & TL are now connected\n")));
} else {
DmTlFunc( tlfDisconnect, NULL, 0, 0);
DmTlFunc( tlfSetBuffer, NULL, 0, 0);
FDMRemote = FALSE;
DmTlFunc = (DMTLFUNCTYPE) NULL;
}
return xosdNone;
} /* DmInit() */
#ifndef KERNEL
VOID Cleanup(VOID)
/*++
Routine Description:
Cleanup of DM, prepare for exit.
--*/
{
HTHDX pht, phtt;
HPRCX php, phpt;
BREAKPOINT *bp, *bpt;
int iDll;
/* Free all threads and close their handles */
for (pht = thdList->nextGlobalThreadThisProbablyIsntTheOneYouWantedToUse; pht; pht = phtt) {
phtt = pht->nextGlobalThreadThisProbablyIsntTheOneYouWantedToUse;
if (pht->rwHand != (HANDLE)INVALID) {
CloseHandle(pht->rwHand);
}
MHFree(pht);
}
thdList->nextGlobalThreadThisProbablyIsntTheOneYouWantedToUse = NULL;
/* Free all processes and close their handles */
for(php = prcList->next; php; php = phpt) {
phpt = php->next;
RemoveExceptionList(php);
// Free any fibers that may be left
RemoveFiberList(php);
for (iDll = 0; iDll < php->cDllList; iDll++) {
DestroyDllLoadItem(&php->rgDllList[iDll]);
}
MHFree(php->rgDllList);
if (php->rwHand != (HANDLE)INVALID) {
CloseHandle(php->rwHand);
}
CloseHandle(php->hExitEvent);
CloseHandle(php->hEventCreateThread);
MHFree(php);
}
prcList->next = NULL;
/* Free all breakpoints */
for(bp = bpList->next; bp; bp = bpt) {
bpt = bp->next;
MHFree(bp);
}
bpList->next = NULL;
// Ask the disassembler to clean itself up.
CleanupDisassembler( );
if (hDmPollThread) {
fDmPollQuit = TRUE;
WaitForSingleObject(hDmPollThread, INFINITE);
hDmPollThread = 0;
}
while (!IsListEmpty(&DmListOfPendingDebugEvents)) {
PLIST_ENTRY p = RemoveHeadList(&DmListOfPendingDebugEvents);
MHFree(p);
}
while (!IsListEmpty(&DmFreeListOfDebugEvents)) {
PLIST_ENTRY p = RemoveHeadList(&DmFreeListOfDebugEvents);
MHFree(p);
}
}
#endif // !KERNEL
BOOL
WINAPIV
DMPrintShellMsg(
LPTSTR szFormat,
...
)
/*++
Routine Description:
This function prints a string on the shell's
command window.
Arguments:
szFormat - Supplies format string for sprintf
... - Supplies variable argument list
Return Value:
TRUE -> all is ok and the string was printed
FALSE -> something's hosed and no string printed
--*/
{
TCHAR buf[512];
DWORD bufLen;
va_list marker;
LPINFOAVAIL lpinf;
LPRTP lprtp = NULL;
BOOL rVal = TRUE;
va_start( marker, szFormat );
bufLen = _vsnprintf(buf, sizeof(buf), szFormat, marker );
va_end( marker);
if (bufLen == -1) {
buf[sizeof(buf) - 1] = _T('\0');
}
__try {
if (!DmTlFunc || (HPID)INVALID == hpidRoot) {
// If we can't print it to the shell, hopefully to an
// attached debugger.
OutputDebugString(buf);
} else {
bufLen = _ftcslen(buf) + 1;
lprtp = (LPRTP) MHAlloc( FIELD_OFFSET(RTP, rgbVar)+sizeof(INFOAVAIL)+bufLen );
lpinf = (LPINFOAVAIL)(lprtp->rgbVar);
lprtp->dbc = dbcInfoAvail;
lprtp->hpid = hpidRoot;
lprtp->htid = NULL;
lprtp->cb = (int)bufLen;
lpinf->fReply = FALSE;
lpinf->fUniCode = FALSE;
memcpy( lpinf->buffer, buf, bufLen );
DmTlFunc( tlfDebugPacket,
lprtp->hpid,
(FIELD_OFFSET(RTP, rgbVar)+sizeof(INFOAVAIL)+bufLen),
(LPARAM) lprtp
);
}
} __except(EXCEPTION_EXECUTE_HANDLER) {
rVal = FALSE;
}
if (lprtp) {
MHFree( lprtp );
}
return rVal;
}
VOID WINAPIV DebugPrint(LPTSTR szFormat, ...)
{
TCHAR rgchDebug[1024];
va_list marker;
int n;
va_start( marker, szFormat );
n = _vsnprintf(rgchDebug, _tsizeof(rgchDebug), szFormat, marker );
va_end( marker);
if (n == -1) {
rgchDebug[_tsizeof(rgchDebug)-1] = 0;
}
OutputDebugString( rgchDebug );
return;
} /* DebugPrint() */
int
pCharMode(
LPTSTR szAppName,
PIMAGETYPE pImageType
)
/*++
Routine Description:
This routine is used to determine the type of exe which we are going
to be debugging. This is decided by looking for exe headers and making
decisions based on the information in the exe headers.
Arguments:
szAppName - Supplies the path to the debugger exe
pImageType - Returns the type of the image
Return Value:
System_Invalid - could not find the exe file
System_GUI - GUI application
System_Console - console application
--*/
{
IMAGE_DOS_HEADER dosHdr;
IMAGE_OS2_HEADER os2Hdr;
IMAGE_NT_HEADERS ntHdr;
DWORD cb;
HANDLE hFile;
int ret;
BOOL GotIt;
_tcscpy(nameBuffer, szAppName);
// don't use OpenFile as it fails paths >127 bytes long
hFile = CreateFile( szAppName, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL, NULL );
if (hFile == (HANDLE)-1) {
/*
* Could not open file!
*/
DEBUG_PRINT_2(_T("CreateFile(%s) --> %u\r\n"), szAppName, GetLastError());
return System_Invalid;
}
/*
* Try and read an MZ Header. If you can't then it can not possibly
* be a legal exe file. (Not strictly true but we will ignore really
* short com files since they are unintersting).
*/
SetFilePointer(hFile, 0, NULL, FILE_BEGIN);
if ((!ReadFile(hFile, &dosHdr, sizeof(dosHdr), &cb, NULL)) ||
(cb != sizeof(dosHdr))) {
if (_ftcsicmp(&szAppName[_ftcslen(szAppName) - 4], _T(".COM")) == 0) {
*pImageType = Image_16;
} else {
DPRINT(1, (_T("dosHdr problem.\n")));
*pImageType = Image_Unknown;
}
CloseHandle(hFile);
return System_GUI;
}
/*
* Verify the MZ header.
* NOTENOTE Messup the case of no MZ header.
*/
if (dosHdr.e_magic != IMAGE_DOS_SIGNATURE) {
/*
* We did not start with the MZ signature. If the extension
* is .COM then it is a COM file.
*/
if (_ftcsicmp(&szAppName[_ftcslen(szAppName) - 4], _T(".COM")) == 0) {
*pImageType = Image_16;
} else {
DPRINT(1, (_T("MAGIC problem(MZ).\n")));
*pImageType = Image_Unknown;
}
CloseHandle(hFile);
#ifndef KERNEL
if (DmpInitialize( szAppName, &CrashContext, &CrashException, &CrashDumpHeader )) {
// Verify the architecture
if (
#if defined(TARGET_i386)
CrashDumpHeader->MachineImageType != IMAGE_FILE_MACHINE_I386
#elif defined(TARGET_ALPHA)
CrashDumpHeader->MachineImageType != IMAGE_FILE_MACHINE_ALPHA
#elif defined(TARGET_AXP64)
CrashDumpHeader->MachineImageType != IMAGE_FILE_MACHINE_AXP64
#elif defined(TARGET_IA64)
CrashDumpHeader->MachineImageType != IMAGE_FILE_MACHINE_IA64
#else
#error( "unknown target machine" );
#endif
) {
return System_DmpWrongPlatform;
}
// User mode validation
if (DmpIsItAUserModeFile()) {
if (!DmpUserModeTestHeader()) {
return System_DmpInvalid;
}
}
*pImageType = Image_Dump;
}
#endif // !KERNEL
return System_Console;
}
if ( dosHdr.e_lfanew == 0 ) {
/*
* Straight DOS exe.
*/
DPRINT(1, (_T("[DOS image].\n")));
*pImageType = Image_16;
CloseHandle(hFile);
return System_Console;
}
/*
* Now look at the next EXE header (either NE or PE)
*/
SetFilePointer(hFile, dosHdr.e_lfanew, NULL, FILE_BEGIN);
GotIt = FALSE;
ret = System_GUI;
/*
* See if this is a Win16 program
*/
if (ReadFile(hFile, &os2Hdr, sizeof(os2Hdr), &cb, NULL) &&
(cb == sizeof(os2Hdr))) {
if ( os2Hdr.ne_magic == IMAGE_OS2_SIGNATURE ) {
/*
* Win16 program (may be an OS/2 exe also)
*/
DPRINT(1, (_T("[Win16 image].\n")));
*pImageType = Image_16;
GotIt = TRUE;
} else if ( os2Hdr.ne_magic == IMAGE_OS2_SIGNATURE_LE ) {
/*
* OS2 program - Not supported
*/
DPRINT(1, (_T("[OS/2 image].\n")));
*pImageType = Image_Unknown;
GotIt = TRUE;
}
}
/*
* If the above failed, see if it is an NT program
*/
if ( !GotIt ) {
SetFilePointer(hFile, dosHdr.e_lfanew, NULL, FILE_BEGIN);
if (ReadFile(hFile, &ntHdr, sizeof(ntHdr), &cb, NULL) &&
(cb == sizeof(ntHdr)) &&
(ntHdr.Signature == IMAGE_NT_SIGNATURE)) {
/*
* All CUI (Character user interface) subsystems
* have the lowermost bit set.
*/
DPRINT(1, ((ntHdr.OptionalHeader.Subsystem & 1) ?
_T("[*Character mode app*]\n") : _T("[*Windows mode app*]\n")));
ret = ((ntHdr.OptionalHeader.Subsystem & 1)) ?
System_Console : System_GUI;
*pImageType = Image_32;
} else {
DWORD FileSize;
FileSize = SetFilePointer(hFile, 0, NULL, FILE_END);
if ( (DWORD)dosHdr.e_lfanew > FileSize ) {
// Bogus e_lfanew, assume DOS
DPRINT(1, (_T("[DOS image assumed].\n")));
*pImageType = Image_16;
ret = System_Console;
} else {
// Not an NT image.
DPRINT(1, (_T("MAGIC problem(PE).\n")));
*pImageType = Image_Unknown;
}
}
}
CloseHandle(hFile);
return ret;
} /* pCharMode() */
VOID
ReConnectDebugger(
DEBUG_EVENT64 *lpde,
BOOL fNoDllLoad
)
/*++
Routine Description:
This function handles the case where the dm/tl is re-connected to a debugger.
This function must re-instate the debugger to the correct state that existed before the disconnect action.
(wesw) 11-3-93
--*/
{
DWORD i;
DEBUG_EVENT64 de;
HPRCX hprc;
HTHDX hthd;
HTHDX hthd_lb;
DWORD id;
HANDLE hThread;
BOOL fException = FALSE;
// the dm is now connected
fDisconnected = FALSE;
// check to see if a re-connection is occurring while the
// process is running or after a non-servicable debug event
if (lpde) {
hthd = HTHDXFromPIDTID((PID)lpde->dwProcessId,(TID)lpde->dwThreadId);
if (hthd) {
if (lpde->dwDebugEventCode == EXCEPTION_DEBUG_EVENT) {
if (lpde->u.Exception.dwFirstChance) {
hthd->tstate |= ts_first;
} else {
hthd->tstate |= ts_second;
}
}
hthd->tstate &= ~ts_running;
hthd->tstate |= ts_stopped;
}
}
fUseRoot = TRUE;
for (hprc = prcList->next; hprc; hprc = hprc->next) {
// generate a create process event
if (fUseRoot) {
hprc->pstate |= ps_root;
hprc->hpid = hpidRoot;
fUseRoot = FALSE;
} else {
hprc->hpid = (HPID)INVALID;
}
assert( Is64PtrSE(hprc->rgDllList[0].offBaseOfImage) );
hthd=hprc->hthdChild;
ResetEvent(hEventCreateProcess);
de.dwDebugEventCode = CREATE_PROCESS_DEBUG_EVENT;
de.dwProcessId = hprc->pid;
de.dwThreadId = hthd->tid;
de.u.CreateProcessInfo.hFile = NULL;
de.u.CreateProcessInfo.hProcess = hprc->rwHand;
de.u.CreateProcessInfo.hThread = hthd->rwHand;
de.u.CreateProcessInfo.lpBaseOfImage = hprc->rgDllList[0].offBaseOfImage;
de.u.CreateProcessInfo.dwDebugInfoFileOffset = 0;
de.u.CreateProcessInfo.nDebugInfoSize = 0;
de.u.CreateProcessInfo.lpStartAddress = PC(hthd);
de.u.CreateProcessInfo.lpThreadLocalBase = 0;
de.u.CreateProcessInfo.lpImageName = 0;
de.u.CreateProcessInfo.fUnicode = 0;
NotifyEM(&de, hthd, 0, (ULONG64)hprc);
WaitForSingleObject(hEventCreateProcess, INFINITE);
// mark the process as 'being connected' so that the continue debug
// events that are received from the shell are ignored
hprc->pstate |= ps_connect;
// look for a thread that is stopped and not dead
for (hthd=hprc->hthdChild,hthd_lb=NULL; hthd; hthd=hthd->nextSibling) {
if ((!(hthd->tstate & ts_dead)) && (hthd->tstate & ts_stopped)) {
hthd_lb = hthd;
break;
}
}
if (hthd_lb == NULL) {
// if we get here then there are no threads that are stopped
// so we must look for the first alive thread
for (hthd=hprc->hthdChild,hthd_lb=NULL; hthd; hthd=hthd->nextSibling) {
if (!(hthd->tstate & ts_dead)) {
hthd_lb = hthd;
break;
}
}
}
if (hthd_lb == NULL) {
// if this happens then we are really screwed. there are no valid
// threads to use, so lets bail out.
return;
}
if ((hthd_lb->tstate & ts_first) || (hthd_lb->tstate & ts_second)) {
fException = TRUE;
}
// generate mod loads for all the dlls for this process
// this MUST be done before the thread creates because the
// current PC of each thread can be in any of the loaded
// modules.
hthd = hthd_lb;
if (!fNoDllLoad) {
for (i=0; i<(DWORD)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;
de.u.LoadDll.hFile = NULL;
de.u.LoadDll.lpBaseOfDll = hprc->rgDllList[i].offBaseOfImage;
de.u.LoadDll.lpImageName = (ULONG64)hprc->rgDllList[i].szDllName;
de.u.LoadDll.fUnicode = FALSE;
if (LoadDll(&de, hthd, &cbPacket, &lpbPacket, FALSE) || (cbPacket == 0)) {
NotifyEM(&de, hthd, cbPacket, (ULONG64)lpbPacket);
}
}
}
}
// loop thru all the threads for this process and
// generate a thread create event for each one
for (hthd=hprc->hthdChild; hthd; hthd=hthd->nextSibling) {
if (!(hthd->tstate & ts_dead)) {
if (fException && hthd_lb == hthd) {
continue;// do this one last
}
// generate a thread create event
ResetEvent( hprc->hEventCreateThread );
ResetEvent( hEventContinue );
de.dwDebugEventCode = CREATE_THREAD_DEBUG_EVENT;
de.dwProcessId = hprc->pid;
de.dwThreadId = hthd->tid;
NotifyEM( &de, hthd, 0, (ULONG64)hprc );
WaitForSingleObject( hprc->hEventCreateThread, INFINITE );
WaitForSingleObject( hEventContinue, INFINITE );// wait for the shell to continue the new thread
}
}
hthd = hthd_lb;
if (fException) {
// We saved this thread for last -
// generate a thread create event
ResetEvent( hprc->hEventCreateThread );
ResetEvent( hEventContinue );
de.dwDebugEventCode = CREATE_THREAD_DEBUG_EVENT;
de.dwProcessId = hprc->pid;
de.dwThreadId = hthd->tid;
NotifyEM( &de, hthd, 0, (ULONG64)hprc );
WaitForSingleObject( hprc->hEventCreateThread, INFINITE );
WaitForSingleObject( hEventContinue, INFINITE );// wait for the shell to continue the new thread
}
if (hthd->tstate & ts_running) {
// we didn't have a stopped thread -
// this will create a thread in the debuggee that will immediately stop at a breakpoint.
// this will cause the shell to think that we are processing a normal attach.
HMODULE hModule = GetModuleHandle("ntdll.dll");
FARPROC ProcAddr = GetProcAddress(hModule, "DbgBreakPoint" );
RegisterExpectedEvent( hprc, (HTHDX)NULL, BREAKPOINT_DEBUG_EVENT, NO_SUBCLASS, DONT_NOTIFY, ActionDebugActiveReady, FALSE, (ULONG64)hprc);
hThread = CreateRemoteThread( (HANDLE) hprc->rwHand, NULL, 4096, (LPTHREAD_START_ROUTINE) ProcAddr, 0, 0, &id);
} else if (!lpde || lpde->dwThreadId != hthd->tid) {
// generate a load complete event
ResetEvent( hEventContinue );
de.dwDebugEventCode = LOAD_COMPLETE_DEBUG_EVENT;
de.dwProcessId = hprc->pid;
de.dwThreadId = hthd->tid;
NotifyEM( &de, hthd, 0, 0L);
WaitForSingleObject( hEventContinue, INFINITE );// wait for the continue...
if (hthd->tstate & ts_stopped) {
// deliver the exception.
de.dwProcessId = hprc->pid;
de.dwThreadId = hthd->tid;
if ((hthd->tstate & ts_first) || (hthd->tstate & ts_second)) {
de.dwDebugEventCode = EXCEPTION_DEBUG_EVENT;
} else {
de.dwDebugEventCode = BREAKPOINT_DEBUG_EVENT;
}
de.u.Exception.dwFirstChance = (hthd->tstate & ts_first) != 0;
de.u.Exception.ExceptionRecord = hthd->ExceptionRecord;
NotifyEM(&de, hthd, 0, 0);
}
}
hprc->pstate &= ~ps_connect;// reset the process state
}
}
/*
* HACK HACK HACK HACK HACK HACK HACK HACK HACK HACK HACK HACK
* In NT 3.x, it is impossible for a GUI app to spawn a console
* app and have one or two of the console app's standard handles
* redirected away from the console; you can only do it with
* either none of the handles redirected, or all three of them.
* See also the struct STARTUPINFO in the Win32 Programmer's Reference
* However, I learned by experimentation that it just so happens
* that there are a few magic values I can pass in for stdin,
* stdout, and stderr, which will have the desired effect of
* leaving that handle attached to the console. For example,
* if I pass ((HANDLE)3) for stdin, stdin will stay attached
* to the console. stdout is ((HANDLE)7), and stderr is
* ((HANDLE)11)
* In UNIX, you would probably use 0, 1, and 2. Perhaps it is no
* conicidence that for x={0, 1, 2}, the magic handles are (x<<2 | 3)
* Not necessary for NT 4.0, but it works there, too.
* HACK HACK HACK HACK HACK HACK HACK HACK HACK HACK HACK HACK
*/
HANDLE const rghMagic[3] = {(HANDLE)3, (HANDLE)7, (HANDLE)11};
/*
* Close3Handles: helper function for I/O redirection
*/
void
Close3Handles(
HANDLE *rgh
)
{
int iFile;
for (iFile=0; iFile<3; ++iFile) {
if (rgh[iFile] != 0 &&
rgh[iFile] != INVALID_HANDLE_VALUE &&
rgh[iFile] != rghMagic[iFile])
{
VERIFY(CloseHandle(rgh[iFile]));
rgh[iFile] = 0;
}
}
}
XOSD
ProcessDebuggeeRedirection(
LPTSTR lszCommandLine,
STARTUPINFO FAR * psi
)
/*++
Routine Description:
Parse command line redirection
Arguments:
szCommandLine A string representing the command line.
psi Where we leave our mark
Return Value:
xosdNone If no error
xosdIORedirBadFile If we can't open the file
xosdIORedirSyntax If the redir syntax is bad
Notes:
The redirection-related text is removed from lszCommandLine
Redirection is a hack in NT
psi -> dwFlags += STARTF+USESTDHANDLES
psi -> hStd{Input,Output,Error} are set
--*/
{
LPTSTR lszArg;
LPTSTR lszBegin;
LPTSTR lszEnd;
BOOL fInQuote = FALSE;
BOOL fAppend;
// iFile is 0,1,2 for std{in,out,err}
int iFile, iFileFrom;
CHAR ch;
lszArg = lszCommandLine;
while (*lszArg) {
// skip over quoted text on command line
if (*lszArg == '"') {
fInQuote = !fInQuote;
}
if (fInQuote) {
// If in quoted text, increment lszArg
lszArg = _ftcsinc( lszArg );
continue;
}
lszBegin = lszArg;
// recognize leading digit for "2>blah", "0<blah", etc.
// Put it in iFile
if (*lszArg >= '0' && *lszArg <= '2') {
// iFile holds the file descriptor
iFile = *lszArg - '0';
lszArg = _ftcsinc( lszArg );
if (*lszArg == '\0') {
break;
}
} else {
// For 'foo.exe > bar.txt' (no file number), we'll figure it out later.
iFile = -1;
}
// If there is redirection going on, process it.
if (*lszArg == '<' || *lszArg == '>') {
psi -> dwFlags |= STARTF_USESTDHANDLES;
// if there was no explicit leading digit, figure out the
// implicit one: 0 for "<", 1 for ">" or ">>"
if (iFile == -1) {
if (*lszArg == '<') {
iFile = 0;
} else {
iFile = 1;
}
} else if (iFile == 0) {
if (*lszArg == '>') {
Close3Handles(rgh);
return xosdIORedirSyntax;
}
} else {
if (*lszArg == '<') {
Close3Handles(rgh);
return xosdIORedirSyntax;
}
}
if (lszArg[0] == '>' && lszArg[1] == '>') {
fAppend = TRUE;
lszArg = _ftcsinc( lszArg );
} else {
fAppend = FALSE;
}
lszArg = _ftcsinc( lszArg );
// deal with "2>&1" and so on
if (*lszArg == '&') {
lszArg = _ftcsinc( lszArg );
while (*lszArg == ' ' || *lszArg == '\t') {
lszArg = _ftcsinc( lszArg );
}
// error conditions:
// 1<&x where ix not in [012]
// 1<&1
// 2>>&1
if (*lszArg < '0' || *lszArg > '2' ||
*lszArg - '0' == iFile || fAppend) {
Close3Handles(rgh);
return xosdIORedirSyntax;
}
iFileFrom = *lszArg - '0';
if (rgh[iFileFrom] == 0 ||
rgh[iFileFrom] == INVALID_HANDLE_VALUE) {
rgh[iFile] = rgh[iFileFrom];
} else {
HANDLE hProcess = GetCurrentProcess();
if (!DuplicateHandle(
hProcess, rgh[iFileFrom],
hProcess, &rgh[iFile],
0, TRUE, DUPLICATE_SAME_ACCESS))
{
Close3Handles(rgh);
return xosdIORedirBadFile;
}
}
lszArg = _ftcsinc( lszArg ); // get past last digit
} else {
static char rgchEndFilename[] = "\t \"&,;<=>";
static SECURITY_ATTRIBUTES sa = {sizeof(sa), 0, TRUE};
// skip blanks after "<" or ">"
while (*lszArg == ' ' || *lszArg == '\t') {
++lszArg;
}
// append null to szArg
lszEnd = lszArg;
while (*lszEnd && !_tcschr(rgchEndFilename, *lszEnd)) {
lszEnd = _ftcsinc( lszEnd );
}
ch = *lszEnd;
*lszEnd = '\0';
if (iFile) {
// std{out,err}
rgh[iFile] = CreateFile (
lszArg,
GENERIC_READ|GENERIC_WRITE,
FILE_SHARE_READ,
&sa,
fAppend ? OPEN_EXISTING : CREATE_ALWAYS,
FILE_ATTRIBUTE_NORMAL,
0);
} else {
// stdin
rgh[iFile] = CreateFile (
lszArg,
GENERIC_READ,
FILE_SHARE_READ|FILE_SHARE_WRITE,
&sa,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,
0);
}
if (rgh[iFile] == INVALID_HANDLE_VALUE) {
// // The file could not be opened as desired.
// _ftcsncpy(szErrArg, lszArg, sizeof(szErrArg));
// szErrArg[sizeof(szErrArg)-1] = '\0';
// _ftcsncpy(szErrExe, lszExe, sizeof(szErrExe));
// szErrExe[sizeof(szErrExe)-1] = '\0';
// restore byte after arg
*lszEnd = ch;
Close3Handles(rgh);
return xosdIORedirBadFile;
}
// restore byte after arg
*lszEnd = ch;
// if ">>", move to end of file
if (fAppend) {
SetFilePointer(rgh[iFile], 0, NULL, FILE_END);
}
// advance lszArg to end of string
lszArg = lszEnd;
}
// remove the redirection from the command line
if (*lszArg == ' ' || *lszArg == '\t') {
lszArg++;
}
_fmemmove(lszBegin, lszArg,
(_ftcslen(lszArg)+1) * sizeof(TCHAR));
lszArg = lszBegin;
} else {
lszArg = _ftcsinc( lszArg );
}
} // while
if (lszCommandLine[0] == ' ' && lszCommandLine[1] == '\0') {
lszCommandLine[0] = '\0';
}
// If we're redirecting at all
if (psi -> dwFlags & STARTF_USESTDHANDLES) {
OSVERSIONINFO ver;
ver.dwOSVersionInfoSize = sizeof (OSVERSIONINFO);
GetVersionEx (&ver);
for (iFile=0; iFile<3; ++iFile) {
// If they're still unset.
if (rgh[iFile] == 0) {
// If we are using NT 3.x or greater,
// use the hack magic handles. See comments
// near the definition of rghMagic
// This is a big-time hack. Luckily, as of NT 4.0 it is no longer necessary.
if (ver.dwPlatformId == VER_PLATFORM_WIN32_NT && ver.dwMajorVersion == 3) {
rgh[iFile] = rghMagic[iFile];
} else {
rgh[iFile] = INVALID_HANDLE_VALUE;
}
}
}
psi -> hStdInput = rgh[0];
psi -> hStdOutput = rgh[1];
psi -> hStdError = rgh[2];
}
return xosdNone;
} //ProcessDebuggeeRedirection
#ifndef KERNEL
void
ClearPendingDebugEvents(
PID pid,
TID tid
)
/*++
Routine Description:
Removes any pending debug events form the list.
The process and thread id pair are used as the
search criteria.
Arguments:
pid - process id
tid - thread id
Return Value:
none
--*/
{
PDETOSAVE pDeToSave;
if (IsListEmpty(&DmListOfPendingDebugEvents)) {
return;
}
pDeToSave = (PDETOSAVE) DmListOfPendingDebugEvents.Flink;
while (&pDeToSave->List != &DmListOfPendingDebugEvents) {
PDETOSAVE pNext = (PDETOSAVE) pDeToSave->List.Flink;
if (pid == pDeToSave->de.dwProcessId
&& tid == pDeToSave->de.dwThreadId) {
RemoveEntryList(&pDeToSave->List);
InsertTailList(&DmFreeListOfDebugEvents, &pDeToSave->List);
}
pDeToSave = pNext;
}
}
PDETOSAVE
GetNextFreeDebugEvent(
VOID
)
/*++
Routine Description:
Returns a PDETOSAVE structure from the free list. If
the free list is empty a new structure is allocated.
Arguments:
none
Return Value:
Pointer to an unused PDETOSAVE structure.
--*/
{
PDETOSAVE pDeToSave;
if (!IsListEmpty(&DmFreeListOfDebugEvents)) {
pDeToSave = (PDETOSAVE) RemoveHeadList(&DmFreeListOfDebugEvents);
memset(pDeToSave, 0, sizeof(*pDeToSave));
} else {
pDeToSave = (PDETOSAVE) MHAlloc(sizeof(DETOSAVE));
if (pDeToSave) {
memset(pDeToSave, 0, sizeof(*pDeToSave));
}
}
return pDeToSave;
}
PDETOSAVE GetMostRecentDebugEvent(PID pid, TID tid)
/*++
Routine Description:
Return the most recent DEBUGEVENT for a given process/thread pair.
Arguments:
pid - process id
tid - thread id
Return Value:
Pointer to the most recent DEBUGEVENT for the given process/thread pair.
NULL if the list is empty.
--*/
{
PDETOSAVE pDeToSave;
if (IsListEmpty(&DmListOfPendingDebugEvents)) {
return NULL;
}
pDeToSave = (PDETOSAVE) DmListOfPendingDebugEvents.Blink;
while (&pDeToSave->List != &DmListOfPendingDebugEvents) {
if (pid == pDeToSave->de.dwProcessId && tid == pDeToSave->de.dwThreadId) {
return pDeToSave;
}
pDeToSave = (PDETOSAVE) pDeToSave->List.Blink;
}
return NULL;
}
#endif // !KERNEL