You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
2661 lines
72 KiB
2661 lines
72 KiB
//----------------------------------------------------------------------------
|
|
//
|
|
// Abstraction of target-specific information.
|
|
//
|
|
// Copyright (C) Microsoft Corporation, 1999-2002.
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
#include "ntsdp.hpp"
|
|
|
|
ULONG g_NumberTargets;
|
|
TargetInfo* g_TargetHead;
|
|
|
|
//----------------------------------------------------------------------------
|
|
//
|
|
// TargetInfo.
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
TargetInfo::TargetInfo(ULONG Class, ULONG Qual, BOOL DynamicEvents)
|
|
{
|
|
m_Class = Class;
|
|
m_ClassQualifier = Qual;
|
|
m_DynamicEvents = DynamicEvents;
|
|
|
|
m_UserId = FindNextUserId(LAYER_TARGET);
|
|
m_Next = NULL;
|
|
|
|
m_NumEvents = 1;
|
|
m_EventIndex = 0;
|
|
m_NextEventIndex = 0;
|
|
m_FirstWait = TRUE;
|
|
m_EventPossible = FALSE;
|
|
m_BreakInMessage = FALSE;
|
|
|
|
FlushSelectorCache();
|
|
|
|
m_PhysicalCache.SetTarget(this);
|
|
|
|
PCHAR CacheEnv = getenv("_NT_DEBUG_CACHE_SIZE");
|
|
if (CacheEnv != NULL)
|
|
{
|
|
m_PhysicalCache.m_MaxSize = atol(CacheEnv);
|
|
m_PhysicalCache.m_UserSize = m_PhysicalCache.m_MaxSize;
|
|
}
|
|
|
|
ResetSystemInfo();
|
|
}
|
|
|
|
TargetInfo::~TargetInfo(void)
|
|
{
|
|
DeleteSystemInfo();
|
|
Unlink();
|
|
|
|
g_UserIdFragmented[LAYER_TARGET]++;
|
|
|
|
if (g_Target == this)
|
|
{
|
|
g_Target = NULL;
|
|
}
|
|
if (g_EventTarget == this)
|
|
{
|
|
g_EventTarget = NULL;
|
|
DiscardLastEvent();
|
|
}
|
|
}
|
|
|
|
void
|
|
TargetInfo::Link(void)
|
|
{
|
|
TargetInfo* Cur;
|
|
TargetInfo* Prev;
|
|
|
|
Prev = NULL;
|
|
for (Cur = g_TargetHead; Cur; Cur = Cur->m_Next)
|
|
{
|
|
if (Cur->m_UserId > this->m_UserId)
|
|
{
|
|
break;
|
|
}
|
|
|
|
Prev = Cur;
|
|
}
|
|
|
|
m_Next = Cur;
|
|
if (!Prev)
|
|
{
|
|
g_TargetHead = this;
|
|
}
|
|
else
|
|
{
|
|
Prev->m_Next = this;
|
|
}
|
|
|
|
g_NumberTargets++;
|
|
|
|
NotifyChangeEngineState(DEBUG_CES_SYSTEMS, m_UserId, TRUE);
|
|
}
|
|
|
|
void
|
|
TargetInfo::Unlink(void)
|
|
{
|
|
TargetInfo* Cur;
|
|
TargetInfo* Prev;
|
|
|
|
Prev = NULL;
|
|
for (Cur = g_TargetHead; Cur; Cur = Cur->m_Next)
|
|
{
|
|
if (Cur == this)
|
|
{
|
|
break;
|
|
}
|
|
|
|
Prev = Cur;
|
|
}
|
|
|
|
if (!Cur)
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (!Prev)
|
|
{
|
|
g_TargetHead = this->m_Next;
|
|
}
|
|
else
|
|
{
|
|
Prev->m_Next = this->m_Next;
|
|
}
|
|
|
|
g_NumberTargets--;
|
|
|
|
NotifyChangeEngineState(DEBUG_CES_SYSTEMS, DEBUG_ANY_ID, TRUE);
|
|
}
|
|
|
|
HRESULT
|
|
TargetInfo::Initialize(void)
|
|
{
|
|
return S_OK;
|
|
}
|
|
|
|
void
|
|
TargetInfo::DebuggeeReset(ULONG Reason, BOOL FromEvent)
|
|
{
|
|
if (Reason == DEBUG_SESSION_REBOOT)
|
|
{
|
|
dprintf("Shutdown occurred...unloading all symbol tables.\n");
|
|
}
|
|
else if (Reason == DEBUG_SESSION_HIBERNATE)
|
|
{
|
|
dprintf("Hibernate occurred\n");
|
|
}
|
|
|
|
if (FromEvent && g_EventTarget == this)
|
|
{
|
|
g_EngStatus &= ~ENG_STATUS_SUSPENDED;
|
|
}
|
|
|
|
DeleteSystemInfo();
|
|
ResetSystemInfo();
|
|
|
|
// If we were waiting for a shutdown event
|
|
// reset the command state to indicate that
|
|
// we successfully received the shutdown.
|
|
if (FromEvent && SPECIAL_EXECUTION(g_CmdState))
|
|
{
|
|
g_CmdState = 'i';
|
|
}
|
|
|
|
DiscardedTargets(Reason);
|
|
}
|
|
|
|
HRESULT
|
|
TargetInfo::SwitchToTarget(TargetInfo* From)
|
|
{
|
|
SetPromptThread(m_CurrentProcess->m_CurrentThread,
|
|
SPT_DEFAULT_OCI_FLAGS);
|
|
return S_OK;
|
|
}
|
|
|
|
ModuleInfo*
|
|
TargetInfo::GetModuleInfo(BOOL UserMode)
|
|
{
|
|
if (UserMode)
|
|
{
|
|
switch(m_PlatformId)
|
|
{
|
|
case VER_PLATFORM_WIN32_NT:
|
|
return &g_NtTargetUserModuleIterator;
|
|
case VER_PLATFORM_WIN32_WINDOWS:
|
|
case VER_PLATFORM_WIN32_CE:
|
|
return &g_ToolHelpModuleIterator;
|
|
default:
|
|
ErrOut("System module info not available\n");
|
|
return NULL;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (m_PlatformId != VER_PLATFORM_WIN32_NT)
|
|
{
|
|
ErrOut("System module info only available on "
|
|
"Windows NT/2000/XP\n");
|
|
return NULL;
|
|
}
|
|
|
|
DBG_ASSERT(IS_KERNEL_TARGET(this));
|
|
return &g_NtKernelModuleIterator;
|
|
}
|
|
}
|
|
|
|
UnloadedModuleInfo*
|
|
TargetInfo::GetUnloadedModuleInfo(void)
|
|
{
|
|
if (m_PlatformId != VER_PLATFORM_WIN32_NT)
|
|
{
|
|
ErrOut("System unloaded module info only available on "
|
|
"Windows NT/2000/XP\n");
|
|
return NULL;
|
|
}
|
|
|
|
if (IS_KERNEL_TARGET(this))
|
|
{
|
|
return &g_NtKernelUnloadedModuleIterator;
|
|
}
|
|
else
|
|
{
|
|
return &g_NtUserUnloadedModuleIterator;
|
|
}
|
|
}
|
|
|
|
HRESULT
|
|
TargetInfo::GetImageVersionInformation(ProcessInfo* Process,
|
|
PCSTR ImagePath,
|
|
ULONG64 ImageBase,
|
|
PCSTR Item,
|
|
PVOID Buffer, ULONG BufferSize,
|
|
PULONG VerInfoSize)
|
|
{
|
|
HRESULT Status;
|
|
IMAGE_NT_HEADERS64 NtHdr;
|
|
|
|
//
|
|
// This default implementation attempts to read the image's
|
|
// raw version information in memory.
|
|
//
|
|
|
|
if ((Status = ReadImageNtHeaders(Process, ImageBase, &NtHdr)) != S_OK)
|
|
{
|
|
return Status;
|
|
}
|
|
|
|
if (NtHdr.OptionalHeader.NumberOfRvaAndSizes <=
|
|
IMAGE_DIRECTORY_ENTRY_RESOURCE)
|
|
{
|
|
// No resource information so no version information.
|
|
return E_NOINTERFACE;
|
|
}
|
|
|
|
return ReadImageVersionInfo(Process, ImageBase, Item,
|
|
Buffer, BufferSize, VerInfoSize,
|
|
&NtHdr.OptionalHeader.
|
|
DataDirectory[IMAGE_DIRECTORY_ENTRY_RESOURCE]);
|
|
}
|
|
|
|
HRESULT
|
|
TargetInfo::Reload(ThreadInfo* Thread,
|
|
PCSTR Args, PCSTR* ArgsRet)
|
|
{
|
|
HRESULT Status;
|
|
CHAR AnsiString[MAX_IMAGE_PATH];
|
|
LPSTR SpecificModule = NULL;
|
|
BOOL SpecificWild = TRUE;
|
|
ULONG64 Address = 0;
|
|
ULONG ImageSize = 0;
|
|
PCHAR Scan;
|
|
ULONG ModCount;
|
|
BOOL IgnoreSignature = FALSE;
|
|
ULONG ReloadSymOptions;
|
|
BOOL UnloadOnly = FALSE;
|
|
BOOL ReallyVerbose = FALSE;
|
|
BOOL LoadUserSymbols = TRUE;
|
|
BOOL UserModeList = IS_USER_TARGET(this);
|
|
BOOL ForceSymbolLoad = FALSE;
|
|
BOOL PrintImageListOnly = FALSE;
|
|
BOOL AddrLoad = FALSE;
|
|
BOOL UseDebuggerModuleList;
|
|
BOOL SkipPathChecks = FALSE;
|
|
ModuleInfo* ModIter;
|
|
BOOL Wow64ModLoaded = FALSE;
|
|
HRESULT RetStatus = S_OK;
|
|
MODULE_INFO_ENTRY ModEntry = {0};
|
|
ProcessInfo* Process;
|
|
ImageInfo* ImageAdded;
|
|
|
|
if ((!IS_USER_TARGET(this) && !IS_KERNEL_TARGET(this)) ||
|
|
!Thread)
|
|
{
|
|
ErrOut("Reload failure, partially initialized target\n");
|
|
return E_UNEXPECTED;
|
|
}
|
|
|
|
Process = Thread->m_Process;
|
|
|
|
// Historically, live user-mode reload has always
|
|
// just used the internal module list so preserve that.
|
|
UseDebuggerModuleList = IS_USER_TARGET(this) && !IS_DUMP_TARGET(this);
|
|
|
|
for (;;)
|
|
{
|
|
while (*Args && *Args <= ' ')
|
|
{
|
|
Args++;
|
|
}
|
|
|
|
if (*Args != '/' && *Args != '-')
|
|
{
|
|
break;
|
|
}
|
|
|
|
Args++;
|
|
while (*Args > ' ' && *Args != ';')
|
|
{
|
|
switch(*Args++)
|
|
{
|
|
case 'a':
|
|
// for internal use only: loads whatever is found at the
|
|
// passed address
|
|
AddrLoad = TRUE;
|
|
break;
|
|
|
|
case 'd':
|
|
UseDebuggerModuleList = TRUE;
|
|
break;
|
|
|
|
case 'f':
|
|
ForceSymbolLoad = TRUE;
|
|
break;
|
|
|
|
case 'i':
|
|
IgnoreSignature = TRUE;
|
|
// We always force symbol loading in this
|
|
// case as we can't delay ignoring the signature.
|
|
ForceSymbolLoad = TRUE;
|
|
break;
|
|
|
|
case 'l':
|
|
PrintImageListOnly = TRUE;
|
|
break;
|
|
|
|
case 'n':
|
|
LoadUserSymbols = FALSE;
|
|
break;
|
|
|
|
case 'P':
|
|
// Internal-only switch.
|
|
SkipPathChecks = TRUE;
|
|
break;
|
|
|
|
case 's':
|
|
UseDebuggerModuleList = FALSE;
|
|
break;
|
|
|
|
case 'u':
|
|
if (!_strnicmp(Args, "ser", 3) &&
|
|
(Args[3] == ' ' || Args[3] == '\t' || !Args[3]))
|
|
{
|
|
UserModeList = TRUE;
|
|
if (!m_SystemRangeStart)
|
|
{
|
|
ErrOut("Unknown system range start, "
|
|
"check kernel symbols\n");
|
|
*ArgsRet = Args;
|
|
return E_INVALIDARG;
|
|
}
|
|
|
|
Args += 3;
|
|
}
|
|
else
|
|
{
|
|
UnloadOnly = TRUE;
|
|
}
|
|
break;
|
|
|
|
case 'v':
|
|
ReallyVerbose = TRUE;
|
|
break;
|
|
|
|
case 'w':
|
|
SpecificWild = FALSE;
|
|
break;
|
|
|
|
default:
|
|
dprintf("Reload: Unknown option '%c'\n", Args[-1]);
|
|
|
|
case '?':
|
|
dprintf("Usage: .reload [flags] [module [= Address "
|
|
"[, Size] ]]\n");
|
|
dprintf(" Flags: /d Use the debugger's module list\n");
|
|
dprintf(" Default for live user-mode "
|
|
"sessions\n");
|
|
dprintf(" /f Force immediate symbol load "
|
|
"instead of deferred\n");
|
|
dprintf(" /i Force symbol load by ignoring "
|
|
"mismatches in the pdb signature\n"
|
|
" (implies /f)\n");
|
|
dprintf(" /l Just list the modules. "
|
|
"Kernel output same as !drivers\n");
|
|
dprintf(" /n Do not load from user-mode list "
|
|
"in kernel sessions\n");
|
|
dprintf(" /s Use the system's module list\n");
|
|
dprintf(" Default for dump and kernel sessions\n");
|
|
dprintf(" /u Unload modules, no reload\n");
|
|
dprintf(" /user Load only user-mode modules "
|
|
"in kernel sessions\n");
|
|
dprintf(" /v Verbose\n");
|
|
dprintf(" /w No wildcard matching on "
|
|
"module name\n");
|
|
|
|
dprintf("\nUse \".hh .reload\" or open debugger.chm in "
|
|
"the debuggers directory to get\n"
|
|
"detailed documentation on this command.\n\n");
|
|
|
|
*ArgsRet = Args;
|
|
return E_INVALIDARG;
|
|
}
|
|
}
|
|
}
|
|
|
|
PSTR RawString;
|
|
ULONG RawStringLen;
|
|
|
|
RawString = BufferStringValue((PSTR*)&Args,
|
|
STRV_SPACE_IS_SEPARATOR |
|
|
STRV_ALLOW_EMPTY_STRING |
|
|
STRV_NO_MODIFICATION,
|
|
&RawStringLen, NULL);
|
|
|
|
*ArgsRet = Args;
|
|
|
|
if (!RawString || !RawStringLen)
|
|
{
|
|
AddrLoad = FALSE;
|
|
}
|
|
else
|
|
{
|
|
if (RawStringLen >= DIMA(AnsiString))
|
|
{
|
|
return E_INVALIDARG;
|
|
}
|
|
|
|
memcpy(AnsiString, RawString, RawStringLen * sizeof(*RawString));
|
|
AnsiString[RawStringLen] = 0;
|
|
|
|
//
|
|
// Support .reload <image.ext>=<base>,<size>.
|
|
//
|
|
|
|
if (Scan = strchr(AnsiString, '='))
|
|
{
|
|
*Scan++ = 0;
|
|
|
|
Address = EvalStringNumAndCatch(Scan);
|
|
if (!Address)
|
|
{
|
|
ErrOut("Invalid address %s\n", Scan);
|
|
return E_INVALIDARG;
|
|
}
|
|
if (!m_Machine->m_Ptr64)
|
|
{
|
|
Address = EXTEND64(Address);
|
|
}
|
|
|
|
if (Scan = strchr(Scan, ','))
|
|
{
|
|
Scan++;
|
|
ImageSize = (ULONG)EvalStringNumAndCatch(Scan);
|
|
if (!ImageSize)
|
|
{
|
|
ErrOut("Invalid ImageSize %s\n", Scan);
|
|
return E_INVALIDARG;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (UnloadOnly)
|
|
{
|
|
BOOL Deleted;
|
|
|
|
Deleted = Process->
|
|
DeleteImageByName(AnsiString, INAME_MODULE);
|
|
if (!Deleted)
|
|
{
|
|
// The user might have given an image name
|
|
// instead of a module name so try that.
|
|
Deleted = Process->DeleteImageByName
|
|
(PathTail(AnsiString), INAME_IMAGE_PATH_TAIL);
|
|
}
|
|
if (Deleted)
|
|
{
|
|
dprintf("Unloaded %s\n", AnsiString);
|
|
return S_OK;
|
|
}
|
|
else
|
|
{
|
|
dprintf("Unable to find module '%s'\n", AnsiString);
|
|
return E_NOINTERFACE;
|
|
}
|
|
}
|
|
|
|
SpecificModule = _strdup(AnsiString);
|
|
if (!SpecificModule)
|
|
{
|
|
return E_OUTOFMEMORY;
|
|
}
|
|
|
|
if (IS_KERNEL_TARGET(this) &&
|
|
_stricmp(AnsiString, KERNEL_MODULE_NAME) == 0)
|
|
{
|
|
ForceSymbolLoad = TRUE;
|
|
}
|
|
else
|
|
{
|
|
if (AddrLoad)
|
|
{
|
|
free(SpecificModule);
|
|
SpecificModule = NULL;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!PrintImageListOnly && !SkipPathChecks)
|
|
{
|
|
if (g_SymbolSearchPath == NULL ||
|
|
*g_SymbolSearchPath == NULL)
|
|
{
|
|
dprintf("*********************************************************************\n");
|
|
dprintf("* Symbols can not be loaded because symbol path is not initialized. *\n");
|
|
dprintf("* *\n");
|
|
dprintf("* The Symbol Path can be set by: *\n");
|
|
dprintf("* using the _NT_SYMBOL_PATH environment variable. *\n");
|
|
dprintf("* using the -y <symbol_path> argument when starting the debugger. *\n");
|
|
dprintf("* using .sympath and .sympath+ *\n");
|
|
dprintf("*********************************************************************\n");
|
|
RetStatus = E_INVALIDARG;
|
|
goto FreeSpecMod;
|
|
}
|
|
|
|
if (IS_DUMP_WITH_MAPPED_IMAGES(this) &&
|
|
(g_ExecutableImageSearchPath == NULL ||
|
|
*g_ExecutableImageSearchPath == NULL))
|
|
{
|
|
dprintf("*********************************************************************\n");
|
|
dprintf("* Analyzing Minidumps requires access to the actual executable *\n");
|
|
dprintf("* images for the crashed system *\n");
|
|
dprintf("* *\n");
|
|
dprintf("* The Executable Image Path can be set by: *\n");
|
|
dprintf("* using the _NT_EXECUTABLE_IMAGE_PATH environment variable. *\n");
|
|
dprintf("* using the -i <image_path> argument when starting the debugger. *\n");
|
|
dprintf("* using .exepath and .exepath+ *\n");
|
|
dprintf("*********************************************************************\n");
|
|
RetStatus = E_INVALIDARG;
|
|
goto FreeSpecMod;
|
|
}
|
|
}
|
|
|
|
//
|
|
// If both the module name and the address are specified, then just load
|
|
// the module right now, as this is only used when normal symbol loading
|
|
// would have failed in the first place.
|
|
//
|
|
|
|
if (SpecificModule && Address)
|
|
{
|
|
if (IgnoreSignature)
|
|
{
|
|
ReloadSymOptions = SymGetOptions();
|
|
SymSetOptions(ReloadSymOptions | SYMOPT_LOAD_ANYTHING);
|
|
}
|
|
|
|
ModEntry.NamePtr = SpecificModule,
|
|
ModEntry.Base = Address;
|
|
ModEntry.Size = ImageSize;
|
|
ModEntry.CheckSum = -1;
|
|
|
|
if ((RetStatus = Process->
|
|
AddImage(&ModEntry, TRUE, &ImageAdded)) != S_OK)
|
|
{
|
|
ErrOut("Unable to add module at %s\n", FormatAddr64(Address));
|
|
}
|
|
|
|
if (IgnoreSignature)
|
|
{
|
|
SymSetOptions(ReloadSymOptions);
|
|
}
|
|
|
|
goto FreeSpecMod;
|
|
}
|
|
|
|
//
|
|
// Don't unload and reset things if we are looking for a specific module
|
|
// or if we're going to use the existing module list.
|
|
//
|
|
|
|
if (SpecificModule == NULL)
|
|
{
|
|
if (!PrintImageListOnly &&
|
|
(!UseDebuggerModuleList || UnloadOnly))
|
|
{
|
|
if (IS_KERNEL_TARGET(this) && UserModeList)
|
|
{
|
|
// This is a .reload /user, so only delete
|
|
// the user-mode modules.
|
|
Process->DeleteImagesBelowOffset(m_SystemRangeStart);
|
|
}
|
|
else
|
|
{
|
|
Process->DeleteImages();
|
|
}
|
|
}
|
|
|
|
if (UnloadOnly)
|
|
{
|
|
dprintf("Unloaded all modules\n");
|
|
return S_OK;
|
|
}
|
|
|
|
if (!IS_USER_TARGET(this) && !UseDebuggerModuleList)
|
|
{
|
|
if (IS_LIVE_KERNEL_TARGET(this))
|
|
{
|
|
// This is just a refresh and hopefully won't fail.
|
|
((LiveKernelTargetInfo*)this)->InitFromKdVersion();
|
|
}
|
|
|
|
QueryKernelInfo(Thread, TRUE);
|
|
}
|
|
|
|
//
|
|
// Print out the correct statement based on the type of output we
|
|
// want to provide
|
|
//
|
|
|
|
if (PrintImageListOnly)
|
|
{
|
|
if (UseDebuggerModuleList)
|
|
{
|
|
dprintf("Debugger Module List Summary\n");
|
|
}
|
|
else
|
|
{
|
|
dprintf("System %s Summary\n",
|
|
IS_USER_TARGET(this) ? "Image" : "Driver and Image");
|
|
}
|
|
|
|
dprintf("Base ");
|
|
if (m_Machine->m_Ptr64)
|
|
{
|
|
dprintf(" ");
|
|
}
|
|
#if 0
|
|
if (Flags & 1)
|
|
{
|
|
dprintf("Code Size Data Size Resident "
|
|
"Standby Driver Name\n");
|
|
}
|
|
else if (Flags & 2)
|
|
{
|
|
dprintf("Code Data Locked Resident Standby "
|
|
"Loader Entry Driver Name\n");
|
|
}
|
|
else
|
|
{
|
|
#endif
|
|
|
|
if (UseDebuggerModuleList)
|
|
{
|
|
dprintf("Image Size "
|
|
"Image Name Creation Time\n");
|
|
}
|
|
else
|
|
{
|
|
dprintf("Code Size Data Size "
|
|
"Image Name Creation Time\n");
|
|
}
|
|
}
|
|
else if (UseDebuggerModuleList)
|
|
{
|
|
dprintf("Reloading current modules\n");
|
|
}
|
|
else if (!IS_USER_TARGET(this))
|
|
{
|
|
dprintf("Loading %s Symbols\n",
|
|
UserModeList ? "User" : "Kernel");
|
|
}
|
|
}
|
|
|
|
//
|
|
// Get the beginning of the module list.
|
|
//
|
|
|
|
if (UseDebuggerModuleList)
|
|
{
|
|
ModIter = &g_DebuggerModuleIterator;
|
|
}
|
|
else
|
|
{
|
|
ModIter = GetModuleInfo(UserModeList);
|
|
}
|
|
|
|
if (ModIter == NULL)
|
|
{
|
|
// Error messages already printed.
|
|
RetStatus = E_UNEXPECTED;
|
|
goto FreeSpecMod;
|
|
}
|
|
if ((Status = ModIter->Initialize(Thread)) != S_OK)
|
|
{
|
|
// Error messages already printed.
|
|
// Fold unprepared-to-reload S_FALSE into S_OK.
|
|
RetStatus = SUCCEEDED(Status) ? S_OK : Status;
|
|
goto FreeSpecMod;
|
|
}
|
|
|
|
if (IgnoreSignature)
|
|
{
|
|
ReloadSymOptions = SymGetOptions();
|
|
SymSetOptions(ReloadSymOptions | SYMOPT_LOAD_ANYTHING);
|
|
}
|
|
|
|
// Suppress notifications until everything is done.
|
|
g_EngNotify++;
|
|
|
|
LoadLoop:
|
|
for (ModCount=0; ; ModCount++)
|
|
{
|
|
// Flush regularly so the user knows something is
|
|
// happening during the reload.
|
|
FlushCallbacks();
|
|
|
|
if (CheckUserInterrupt())
|
|
{
|
|
break;
|
|
}
|
|
|
|
if (ModCount > 1000)
|
|
{
|
|
ErrOut("ModuleList is corrupt - walked over 1000 module entries\n");
|
|
break;
|
|
}
|
|
|
|
if (ModEntry.DebugHeader)
|
|
{
|
|
free(ModEntry.DebugHeader);
|
|
}
|
|
|
|
ZeroMemory(&ModEntry, sizeof(ModEntry));
|
|
if ((Status = ModIter->GetEntry(&ModEntry)) != S_OK)
|
|
{
|
|
// Error message already printed in error case.
|
|
// Works for end-of-list case also.
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Check size of images
|
|
//
|
|
|
|
if (!ModEntry.Size)
|
|
{
|
|
VerbOut("Image at %s had size 0\n",
|
|
FormatAddr64(ModEntry.Base));
|
|
|
|
//
|
|
// Override this since we know all images are at least 1 page long
|
|
//
|
|
|
|
ModEntry.Size = m_Machine->m_PageSize;
|
|
}
|
|
|
|
//
|
|
// Warn if not all the information was gathered
|
|
//
|
|
|
|
if (!ModEntry.ImageInfoValid)
|
|
{
|
|
VerbOut("Unable to read image header at %s\n",
|
|
FormatAddr64(ModEntry.Base));
|
|
}
|
|
|
|
//
|
|
// Are we looking for a module at a specific address ?
|
|
//
|
|
|
|
if (AddrLoad)
|
|
{
|
|
if (Address < ModEntry.Base ||
|
|
Address >= ModEntry.Base + ModEntry.Size)
|
|
{
|
|
continue;
|
|
}
|
|
}
|
|
|
|
if (ModEntry.UnicodeNamePtr)
|
|
{
|
|
ModEntry.NamePtr =
|
|
ConvertAndValidateImagePathW((PWSTR)ModEntry.NamePtr,
|
|
ModEntry.NameLength /
|
|
sizeof(WCHAR),
|
|
ModEntry.Base,
|
|
AnsiString,
|
|
DIMA(AnsiString));
|
|
ModEntry.UnicodeNamePtr = 0;
|
|
}
|
|
else
|
|
{
|
|
ModEntry.NamePtr =
|
|
ValidateImagePath((PSTR)ModEntry.NamePtr,
|
|
ModEntry.NameLength,
|
|
ModEntry.Base,
|
|
AnsiString,
|
|
DIMA(AnsiString));
|
|
}
|
|
|
|
//
|
|
// If we are loading a specific module:
|
|
//
|
|
// If the Module is NT, we take the first module in the list as it is
|
|
// guaranteed to be the kernel. Reset the Base address if it was
|
|
// not set.
|
|
//
|
|
// Otherwise, actually compare the strings and continue if they don't
|
|
// match
|
|
//
|
|
|
|
if (SpecificModule)
|
|
{
|
|
if (!UserModeList &&
|
|
_stricmp( SpecificModule, KERNEL_MODULE_NAME ) == 0)
|
|
{
|
|
if (!m_KdVersion.KernBase)
|
|
{
|
|
m_KdVersion.KernBase = ModEntry.Base;
|
|
}
|
|
if (!m_KdDebuggerData.KernBase)
|
|
{
|
|
m_KdDebuggerData.KernBase = ModEntry.Base;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (!MatchPathTails(SpecificModule, ModEntry.NamePtr,
|
|
SpecificWild))
|
|
{
|
|
continue;
|
|
}
|
|
}
|
|
}
|
|
|
|
PCSTR NamePtrTail = PathTail(ModEntry.NamePtr);
|
|
|
|
if (PrintImageListOnly)
|
|
{
|
|
PCHAR Time;
|
|
|
|
//
|
|
// The timestamp in minidumps was corrupt until NT5 RC3
|
|
// The timestamp could also be invalid because it was paged out
|
|
// in which case it's value is UNKNOWN_TIMESTAMP.
|
|
|
|
if (IS_KERNEL_TRIAGE_DUMP(this) &&
|
|
(m_ActualSystemVersion > NT_SVER_START &&
|
|
m_ActualSystemVersion <= NT_SVER_W2K_RC3))
|
|
{
|
|
Time = "";
|
|
}
|
|
|
|
Time = TimeToStr(ModEntry.TimeDateStamp);
|
|
|
|
if (UseDebuggerModuleList)
|
|
{
|
|
dprintf("%s %6lx (%4ld k) %12s %s\n",
|
|
FormatAddr64(ModEntry.Base), ModEntry.Size,
|
|
KBYTES(ModEntry.Size), NamePtrTail,
|
|
Time);
|
|
}
|
|
else
|
|
{
|
|
dprintf("%s %6lx (%4ld k) %5lx (%3ld k) %12s %s\n",
|
|
FormatAddr64(ModEntry.Base),
|
|
ModEntry.SizeOfCode, KBYTES(ModEntry.SizeOfCode),
|
|
ModEntry.SizeOfData, KBYTES(ModEntry.SizeOfData),
|
|
NamePtrTail, Time);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Don't bother reloading the kernel if we are not specifically
|
|
// asked since we know those symbols were reloaded by the
|
|
// QueryKernelInfo call.
|
|
//
|
|
|
|
if (!SpecificModule && !UserModeList &&
|
|
m_KdDebuggerData.KernBase == ModEntry.Base)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
if (ReallyVerbose)
|
|
{
|
|
dprintf("AddImage: %s\n DllBase = %s\n Size = %08x\n "
|
|
"Checksum = %08x\n TimeDateStamp = %08x\n",
|
|
ModEntry.NamePtr, FormatAddr64(ModEntry.Base),
|
|
ModEntry.Size, ModEntry.CheckSum,
|
|
ModEntry.TimeDateStamp);
|
|
}
|
|
else
|
|
{
|
|
if (!SpecificModule)
|
|
{
|
|
dprintf(".");
|
|
}
|
|
}
|
|
|
|
if (Address)
|
|
{
|
|
ModEntry.Base = Address;
|
|
}
|
|
|
|
if ((RetStatus = Process->
|
|
AddImage(&ModEntry, ForceSymbolLoad, &ImageAdded)) != S_OK)
|
|
{
|
|
ErrOut("Unable to add module at %s\n",
|
|
FormatAddr64(ModEntry.Base));
|
|
}
|
|
}
|
|
|
|
if (SpecificModule)
|
|
{
|
|
free( SpecificModule );
|
|
goto Notify;
|
|
}
|
|
|
|
if (AddrLoad)
|
|
{
|
|
goto Notify;
|
|
}
|
|
}
|
|
|
|
if (UseDebuggerModuleList || IS_KERNEL_TARGET(this) || UserModeList)
|
|
{
|
|
// print newline after all the '.'
|
|
dprintf("\n");
|
|
}
|
|
|
|
if (!UseDebuggerModuleList && !UserModeList && SpecificModule == NULL)
|
|
{
|
|
// If we just reloaded the kernel modules
|
|
// go through the unloaded module list.
|
|
if (!PrintImageListOnly)
|
|
{
|
|
dprintf("Loading unloaded module list\n");
|
|
}
|
|
ListUnloadedModules(PrintImageListOnly ?
|
|
LUM_OUTPUT : LUM_OUTPUT_TERSE, NULL);
|
|
}
|
|
|
|
//
|
|
// If we got to the end of the kernel symbols, try to load the
|
|
// user mode symbols for the current process.
|
|
//
|
|
|
|
if (!UseDebuggerModuleList &&
|
|
(UserModeList == FALSE) &&
|
|
(LoadUserSymbols == TRUE) &&
|
|
SUCCEEDED(Status))
|
|
{
|
|
if (!AddrLoad && !SpecificModule)
|
|
{
|
|
dprintf("Loading User Symbols\n");
|
|
}
|
|
|
|
UserModeList = TRUE;
|
|
ModIter = GetModuleInfo(UserModeList);
|
|
if (ModIter != NULL && ModIter->Initialize(Thread) == S_OK)
|
|
{
|
|
goto LoadLoop;
|
|
}
|
|
}
|
|
|
|
if (!SpecificModule && !Wow64ModLoaded)
|
|
{
|
|
ModIter = &g_NtWow64UserModuleIterator;
|
|
|
|
Wow64ModLoaded = TRUE;
|
|
if (ModIter->Initialize(Thread) == S_OK)
|
|
{
|
|
dprintf("Loading Wow64 Symbols\n");
|
|
goto LoadLoop;
|
|
}
|
|
}
|
|
|
|
// In the multiple load situation we always return OK
|
|
// since an error wouldn't tell you much about what
|
|
// actually occurred.
|
|
// Specific loads that haven't already been handled are checked
|
|
// right after this.
|
|
RetStatus = S_OK;
|
|
|
|
//
|
|
// If we still have not managed to load a named file, just pass the name
|
|
// and the address and hope for the best.
|
|
//
|
|
|
|
if (SpecificModule && !PrintImageListOnly)
|
|
{
|
|
WarnOut("\nModule \"%s\" was not found in the module list.\n",
|
|
SpecificModule);
|
|
WarnOut("Debugger will attempt to load \"%s\" at given base %s.\n\n",
|
|
SpecificModule, FormatAddr64(Address));
|
|
WarnOut("Please provide the full image name, including the "
|
|
"extension (i.e. kernel32.dll)\nfor more reliable results. "
|
|
"Base address and size overrides can be given as\n"
|
|
".reload <image.ext>=<base>,<size>.\n");
|
|
|
|
ZeroMemory(&ModEntry, sizeof(ModEntry));
|
|
|
|
ModEntry.NamePtr = SpecificModule,
|
|
ModEntry.Base = Address;
|
|
ModEntry.Size = ImageSize;
|
|
|
|
if ((RetStatus = Process->
|
|
AddImage(&ModEntry, TRUE, &ImageAdded)) != S_OK)
|
|
{
|
|
ErrOut("Unable to add module at %s\n", FormatAddr64(Address));
|
|
}
|
|
|
|
free(SpecificModule);
|
|
}
|
|
|
|
Notify:
|
|
// If we've gotten this far we've done one or more reloads
|
|
// and postponed notifications. Do them now that all the work
|
|
// has been done.
|
|
g_EngNotify--;
|
|
if (SUCCEEDED(RetStatus))
|
|
{
|
|
NotifyChangeSymbolState(DEBUG_CSS_LOADS | DEBUG_CSS_UNLOADS, 0,
|
|
Process);
|
|
}
|
|
|
|
if (IgnoreSignature)
|
|
{
|
|
SymSetOptions(ReloadSymOptions);
|
|
}
|
|
|
|
if (ModEntry.DebugHeader)
|
|
{
|
|
free(ModEntry.DebugHeader);
|
|
}
|
|
|
|
return RetStatus;
|
|
|
|
FreeSpecMod:
|
|
free(SpecificModule);
|
|
return RetStatus;
|
|
}
|
|
|
|
ULONG64
|
|
TargetInfo::GetCurrentTimeDateN(void)
|
|
{
|
|
// No information.
|
|
return 0;
|
|
}
|
|
|
|
ULONG64
|
|
TargetInfo::GetCurrentSystemUpTimeN(void)
|
|
{
|
|
// No information.
|
|
return 0;
|
|
}
|
|
|
|
ULONG64
|
|
TargetInfo::GetProcessUpTimeN(ProcessInfo* Process)
|
|
{
|
|
// No information.
|
|
return 0;
|
|
}
|
|
|
|
HRESULT
|
|
TargetInfo::GetProcessTimes(ProcessInfo* Process,
|
|
PULONG64 Create,
|
|
PULONG64 Exit,
|
|
PULONG64 Kernel,
|
|
PULONG64 User)
|
|
{
|
|
// No information.
|
|
return E_NOTIMPL;
|
|
}
|
|
|
|
HRESULT
|
|
TargetInfo::GetThreadTimes(ThreadInfo* Thread,
|
|
PULONG64 Create,
|
|
PULONG64 Exit,
|
|
PULONG64 Kernel,
|
|
PULONG64 User)
|
|
{
|
|
// No information.
|
|
return E_NOTIMPL;
|
|
}
|
|
|
|
HRESULT
|
|
TargetInfo::GetProductInfo(PULONG ProductType, PULONG SuiteMask)
|
|
{
|
|
if (m_PlatformId == VER_PLATFORM_WIN32_NT)
|
|
{
|
|
return ReadSharedUserProductInfo(ProductType, SuiteMask);
|
|
}
|
|
else
|
|
{
|
|
return E_NOTIMPL;
|
|
}
|
|
}
|
|
|
|
HRESULT
|
|
TargetInfo::GetEventIndexDescription(IN ULONG Index,
|
|
IN ULONG Which,
|
|
IN OPTIONAL PSTR Buffer,
|
|
IN ULONG BufferSize,
|
|
OUT OPTIONAL PULONG DescSize)
|
|
{
|
|
switch(Which)
|
|
{
|
|
case DEBUG_EINDEX_NAME:
|
|
return FillStringBuffer("Default", 0,
|
|
Buffer, BufferSize, DescSize);
|
|
default:
|
|
return E_INVALIDARG;
|
|
}
|
|
}
|
|
|
|
HRESULT
|
|
TargetInfo::WaitInitialize(ULONG Flags,
|
|
ULONG Timeout,
|
|
WAIT_INIT_TYPE Type,
|
|
PULONG DesiredTimeout)
|
|
{
|
|
// Placeholder.
|
|
return S_OK;
|
|
}
|
|
|
|
HRESULT
|
|
TargetInfo::ReleaseLastEvent(ULONG ContinueStatus)
|
|
{
|
|
// Placeholder.
|
|
return S_OK;
|
|
}
|
|
|
|
HRESULT
|
|
TargetInfo::ClearBreakIn(void)
|
|
{
|
|
// Placeholder.
|
|
return S_OK;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
//
|
|
// LiveKernelTargetInfo miscellaneous methods.
|
|
//
|
|
// Data space methods and system objects methods are elsewhere.
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
LiveKernelTargetInfo::LiveKernelTargetInfo(ULONG Qual, BOOL DynamicEvents)
|
|
: TargetInfo(DEBUG_CLASS_KERNEL, Qual, DynamicEvents)
|
|
{
|
|
m_ConnectOptions = NULL;
|
|
}
|
|
|
|
HRESULT
|
|
LiveKernelTargetInfo::ReadBugCheckData(PULONG Code, ULONG64 Args[4])
|
|
{
|
|
ULONG64 BugCheckData;
|
|
ULONG64 Data[5];
|
|
HRESULT Status;
|
|
ULONG Read;
|
|
|
|
if (!(BugCheckData = m_KdDebuggerData.KiBugcheckData))
|
|
{
|
|
if (!GetOffsetFromSym(m_ProcessHead,
|
|
"nt!KiBugCheckData", &BugCheckData, NULL) ||
|
|
!BugCheckData)
|
|
{
|
|
ErrOut("Unable to resolve nt!KiBugCheckData\n");
|
|
return E_NOINTERFACE;
|
|
}
|
|
}
|
|
|
|
if (m_Machine->m_Ptr64)
|
|
{
|
|
Status = ReadVirtual(m_ProcessHead, BugCheckData, Data,
|
|
sizeof(Data), &Read);
|
|
}
|
|
else
|
|
{
|
|
ULONG i;
|
|
ULONG Data32[5];
|
|
|
|
Status = ReadVirtual(m_ProcessHead, BugCheckData, Data32,
|
|
sizeof(Data32), &Read);
|
|
Read *= 2;
|
|
for (i = 0; i < DIMA(Data); i++)
|
|
{
|
|
Data[i] = EXTEND64(Data32[i]);
|
|
}
|
|
}
|
|
|
|
if (Status != S_OK || Read != sizeof(Data))
|
|
{
|
|
ErrOut("Unable to read KiBugCheckData\n");
|
|
return Status == S_OK ? E_FAIL : Status;
|
|
}
|
|
|
|
*Code = (ULONG)Data[0];
|
|
memcpy(Args, Data + 1, sizeof(Data) - sizeof(ULONG64));
|
|
return S_OK;
|
|
}
|
|
|
|
ULONG64
|
|
LiveKernelTargetInfo::GetCurrentTimeDateN(void)
|
|
{
|
|
ULONG64 TimeDate;
|
|
|
|
if (m_ActualSystemVersion > NT_SVER_START &&
|
|
m_ActualSystemVersion < NT_SVER_END &&
|
|
ReadSharedUserTimeDateN(&TimeDate) == S_OK)
|
|
{
|
|
return TimeDate;
|
|
}
|
|
else
|
|
{
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
ULONG64
|
|
LiveKernelTargetInfo::GetCurrentSystemUpTimeN(void)
|
|
{
|
|
ULONG64 UpTime;
|
|
|
|
if (m_ActualSystemVersion > NT_SVER_START &&
|
|
m_ActualSystemVersion < NT_SVER_END &&
|
|
ReadSharedUserUpTimeN(&UpTime) == S_OK)
|
|
{
|
|
return UpTime;
|
|
}
|
|
else
|
|
{
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
//
|
|
// ConnLiveKernelTargetInfo miscellaneous methods.
|
|
//
|
|
// Data space methods and system objects methods are elsewhere.
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
ConnLiveKernelTargetInfo::ConnLiveKernelTargetInfo(void)
|
|
: LiveKernelTargetInfo(DEBUG_KERNEL_CONNECTION, TRUE)
|
|
{
|
|
m_Transport = NULL;
|
|
ResetConnection();
|
|
}
|
|
|
|
ConnLiveKernelTargetInfo::~ConnLiveKernelTargetInfo(void)
|
|
{
|
|
RELEASE(m_Transport);
|
|
}
|
|
|
|
#define BUS_TYPE "_NT_DEBUG_BUS"
|
|
#define DBG_BUS1394_NAME "1394"
|
|
|
|
HRESULT
|
|
ConnLiveKernelTargetInfo::Initialize(void)
|
|
{
|
|
HRESULT Status;
|
|
DbgKdTransport* Trans = NULL;
|
|
ULONG Index;
|
|
|
|
// Try and find the transport by name.
|
|
Index = ParameterStringParser::
|
|
GetParser(m_ConnectOptions, DBGKD_TRANSPORT_COUNT,
|
|
g_DbgKdTransportNames);
|
|
if (Index < DBGKD_TRANSPORT_COUNT)
|
|
{
|
|
switch(Index)
|
|
{
|
|
case DBGKD_TRANSPORT_COM:
|
|
Trans = new DbgKdComTransport(this);
|
|
break;
|
|
case DBGKD_TRANSPORT_1394:
|
|
Trans = new DbgKd1394Transport(this);
|
|
break;
|
|
}
|
|
|
|
if (!Trans)
|
|
{
|
|
return E_OUTOFMEMORY;
|
|
}
|
|
}
|
|
|
|
if (Trans == NULL)
|
|
{
|
|
PCHAR BusType;
|
|
|
|
// Couldn't identify the transport from options so check
|
|
// the environment. Default to com port.
|
|
|
|
if (BusType = getenv(BUS_TYPE))
|
|
{
|
|
if (strstr(BusType, DBG_BUS1394_NAME))
|
|
{
|
|
Trans = new DbgKd1394Transport(this);
|
|
if (!Trans)
|
|
{
|
|
return E_OUTOFMEMORY;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!Trans)
|
|
{
|
|
Trans = new DbgKdComTransport(this);
|
|
if (!Trans)
|
|
{
|
|
return E_OUTOFMEMORY;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Clear parameter state.
|
|
Trans->ResetParameters();
|
|
|
|
if (!Trans->ParseParameters(m_ConnectOptions))
|
|
{
|
|
Status = E_INVALIDARG;
|
|
}
|
|
else
|
|
{
|
|
Status = Trans->Initialize();
|
|
if (Status != S_OK)
|
|
{
|
|
ErrOut("Kernel debugger failed initialization, %s\n \"%s\"\n",
|
|
FormatStatusCode(Status), FormatStatus(Status));
|
|
}
|
|
}
|
|
|
|
if (Status == S_OK)
|
|
{
|
|
m_Transport = Trans;
|
|
// The initial target must always be considered the
|
|
// current partition so that it can successfully
|
|
// attempt the first wait.
|
|
m_CurrentPartition = TRUE;
|
|
|
|
Status = LiveKernelTargetInfo::Initialize();
|
|
}
|
|
else
|
|
{
|
|
delete Trans;
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
HRESULT
|
|
ConnLiveKernelTargetInfo::GetDescription(PSTR Buffer, ULONG BufferLen,
|
|
PULONG DescLen)
|
|
{
|
|
HRESULT Status;
|
|
|
|
if (m_Transport)
|
|
{
|
|
char Buf[MAX_PATH];
|
|
|
|
m_Transport->GetParameters(Buf, sizeof(Buf));
|
|
Status = AppendToStringBuffer(S_OK, "Remote KD: ", TRUE,
|
|
&Buffer, &BufferLen, DescLen);
|
|
return AppendToStringBuffer(Status, Buf, FALSE,
|
|
&Buffer, &BufferLen, DescLen);
|
|
}
|
|
else
|
|
{
|
|
return FillStringBuffer("", 1,
|
|
Buffer, BufferLen, DescLen);
|
|
}
|
|
}
|
|
|
|
void
|
|
ConnLiveKernelTargetInfo::DebuggeeReset(ULONG Reason, BOOL FromEvent)
|
|
{
|
|
if (m_Transport != NULL)
|
|
{
|
|
m_Transport->Restart();
|
|
}
|
|
|
|
//
|
|
// If alternate partitions were created get rid of them.
|
|
//
|
|
|
|
TargetInfo* Target = FindTargetBySystemId(DBGKD_PARTITION_ALTERNATE);
|
|
if (Target == this)
|
|
{
|
|
Target = FindTargetBySystemId(DBGKD_PARTITION_DEFAULT);
|
|
}
|
|
delete Target;
|
|
|
|
ResetConnection();
|
|
m_CurrentPartition = TRUE;
|
|
|
|
LiveKernelTargetInfo::DebuggeeReset(Reason, FromEvent);
|
|
}
|
|
|
|
HRESULT
|
|
ConnLiveKernelTargetInfo::SwitchProcessors(ULONG Processor)
|
|
{
|
|
m_SwitchProcessor = Processor + 1;
|
|
g_CmdState = 's';
|
|
// Return S_FALSE to indicate that the switch is pending.
|
|
return S_FALSE;
|
|
}
|
|
|
|
HRESULT
|
|
ConnLiveKernelTargetInfo::SwitchToTarget(TargetInfo* From)
|
|
{
|
|
if (!IS_CONN_KERNEL_TARGET(From))
|
|
{
|
|
return E_NOTIMPL;
|
|
}
|
|
|
|
((ConnLiveKernelTargetInfo*)From)->m_SwitchTarget = this;
|
|
g_CmdState = 's';
|
|
// Return S_FALSE to indicate that the switch is pending.
|
|
return S_FALSE;
|
|
}
|
|
|
|
HRESULT
|
|
ConnLiveKernelTargetInfo::GetTargetKdVersion(PDBGKD_GET_VERSION64 Version)
|
|
{
|
|
DBGKD_MANIPULATE_STATE64 m;
|
|
PDBGKD_MANIPULATE_STATE64 Reply;
|
|
PDBGKD_GET_VERSION64 a = &m.u.GetVersion64;
|
|
ULONG rc;
|
|
|
|
m.ApiNumber = DbgKdGetVersionApi;
|
|
m.ReturnStatus = STATUS_PENDING;
|
|
a->ProtocolVersion = 1; // request context records on state changes
|
|
|
|
do
|
|
{
|
|
m_Transport->WritePacket(&m, sizeof(m),
|
|
PACKET_TYPE_KD_STATE_MANIPULATE,
|
|
NULL, 0);
|
|
rc = m_Transport->
|
|
WaitForPacket(PACKET_TYPE_KD_STATE_MANIPULATE, &Reply);
|
|
} while (rc != DBGKD_WAIT_PACKET);
|
|
|
|
*Version = Reply->u.GetVersion64;
|
|
|
|
KdOut("DbgKdGetVersion returns %08lx\n", Reply->ReturnStatus);
|
|
return CONV_NT_STATUS(Reply->ReturnStatus);
|
|
}
|
|
|
|
HRESULT
|
|
ConnLiveKernelTargetInfo::RequestBreakIn(void)
|
|
{
|
|
// Tell the waiting thread to break in.
|
|
m_Transport->m_BreakIn = TRUE;
|
|
return S_OK;
|
|
}
|
|
|
|
HRESULT
|
|
ConnLiveKernelTargetInfo::ClearBreakIn(void)
|
|
{
|
|
m_Transport->m_BreakIn = FALSE;
|
|
return S_OK;
|
|
}
|
|
|
|
HRESULT
|
|
ConnLiveKernelTargetInfo::Reboot(void)
|
|
{
|
|
DBGKD_MANIPULATE_STATE64 m;
|
|
|
|
//
|
|
// Format state manipulate message
|
|
//
|
|
|
|
m.ApiNumber = DbgKdRebootApi;
|
|
m.ReturnStatus = STATUS_PENDING;
|
|
|
|
//
|
|
// Send the message.
|
|
//
|
|
|
|
m_Transport->WritePacket(&m, sizeof(m),
|
|
PACKET_TYPE_KD_STATE_MANIPULATE,
|
|
NULL, 0);
|
|
|
|
InvalidateMemoryCaches(FALSE);
|
|
DebuggeeReset(DEBUG_SESSION_REBOOT, TRUE);
|
|
|
|
KdOut("DbgKdReboot returns 0x00000000\n");
|
|
return S_OK;
|
|
}
|
|
|
|
HRESULT
|
|
ConnLiveKernelTargetInfo::Crash(ULONG Code)
|
|
{
|
|
DBGKD_MANIPULATE_STATE64 m;
|
|
|
|
//
|
|
// Format state manipulate message
|
|
//
|
|
|
|
m.ApiNumber = DbgKdCauseBugCheckApi;
|
|
m.ReturnStatus = STATUS_PENDING;
|
|
*(PULONG)&m.u = Code;
|
|
|
|
m_Transport->WritePacket(&m, sizeof(m),
|
|
PACKET_TYPE_KD_STATE_MANIPULATE,
|
|
NULL, 0);
|
|
|
|
DiscardLastEvent();
|
|
|
|
KdOut("DbgKdCrash returns 0x00000000\n");
|
|
return S_OK;
|
|
}
|
|
|
|
void
|
|
ConnLiveKernelTargetInfo::ResetConnection(void)
|
|
{
|
|
m_CurrentPartition = FALSE;
|
|
m_SwitchTarget = NULL;
|
|
|
|
m_KdpSearchPageHits = 0;
|
|
m_KdpSearchPageHitOffsets = 0;
|
|
m_KdpSearchPageHitIndex = 0;
|
|
m_KdpSearchCheckPoint = 0;
|
|
m_KdpSearchInProgress = 0;
|
|
m_KdpSearchStartPageFrame = 0;
|
|
m_KdpSearchEndPageFrame = 0;
|
|
m_KdpSearchAddressRangeStart = 0;
|
|
m_KdpSearchAddressRangeEnd = 0;
|
|
m_KdpSearchPfnValue = 0;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
//
|
|
// LocalLiveKernelTargetInfo miscellaneous methods.
|
|
//
|
|
// Data space methods and system objects methods are elsewhere.
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
HRESULT
|
|
LocalLiveKernelTargetInfo::Initialize(void)
|
|
{
|
|
DBGKD_GET_VERSION64 Version;
|
|
|
|
// Do a quick check to see if this kernel even
|
|
// supports the necessary debug services.
|
|
if (!NT_SUCCESS(g_NtDllCalls.
|
|
NtSystemDebugControl(SysDbgQueryVersion, NULL, 0,
|
|
&Version, sizeof(Version), NULL)))
|
|
{
|
|
ErrOut("The system does not support local kernel debugging.\n");
|
|
ErrOut("Local kernel debugging requires Windows XP, Administrative\n"
|
|
"privileges, and is not supported by WOW64.\n");
|
|
return E_NOTIMPL;
|
|
}
|
|
|
|
return LiveKernelTargetInfo::Initialize();
|
|
}
|
|
|
|
HRESULT
|
|
LocalLiveKernelTargetInfo::GetDescription(PSTR Buffer, ULONG BufferLen,
|
|
PULONG DescLen)
|
|
{
|
|
return FillStringBuffer("Local KD", 0,
|
|
Buffer, BufferLen, DescLen);
|
|
}
|
|
|
|
HRESULT
|
|
LocalLiveKernelTargetInfo::GetTargetKdVersion(PDBGKD_GET_VERSION64 Version)
|
|
{
|
|
NTSTATUS Status = g_NtDllCalls.
|
|
NtSystemDebugControl(SysDbgQueryVersion, NULL, 0,
|
|
Version, sizeof(*Version), NULL);
|
|
return CONV_NT_STATUS(Status);
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
//
|
|
// ExdiLiveKernelTargetInfo miscellaneous methods.
|
|
//
|
|
// Data space methods and system objects methods are elsewhere.
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
ExdiNotifyRunChange::ExdiNotifyRunChange(void)
|
|
{
|
|
m_Event = NULL;
|
|
}
|
|
|
|
ExdiNotifyRunChange::~ExdiNotifyRunChange(void)
|
|
{
|
|
Uninitialize();
|
|
}
|
|
|
|
HRESULT
|
|
ExdiNotifyRunChange::Initialize(void)
|
|
{
|
|
m_Event = CreateEvent(NULL, FALSE, FALSE, NULL);
|
|
if (m_Event == NULL)
|
|
{
|
|
return WIN32_LAST_STATUS();
|
|
}
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
void
|
|
ExdiNotifyRunChange::Uninitialize(void)
|
|
{
|
|
if (m_Event != NULL)
|
|
{
|
|
CloseHandle(m_Event);
|
|
m_Event = NULL;
|
|
}
|
|
}
|
|
|
|
STDMETHODIMP
|
|
ExdiNotifyRunChange::QueryInterface(
|
|
THIS_
|
|
IN REFIID InterfaceId,
|
|
OUT PVOID* Interface
|
|
)
|
|
{
|
|
if (DbgIsEqualIID(IID_IUnknown, InterfaceId) ||
|
|
DbgIsEqualIID(__uuidof(IeXdiClientNotifyRunChg), InterfaceId))
|
|
{
|
|
*Interface = this;
|
|
return S_OK;
|
|
}
|
|
|
|
*Interface = NULL;
|
|
return E_NOINTERFACE;
|
|
}
|
|
|
|
STDMETHODIMP_(ULONG)
|
|
ExdiNotifyRunChange::AddRef(
|
|
THIS
|
|
)
|
|
{
|
|
return 1;
|
|
}
|
|
|
|
STDMETHODIMP_(ULONG)
|
|
ExdiNotifyRunChange::Release(
|
|
THIS
|
|
)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
STDMETHODIMP
|
|
ExdiNotifyRunChange::NotifyRunStateChange(RUN_STATUS_TYPE ersCurrent,
|
|
HALT_REASON_TYPE ehrCurrent,
|
|
ADDRESS_TYPE CurrentExecAddress,
|
|
DWORD dwExceptionCode)
|
|
{
|
|
if (ersCurrent == rsRunning)
|
|
{
|
|
// We're waiting for things to stop so ignore this.
|
|
return S_OK;
|
|
}
|
|
|
|
m_HaltReason = ehrCurrent;
|
|
m_ExecAddress = CurrentExecAddress;
|
|
m_ExceptionCode = dwExceptionCode;
|
|
SetEvent(m_Event);
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
class ExdiParams : public ParameterStringParser
|
|
{
|
|
public:
|
|
virtual ULONG GetNumberParameters(void)
|
|
{
|
|
// No need to get.
|
|
return 0;
|
|
}
|
|
virtual void GetParameter(ULONG Index,
|
|
PSTR Name, ULONG NameSize,
|
|
PSTR Value, ULONG ValueSize)
|
|
{
|
|
}
|
|
|
|
virtual void ResetParameters(void);
|
|
virtual BOOL SetParameter(PCSTR Name, PCSTR Value);
|
|
|
|
CLSID m_Clsid;
|
|
EXDI_KD_SUPPORT m_KdSupport;
|
|
BOOL m_ForceX86;
|
|
BOOL m_ExdiDataBreaks;
|
|
};
|
|
|
|
void
|
|
ExdiParams::ResetParameters(void)
|
|
{
|
|
ZeroMemory(&m_Clsid, sizeof(m_Clsid));
|
|
m_KdSupport = EXDI_KD_NONE;
|
|
m_ForceX86 = FALSE;
|
|
m_ExdiDataBreaks = FALSE;
|
|
}
|
|
|
|
BOOL
|
|
ScanExdiDriverList(PCSTR Name, LPCLSID Clsid)
|
|
{
|
|
char Pattern[MAX_PARAM_VALUE];
|
|
|
|
CopyString(Pattern, Name, DIMA(Pattern));
|
|
_strupr(Pattern);
|
|
|
|
HKEY ListKey;
|
|
|
|
if (RegOpenKeyEx(HKEY_LOCAL_MACHINE,
|
|
"Software\\Microsoft\\eXdi\\DriverList", 0,
|
|
KEY_ALL_ACCESS, &ListKey) != ERROR_SUCCESS)
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
ULONG Index = 0;
|
|
BOOL Status = FALSE;
|
|
char ValName[MAX_PARAM_VALUE];
|
|
WCHAR WideValName[MAX_PARAM_VALUE];
|
|
ULONG NameLen, ValLen;
|
|
ULONG Type;
|
|
char Value[MAX_PARAM_VALUE];
|
|
|
|
for (;;)
|
|
{
|
|
NameLen = sizeof(ValName);
|
|
ValLen = sizeof(Value);
|
|
if (RegEnumValue(ListKey, Index, ValName, &NameLen, NULL,
|
|
&Type, (PBYTE)Value, &ValLen) != ERROR_SUCCESS)
|
|
{
|
|
break;
|
|
}
|
|
|
|
if (Type == REG_SZ &&
|
|
MatchPattern(Value, Pattern) &&
|
|
MultiByteToWideChar(CP_ACP, 0, ValName, -1, WideValName,
|
|
sizeof(WideValName) / sizeof(WCHAR)) > 0 &&
|
|
g_Ole32Calls.CLSIDFromString(WideValName, Clsid) == S_OK)
|
|
{
|
|
Status = TRUE;
|
|
break;
|
|
}
|
|
|
|
Index++;
|
|
}
|
|
|
|
RegCloseKey(ListKey);
|
|
return Status;
|
|
}
|
|
|
|
BOOL
|
|
ExdiParams::SetParameter(PCSTR Name, PCSTR Value)
|
|
{
|
|
if (!_strcmpi(Name, "CLSID"))
|
|
{
|
|
WCHAR WideValue[MAX_PARAM_VALUE];
|
|
|
|
if (MultiByteToWideChar(CP_ACP, 0, Value, -1, WideValue,
|
|
sizeof(WideValue) / sizeof(WCHAR)) == 0)
|
|
{
|
|
return FALSE;
|
|
}
|
|
return g_Ole32Calls.CLSIDFromString(WideValue, &m_Clsid) == S_OK;
|
|
}
|
|
else if (!_strcmpi(Name, "Desc"))
|
|
{
|
|
return ScanExdiDriverList(Value, &m_Clsid);
|
|
}
|
|
else if (!_strcmpi(Name, "DataBreaks"))
|
|
{
|
|
if (!Value)
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
if (!_strcmpi(Value, "Exdi"))
|
|
{
|
|
m_ExdiDataBreaks = TRUE;
|
|
}
|
|
else if (!_strcmpi(Value, "Default"))
|
|
{
|
|
m_ExdiDataBreaks = FALSE;
|
|
}
|
|
else
|
|
{
|
|
return FALSE;
|
|
}
|
|
}
|
|
else if (!_strcmpi(Name, "ForceX86"))
|
|
{
|
|
m_ForceX86 = TRUE;
|
|
}
|
|
else if (!_strcmpi(Name, "Kd"))
|
|
{
|
|
if (!Value)
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
if (!_strcmpi(Value, "Ioctl"))
|
|
{
|
|
m_KdSupport = EXDI_KD_IOCTL;
|
|
}
|
|
else if (!_strcmpi(Value, "GsPcr"))
|
|
{
|
|
m_KdSupport = EXDI_KD_GS_PCR;
|
|
}
|
|
else
|
|
{
|
|
return FALSE;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
PCSTR g_ExdiGroupNames[] =
|
|
{
|
|
"exdi",
|
|
};
|
|
|
|
ExdiLiveKernelTargetInfo::ExdiLiveKernelTargetInfo(void)
|
|
: LiveKernelTargetInfo(DEBUG_KERNEL_EXDI_DRIVER, TRUE)
|
|
{
|
|
m_Server = NULL;
|
|
m_MarshalledServer = NULL;
|
|
m_Context = NULL;
|
|
m_ContextValid = FALSE;
|
|
m_IoctlMin = DBGENG_EXDI_IOC_BEFORE_FIRST;
|
|
m_IoctlMax = DBGENG_EXDI_IOC_BEFORE_FIRST;
|
|
m_BpHit.Type = DBGENG_EXDI_IOCTL_BREAKPOINT_NONE;
|
|
}
|
|
|
|
ExdiLiveKernelTargetInfo::~ExdiLiveKernelTargetInfo(void)
|
|
{
|
|
m_RunChange.Uninitialize();
|
|
RELEASE(m_Context);
|
|
RELEASE(m_MarshalledServer);
|
|
RELEASE(m_Server);
|
|
g_Ole32Calls.CoUninitialize();
|
|
}
|
|
|
|
HRESULT
|
|
ExdiLiveKernelTargetInfo::Initialize(void)
|
|
{
|
|
HRESULT Status;
|
|
|
|
// Load ole32.dll so we can call CoCreateInstance.
|
|
if ((Status = InitDynamicCalls(&g_Ole32CallsDesc)) != S_OK)
|
|
{
|
|
return Status;
|
|
}
|
|
|
|
ULONG Group;
|
|
|
|
Group = ParameterStringParser::
|
|
GetParser(m_ConnectOptions, DIMA(g_ExdiGroupNames), g_ExdiGroupNames);
|
|
if (Group == PARSER_INVALID)
|
|
{
|
|
return E_INVALIDARG;
|
|
}
|
|
|
|
ExdiParams Params;
|
|
|
|
Params.ResetParameters();
|
|
if (!Params.ParseParameters(m_ConnectOptions))
|
|
{
|
|
return E_INVALIDARG;
|
|
}
|
|
|
|
m_KdSupport = Params.m_KdSupport;
|
|
m_ExdiDataBreaks = Params.m_ExdiDataBreaks;
|
|
|
|
if (FAILED(Status = g_Ole32Calls.CoInitializeEx(NULL, COM_THREAD_MODEL)))
|
|
{
|
|
return Status;
|
|
}
|
|
if ((Status = g_Ole32Calls.CoCreateInstance(Params.m_Clsid, NULL,
|
|
CLSCTX_LOCAL_SERVER,
|
|
__uuidof(IeXdiServer),
|
|
(PVOID*)&m_Server)) != S_OK)
|
|
{
|
|
goto EH_CoInit;
|
|
}
|
|
|
|
if ((Status = g_Ole32Calls.CoMarshalInterThreadInterfaceInStream
|
|
(__uuidof(IeXdiServer), m_Server, &m_MarshalledServer)) != S_OK)
|
|
{
|
|
goto EH_Server;
|
|
}
|
|
|
|
if ((Status = m_Server->GetTargetInfo(&m_GlobalInfo)) != S_OK)
|
|
{
|
|
goto EH_MarshalledServer;
|
|
}
|
|
|
|
if (Params.m_ForceX86 ||
|
|
m_GlobalInfo.TargetProcessorFamily == PROCESSOR_FAMILY_X86)
|
|
{
|
|
if (!Params.m_ForceX86 &&
|
|
(Status = m_Server->
|
|
QueryInterface(__uuidof(IeXdiX86_64Context),
|
|
(PVOID*)&m_Context)) == S_OK)
|
|
{
|
|
m_ContextType = EXDI_CTX_AMD64;
|
|
m_ExpectedMachine = IMAGE_FILE_MACHINE_AMD64;
|
|
}
|
|
else if ((Status = m_Server->
|
|
QueryInterface(__uuidof(IeXdiX86ExContext),
|
|
(PVOID*)&m_Context)) == S_OK)
|
|
{
|
|
m_ContextType = EXDI_CTX_X86_EX;
|
|
m_ExpectedMachine = IMAGE_FILE_MACHINE_I386;
|
|
}
|
|
else if ((Status = m_Server->
|
|
QueryInterface(__uuidof(IeXdiX86Context),
|
|
(PVOID*)&m_Context)) == S_OK)
|
|
{
|
|
m_ContextType = EXDI_CTX_X86;
|
|
m_ExpectedMachine = IMAGE_FILE_MACHINE_I386;
|
|
}
|
|
else
|
|
{
|
|
goto EH_MarshalledServer;
|
|
}
|
|
}
|
|
else if (m_GlobalInfo.TargetProcessorFamily == PROCESSOR_FAMILY_IA64)
|
|
{
|
|
if ((Status = m_Server->
|
|
QueryInterface(__uuidof(IeXdiIA64Context),
|
|
(PVOID*)&m_Context)) == S_OK)
|
|
{
|
|
m_ContextType = EXDI_CTX_IA64;
|
|
m_ExpectedMachine = IMAGE_FILE_MACHINE_IA64;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
Status = E_NOINTERFACE;
|
|
goto EH_MarshalledServer;
|
|
}
|
|
|
|
DWORD HwCode, SwCode;
|
|
|
|
if ((Status = m_Server->GetNbCodeBpAvail(&HwCode, &SwCode)) != S_OK)
|
|
{
|
|
goto EH_Context;
|
|
}
|
|
|
|
// We'd prefer to use software code breakpoints for our
|
|
// software code breakpoints so that hardware resources
|
|
// aren't consumed for a breakpoint we don't need to
|
|
// use hardware for. However, some servers, such as
|
|
// the x86-64 SimNow implementation, do not support
|
|
// software breakpoints.
|
|
// Also, if the number of hardware breakpoints is
|
|
// unbounded, go ahead and let the server choose.
|
|
// SimNow advertises -1 -1 for some reason and
|
|
// this is necessary to get things to work.
|
|
|
|
if (SwCode > 0 && HwCode != (DWORD)-1)
|
|
{
|
|
m_CodeBpType = cbptSW;
|
|
}
|
|
else
|
|
{
|
|
m_CodeBpType = cbptAlgo;
|
|
}
|
|
|
|
if ((Status = m_RunChange.Initialize()) != S_OK)
|
|
{
|
|
goto EH_Context;
|
|
}
|
|
|
|
if ((Status = LiveKernelTargetInfo::Initialize()) != S_OK)
|
|
{
|
|
goto EH_RunChange;
|
|
}
|
|
|
|
//
|
|
// Check and see if this EXDI implementation supports
|
|
// the extended Ioctl's we've defined.
|
|
//
|
|
|
|
DBGENG_EXDI_IOCTL_BASE_IN IoctlIn;
|
|
DBGENG_EXDI_IOCTL_IDENTIFY_OUT IoctlOut;
|
|
ULONG OutUsed;
|
|
|
|
IoctlIn.Code = DBGENG_EXDI_IOC_IDENTIFY;
|
|
if (m_Server->
|
|
Ioctl(sizeof(IoctlIn), (PBYTE)&IoctlIn,
|
|
sizeof(IoctlOut), &OutUsed, (PBYTE)&IoctlOut) == S_OK &&
|
|
IoctlOut.Signature == DBGENG_EXDI_IOCTL_IDENTIFY_SIGNATURE)
|
|
{
|
|
m_IoctlMin = IoctlOut.BeforeFirst;
|
|
m_IoctlMax = IoctlOut.AfterLast;
|
|
|
|
if (DBGENG_EXDI_IOC_GET_BREAKPOINT_HIT <= m_IoctlMin ||
|
|
DBGENG_EXDI_IOC_GET_BREAKPOINT_HIT >= m_IoctlMax)
|
|
{
|
|
// Can't use EXDI data breakpoints without this ioctl.
|
|
WarnOut("EXDI data breakpoints not supported\n");
|
|
m_ExdiDataBreaks = FALSE;
|
|
}
|
|
}
|
|
|
|
m_ContextValid = FALSE;
|
|
return S_OK;
|
|
|
|
EH_RunChange:
|
|
m_RunChange.Uninitialize();
|
|
EH_Context:
|
|
RELEASE(m_Context);
|
|
EH_MarshalledServer:
|
|
RELEASE(m_MarshalledServer);
|
|
EH_Server:
|
|
RELEASE(m_Server);
|
|
EH_CoInit:
|
|
g_Ole32Calls.CoUninitialize();
|
|
return Status;
|
|
}
|
|
|
|
HRESULT
|
|
ExdiLiveKernelTargetInfo::GetDescription(PSTR Buffer, ULONG BufferLen,
|
|
PULONG DescLen)
|
|
{
|
|
return FillStringBuffer("eXDI KD", 0,
|
|
Buffer, BufferLen, DescLen);
|
|
}
|
|
|
|
HRESULT
|
|
ExdiLiveKernelTargetInfo::SwitchProcessors(ULONG Processor)
|
|
{
|
|
HRESULT Status;
|
|
|
|
if (DBGENG_EXDI_IOC_SET_CURRENT_PROCESSOR <= m_IoctlMin ||
|
|
DBGENG_EXDI_IOC_SET_CURRENT_PROCESSOR >= m_IoctlMax)
|
|
{
|
|
// Switch Ioctl not supported.
|
|
return E_NOTIMPL;
|
|
}
|
|
|
|
DBGENG_EXDI_IOCTL_SET_CURRENT_PROCESSOR_IN IoctlIn;
|
|
ULONG OutUsed;
|
|
|
|
IoctlIn.Code = DBGENG_EXDI_IOC_SET_CURRENT_PROCESSOR;
|
|
IoctlIn.Processor = Processor;
|
|
if ((Status = m_Server->Ioctl(sizeof(IoctlIn), (PBYTE)&IoctlIn,
|
|
0, &OutUsed, (PBYTE)&IoctlIn)) != S_OK)
|
|
{
|
|
ErrOut("Unable to switch processors, %s\n",
|
|
FormatStatusCode(Status));
|
|
return Status;
|
|
}
|
|
|
|
SetCurrentProcessorThread(this, Processor, FALSE);
|
|
return S_OK;
|
|
}
|
|
|
|
#define EXDI_IOCTL_GET_KD_VERSION ((ULONG)'VDKG')
|
|
|
|
HRESULT
|
|
ExdiLiveKernelTargetInfo::GetTargetKdVersion(PDBGKD_GET_VERSION64 Version)
|
|
{
|
|
switch(m_KdSupport)
|
|
{
|
|
case EXDI_KD_IOCTL:
|
|
//
|
|
// User has indicated the target supports the
|
|
// KD version ioctl.
|
|
//
|
|
|
|
ULONG Command;
|
|
ULONG Retrieved;
|
|
HRESULT Status;
|
|
|
|
Command = EXDI_IOCTL_GET_KD_VERSION;
|
|
if ((Status = m_Server->Ioctl(sizeof(Command), (PBYTE)&Command,
|
|
sizeof(*Version), &Retrieved,
|
|
(PBYTE)Version)) != S_OK)
|
|
{
|
|
return Status;
|
|
}
|
|
if (Retrieved != sizeof(*Version))
|
|
{
|
|
return E_FAIL;
|
|
}
|
|
|
|
// This mode implies a recent kernel so we can
|
|
// assume 64-bit kd.
|
|
m_KdApi64 = TRUE;
|
|
break;
|
|
|
|
case EXDI_KD_GS_PCR:
|
|
//
|
|
// User has indicated that a version of NT
|
|
// is running and that the PCR can be accessed
|
|
// through GS. Look up the version block from
|
|
// the PCR.
|
|
//
|
|
|
|
if (m_ExpectedMachine == IMAGE_FILE_MACHINE_AMD64)
|
|
{
|
|
ULONG64 KdVer;
|
|
ULONG Done;
|
|
|
|
if ((Status = Amd64MachineInfo::
|
|
StaticGetExdiContext(m_Context, &m_ContextData,
|
|
m_ContextType)) != S_OK)
|
|
{
|
|
return Status;
|
|
}
|
|
if ((Status = m_Server->
|
|
ReadVirtualMemory(m_ContextData.Amd64Context.
|
|
DescriptorGs.SegBase +
|
|
AMD64_KPCR_KD_VERSION_BLOCK,
|
|
sizeof(KdVer), 8, (PBYTE)&KdVer,
|
|
&Done)) != S_OK)
|
|
{
|
|
return Status;
|
|
}
|
|
if (Done != sizeof(KdVer))
|
|
{
|
|
return HRESULT_FROM_WIN32(ERROR_READ_FAULT);
|
|
}
|
|
if ((Status = m_Server->
|
|
ReadVirtualMemory(KdVer, sizeof(*Version), 8, (PBYTE)Version,
|
|
&Done)) != S_OK)
|
|
{
|
|
return Status;
|
|
}
|
|
if (Done != sizeof(*Version))
|
|
{
|
|
return HRESULT_FROM_WIN32(ERROR_READ_FAULT);
|
|
}
|
|
|
|
// This mode implies a recent kernel so we can
|
|
// assume 64-bit kd.
|
|
m_KdApi64 = TRUE;
|
|
|
|
// Update the version block's Simulation field to
|
|
// indicate that this is a simulated execution.
|
|
Version->Simulation = DBGKD_SIMULATION_EXDI;
|
|
if ((Status = m_Server->
|
|
WriteVirtualMemory(KdVer, sizeof(*Version), 8, (PBYTE)Version,
|
|
&Done)) != S_OK)
|
|
{
|
|
return Status;
|
|
}
|
|
if (Done != sizeof(*Version))
|
|
{
|
|
return HRESULT_FROM_WIN32(ERROR_WRITE_FAULT);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
return E_INVALIDARG;
|
|
}
|
|
break;
|
|
|
|
case EXDI_KD_NONE:
|
|
//
|
|
// Fake up a version structure.
|
|
//
|
|
|
|
Version->MajorVersion = DBGKD_MAJOR_EXDI << 8;
|
|
Version->ProtocolVersion = 0;
|
|
Version->Flags = DBGKD_VERS_FLAG_PTR64 | DBGKD_VERS_FLAG_NOMM;
|
|
Version->MachineType = (USHORT)m_ExpectedMachine;
|
|
Version->KernBase = 0;
|
|
Version->PsLoadedModuleList = 0;
|
|
Version->DebuggerDataList = 0;
|
|
break;
|
|
}
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
HRESULT
|
|
ExdiLiveKernelTargetInfo::RequestBreakIn(void)
|
|
{
|
|
//
|
|
// m_Server was created by the session thread but
|
|
// RequestBreakIn can be called from any thread.
|
|
// The thread may not be initialized for multithreading
|
|
// and so we have to explicitly unmarshal the server
|
|
// interface into this thread to make sure that
|
|
// the method call will be successful regardless of
|
|
// the COM threading model for the current thread.
|
|
//
|
|
|
|
if (GetCurrentThreadId() == g_SessionThread)
|
|
{
|
|
return m_Server->Halt();
|
|
}
|
|
else
|
|
{
|
|
HRESULT Status;
|
|
IeXdiServer* Server;
|
|
LARGE_INTEGER Move;
|
|
|
|
ZeroMemory(&Move, sizeof(Move));
|
|
if ((Status = m_MarshalledServer->
|
|
Seek(Move, STREAM_SEEK_SET, NULL)) != S_OK ||
|
|
(Status = g_Ole32Calls.CoUnmarshalInterface
|
|
(m_MarshalledServer, __uuidof(IeXdiServer),
|
|
(void **)&Server)) != S_OK)
|
|
{
|
|
return Status;
|
|
}
|
|
|
|
Status = Server->Halt();
|
|
|
|
Server->Release();
|
|
return Status;
|
|
}
|
|
}
|
|
|
|
HRESULT
|
|
ExdiLiveKernelTargetInfo::Reboot(void)
|
|
{
|
|
HRESULT Status = m_Server->Reboot();
|
|
if (Status == S_OK)
|
|
{
|
|
DebuggeeReset(DEBUG_SESSION_REBOOT, TRUE);
|
|
}
|
|
return Status;
|
|
}
|
|
|
|
ULONG
|
|
ExdiLiveKernelTargetInfo::GetCurrentProcessor(void)
|
|
{
|
|
if (DBGENG_EXDI_IOC_GET_CURRENT_PROCESSOR <= m_IoctlMin ||
|
|
DBGENG_EXDI_IOC_GET_CURRENT_PROCESSOR >= m_IoctlMax)
|
|
{
|
|
// Ioctl unsupported so assume processor zero.
|
|
return 0;
|
|
}
|
|
|
|
DBGENG_EXDI_IOCTL_BASE_IN IoctlIn;
|
|
DBGENG_EXDI_IOCTL_GET_CURRENT_PROCESSOR_OUT IoctlOut;
|
|
ULONG OutUsed;
|
|
|
|
IoctlIn.Code = DBGENG_EXDI_IOC_GET_CURRENT_PROCESSOR;
|
|
if (m_Server->
|
|
Ioctl(sizeof(IoctlIn), (PBYTE)&IoctlIn,
|
|
sizeof(IoctlOut), &OutUsed, (PBYTE)&IoctlOut) == S_OK)
|
|
{
|
|
return IoctlOut.Processor;
|
|
}
|
|
|
|
// Failure, assume processor zero.
|
|
ErrOut("Unable to get current processor\n");
|
|
return 0;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
//
|
|
// UserTargetInfo miscellaneous methods.
|
|
//
|
|
// Data space methods and system objects methods are elsewhere.
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
LiveUserTargetInfo::LiveUserTargetInfo(ULONG Qual)
|
|
: TargetInfo(DEBUG_CLASS_USER_WINDOWS, Qual, TRUE)
|
|
{
|
|
m_Services = NULL;
|
|
m_ServiceFlags = 0;
|
|
strcpy(m_ProcessServer, "<Local>");
|
|
m_Local = TRUE;
|
|
m_DataBpAddrValid = FALSE;
|
|
m_ProcessPending = NULL;
|
|
m_AllPendingFlags = 0;
|
|
}
|
|
|
|
LiveUserTargetInfo::~LiveUserTargetInfo(void)
|
|
{
|
|
// Force processes and threads to get cleaned up while
|
|
// the services are still available to close handles.
|
|
DeleteSystemInfo();
|
|
|
|
RELEASE(m_Services);
|
|
}
|
|
|
|
HRESULT
|
|
LiveUserTargetInfo::Initialize(void)
|
|
{
|
|
// Nothing to do right now.
|
|
return TargetInfo::Initialize();
|
|
}
|
|
|
|
HRESULT
|
|
LiveUserTargetInfo::GetDescription(PSTR Buffer, ULONG BufferLen,
|
|
PULONG DescLen)
|
|
{
|
|
HRESULT Status;
|
|
|
|
Status = AppendToStringBuffer(S_OK, "Live user mode", TRUE,
|
|
&Buffer, &BufferLen, DescLen);
|
|
Status = AppendToStringBuffer(Status, ": ", FALSE,
|
|
&Buffer, &BufferLen, DescLen);
|
|
Status = AppendToStringBuffer(Status, m_ProcessServer, FALSE,
|
|
&Buffer, &BufferLen, DescLen);
|
|
return Status;
|
|
}
|
|
|
|
HRESULT
|
|
LiveUserTargetInfo::GetImageVersionInformation(ProcessInfo* Process,
|
|
PCSTR ImagePath,
|
|
ULONG64 ImageBase,
|
|
PCSTR Item,
|
|
PVOID Buffer,
|
|
ULONG BufferSize,
|
|
PULONG VerInfoSize)
|
|
{
|
|
HRESULT Status;
|
|
PWSTR FileW;
|
|
|
|
if ((Status = AnsiToWide(ImagePath, &FileW)) != S_OK)
|
|
{
|
|
return Status;
|
|
}
|
|
|
|
Status = m_Services->
|
|
GetFileVersionInformationA(FileW, Item,
|
|
Buffer, BufferSize, VerInfoSize);
|
|
|
|
FreeWide(FileW);
|
|
return Status;
|
|
}
|
|
|
|
ULONG64
|
|
LiveUserTargetInfo::GetCurrentTimeDateN(void)
|
|
{
|
|
ULONG64 TimeDate;
|
|
|
|
if (m_Services->GetCurrentTimeDateN(&TimeDate) == S_OK)
|
|
{
|
|
return TimeDate;
|
|
}
|
|
else
|
|
{
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
ULONG64
|
|
LiveUserTargetInfo::GetCurrentSystemUpTimeN(void)
|
|
{
|
|
ULONG64 UpTime;
|
|
|
|
if (m_Services->GetCurrentSystemUpTimeN(&UpTime) == S_OK)
|
|
{
|
|
return UpTime;
|
|
}
|
|
else
|
|
{
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
ULONG64
|
|
LiveUserTargetInfo::GetProcessUpTimeN(ProcessInfo* Process)
|
|
{
|
|
ULONG64 UpTime;
|
|
|
|
if (Process &&
|
|
m_Services->GetProcessUpTimeN(Process->m_SysHandle, &UpTime) == S_OK)
|
|
{
|
|
return UpTime;
|
|
}
|
|
else
|
|
{
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
HRESULT
|
|
LiveUserTargetInfo::GetProcessTimes(ProcessInfo* Process,
|
|
PULONG64 Create,
|
|
PULONG64 Exit,
|
|
PULONG64 Kernel,
|
|
PULONG64 User)
|
|
{
|
|
return m_Services->GetProcessTimes(Process->m_SysHandle,
|
|
Create, Exit, Kernel, User);
|
|
}
|
|
|
|
HRESULT
|
|
LiveUserTargetInfo::GetThreadTimes(ThreadInfo* Thread,
|
|
PULONG64 Create,
|
|
PULONG64 Exit,
|
|
PULONG64 Kernel,
|
|
PULONG64 User)
|
|
{
|
|
return m_Services->GetThreadTimes(Thread->m_Handle,
|
|
Create, Exit, Kernel, User);
|
|
}
|
|
|
|
HRESULT
|
|
LiveUserTargetInfo::RequestBreakIn(void)
|
|
{
|
|
ProcessInfo* Process = g_Process;
|
|
|
|
if (!Process)
|
|
{
|
|
// No current process, so find any process.
|
|
Process = m_ProcessHead;
|
|
if (!Process)
|
|
{
|
|
return E_UNEXPECTED;
|
|
}
|
|
}
|
|
|
|
return m_Services->
|
|
RequestBreakIn(Process->m_SysHandle);
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
//
|
|
// Base TargetInfo methods that trivially fail.
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
#define UNEXPECTED_VOID(Class, Method, Args) \
|
|
void \
|
|
Class::Method Args \
|
|
{ \
|
|
ErrOut("TargetInfo::" #Method " is not available in the current debug session\n"); \
|
|
}
|
|
|
|
#define UNEXPECTED_HR(Class, Method, Args) \
|
|
HRESULT \
|
|
Class::Method Args \
|
|
{ \
|
|
ErrOut("TargetInfo::" #Method " is not available in the current debug session\n"); \
|
|
return E_UNEXPECTED; \
|
|
}
|
|
|
|
#define UNEXPECTED_ULONG64(Class, Method, Val, Args) \
|
|
ULONG64 \
|
|
Class::Method Args \
|
|
{ \
|
|
ErrOut("TargetInfo::" #Method " is not available in the current debug session\n"); \
|
|
return Val; \
|
|
}
|
|
|
|
UNEXPECTED_HR(TargetInfo, ReadVirtual, (
|
|
IN ProcessInfo* Process,
|
|
IN ULONG64 Offset,
|
|
OUT PVOID Buffer,
|
|
IN ULONG BufferSize,
|
|
OUT OPTIONAL PULONG BytesRead
|
|
))
|
|
UNEXPECTED_HR(TargetInfo, WriteVirtual, (
|
|
IN ProcessInfo* Process,
|
|
IN ULONG64 Offset,
|
|
IN PVOID Buffer,
|
|
IN ULONG BufferSize,
|
|
OUT OPTIONAL PULONG BytesWritten
|
|
))
|
|
UNEXPECTED_HR(TargetInfo, ReadPhysical, (
|
|
IN ULONG64 Offset,
|
|
OUT PVOID Buffer,
|
|
IN ULONG BufferSize,
|
|
IN ULONG Flags,
|
|
OUT OPTIONAL PULONG BytesRead
|
|
))
|
|
UNEXPECTED_HR(TargetInfo, WritePhysical, (
|
|
IN ULONG64 Offset,
|
|
IN PVOID Buffer,
|
|
IN ULONG BufferSize,
|
|
IN ULONG Flags,
|
|
OUT OPTIONAL PULONG BytesWritten
|
|
))
|
|
UNEXPECTED_HR(TargetInfo, ReadControl, (
|
|
IN ULONG Processor,
|
|
IN ULONG64 Offset,
|
|
OUT PVOID Buffer,
|
|
IN ULONG BufferSize,
|
|
OUT OPTIONAL PULONG BytesRead
|
|
))
|
|
UNEXPECTED_HR(TargetInfo, WriteControl, (
|
|
IN ULONG Processor,
|
|
IN ULONG64 Offset,
|
|
IN PVOID Buffer,
|
|
IN ULONG BufferSize,
|
|
OUT OPTIONAL PULONG BytesWritten
|
|
))
|
|
UNEXPECTED_HR(TargetInfo, ReadIo, (
|
|
IN ULONG InterfaceType,
|
|
IN ULONG BusNumber,
|
|
IN ULONG AddressSpace,
|
|
IN ULONG64 Offset,
|
|
OUT PVOID Buffer,
|
|
IN ULONG BufferSize,
|
|
OUT OPTIONAL PULONG BytesRead
|
|
))
|
|
UNEXPECTED_HR(TargetInfo, WriteIo, (
|
|
IN ULONG InterfaceType,
|
|
IN ULONG BusNumber,
|
|
IN ULONG AddressSpace,
|
|
IN ULONG64 Offset,
|
|
IN PVOID Buffer,
|
|
IN ULONG BufferSize,
|
|
OUT OPTIONAL PULONG BytesWritten
|
|
))
|
|
UNEXPECTED_HR(TargetInfo, ReadMsr, (
|
|
IN ULONG Msr,
|
|
OUT PULONG64 Value
|
|
))
|
|
UNEXPECTED_HR(TargetInfo, WriteMsr, (
|
|
IN ULONG Msr,
|
|
IN ULONG64 Value
|
|
))
|
|
UNEXPECTED_HR(TargetInfo, ReadBusData, (
|
|
IN ULONG BusDataType,
|
|
IN ULONG BusNumber,
|
|
IN ULONG SlotNumber,
|
|
IN ULONG Offset,
|
|
OUT PVOID Buffer,
|
|
IN ULONG BufferSize,
|
|
OUT OPTIONAL PULONG BytesRead
|
|
))
|
|
UNEXPECTED_HR(TargetInfo, WriteBusData, (
|
|
IN ULONG BusDataType,
|
|
IN ULONG BusNumber,
|
|
IN ULONG SlotNumber,
|
|
IN ULONG Offset,
|
|
IN PVOID Buffer,
|
|
IN ULONG BufferSize,
|
|
OUT OPTIONAL PULONG BytesWritten
|
|
))
|
|
UNEXPECTED_HR(TargetInfo, CheckLowMemory, (
|
|
))
|
|
UNEXPECTED_HR(TargetInfo, GetTargetContext, (
|
|
ULONG64 Thread,
|
|
PVOID Context
|
|
))
|
|
UNEXPECTED_HR(TargetInfo, SetTargetContext, (
|
|
ULONG64 Thread,
|
|
PVOID Context
|
|
))
|
|
UNEXPECTED_HR(TargetInfo, GetThreadIdByProcessor, (
|
|
IN ULONG Processor,
|
|
OUT PULONG Id
|
|
))
|
|
UNEXPECTED_HR(TargetInfo, GetThreadInfoDataOffset, (
|
|
ThreadInfo* Thread,
|
|
ULONG64 ThreadHandle,
|
|
PULONG64 Offset))
|
|
UNEXPECTED_HR(TargetInfo, GetProcessInfoDataOffset, (
|
|
ThreadInfo* Thread,
|
|
ULONG Processor,
|
|
ULONG64 ThreadData,
|
|
PULONG64 Offset))
|
|
UNEXPECTED_HR(TargetInfo, GetThreadInfoTeb, (
|
|
ThreadInfo* Thread,
|
|
ULONG Processor,
|
|
ULONG64 ThreadData,
|
|
PULONG64 Offset))
|
|
UNEXPECTED_HR(TargetInfo, GetProcessInfoPeb, (
|
|
ThreadInfo* Thread,
|
|
ULONG Processor,
|
|
ULONG64 ThreadData,
|
|
PULONG64 Offset))
|
|
UNEXPECTED_HR(TargetInfo, GetSelDescriptor, (
|
|
ThreadInfo* Thread,
|
|
MachineInfo* Machine,
|
|
ULONG Selector,
|
|
PDESCRIPTOR64 Desc))
|
|
UNEXPECTED_HR(TargetInfo, SwitchProcessors, (
|
|
ULONG Processor))
|
|
UNEXPECTED_HR(TargetInfo, GetTargetKdVersion, (
|
|
PDBGKD_GET_VERSION64 Version))
|
|
UNEXPECTED_HR(TargetInfo, ReadBugCheckData, (
|
|
PULONG Code, ULONG64 Args[4]))
|
|
UNEXPECTED_HR(TargetInfo, GetExceptionContext, (
|
|
PCROSS_PLATFORM_CONTEXT Context))
|
|
UNEXPECTED_VOID(TargetInfo, InitializeWatchTrace, (
|
|
void))
|
|
UNEXPECTED_VOID(TargetInfo, ProcessWatchTraceEvent, (
|
|
PDBGKD_TRACE_DATA TraceData,
|
|
PADDR PcAddr,
|
|
PBOOL StepOver))
|
|
UNEXPECTED_HR(TargetInfo, WaitForEvent, (
|
|
ULONG Flags,
|
|
ULONG Timeout,
|
|
ULONG ElapsedTime,
|
|
PULONG EventStatus))
|
|
UNEXPECTED_HR(TargetInfo, RequestBreakIn, (void))
|
|
UNEXPECTED_HR(TargetInfo, Reboot, (void))
|
|
UNEXPECTED_HR(TargetInfo, Crash, (ULONG Code))
|
|
UNEXPECTED_HR(TargetInfo, InsertCodeBreakpoint, (
|
|
ProcessInfo* Process,
|
|
MachineInfo* Machine,
|
|
PADDR Addr,
|
|
ULONG InstrFlags,
|
|
PUCHAR StorageSpace))
|
|
UNEXPECTED_HR(TargetInfo, RemoveCodeBreakpoint, (
|
|
ProcessInfo* Process,
|
|
MachineInfo* Machine,
|
|
PADDR Addr,
|
|
ULONG InstrFlags,
|
|
PUCHAR StorageSpace))
|
|
UNEXPECTED_HR(TargetInfo, InsertTargetCountBreakpoint, (
|
|
PADDR Addr,
|
|
ULONG Flags))
|
|
UNEXPECTED_HR(TargetInfo, RemoveTargetCountBreakpoint, (
|
|
PADDR Addr))
|
|
UNEXPECTED_HR(TargetInfo, QueryTargetCountBreakpoint, (
|
|
PADDR Addr,
|
|
PULONG Flags,
|
|
PULONG Calls,
|
|
PULONG MinInstr,
|
|
PULONG MaxInstr,
|
|
PULONG TotInstr,
|
|
PULONG MaxCps))
|
|
UNEXPECTED_HR(TargetInfo, QueryMemoryRegion, (
|
|
ProcessInfo* Process,
|
|
PULONG64 Handle,
|
|
BOOL HandleIsOffset,
|
|
PMEMORY_BASIC_INFORMATION64 Info))
|