Windows-Server-2003/sdktools/debuggers/ntsd64/process.cpp

2494 lines
61 KiB
C++

//----------------------------------------------------------------------------
//
// Process abstraction.
//
// Copyright (C) Microsoft Corporation, 2001-2002.
//
//----------------------------------------------------------------------------
#include "ntsdp.hpp"
#include <common.ver>
#define NTLDR_IMAGE_NAME "ntldr"
#define OSLOADER_IMAGE_NAME "osloader"
#define SETUPLDR_IMAGE_NAME "setupldr"
#define LDR_IMAGE_SIZE 0x80000
#define CSRSS_IMAGE_NAME "csrss.exe"
#define LSASS_IMAGE_NAME "lsass.exe"
#define SERVICES_IMAGE_NAME "services.exe"
void
RestrictModNameChars(PCSTR ModName, PSTR RewriteBuffer)
{
PCSTR Scan = ModName;
PSTR Write = RewriteBuffer;
while (*Scan)
{
if ((*Scan < 'a' || *Scan > 'z') &&
(*Scan < 'A' || *Scan > 'Z') &&
(*Scan < '0' || *Scan > '9') &&
*Scan != '_')
{
*Write++ = '_';
}
else
{
*Write++ = *Scan;
}
Scan++;
}
*Write = 0;
}
//----------------------------------------------------------------------------
//
// ProcCorDataAccessServices.
//
//----------------------------------------------------------------------------
ProcCorDataAccessServices::ProcCorDataAccessServices(void)
{
m_Process = NULL;
}
STDMETHODIMP
ProcCorDataAccessServices::QueryInterface(
THIS_
IN REFIID InterfaceId,
OUT PVOID* Interface
)
{
if (DbgIsEqualIID(InterfaceId, IID_IUnknown) ||
DbgIsEqualIID(InterfaceId, __uuidof(ICorDataAccessServices)))
{
*Interface = (ICorDataAccessServices*)this;
// No need to refcount as this class is contained.
return S_OK;
}
else
{
*Interface = NULL;
return E_NOINTERFACE;
}
}
STDMETHODIMP_(ULONG)
ProcCorDataAccessServices::AddRef(
THIS
)
{
// No need to refcount as this class is contained.
return 1;
}
STDMETHODIMP_(ULONG)
ProcCorDataAccessServices::Release(
THIS
)
{
// No need to refcount as this class is contained.
return 0;
}
HRESULT STDMETHODCALLTYPE
ProcCorDataAccessServices::GetMachineType(
/* [out] */ ULONG32 *machine)
{
*machine = m_Process->m_Target->m_MachineType;
return S_OK;
}
HRESULT STDMETHODCALLTYPE
ProcCorDataAccessServices::GetPointerSize(
/* [out] */ ULONG32 *size)
{
*size = m_Process->m_Target->m_Machine->m_Ptr64 ? 8 : 4;
return S_OK;
}
HRESULT STDMETHODCALLTYPE
ProcCorDataAccessServices::GetImageBase(
/* [string][in] */ LPCWSTR name,
/* [out] */ CORDATA_ADDRESS *base)
{
HRESULT Status;
PSTR Ansi;
INAME WhichName;
ImageInfo* Image;
if ((Status = WideToAnsi(name, &Ansi)) != S_OK)
{
return Status;
}
if (PathTail(Ansi) != Ansi)
{
WhichName = INAME_IMAGE_PATH;
}
else
{
WhichName = INAME_IMAGE_PATH_TAIL;
}
if (Image = m_Process->FindImageByName(Ansi, 0, WhichName, FALSE))
{
*base = Image->m_BaseOfImage;
Status = S_OK;
}
else
{
Status = E_NOINTERFACE;
}
FreeAnsi(Ansi);
return Status;
}
HRESULT STDMETHODCALLTYPE
ProcCorDataAccessServices::ReadVirtual(
/* [in] */ CORDATA_ADDRESS address,
/* [length_is][size_is][out] */ PBYTE buffer,
/* [in] */ ULONG32 request,
/* [optional][out] */ ULONG32 *done)
{
return m_Process->m_Target->
ReadVirtual(m_Process, address, buffer, request, (PULONG)done);
}
HRESULT STDMETHODCALLTYPE
ProcCorDataAccessServices::WriteVirtual(
/* [in] */ CORDATA_ADDRESS address,
/* [size_is][in] */ PBYTE buffer,
/* [in] */ ULONG32 request,
/* [optional][out] */ ULONG32 *done)
{
return m_Process->m_Target->
WriteVirtual(m_Process, address, buffer, request, (PULONG)done);
}
HRESULT STDMETHODCALLTYPE
ProcCorDataAccessServices::GetTlsValue(
/* [in] */ ULONG32 index,
/* [out] */ CORDATA_ADDRESS* value)
{
HRESULT Status;
ULONG64 SlotAddr;
if ((Status = m_Process->m_CurrentThread->
GetTlsSlotAddress((ULONG)index, &SlotAddr)) != S_OK)
{
return Status;
}
return m_Process->m_Target->
ReadPointer(m_Process, m_Process->m_Target->m_Machine,
SlotAddr, value);
}
HRESULT STDMETHODCALLTYPE
ProcCorDataAccessServices::SetTlsValue(
/* [in] */ ULONG32 index,
/* [in] */ CORDATA_ADDRESS value)
{
HRESULT Status;
ULONG64 SlotAddr;
if ((Status = m_Process->m_CurrentThread->
GetTlsSlotAddress((ULONG)index, &SlotAddr)) != S_OK)
{
return Status;
}
return m_Process->m_Target->
WritePointer(m_Process, m_Process->m_Target->m_Machine,
SlotAddr, value);
}
HRESULT STDMETHODCALLTYPE
ProcCorDataAccessServices::GetCurrentThreadId(
/* [out] */ ULONG32* threadId)
{
*threadId = g_Thread->m_SystemId;
return S_OK;
}
HRESULT STDMETHODCALLTYPE
ProcCorDataAccessServices::GetThreadContext(
/* [in] */ ULONG32 threadId,
/* [in] */ ULONG32 contextFlags,
/* [in] */ ULONG32 contextSize,
/* [out, size_is(contextSize)] */ PBYTE context)
{
HRESULT Status;
if (contextSize < m_Process->m_Target->m_TypeInfo.SizeTargetContext)
{
return E_INVALIDARG;
}
ThreadInfo* Thread = m_Process->FindThreadBySystemId(threadId);
if (!Thread)
{
return E_NOINTERFACE;
}
m_Process->m_Target->ChangeRegContext(Thread);
if ((Status = m_Process->m_Target->m_Machine->
GetContextState(MCTX_CONTEXT)) == S_OK)
{
Status = m_Process->m_Target->m_Machine->
ConvertContextTo(&m_Process->m_Target->m_Machine->m_Context,
m_Process->m_Target->m_SystemVersion,
m_Process->m_Target->m_TypeInfo.SizeTargetContext,
context);
}
m_Process->m_Target->ChangeRegContext(m_Process->m_CurrentThread);
return Status;
}
HRESULT STDMETHODCALLTYPE
ProcCorDataAccessServices::SetThreadContext(
/* [in] */ ULONG32 threadId,
/* [in] */ ULONG32 contextSize,
/* [out, size_is(contextSize)] */ PBYTE context)
{
HRESULT Status;
if (contextSize < m_Process->m_Target->m_TypeInfo.SizeTargetContext)
{
return E_INVALIDARG;
}
ThreadInfo* Thread = m_Process->FindThreadBySystemId(threadId);
if (!Thread)
{
return E_NOINTERFACE;
}
m_Process->m_Target->ChangeRegContext(Thread);
if ((Status = m_Process->m_Target->m_Machine->
GetContextState(MCTX_DIRTY)) == S_OK)
{
Status = m_Process->m_Target->m_Machine->
ConvertContextFrom(&m_Process->m_Target->m_Machine->m_Context,
m_Process->m_Target->m_SystemVersion,
m_Process->m_Target->
m_TypeInfo.SizeTargetContext,
context);
if (Status == S_OK)
{
NotifyChangeDebuggeeState(DEBUG_CDS_REGISTERS, DEBUG_ANY_ID);
}
}
m_Process->m_Target->ChangeRegContext(m_Process->m_CurrentThread);
return Status;
}
//----------------------------------------------------------------------------
//
// ProcessInfo.
//
//----------------------------------------------------------------------------
ProcessInfo::ProcessInfo(TargetInfo* Target,
ULONG SystemId,
HANDLE SymHandle,
ULONG64 SysHandle,
ULONG Flags,
ULONG Options)
{
m_Target = Target;
m_UserId = FindNextUserId(LAYER_PROCESS);
m_Next = NULL;
m_NumImages = 0;
m_NumUnloadedModules = 0;
m_ImageHead = NULL;
m_ExecutableImage = NULL;
m_SynthesizedImage = NULL;
m_NumThreads = 0;
m_ThreadHead = NULL;
m_CurrentThread = NULL;
m_SystemId = SystemId;
m_Exited = FALSE;
m_ModulesLoaded = FALSE;
m_InitialBreakDone = (Flags & ENG_PROC_NO_INITIAL_BREAK) != 0;
// Always ask for an "initial" break when the process
// won't generate its own initial break as the first
// break encountered will just be a normal break and
// so should cause a break-in.
m_InitialBreak =
(Flags & ENG_PROC_NO_INITIAL_BREAK) ||
IS_KERNEL_TARGET(m_Target) ||
(g_EngOptions & DEBUG_ENGOPT_INITIAL_BREAK) != 0;
m_InitialBreakWx86 =
(g_EngOptions & DEBUG_ENGOPT_INITIAL_BREAK) != 0;
m_DataOffset = 0;
m_SymHandle = SymHandle;
m_SysHandle = SysHandle;
m_Flags = Flags;
m_Options = Options;
m_NumBreakpoints = 0;
m_Breakpoints = NULL;
m_BreakpointsTail = NULL;
m_DynFuncTableList = 0;
m_OopFuncTableDlls = NULL;
m_RtlUnloadList = 0;
ResetImplicitData();
m_CorImage = NULL;
m_CorImageType = COR_DLL_INVALID;
m_CorDebugDll = NULL;
m_CorAccess = NULL;
m_CorServices.m_Process = this;
m_VirtualCache.SetProcess(this);
PCHAR CacheEnv = getenv("_NT_DEBUG_CACHE_SIZE");
if (CacheEnv != NULL)
{
m_VirtualCache.m_MaxSize = atol(CacheEnv);
}
m_VirtualCache.m_DecodePTEs =
IS_KERNEL_TARGET(m_Target) &&
!(m_Target->m_KdVersion.Flags & DBGKD_VERS_FLAG_NOMM);
m_Target->InsertProcess(this);
SymInitialize(m_SymHandle, NULL, FALSE);
SymRegisterCallback64(m_SymHandle, SymbolCallbackFunction,
(ULONG_PTR)this);
if (IS_USER_TARGET(m_Target) &&
m_Target->m_MachineType != IMAGE_FILE_MACHINE_I386)
{
SymRegisterFunctionEntryCallback64
(m_SymHandle, TargetInfo::DynamicFunctionTableCallback,
(ULONG_PTR)this);
}
SetSymbolSearchPath(this);
SynthesizeSymbols();
}
ProcessInfo::~ProcessInfo(void)
{
ClearOopFuncTableDlls();
RELEASE(m_CorAccess);
if (m_CorDebugDll)
{
FreeLibrary(m_CorDebugDll);
}
while (m_ThreadHead)
{
delete m_ThreadHead;
}
// Suppress notifications until all images are deleted.
g_EngNotify++;
while (m_ImageHead)
{
delete m_ImageHead;
}
delete m_SynthesizedImage;
// Notification can use process information so put things
// in a clean state before notify.
m_SynthesizedImage = NULL;
g_EngNotify--;
NotifyChangeSymbolState(DEBUG_CSS_UNLOADS, 0, this);
SymCleanup(m_SymHandle);
RemoveProcessBreakpoints(this);
if (m_Flags & ENG_PROC_THREAD_CLOSE_HANDLE)
{
DBG_ASSERT(IS_LIVE_USER_TARGET(m_Target) &&
((LiveUserTargetInfo*)m_Target)->m_Services);
((LiveUserTargetInfo*)m_Target)->m_Services->
CloseHandle(m_SysHandle);
}
m_Target->RemoveProcess(this);
g_UserIdFragmented[LAYER_PROCESS]++;
if (this == g_Process)
{
g_Process = NULL;
}
if (this == g_EventProcess)
{
g_EventProcess = NULL;
DiscardLastEvent();
}
if (g_StepTraceBp && g_StepTraceBp->m_Process == this)
{
g_StepTraceBp->m_Process = NULL;
if (g_WatchFunctions.IsStarted())
{
g_WatchFunctions.End(NULL);
}
ResetStepTrace();
g_StepTraceBp->m_Flags &= ~(DEBUG_BREAKPOINT_ENABLED |
BREAKPOINT_INSERTED);
}
if (g_ScopeBuffer.State == ScopeFromContext &&
g_ScopeBuffer.Process == this)
{
ResetCurrentScopeLazy();
}
}
ThreadInfo*
ProcessInfo::FindThreadByUserId(ULONG Id)
{
ThreadInfo* Thread;
ForProcessThreads(this)
{
if (Thread->m_UserId == Id)
{
return Thread;
}
}
return NULL;
}
ThreadInfo*
ProcessInfo::FindThreadBySystemId(ULONG Id)
{
ThreadInfo* Thread;
ForProcessThreads(this)
{
if (Thread->m_SystemId == Id)
{
return Thread;
}
}
return NULL;
}
ThreadInfo*
ProcessInfo::FindThreadByHandle(ULONG64 Handle)
{
ThreadInfo* Thread;
ForProcessThreads(this)
{
if (Thread->m_Handle == Handle)
{
return Thread;
}
}
return NULL;
}
void
ProcessInfo::InsertThread(ThreadInfo* Thread)
{
ThreadInfo* Cur;
ThreadInfo* Prev;
Prev = NULL;
for (Cur = m_ThreadHead; Cur; Cur = Cur->m_Next)
{
if (Cur->m_UserId > Thread->m_UserId)
{
break;
}
Prev = Cur;
}
Thread->m_Next = Cur;
if (!Prev)
{
m_ThreadHead = Thread;
}
else
{
Prev->m_Next = Thread;
}
m_NumThreads++;
Thread->m_Process = this;
if (!m_CurrentThread)
{
m_CurrentThread = Thread;
}
m_Target->AddThreadToAllProcessInfo(this, Thread);
}
void
ProcessInfo::RemoveThread(ThreadInfo* Thread)
{
ThreadInfo* Cur;
ThreadInfo* Prev;
Prev = NULL;
for (Cur = m_ThreadHead; Cur; Cur = Cur->m_Next)
{
if (Cur == Thread)
{
break;
}
Prev = Cur;
}
if (!Cur)
{
return;
}
if (!Prev)
{
m_ThreadHead = Thread->m_Next;
}
else
{
Prev->m_Next = Thread->m_Next;
}
m_NumThreads--;
Thread->m_Process = NULL;
if (m_CurrentThread == Thread)
{
m_CurrentThread = m_ThreadHead;
}
m_Target->RemoveThreadFromAllProcessInfo(this, Thread);
}
HRESULT
ProcessInfo::CreateVirtualThreads(ULONG StartId, ULONG Threads)
{
ThreadInfo* Thread;
while (Threads-- > 0)
{
Thread = new ThreadInfo(this, VIRTUAL_THREAD_ID(StartId), 0,
VIRTUAL_THREAD_HANDLE(StartId), 0, 0);
if (!Thread)
{
return E_OUTOFMEMORY;
}
StartId++;
}
return S_OK;
}
ImageInfo*
ProcessInfo::FindImageByIndex(ULONG Index)
{
ImageInfo* Image = m_ImageHead;
while (Index > 0 && Image != NULL)
{
Index--;
Image = Image->m_Next;
}
return Image;
}
ImageInfo*
ProcessInfo::FindImageByOffset(ULONG64 Offset, BOOL AllowSynth)
{
ImageInfo* Image = m_ImageHead;
while (Image != NULL &&
(Offset < Image->m_BaseOfImage ||
Offset >= Image->m_BaseOfImage + Image->m_SizeOfImage))
{
Image = Image->m_Next;
}
//
// If synthesized symbols are allowed, check those images
// also.
//
if (!Image &&
AllowSynth &&
m_SynthesizedImage &&
Offset >= m_SynthesizedImage->m_BaseOfImage &&
Offset < m_SynthesizedImage->m_BaseOfImage +
m_SynthesizedImage->m_SizeOfImage)
{
Image = m_SynthesizedImage;
}
return Image;
}
ImageInfo*
ProcessInfo::FindImageByName(PCSTR Name, ULONG NameChars,
INAME Which, BOOL AllowSynth)
{
if (NameChars == 0)
{
NameChars = strlen(Name);
}
ImageInfo* Image = m_ImageHead;
while (Image != NULL)
{
PCSTR WhichStr;
switch(Which)
{
case INAME_IMAGE_PATH:
WhichStr = Image->m_ImagePath;
break;
case INAME_IMAGE_PATH_TAIL:
WhichStr = PathTail(Image->m_ImagePath);
break;
case INAME_MODULE:
if (Image->m_OriginalModuleName[0] &&
strlen(Image->m_OriginalModuleName) == NameChars &&
!_memicmp(Image->m_OriginalModuleName, Name, NameChars))
{
return Image;
}
WhichStr = Image->m_ModuleName;
break;
}
if (strlen(WhichStr) == NameChars &&
_memicmp(WhichStr, Name, NameChars) == 0)
{
break;
}
Image = Image->m_Next;
}
//
// If synthesized symbols are allowed, check those images
// also.
//
if (!Image &&
AllowSynth &&
m_SynthesizedImage &&
Which == INAME_MODULE &&
strlen(m_SynthesizedImage->m_ModuleName) == NameChars &&
!_memicmp(m_SynthesizedImage->m_ModuleName, Name, NameChars))
{
Image = m_SynthesizedImage;
}
return Image;
}
BOOL
ProcessInfo::GetOffsetFromMod(PCSTR String, PULONG64 Offset)
{
if (!strchr(String, '!'))
{
ULONG Len;
BOOL End;
//
// Could be a module name or module$ for the end
// of a module.
//
Len = strlen(String);
if (Len == 0)
{
return FALSE;
}
if (String[Len - 1] == '$')
{
// Retrieving end-of-module.
Len--;
End = TRUE;
}
else
{
End = FALSE;
}
ImageInfo* Image = FindImageByName(String, Len, INAME_MODULE, TRUE);
if (Image)
{
if (End)
{
*Offset = Image->m_BaseOfImage + Image->m_SizeOfImage;
}
else
{
*Offset = Image->m_BaseOfImage;
}
return TRUE;
}
}
return FALSE;
}
COR_DLL
IsCorDll(PCSTR ImagePath)
{
PCSTR ImagePathTail = PathTail(ImagePath);
if (!_stricmp(ImagePathTail, "mscoree.dll"))
{
return COR_DLL_EE;
}
if (!_stricmp(ImagePathTail, "mscorwks.dll"))
{
return COR_DLL_WKS;
}
if (!_stricmp(ImagePathTail, "mscorsvr.dll"))
{
return COR_DLL_SVR;
}
return COR_DLL_INVALID;
}
void
ProcessInfo::InsertImage(ImageInfo* Image)
{
ImageInfo* Cur;
ImageInfo* Prev;
Prev = NULL;
for (Cur = m_ImageHead; Cur; Cur = Cur->m_Next)
{
if (Cur->m_BaseOfImage > Image->m_BaseOfImage)
{
break;
}
Prev = Cur;
}
Image->m_Next = Cur;
if (!Prev)
{
m_ImageHead = Image;
}
else
{
Prev->m_Next = Image;
}
m_NumImages++;
Image->m_Process = this;
Image->m_Linked = TRUE;
if (m_ExecutableImage == NULL)
{
// Try and locate the executable image entry for
// the process to use as the process's name.
ULONG NameLen = strlen(Image->m_ImagePath);
if (NameLen > 4 &&
!_stricmp(Image->m_ImagePath + NameLen - 4, ".exe"))
{
m_ExecutableImage = Image;
}
}
COR_DLL CorDll = IsCorDll(Image->m_ImagePath);
if (CorDll > m_CorImageType)
{
m_CorImage = Image;
m_CorImageType = CorDll;
}
}
void
ProcessInfo::RemoveImage(ImageInfo* Image)
{
ImageInfo* Cur;
ImageInfo* Prev;
Prev = NULL;
for (Cur = m_ImageHead; Cur; Cur = Cur->m_Next)
{
if (Cur == Image)
{
break;
}
Prev = Cur;
}
if (!Cur)
{
return;
}
if (!Prev)
{
m_ImageHead = Image->m_Next;
}
else
{
Prev->m_Next = Image->m_Next;
}
m_NumImages--;
Image->m_Process = NULL;
Image->m_Linked = FALSE;
if (m_ExecutableImage == Image)
{
m_ExecutableImage = NULL;
}
if (m_CorImageType != COR_DLL_INVALID &&
IsCorDll(Image->m_ImagePath) != COR_DLL_INVALID)
{
m_CorImage = NULL;
m_CorImageType = COR_DLL_INVALID;
}
}
BOOL
ProcessInfo::DeleteImageByName(PCSTR Name, INAME Which)
{
ImageInfo* Image;
for (Image = m_ImageHead; Image; Image = Image->m_Next)
{
PCSTR WhichStr;
switch(Which)
{
case INAME_IMAGE_PATH:
WhichStr = Image->m_ImagePath;
break;
case INAME_IMAGE_PATH_TAIL:
WhichStr = PathTail(Image->m_ImagePath);
break;
case INAME_MODULE:
WhichStr = Image->m_ModuleName;
break;
}
if (!_stricmp(WhichStr, Name))
{
delete Image;
return TRUE;
}
}
return FALSE;
}
BOOL
ProcessInfo::DeleteImageByBase(ULONG64 BaseOfImage)
{
ImageInfo* Image;
for (Image = m_ImageHead; Image; Image = Image->m_Next)
{
if (Image->m_BaseOfImage == BaseOfImage)
{
delete Image;
return TRUE;
}
}
return FALSE;
}
void
ProcessInfo::DeleteImagesBelowOffset(ULONG64 Offset)
{
ImageInfo* Image, *Next;
ULONG Count = 0;
// Suppress notifications until all images are deleted.
g_EngNotify++;
for (Image = m_ImageHead; Image; Image = Next)
{
Next = Image->m_Next;
if (Image->m_BaseOfImage < Offset)
{
delete Image;
Count++;
}
}
g_EngNotify--;
if (Count)
{
NotifyChangeSymbolState(DEBUG_CSS_UNLOADS, 0, this);
}
}
void
ProcessInfo::DeleteImages(void)
{
// Suppress notifications until all images are deleted.
g_EngNotify++;
while (m_ImageHead)
{
delete m_ImageHead;
}
g_EngNotify--;
NotifyChangeSymbolState(DEBUG_CSS_UNLOADS, 0, this);
}
HRESULT
ProcessInfo::AddImage(PMODULE_INFO_ENTRY ModEntry,
BOOL ForceSymbolLoad,
ImageInfo** ImageAdded)
{
ImageInfo* ImageNew;
IMAGEHLP_MODULE64 SymModInfo;
ULONG64 LoadAddress;
BOOL LoadSymbols = TRUE;
PCSTR ImagePathTail;
MODLOAD_DATA ModLoadData;
HRESULT Status;
BOOL ReusedExisting = FALSE;
#if DBG_MOD_LIST
dprintf("AddImage:\n"
" ImagePath %s\n"
" File %I64x\n"
" BaseOfImage %I64x\n"
" SizeOfImage %x\n"
" CheckSum %x\n"
" ModuleName %s\n"
" ForceSymbolLoad %d\n",
ModEntry->NamePtr,
(ULONG64)(ULONG_PTR)File,
ModEntry->Base,
ModEntry->Size,
ModEntry->CheckSum,
ModEntry->ModuleName,
ForceSymbolLoad);
#endif
if (ModEntry->NamePtr == NULL)
{
WarnOut("AddImage(NULL) fails\n");
return E_INVALIDARG;
}
if (ModEntry->NamePtr &&
IS_USER_TARGET(m_Target) &&
m_Target->m_PlatformId == VER_PLATFORM_WIN32_NT)
{
TranslateNtPathName(ModEntry->NamePtr);
}
//
// Search for existing image at same base address.
// If found, remove symbols, but leave image structure intact.
//
for (ImageNew = m_ImageHead; ImageNew; ImageNew = ImageNew->m_Next)
{
if (ImageNew->m_BaseOfImage == ModEntry->Base)
{
VerbOut("Force unload of %s\n", ImageNew->m_ImagePath);
ImageNew->DeleteResources(FALSE);
break;
}
}
//
// If we didn't reuse an existing image, allocate
// a new one.
//
if (!ImageNew)
{
ImageNew = new ImageInfo(this,
ModEntry->NamePtr, ModEntry->Base, TRUE);
if (ImageNew == NULL)
{
ErrOut("Unable to allocate memory for %s symbols.\n",
ModEntry->NamePtr);
return E_OUTOFMEMORY;
}
// Module name is set later after possible renaming.
ImageNew->m_File = ModEntry->File;
ImageNew->m_CheckSum = ModEntry->CheckSum;
ImageNew->m_TimeDateStamp = ModEntry->TimeDateStamp;
ImageNew->m_SymState = ISS_MATCHED;
ImageNew->m_UserMode = ModEntry->UserMode;
}
else
{
ReusedExisting = TRUE;
// Update the reused entry with the latest information.
if (ModEntry->NamePtr)
{
CopyString(ImageNew->m_ImagePath, ModEntry->NamePtr,
DIMA(ImageNew->m_ImagePath));
}
else
{
ImageNew->m_ImagePath[0] = 0;
}
}
// Always update the entry size as this allows users
// to update explicit entries (.reload image.ext=base,size)
// without having to unload them first.
ImageNew->m_SizeOfImage = ModEntry->Size;
ImagePathTail = PathTail(ImageNew->m_ImagePath);
if (ForceSymbolLoad)
{
// Try to load the image memory right away in this
// case to catch incomplete-information errors.
if (!ImageNew->DemandLoadImageMemory(!ReusedExisting, TRUE))
{
if (!ReusedExisting)
{
g_EngNotify++;
delete ImageNew;
g_EngNotify--;
}
return E_OUTOFMEMORY;
}
}
if (IS_KERNEL_TARGET(m_Target))
{
CHAR Buf[MAX_IMAGE_PATH];
MODULE_ALIAS_LIST* ModAlias;
//
// Determine the actual image name for kernel images which
// are known to have multiple identities.
//
if ((ModEntry->ModuleName &&
_stricmp(ModEntry->ModuleName, KERNEL_MODULE_NAME) == 0) ||
ModEntry->Base == m_Target->m_KdDebuggerData.KernBase)
{
ModAlias = &g_AliasLists[MODALIAS_KERNEL];
}
else
{
ModAlias = FindModuleAliasList(ImagePathTail, NULL);
}
if (ModAlias)
{
if (GetModnameFromImage(this, ModEntry->Base, NULL,
Buf, sizeof(Buf), FALSE))
{
CopyString(ImageNew->m_ImagePath, Buf,
DIMA(ImageNew->m_ImagePath));
}
ModEntry->ModuleName = (PSTR)ModAlias->BaseModule;
}
else if (ImageNew->m_SizeOfImage == 0 &&
((_stricmp(ImagePathTail, NTLDR_IMAGE_NAME) == 0) ||
(_stricmp(ImagePathTail, NTLDR_IMAGE_NAME ".exe") == 0) ||
(_stricmp(ImagePathTail, OSLOADER_IMAGE_NAME) == 0) ||
(_stricmp(ImagePathTail, OSLOADER_IMAGE_NAME ".exe") == 0) ||
(_stricmp(ImagePathTail, SETUPLDR_IMAGE_NAME) == 0) ||
(_stricmp(ImagePathTail, SETUPLDR_IMAGE_NAME ".exe") == 0)))
{
ImageNew->m_SizeOfImage = LDR_IMAGE_SIZE;
}
}
else if (!IS_DUMP_TARGET(m_Target))
{
//
// When debugging CSR, LSA or Services.exe, force the use of local-only
// symbols. Otherwise we can deadlock the entire machine when trying
// to load the symbol file from the network.
//
if (((LiveUserTargetInfo*)m_Target)->m_Local &&
(_stricmp(ImagePathTail, CSRSS_IMAGE_NAME) == 0 ||
_stricmp(ImagePathTail, LSASS_IMAGE_NAME) == 0 ||
_stricmp(ImagePathTail, SERVICES_IMAGE_NAME) == 0))
{
if (g_EngOptions & DEBUG_ENGOPT_ALLOW_NETWORK_PATHS)
{
//
// Since the user has chambered a round and pointed the barrel
// of the gun at his head, we may as well tell him that it's
// going to hurt if he pulls the trigger.
//
WarnOut("WARNING: Using network symbols with %s\n",
ImagePathTail);
WarnOut("WARNING: You may deadlock your machine.\n");
}
else
{
g_EngOptions |= DEBUG_ENGOPT_DISALLOW_NETWORK_PATHS;
}
}
if (g_EngOptions & DEBUG_ENGOPT_DISALLOW_NETWORK_PATHS)
{
DWORD NetPath;
NetPath = NetworkPathCheck(g_SymbolSearchPath);
if (NetPath == ERROR_FILE_OFFLINE)
{
ErrOut("ERROR: sympath contained network references but "
"they were not allowed.\n");
ErrOut("Symbols not loaded for %s\n",
ImagePathTail);
LoadSymbols = FALSE;
}
else if (NetPath == ERROR_BAD_PATHNAME)
{
VerbOut("WARNING: sympath contains invalid references.\n");
}
}
}
if (ModEntry->ModuleName == NULL)
{
CreateModuleNameFromPath(ImageNew->m_ImagePath,
ImageNew->m_ModuleName);
RestrictModNameChars(ImageNew->m_ModuleName, ImageNew->m_ModuleName);
strcpy(ImageNew->m_OriginalModuleName, ImageNew->m_ModuleName);
}
else
{
RestrictModNameChars(ModEntry->ModuleName, ImageNew->m_ModuleName);
//
// We have an alias, so keep original module name from path
//
CreateModuleNameFromPath(ImageNew->m_ImagePath,
ImageNew->m_OriginalModuleName);
RestrictModNameChars(ImageNew->m_OriginalModuleName,
ImageNew->m_OriginalModuleName);
}
//
// Check for duplicate module names and
// image overlaps.
//
ImageInfo* Check;
for (Check = m_ImageHead; Check != NULL; Check = Check->m_Next)
{
if (Check != ImageNew &&
!_strcmpi(ImageNew->m_ModuleName, Check->m_ModuleName))
{
int Len = strlen(ImageNew->m_ModuleName);
// Module names match, so qualify with the base address.
// Resulting name must be unique since base addresses are.
if (m_Target->m_Machine->m_Ptr64)
{
if (Len >= MAX_MODULE - 17)
{
Len = MAX_MODULE - 18;
}
sprintf(ImageNew->m_ModuleName + Len, "_%I64x",
ModEntry->Base);
}
else
{
if (Len >= MAX_MODULE - 9)
{
Len = MAX_MODULE - 10;
}
sprintf(ImageNew->m_ModuleName + Len, "_%x",
(ULONG)ModEntry->Base);
}
// Force all references to this module to be
// through the exact module name and not through
// an original name that may have been ambiguous.
ImageNew->m_OriginalModuleName[0] = 0;
}
if (Check != ImageNew &&
ImageNew->m_BaseOfImage <
Check->m_BaseOfImage + Check->m_SizeOfImage &&
Check->m_BaseOfImage <
ImageNew->m_BaseOfImage + ImageNew->m_SizeOfImage)
{
WarnOut("WARNING: %s overlaps %s\n",
ImageNew->m_ModuleName,
Check->m_ModuleName);
}
}
//
// If we do not want to load symbolic information, just return here.
//
if (!LoadSymbols)
{
*ImageAdded = ImageNew;
return S_OK;
}
if (ModEntry->DebugHeader)
{
ModLoadData.ssize = sizeof(ModLoadData);
ModLoadData.ssig = DBHHEADER_DEBUGDIRS;
ModLoadData.data = ModEntry->DebugHeader;
ModLoadData.size = ModEntry->SizeOfDebugHeader;
ModLoadData.flags = 0;
}
LoadAddress = SymLoadModuleEx(m_SymHandle,
ImageNew->m_File,
PrepareImagePath(ImageNew->m_ImagePath),
ImageNew->m_ModuleName,
ImageNew->m_BaseOfImage,
ImageNew->m_SizeOfImage,
ModEntry->DebugHeader ? &ModLoadData : NULL,
0);
if (!LoadAddress)
{
Status = WIN32_LAST_STATUS();
VerbOut("SymLoadModule(%N, %N, \"%s\", \"%s\", %s, %x) fails\n",
m_SymHandle,
ImageNew->m_File,
ImageNew->m_ImagePath,
ImageNew->m_ModuleName,
FormatAddr64(ImageNew->m_BaseOfImage),
ImageNew->m_SizeOfImage);
// We don't want deletion to notify of a symbol change
// if this is a partially newly created image.
if (!ReusedExisting)
{
g_EngNotify++;
}
delete ImageNew;
if (!ReusedExisting)
{
g_EngNotify--;
}
return Status;
}
if (!ImageNew->m_BaseOfImage)
{
ImageNew->m_BaseOfImage = LoadAddress;
}
if (ForceSymbolLoad)
{
SymLoadModule64(m_SymHandle,
NULL,
NULL,
NULL,
ImageNew->m_BaseOfImage,
0);
}
SymModInfo.SizeOfStruct = sizeof(SymModInfo);
if (SymGetModuleInfo64(m_SymHandle,
ImageNew->m_BaseOfImage, &SymModInfo))
{
ImageNew->m_SizeOfImage = SymModInfo.ImageSize;
}
else
{
Status = WIN32_LAST_STATUS();
VerbOut("SymGetModuleInfo(%N, %s) fails\n",
m_SymHandle, FormatAddr64(ImageNew->m_BaseOfImage));
// We don't want DelImage to notify of a symbol change
// if this is a partially newly created image.
if (!ReusedExisting)
{
g_EngNotify++;
}
delete ImageNew;
if (!ReusedExisting)
{
g_EngNotify--;
}
return Status;
}
if (ModEntry->ImageMachineTypeValid)
{
ImageNew->m_MachineType = ModEntry->MachineType;
}
StartOutLine(DEBUG_OUTPUT_VERBOSE, OUT_LINE_NO_PREFIX);
VerbOut("ModLoad: %s %s %-8s\n",
FormatAddr64(ImageNew->m_BaseOfImage),
FormatAddr64(ImageNew->m_BaseOfImage + ImageNew->m_SizeOfImage),
ImageNew->m_ImagePath);
NotifyChangeSymbolState(DEBUG_CSS_LOADS, ImageNew->m_BaseOfImage, this);
*ImageAdded = ImageNew;
return S_OK;
}
void
ProcessInfo::SynthesizeSymbols(void)
{
// SynthesizeSymbols can result in symbol
// callbacks but the symbols involved aren't real symbols
// so suppress notifications.
g_EngNotify++;
if (m_Target->m_TypeInfo.UmSharedUserDataOffset)
{
//
// Create a fake module for the shared user
// data area. Add a symbol for the system
// call stub there if a stub exists.
//
ImageInfo* Image;
Image = new ImageInfo(this, "SharedUserData",
m_Target->m_TypeInfo.UmSharedUserDataOffset,
FALSE);
if (!Image)
{
WarnOut("Unable to create shared user data image\n");
}
else
{
Image->m_SizeOfImage = m_Target->m_Machine->m_PageSize;
strcpy(Image->m_ModuleName, Image->m_ImagePath);
strcpy(Image->m_OriginalModuleName, Image->m_ImagePath);
if (!SymLoadModuleEx(m_SymHandle, NULL,
Image->m_ImagePath,
Image->m_ModuleName, Image->m_BaseOfImage,
Image->m_SizeOfImage, NULL, SLMFLAG_VIRTUAL))
{
WarnOut("Unable to add shared user data module\n");
delete Image;
}
else if (m_Target->m_TypeInfo.UmSharedSysCallOffset &&
!SymAddSymbol(m_SymHandle,
Image->m_BaseOfImage,
"SystemCallStub",
m_Target->m_TypeInfo.UmSharedSysCallOffset,
m_Target->m_TypeInfo.UmSharedSysCallSize,
0))
{
WarnOut("Unable to add system call symbol\n");
delete Image;
}
else
{
m_SynthesizedImage = Image;
}
}
}
g_EngNotify--;
}
void
ProcessInfo::VerifyKernel32Version(void)
{
ImageInfo* Image = FindImageByName("kernel32", 8, INAME_MODULE, FALSE);
if (!Image)
{
return;
}
//
// Verify the OS version matches kernel32's image version.
//
IMAGE_NT_HEADERS64 NtHeaders;
if (m_Target->ReadImageNtHeaders(this, Image->m_BaseOfImage,
&NtHeaders) == S_OK)
{
if (NtHeaders.OptionalHeader.MajorImageVersion !=
m_Target->m_KdVersion.MajorVersion ||
NtHeaders.OptionalHeader.MinorImageVersion !=
m_Target->m_KdVersion.MinorVersion)
{
WarnOut("WARNING: System version is %d.%d but "
"kernel32.dll is version %d.%d.\n",
m_Target->m_KdVersion.MajorVersion,
m_Target->m_KdVersion.MinorVersion,
NtHeaders.OptionalHeader.MajorImageVersion,
NtHeaders.OptionalHeader.MinorImageVersion);
if (IS_USER_DUMP(m_Target))
{
WarnOut("If this dump was generated by userdump.exe on "
"Windows NT 4.0 the dump\n"
"version is probably incorrect. "
"Set DBGENG_FULL_DUMP_VERSION=4.0 in your\n"
"environment to override the dump version.\n");
}
}
}
}
BOOL
ProcessInfo::DeleteExitedInfos(void)
{
ThreadInfo* Thread;
ThreadInfo* ThreadNext;
BOOL DeletedSomething = FALSE;
for (Thread = m_ThreadHead; Thread; Thread = ThreadNext)
{
ThreadNext = Thread->m_Next;
if (Thread->m_Exited)
{
delete Thread;
DeletedSomething = TRUE;
}
}
ImageInfo* Image;
ImageInfo* ImagePrev;
ImageInfo* ImageNext;
ImagePrev = NULL;
for (Image = m_ImageHead; Image; Image = ImageNext)
{
ImageNext = Image->m_Next;
if (Image->m_Unloaded)
{
delete Image;
DeletedSomething = TRUE;
}
else
{
ImagePrev = Image;
}
}
return DeletedSomething;
}
void
ProcessInfo::PrepareForExecution(void)
{
ThreadInfo* Thread;
ForProcessThreads(this)
{
Thread->PrepareForExecution();
}
ResetImplicitData();
m_VirtualCache.Empty();
m_VirtualCache.SetForceDecodePtes(FALSE, m_Target);
FlushCorState();
}
void
ProcessInfo::OutputThreadInfo(ThreadInfo* Match, BOOL Verbose)
{
ThreadInfo* Thread;
Thread = m_ThreadHead;
while (Thread)
{
if (Match == NULL || Match == Thread)
{
ULONG64 DataOffset;
HRESULT Status;
char CurMark;
Status =
m_Target->
GetThreadInfoDataOffset(Thread, 0, &DataOffset);
if (Status != S_OK)
{
WarnOut("Unable to get thread data for thread %u\n",
Thread->m_UserId);
DataOffset = 0;
}
if (Thread == g_Thread)
{
CurMark = '.';
}
else if (Thread == g_EventThread)
{
CurMark = '#';
}
else
{
CurMark = ' ';
}
dprintf("%c%3ld Id: %lx.%lx Suspend: %d Teb: %s ",
CurMark,
Thread->m_UserId,
m_SystemId,
Thread->m_SystemId,
Thread->m_SuspendCount,
FormatAddr64(DataOffset));
if (Thread->m_Frozen)
{
dprintf("Frozen ");
}
else
{
dprintf("Unfrozen");
}
if (Thread->m_Name[0])
{
dprintf(" \"%s\"", Thread->m_Name);
}
dprintf("\n");
if (Verbose)
{
dprintf(" ");
if (!Thread->m_StartOffset)
{
if (m_Target->
GetThreadStartOffset(Thread,
&Thread->m_StartOffset) != S_OK)
{
Thread->m_StartOffset = 0;
}
}
if (Thread->m_StartOffset)
{
dprintf("Start: ");
OutputSymAddr(Thread->m_StartOffset,
SYMADDR_FORCE | SYMADDR_OFFSET, NULL);
}
dprintf("\n");
}
}
if (CheckUserInterrupt())
{
break;
}
Thread = Thread->m_Next;
}
}
HRESULT
ProcessInfo::AddOopFuncTableDll(PWSTR Dll, ULONG64 Handle)
{
OUT_OF_PROC_FUNC_TABLE_DLL* OopDll;
OopDll = (OUT_OF_PROC_FUNC_TABLE_DLL*)
malloc(sizeof(*OopDll) + (wcslen(Dll) + 1) * sizeof(Dll));
if (!OopDll)
{
return E_OUTOFMEMORY;
}
OopDll->Next = m_OopFuncTableDlls;
m_OopFuncTableDlls = OopDll;
OopDll->Handle = Handle;
wcscpy((PWSTR)(OopDll + 1), Dll);
return S_OK;
}
void
ProcessInfo::ClearOopFuncTableDlls(void)
{
while (m_OopFuncTableDlls)
{
OUT_OF_PROC_FUNC_TABLE_DLL* OopDll = m_OopFuncTableDlls;
m_OopFuncTableDlls = OopDll->Next;
if (IS_LIVE_USER_TARGET(m_Target))
{
((LiveUserTargetInfo*)m_Target)->m_Services->
FreeLibrary(OopDll->Handle);
}
free(OopDll);
}
}
OUT_OF_PROC_FUNC_TABLE_DLL*
ProcessInfo::FindOopFuncTableDll(PWSTR Dll)
{
OUT_OF_PROC_FUNC_TABLE_DLL* OopDll;
for (OopDll = m_OopFuncTableDlls; OopDll; OopDll = OopDll->Next)
{
if (!_wcsicmp(Dll, (PWSTR)(OopDll + 1)))
{
return OopDll;
}
}
return NULL;
}
HRESULT
ProcessInfo::LoadCorDebugDll(void)
{
HRESULT Status;
MachineInfo* CorMachine;
if (m_CorDebugDll)
{
return S_OK;
}
if (!m_CorImage ||
!(CorMachine = MachineTypeInfo(m_Target,
m_CorImage->GetMachineType())))
{
return E_UNEXPECTED;
}
VS_FIXEDFILEINFO CorVer;
if ((Status = m_Target->
GetImageVersionInformation(this,
m_CorImage->m_ImagePath,
m_CorImage->m_BaseOfImage,
"\\", &CorVer, sizeof(CorVer),
NULL)) != S_OK)
{
ErrOut("Unable to get version information for %s\n",
m_CorImage->m_ModuleName);
return Status;
}
PSTR CorDacType;
switch(m_CorImageType)
{
case COR_DLL_EE:
CorDacType = "ee";
break;
case COR_DLL_WKS:
CorDacType = "wks";
break;
case COR_DLL_SVR:
CorDacType = "svr";
break;
default:
ErrOut("Unknown runtime DLL type %s\n",
m_CorImage->m_ModuleName);
return E_INVALIDARG;
}
char DacDll[MAX_PATH];
PSTR CorImagePath = NULL;
PSTR Tail;
// If this is a live session we want to look for the
// debug DLL in the same place that the runtime is.
// If this is a dump, we want to look in the same place
// that the runtime image file was mapped from.
if (IS_DUMP_TARGET(m_Target) &&
m_CorImage->m_MappedImagePath[0])
{
CorImagePath = m_CorImage->m_MappedImagePath;
}
if (!CorImagePath)
{
CorImagePath = m_CorImage->m_ImagePath;
}
if (!CopyString(DacDll, CorImagePath, DIMA(DacDll)))
{
ErrOut("Runtime debugging DLL path overflow\n");
return E_OUTOFMEMORY;
}
// We need to request the debugging DLL that runs
// using the currently running processor type and
// that can debug the current target's processor type.
#if defined(_X86_)
PSTR HostCpu = "x86";
#elif defined(_IA64_)
PSTR HostCpu = "IA64";
#elif defined(_AMD64_)
PSTR HostCpu = "AMD64";
#elif defined(_ARM_)
PSTR HostCpu = "ARM";
#else
#error Unknown processor.
#endif
Tail = (PSTR)PathTail(DacDll);
if (!PrintString(Tail, (ULONG)(DIMA(DacDll) - (Tail - DacDll)),
"mscordac%s_%s_%s_%u.%u.%u.%u%s.dll",
CorDacType,
HostCpu,
CorMachine->m_AbbrevName,
CorVer.dwFileVersionMS >> 16,
CorVer.dwFileVersionMS & 0xffff,
CorVer.dwFileVersionLS >> 16,
CorVer.dwFileVersionLS & 0xffff,
(CorVer.dwFileFlags & VS_FF_DEBUG) ?
((CorVer.dwFileFlags & VS_FF_PRIVATEBUILD) ?
".fastchecked" : ".checked") : ""))
{
ErrOut("Runtime debugging DLL path overflow\n");
return E_OUTOFMEMORY;
}
//
// First try and load the debugging DLL using the same
// path as the runtime DLL. This makes it easy to pick
// up debugging DLLs that come with runtime installations.
//
PSTR DllPath = DacDll;
m_CorDebugDll = LoadLibrary(DllPath);
if (!m_CorDebugDll &&
(GetLastError() == ERROR_PATH_NOT_FOUND ||
GetLastError() == ERROR_FILE_NOT_FOUND ||
GetLastError() == ERROR_MOD_NOT_FOUND))
{
// Couldn't find it, so allow a path search.
DllPath = Tail;
m_CorDebugDll = LoadLibrary(DllPath);
}
if (!m_CorDebugDll)
{
Status = WIN32_LAST_STATUS();
VerbOut("Unable to load runtime debug DLL %s, %s\n",
DllPath, FormatStatusCode(Status));
return Status;
}
PFN_CreateCorDataAccess Entry = (PFN_CreateCorDataAccess)
GetProcAddress(m_CorDebugDll, "CreateCorDataAccess");
if (!Entry)
{
Status = WIN32_LAST_STATUS();
FreeLibrary(m_CorDebugDll);
m_CorDebugDll = NULL;
ErrOut("Runtime debug DLL %s missing entry point\n", DllPath);
return Status;
}
if ((Status = Entry(__uuidof(ICorDataAccess), &m_CorServices,
(void**)&m_CorAccess)) != S_OK)
{
FreeLibrary(m_CorDebugDll);
m_CorDebugDll = NULL;
ErrOut("Runtime debug DLL %s init failure, %s\n",
DllPath, FormatStatusCode(Status));
return Status;
}
VerbOut("Loaded runtime debug DLL %s\n", DllPath);
return S_OK;
}
HRESULT
ProcessInfo::IsCorCode(ULONG64 Native)
{
HRESULT Status;
if ((Status = LoadCorDebugDll()) != S_OK)
{
return Status;
}
return m_CorAccess->IsCorCode(Native);
}
HRESULT
ProcessInfo::ConvertNativeToIlOffset(ULONG64 Native,
PULONG64 ImageBase,
PULONG32 MethodToken,
PULONG32 MethodOffs)
{
HRESULT Status;
if ((Status = LoadCorDebugDll()) != S_OK)
{
return Status;
}
if ((Status = m_CorAccess->
GetILOffsetFromTargetAddress(Native, ImageBase,
MethodToken, MethodOffs)) != S_OK)
{
return Status;
}
if (!FindImageByOffset(*ImageBase, FALSE))
{
IMAGE_NT_HEADERS64 NtHdr;
char Name[MAX_IMAGE_PATH];
// XXX drewb - Need to work out when the
// module list should be updated for managed
// code that's not system-loaded.
// We don't know about this module, so load it
// right now.
if (m_Target->ReadImageNtHeaders(this, *ImageBase, &NtHdr) == S_OK &&
GetModnameFromImage(this, *ImageBase, NULL,
Name, DIMA(Name), TRUE))
{
char ReloadCmd[MAX_IMAGE_PATH];
PCSTR ArgsRet;
PrintString(ReloadCmd, DIMA(ReloadCmd), "%s=0x%s,0x%x",
Name, FormatAddr64(*ImageBase),
NtHdr.OptionalHeader.SizeOfImage);
m_Target->Reload(m_CurrentThread, ReloadCmd, &ArgsRet);
ImageInfo* Image = FindImageByOffset(*ImageBase, FALSE);
if (Image)
{
Image->m_CorImage = TRUE;
}
}
}
return S_OK;
}
HRESULT
ProcessInfo::GetCorSymbol(ULONG64 Native, PSTR Buffer, ULONG BufferChars,
PULONG64 Displacement)
{
HRESULT Status;
if ((Status = LoadCorDebugDll()) != S_OK)
{
return Status;
}
WCHAR SymWide[MAX_SYMBOL_LEN];
if ((Status = m_CorAccess->
GetCodeSymbolForTargetAddress(Native, SymWide, DIMA(SymWide),
Displacement)) != S_OK)
{
return Status;
}
if (!WideCharToMultiByte(CP_ACP, 0, SymWide, -1,
Buffer, BufferChars,
NULL, NULL))
{
return WIN32_LAST_STATUS();
}
return S_OK;
}
ICorDataStackWalk*
ProcessInfo::StartCorStack(ULONG32 CorThreadId)
{
HRESULT Status;
ICorDataStackWalk* Walk;
if ((Status = LoadCorDebugDll()) != S_OK)
{
return NULL;
}
if ((Status = m_CorAccess->StartStackWalk(CorThreadId,
DAC_STACK_ALL_FRAMES,
&Walk)) != S_OK)
{
return NULL;
}
return Walk;
}
HRESULT
ProcessInfo::Terminate(void)
{
if (!IS_LIVE_USER_TARGET(m_Target))
{
return E_UNEXPECTED;
}
PUSER_DEBUG_SERVICES Services =
((LiveUserTargetInfo*)m_Target)->m_Services;
HRESULT Status = S_OK;
if (!m_Exited)
{
if ((m_Flags & ENG_PROC_EXAMINED) == 0 &&
(Status = Services->TerminateProcess(m_SysHandle,
E_FAIL)) != S_OK)
{
ErrOut("TerminateProcess failed, %s\n",
FormatStatusCode(Status));
}
else
{
MarkExited();
}
}
return Status;
}
HRESULT
ProcessInfo::Detach(void)
{
if (!IS_LIVE_USER_TARGET(m_Target))
{
return E_UNEXPECTED;
}
PUSER_DEBUG_SERVICES Services =
((LiveUserTargetInfo*)m_Target)->m_Services;
HRESULT Status = S_OK;
if (!m_Exited)
{
if ((m_Flags & ENG_PROC_EXAMINED) == 0 &&
(Status = Services->DetachProcess(m_SystemId)) != S_OK)
{
// Older systems don't support detach so
// don't show an error message in that case.
if (Status != E_NOTIMPL)
{
ErrOut("DebugActiveProcessStop(%d) failed, %s\n",
m_SystemId, FormatStatusCode(Status));
}
}
else
{
MarkExited();
}
}
return Status;
}
HRESULT
ProcessInfo::Abandon(void)
{
if (!IS_LIVE_USER_TARGET(m_Target))
{
return E_UNEXPECTED;
}
PUSER_DEBUG_SERVICES Services =
((LiveUserTargetInfo*)m_Target)->m_Services;
HRESULT Status;
if ((Status = Services->AbandonProcess(m_SysHandle)) != S_OK)
{
// Older systems don't support abandon so
// don't show an error message in that case.
if (Status != E_NOTIMPL)
{
ErrOut("Unable to abandon process\n");
}
return Status;
}
// We need to continue any existing event as it won't
// be returned from WaitForDebugEvent again. We
// do not want to resume execution, though.
if (m_Target->m_DeferContinueEvent)
{
if ((Status = Services->ContinueEvent(DBG_CONTINUE)) != S_OK)
{
ErrOut("Unable to continue abandoned event, %s\n",
FormatStatusCode(Status));
return Status;
}
m_Target->m_DeferContinueEvent = FALSE;
}
return Status;
}
HRESULT
ProcessInfo::GetImplicitThreadData(ThreadInfo* Thread, PULONG64 Offset)
{
HRESULT Status;
if (m_ImplicitThreadData == 0 ||
(m_ImplicitThreadDataIsDefault &&
Thread != m_ImplicitThreadDataThread))
{
Status = SetImplicitThreadData(Thread, 0, FALSE);
}
else
{
Status = S_OK;
}
*Offset = m_ImplicitThreadData;
return Status;
}
HRESULT
ProcessInfo::GetImplicitThreadDataTeb(ThreadInfo* Thread, PULONG64 Offset)
{
if (IS_USER_TARGET(m_Target))
{
// In user mode the thread data is the TEB.
return GetImplicitThreadData(Thread, Offset);
}
else if (IS_KERNEL_TARGET(m_Target))
{
return ReadImplicitThreadInfoPointer
(Thread, m_Target->m_KdDebuggerData.OffsetKThreadTeb, Offset);
}
else
{
return E_UNEXPECTED;
}
}
HRESULT
ProcessInfo::SetImplicitThreadData(ThreadInfo* Thread,
ULONG64 Offset, BOOL Verbose)
{
HRESULT Status;
BOOL Default = FALSE;
if (Offset == 0)
{
if (!Thread || Thread->m_Process != this)
{
if (Verbose)
{
ErrOut("Unable to get the current thread data\n");
}
return E_UNEXPECTED;
}
if ((Status = m_Target->
GetThreadInfoDataOffset(Thread, 0, &Offset)) != S_OK)
{
if (Verbose)
{
ErrOut("Unable to get the current thread data\n");
}
return Status;
}
if (Offset == 0)
{
if (Verbose)
{
ErrOut("Current thread data is NULL\n");
}
return E_FAIL;
}
Default = TRUE;
}
m_ImplicitThreadData = Offset;
m_ImplicitThreadDataIsDefault = Default;
m_ImplicitThreadDataThread = Thread;
return S_OK;
}
HRESULT
ProcessInfo::ReadImplicitThreadInfoPointer(ThreadInfo* Thread,
ULONG Offset, PULONG64 Ptr)
{
HRESULT Status;
ULONG64 CurThread;
// Retrieve the current ETHREAD.
if ((Status = GetImplicitThreadData(Thread, &CurThread)) != S_OK)
{
return Status;
}
return m_Target->
ReadPointer(this, m_Target->m_Machine, CurThread + Offset, Ptr);
}
//----------------------------------------------------------------------------
//
// Functions.
//
//----------------------------------------------------------------------------
ProcessInfo*
FindAnyProcessByUserId(ULONG Id)
{
TargetInfo* Target;
ProcessInfo* Process;
ForAllLayersToProcess()
{
if (Process->m_UserId == Id)
{
return Process;
}
}
return NULL;
}
ProcessInfo*
FindAnyProcessBySystemId(ULONG Id)
{
TargetInfo* Target;
ProcessInfo* Process;
ForAllLayersToProcess()
{
if (Process->m_SystemId == Id)
{
return Process;
}
}
return NULL;
}
void
ParseProcessCmds(void)
{
char Ch;
ProcessInfo* Process;
ULONG Id;
if (!g_Target)
{
error(BADTHREAD);
}
Ch = PeekChar();
if (Ch == '\0' || Ch == ';')
{
g_Target->OutputProcessInfo(NULL);
}
else
{
Process = g_Process;
g_CurCmd++;
if (Ch == '.')
{
;
}
else if (Ch == '#')
{
Process = g_EventProcess;
}
else if (Ch == '*')
{
Process = NULL;
}
else if (Ch == '[')
{
g_CurCmd--;
Id = (ULONG)GetTermExpression("Process ID missing from");
Process = FindAnyProcessByUserId(Id);
if (Process == NULL)
{
error(BADPROCESS);
}
}
else if (Ch == '~')
{
if (*g_CurCmd != '[')
{
error(SYNTAX);
}
Id = (ULONG)GetTermExpression("Process ID missing from");
Process = FindAnyProcessBySystemId(Id);
if (Process == NULL)
{
error(BADPROCESS);
}
}
else if (Ch >= '0' && Ch <= '9')
{
Id = 0;
do
{
Id = Id * 10 + Ch - '0';
Ch = *g_CurCmd++;
} while (Ch >= '0' && Ch <= '9');
g_CurCmd--;
Process = FindAnyProcessByUserId(Id);
if (Process == NULL)
{
error(BADPROCESS);
}
}
else
{
g_CurCmd--;
}
Ch = PeekChar();
if (Ch == '\0' || Ch == ';')
{
g_Target->OutputProcessInfo(Process);
}
else
{
g_CurCmd++;
if (tolower(Ch) == 's')
{
if (Process == NULL)
{
error(BADPROCESS);
}
if (Process->m_CurrentThread == NULL)
{
Process->m_CurrentThread = Process->m_ThreadHead;
if (Process->m_CurrentThread == NULL)
{
error(BADPROCESS);
}
}
SetPromptThread(Process->m_CurrentThread,
SPT_DEFAULT_OCI_FLAGS);
}
else
{
g_CurCmd--;
}
}
}
}
//----------------------------------------------------------------------------
//
// Process creation and attach functions.
//
//----------------------------------------------------------------------------
HRESULT
TerminateProcesses(void)
{
HRESULT Status;
if (!AnyLiveUserTargets())
{
// Nothing to do.
return S_OK;
}
if (g_SymOptions & SYMOPT_SECURE)
{
ErrOut("SECURE: Process termination disallowed\n");
return HRESULT_FROM_WIN32(ERROR_ACCESS_DENIED);
}
Status = PrepareForSeparation();
if (Status != S_OK)
{
ErrOut("Unable to prepare for termination, %s\n",
FormatStatusCode(Status));
return Status;
}
TargetInfo* Target;
HRESULT SingleStatus;
ForAllLayersToTarget()
{
// If we fail in the middle of killing things there
// isn't much that can be done so just attempt to
// kill as many things as possible.
if (IS_LIVE_USER_TARGET(Target) &&
(SingleStatus = Target->TerminateProcesses()) != S_OK)
{
Status = SingleStatus;
}
}
DiscardLastEvent();
return Status;
}
HRESULT
DetachProcesses(void)
{
HRESULT Status;
if (!AnyLiveUserTargets())
{
// Nothing to do.
return S_OK;
}
if (g_SymOptions & SYMOPT_SECURE)
{
ErrOut("SECURE: Process detach disallowed\n");
return HRESULT_FROM_WIN32(ERROR_ACCESS_DENIED);
}
Status = PrepareForSeparation();
if (Status != S_OK)
{
ErrOut("Unable to prepare for detach, %s\n",
FormatStatusCode(Status));
return Status;
}
TargetInfo* Target;
HRESULT SingleStatus;
ForAllLayersToTarget()
{
// If we fail in the middle of detaching things there
// isn't much that can be done so just attempt to
// detach as many things as possible.
if (IS_LIVE_USER_TARGET(Target) &&
(SingleStatus = Target->DetachProcesses()) != S_OK)
{
Status = SingleStatus;
}
}
DiscardLastEvent();
return Status;
}
HRESULT
SeparateCurrentProcess(ULONG Mode, PSTR Description)
{
if (!IS_LIVE_USER_TARGET(g_Target) ||
g_Process == NULL)
{
return E_INVALIDARG;
}
if (g_SymOptions & SYMOPT_SECURE)
{
ErrOut("SECURE: Process separation disallowed\n");
return HRESULT_FROM_WIN32(ERROR_ACCESS_DENIED);
}
PUSER_DEBUG_SERVICES Services =
((LiveUserTargetInfo*)g_Target)->m_Services;
if ((Mode == SEP_DETACH || Mode == SEP_TERMINATE) &&
IS_CUR_CONTEXT_ACCESSIBLE())
{
ADDR Pc;
// Move the PC past any current breakpoint instruction so that
// the process has a chance of running.
g_Machine->GetPC(&Pc);
if (g_Machine->IsBreakpointInstruction(g_Process, &Pc))
{
g_Machine->AdjustPCPastBreakpointInstruction
(&Pc, DEBUG_BREAKPOINT_CODE);
}
}
// Flush any buffered changes.
if (IS_CUR_CONTEXT_ACCESSIBLE())
{
g_Target->FlushRegContext();
}
if (g_EventProcess == g_Process && Mode != SEP_TERMINATE)
{
if (g_Target->m_DeferContinueEvent)
{
Services->ContinueEvent(DBG_CONTINUE);
g_Target->m_DeferContinueEvent = FALSE;
}
}
((LiveUserTargetInfo*)g_Target)->
SuspendResumeThreads(g_Process, FALSE, NULL);
HRESULT Status;
PSTR Operation;
switch(Mode)
{
case SEP_DETACH:
Status = g_Process->Detach();
Operation = "Detached";
break;
case SEP_TERMINATE:
Status = g_Process->Terminate();
if ((g_Process->m_Flags & ENG_PROC_EXAMINED) == 0)
{
Operation = "Terminated. "
"Exit thread and process events will occur.";
}
else
{
Operation = "Terminated";
}
break;
case SEP_ABANDON:
Status = g_Process->Abandon();
Operation = "Abandoned";
break;
}
if (Status == S_OK)
{
if (Description != NULL)
{
strcpy(Description, Operation);
}
// If we're detaching or abandoning it's safe to go
// ahead and remove the process now so that it
// can't be access further.
// If we're terminating we have to wait for
// the exit events to come through so
// keep the process until that happens.
if (Mode != SEP_TERMINATE)
{
delete g_Process;
SetToAnyLayers(TRUE);
}
else
{
g_Process->m_Exited = FALSE;
}
}
else
{
((LiveUserTargetInfo*)g_Target)->
SuspendResumeThreads(g_Process, TRUE, NULL);
}
return Status;
}