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

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);
}
}