windows-nt/Source/XPSP1/NT/sdktools/debuggers/ntsd64/exts.cpp
2020-09-26 16:20:57 +08:00

3448 lines
92 KiB
C++

//----------------------------------------------------------------------------
//
// Extension DLL support.
//
// Copyright (C) Microsoft Corporation, 1997-2001.
//
//----------------------------------------------------------------------------
#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",
};
ULONG g_PipeSerialNumber;
EXTDLL *g_ExtDlls;
LPTSTR g_ExtensionSearchPath = NULL;
ULONG64 g_ExtThread;
ULONG g_ExtGetExpressionRemainderIndex;
BOOL g_ExtGetExpressionSuccess;
WOW64EXTSPROC g_Wow64exts;
WMI_FORMAT_TRACE_DATA g_WmiFormatTraceData;
DEBUG_SCOPE g_ExtThreadSavedScope;
BOOL g_ExtThreadScopeSaved;
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
};
NTSD_EXTENSION_APIS g_NtsdExtensions64 =
{
sizeof(g_NtsdExtensions64),
(PNTSD_OUTPUT_ROUTINE)ExtOutput32,
(PNTSD_GET_EXPRESSION)ExtGetExpression,
(PNTSD_GET_SYMBOL)ExtGetSymbol,
(PNTSD_DISASM)ExtDisasm,
(PNTSD_CHECK_CONTROL_C)CheckUserInterrupt,
};
NTSD_EXTENSION_APIS g_NtsdExtensions32 =
{
sizeof(g_NtsdExtensions32),
(PNTSD_OUTPUT_ROUTINE)ExtOutput32,
(PNTSD_GET_EXPRESSION)ExtGetExpression32,
(PNTSD_GET_SYMBOL)ExtGetSymbol32,
(PNTSD_DISASM)ExtDisasm32,
(PNTSD_CHECK_CONTROL_C)CheckUserInterrupt,
};
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;
PSTR SaveStart = g_CommandStart;
if (IS_USER_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++;
}
}
SaveCommand = g_CurCmd;
g_CurCmd = (PSTR)CommandString;
g_CommandStart = (PSTR)CommandString;
g_DisableErrorPrint = TRUE;
__try
{
ReturnValue = GetExpression();
g_ExtGetExpressionSuccess = TRUE;
}
__except(CommandExceptionFilter(GetExceptionInformation()))
{
ReturnValue = 0;
}
g_ExtGetExpressionRemainderIndex =
(ULONG)(g_CurCmd - g_CommandStart);
g_DisableErrorPrint = FALSE;
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
GetSymbolStdCall(offset, pchBuffer, 256, pDisplacement, NULL);
// 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
GetSymbolStdCall(EXTEND64(offset), pchBuffer, 256,
&Displacement, NULL);
*pDisplacement = (ULONG)Displacement;
// Make sure output for long-running extensions appears regularly.
TimedFlushCallbacks();
}
DWORD
ExtDisasm(
ULONG64 *lpOffset,
PCSTR lpBuffer,
ULONG fShowEA
)
{
if (!IS_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(&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 lpContext,
DWORD cbSizeOfContext
)
{
if (!IS_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.
FlushRegContext();
CROSS_PLATFORM_CONTEXT TargetContext;
g_TargetMachine->InitializeContextFlags(&TargetContext, g_SystemVersion);
if (g_Target->GetContext(IS_KERNEL_TARGET() ?
VIRTUAL_THREAD_HANDLE(Processor) : Processor,
&TargetContext) == S_OK &&
g_Machine->ConvertContextTo(&TargetContext, g_SystemVersion,
cbSizeOfContext, lpContext) == S_OK)
{
return TRUE;
}
return FALSE;
}
BOOL
ExtSetThreadContext(
DWORD Processor,
PVOID lpContext,
DWORD cbSizeOfContext
)
{
if (!IS_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.
ChangeRegContext(NULL);
CROSS_PLATFORM_CONTEXT TargetContext;
if (g_Machine->ConvertContextFrom(&TargetContext, g_SystemVersion,
cbSizeOfContext, lpContext) == S_OK &&
g_Target->SetContext(IS_KERNEL_TARGET() ?
VIRTUAL_THREAD_HANDLE(Processor) : Processor,
&TargetContext) == S_OK)
{
Status = TRUE;
}
else
{
Status = FALSE;
}
// Reset the current thread.
ChangeRegContext(g_CurrentProcess->CurrentThread);
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(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(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(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(EXTEND64(addr),
(PVOID)buffer, count,
pcBytesWritten != NULL ?
pcBytesWritten : &BytesTemp) == S_OK);
}
BOOL
ExtReadPhysicalMemory(
ULONGLONG pBufSrc,
PVOID pBufDest,
ULONG count,
PULONG pcTotalBytesRead
)
{
// Make sure output for long-running extensions appears regularly.
TimedFlushCallbacks();
if (ARGUMENT_PRESENT(pcTotalBytesRead)) {
*pcTotalBytesRead = 0;
}
ULONG BytesTemp;
return g_Target->ReadPhysical(pBufSrc, pBufDest, count,
pcTotalBytesRead != NULL ?
pcTotalBytesRead : &BytesTemp) == S_OK;
}
BOOL
ExtWritePhysicalMemory (
ULONGLONG pBufDest,
LPCVOID pBufSrc,
ULONG count,
PULONG pcTotalBytesWritten
)
{
if (ARGUMENT_PRESENT(pcTotalBytesWritten)) {
*pcTotalBytesWritten = 0;
}
ULONG BytesTemp;
return g_Target->WritePhysical(pBufDest, (PVOID)pBufSrc, count,
pcTotalBytesWritten != NULL ?
pcTotalBytesWritten : &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;
}
FrameCount = StackTrace( FramePointer, StackPointer, ProgramCounter,
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;
}
FrameCount = StackTrace(EXTEND64(FramePointer),
EXTEND64(StackPointer),
EXTEND64(ProgramCounter),
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;
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();
switch( IoctlType )
{
case IG_KD_CONTEXT:
pi = (PPROCESSORINFO) lpvData;
pi->Processor = (USHORT)CURRENT_PROC;
pi->NumberProcessors = (USHORT) g_TargetNumberProcessors;
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_MACHINE_ACCESSIBLE())
{
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_MACHINE_ACCESSIBLE())
{
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_LOWMEM_CHECK:
Status = g_Target->CheckLowMemory();
return Status == S_OK;
case IG_SEARCH_MEMORY:
psr = (PSEARCHMEMORY)lpvData;
Status = g_Target->SearchVirtual(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_EngNotify++; // Turn off engine notifications since this setthread is temporary
PushScope(&g_ExtThreadSavedScope);
g_ExtThread = *(PULONG64)lpvData;
Bool = SetContextFromThreadData(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:
*((PDBGKD_GET_VERSION64)lpvData) = g_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:
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:
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() ||
((PDBGKD_DEBUG_DATA_HEADER64)lpvData)->OwnerTag != KDBG_TAG)
{
return FALSE;
}
// Don't refresh if asking for the kernel header.
memcpy(lpvData, &KdDebuggerData, min(sizeof(KdDebuggerData), cbSize));
return TRUE;
case IG_RELOAD_SYMBOLS:
return g_Target->Reload((PCHAR)lpvData) == S_OK;
case IG_GET_SET_SYMPATH:
PGET_SET_SYMPATH pgs;
pgs = (PGET_SET_SYMPATH)lpvData;
bangSymPath((PCHAR)pgs->Args, FALSE, (PCHAR)pgs->Result,
pgs->Length);
return TRUE;
case IG_IS_PTR64:
*((PBOOL)lpvData) = g_TargetMachine->m_Ptr64;
return TRUE;
case IG_DUMP_SYMBOL_INFO:
pSym = (PSYM_DUMP_PARAM) lpvData;
SymbolTypeDump(g_CurrentProcess->Handle,
g_CurrentProcess->ImageHead,
pSym, (PULONG)&Status);
return Status;
case IG_GET_TYPE_SIZE:
pSym = (PSYM_DUMP_PARAM) lpvData;
return SymbolTypeDump(g_CurrentProcess->Handle,
g_CurrentProcess->ImageHead,
pSym, (PULONG)&Status);
case IG_GET_TEB_ADDRESS:
PGET_TEB_ADDRESS pTeb;
pTeb = (PGET_TEB_ADDRESS) lpvData;
return g_Target->
GetThreadInfoTeb(NULL,
0,
NULL,
&pTeb->Address) == S_OK;
case IG_GET_PEB_ADDRESS:
PGET_PEB_ADDRESS pPeb;
pPeb = (PGET_PEB_ADDRESS) lpvData;
return g_Target->
GetProcessInfoPeb(NULL,
0,
pPeb->CurrentThread,
&pPeb->Address) == S_OK;
case IG_GET_CURRENT_PROCESS_HANDLE:
*(PHANDLE)lpvData = g_CurrentProcess->Handle;
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);
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_MACHINE_ACCESSIBLE())
{
return FALSE;
}
PTRANSLATE_VIRTUAL_TO_PHYSICAL Tvtp;
Tvtp = (PTRANSLATE_VIRTUAL_TO_PHYSICAL)lpvData;
ULONG Levels, PfIndex;
return g_Machine->
GetVirtualTranslationPhysicalOffsets(Tvtp->Virtual, NULL, 0,
&Levels, &PfIndex,
&Tvtp->Physical) == S_OK;
case IG_GET_CACHE_SIZE:
PULONG64 pCacheSize;
pCacheSize = (PULONG64)lpvData;
if (IS_KERNEL_TARGET()) {
*pCacheSize = g_VirtualCache.m_MaxSize;
return TRUE;
}
return FALSE;
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_DEBUG_DATA_HEADER32 hdr;
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_MACHINE_ACCESSIBLE())
{
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_MACHINE_ACCESSIBLE())
{
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_EngNotify++; // Turn off engine notifications since this setthread is temporary
g_ExtThread = EXTEND64(*(PULONG)lpvData);
PushScope(&g_ExtThreadSavedScope);
SetContextFromThreadData(g_ExtThread, FALSE);
g_ExtThreadScopeSaved = TRUE;
g_EngNotify--;
return TRUE;
}
else
{
return FALSE;
}
case IG_GET_KERNEL_VERSION:
//
// Convert to 32 bit
//
pv32 = (PDBGKD_GET_VERSION32)lpvData;
pv32->MajorVersion = g_KdVersion.MajorVersion;
pv32->MinorVersion = g_KdVersion.MinorVersion;
pv32->ProtocolVersion = g_KdVersion.ProtocolVersion;
pv32->Flags = g_KdVersion.Flags;
pv32->KernBase = (ULONG)g_KdVersion.KernBase;
pv32->PsLoadedModuleList = (ULONG)g_KdVersion.PsLoadedModuleList;
pv32->MachineType = g_KdVersion.MachineType;
pv32->DebuggerDataList = (ULONG)g_KdVersion.DebuggerDataList;
pv32->ThCallbackStack = KdDebuggerData.ThCallbackStack;
pv32->NextCallback = KdDebuggerData.NextCallback;
pv32->FramePointer = KdDebuggerData.FramePointer;
pv32->KiCallUserMode =
(ULONG)KdDebuggerData.KiCallUserMode;
pv32->KeUserCallbackDispatcher =
(ULONG)KdDebuggerData.KeUserCallbackDispatcher;
pv32->BreakpointWithStatus =
(ULONG)KdDebuggerData.BreakpointWithStatus;
return TRUE;
case IG_GET_DEBUGGER_DATA:
if (!IS_KERNEL_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)KdDebuggerData.Header.List.Flink;
pdbg32->Header.List.Blink = (ULONG)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)(KdDebuggerData.f)
#define CP(f) pdbg32->f = (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_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:
// 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;
}
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())
{
_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() && !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_Machine->GetPC(&TempAddr);
}
else
{
if (!IS_LOCAL_KERNEL_TARGET())
{
WarnOut("Extension called without current PC\n");
}
ADDRFLAT(&TempAddr, 0);
}
*ExtStatus = S_OK;
__try
{
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)
(g_CurrentProcess->Handle,
OS_HANDLE(g_CurrentProcess->CurrentThread->Handle),
(ULONG)Flat(TempAddr),
g_TargetMachine->m_Ptr64 ?
(PNTSD_EXTENSION_APIS)&g_WindbgExtensions64 :
(PNTSD_EXTENSION_APIS)&g_WindbgExtensions32,
(PSTR)Args
);
break;
case DEBUG_EXTENSION_TYPE:
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) (
g_CurrentProcess->Handle,
OS_HANDLE(g_CurrentProcess->CurrentThread->Handle),
(ULONG)Flat(TempAddr),
CURRENT_PROC,
Args
);
}
else
{
((PWINDBG_EXTENSION_ROUTINE64)Routine) (
g_CurrentProcess->Handle,
OS_HANDLE(g_CurrentProcess->CurrentThread->Handle),
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,
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;
}
Last = Name;
while (*Last != 0 && *Last != ' ' && *Last != '\t')
{
Last++;
}
if (End != NULL)
{
*End = Last;
}
Len = (ULONG)(Last - Name);
// See if it's already in the list.
for (Ext = g_ExtDlls; Ext != NULL; Ext = Ext->Next)
{
if (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;
LinkExtensionDll(Ext);
NotifyChangeEngineState(DEBUG_CES_EXTENSIONS, 0, TRUE);
return Ext;
}
PCTSTR
BuildExtensionSearchPath(
VOID
)
{
DWORD dwSize;
DWORD dwTotalSize;
CHAR ExeDir[MAX_PATH];
int ExeRootLen;
PSTR OsDirPath;
CHAR OsDirTail[32];
BOOL PriPaths = FALSE;
//
// If we are not connected, don't build a path, since we have to pick
// up extensions based on the OS version.
//
if (g_ActualSystemVersion == SVER_INVALID)
{
return NULL;
}
//
// If we already have a search path, do not rebuild it.
//
if (g_ExtensionSearchPath)
{
return g_ExtensionSearchPath;
}
// Get the directory the debugger executable is in.
// -8 because we assume we're adding \w2kfre to the path.
if (!GetModuleFileName(NULL, ExeDir, MAX_PATH - 8))
{
// Error. Use the current directory.
strcpy(ExeDir, ".");
ExeRootLen = 1;
}
else
{
// Remove the executable name.
LPSTR pszTmp = strrchr(ExeDir, '\\');
if (pszTmp)
{
*pszTmp = 0;
}
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.
//
dwTotalSize = GetEnvironmentVariable("PATH", NULL, 0) +
GetEnvironmentVariable("_NT_DEBUGGER_EXTENSION_PATH",
NULL, 0) +
MAX_PATH * 3;
g_ExtensionSearchPath = (LPTSTR)calloc(dwTotalSize, sizeof(TCHAR));
if (!g_ExtensionSearchPath)
{
return NULL;
}
*g_ExtensionSearchPath = 0;
//
// 1 - User specified search path
//
if (GetEnvironmentVariable("_NT_DEBUGGER_EXTENSION_PATH",
g_ExtensionSearchPath,
dwTotalSize - 2))
{
strcat(g_ExtensionSearchPath, ";");
}
//
// Figure out whether we need NT6, or NT5/NT4 free or checked extensions
//
if (g_ActualSystemVersion > BIG_SVER_START &&
g_ActualSystemVersion < BIG_SVER_END)
{
OsDirPath = "DbgExt";
strcpy(OsDirTail, "BIG");
}
else if (g_ActualSystemVersion > XBOX_SVER_START &&
g_ActualSystemVersion < XBOX_SVER_END)
{
OsDirPath = "DbgExt";
strcpy(OsDirTail, "XBox");
}
else if (g_ActualSystemVersion > NTBD_SVER_START &&
g_ActualSystemVersion < NTBD_SVER_END)
{
OsDirPath = "DbgExt";
strcpy(OsDirTail, "NtBd");
}
else if (g_ActualSystemVersion > EFI_SVER_START &&
g_ActualSystemVersion < EFI_SVER_END)
{
OsDirPath = "DbgExt";
strcpy(OsDirTail, "EFI");
}
else
{
// Treat everything else as an NT system. Use
// the translated system version now rather than
// the actual system version.
PriPaths = TRUE;
// Skip root as it is already taken from ExeDir.
OsDirPath = ExeDir + ExeRootLen;
if (*OsDirPath == '\\')
{
OsDirPath++;
}
if (g_SystemVersion > NT_SVER_W2K)
{
strcpy(OsDirTail, "WINXP");
}
else
{
if (g_SystemVersion <= NT_SVER_NT4)
{
strcpy(OsDirTail, "NT4");
}
else
{
strcpy(OsDirTail, "W2K");
}
if (0xC == g_TargetCheckedBuild)
{
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;
dwSize = strlen(g_ExtensionSearchPath);
End = g_ExtensionSearchPath + dwSize;
memcpy(End, ExeDir, ExeRootLen);
End += ExeRootLen;
if (*OsDirPath)
{
*End++ = '\\';
strcpy(End, OsDirPath);
End += strlen(End);
}
if (PriPaths)
{
sprintf(End, "\\winext;%s\\pri;%s", ExeDir, ExeDir);
End += strlen(End);
}
sprintf(End, "\\%s;%s", OsDirTail, ExeDir);
End += strlen(End) - 1;
if (*End == ':')
{
*++End = '\\';
}
*++End = ';';
*++End = 0;
//
// 4 - Copy environment path
//
dwSize = strlen(g_ExtensionSearchPath);
GetEnvironmentVariable("PATH",
g_ExtensionSearchPath + dwSize,
dwTotalSize - dwSize - sizeof(TCHAR));
return g_ExtensionSearchPath;
}
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;
}
BOOL
LoadExtensionDll(
EXTDLL *Ext
)
{
BOOL Found;
TCHAR szExtPath[_MAX_PATH];
if (Ext->Dll != NULL)
{
// Extension is already loaded.
return TRUE;
}
//
// 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 ());
//
// 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(),
Ext->Name,
".dll",
sizeof(szExtPath),
szExtPath,
NULL);
UINT OldMode = SetErrorMode(SEM_FAILCRITICALERRORS);
if ( Found )
{
Ext->Dll = LoadLibrary ( szExtPath );
}
else if (IsAbsolutePath ( Ext->Name ))
{
Ext->Dll = LoadLibrary ( Ext->Name );
}
SetErrorMode(OldMode);
if (Ext->Dll == NULL)
{
ErrOut("The call to LoadLibrary(%s) failed with error %d.\n"
"Please check your debugger configuration "
"and/or network access\n",
Ext->Name, GetLastError());
return FALSE;
}
if (!_stricmp(Ext->Name, "wow64exts.dll") ||
!_stricmp(Ext->Name, "wow64exts"))
{
g_Wow64exts = (WOW64EXTSPROC)GetProcAddress(Ext->Dll,"Wow64extsfn");
DBG_ASSERT(g_Wow64exts);
}
if (!_stricmp(Ext->Name, "wmikd.dll") ||
!_stricmp(Ext->Name, "wmikd"))
{
g_WmiFormatTraceData = (WMI_FORMAT_TRACE_DATA)
GetProcAddress(Ext->Dll, "WmiFormatTraceData");
}
if (!g_QuietMode)
{
dprintf("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);
FreeLibrary(Ext->Dll);
Ext->Dll = NULL;
return FALSE;
}
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)
{
FreeLibrary(Ext->Dll);
Ext->Dll = NULL;
ErrOut("%s is not a valid windbg extension DLL\n",
Ext->Name);
return FALSE;
}
Ext->CheckVersionRoutine = (PWINDBG_CHECK_VERSION)
GetProcAddress(Ext->Dll, "CheckVersion");
Ext->ApiVersion = *(Ext->ApiVersionRoutine());
if (Ext->ApiVersion.Revision >= 6)
{
(Ext->Init)(&g_WindbgExtensions64,
(USHORT)g_TargetCheckedBuild,
(USHORT)g_TargetBuildNumber);
}
else
{
(Ext->Init)((PWINDBG_EXTENSION_APIS64)&g_WindbgExtensions32,
(USHORT)g_TargetCheckedBuild,
(USHORT)g_TargetBuildNumber);
}
}
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() ?
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)
{
if (IS_MACHINE_SET())
{
Ext->Notify(DEBUG_NOTIFY_SESSION_ACTIVE, 0);
}
if (IS_MACHINE_ACCESSIBLE())
{
Ext->Notify(DEBUG_NOTIFY_SESSION_ACCESSIBLE, 0);
}
}
return TRUE;
}
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
)
{
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 (!g_QuietMode)
{
dprintf("Unloading %s extension DLL\n", Ext->Name);
}
FreeLibrary(Ext->Dll);
Ext->Dll = NULL;
}
}
void
UnloadExtensionDll(
EXTDLL *Ext
)
{
UnlinkExtensionDll(Ext);
DeferExtensionDll(Ext);
free(Ext);
NotifyChangeEngineState(DEBUG_CES_EXTENSIONS, 0, TRUE);
}
void
MoveExtensionToHead(EXTDLL* Ext)
{
UnlinkExtensionDll(Ext);
LinkExtensionDll(Ext);
}
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.
//
char Name[_MAX_FNAME + 1];
_splitpath(Ext->Name,NULL,NULL,Name,NULL);
if (!IS_KERNEL_TRIAGE_DUMP() ||
!strcmp(Name, "dbghelp") ||
!_stricmp(Name, "dbgtstext") ||
!_stricmp(Function, "triage") ||
!_stricmp(Function, "analyzebugcheck"))
{
if (LoadExtensionDll(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 && IS_KERNEL_TRIAGE_DUMP())
{
ErrOut("Standard debugger extensions do not work with kernel minidump\n"
"files because no data is present in the dump file.\n"
"Consult the debugger documentation for more information on\n"
"kernel minidump files\n");
}
else 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)
{
char FileBuf[MAX_IMAGE_PATH];
char *File;
time_t TimeStamp;
char *TimeStr;
char VerStr[64];
if (Mod == NULL)
{
Mod = GetModuleHandle(ModFile);
}
if (GetFileStringFileInfo(ModFile, "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, FileBuf, sizeof(FileBuf) - 1) == 0)
{
File = "Unable to get filename";
}
else
{
File = FileBuf;
}
dprintf("built %s\n [path: %s]\n", TimeStr, File);
}
void
OutputExtensions(DebugClient* Client, BOOL Versions)
{
if (g_ExtensionSearchPath != NULL)
{
dprintf("Extension DLL search Path:\n %s\n",
g_ExtensionSearchPath);
}
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 (Versions & (Ext->Dll == NULL))
{
LoadExtensionDll(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
LoadMachineExtensions(void)
{
// Only notify once for all the adds in this function;
g_EngNotify++;
//
// Now that we have determined the type of architecture,
// we can load the right debugger extensions
//
if (g_ActualSystemVersion > BIG_SVER_START &&
g_ActualSystemVersion < BIG_SVER_END)
{
goto Refresh;
}
if (g_ActualSystemVersion > XBOX_SVER_START &&
g_ActualSystemVersion < XBOX_SVER_END)
{
AddExtensionDll("kdextx86", FALSE, NULL);
goto Refresh;
}
if (g_ActualSystemVersion > NTBD_SVER_START &&
g_ActualSystemVersion < NTBD_SVER_END)
{
goto Refresh;
}
// Treat everything else as an NT system.
if (IS_KERNEL_TARGET())
{
if (g_TargetMachineType == IMAGE_FILE_MACHINE_IA64)
{
//
// We rely on force loading of extensions at the end of this
// routine in order to get the entry point the debugger needs.
//
AddExtensionDll("wow64exts", FALSE, NULL);
}
switch (g_TargetMachineType)
{
case IMAGE_FILE_MACHINE_ALPHA:
AddExtensionDll("kdextalp", FALSE, NULL);
break;
case IMAGE_FILE_MACHINE_I386:
if (g_SystemVersion > NT_SVER_START &&
g_SystemVersion <= NT_SVER_W2K)
{
AddExtensionDll("kdextx86", FALSE, NULL);
break;
}
// Fall through
default:
//
// For all new architectures and new X86 builds, load
// kdexts
//
AddExtensionDll("kdexts", FALSE, NULL);
break;
}
//
// Extensions that work on all versions of the OS for kernel mode
// Many of these are messages about legacy extensions.
AddExtensionDll("kext", FALSE, NULL);
}
else
{
// User mode only extensions
AddExtensionDll("ntsdexts", FALSE, NULL);
AddExtensionDll("uext", FALSE, NULL);
}
// Use the translated system version now rather than
// the actual system version.
if (g_SystemVersion > NT_SVER_W2K &&
g_SystemVersion < NT_SVER_END)
{
AddExtensionDll("exts", FALSE, NULL);
}
// Load ext.dll for all NT versions
AddExtensionDll("ext", FALSE, NULL);
Refresh:
// Always load the Dbghelp extensions last so they are first on the list
AddExtensionDll("dbghelp", FALSE, NULL);
EXTDLL *Ext;
for (Ext = g_ExtDlls; Ext != NULL; Ext = Ext->Next)
{
LoadExtensionDll(Ext);
}
g_EngNotify--;
NotifyChangeEngineState(DEBUG_CES_EXTENSIONS, 0, TRUE);
}
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->Notify != NULL)
{
Ext->Notify(Notify, Argument);
}
}
}
struct SHELL_READER_INFO
{
HANDLE IoHandles[3];
HANDLE OutEvent;
};
DWORD WINAPI
ShellReaderThread(
LPVOID Param
)
{
SHELL_READER_INFO* ReaderInfo = (SHELL_READER_INFO*)Param;
OVERLAPPED Overlapped;
HANDLE WaitHandles[2];
DWORD Error;
UCHAR Buffer[_MAX_PATH];
DWORD BytesRead;
DWORD WaitStatus;
memset(&Overlapped, 0, sizeof(Overlapped));
Overlapped.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
if (Overlapped.hEvent == NULL)
{
return ERROR_NOT_ENOUGH_MEMORY;
}
WaitHandles[0] = Overlapped.hEvent;
WaitHandles[1] = ReaderInfo->IoHandles[2];
//
// wait for data on handle 1.
// wait for signal on handle 2.
//
while (1)
{
//
// initiate the read
//
ResetEvent( Overlapped.hEvent );
if (ReadFile(ReaderInfo->IoHandles[1], Buffer, sizeof(Buffer) - 1,
&BytesRead, &Overlapped))
{
//
// Read has successfully completed, print and repeat.
//
Buffer[BytesRead] = 0;
dprintf("%s", Buffer);
// Notify the main thread that output was produced.
SetEvent(ReaderInfo->OutEvent);
}
else
{
Error = GetLastError();
if (Error != ERROR_IO_PENDING)
{
dprintf(".shell: ReadFile failed, error == %d\n", Error);
break;
}
// Flush output before waiting.
FlushCallbacks();
WaitStatus = WaitForMultipleObjects(2, WaitHandles, FALSE,
INFINITE);
if (WaitStatus == WAIT_OBJECT_0)
{
if (GetOverlappedResult(ReaderInfo->IoHandles[1], &Overlapped,
&BytesRead, TRUE))
{
//
// Read has successfully completed
//
Buffer[BytesRead] = 0;
dprintf("%s", Buffer);
// Notify the main thread that output was produced.
SetEvent(ReaderInfo->OutEvent);
}
else
{
Error = GetLastError();
dprintf(".shell: GetOverlappedResult failed, "
"error == %d\n",
Error);
break;
}
}
else if (WaitStatus == WAIT_OBJECT_0 + 1)
{
//
// process exited.
//
dprintf(".shell: Process exited\n");
break;
}
else
{
dprintf(".shell: WaitForMultipleObjects failed; error == %d\n",
Error);
break;
}
}
}
CloseHandle(Overlapped.hEvent);
dprintf("Press ENTER to continue\n");
// Flush all remaining output.
FlushCallbacks();
// Notify the main thread that output was produced.
SetEvent(ReaderInfo->OutEvent);
return 0;
}
BOOL
APIENTRY
MyCreatePipeEx(
OUT LPHANDLE lpReadPipe,
OUT LPHANDLE lpWritePipe,
IN LPSECURITY_ATTRIBUTES lpPipeAttributes,
IN DWORD nSize,
DWORD dwReadMode,
DWORD dwWriteMode
)
/*++
Routine Description:
The CreatePipeEx API is used to create an anonymous pipe I/O device.
Unlike CreatePipe FILE_FLAG_OVERLAPPED may be specified for one or
both handles.
Two handles to the device are created. One handle is opened for
reading and the other is opened for writing. These handles may be
used in subsequent calls to ReadFile and WriteFile to transmit data
through the pipe.
Arguments:
lpReadPipe - Returns a handle to the read side of the pipe. Data
may be read from the pipe by specifying this handle value in a
subsequent call to ReadFile.
lpWritePipe - Returns a handle to the write side of the pipe. Data
may be written to the pipe by specifying this handle value in a
subsequent call to WriteFile.
lpPipeAttributes - An optional parameter that may be used to specify
the attributes of the new pipe. If the parameter is not
specified, then the pipe is created without a security
descriptor, and the resulting handles are not inherited on
process creation. Otherwise, the optional security attributes
are used on the pipe, and the inherit handles flag effects both
pipe handles.
nSize - Supplies the requested buffer size for the pipe. This is
only a suggestion and is used by the operating system to
calculate an appropriate buffering mechanism. A value of zero
indicates that the system is to choose the default buffering
scheme.
Return Value:
TRUE - The operation was successful.
FALSE/NULL - The operation failed. Extended error status is available
using GetLastError.
--*/
{
HANDLE ReadPipeHandle, WritePipeHandle;
DWORD dwError;
CHAR PipeNameBuffer[ MAX_PATH ];
//
// Only one valid OpenMode flag - FILE_FLAG_OVERLAPPED
//
if ((dwReadMode | dwWriteMode) & (~FILE_FLAG_OVERLAPPED))
{
SetLastError(ERROR_INVALID_PARAMETER);
return FALSE;
}
//
// Set the default timeout to 120 seconds
//
if (nSize == 0)
{
nSize = 4096;
}
sprintf( PipeNameBuffer,
"\\\\.\\Pipe\\Win32PipesEx.%08x.%08x",
GetCurrentProcessId(),
g_PipeSerialNumber++
);
ReadPipeHandle = CreateNamedPipeA(
PipeNameBuffer,
PIPE_ACCESS_INBOUND | dwReadMode,
PIPE_TYPE_BYTE | PIPE_WAIT,
1, // Number of pipes
nSize, // Out buffer size
nSize, // In buffer size
120 * 1000, // Timeout in ms
lpPipeAttributes
);
if (ReadPipeHandle == INVALID_HANDLE_VALUE)
{
return FALSE;
}
WritePipeHandle = CreateFileA(
PipeNameBuffer,
GENERIC_WRITE,
0, // No sharing
lpPipeAttributes,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL | dwWriteMode,
NULL // Template file
);
if (INVALID_HANDLE_VALUE == WritePipeHandle)
{
dwError = GetLastError();
CloseHandle( ReadPipeHandle );
SetLastError(dwError);
return FALSE;
}
*lpReadPipe = ReadPipeHandle;
*lpWritePipe = WritePipeHandle;
return( TRUE );
}
VOID
fnShell(
PCSTR Args
)
{
//
// If the debugger always ran through stdin/stdout, we
// could just run a shell and wait for it. However, in order
// to handle fDebugOutput, we have to open pipes and manage
// the i/o stream for the shell. Since we need to have that
// code anyway, always use it.
//
//
// handles 0 and 1 are stdin, stdout.
// the third handle on the debugger side is
// the process handle, and the third handle
// on the shell side is stderr, which is a dup
// of stdout.
// The other handle for the debugger is an output event handle
// that is set by the reader thread when output is generated.
//
SHELL_READER_INFO ReaderInfo;
HANDLE HandlesForShell[3] = {0};
HANDLE ReaderThreadHandle = 0;
DWORD ThreadId;
SECURITY_ATTRIBUTES sa;
CHAR Shell[_MAX_PATH];
CHAR Command[2 * _MAX_PATH];
STARTUPINFO si;
PROCESS_INFORMATION pi;
CHAR InputBuffer[_MAX_PATH];
DWORD Bytes;
DWORD BytesWritten;
int i;
C_ASSERT(DIMA(ReaderInfo.IoHandles) == DIMA(HandlesForShell));
if (g_EngOptions & DEBUG_ENGOPT_DISALLOW_SHELL_COMMANDS)
{
ErrOut(".shell has been disabled\n");
return;
}
if (SYSTEM_PROCESSES())
{
ErrOut(".shell: can't create a process while debugging CSRSS.\n");
return;
}
ZeroMemory(&ReaderInfo, sizeof(ReaderInfo));
ZeroMemory(&pi, sizeof(pi));
sa.nLength = sizeof(sa);
sa.lpSecurityDescriptor = NULL;
sa.bInheritHandle = TRUE;
__try
{
//
// Create stdin pipe for ntsd->shell.
// Neither end needs to be overlapped.
//
if ( ! MyCreatePipeEx(
&HandlesForShell[0], // read handle
&ReaderInfo.IoHandles[0], // write handle
&sa, // security
0, // size
0, // read handle overlapped?
0 // write handle overlapped?
))
{
ErrOut(".shell: Unable to create stdin pipe.\n");
__leave;
}
//
// We don't want the shell to inherit our end of the pipe
// so duplicate it to a non-inheritable one.
//
if ( ! DuplicateHandle(
GetCurrentProcess(), // src process
ReaderInfo.IoHandles[0], // src handle
GetCurrentProcess(), // targ process
&ReaderInfo.IoHandles[0], // targ handle
0, // access
FALSE, // inheritable
DUPLICATE_SAME_ACCESS |
DUPLICATE_CLOSE_SOURCE // options
))
{
ErrOut(".shell: Unable to duplicate stdin handle.\n");
__leave;
}
//
// Create stdout shell->ntsd pipe
//
if ( ! MyCreatePipeEx(
&ReaderInfo.IoHandles[1], // read handle
&HandlesForShell[1], // write handle
&sa, // security
0, // size
FILE_FLAG_OVERLAPPED, // read handle overlapped?
0 // write handle overlapped?
))
{
ErrOut(".shell: Unable to create stdout pipe.\n");
__leave;
}
//
// We don't want the shell to inherit our end of the pipe
// so duplicate it to a non-inheritable one.
//
if ( ! DuplicateHandle(
GetCurrentProcess(), // src process
ReaderInfo.IoHandles[1], // src handle
GetCurrentProcess(), // targ process
&ReaderInfo.IoHandles[1], // targ handle
0, // access
FALSE, // inheritable
DUPLICATE_SAME_ACCESS |
DUPLICATE_CLOSE_SOURCE // options
))
{
ErrOut(".shell: Unable to duplicate local stdout handle.\n");
__leave;
}
//
// Duplicate shell's stdout to a new stderr.
//
if ( ! DuplicateHandle(
GetCurrentProcess(), // src process
HandlesForShell[1], // src handle
GetCurrentProcess(), // targ process
&HandlesForShell[2], // targ handle
0, // access
TRUE, // inheritable
DUPLICATE_SAME_ACCESS // options
))
{
ErrOut(".shell: Unable to duplicate stdout handle for stderr.\n");
__leave;
}
//
// Create an event for output monitoring.
//
ReaderInfo.OutEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
if (ReaderInfo.OutEvent == NULL)
{
ErrOut(".shell: Unable to allocate event.\n");
__leave;
}
if (!GetEnvironmentVariable("SHELL", Shell, sizeof(Shell)))
{
if (!GetEnvironmentVariable("ComSpec", Shell, sizeof(Shell)))
{
strcpy(Shell, "cmd.exe");
}
}
// Skip leading whitespace on the command string.
// Some commands, such as "net use", can't handle it.
if (Args != NULL)
{
while (isspace(*Args))
{
Args++;
}
}
if (Args && *Args)
{
//
// If there was a command, use SHELL /c Command
//
sprintf(Command, "%s /c \"%s\"", Shell, Args);
}
else
{
//
// If there was no command, just run the shell
//
sprintf(Command, "%s", Shell);
}
ZeroMemory(&si, sizeof(si));
si.cb = sizeof(si);
si.dwFlags = STARTF_USESTDHANDLES;
si.hStdInput = HandlesForShell[0];
si.hStdOutput = HandlesForShell[1];
si.hStdError = HandlesForShell[2];
si.wShowWindow = SW_SHOW;
//
// Create Child Process
//
if ( ! CreateProcess(
NULL,
Command,
NULL,
NULL,
TRUE,
GetPriorityClass( GetCurrentProcess() ),
NULL,
NULL,
&si,
&pi))
{
if (GetLastError() == ERROR_FILE_NOT_FOUND)
{
dprintf("%s not found\n", Shell);
}
else
{
ErrOut("CreateProcess(%s) failed, error %d.\n",
Command, GetLastError());
}
__leave;
}
ReaderInfo.IoHandles[2] = pi.hProcess;
//
// Start reader thread to copy shell output
//
ReaderThreadHandle = CreateThread(
NULL,
0,
ShellReaderThread,
&ReaderInfo,
0,
&ThreadId
);
ULONG Timeout = 1000;
//
// Feed input to shell; wait for it to exit.
//
while (1)
{
ULONG WaitStatus;
// Give the other process a little time to run.
// This is critical when output is being piped
// across kd as GetInput causes the machine to
// sit in the kernel debugger input routine and
// nobody gets any time to run.
WaitStatus = WaitForSingleObject(ReaderInfo.OutEvent, 100);
if (WaitStatus == WAIT_OBJECT_0)
{
// Reset the timeout since the process seems to
// be active.
Timeout = 1000;
// Some output was produced so let the child keep
// running to keep the output flowing. If this
// was the final output of the process, though,
// go to the last input request.
if (WaitForSingleObject(pi.hProcess, 0) != WAIT_OBJECT_0)
{
continue;
}
}
// We've run out of immediate output, so wait for a
// larger interval to give the process a reasonable
// amount of time to run. Show a message to keep
// users in the loop.
dprintf("<.shell waiting %d second(s) for process>\n",
Timeout / 1000);
FlushCallbacks();
WaitStatus = WaitForSingleObject(ReaderInfo.OutEvent, Timeout);
if (WaitStatus == WAIT_OBJECT_0 &&
WaitForSingleObject(pi.hProcess, 0) != WAIT_OBJECT_0)
{
// Reset the timeout since the process seems to
// be active.
Timeout = 1000;
continue;
}
Bytes = GetInput("<.shell process may need input>",
InputBuffer, sizeof(InputBuffer) - 2);
// The user may not want to wait, so check for
// a magic input string that'll abandon the process.
if (!_strcmpi(InputBuffer, ".shell_quit"))
{
break;
}
//
// see if client is still running
//
if (WaitForSingleObject(pi.hProcess, 0) == WAIT_OBJECT_0)
{
break;
}
//
// GetInput always returns a string without a newline
//
strcat(InputBuffer, "\n");
if (!WriteFile( ReaderInfo.IoHandles[0],
InputBuffer,
strlen(InputBuffer),
&BytesWritten,
NULL
))
{
//
// if the write fails, we're done...
//
break;
}
// The process has some input to chew on so
// increase the amount of time we'll wait for it.
Timeout *= 2;
}
}
__finally
{
//
// Close all of the i/o handles first.
// That will make the reader thread exit if it was running.
//
for (i = 0; i < DIMA(ReaderInfo.IoHandles); i++)
{
if (ReaderInfo.IoHandles[i])
{
CloseHandle(ReaderInfo.IoHandles[i]);
}
if (HandlesForShell[i])
{
CloseHandle(HandlesForShell[i]);
}
}
if (pi.hThread)
{
CloseHandle(pi.hThread);
}
if (ReaderThreadHandle)
{
WaitForSingleObject(ReaderThreadHandle, INFINITE);
CloseHandle(ReaderThreadHandle);
}
// Close this handle after the thread has exited
// to avoid it using a bad handle.
CloseHandle(ReaderInfo.OutEvent);
}
}
VOID
fnBangCmd(
DebugClient* Client,
PSTR ArgString,
PSTR *ArgNext,
BOOL BuiltInOnly
)
{
PCHAR pc;
PCHAR pc1;
PCHAR ModName;
PCHAR FnName;
CHAR String[MAX_COMMAND];
EXTDLL *Ext;
CHAR Save;
PSTR FnArgs;
//
// Shell escape always consumes the entire string.
//
if (*ArgString == '!')
{
if (ArgNext)
{
*ArgNext = ArgString + strlen(ArgString);
}
fnShell(ArgString + 1);
return;
}
// Copy the command into a local buffer so that we
// can modify it.
strcpy(String, ArgString);
//
// Syntax is [path-without-spaces]module.function argument-string.
//
pc = String;
while ((*pc == ' ') || (*pc == '\t'))
{
pc++;
}
ModName = pc;
FnName = NULL;
while ((*pc != ' ') && (*pc != '\t') && (*pc != '\0') &&
(*pc != ';') && (*pc != '"'))
{
pc++;
}
pc1 = pc;
if (*pc != '\000' && *pc != ';' && *pc != '"')
{
*pc = '\000';
pc++; // now pc points to any args
}
while (*pc1 != '.' && pc1 != ModName)
{
pc1--;
}
if (*pc1 == '.' && !BuiltInOnly)
{
*pc1 = '\0';
pc1++;
FnName = pc1;
}
else
{
FnName = ModName;
ModName = NULL;
}
if ((FnArgs = BufferStringValue(&pc, STRV_ESCAPED_CHARACTERS,
&Save)) == NULL)
{
ErrOut("Syntax error in extension string\n");
return;
}
//
// point to next command:
//
if (ArgNext)
{
*ArgNext = ArgString + (pc - String);
}
//
// ModName -> Name of module
// FnName -> Name of command to process
// FnArgs -> argument to command
//
if (ModName != NULL)
{
Ext = AddExtensionDll(ModName, TRUE, NULL);
if (Ext == NULL)
{
return;
}
}
else
{
Ext = g_ExtDlls;
}
if (!_stricmp(FnName, "load"))
{
if (ModName == NULL)
{
Ext = AddExtensionDll(FnArgs, TRUE, NULL);
if (Ext == NULL)
{
return;
}
}
LoadExtensionDll(Ext);
return;
}
else if (!_stricmp(FnName, "setdll"))
{
if (ModName == NULL)
{
Ext = AddExtensionDll(FnArgs, TRUE, 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);
}
if (Ext == NULL)
{
return;
}
}
if (Ext != NULL)
{
UnloadExtensionDll(Ext);
}
return;
}
else if (!_stricmp(FnName, "unloadall"))
{
g_EngNotify++;
while (g_ExtDlls != NULL)
{
UnloadExtensionDll(g_ExtDlls);
}
g_EngNotify--;
NotifyChangeEngineState(DEBUG_CES_EXTENSIONS, 0, TRUE);
return;
}
if (ModName == NULL)
{
// Handle built-in commands.
if (!_stricmp(FnName, "chain"))
{
OutputExtensions(Client, FALSE);
return;
}
else if (!_stricmp(FnName, "lines"))
{
ParseLines(FnArgs);
return;
}
else if (!_stricmp(FnName, "noversion"))
{
dprintf("Extension DLL system version checking is disabled\n");
g_EnvDbgOptions |= OPTION_NOVERSIONCHECK;
return;
}
else if (!_stricmp(FnName, "reload"))
{
g_Target->Reload(FnArgs);
ClearStoredTypes(0);
return;
}
else if (!_stricmp(FnName, "srcpath") ||
!_stricmp(FnName, "srcpath+"))
{
//
// .srcpath needs complete command tail not
// stopping at semicolons
//
FnArgs = ArgString + (FnArgs - String);
if (ArgNext)
{
*ArgNext = FnArgs + strlen(FnArgs);
}
ChangeSrcPath(FnArgs, FnName[7] == '+');
return;
}
else if (!_stricmp(FnName, "sympath") ||
!_stricmp(FnName, "sympath+"))
{
//
// .sympath needs complete command tail not
// stopping at semicolons
//
FnArgs = ArgString + (FnArgs - String);
if (ArgNext)
{
*ArgNext = FnArgs + strlen(FnArgs);
}
bangSymPath(FnArgs, FnName[7] == '+', NULL, 0);
return;
}
else if (!_stricmp(FnName, "exepath") ||
!_stricmp(FnName, "exepath+"))
{
//
// .exepath needs complete command tail not
// stopping at semicolons
//
FnArgs = ArgString + (FnArgs - String);
if (ArgNext)
{
*ArgNext = FnArgs + strlen(FnArgs);
}
ChangeExePath(FnArgs, FnName[7] == '+');
return;
}
else if (!_stricmp(FnName, "netsyms"))
{
if (_stricmp(FnArgs, "1") == 0 ||
_stricmp(FnArgs, "true") == 0 ||
_stricmp(FnArgs, "yes") == 0)
{
g_EngOptions |= DEBUG_ENGOPT_ALLOW_NETWORK_PATHS;
g_EngOptions &= ~DEBUG_ENGOPT_DISALLOW_NETWORK_PATHS;
}
else if (_stricmp(FnArgs, "0") == 0 ||
_stricmp(FnArgs, "false") == 0 ||
_stricmp(FnArgs, "no") == 0)
{
g_EngOptions |= DEBUG_ENGOPT_DISALLOW_NETWORK_PATHS;
g_EngOptions &= ~DEBUG_ENGOPT_ALLOW_NETWORK_PATHS;
}
if (g_EngOptions & DEBUG_ENGOPT_ALLOW_NETWORK_PATHS)
{
dprintf("netsyms = yes\n");
}
else if (g_EngOptions & DEBUG_ENGOPT_DISALLOW_NETWORK_PATHS)
{
dprintf("netsyms = no\n");
}
else
{
dprintf("netsyms = don't care\n");
}
return;
}
else if (!_stricmp(FnName, "context"))
{
if (*FnArgs != 0)
{
ULONG64 Base = ExtGetExpression(FnArgs);
ULONG NextIdx;
if (g_Machine->SetPageDirectory(PAGE_DIR_USER, Base,
&NextIdx) != S_OK)
{
WarnOut("WARNING: Unable to reset page directory base\n");
}
// Flush the cache as anything we read from user mode is
// no longer valid
g_VirtualCache.Empty();
if (Base && !g_VirtualCache.m_ForceDecodePTEs)
{
WarnOut("WARNING: "
".cache forcedecodeptes is not enabled\n");
}
}
else
{
dprintf("User-mode page directory base is %I64x\n",
g_Machine->m_PageDirectories[PAGE_DIR_USER]);
}
return;
}
else if (!_stricmp(FnName, "symfix"))
{
bangSymPath("symsrv*symsrv.dll*\\\\symbols\\symbols",
FALSE, NULL, 0);
return;
}
}
if (BuiltInOnly)
{
error(SYNTAX);
}
HRESULT ExtStatus;
CallAnyExtension(Client, Ext, FnName, FnArgs, ModName != NULL, TRUE,
&ExtStatus);
}
BOOL
bangSymPath(
IN PCSTR args,
IN BOOL Append,
OUT PSTR string,
IN ULONG len
)
{
PPROCESS_INFO pProcess;
__try
{
if (args != NULL)
{
while (*args == ' ' || *args == '\t')
{
args++;
}
}
if ( args != NULL && *args )
{
if (ChangePath(&g_SymbolSearchPath, args, Append,
DEBUG_CSS_PATHS) != S_OK)
{
return FALSE;
}
pProcess = g_ProcessHead;
while (pProcess)
{
SymSetSearchPath( pProcess->Handle, g_SymbolSearchPath );
pProcess = pProcess->Next;
}
}
if (string)
{
strncpy( string, g_SymbolSearchPath, len );
string[len - 1] = 0;
}
else
{
dprintf( "Symbol search path is: %s\n", g_SymbolSearchPath );
CheckPath(g_SymbolSearchPath);
}
}
__except(EXCEPTION_EXECUTE_HANDLER)
{
return FALSE;
}
return TRUE;
}
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(
VOID
)
{
LONG_PTR Wow64Info;
NTSTATUS Status;
EXTDLL * Extension;
// Win9x doesn't support wx86.
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(g_CurrentProcess->Handle,
ProcessWow64Information,
&Wow64Info,
sizeof(Wow64Info),
NULL
);
if (NT_SUCCESS(Status) && Wow64Info)
{
Extension = AddExtensionDll("wow64exts", FALSE, NULL);
//
// Force load it so we get the entry point the debugger needs
//
LoadExtensionDll(Extension);
}
}