|
|
/*++
Copyright (c) 1993-2000 Microsoft Corporation
Module Name:
exts.c
Abstract:
This file contains the generic routines and initialization code for the kernel debugger extensions dll.
Environment:
User Mode
--*/
#include "precomp.h"
#pragma hdrstop
#include <ntverp.h>
//
// Valid for the lifetime of the debug session.
//
WINDBG_EXTENSION_APIS ExtensionApis; ULONG TargetMachine; BOOL Connected; ULONG g_TargetClass;
//
// Valid only during an extension API call
//
PDEBUG_ADVANCED g_ExtAdvanced; PDEBUG_CLIENT g_ExtClient; PDEBUG_CONTROL g_ExtControl; PDEBUG_DATA_SPACES g_ExtData; PDEBUG_REGISTERS g_ExtRegisters; PDEBUG_SYMBOLS2 g_ExtSymbols; PDEBUG_SYSTEM_OBJECTS g_ExtSystem;
#define SKIP_WSPACE(s) while (*s && (*s == ' ' || *s == '\t')) {++s;}
// Queries for all debugger interfaces.
extern "C" HRESULT ExtQuery(PDEBUG_CLIENT Client) { HRESULT Status; if ((Status = Client->QueryInterface(__uuidof(IDebugAdvanced), (void **)&g_ExtAdvanced)) != S_OK) { goto Fail; } if ((Status = Client->QueryInterface(__uuidof(IDebugControl), (void **)&g_ExtControl)) != S_OK) { goto Fail; } if ((Status = Client->QueryInterface(__uuidof(IDebugDataSpaces), (void **)&g_ExtData)) != S_OK) { goto Fail; } if ((Status = Client->QueryInterface(__uuidof(IDebugRegisters), (void **)&g_ExtRegisters)) != S_OK) { goto Fail; } if ((Status = Client->QueryInterface(__uuidof(IDebugSymbols2), (void **)&g_ExtSymbols)) != S_OK) { goto Fail; } if ((Status = Client->QueryInterface(__uuidof(IDebugSystemObjects), (void **)&g_ExtSystem)) != S_OK) { goto Fail; }
g_ExtClient = Client; return S_OK;
Fail: ExtRelease(); return Status; }
// Cleans up all debugger interfaces.
void ExtRelease(void) { g_ExtClient = NULL; EXT_RELEASE(g_ExtAdvanced); EXT_RELEASE(g_ExtControl); EXT_RELEASE(g_ExtData); EXT_RELEASE(g_ExtRegisters); EXT_RELEASE(g_ExtSymbols); EXT_RELEASE(g_ExtSystem); }
// Normal output.
void __cdecl ExtOut(PCSTR Format, ...) { va_list Args; va_start(Args, Format); g_ExtControl->OutputVaList(DEBUG_OUTPUT_NORMAL, Format, Args); va_end(Args); }
// Error output.
void __cdecl ExtErr(PCSTR Format, ...) { va_list Args; va_start(Args, Format); g_ExtControl->OutputVaList(DEBUG_OUTPUT_ERROR, Format, Args); va_end(Args); }
// Warning output.
void __cdecl ExtWarn(PCSTR Format, ...) { va_list Args; va_start(Args, Format); g_ExtControl->OutputVaList(DEBUG_OUTPUT_WARNING, Format, Args); va_end(Args); }
// Verbose output.
void __cdecl ExtVerb(PCSTR Format, ...) { va_list Args; va_start(Args, Format); g_ExtControl->OutputVaList(DEBUG_OUTPUT_VERBOSE, Format, Args); va_end(Args); }
extern "C" HRESULT CALLBACK DebugExtensionInitialize(PULONG Version, PULONG Flags) { IDebugClient *DebugClient; PDEBUG_CONTROL DebugControl; HRESULT Hr;
*Version = DEBUG_EXTENSION_VERSION(1, 0); *Flags = 0;
if ((Hr = DebugCreate(__uuidof(IDebugClient), (void **)&DebugClient)) != S_OK) { return Hr; } if ((Hr = DebugClient->QueryInterface(__uuidof(IDebugControl), (void **)&DebugControl)) != S_OK) { return Hr; }
ExtensionApis.nSize = sizeof (ExtensionApis); if ((Hr = DebugControl->GetWindbgExtensionApis64(&ExtensionApis)) != S_OK) { return Hr; }
DebugControl->Release(); DebugClient->Release(); return S_OK; }
extern "C" void CALLBACK DebugExtensionNotify(ULONG Notify, ULONG64 Argument) { //
// The first time we actually connect to a target, get the page size
//
if ((Notify == DEBUG_NOTIFY_SESSION_ACCESSIBLE) && (!Connected)) { IDebugClient *DebugClient; PDEBUG_DATA_SPACES DebugDataSpaces; PDEBUG_CONTROL DebugControl; HRESULT Hr; ULONG64 Page;
if ((Hr = DebugCreate(__uuidof(IDebugClient), (void **)&DebugClient)) == S_OK) { //
// Get the page size and PAE enable flag
//
if ((Hr = DebugClient->QueryInterface(__uuidof(IDebugDataSpaces), (void **)&DebugDataSpaces)) == S_OK) { if ((Hr = DebugDataSpaces->ReadDebuggerData( DEBUG_DATA_MmPageSize, &Page, sizeof(Page), NULL)) == S_OK) { PageSize = (ULONG)(ULONG_PTR)Page; }
DebugDataSpaces->Release(); } //
// Get the architecture type.
//
if ((Hr = DebugClient->QueryInterface(__uuidof(IDebugControl), (void **)&DebugControl)) == S_OK) { if ((Hr = DebugControl->GetActualProcessorType( &TargetMachine)) == S_OK) { Connected = TRUE; } ULONG Qualifier; if ((Hr = DebugControl->GetDebuggeeType(&g_TargetClass, &Qualifier)) == S_OK) { }
DebugControl->Release(); }
DebugClient->Release(); } }
if (Notify == DEBUG_NOTIFY_SESSION_INACTIVE) { Connected = FALSE; PageSize = 0; TargetMachine = 0; }
return; }
extern "C" void CALLBACK DebugExtensionUninitialize(void) { return; }
DllInit( HANDLE hModule, DWORD dwReason, DWORD dwReserved ) { switch (dwReason) { case DLL_THREAD_ATTACH: break;
case DLL_THREAD_DETACH: break;
case DLL_PROCESS_DETACH: break;
case DLL_PROCESS_ATTACH: break; }
return TRUE; }
VOID CxrHelp() { dprintf("\n"); dprintf(" !cxr has been replaced with the new built-in debugger command .cxr\n"); dprintf(" !exr, !trap and !tss (both Kernelmode only) too are built-in . commands now\n"); dprintf(" There is also a new \".thread\" command.\n"); dprintf("\n"); dprintf(" These new commands no longer require symbols to work correctly.\n"); dprintf("\n"); dprintf(" Another change that comes with these new commands is that they actually\n"); dprintf(" change the internal state of the debugger engine \"permanently\" (until\n"); dprintf(" reverted). Any other debugger or extension command issued after the \n"); dprintf(" \".cxr\", \".trap\" or \".thread\" command will be executed with the new context.\n"); dprintf("\n"); dprintf(" For example, commands such as stack walk (\"k\", \"kb\", \"kv\" ), \"r\" and \"dv\"\n"); dprintf(" (show local variables) will all work based off the new context that was\n"); dprintf(" supplied by \".cxr\", \".trap\" or \".thread\".\n"); dprintf("\n"); dprintf(" \".cxr\", \".trap\" and \".thread\" also apply to WinDBG:\n"); dprintf(" using \".cxr\" , \".trap\" and \".thread\" will automatically show you the\n"); dprintf(" new stack in the WinDBG stack window and allow you to click on a frame and\n"); dprintf(" see local variables and source code (if source is available).\n"); dprintf("\n"); dprintf(" \".cxr\", \".trap\" or \".thread\" with no parameters will give you back the\n"); dprintf(" default context that was in effect before the command was executed.\n"); dprintf("\n"); dprintf(" For example, to exactly duplicate \n"); dprintf("\n"); dprintf(" !cxr <foo> !trap <foo>\n"); dprintf(" !kb !kb\n"); dprintf("\n"); dprintf(" you would now use\n"); dprintf("\n"); dprintf(" .cxr <foo> .trap <foo>\n"); dprintf(" kb kb\n"); dprintf(" .cxr .trap\n"); dprintf("\n"); }
DECLARE_API ( cxr ) { CxrHelp(); return S_OK; }
DECLARE_API ( exr ) { CHAR Cmd[400];
dprintf("*** !exr obsolete: Use '.exr %s'\n", args);
sprintf(Cmd, ".exr %s", args); if (Client && (ExtQuery(Client) == S_OK)) { g_ExtControl->Execute(DEBUG_OUTCTL_ALL_CLIENTS | DEBUG_OUTCTL_OVERRIDE_MASK | DEBUG_OUTCTL_NOT_LOGGED, Cmd, DEBUG_EXECUTE_DEFAULT );
ExtRelease(); return S_OK; } return E_FAIL;
}
HRESULT PrintString( BOOL Unicode, PDEBUG_CLIENT Client, LPCSTR args ) { ULONG64 AddrString; ULONG64 Displacement; STRING32 String; UNICODE_STRING UnicodeString; ULONG64 AddrBuffer; CHAR Symbol[1024]; LPSTR StringData; HRESULT hResult; BOOL b;
AddrString = GetExpression(args); if (!AddrString) { return E_FAIL; }
//
// Get the symbolic name of the string
//
GetSymbol(AddrString, Symbol, &Displacement);
//
// Read the string from the debuggees address space into our
// own.
b = ReadMemory(AddrString, &String, sizeof(String), NULL);
if ( !b ) { return E_FAIL; }
INIT_API();
if (IsPtr64()) { hResult = g_ExtData->ReadPointersVirtual(1, AddrString + FIELD_OFFSET(STRING64, Buffer), &AddrBuffer); } else { hResult = g_ExtData->ReadPointersVirtual(1, AddrString + FIELD_OFFSET(STRING32, Buffer), &AddrBuffer); }
EXIT_API();
if (hResult != S_OK) { return E_FAIL; }
StringData = (LPSTR) LocalAlloc(LMEM_ZEROINIT, String.Length + sizeof(UNICODE_NULL));
if (!StringData) { return E_FAIL; }
dprintf("String(%d,%d)", String.Length, String.MaximumLength); if (Symbol[0]) { dprintf(" %s+%p", Symbol, Displacement); }
b = ReadMemory(AddrBuffer, StringData, String.Length, NULL);
if ( b ) { if (Unicode) { ANSI_STRING AnsiString;
UnicodeString.Buffer = (PWSTR)StringData; UnicodeString.Length = String.Length; UnicodeString.MaximumLength = String.Length+sizeof(UNICODE_NULL);
RtlUnicodeStringToAnsiString(&AnsiString, &UnicodeString,TRUE);
dprintf(" at %p: %s\n", AddrString, AnsiString.Buffer);
RtlFreeAnsiString(&AnsiString); } else { dprintf(" at %p: %s\n", AddrString, StringData); }
LocalFree(StringData); return S_OK; } else { LocalFree(StringData); return E_FAIL; } }
DECLARE_API( str )
/*++
Routine Description:
This function is called to format and dump counted (ansi) string.
Arguments:
args - Address
Return Value:
None.
--*/ { return PrintString(FALSE, Client, args); }
DECLARE_API( ustr )
/*++
Routine Description:
This function is called to format and dump counted (unicode) string.
Arguments:
args - Address
Return Value:
None.
--*/
{ return PrintString(TRUE, Client, args); }
DECLARE_API( obja )
/*++
Routine Description:
This function is called to format and dump an object attributes structure.
Arguments:
args - Address
Return Value:
None.
--*/
{ ULONG64 AddrObja; ULONG64 Displacement; ULONG64 AddrString; STRING32 String; ULONG64 StrAddr = NULL; CHAR Symbol[1024]; LPSTR StringData; BOOL b; ULONG Attr; HRESULT hResult; ULONG ObjectNameOffset; ULONG AttrOffset; ULONG StringOffset;
if (IsPtr64()) { ObjectNameOffset = FIELD_OFFSET(OBJECT_ATTRIBUTES64, ObjectName); AttrOffset = FIELD_OFFSET(OBJECT_ATTRIBUTES64, Attributes); StringOffset = FIELD_OFFSET(STRING64, Buffer); } else { ObjectNameOffset = FIELD_OFFSET(OBJECT_ATTRIBUTES32, ObjectName); AttrOffset = FIELD_OFFSET(OBJECT_ATTRIBUTES32, Attributes); StringOffset = FIELD_OFFSET(STRING32, Buffer); }
AddrObja = GetExpression(args); if (!AddrObja) { return E_FAIL; }
//
// Get the symbolic name of the Obja
//
GetSymbol(AddrObja, Symbol, &Displacement);
dprintf("Obja %s+%p at %p:\n", Symbol, Displacement, AddrObja);
INIT_API();
hResult = g_ExtData->ReadPointersVirtual(1, AddrObja + ObjectNameOffset, &AddrString);
if (hResult != S_OK) { return E_FAIL; }
if (AddrString) { b = ReadMemory(AddrString, &String, sizeof(String), NULL);
hResult = g_ExtData->ReadPointersVirtual(1, AddrString + StringOffset, &StrAddr); }
EXIT_API();
if (StrAddr) { StringData = (LPSTR)LocalAlloc(LMEM_ZEROINIT, String.Length+sizeof(UNICODE_NULL));
if (StringData) {
b = ReadMemory(StrAddr, StringData, String.Length, NULL);
if (b) { dprintf("\tName is %ws\n", StringData); }
LocalFree(StringData); }
}
b = ReadMemory(AddrObja + AttrOffset, &Attr, sizeof(Attr), NULL);
if (!b) { return E_FAIL; }
if (Attr & OBJ_INHERIT ) { dprintf("\tOBJ_INHERIT\n"); } if (Attr & OBJ_PERMANENT ) { dprintf("\tOBJ_PERMANENT\n"); } if (Attr & OBJ_EXCLUSIVE ) { dprintf("\tOBJ_EXCLUSIVE\n"); } if (Attr & OBJ_CASE_INSENSITIVE ) { dprintf("\tOBJ_CASE_INSENSITIVE\n"); } if (Attr & OBJ_OPENIF ) { dprintf("\tOBJ_OPENIF\n"); }
return S_OK; }
typedef struct _LIST_TYPE_PARAMS { PCHAR Command; PCHAR CommandArgs; ULONG FieldOffset; ULONG nElement; } LIST_TYPE_PARAMS, *PLIST_TYPE_PARAMS;
ULONG ListCallback( PFIELD_INFO Field, PVOID Context ) { CHAR Execute[MAX_PATH]; PCHAR Buffer; PLIST_TYPE_PARAMS pListParams = (PLIST_TYPE_PARAMS) Context;
// Execute command
sprintf(Execute, "%s %I64lx %s", pListParams->Command, Field->address,// - pListParams->FieldOffset,
pListParams->CommandArgs); g_ExtControl->Execute(DEBUG_OUTCTL_AMBIENT,Execute,DEBUG_EXECUTE_DEFAULT); dprintf("\n");
if (CheckControlC()) { return TRUE; }
return FALSE; }
/*
!list [-type <Type>] [-x "<Command>"] <address> This dumps out a list starting at ginve <address> <Type> If types is specified, it'll list that particular type accoring to the given field. <Command> If specified it'll execute the command for every list element. */ DECLARE_API ( list ) { CHAR Command[MAX_PATH], CmdArgs[MAX_PATH]; CHAR Type[MAX_PATH]; CHAR Field[MAX_PATH]; ULONG64 Start; ULONG i, Offset, Commandlen; ULONG64 Next, Current; LIST_TYPE_PARAMS ListParams;
ZeroMemory(Type, sizeof(Type)); ZeroMemory(Field, sizeof(Field)); ZeroMemory(Command, sizeof(Command)); CmdArgs[0] = 0; Start = 0; while (args && *args) { SKIP_WSPACE(args);
if (*args == '-' || *args == '/') { ++args; if (*args == 't') { PCHAR Dot;
args++; SKIP_WSPACE(args);
Dot = strchr(args, '.'); if (Dot) { strncpy(Type, args, (ULONG) (ULONG_PTR) (Dot-args)); Dot++; i=0; while (*Dot && (i < MAX_PATH-1) && (*Dot != ' ') && (*Dot != '\t')) Field[i++] = *Dot++; args = Dot; } } else if (*args == 'x') { ++args; SKIP_WSPACE(args); i=0; if (*args == '"') { ++args; while (*args && (i < MAX_PATH-1) && (*args != '"')) Command[i++] = *args++; ++args; } else { dprintf("Invalid command specification. See !list -h\n"); return E_INVALIDARG; } } else if (*args == 'a') { ++args; SKIP_WSPACE(args); i=0; if (*args == '"') { ++args; while (*args && (i < MAX_PATH-1) && (*args != '"')) CmdArgs[i++] = *args++; ++args; CmdArgs[i] = 0; } else { dprintf("Invalid command argument specification. See !list -h\n"); return E_INVALIDARG; } } else if (*args == 'h' || *args == '?') { dprintf("Usage: !list -t [mod!]TYPE.Field <Start-Address>\n" " -x \"Command-for-each-element\"\n" " -a \"Command-arguments\"\n" " -h\n" "Command after -x is executed for each list element. Its first argument is\n" "list-head address and remaining arguments are specified after -a\n" "eg. !list -t MYTYPE.l.Flink -x \"dd\" -a \"l2\" 0x6bc00\n" " dumps first 2 dwords in list of MYTYPE at 0x6bc00\n\n" ); return S_OK; } else { dprintf("Invalid flag -%c in !list\n", *args); return E_INVALIDARG; } } else { if (!GetExpressionEx(args, &Start, &args)) { dprintf("Invalid expression in %s\n", args); return E_FAIL; } } }
Offset = 0; if (!Command[0]) { strcat(Command, "dp"); }
if (Type[0] && Field[0]) { if (GetFieldOffset(Type, Field, &Offset)) { dprintf("GetFieldOffset failed for %s.%s\n", Type, Field); return E_FAIL; } ListParams.Command = Command; ListParams.CommandArgs = CmdArgs; ListParams.FieldOffset = Offset; ListParams.nElement = 0;
INIT_API(); ListType(Type, Start, FALSE, Field, (PVOID) &ListParams, &ListCallback );
EXIT_API(); return S_OK;
} Current = Start; Next = 0; INIT_API();
while (Next != Start) { CHAR Execute[MAX_PATH]; PCHAR Buffer;
// Execute command
sprintf(Execute, "%s %I64lx %s", Command, Current, CmdArgs); g_ExtControl->Execute(DEBUG_OUTCTL_AMBIENT,Execute,DEBUG_EXECUTE_DEFAULT); dprintf("\n"); if (!ReadPointer(Current + Offset, &Next)) { dprintf("Cannot read next element at %p\n", Current + Offset); break; } if (!Next) { break; } Next -= Offset; Current = Next; if (CheckControlC()) { break; } }
EXIT_API();
return S_OK; }
BOOL AnalyzeExceptionPointer( ULONG64 ExcepPtr ) { ULONG64 Exr, Cxr; ULONG PtrSize= IsPtr64() ? 8 : 4;
if (!ReadPointer(ExcepPtr, &Exr) || !ReadPointer(ExcepPtr + PtrSize, &Cxr)) { dprintf("Unable to read exception poointers at %p\n", ExcepPtr); return FALSE; }
CHAR Cmd[100];
sprintf(Cmd, ".exr %I64lx", Exr); g_ExtControl->Execute(DEBUG_OUTCTL_ALL_CLIENTS | DEBUG_OUTCTL_OVERRIDE_MASK, Cmd, DEBUG_EXECUTE_DEFAULT); sprintf(Cmd, ".cxr %I64lx", Cxr); g_ExtControl->Execute(DEBUG_OUTCTL_ALL_CLIENTS | DEBUG_OUTCTL_OVERRIDE_MASK, Cmd, DEBUG_EXECUTE_DEFAULT); g_ExtControl->Execute(DEBUG_OUTCTL_ALL_CLIENTS | DEBUG_OUTCTL_OVERRIDE_MASK, "kb", DEBUG_EXECUTE_DEFAULT); g_ExtControl->Execute(DEBUG_OUTCTL_ALL_CLIENTS | DEBUG_OUTCTL_OVERRIDE_MASK, ".cxr", DEBUG_EXECUTE_DEFAULT); return TRUE; }
BOOL IsFunctionAddr( ULONG64 IP, PSTR FuncName ) // Check if IP is in the function FuncName
{ CHAR Buffer[MAX_PATH], *scan, *FnIP; ULONG64 Disp;
GetSymbol(IP, Buffer, &Disp);
if (scan = strchr(Buffer, '!')) { FnIP = scan+1; while (*FnIP == '_') ++FnIP; } else { FnIP = &Buffer[0]; }
return !strncmp(FnIP, FuncName, strlen(FuncName)); }
BOOL GetExceptionPointerFromStack( PULONG64 pExcePtr ) { #define MAX_STACK_FRAMES 50
DEBUG_STACK_FRAME stk[MAX_STACK_FRAMES], *Frame; ULONG nFrames;
if (g_ExtControl->GetStackTrace(0, 0, 0, stk, MAX_STACK_FRAMES, &nFrames ) == S_OK) { for (unsigned int i=0; i<nFrames; ++i) { if (IsFunctionAddr(stk[i].InstructionOffset, "UnhandledExceptionFilter")) { *pExcePtr = stk[i].Params[0]; return TRUE; } } } return FALSE; }
/*****************************************************************************************
* * Do analysis for a given exception pointer * ******************************************************************************************/
DECLARE_API ( analyzeexcep ) { ULONG64 ExcepPtr=0; INIT_API();
GetExpressionEx(args, &ExcepPtr, &args);
if (!ExcepPtr) { GetExceptionPointerFromStack(&ExcepPtr); } if (ExcepPtr) { AnalyzeExceptionPointer(ExcepPtr); } else { dprintf("Cannot get exception pointer\n"); } EXIT_API(); return S_OK; }
|