3029 lines
78 KiB
C++
3029 lines
78 KiB
C++
//----------------------------------------------------------------------------
|
|
//
|
|
// Extension DLL support.
|
|
//
|
|
// Copyright (C) Microsoft Corporation, 1997-2002.
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
#include "ntsdp.hpp"
|
|
#include <time.h>
|
|
|
|
/*
|
|
* _NT_DEBUG_OPTIONS support. Each option in g_EnvDbgOptionNames must have a
|
|
* corresponding OPTION_* define, in the same order.
|
|
*/
|
|
DWORD g_EnvDbgOptions;
|
|
char* g_EnvDbgOptionNames[OPTION_COUNT] =
|
|
{
|
|
"NOEXTWARNING",
|
|
"NOVERSIONCHECK",
|
|
};
|
|
|
|
EXTDLL *g_ExtDlls;
|
|
LPTSTR g_BaseExtensionSearchPath = NULL;
|
|
|
|
ULONG64 g_ExtThread;
|
|
|
|
ULONG g_ExtGetExpressionRemainderIndex;
|
|
BOOL g_ExtGetExpressionSuccess;
|
|
|
|
WOW64EXTSPROC g_Wow64exts;
|
|
EXTDLL* g_Wow64ExtDll;
|
|
|
|
WMI_FORMAT_TRACE_DATA g_WmiFormatTraceData;
|
|
EXTDLL* g_WmiExtDll;
|
|
|
|
DEBUG_SCOPE g_ExtThreadSavedScope;
|
|
BOOL g_ExtThreadScopeSaved;
|
|
|
|
//
|
|
// Functions prototyped specifically for compatibility with extension
|
|
// callback prototypes.
|
|
//
|
|
|
|
VOID WDBGAPIV
|
|
ExtOutput64(
|
|
PCSTR lpFormat,
|
|
...
|
|
);
|
|
|
|
VOID WDBGAPIV
|
|
ExtOutput32(
|
|
PCSTR lpFormat,
|
|
...
|
|
);
|
|
|
|
ULONG64
|
|
ExtGetExpression(
|
|
PCSTR CommandString
|
|
);
|
|
|
|
ULONG
|
|
ExtGetExpression32(
|
|
PCSTR CommandString
|
|
);
|
|
|
|
void
|
|
ExtGetSymbol(
|
|
ULONG64 Offset,
|
|
PCHAR Buffer,
|
|
PULONG64 Displacement
|
|
);
|
|
|
|
void
|
|
ExtGetSymbol32(
|
|
ULONG Offset,
|
|
PCHAR Buffer,
|
|
PULONG Displacement
|
|
);
|
|
|
|
DWORD
|
|
ExtDisasm(
|
|
PULONG64 lpOffset,
|
|
PCSTR lpBuffer,
|
|
ULONG fShowEA
|
|
);
|
|
|
|
DWORD
|
|
ExtDisasm32(
|
|
PULONG lpOffset,
|
|
PCSTR lpBuffer,
|
|
ULONG fShowEA
|
|
);
|
|
|
|
BOOL
|
|
ExtReadVirtualMemory(
|
|
IN ULONG64 Address,
|
|
OUT PUCHAR Buffer,
|
|
IN ULONG Length,
|
|
OUT PULONG BytesRead
|
|
);
|
|
|
|
BOOL
|
|
ExtReadVirtualMemory32(
|
|
IN ULONG Address,
|
|
OUT PUCHAR Buffer,
|
|
IN ULONG Length,
|
|
OUT PULONG BytesRead
|
|
);
|
|
|
|
ULONG
|
|
ExtWriteVirtualMemory(
|
|
IN ULONG64 Address,
|
|
IN LPCVOID Buffer,
|
|
IN ULONG Length,
|
|
OUT PULONG BytesWritten
|
|
);
|
|
|
|
ULONG
|
|
ExtWriteVirtualMemory32(
|
|
IN ULONG Address,
|
|
IN LPCVOID Buffer,
|
|
IN ULONG Length,
|
|
OUT PULONG BytesWritten
|
|
);
|
|
|
|
BOOL ExtGetThreadContext(DWORD Processor,
|
|
PVOID Context,
|
|
DWORD SizeOfContext);
|
|
BOOL ExtSetThreadContext(DWORD Processor,
|
|
PVOID Context,
|
|
DWORD SizeOfContext);
|
|
|
|
BOOL
|
|
ExtIoctl(
|
|
USHORT IoctlType,
|
|
LPVOID lpvData,
|
|
DWORD cbSize
|
|
);
|
|
|
|
BOOL
|
|
ExtIoctl32(
|
|
USHORT IoctlType,
|
|
LPVOID lpvData,
|
|
DWORD cbSize
|
|
);
|
|
|
|
DWORD
|
|
ExtCallStack(
|
|
DWORD64 FramePointer,
|
|
DWORD64 StackPointer,
|
|
DWORD64 ProgramCounter,
|
|
PEXTSTACKTRACE64 StackFrames,
|
|
DWORD Frames
|
|
);
|
|
|
|
DWORD
|
|
ExtCallStack32(
|
|
DWORD FramePointer,
|
|
DWORD StackPointer,
|
|
DWORD ProgramCounter,
|
|
PEXTSTACKTRACE32 StackFrames,
|
|
DWORD Frames
|
|
);
|
|
|
|
BOOL
|
|
ExtReadPhysicalMemory(
|
|
ULONGLONG Address,
|
|
PVOID Buffer,
|
|
ULONG Length,
|
|
PULONG BytesRead
|
|
);
|
|
|
|
BOOL
|
|
ExtWritePhysicalMemory(
|
|
ULONGLONG Address,
|
|
LPCVOID Buffer,
|
|
ULONG Length,
|
|
PULONG BytesWritten
|
|
);
|
|
|
|
WINDBG_EXTENSION_APIS64 g_WindbgExtensions64 =
|
|
{
|
|
sizeof(g_WindbgExtensions64),
|
|
ExtOutput64,
|
|
ExtGetExpression,
|
|
ExtGetSymbol,
|
|
ExtDisasm,
|
|
CheckUserInterrupt,
|
|
(PWINDBG_READ_PROCESS_MEMORY_ROUTINE64)ExtReadVirtualMemory,
|
|
ExtWriteVirtualMemory,
|
|
(PWINDBG_GET_THREAD_CONTEXT_ROUTINE)ExtGetThreadContext,
|
|
(PWINDBG_SET_THREAD_CONTEXT_ROUTINE)ExtSetThreadContext,
|
|
(PWINDBG_IOCTL_ROUTINE)ExtIoctl,
|
|
ExtCallStack
|
|
};
|
|
|
|
WINDBG_EXTENSION_APIS32 g_WindbgExtensions32 =
|
|
{
|
|
sizeof(g_WindbgExtensions32),
|
|
ExtOutput32,
|
|
ExtGetExpression32,
|
|
ExtGetSymbol32,
|
|
ExtDisasm32,
|
|
CheckUserInterrupt,
|
|
(PWINDBG_READ_PROCESS_MEMORY_ROUTINE32)ExtReadVirtualMemory32,
|
|
ExtWriteVirtualMemory32,
|
|
(PWINDBG_GET_THREAD_CONTEXT_ROUTINE)ExtGetThreadContext,
|
|
(PWINDBG_SET_THREAD_CONTEXT_ROUTINE)ExtSetThreadContext,
|
|
(PWINDBG_IOCTL_ROUTINE)ExtIoctl32,
|
|
ExtCallStack32
|
|
};
|
|
|
|
WINDBG_OLDKD_EXTENSION_APIS g_KdExtensions =
|
|
{
|
|
sizeof(g_KdExtensions),
|
|
ExtOutput32,
|
|
ExtGetExpression32,
|
|
ExtGetSymbol32,
|
|
ExtDisasm32,
|
|
CheckUserInterrupt,
|
|
(PWINDBG_READ_PROCESS_MEMORY_ROUTINE32)ExtReadVirtualMemory32,
|
|
ExtWriteVirtualMemory32,
|
|
(PWINDBG_OLDKD_READ_PHYSICAL_MEMORY)ExtReadPhysicalMemory,
|
|
(PWINDBG_OLDKD_WRITE_PHYSICAL_MEMORY)ExtWritePhysicalMemory
|
|
};
|
|
|
|
//----------------------------------------------------------------------------
|
|
//
|
|
// Callback functions for extensions.
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
VOID WDBGAPIV
|
|
ExtOutput64(
|
|
PCSTR lpFormat,
|
|
...
|
|
)
|
|
{
|
|
va_list Args;
|
|
va_start(Args, lpFormat);
|
|
MaskOutVa(DEBUG_OUTPUT_NORMAL, lpFormat, Args, TRUE);
|
|
va_end(Args);
|
|
|
|
// Make sure output for long-running extensions appears regularly.
|
|
TimedFlushCallbacks();
|
|
}
|
|
|
|
VOID WDBGAPIV
|
|
ExtOutput32(
|
|
PCSTR lpFormat,
|
|
...
|
|
)
|
|
{
|
|
va_list Args;
|
|
va_start(Args, lpFormat);
|
|
MaskOutVa(DEBUG_OUTPUT_NORMAL, lpFormat, Args, FALSE);
|
|
va_end(Args);
|
|
|
|
// Make sure output for long-running extensions appears regularly.
|
|
TimedFlushCallbacks();
|
|
}
|
|
|
|
ULONG64
|
|
ExtGetExpression(
|
|
PCSTR CommandString
|
|
)
|
|
{
|
|
g_ExtGetExpressionSuccess = FALSE;
|
|
|
|
if (CommandString == NULL)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
ULONG64 ReturnValue;
|
|
PSTR SaveCommand = g_CurCmd;
|
|
PSTR SaveStart = g_CommandStart;
|
|
|
|
if (IS_USER_TARGET(g_Target))
|
|
{
|
|
if ( strcmp(CommandString, "WOW_BIG_BDE_HACK") == 0 )
|
|
{
|
|
return( (ULONG_PTR)(&segtable[0]) );
|
|
}
|
|
|
|
//
|
|
// this is because the kdexts MUST include the address-of operator
|
|
// on all getexpression calls for windbg/c expression evaluators
|
|
//
|
|
if (*CommandString == '&')
|
|
{
|
|
CommandString++;
|
|
}
|
|
}
|
|
|
|
g_CurCmd = (PSTR)CommandString;
|
|
g_CommandStart = (PSTR)CommandString;
|
|
g_DisableErrorPrint++;
|
|
|
|
EvalExpression* RelChain = g_EvalReleaseChain;
|
|
g_EvalReleaseChain = NULL;
|
|
|
|
__try
|
|
{
|
|
// ntsd/windbg extensions always use the MASM-style
|
|
// expression evaluator for compatibility.
|
|
EvalExpression* Eval = GetEvaluator(DEBUG_EXPR_MASM, FALSE);
|
|
ReturnValue = Eval->EvalCurNum();
|
|
ReleaseEvaluator(Eval);
|
|
g_ExtGetExpressionSuccess = TRUE;
|
|
}
|
|
__except(CommandExceptionFilter(GetExceptionInformation()))
|
|
{
|
|
ReturnValue = 0;
|
|
}
|
|
g_ExtGetExpressionRemainderIndex =
|
|
(ULONG)(g_CurCmd - g_CommandStart);
|
|
g_EvalReleaseChain = RelChain;
|
|
g_DisableErrorPrint--;
|
|
g_CurCmd = SaveCommand;
|
|
g_CommandStart = SaveStart;
|
|
|
|
// Make sure output for long-running extensions appears regularly.
|
|
TimedFlushCallbacks();
|
|
|
|
return ReturnValue;
|
|
}
|
|
|
|
ULONG
|
|
ExtGetExpression32(
|
|
LPCSTR CommandString
|
|
)
|
|
{
|
|
return (ULONG)ExtGetExpression(CommandString);
|
|
}
|
|
|
|
void
|
|
ExtGetSymbol (
|
|
ULONG64 offset,
|
|
PCHAR pchBuffer,
|
|
PULONG64 pDisplacement
|
|
)
|
|
{
|
|
// No way to know how much space we're given, so
|
|
// just assume 256, which many extensions pass in
|
|
GetSymbol(offset, pchBuffer, 256, pDisplacement);
|
|
|
|
// Make sure output for long-running extensions appears regularly.
|
|
TimedFlushCallbacks();
|
|
}
|
|
|
|
void
|
|
ExtGetSymbol32(
|
|
ULONG offset,
|
|
PCHAR pchBuffer,
|
|
PULONG pDisplacement
|
|
)
|
|
{
|
|
ULONG64 Displacement;
|
|
|
|
// No way to know how much space we're given, so
|
|
// just assume 256, which many extensions pass in
|
|
GetSymbol(EXTEND64(offset), pchBuffer, 256, &Displacement);
|
|
*pDisplacement = (ULONG)Displacement;
|
|
|
|
// Make sure output for long-running extensions appears regularly.
|
|
TimedFlushCallbacks();
|
|
}
|
|
|
|
DWORD
|
|
ExtDisasm(
|
|
ULONG64 *lpOffset,
|
|
PCSTR lpBuffer,
|
|
ULONG fShowEA
|
|
)
|
|
{
|
|
if (!IS_CUR_MACHINE_ACCESSIBLE())
|
|
{
|
|
ErrOut("ExtDisasm called before debugger initialized\n");
|
|
return FALSE;
|
|
}
|
|
|
|
ADDR tempAddr;
|
|
BOOL ret;
|
|
|
|
Type(tempAddr) = ADDR_FLAT | FLAT_COMPUTED;
|
|
Off(tempAddr) = Flat(tempAddr) = *lpOffset;
|
|
ret = g_Machine->
|
|
Disassemble(g_Process, &tempAddr, (PSTR)lpBuffer, (BOOL) fShowEA);
|
|
*lpOffset = Flat(tempAddr);
|
|
return ret;
|
|
}
|
|
|
|
DWORD
|
|
ExtDisasm32(
|
|
ULONG *lpOffset,
|
|
PCSTR lpBuffer,
|
|
ULONG fShowEA
|
|
)
|
|
{
|
|
ULONG64 Offset = EXTEND64(*lpOffset);
|
|
DWORD rval = ExtDisasm(&Offset, lpBuffer, fShowEA);
|
|
*lpOffset = (ULONG)Offset;
|
|
return rval;
|
|
}
|
|
|
|
BOOL
|
|
ExtGetThreadContext(DWORD Processor,
|
|
PVOID Context,
|
|
DWORD SizeOfContext)
|
|
{
|
|
if (!IS_CUR_MACHINE_ACCESSIBLE())
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
// This get may be getting the context of the thread
|
|
// currently cached by the register code. Make sure
|
|
// the cache is flushed.
|
|
g_Target->FlushRegContext();
|
|
|
|
CROSS_PLATFORM_CONTEXT TargetContext;
|
|
|
|
g_Target->m_Machine->
|
|
InitializeContextFlags(&TargetContext, g_Target->m_SystemVersion);
|
|
if (g_Target->GetContext(IS_KERNEL_TARGET(g_Target) ?
|
|
VIRTUAL_THREAD_HANDLE(Processor) : Processor,
|
|
&TargetContext) == S_OK &&
|
|
g_Machine->ConvertContextTo(&TargetContext, g_Target->m_SystemVersion,
|
|
SizeOfContext, Context) == S_OK)
|
|
{
|
|
return TRUE;
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
BOOL
|
|
ExtSetThreadContext(DWORD Processor,
|
|
PVOID Context,
|
|
DWORD SizeOfContext)
|
|
{
|
|
if (!IS_CUR_MACHINE_ACCESSIBLE())
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
BOOL Status;
|
|
|
|
// This set may be setting the context of the thread
|
|
// currently cached by the register code. Make sure
|
|
// the cache is invalidated.
|
|
g_Target->ChangeRegContext(NULL);
|
|
|
|
CROSS_PLATFORM_CONTEXT TargetContext;
|
|
if (g_Machine->
|
|
ConvertContextFrom(&TargetContext, g_Target->m_SystemVersion,
|
|
SizeOfContext, Context) == S_OK &&
|
|
g_Target->SetContext(IS_KERNEL_TARGET(g_Target) ?
|
|
VIRTUAL_THREAD_HANDLE(Processor) : Processor,
|
|
&TargetContext) == S_OK)
|
|
{
|
|
Status = TRUE;
|
|
}
|
|
else
|
|
{
|
|
Status = FALSE;
|
|
}
|
|
|
|
// Reset the current thread.
|
|
g_Target->ChangeRegContext(g_Thread);
|
|
|
|
return Status;
|
|
}
|
|
|
|
BOOL
|
|
ExtReadVirtualMemory(
|
|
IN ULONG64 pBufSrc,
|
|
OUT PUCHAR pBufDest,
|
|
IN ULONG count,
|
|
OUT PULONG pcTotalBytesRead
|
|
)
|
|
{
|
|
// Make sure output for long-running extensions appears regularly.
|
|
TimedFlushCallbacks();
|
|
|
|
ULONG BytesTemp;
|
|
return g_Target->
|
|
ReadVirtual(g_Process,
|
|
pBufSrc, pBufDest, count, pcTotalBytesRead != NULL ?
|
|
pcTotalBytesRead : &BytesTemp) == S_OK;
|
|
}
|
|
|
|
BOOL
|
|
ExtReadVirtualMemory32(
|
|
IN ULONG pBufSrc,
|
|
OUT PUCHAR pBufDest,
|
|
IN ULONG count,
|
|
OUT PULONG pcTotalBytesRead
|
|
)
|
|
{
|
|
// Make sure output for long-running extensions appears regularly.
|
|
TimedFlushCallbacks();
|
|
|
|
ULONG BytesTemp;
|
|
return g_Target->
|
|
ReadVirtual(g_Process, EXTEND64(pBufSrc), pBufDest, count,
|
|
pcTotalBytesRead != NULL ?
|
|
pcTotalBytesRead : &BytesTemp) == S_OK;
|
|
}
|
|
|
|
DWORD
|
|
ExtWriteVirtualMemory(
|
|
IN ULONG64 addr,
|
|
IN LPCVOID buffer,
|
|
IN ULONG count,
|
|
OUT PULONG pcBytesWritten
|
|
)
|
|
{
|
|
ULONG BytesTemp;
|
|
|
|
return (g_Target->WriteVirtual(g_Process, addr, (PVOID)buffer, count,
|
|
pcBytesWritten != NULL ?
|
|
pcBytesWritten : &BytesTemp) == S_OK);
|
|
}
|
|
|
|
ULONG
|
|
ExtWriteVirtualMemory32 (
|
|
IN ULONG addr,
|
|
IN LPCVOID buffer,
|
|
IN ULONG count,
|
|
OUT PULONG pcBytesWritten
|
|
)
|
|
{
|
|
ULONG BytesTemp;
|
|
return (g_Target->WriteVirtual(g_Process, EXTEND64(addr),
|
|
(PVOID)buffer, count,
|
|
pcBytesWritten != NULL ?
|
|
pcBytesWritten : &BytesTemp) == S_OK);
|
|
}
|
|
|
|
BOOL
|
|
ExtReadPhysicalMemory(
|
|
ULONGLONG pBufSrc,
|
|
PVOID pBufDest,
|
|
ULONG count,
|
|
PULONG TotalBytesRead
|
|
)
|
|
{
|
|
// Make sure output for long-running extensions appears regularly.
|
|
TimedFlushCallbacks();
|
|
|
|
if (ARGUMENT_PRESENT(TotalBytesRead))
|
|
{
|
|
*TotalBytesRead = 0;
|
|
}
|
|
|
|
ULONG BytesTemp;
|
|
return g_Target->ReadPhysical(pBufSrc, pBufDest, count,
|
|
PHYS_FLAG_DEFAULT,
|
|
TotalBytesRead != NULL ?
|
|
TotalBytesRead : &BytesTemp) == S_OK;
|
|
}
|
|
|
|
BOOL
|
|
ExtWritePhysicalMemory (
|
|
ULONGLONG pBufDest,
|
|
LPCVOID pBufSrc,
|
|
ULONG count,
|
|
PULONG TotalBytesWritten
|
|
)
|
|
{
|
|
if (ARGUMENT_PRESENT(TotalBytesWritten))
|
|
{
|
|
*TotalBytesWritten = 0;
|
|
}
|
|
|
|
ULONG BytesTemp;
|
|
return g_Target->WritePhysical(pBufDest, (PVOID)pBufSrc, count,
|
|
PHYS_FLAG_DEFAULT,
|
|
TotalBytesWritten != NULL ?
|
|
TotalBytesWritten : &BytesTemp) == S_OK;
|
|
}
|
|
|
|
BOOL
|
|
ExtReadPhysicalMemoryWithFlags(
|
|
ULONGLONG pBufSrc,
|
|
PVOID pBufDest,
|
|
ULONG count,
|
|
ULONG Flags,
|
|
PULONG TotalBytesRead
|
|
)
|
|
{
|
|
// Make sure output for long-running extensions appears regularly.
|
|
TimedFlushCallbacks();
|
|
|
|
if (ARGUMENT_PRESENT(TotalBytesRead))
|
|
{
|
|
*TotalBytesRead = 0;
|
|
}
|
|
|
|
ULONG BytesTemp;
|
|
return g_Target->ReadPhysical(pBufSrc, pBufDest, count, Flags,
|
|
TotalBytesRead != NULL ?
|
|
TotalBytesRead : &BytesTemp) == S_OK;
|
|
}
|
|
|
|
BOOL
|
|
ExtWritePhysicalMemoryWithFlags(
|
|
ULONGLONG pBufDest,
|
|
LPCVOID pBufSrc,
|
|
ULONG count,
|
|
ULONG Flags,
|
|
PULONG TotalBytesWritten
|
|
)
|
|
{
|
|
if (ARGUMENT_PRESENT(TotalBytesWritten))
|
|
{
|
|
*TotalBytesWritten = 0;
|
|
}
|
|
|
|
ULONG BytesTemp;
|
|
return g_Target->WritePhysical(pBufDest, (PVOID)pBufSrc, count, Flags,
|
|
TotalBytesWritten != NULL ?
|
|
TotalBytesWritten : &BytesTemp) == S_OK;
|
|
}
|
|
|
|
DWORD
|
|
ExtCallStack(
|
|
DWORD64 FramePointer,
|
|
DWORD64 StackPointer,
|
|
DWORD64 ProgramCounter,
|
|
PEXTSTACKTRACE64 ExtStackFrames,
|
|
DWORD Frames
|
|
)
|
|
{
|
|
PDEBUG_STACK_FRAME StackFrames;
|
|
DWORD FrameCount;
|
|
DWORD i;
|
|
|
|
StackFrames = (PDEBUG_STACK_FRAME)
|
|
malloc( sizeof(StackFrames[0]) * Frames );
|
|
if (!StackFrames)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
ULONG PtrDef =
|
|
(!ProgramCounter ? STACK_INSTR_DEFAULT : 0) |
|
|
(!StackPointer ? STACK_STACK_DEFAULT : 0) |
|
|
(!FramePointer ? STACK_FRAME_DEFAULT : 0);
|
|
|
|
FrameCount = StackTrace( NULL,
|
|
FramePointer, StackPointer, ProgramCounter,
|
|
PtrDef, StackFrames, Frames,
|
|
g_ExtThread, 0, FALSE );
|
|
|
|
for (i = 0; i < FrameCount; i++)
|
|
{
|
|
ExtStackFrames[i].FramePointer = StackFrames[i].FrameOffset;
|
|
ExtStackFrames[i].ProgramCounter = StackFrames[i].InstructionOffset;
|
|
ExtStackFrames[i].ReturnAddress = StackFrames[i].ReturnOffset;
|
|
ExtStackFrames[i].Args[0] = StackFrames[i].Params[0];
|
|
ExtStackFrames[i].Args[1] = StackFrames[i].Params[1];
|
|
ExtStackFrames[i].Args[2] = StackFrames[i].Params[2];
|
|
ExtStackFrames[i].Args[3] = StackFrames[i].Params[3];
|
|
}
|
|
|
|
free( StackFrames );
|
|
|
|
if (g_ExtThreadScopeSaved)
|
|
{
|
|
PopScope(&g_ExtThreadSavedScope);
|
|
g_ExtThreadScopeSaved = FALSE;
|
|
}
|
|
|
|
g_ExtThread = 0;
|
|
|
|
return FrameCount;
|
|
}
|
|
|
|
DWORD
|
|
ExtCallStack32(
|
|
DWORD FramePointer,
|
|
DWORD StackPointer,
|
|
DWORD ProgramCounter,
|
|
PEXTSTACKTRACE32 ExtStackFrames,
|
|
DWORD Frames
|
|
)
|
|
{
|
|
PDEBUG_STACK_FRAME StackFrames;
|
|
DWORD FrameCount;
|
|
DWORD i;
|
|
|
|
StackFrames = (PDEBUG_STACK_FRAME)
|
|
malloc( sizeof(StackFrames[0]) * Frames );
|
|
if (!StackFrames)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
ULONG PtrDef =
|
|
(!ProgramCounter ? STACK_INSTR_DEFAULT : 0) |
|
|
(!StackPointer ? STACK_STACK_DEFAULT : 0) |
|
|
(!FramePointer ? STACK_FRAME_DEFAULT : 0);
|
|
|
|
FrameCount = StackTrace(NULL,
|
|
EXTEND64(FramePointer),
|
|
EXTEND64(StackPointer),
|
|
EXTEND64(ProgramCounter),
|
|
PtrDef,
|
|
StackFrames,
|
|
Frames,
|
|
g_ExtThread,
|
|
0,
|
|
FALSE);
|
|
|
|
for (i=0; i<FrameCount; i++)
|
|
{
|
|
ExtStackFrames[i].FramePointer = (ULONG)StackFrames[i].FrameOffset;
|
|
ExtStackFrames[i].ProgramCounter = (ULONG)StackFrames[i].InstructionOffset;
|
|
ExtStackFrames[i].ReturnAddress = (ULONG)StackFrames[i].ReturnOffset;
|
|
ExtStackFrames[i].Args[0] = (ULONG)StackFrames[i].Params[0];
|
|
ExtStackFrames[i].Args[1] = (ULONG)StackFrames[i].Params[1];
|
|
ExtStackFrames[i].Args[2] = (ULONG)StackFrames[i].Params[2];
|
|
ExtStackFrames[i].Args[3] = (ULONG)StackFrames[i].Params[3];
|
|
}
|
|
|
|
free( StackFrames );
|
|
if (g_ExtThreadScopeSaved)
|
|
{
|
|
PopScope(&g_ExtThreadSavedScope);
|
|
g_ExtThreadScopeSaved = FALSE;
|
|
}
|
|
|
|
g_ExtThread = 0;
|
|
|
|
return FrameCount;
|
|
}
|
|
|
|
BOOL
|
|
ExtIoctl(
|
|
USHORT IoctlType,
|
|
LPVOID lpvData,
|
|
DWORD cbSize
|
|
)
|
|
{
|
|
HRESULT Status;
|
|
BOOL Bool;
|
|
DWORD cb = 0;
|
|
PPHYSICAL phy;
|
|
PPHYSICAL_WITH_FLAGS phyf;
|
|
PIOSPACE64 is;
|
|
PIOSPACE_EX64 isex;
|
|
PBUSDATA busdata;
|
|
PREAD_WRITE_MSR msr;
|
|
PREADCONTROLSPACE64 prc;
|
|
PPROCESSORINFO pi;
|
|
PSEARCHMEMORY psr;
|
|
PSYM_DUMP_PARAM pSym;
|
|
PGET_CURRENT_THREAD_ADDRESS pct;
|
|
PGET_CURRENT_PROCESS_ADDRESS pcp;
|
|
|
|
// Make sure output for long-running extensions appears regularly.
|
|
TimedFlushCallbacks();
|
|
|
|
if (!g_Target)
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
switch( IoctlType )
|
|
{
|
|
case IG_KD_CONTEXT:
|
|
if (!g_Target)
|
|
{
|
|
return FALSE;
|
|
}
|
|
pi = (PPROCESSORINFO) lpvData;
|
|
pi->Processor = (USHORT)CURRENT_PROC;
|
|
pi->NumberProcessors = (USHORT) g_Target->m_NumProcessors;
|
|
return TRUE;
|
|
|
|
case IG_READ_CONTROL_SPACE:
|
|
// KSPECIAL_REGISTER content is kept in control space
|
|
// so accessing control space may touch data that's
|
|
// cached in the current machine KSPECIAL_REGISTERS.
|
|
// Flush the current machine to maintain consistency.
|
|
if (IS_CUR_MACHINE_ACCESSIBLE())
|
|
{
|
|
g_Target->FlushRegContext();
|
|
}
|
|
|
|
prc = (PREADCONTROLSPACE64)lpvData;
|
|
Status = g_Target->ReadControl( prc->Processor,
|
|
prc->Address,
|
|
prc->Buf,
|
|
prc->BufLen,
|
|
&cb
|
|
);
|
|
prc->BufLen = cb;
|
|
return Status == S_OK;
|
|
|
|
case IG_WRITE_CONTROL_SPACE:
|
|
// KSPECIAL_REGISTER content is kept in control space
|
|
// so accessing control space may touch data that's
|
|
// cached in the current machine KSPECIAL_REGISTERS.
|
|
// Flush the current machine to maintain consistency.
|
|
if (IS_CUR_MACHINE_ACCESSIBLE())
|
|
{
|
|
g_Target->FlushRegContext();
|
|
}
|
|
|
|
prc = (PREADCONTROLSPACE64)lpvData;
|
|
Status = g_Target->WriteControl( prc->Processor,
|
|
prc->Address,
|
|
prc->Buf,
|
|
prc->BufLen,
|
|
&cb
|
|
);
|
|
prc->BufLen = cb;
|
|
return Status == S_OK;
|
|
|
|
case IG_READ_IO_SPACE:
|
|
is = (PIOSPACE64)lpvData;
|
|
Status = g_Target->ReadIo( Isa, 0, 1, is->Address, &is->Data,
|
|
is->Length, &cb );
|
|
return Status == S_OK;
|
|
|
|
case IG_WRITE_IO_SPACE:
|
|
is = (PIOSPACE64)lpvData;
|
|
Status = g_Target->WriteIo( Isa, 0, 1, is->Address, &is->Data,
|
|
is->Length, &cb );
|
|
return Status == S_OK;
|
|
|
|
case IG_READ_IO_SPACE_EX:
|
|
isex = (PIOSPACE_EX64)lpvData;
|
|
Status = g_Target->ReadIo( isex->InterfaceType,
|
|
isex->BusNumber,
|
|
isex->AddressSpace,
|
|
isex->Address,
|
|
&isex->Data,
|
|
isex->Length,
|
|
&cb
|
|
);
|
|
return Status == S_OK;
|
|
|
|
case IG_WRITE_IO_SPACE_EX:
|
|
isex = (PIOSPACE_EX64)lpvData;
|
|
Status = g_Target->WriteIo( isex->InterfaceType,
|
|
isex->BusNumber,
|
|
isex->AddressSpace,
|
|
isex->Address,
|
|
&isex->Data,
|
|
isex->Length,
|
|
&cb
|
|
);
|
|
return Status == S_OK;
|
|
|
|
case IG_READ_PHYSICAL:
|
|
phy = (PPHYSICAL)lpvData;
|
|
Bool =
|
|
ExtReadPhysicalMemory( phy->Address, phy->Buf, phy->BufLen, &cb );
|
|
phy->BufLen = cb;
|
|
return Bool;
|
|
|
|
case IG_WRITE_PHYSICAL:
|
|
phy = (PPHYSICAL)lpvData;
|
|
Bool =
|
|
ExtWritePhysicalMemory( phy->Address, phy->Buf, phy->BufLen, &cb );
|
|
phy->BufLen = cb;
|
|
return Bool;
|
|
|
|
case IG_READ_PHYSICAL_WITH_FLAGS:
|
|
phyf = (PPHYSICAL_WITH_FLAGS)lpvData;
|
|
Bool =
|
|
ExtReadPhysicalMemoryWithFlags( phyf->Address, phyf->Buf,
|
|
phyf->BufLen, phyf->Flags,
|
|
&cb );
|
|
phyf->BufLen = cb;
|
|
return Bool;
|
|
|
|
case IG_WRITE_PHYSICAL_WITH_FLAGS:
|
|
phyf = (PPHYSICAL_WITH_FLAGS)lpvData;
|
|
Bool =
|
|
ExtWritePhysicalMemoryWithFlags( phyf->Address, phyf->Buf,
|
|
phyf->BufLen, phyf->Flags,
|
|
&cb );
|
|
phyf->BufLen = cb;
|
|
return Bool;
|
|
|
|
case IG_LOWMEM_CHECK:
|
|
Status = g_Target->CheckLowMemory();
|
|
return Status == S_OK;
|
|
|
|
case IG_SEARCH_MEMORY:
|
|
psr = (PSEARCHMEMORY)lpvData;
|
|
Status = g_Target->SearchVirtual(g_Process,
|
|
psr->SearchAddress,
|
|
psr->SearchLength,
|
|
psr->Pattern,
|
|
psr->PatternLength,
|
|
1,
|
|
&psr->FoundAddress);
|
|
return Status == S_OK;
|
|
|
|
case IG_SET_THREAD:
|
|
Bool = FALSE;
|
|
if (IS_KERNEL_TARGET(g_Target))
|
|
{
|
|
// Turn off engine notifications since this setthread is temporary
|
|
g_EngNotify++;
|
|
PushScope(&g_ExtThreadSavedScope);
|
|
g_ExtThread = *(PULONG64)lpvData;
|
|
Bool = SetScopeContextFromThreadData(g_ExtThread, FALSE) == S_OK;
|
|
g_ExtThreadScopeSaved = TRUE;
|
|
g_EngNotify--;
|
|
}
|
|
return Bool;
|
|
|
|
case IG_READ_MSR:
|
|
msr = (PREAD_WRITE_MSR)lpvData;
|
|
Status = g_Target->ReadMsr(msr->Msr, (PULONG64)&msr->Value);
|
|
return Status == S_OK;
|
|
|
|
case IG_WRITE_MSR:
|
|
msr = (PREAD_WRITE_MSR)lpvData;
|
|
Status = g_Target->WriteMsr(msr->Msr, msr->Value);
|
|
return Status == S_OK;
|
|
|
|
case IG_GET_KERNEL_VERSION:
|
|
if (!g_Target)
|
|
{
|
|
return FALSE;
|
|
}
|
|
*((PDBGKD_GET_VERSION64)lpvData) = g_Target->m_KdVersion;
|
|
return TRUE;
|
|
|
|
case IG_GET_BUS_DATA:
|
|
busdata = (PBUSDATA)lpvData;
|
|
Status = g_Target->ReadBusData( busdata->BusDataType,
|
|
busdata->BusNumber,
|
|
busdata->SlotNumber,
|
|
busdata->Offset,
|
|
busdata->Buffer,
|
|
busdata->Length,
|
|
&cb
|
|
);
|
|
busdata->Length = cb;
|
|
return Status == S_OK;
|
|
|
|
case IG_SET_BUS_DATA:
|
|
busdata = (PBUSDATA)lpvData;
|
|
Status = g_Target->WriteBusData( busdata->BusDataType,
|
|
busdata->BusNumber,
|
|
busdata->SlotNumber,
|
|
busdata->Offset,
|
|
busdata->Buffer,
|
|
busdata->Length,
|
|
&cb
|
|
);
|
|
busdata->Length = cb;
|
|
return Status == S_OK;
|
|
|
|
case IG_GET_CURRENT_THREAD:
|
|
if (!g_Target)
|
|
{
|
|
return FALSE;
|
|
}
|
|
pct = (PGET_CURRENT_THREAD_ADDRESS) lpvData;
|
|
return g_Target->
|
|
GetThreadInfoDataOffset(NULL,
|
|
VIRTUAL_THREAD_HANDLE(pct->Processor),
|
|
&pct->Address) == S_OK;
|
|
|
|
case IG_GET_CURRENT_PROCESS:
|
|
if (!g_Target)
|
|
{
|
|
return FALSE;
|
|
}
|
|
pcp = (PGET_CURRENT_PROCESS_ADDRESS) lpvData;
|
|
return g_Target->
|
|
GetProcessInfoDataOffset(NULL,
|
|
pcp->Processor,
|
|
pcp->CurrentThread,
|
|
&pcp->Address) == S_OK;
|
|
|
|
case IG_GET_DEBUGGER_DATA:
|
|
if (!IS_KERNEL_TARGET(g_Target) ||
|
|
!g_Target ||
|
|
((PDBGKD_DEBUG_DATA_HEADER64)lpvData)->OwnerTag != KDBG_TAG)
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
// Don't refresh if asking for the kernel header.
|
|
|
|
memcpy(lpvData, &g_Target->m_KdDebuggerData,
|
|
min(sizeof(g_Target->m_KdDebuggerData), cbSize));
|
|
return TRUE;
|
|
|
|
case IG_RELOAD_SYMBOLS:
|
|
PCSTR ArgsRet;
|
|
return g_Target->Reload(g_Thread, (PCHAR)lpvData, &ArgsRet) == S_OK;
|
|
|
|
case IG_GET_SET_SYMPATH:
|
|
PGET_SET_SYMPATH pgs;
|
|
pgs = (PGET_SET_SYMPATH)lpvData;
|
|
ChangeSymPath((PCHAR)pgs->Args, FALSE, (PCHAR)pgs->Result,
|
|
pgs->Length);
|
|
return TRUE;
|
|
|
|
case IG_IS_PTR64:
|
|
if (!g_Target)
|
|
{
|
|
return FALSE;
|
|
}
|
|
*((PBOOL)lpvData) = g_Target->m_Machine->m_Ptr64;
|
|
return TRUE;
|
|
|
|
case IG_DUMP_SYMBOL_INFO:
|
|
if (!g_Process)
|
|
{
|
|
return FALSE;
|
|
}
|
|
pSym = (PSYM_DUMP_PARAM) lpvData;
|
|
SymbolTypeDump(g_Process->m_SymHandle,
|
|
g_Process->m_ImageHead,
|
|
pSym, (PULONG)&Status);
|
|
return (BOOL)(ULONG)Status;
|
|
|
|
case IG_GET_TYPE_SIZE:
|
|
if (!g_Process)
|
|
{
|
|
return FALSE;
|
|
}
|
|
pSym = (PSYM_DUMP_PARAM) lpvData;
|
|
return SymbolTypeDump(g_Process->m_SymHandle,
|
|
g_Process->m_ImageHead,
|
|
pSym, (PULONG)&Status);
|
|
|
|
case IG_GET_TEB_ADDRESS:
|
|
if (!g_Target)
|
|
{
|
|
return FALSE;
|
|
}
|
|
PGET_TEB_ADDRESS pTeb;
|
|
pTeb = (PGET_TEB_ADDRESS) lpvData;
|
|
return g_Target->
|
|
GetThreadInfoTeb(g_Thread,
|
|
CURRENT_PROC,
|
|
0,
|
|
&pTeb->Address) == S_OK;
|
|
|
|
case IG_GET_PEB_ADDRESS:
|
|
if (!g_Target)
|
|
{
|
|
return FALSE;
|
|
}
|
|
PGET_PEB_ADDRESS pPeb;
|
|
pPeb = (PGET_PEB_ADDRESS) lpvData;
|
|
return g_Target->
|
|
GetProcessInfoPeb(g_Thread,
|
|
CURRENT_PROC,
|
|
pPeb->CurrentThread,
|
|
&pPeb->Address) == S_OK;
|
|
|
|
case IG_GET_CURRENT_PROCESS_HANDLE:
|
|
if (!g_Process)
|
|
{
|
|
return FALSE;
|
|
}
|
|
*(PHANDLE)lpvData = OS_HANDLE(g_Process->m_SysHandle);
|
|
return TRUE;
|
|
|
|
case IG_GET_INPUT_LINE:
|
|
PGET_INPUT_LINE Gil;
|
|
Gil = (PGET_INPUT_LINE)lpvData;
|
|
Gil->InputSize = GetInput(Gil->Prompt, Gil->Buffer, Gil->BufferSize,
|
|
GETIN_LOG_INPUT_LINE);
|
|
return TRUE;
|
|
|
|
case IG_GET_EXPRESSION_EX:
|
|
PGET_EXPRESSION_EX Gee;
|
|
Gee = (PGET_EXPRESSION_EX)lpvData;
|
|
Gee->Value = ExtGetExpression(Gee->Expression);
|
|
Gee->Remainder = Gee->Expression + g_ExtGetExpressionRemainderIndex;
|
|
return g_ExtGetExpressionSuccess;
|
|
|
|
case IG_TRANSLATE_VIRTUAL_TO_PHYSICAL:
|
|
if (!IS_CUR_MACHINE_ACCESSIBLE())
|
|
{
|
|
return FALSE;
|
|
}
|
|
PTRANSLATE_VIRTUAL_TO_PHYSICAL Tvtp;
|
|
Tvtp = (PTRANSLATE_VIRTUAL_TO_PHYSICAL)lpvData;
|
|
ULONG Levels, PfIndex;
|
|
return g_Machine->
|
|
GetVirtualTranslationPhysicalOffsets(g_Thread,
|
|
Tvtp->Virtual, NULL, 0,
|
|
&Levels, &PfIndex,
|
|
&Tvtp->Physical) == S_OK;
|
|
|
|
case IG_GET_CACHE_SIZE:
|
|
if (!g_Process)
|
|
{
|
|
return FALSE;
|
|
}
|
|
PULONG64 pCacheSize;
|
|
|
|
pCacheSize = (PULONG64)lpvData;
|
|
if (IS_KERNEL_TARGET(g_Target))
|
|
{
|
|
*pCacheSize = g_Process->m_VirtualCache.m_MaxSize;
|
|
return TRUE;
|
|
}
|
|
return FALSE;
|
|
|
|
case IG_POINTER_SEARCH_PHYSICAL:
|
|
if (!IS_CUR_MACHINE_ACCESSIBLE())
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
PPOINTER_SEARCH_PHYSICAL Psp;
|
|
Psp = (PPOINTER_SEARCH_PHYSICAL)lpvData;
|
|
return g_Target->PointerSearchPhysical(Psp->Offset,
|
|
Psp->Length,
|
|
Psp->PointerMin,
|
|
Psp->PointerMax,
|
|
Psp->Flags,
|
|
Psp->MatchOffsets,
|
|
Psp->MatchOffsetsSize,
|
|
&Psp->MatchOffsetsCount) ==
|
|
S_OK;
|
|
|
|
case IG_GET_COR_DATA_ACCESS:
|
|
if (cbSize != sizeof(void*) ||
|
|
!g_Process ||
|
|
g_Process->LoadCorDebugDll() != S_OK)
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
*(ICorDataAccess**)lpvData = g_Process->m_CorAccess;
|
|
return TRUE;
|
|
|
|
default:
|
|
ErrOut( "\n*** Bad IOCTL request from an extension [%d]\n\n",
|
|
IoctlType );
|
|
return FALSE;
|
|
}
|
|
|
|
// NOTREACHED.
|
|
DBG_ASSERT(FALSE);
|
|
return FALSE;
|
|
}
|
|
|
|
BOOL
|
|
ExtIoctl32(
|
|
USHORT IoctlType,
|
|
LPVOID lpvData,
|
|
DWORD cbSize
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This is the extension Ioctl routine for backward compatibility with
|
|
old extension dlls. This routine is frozen, and new ioctl support
|
|
should not be added to it.
|
|
|
|
Arguments:
|
|
|
|
|
|
Return Value:
|
|
|
|
--*/
|
|
{
|
|
HRESULT Status;
|
|
DWORD cb = 0;
|
|
PIOSPACE32 is;
|
|
PIOSPACE_EX32 isex;
|
|
PREADCONTROLSPACE prc;
|
|
PDBGKD_GET_VERSION32 pv32;
|
|
PKDDEBUGGER_DATA32 pdbg32;
|
|
|
|
// Make sure output for long-running extensions appears regularly.
|
|
TimedFlushCallbacks();
|
|
|
|
switch( IoctlType )
|
|
{
|
|
case IG_READ_CONTROL_SPACE:
|
|
// KSPECIAL_REGISTER content is kept in control space
|
|
// so accessing control space may touch data that's
|
|
// cached in the current machine KSPECIAL_REGISTERS.
|
|
// Flush the current machine to maintain consistency.
|
|
if (IS_CUR_MACHINE_ACCESSIBLE())
|
|
{
|
|
g_Target->FlushRegContext();
|
|
}
|
|
|
|
prc = (PREADCONTROLSPACE)lpvData;
|
|
Status = g_Target->ReadControl( prc->Processor,
|
|
prc->Address,
|
|
prc->Buf,
|
|
prc->BufLen,
|
|
&cb
|
|
);
|
|
prc->BufLen = cb;
|
|
return Status == S_OK;
|
|
|
|
case IG_WRITE_CONTROL_SPACE:
|
|
// KSPECIAL_REGISTER content is kept in control space
|
|
// so accessing control space may touch data that's
|
|
// cached in the current machine KSPECIAL_REGISTERS.
|
|
// Flush the current machine to maintain consistency.
|
|
if (IS_CUR_MACHINE_ACCESSIBLE())
|
|
{
|
|
g_Target->FlushRegContext();
|
|
}
|
|
|
|
prc = (PREADCONTROLSPACE)lpvData;
|
|
Status = g_Target->WriteControl( prc->Processor,
|
|
prc->Address,
|
|
prc->Buf,
|
|
prc->BufLen,
|
|
&cb
|
|
);
|
|
prc->BufLen = cb;
|
|
return Status == S_OK;
|
|
|
|
case IG_READ_IO_SPACE:
|
|
is = (PIOSPACE32)lpvData;
|
|
Status = g_Target->ReadIo( Isa, 0, 1, is->Address, &is->Data,
|
|
is->Length, &cb );
|
|
return Status == S_OK;
|
|
|
|
case IG_WRITE_IO_SPACE:
|
|
is = (PIOSPACE32)lpvData;
|
|
Status = g_Target->WriteIo( Isa, 0, 1, is->Address, &is->Data,
|
|
is->Length, &cb );
|
|
return Status == S_OK;
|
|
|
|
case IG_READ_IO_SPACE_EX:
|
|
isex = (PIOSPACE_EX32)lpvData;
|
|
Status = g_Target->ReadIo( isex->InterfaceType,
|
|
isex->BusNumber,
|
|
isex->AddressSpace,
|
|
isex->Address,
|
|
&isex->Data,
|
|
isex->Length,
|
|
&cb
|
|
);
|
|
return Status == S_OK;
|
|
|
|
case IG_WRITE_IO_SPACE_EX:
|
|
isex = (PIOSPACE_EX32)lpvData;
|
|
Status = g_Target->WriteIo( isex->InterfaceType,
|
|
isex->BusNumber,
|
|
isex->AddressSpace,
|
|
isex->Address,
|
|
&isex->Data,
|
|
isex->Length,
|
|
&cb
|
|
);
|
|
return Status == S_OK;
|
|
|
|
case IG_SET_THREAD:
|
|
if (IS_KERNEL_TARGET(g_Target))
|
|
{
|
|
g_EngNotify++; // Turn off engine notifications since this setthread is temporary
|
|
g_ExtThread = EXTEND64(*(PULONG)lpvData);
|
|
PushScope(&g_ExtThreadSavedScope);
|
|
SetScopeContextFromThreadData(g_ExtThread, FALSE);
|
|
g_ExtThreadScopeSaved = TRUE;
|
|
g_EngNotify--;
|
|
return TRUE;
|
|
}
|
|
else
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
case IG_GET_KERNEL_VERSION:
|
|
if (!g_Target)
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// Convert to 32 bit
|
|
//
|
|
|
|
pv32 = (PDBGKD_GET_VERSION32)lpvData;
|
|
|
|
pv32->MajorVersion = g_Target->m_KdVersion.MajorVersion;
|
|
pv32->MinorVersion = g_Target->m_KdVersion.MinorVersion;
|
|
pv32->ProtocolVersion = g_Target->m_KdVersion.ProtocolVersion;
|
|
pv32->Flags = g_Target->m_KdVersion.Flags;
|
|
|
|
pv32->KernBase =
|
|
(ULONG)g_Target->m_KdVersion.KernBase;
|
|
pv32->PsLoadedModuleList =
|
|
(ULONG)g_Target->m_KdVersion.PsLoadedModuleList;
|
|
pv32->MachineType =
|
|
g_Target->m_KdVersion.MachineType;
|
|
pv32->DebuggerDataList =
|
|
(ULONG)g_Target->m_KdVersion.DebuggerDataList;
|
|
|
|
pv32->ThCallbackStack = g_Target->m_KdDebuggerData.ThCallbackStack;
|
|
pv32->NextCallback = g_Target->m_KdDebuggerData.NextCallback;
|
|
pv32->FramePointer = g_Target->m_KdDebuggerData.FramePointer;
|
|
|
|
pv32->KiCallUserMode =
|
|
(ULONG)g_Target->m_KdDebuggerData.KiCallUserMode;
|
|
pv32->KeUserCallbackDispatcher =
|
|
(ULONG)g_Target->m_KdDebuggerData.KeUserCallbackDispatcher;
|
|
pv32->BreakpointWithStatus =
|
|
(ULONG)g_Target->m_KdDebuggerData.BreakpointWithStatus;
|
|
return TRUE;
|
|
|
|
case IG_GET_DEBUGGER_DATA:
|
|
if (!IS_KERNEL_TARGET(g_Target) ||
|
|
!g_Target ||
|
|
((PDBGKD_DEBUG_DATA_HEADER32)lpvData)->OwnerTag != KDBG_TAG)
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
// Don't refresh if asking for the kernel header.
|
|
|
|
pdbg32 = (PKDDEBUGGER_DATA32)lpvData;
|
|
|
|
pdbg32->Header.List.Flink =
|
|
(ULONG)g_Target->m_KdDebuggerData.Header.List.Flink;
|
|
pdbg32->Header.List.Blink =
|
|
(ULONG)g_Target->m_KdDebuggerData.Header.List.Blink;
|
|
pdbg32->Header.OwnerTag = KDBG_TAG;
|
|
pdbg32->Header.Size = sizeof(KDDEBUGGER_DATA32);
|
|
|
|
#undef UIP
|
|
#undef CP
|
|
#define UIP(f) pdbg32->f = (ULONG)(g_Target->m_KdDebuggerData.f)
|
|
#define CP(f) pdbg32->f = (g_Target->m_KdDebuggerData.f)
|
|
|
|
UIP(KernBase);
|
|
UIP(BreakpointWithStatus);
|
|
UIP(SavedContext);
|
|
CP(ThCallbackStack);
|
|
CP(NextCallback);
|
|
CP(FramePointer);
|
|
CP(PaeEnabled);
|
|
UIP(KiCallUserMode);
|
|
UIP(KeUserCallbackDispatcher);
|
|
UIP(PsLoadedModuleList);
|
|
UIP(PsActiveProcessHead);
|
|
UIP(PspCidTable);
|
|
UIP(ExpSystemResourcesList);
|
|
UIP(ExpPagedPoolDescriptor);
|
|
UIP(ExpNumberOfPagedPools);
|
|
UIP(KeTimeIncrement);
|
|
UIP(KeBugCheckCallbackListHead);
|
|
UIP(KiBugcheckData);
|
|
UIP(IopErrorLogListHead);
|
|
UIP(ObpRootDirectoryObject);
|
|
UIP(ObpTypeObjectType);
|
|
UIP(MmSystemCacheStart);
|
|
UIP(MmSystemCacheEnd);
|
|
UIP(MmSystemCacheWs);
|
|
UIP(MmPfnDatabase);
|
|
UIP(MmSystemPtesStart);
|
|
UIP(MmSystemPtesEnd);
|
|
UIP(MmSubsectionBase);
|
|
UIP(MmNumberOfPagingFiles);
|
|
UIP(MmLowestPhysicalPage);
|
|
UIP(MmHighestPhysicalPage);
|
|
UIP(MmNumberOfPhysicalPages);
|
|
UIP(MmMaximumNonPagedPoolInBytes);
|
|
UIP(MmNonPagedSystemStart);
|
|
UIP(MmNonPagedPoolStart);
|
|
UIP(MmNonPagedPoolEnd);
|
|
UIP(MmPagedPoolStart);
|
|
UIP(MmPagedPoolEnd);
|
|
UIP(MmPagedPoolInformation);
|
|
UIP(MmPageSize);
|
|
UIP(MmSizeOfPagedPoolInBytes);
|
|
UIP(MmTotalCommitLimit);
|
|
UIP(MmTotalCommittedPages);
|
|
UIP(MmSharedCommit);
|
|
UIP(MmDriverCommit);
|
|
UIP(MmProcessCommit);
|
|
UIP(MmPagedPoolCommit);
|
|
UIP(MmExtendedCommit);
|
|
UIP(MmZeroedPageListHead);
|
|
UIP(MmFreePageListHead);
|
|
UIP(MmStandbyPageListHead);
|
|
UIP(MmModifiedPageListHead);
|
|
UIP(MmModifiedNoWritePageListHead);
|
|
UIP(MmAvailablePages);
|
|
UIP(MmResidentAvailablePages);
|
|
UIP(PoolTrackTable);
|
|
UIP(NonPagedPoolDescriptor);
|
|
UIP(MmHighestUserAddress);
|
|
UIP(MmSystemRangeStart);
|
|
UIP(MmUserProbeAddress);
|
|
UIP(KdPrintCircularBuffer);
|
|
UIP(KdPrintCircularBufferEnd);
|
|
UIP(KdPrintWritePointer);
|
|
UIP(KdPrintRolloverCount);
|
|
UIP(MmLoadedUserImageList);
|
|
//
|
|
// DO NOT ADD ANY FIELDS HERE
|
|
// The 32 bit structure should not be changed
|
|
//
|
|
return TRUE;
|
|
|
|
case IG_KD_CONTEXT:
|
|
case IG_READ_PHYSICAL:
|
|
case IG_WRITE_PHYSICAL:
|
|
case IG_READ_PHYSICAL_WITH_FLAGS:
|
|
case IG_WRITE_PHYSICAL_WITH_FLAGS:
|
|
case IG_LOWMEM_CHECK:
|
|
case IG_SEARCH_MEMORY:
|
|
case IG_READ_MSR:
|
|
case IG_WRITE_MSR:
|
|
case IG_GET_BUS_DATA:
|
|
case IG_SET_BUS_DATA:
|
|
case IG_GET_CURRENT_THREAD:
|
|
case IG_GET_CURRENT_PROCESS:
|
|
case IG_RELOAD_SYMBOLS:
|
|
case IG_GET_SET_SYMPATH:
|
|
case IG_IS_PTR64:
|
|
case IG_DUMP_SYMBOL_INFO:
|
|
case IG_GET_TYPE_SIZE:
|
|
case IG_GET_TEB_ADDRESS:
|
|
case IG_GET_PEB_ADDRESS:
|
|
case IG_GET_INPUT_LINE:
|
|
case IG_GET_EXPRESSION_EX:
|
|
case IG_TRANSLATE_VIRTUAL_TO_PHYSICAL:
|
|
case IG_POINTER_SEARCH_PHYSICAL:
|
|
case IG_GET_COR_DATA_ACCESS:
|
|
// All of these ioctls are handled identically for
|
|
// 32 and 64 bits. Avoid duplicating all the code.
|
|
return ExtIoctl(IoctlType, lpvData, cbSize);
|
|
|
|
default:
|
|
ErrOut( "\n*** Bad IOCTL32 request from an extension [%d]\n\n",
|
|
IoctlType );
|
|
return FALSE;
|
|
}
|
|
|
|
// NOTREACHED.
|
|
DBG_ASSERT(FALSE);
|
|
return FALSE;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
//
|
|
// Extension management.
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
DebugClient*
|
|
FindExtClient(void)
|
|
{
|
|
DebugClient* Client;
|
|
|
|
//
|
|
// Try to find the most appropriate client for
|
|
// executing an extension command on. The first
|
|
// choice is the session client, then any local
|
|
// primary client, then any primary client.
|
|
//
|
|
|
|
if (!(Client = FindClient(g_SessionThread, CLIENT_PRIMARY, 0)) &&
|
|
!(Client = FindClient(0, CLIENT_PRIMARY, CLIENT_REMOTE)) &&
|
|
!(Client = FindClient(0, CLIENT_PRIMARY, 0)))
|
|
{
|
|
Client = g_Clients;
|
|
}
|
|
|
|
return Client;
|
|
}
|
|
|
|
LONG
|
|
ExtensionExceptionFilter(struct _EXCEPTION_POINTERS *ExceptionInfo,
|
|
PCSTR Module,
|
|
PCSTR Func)
|
|
{
|
|
// Any references to objects will be leaked.
|
|
// There's not much the engine can do about this, although
|
|
// it would be possible to record old refcounts and
|
|
// try to restore them.
|
|
|
|
if (Module != NULL && Func != NULL)
|
|
{
|
|
ErrOut("%08x Exception in %s.%s debugger extension.\n",
|
|
ExceptionInfo->ExceptionRecord->ExceptionCode,
|
|
Module,
|
|
Func
|
|
);
|
|
}
|
|
else
|
|
{
|
|
ErrOut("%08x Exception in debugger client %s callback.\n",
|
|
ExceptionInfo->ExceptionRecord->ExceptionCode,
|
|
Func
|
|
);
|
|
}
|
|
|
|
ErrOut(" PC: %s VA: %s R/W: %x Parameter: %s\n",
|
|
FormatAddr64((ULONG_PTR)ExceptionInfo->ExceptionRecord->ExceptionAddress),
|
|
FormatAddr64(ExceptionInfo->ExceptionRecord->ExceptionInformation[1]),
|
|
ExceptionInfo->ExceptionRecord->ExceptionInformation[0],
|
|
FormatAddr64(ExceptionInfo->ExceptionRecord->ExceptionInformation[2])
|
|
);
|
|
|
|
return EXCEPTION_EXECUTE_HANDLER;
|
|
}
|
|
|
|
BOOL
|
|
CallExtension(DebugClient* Client,
|
|
EXTDLL *Ext,
|
|
PSTR Func,
|
|
PCSTR Args,
|
|
HRESULT* ExtStatus)
|
|
{
|
|
FARPROC Routine;
|
|
ADDR TempAddr;
|
|
|
|
if (IS_KERNEL_TARGET(g_Target))
|
|
{
|
|
_strlwr(Func);
|
|
}
|
|
|
|
Routine = GetProcAddress(Ext->Dll, Func);
|
|
if (Routine == NULL)
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
if (!(g_EnvDbgOptions & OPTION_NOVERSIONCHECK) && Ext->CheckVersionRoutine)
|
|
{
|
|
Ext->CheckVersionRoutine();
|
|
}
|
|
|
|
if (IS_KERNEL_TARGET(g_Target) && !strcmp(Func, "version"))
|
|
{
|
|
//
|
|
// This is a bit of a hack to avoid a problem with the
|
|
// extension version checking. Extension version checking
|
|
// comes before the KD connection is established so there's
|
|
// no register context. If the version checking fails it
|
|
// prints out version information, which tries to call
|
|
// version extensions, which will get here when there's
|
|
// no register context.
|
|
//
|
|
// To work around this, just pass zero to the version extension
|
|
// function since it presumably doesn't care about the
|
|
// address.
|
|
//
|
|
ADDRFLAT(&TempAddr, 0);
|
|
}
|
|
else if (IS_CONTEXT_POSSIBLE(g_Target))
|
|
{
|
|
g_Machine->GetPC(&TempAddr);
|
|
}
|
|
else
|
|
{
|
|
if (!IS_LOCAL_KERNEL_TARGET(g_Target))
|
|
{
|
|
WarnOut("Extension called without current PC\n");
|
|
}
|
|
|
|
ADDRFLAT(&TempAddr, 0);
|
|
}
|
|
|
|
*ExtStatus = S_OK;
|
|
|
|
__try
|
|
{
|
|
HANDLE ProcHandle, ThreadHandle;
|
|
|
|
if (g_Process)
|
|
{
|
|
ProcHandle = OS_HANDLE(g_Process->m_SysHandle);
|
|
}
|
|
else
|
|
{
|
|
ProcHandle = NULL;
|
|
}
|
|
if (g_Thread)
|
|
{
|
|
ThreadHandle = OS_HANDLE(g_Thread->m_Handle);
|
|
}
|
|
else
|
|
{
|
|
ThreadHandle = NULL;
|
|
}
|
|
|
|
switch(Ext->ExtensionType)
|
|
{
|
|
case NTSD_EXTENSION_TYPE:
|
|
//
|
|
// NOTE:
|
|
// Eventhough this type should receive an NTSD_EXTENSION_API
|
|
// structure, ntsdexts.dll (and possibly others) depend on
|
|
// receiving the WinDBG version of the extensions, because they
|
|
// check the size of the structure, and actually use some of the
|
|
// newer exports. This works because the WinDBG extension API was
|
|
// a superset of the NTSD version.
|
|
//
|
|
|
|
((PNTSD_EXTENSION_ROUTINE)Routine)
|
|
(ProcHandle,
|
|
ThreadHandle,
|
|
(ULONG)Flat(TempAddr),
|
|
g_Target->m_Machine->m_Ptr64 ?
|
|
(PNTSD_EXTENSION_APIS)&g_WindbgExtensions64 :
|
|
(PNTSD_EXTENSION_APIS)&g_WindbgExtensions32,
|
|
(PSTR)Args
|
|
);
|
|
break;
|
|
|
|
case DEBUG_EXTENSION_TYPE:
|
|
if (Client == NULL)
|
|
{
|
|
Client = FindExtClient();
|
|
}
|
|
if (Client == NULL)
|
|
{
|
|
ErrOut("Unable to call client-style extension "
|
|
"without a client\n");
|
|
}
|
|
else
|
|
{
|
|
*ExtStatus = ((PDEBUG_EXTENSION_CALL)Routine)
|
|
((PDEBUG_CLIENT)(IDebugClientN *)Client, Args);
|
|
}
|
|
break;
|
|
|
|
case WINDBG_EXTENSION_TYPE:
|
|
//
|
|
// Support Windbg type extensions for ntsd too
|
|
//
|
|
if (Ext->ApiVersion.Revision < 6 )
|
|
{
|
|
((PWINDBG_EXTENSION_ROUTINE32)Routine) (
|
|
ProcHandle,
|
|
ThreadHandle,
|
|
(ULONG)Flat(TempAddr),
|
|
CURRENT_PROC,
|
|
Args
|
|
);
|
|
}
|
|
else
|
|
{
|
|
((PWINDBG_EXTENSION_ROUTINE64)Routine) (
|
|
ProcHandle,
|
|
ThreadHandle,
|
|
Flat(TempAddr),
|
|
CURRENT_PROC,
|
|
Args
|
|
);
|
|
}
|
|
break;
|
|
|
|
case WINDBG_OLDKD_EXTENSION_TYPE:
|
|
((PWINDBG_OLDKD_EXTENSION_ROUTINE)Routine) (
|
|
(ULONG)Flat(TempAddr),
|
|
&g_KdExtensions,
|
|
Args
|
|
);
|
|
break;
|
|
}
|
|
}
|
|
__except(ExtensionExceptionFilter(GetExceptionInformation(),
|
|
Ext->Name, Func))
|
|
{
|
|
;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
void
|
|
LinkExtensionDll(EXTDLL* Ext)
|
|
{
|
|
// Put user-loaded DLLs before default DLLs.
|
|
if (Ext->UserLoaded)
|
|
{
|
|
Ext->Next = g_ExtDlls;
|
|
g_ExtDlls = Ext;
|
|
}
|
|
else
|
|
{
|
|
EXTDLL* Prev;
|
|
EXTDLL* Cur;
|
|
|
|
Prev = NULL;
|
|
for (Cur = g_ExtDlls; Cur != NULL; Cur = Cur->Next)
|
|
{
|
|
if (!Cur->UserLoaded)
|
|
{
|
|
break;
|
|
}
|
|
|
|
Prev = Cur;
|
|
}
|
|
|
|
Ext->Next = Cur;
|
|
if (Prev == NULL)
|
|
{
|
|
g_ExtDlls = Ext;
|
|
}
|
|
else
|
|
{
|
|
Prev->Next = Ext;
|
|
}
|
|
}
|
|
}
|
|
|
|
EXTDLL *
|
|
AddExtensionDll(char *Name, BOOL UserLoaded, TargetInfo* Target,
|
|
char **End)
|
|
{
|
|
EXTDLL *Ext;
|
|
ULONG Len;
|
|
char *Last;
|
|
|
|
while (*Name == ' ' || *Name == '\t')
|
|
{
|
|
Name++;
|
|
}
|
|
if (*Name == 0)
|
|
{
|
|
ErrOut("No extension DLL name provided\n");
|
|
return NULL;
|
|
}
|
|
|
|
Len = strlen(Name);
|
|
if (End != NULL)
|
|
{
|
|
*End = Name + Len;
|
|
}
|
|
|
|
Last = Name + (Len - 1);
|
|
while (Last >= Name && (*Last == ' ' || *Last == '\t'))
|
|
{
|
|
Last--;
|
|
}
|
|
Len = (ULONG)((Last + 1) - Name);
|
|
|
|
// See if it's already in the list.
|
|
for (Ext = g_ExtDlls; Ext != NULL; Ext = Ext->Next)
|
|
{
|
|
if ((!Target || Target == Ext->Target) &&
|
|
strlen(Ext->Name) == Len &&
|
|
!_memicmp(Name, Ext->Name, Len))
|
|
{
|
|
return Ext;
|
|
}
|
|
}
|
|
|
|
Ext = (EXTDLL *)malloc(sizeof(EXTDLL) + Len);
|
|
if (Ext == NULL)
|
|
{
|
|
ErrOut("Unable to allocate memory for extension DLL\n");
|
|
return NULL;
|
|
}
|
|
|
|
ZeroMemory(Ext, sizeof(EXTDLL) + Len);
|
|
memcpy(Ext->Name, Name, Len + 1);
|
|
Ext->UserLoaded = UserLoaded;
|
|
Ext->Target = Target;
|
|
|
|
LinkExtensionDll(Ext);
|
|
|
|
NotifyChangeEngineState(DEBUG_CES_EXTENSIONS, 0, TRUE);
|
|
return Ext;
|
|
}
|
|
|
|
PCTSTR
|
|
BuildExtensionSearchPath(TargetInfo* Target)
|
|
{
|
|
DWORD Size;
|
|
DWORD TotalSize;
|
|
CHAR ExeDir[MAX_PATH];
|
|
int ExeRootLen;
|
|
PSTR OsDirPath;
|
|
CHAR OsDirTail[32];
|
|
BOOL PriPaths = FALSE;
|
|
BOOL WinPaths = FALSE;
|
|
PSTR NewPath;
|
|
|
|
//
|
|
// If we are not connected, don't build a path, since we have to pick
|
|
// up extensions based on the OS version.
|
|
//
|
|
if (Target && Target->m_ActualSystemVersion == SVER_INVALID)
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
//
|
|
// If we already have a search path, do not rebuild it.
|
|
//
|
|
|
|
if (Target)
|
|
{
|
|
if (Target->m_ExtensionSearchPath)
|
|
{
|
|
return Target->m_ExtensionSearchPath;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (g_BaseExtensionSearchPath)
|
|
{
|
|
return g_BaseExtensionSearchPath;
|
|
}
|
|
}
|
|
|
|
// Get the directory the debugger executable is in.
|
|
// -8 because we assume we're adding \w2kfre to the path.
|
|
if (!GetEngineDirectory(ExeDir, MAX_PATH - 8))
|
|
{
|
|
// Error. Using the current directory.
|
|
ExeRootLen = 1;
|
|
}
|
|
else
|
|
{
|
|
if (ExeDir[0] == '\\' && ExeDir[1] == '\\')
|
|
{
|
|
PSTR ExeRootEnd;
|
|
|
|
// UNC path root.
|
|
ExeRootEnd = strchr(ExeDir + 2, '\\');
|
|
if (ExeRootEnd != NULL)
|
|
{
|
|
ExeRootEnd = strchr(ExeRootEnd + 1, '\\');
|
|
}
|
|
if (ExeRootEnd == NULL)
|
|
{
|
|
ExeRootLen = strlen(ExeDir);
|
|
}
|
|
else
|
|
{
|
|
ExeRootLen = (int)(ExeRootEnd - ExeDir);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Drive letter and colon root.
|
|
ExeRootLen = 2;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Calc how much space we will need to use.
|
|
//
|
|
// Leave extra room for the current directory, path, and directory of
|
|
// where debugger extensions are located.
|
|
//
|
|
|
|
TotalSize = GetEnvironmentVariable("PATH", NULL, 0) +
|
|
GetEnvironmentVariable("_NT_DEBUGGER_EXTENSION_PATH",
|
|
NULL, 0) + MAX_PATH * 4;
|
|
|
|
NewPath = (LPTSTR)calloc(TotalSize, sizeof(TCHAR));
|
|
if (!NewPath)
|
|
{
|
|
return NULL;
|
|
}
|
|
*NewPath = 0;
|
|
|
|
//
|
|
// 1 - User specified search path
|
|
//
|
|
|
|
if (GetEnvironmentVariable("_NT_DEBUGGER_EXTENSION_PATH",
|
|
NewPath, TotalSize - 2))
|
|
{
|
|
CatString(NewPath, ";", TotalSize);
|
|
}
|
|
|
|
// Generate default path for the exe dir
|
|
// Skip root as it is already taken from ExeDir.
|
|
OsDirPath = ExeDir + ExeRootLen;
|
|
if (*OsDirPath == '\\')
|
|
{
|
|
OsDirPath++;
|
|
}
|
|
|
|
//
|
|
// Figure out whether we need NT6, or NT5/NT4 free or checked extensions
|
|
//
|
|
|
|
if (!Target)
|
|
{
|
|
OsDirPath = "";
|
|
OsDirTail[0] = 0;
|
|
}
|
|
else if (Target->m_ActualSystemVersion > BIG_SVER_START &&
|
|
Target->m_ActualSystemVersion < BIG_SVER_END)
|
|
{
|
|
OsDirPath = "DbgExt";
|
|
strcpy(OsDirTail, "BIG");
|
|
}
|
|
else if (Target->m_ActualSystemVersion > XBOX_SVER_START &&
|
|
Target->m_ActualSystemVersion < XBOX_SVER_END)
|
|
{
|
|
OsDirPath = "DbgExt";
|
|
strcpy(OsDirTail, "XBox");
|
|
}
|
|
else if (Target->m_ActualSystemVersion > NTBD_SVER_START &&
|
|
Target->m_ActualSystemVersion < NTBD_SVER_END)
|
|
{
|
|
OsDirPath = "DbgExt";
|
|
strcpy(OsDirTail, "NtBd");
|
|
}
|
|
else if (Target->m_ActualSystemVersion > EFI_SVER_START &&
|
|
Target->m_ActualSystemVersion < EFI_SVER_END)
|
|
{
|
|
OsDirPath = "DbgExt";
|
|
strcpy(OsDirTail, "EFI");
|
|
}
|
|
else if (Target->m_ActualSystemVersion > W9X_SVER_START &&
|
|
Target->m_ActualSystemVersion < W9X_SVER_END)
|
|
{
|
|
strcpy(OsDirTail, "Win9X");
|
|
WinPaths = TRUE;
|
|
}
|
|
else if (Target->m_ActualSystemVersion > WCE_SVER_START &&
|
|
Target->m_ActualSystemVersion < WCE_SVER_END)
|
|
{
|
|
strcpy(OsDirTail, "WinCE");
|
|
WinPaths = TRUE;
|
|
}
|
|
else
|
|
{
|
|
// Treat everything else as an NT system. Use
|
|
// the translated system version now rather than
|
|
// the actual system version.
|
|
|
|
PriPaths = TRUE;
|
|
WinPaths = TRUE;
|
|
|
|
if (Target->m_SystemVersion > NT_SVER_W2K)
|
|
{
|
|
strcpy(OsDirTail, "WINXP");
|
|
}
|
|
else
|
|
{
|
|
if (Target->m_SystemVersion <= NT_SVER_NT4)
|
|
{
|
|
strcpy(OsDirTail, "NT4");
|
|
}
|
|
else
|
|
{
|
|
strcpy(OsDirTail, "W2K");
|
|
}
|
|
|
|
if (0xC == Target->m_CheckedBuild)
|
|
{
|
|
strcat(OsDirTail, "Chk");
|
|
}
|
|
else
|
|
{
|
|
strcat(OsDirTail, "Fre");
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// 2 - OS specific subdirectories from where we launched the debugger.
|
|
// 3 - pri subdirectory from where we launched the debugger.
|
|
// 4 - Directory from where we launched the debugger.
|
|
//
|
|
|
|
PSTR End;
|
|
|
|
Size = strlen(NewPath);
|
|
End = NewPath + Size;
|
|
memcpy(End, ExeDir, ExeRootLen);
|
|
End += ExeRootLen;
|
|
TotalSize -= Size + ExeRootLen;
|
|
if (*OsDirPath)
|
|
{
|
|
if (TotalSize)
|
|
{
|
|
*End++ = '\\';
|
|
TotalSize--;
|
|
}
|
|
CopyString(End, OsDirPath, TotalSize);
|
|
Size = strlen(End);
|
|
End += Size;
|
|
TotalSize -= Size;
|
|
}
|
|
if (WinPaths)
|
|
{
|
|
PrintString(End, TotalSize, "\\winext;%s", ExeDir);
|
|
Size = strlen(End);
|
|
End += Size;
|
|
TotalSize -= Size;
|
|
}
|
|
if (*OsDirTail)
|
|
{
|
|
PrintString(End, TotalSize, "\\%s;%s", OsDirTail, ExeDir);
|
|
Size = strlen(End);
|
|
End += Size;
|
|
TotalSize -= Size;
|
|
}
|
|
if (PriPaths)
|
|
{
|
|
PrintString(End, TotalSize, "\\pri;%s", ExeDir);
|
|
Size = strlen(End);
|
|
End += Size;
|
|
TotalSize -= Size;
|
|
}
|
|
|
|
if (*--End == ':')
|
|
{
|
|
*++End = '\\';
|
|
}
|
|
else
|
|
{
|
|
TotalSize--;
|
|
}
|
|
|
|
if (TotalSize > 1)
|
|
{
|
|
*++End = ';';
|
|
*++End = 0;
|
|
TotalSize -= 2;
|
|
}
|
|
|
|
//
|
|
// 4 - Copy environment path
|
|
//
|
|
|
|
GetEnvironmentVariable("PATH", End, TotalSize);
|
|
|
|
if (Target)
|
|
{
|
|
Target->m_ExtensionSearchPath = NewPath;
|
|
}
|
|
else
|
|
{
|
|
g_BaseExtensionSearchPath = NewPath;
|
|
}
|
|
return NewPath;
|
|
}
|
|
|
|
BOOL
|
|
IsAbsolutePath(PCTSTR Path)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Is this path an absolute path? Does not guarentee that the path exists. The
|
|
method is:
|
|
|
|
"\\<anything>" is an absolute path
|
|
|
|
"{char}:\<anything>" is an absolute path
|
|
|
|
anything else is not
|
|
--*/
|
|
|
|
{
|
|
BOOL Ret;
|
|
|
|
if ( (Path [0] == '\\' && Path [1] == '\\') ||
|
|
(isalpha ( Path [0] ) && Path [1] == ':' && Path [ 2 ] == '\\') )
|
|
{
|
|
Ret = TRUE;
|
|
}
|
|
else
|
|
{
|
|
Ret = FALSE;
|
|
}
|
|
|
|
return Ret;
|
|
}
|
|
|
|
void
|
|
FreeExtensionLibrary(EXTDLL* Ext)
|
|
{
|
|
FreeLibrary(Ext->Dll);
|
|
Ext->Dll = NULL;
|
|
|
|
if (Ext == g_Wow64ExtDll)
|
|
{
|
|
g_Wow64exts = NULL;
|
|
g_Wow64ExtDll = NULL;
|
|
}
|
|
|
|
if (Ext == g_WmiExtDll)
|
|
{
|
|
g_WmiFormatTraceData = NULL;
|
|
g_WmiExtDll = NULL;
|
|
}
|
|
}
|
|
|
|
BOOL
|
|
LoadExtensionDll(TargetInfo* Target, EXTDLL *Ext)
|
|
{
|
|
BOOL Found;
|
|
CHAR ExtPath[_MAX_PATH];
|
|
|
|
if (Ext->Dll != NULL)
|
|
{
|
|
// Extension is already loaded.
|
|
return TRUE;
|
|
}
|
|
|
|
//
|
|
// Do not allow extensions to be loaded via arbitrary UNC
|
|
// paths when in secure mode.
|
|
//
|
|
|
|
if ((g_SymOptions & SYMOPT_SECURE) &&
|
|
((IS_SLASH(Ext->Name[0]) && IS_SLASH(Ext->Name[1])) ||
|
|
IsUrlPathComponent(Ext->Name)))
|
|
{
|
|
ErrOut("SECURE: UNC paths not allowed for extension DLLs - %s\n",
|
|
Ext->Name);
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// If we are not allowing network paths, verify that the extension will
|
|
// not be loaded from a network path.
|
|
//
|
|
|
|
if (g_EngOptions & DEBUG_ENGOPT_DISALLOW_NETWORK_PATHS)
|
|
{
|
|
DWORD NetCheck;
|
|
|
|
NetCheck = NetworkPathCheck(BuildExtensionSearchPath(Target));
|
|
|
|
//
|
|
// Check full path of the extension.
|
|
//
|
|
|
|
if (NetCheck != ERROR_FILE_OFFLINE)
|
|
{
|
|
CHAR Drive [ _MAX_DRIVE + 1];
|
|
CHAR Dir [ _MAX_DIR + 1];
|
|
CHAR Path [ _MAX_PATH + 1];
|
|
|
|
*Drive = '\000';
|
|
*Dir = '\000';
|
|
_splitpath (Ext->Name, Drive, Dir, NULL, NULL);
|
|
_makepath (Path, Drive, Dir, NULL, NULL);
|
|
|
|
NetCheck = NetworkPathCheck (Path);
|
|
}
|
|
|
|
if (NetCheck == ERROR_FILE_OFFLINE)
|
|
{
|
|
ErrOut("ERROR: extension search path contains "
|
|
"network references.\n");
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
Found = SearchPath(BuildExtensionSearchPath(Target),
|
|
Ext->Name,
|
|
".dll",
|
|
DIMA(ExtPath),
|
|
ExtPath,
|
|
NULL);
|
|
|
|
UINT OldMode = SetErrorMode(SEM_FAILCRITICALERRORS);
|
|
|
|
if ( Found )
|
|
{
|
|
Ext->Dll = LoadLibrary(ExtPath);
|
|
}
|
|
else if (IsAbsolutePath(Ext->Name))
|
|
{
|
|
Ext->Dll = LoadLibrary(Ext->Name);
|
|
}
|
|
|
|
SetErrorMode(OldMode);
|
|
|
|
if (Ext->Dll == NULL)
|
|
{
|
|
HRESULT Status = WIN32_LAST_STATUS();
|
|
|
|
ErrOut("The call to LoadLibrary(%s) failed, %s\n \"%s\"\n"
|
|
"Please check your debugger configuration "
|
|
"and/or network access.\n",
|
|
Ext->Name, FormatStatusCode(Status), FormatStatus(Status));
|
|
return FALSE;
|
|
}
|
|
|
|
PCSTR ExtPathTail = PathTail(Ext->Name);
|
|
|
|
if (!_stricmp(ExtPathTail, "wow64exts.dll") ||
|
|
!_stricmp(ExtPathTail, "wow64exts"))
|
|
{
|
|
g_Wow64exts = (WOW64EXTSPROC)GetProcAddress(Ext->Dll,"Wow64extsfn");
|
|
DBG_ASSERT(g_Wow64exts);
|
|
g_Wow64ExtDll = Ext;
|
|
}
|
|
|
|
if (!_stricmp(ExtPathTail, "wmitrace.dll") ||
|
|
!_stricmp(ExtPathTail, "wmitrace"))
|
|
{
|
|
g_WmiFormatTraceData = (WMI_FORMAT_TRACE_DATA)
|
|
GetProcAddress(Ext->Dll, "WmiFormatTraceData");
|
|
g_WmiExtDll = Ext;
|
|
}
|
|
|
|
if (!g_QuietMode)
|
|
{
|
|
VerbOut("Loaded %s extension DLL\n", Ext->Name);
|
|
}
|
|
|
|
//
|
|
// Now that the extension is loaded, refresh it.
|
|
//
|
|
|
|
Ext->Uninit = NULL;
|
|
|
|
PDEBUG_EXTENSION_INITIALIZE EngExt;
|
|
|
|
EngExt = (PDEBUG_EXTENSION_INITIALIZE)
|
|
GetProcAddress(Ext->Dll, "DebugExtensionInitialize");
|
|
if (EngExt != NULL)
|
|
{
|
|
ULONG Version, Flags;
|
|
HRESULT Status;
|
|
|
|
// This is an engine extension. Initialize it.
|
|
|
|
Status = EngExt(&Version, &Flags);
|
|
if (Status != S_OK)
|
|
{
|
|
ErrOut("%s!DebugExtensionInitialize failed with 0x%08lX\n",
|
|
Ext->Name, Status);
|
|
goto EH_Free;
|
|
}
|
|
|
|
Ext->ApiVersion.MajorVersion = HIWORD(Version);
|
|
Ext->ApiVersion.MinorVersion = LOWORD(Version);
|
|
Ext->ApiVersion.Revision = 0;
|
|
|
|
Ext->Notify = (PDEBUG_EXTENSION_NOTIFY)
|
|
GetProcAddress(Ext->Dll, "DebugExtensionNotify");
|
|
Ext->Uninit = (PDEBUG_EXTENSION_UNINITIALIZE)
|
|
GetProcAddress(Ext->Dll, "DebugExtensionUninitialize");
|
|
|
|
Ext->ExtensionType = DEBUG_EXTENSION_TYPE;
|
|
Ext->Init = NULL;
|
|
Ext->ApiVersionRoutine = NULL;
|
|
Ext->CheckVersionRoutine = NULL;
|
|
|
|
goto VersionCheck;
|
|
}
|
|
|
|
Ext->Init = (PWINDBG_EXTENSION_DLL_INIT64)
|
|
GetProcAddress(Ext->Dll, "WinDbgExtensionDllInit");
|
|
// Windbg Api
|
|
if (Ext->Init != NULL)
|
|
{
|
|
Ext->ExtensionType = WINDBG_EXTENSION_TYPE;
|
|
Ext->ApiVersionRoutine = (PWINDBG_EXTENSION_API_VERSION)
|
|
GetProcAddress(Ext->Dll, "ExtensionApiVersion");
|
|
if (Ext->ApiVersionRoutine == NULL)
|
|
{
|
|
ErrOut("%s is not a valid windbg extension DLL\n",
|
|
Ext->Name);
|
|
goto EH_Free;
|
|
}
|
|
Ext->CheckVersionRoutine = (PWINDBG_CHECK_VERSION)
|
|
GetProcAddress(Ext->Dll, "CheckVersion");
|
|
|
|
Ext->ApiVersion = *(Ext->ApiVersionRoutine());
|
|
|
|
if (Ext->ApiVersion.Revision >= 6)
|
|
{
|
|
(Ext->Init)(&g_WindbgExtensions64,
|
|
Target ? (USHORT)Target->m_CheckedBuild : 0,
|
|
Target ? (USHORT)Target->m_BuildNumber : 0);
|
|
}
|
|
else
|
|
{
|
|
(Ext->Init)((PWINDBG_EXTENSION_APIS64)&g_WindbgExtensions32,
|
|
Target ? (USHORT)Target->m_CheckedBuild : 0,
|
|
Target ? (USHORT)Target->m_BuildNumber : 0);
|
|
}
|
|
}
|
|
else if (g_SymOptions & SYMOPT_SECURE)
|
|
{
|
|
ErrOut("SECURE: Cannot determine extension DLL type - %s\n",
|
|
Ext->Name);
|
|
goto EH_Free;
|
|
}
|
|
else
|
|
{
|
|
Ext->ApiVersion.Revision = EXT_API_VERSION_NUMBER;
|
|
Ext->ApiVersionRoutine = NULL;
|
|
Ext->CheckVersionRoutine = NULL;
|
|
if (GetProcAddress(Ext->Dll, "NtsdExtensionDllInit"))
|
|
{
|
|
Ext->ExtensionType = NTSD_EXTENSION_TYPE;
|
|
}
|
|
else
|
|
{
|
|
Ext->ExtensionType = IS_KERNEL_TARGET(g_Target) ?
|
|
WINDBG_OLDKD_EXTENSION_TYPE : NTSD_EXTENSION_TYPE;
|
|
}
|
|
}
|
|
|
|
VersionCheck:
|
|
|
|
#if 0
|
|
// Temporarily remove this print statements.
|
|
|
|
if (!(g_EnvDbgOptions & OPTION_NOVERSIONCHECK))
|
|
{
|
|
if (Ext->ApiVersion.Revision < 6)
|
|
{
|
|
dprintf("%s uses the old 32 bit extension API and may not be fully\n", Ext->Name);
|
|
dprintf("compatible with current systems.\n");
|
|
}
|
|
else if (Ext->ApiVersion.Revision < EXT_API_VERSION_NUMBER)
|
|
{
|
|
dprintf("%s uses an earlier version of the extension API than that\n", Ext->Name);
|
|
dprintf("supported by this debugger, and should work properly, but there\n");
|
|
dprintf("may be unexpected incompatibilities.\n");
|
|
}
|
|
else if (Ext->ApiVersion.Revision > EXT_API_VERSION_NUMBER)
|
|
{
|
|
dprintf("%s uses a later version of the extension API than that\n", Ext->Name);
|
|
dprintf("supported by this debugger, and might not function correctly.\n");
|
|
dprintf("You should use the debugger from the SDK or DDK which was used\n");
|
|
dprintf("to build the extension library.\n");
|
|
}
|
|
}
|
|
#endif
|
|
|
|
// If the extension has a notification routine send
|
|
// notifications appropriate to the current state.
|
|
if (Ext->Notify != NULL)
|
|
{
|
|
__try
|
|
{
|
|
if (IS_MACHINE_SET(g_Target))
|
|
{
|
|
Ext->Notify(DEBUG_NOTIFY_SESSION_ACTIVE, 0);
|
|
}
|
|
if (IS_CUR_MACHINE_ACCESSIBLE())
|
|
{
|
|
Ext->Notify(DEBUG_NOTIFY_SESSION_ACCESSIBLE, 0);
|
|
}
|
|
}
|
|
__except(ExtensionExceptionFilter(GetExceptionInformation(),
|
|
Ext->Name,
|
|
"DebugExtensionNotify"))
|
|
{
|
|
// Empty.
|
|
}
|
|
}
|
|
|
|
return TRUE;
|
|
|
|
EH_Free:
|
|
FreeExtensionLibrary(Ext);
|
|
return FALSE;
|
|
}
|
|
|
|
void
|
|
UnlinkExtensionDll(EXTDLL* Match)
|
|
{
|
|
EXTDLL *Ext;
|
|
EXTDLL *Prev;
|
|
|
|
Prev = NULL;
|
|
for (Ext = g_ExtDlls; Ext != NULL; Ext = Ext->Next)
|
|
{
|
|
if (Match == Ext)
|
|
{
|
|
break;
|
|
}
|
|
|
|
Prev = Ext;
|
|
}
|
|
|
|
if (Ext == NULL)
|
|
{
|
|
ErrOut("! Extension DLL list inconsistency !\n");
|
|
}
|
|
else if (Prev == NULL)
|
|
{
|
|
g_ExtDlls = Ext->Next;
|
|
}
|
|
else
|
|
{
|
|
Prev->Next = Ext->Next;
|
|
}
|
|
}
|
|
|
|
void
|
|
DeferExtensionDll(EXTDLL *Ext, BOOL Verbose)
|
|
{
|
|
if (Ext->Dll == NULL)
|
|
{
|
|
// Already deferred.
|
|
return;
|
|
}
|
|
|
|
Ext->Init = NULL;
|
|
Ext->Notify = NULL;
|
|
Ext->ApiVersionRoutine = NULL;
|
|
Ext->CheckVersionRoutine = NULL;
|
|
|
|
if (Ext->Uninit != NULL)
|
|
{
|
|
Ext->Uninit();
|
|
Ext->Uninit = NULL;
|
|
}
|
|
|
|
if (Ext->Dll != NULL)
|
|
{
|
|
if (Verbose)
|
|
{
|
|
dprintf("Unloading %s extension DLL\n", Ext->Name);
|
|
}
|
|
else if (!g_QuietMode)
|
|
{
|
|
VerbOut("Unloading %s extension DLL\n", Ext->Name);
|
|
}
|
|
|
|
FreeExtensionLibrary(Ext);
|
|
}
|
|
}
|
|
|
|
void
|
|
UnloadExtensionDll(EXTDLL *Ext, BOOL Verbose)
|
|
{
|
|
UnlinkExtensionDll(Ext);
|
|
DeferExtensionDll(Ext, Verbose);
|
|
free(Ext);
|
|
NotifyChangeEngineState(DEBUG_CES_EXTENSIONS, 0, TRUE);
|
|
}
|
|
|
|
void
|
|
MoveExtensionToHead(EXTDLL* Ext)
|
|
{
|
|
UnlinkExtensionDll(Ext);
|
|
LinkExtensionDll(Ext);
|
|
}
|
|
|
|
void
|
|
UnloadTargetExtensionDlls(TargetInfo* Target)
|
|
{
|
|
EXTDLL* Ext;
|
|
|
|
for (;;)
|
|
{
|
|
for (Ext = g_ExtDlls; Ext != NULL; Ext = Ext->Next)
|
|
{
|
|
if (Ext->Target == Target)
|
|
{
|
|
UnloadExtensionDll(Ext, FALSE);
|
|
// Force a loop around as the list has
|
|
// changed.
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!Ext)
|
|
{
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
void
|
|
DeferAllExtensionDlls(void)
|
|
{
|
|
EXTDLL* Ext;
|
|
|
|
for (Ext = g_ExtDlls; Ext != NULL; Ext = Ext->Next)
|
|
{
|
|
DeferExtensionDll(Ext, FALSE);
|
|
}
|
|
}
|
|
|
|
BOOL
|
|
CallAnyExtension(DebugClient* Client,
|
|
EXTDLL* Ext, PSTR Function, PCSTR Arguments,
|
|
BOOL ModuleSpecified, BOOL ShowWarnings,
|
|
HRESULT* ExtStatus)
|
|
{
|
|
if (Ext == NULL)
|
|
{
|
|
Ext = g_ExtDlls;
|
|
}
|
|
|
|
// Walk through the list of extension DLLs and attempt to
|
|
// call the given extension function on them.
|
|
while (Ext != NULL)
|
|
{
|
|
//
|
|
// hack : only dbghelp extensions or analyzebugcheck
|
|
// will work on minidump files right now.
|
|
//
|
|
|
|
// Let all the extensions run on minidumps since there is more data
|
|
// in the dumps now.
|
|
//
|
|
// char Name[_MAX_FNAME + 1];
|
|
//
|
|
// _splitpath(Ext->Name, NULL, NULL, Name, NULL);
|
|
//
|
|
// if (!IS_KERNEL_TRIAGE_DUMP(g_Target) ||
|
|
// !_stricmp(Name, "dbghelp") ||
|
|
// !_stricmp(Name, "dbgtstext") || // used by the test team
|
|
// !_stricmp(Name, "dt_exts") || // used by the test team
|
|
// !_stricmp(Name, "ext"))
|
|
{
|
|
if ((!Ext->Target || Ext->Target == g_Target) &&
|
|
LoadExtensionDll(g_Target, Ext))
|
|
{
|
|
BOOL DidCall;
|
|
|
|
DidCall = CallExtension(Client, Ext, Function, Arguments,
|
|
ExtStatus);
|
|
if (DidCall &&
|
|
*ExtStatus != DEBUG_EXTENSION_CONTINUE_SEARCH)
|
|
{
|
|
return TRUE;
|
|
}
|
|
|
|
if (!DidCall && ModuleSpecified)
|
|
{
|
|
// If a DLL was explicitly specified then the
|
|
// missing function is an error.
|
|
if (ShowWarnings &&
|
|
!(g_EnvDbgOptions & OPTION_NOEXTWARNING))
|
|
{
|
|
MaskOut(DEBUG_OUTPUT_EXTENSION_WARNING,
|
|
"%s has no %s export\n", Ext->Name, Function);
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
}
|
|
}
|
|
|
|
Ext = Ext->Next;
|
|
}
|
|
|
|
if (ShowWarnings && !(g_EnvDbgOptions & OPTION_NOEXTWARNING))
|
|
{
|
|
MaskOut(DEBUG_OUTPUT_EXTENSION_WARNING,
|
|
"No export %s found\n", Function);
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
void
|
|
OutputModuleIdInfo(HMODULE Mod, PSTR ModFile, LPEXT_API_VERSION ApiVer)
|
|
{
|
|
WCHAR FileBuf[MAX_IMAGE_PATH];
|
|
char *File;
|
|
time_t TimeStamp;
|
|
char *TimeStr;
|
|
char VerStr[64];
|
|
|
|
if (Mod == NULL)
|
|
{
|
|
Mod = GetModuleHandle(ModFile);
|
|
}
|
|
|
|
if (MultiByteToWideChar(CP_ACP, 0, ModFile, -1,
|
|
FileBuf, DIMA(FileBuf)) &&
|
|
GetFileStringFileInfo(FileBuf, "ProductVersion",
|
|
VerStr, sizeof(VerStr)))
|
|
{
|
|
dprintf("image %s, ", VerStr);
|
|
}
|
|
|
|
if (ApiVer != NULL)
|
|
{
|
|
dprintf("API %d.%d.%d, ",
|
|
ApiVer->MajorVersion,
|
|
ApiVer->MinorVersion,
|
|
ApiVer->Revision);
|
|
}
|
|
|
|
TimeStamp = GetTimestampForLoadedLibrary(Mod);
|
|
TimeStr = ctime(&TimeStamp);
|
|
// Delete newline.
|
|
TimeStr[strlen(TimeStr) - 1] = 0;
|
|
|
|
if (GetModuleFileName(Mod, (PSTR)FileBuf, DIMA(FileBuf) - 1) == 0)
|
|
{
|
|
File = "Unable to get filename";
|
|
}
|
|
else
|
|
{
|
|
File = (PSTR)FileBuf;
|
|
}
|
|
|
|
dprintf("built %s\n [path: %s]\n", TimeStr, File);
|
|
}
|
|
|
|
void
|
|
OutputExtensions(DebugClient* Client, BOOL Versions)
|
|
{
|
|
if ((g_Target && g_Target->m_ExtensionSearchPath) ||
|
|
(!g_Target && g_BaseExtensionSearchPath))
|
|
{
|
|
dprintf("Extension DLL search Path:\n %s\n",
|
|
g_Target ?
|
|
g_Target->m_ExtensionSearchPath : g_BaseExtensionSearchPath);
|
|
}
|
|
else
|
|
{
|
|
dprintf("Default extension DLLs are not loaded until "
|
|
"after initial connection\n");
|
|
if (g_ExtDlls == NULL)
|
|
{
|
|
return;
|
|
}
|
|
}
|
|
|
|
dprintf("Extension DLL chain:\n");
|
|
if (g_ExtDlls == NULL)
|
|
{
|
|
dprintf(" <Empty>\n");
|
|
return;
|
|
}
|
|
|
|
EXTDLL *Ext;
|
|
|
|
for (Ext = g_ExtDlls; Ext != NULL; Ext = Ext->Next)
|
|
{
|
|
if (Ext->Target && Ext->Target != g_Target)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
if (Versions & (Ext->Dll == NULL))
|
|
{
|
|
LoadExtensionDll(g_Target, Ext);
|
|
}
|
|
|
|
dprintf(" %s: ", Ext->Name);
|
|
if (Ext->Dll != NULL)
|
|
{
|
|
LPEXT_API_VERSION ApiVer;
|
|
|
|
if ((Ext->ExtensionType == DEBUG_EXTENSION_TYPE) ||
|
|
(Ext->ApiVersionRoutine != NULL))
|
|
{
|
|
ApiVer = &Ext->ApiVersion;
|
|
}
|
|
else
|
|
{
|
|
ApiVer = NULL;
|
|
}
|
|
|
|
OutputModuleIdInfo(Ext->Dll, Ext->Name, ApiVer);
|
|
|
|
if (Versions)
|
|
{
|
|
HRESULT ExtStatus;
|
|
|
|
CallExtension(Client, Ext, "version", "", &ExtStatus);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
dprintf("(Not loaded)\n");
|
|
}
|
|
}
|
|
}
|
|
|
|
void
|
|
NotifyExtensions(ULONG Notify, ULONG64 Argument)
|
|
{
|
|
EXTDLL *Ext;
|
|
|
|
// This routine deliberately does not provoke
|
|
// a DLL load.
|
|
for (Ext = g_ExtDlls; Ext != NULL; Ext = Ext->Next)
|
|
{
|
|
if ((!Ext->Target || Ext->Target == g_Target) &&
|
|
Ext->Notify != NULL)
|
|
{
|
|
__try
|
|
{
|
|
Ext->Notify(Notify, Argument);
|
|
}
|
|
__except(ExtensionExceptionFilter(GetExceptionInformation(),
|
|
Ext->Name,
|
|
"DebugExtensionNotify"))
|
|
{
|
|
// Empty.
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
//
|
|
// Built-in extension commands.
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
VOID
|
|
ParseBangCmd(DebugClient* Client,
|
|
BOOL BuiltInOnly)
|
|
{
|
|
PSTR Cmd, Scan;
|
|
PSTR ModName;
|
|
PSTR FnName;
|
|
EXTDLL* Ext;
|
|
char CmdCopy[MAX_COMMAND];
|
|
char Save;
|
|
PSTR FnArgs;
|
|
|
|
//
|
|
// Shell escape always consumes the entire string.
|
|
//
|
|
|
|
if (*g_CurCmd == '!')
|
|
{
|
|
g_CurCmd++;
|
|
DotShell(NULL, Client);
|
|
*g_CurCmd = 0;
|
|
return;
|
|
}
|
|
|
|
PeekChar();
|
|
|
|
// Make a copy of the command string so that modifications
|
|
// do not change the actual command string the debugger is processing.
|
|
CopyString(CmdCopy, g_CurCmd, DIMA(CmdCopy));
|
|
|
|
//
|
|
// Syntax is [path-without-spaces]module.function argument-string.
|
|
//
|
|
|
|
ModName = CmdCopy;
|
|
FnName = NULL;
|
|
|
|
Cmd = CmdCopy;
|
|
while (*Cmd != ' ' && *Cmd != '\t' && *Cmd &&
|
|
*Cmd != ';' && *Cmd != '"')
|
|
{
|
|
Cmd++;
|
|
}
|
|
|
|
Scan = Cmd;
|
|
if (*Cmd && *Cmd != ';' && *Cmd != '"')
|
|
{
|
|
*Cmd = 0;
|
|
Cmd++;
|
|
}
|
|
|
|
while (*Scan != '.' && Scan != ModName)
|
|
{
|
|
Scan--;
|
|
}
|
|
|
|
if (*Scan == '.' && !BuiltInOnly)
|
|
{
|
|
*Scan = 0;
|
|
Scan++;
|
|
FnName = Scan;
|
|
}
|
|
else
|
|
{
|
|
FnName = ModName;
|
|
ModName = NULL;
|
|
}
|
|
|
|
if ((FnArgs = BufferStringValue(&Cmd,
|
|
STRV_ESCAPED_CHARACTERS |
|
|
STRV_ALLOW_EMPTY_STRING,
|
|
NULL, &Save)) == NULL)
|
|
{
|
|
ErrOut("Syntax error in extension string\n");
|
|
return;
|
|
}
|
|
|
|
// Update the real command string pointer to account for
|
|
// the characters parsed in the copy.
|
|
g_CurCmd += Cmd - CmdCopy;
|
|
|
|
//
|
|
// ModName -> Name of module
|
|
// FnName -> Name of command to process
|
|
// FnArgs -> argument to command
|
|
//
|
|
|
|
if (ModName != NULL)
|
|
{
|
|
Ext = AddExtensionDll(ModName, TRUE, NULL, NULL);
|
|
if (Ext == NULL)
|
|
{
|
|
return;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
Ext = g_ExtDlls;
|
|
}
|
|
|
|
if (!_stricmp(FnName, "load"))
|
|
{
|
|
if (ModName == NULL)
|
|
{
|
|
Ext = AddExtensionDll(FnArgs, TRUE, NULL, NULL);
|
|
if (Ext == NULL)
|
|
{
|
|
return;
|
|
}
|
|
}
|
|
LoadExtensionDll(g_Target, Ext);
|
|
return;
|
|
}
|
|
else if (!_stricmp(FnName, "setdll"))
|
|
{
|
|
if (ModName == NULL)
|
|
{
|
|
Ext = AddExtensionDll(FnArgs, TRUE, NULL, NULL);
|
|
if (Ext == NULL)
|
|
{
|
|
return;
|
|
}
|
|
}
|
|
MoveExtensionToHead(Ext);
|
|
if (ModName != NULL && Ext->Dll == NULL)
|
|
{
|
|
dprintf("Added %s to extension DLL chain\n", Ext->Name);
|
|
}
|
|
return;
|
|
}
|
|
else if (!_stricmp(FnName, "unload"))
|
|
{
|
|
if (ModName == NULL)
|
|
{
|
|
if (*FnArgs == '\0')
|
|
{
|
|
Ext = g_ExtDlls;
|
|
}
|
|
else
|
|
{
|
|
Ext = AddExtensionDll(FnArgs, TRUE, NULL, NULL);
|
|
}
|
|
if (Ext == NULL)
|
|
{
|
|
return;
|
|
}
|
|
}
|
|
if (Ext != NULL)
|
|
{
|
|
UnloadExtensionDll(Ext, TRUE);
|
|
}
|
|
return;
|
|
}
|
|
else if (!_stricmp(FnName, "unloadall"))
|
|
{
|
|
g_EngNotify++;
|
|
while (g_ExtDlls != NULL)
|
|
{
|
|
UnloadExtensionDll(g_ExtDlls, TRUE);
|
|
}
|
|
g_EngNotify--;
|
|
NotifyChangeEngineState(DEBUG_CES_EXTENSIONS, 0, TRUE);
|
|
return;
|
|
}
|
|
|
|
if (BuiltInOnly)
|
|
{
|
|
error(SYNTAX);
|
|
}
|
|
|
|
HRESULT ExtStatus;
|
|
|
|
CallAnyExtension(Client, Ext, FnName, FnArgs, ModName != NULL, TRUE,
|
|
&ExtStatus);
|
|
}
|
|
|
|
void
|
|
ReadDebugOptions (BOOL fQuiet, char * pszOptionsStr)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Parses an options string (see g_EnvDbgOptionNames) and maps
|
|
it to OPTION_ flags (in g_EnvDbgOptions).
|
|
|
|
Arguments:
|
|
|
|
fQuiet - If TRUE, do not print option settings.
|
|
pszOptionsStr - Options string; if NULL, get it from _NT_DEBUG_OPTIONS
|
|
|
|
Return Value:
|
|
|
|
None
|
|
--*/
|
|
{
|
|
BOOL fInit;
|
|
char ** ppszOption;
|
|
char * psz;
|
|
DWORD dwMask;
|
|
int iOptionCount;
|
|
|
|
fInit = (pszOptionsStr == NULL);
|
|
if (fInit)
|
|
{
|
|
g_EnvDbgOptions = 0;
|
|
pszOptionsStr = getenv("_NT_DEBUG_OPTIONS");
|
|
}
|
|
if (pszOptionsStr == NULL)
|
|
{
|
|
if (!fQuiet)
|
|
{
|
|
dprintf("_NT_DEBUG_OPTIONS is not defined\n");
|
|
}
|
|
return;
|
|
}
|
|
psz = pszOptionsStr;
|
|
while (*psz != '\0')
|
|
{
|
|
*psz = (char)toupper(*psz);
|
|
psz++;
|
|
}
|
|
ppszOption = g_EnvDbgOptionNames;
|
|
for (iOptionCount = 0;
|
|
iOptionCount < OPTION_COUNT;
|
|
iOptionCount++, ppszOption++)
|
|
{
|
|
if ((strstr(pszOptionsStr, *ppszOption) == NULL))
|
|
{
|
|
continue;
|
|
}
|
|
dwMask = (1 << iOptionCount);
|
|
if (fInit)
|
|
{
|
|
g_EnvDbgOptions |= dwMask;
|
|
}
|
|
else
|
|
{
|
|
g_EnvDbgOptions ^= dwMask;
|
|
}
|
|
}
|
|
if (!fQuiet)
|
|
{
|
|
dprintf("Debug Options:");
|
|
if (g_EnvDbgOptions == 0)
|
|
{
|
|
dprintf(" <none>\n");
|
|
}
|
|
else
|
|
{
|
|
dwMask = g_EnvDbgOptions;
|
|
ppszOption = g_EnvDbgOptionNames;
|
|
while (dwMask != 0)
|
|
{
|
|
if (dwMask & 0x1)
|
|
{
|
|
dprintf(" %s", *ppszOption);
|
|
}
|
|
dwMask >>= 1;
|
|
ppszOption++;
|
|
}
|
|
dprintf("\n");
|
|
}
|
|
}
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
//
|
|
// LoadWow64ExtsIfNeeded
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
VOID
|
|
LoadWow64ExtsIfNeeded(ULONG64 Process)
|
|
{
|
|
LONG_PTR Wow64Info;
|
|
NTSTATUS Status;
|
|
EXTDLL * Extension;
|
|
|
|
// wx86 only runs on NT.
|
|
if (g_DebuggerPlatformId != VER_PLATFORM_WIN32_NT)
|
|
{
|
|
return;
|
|
}
|
|
|
|
//
|
|
// if New process is a Wx86 process, load in the wx86 extensions
|
|
// dll. This will stay loaded until ntsd exits.
|
|
//
|
|
|
|
Status = g_NtDllCalls.NtQueryInformationProcess(OS_HANDLE(Process),
|
|
ProcessWow64Information,
|
|
&Wow64Info,
|
|
sizeof(Wow64Info),
|
|
NULL
|
|
);
|
|
|
|
if (NT_SUCCESS(Status) && Wow64Info)
|
|
{
|
|
Extension = AddExtensionDll("wow64exts", FALSE, g_Target, NULL);
|
|
|
|
//
|
|
// Force load it so we get the entry point the debugger needs
|
|
//
|
|
LoadExtensionDll(g_Target, Extension);
|
|
}
|
|
}
|