2494 lines
61 KiB
C++
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;
|
||
|
}
|