//---------------------------------------------------------------------------- // // Extension DLL support. // // Copyright (C) Microsoft Corporation, 1997-2002. // //---------------------------------------------------------------------------- #include "ntsdp.hpp" #include /* * _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; iProcessor = (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: "\\" is an absolute path "{char}:\" 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(" \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(" \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); } }