3102 lines
83 KiB
C++
3102 lines
83 KiB
C++
//----------------------------------------------------------------------------
|
|
//
|
|
// Symbol-handling routines.
|
|
//
|
|
// Copyright (C) Microsoft Corporation, 1997-2001.
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
#include "ntsdp.hpp"
|
|
|
|
#include <stddef.h>
|
|
#include <cvconst.h>
|
|
#include <common.ver>
|
|
|
|
typedef struct _EXAMINE_INFO
|
|
{
|
|
BOOL Verbose;
|
|
} EXAMINE_INFO, *PEXAMINE_INFO;
|
|
|
|
LPSTR g_SymbolSearchPath;
|
|
LPSTR g_ExecutableImageSearchPath;
|
|
|
|
// Symbol options that require symbol reloading to take effect.
|
|
#define RELOAD_SYM_OPTIONS \
|
|
(SYMOPT_UNDNAME | SYMOPT_NO_CPP | SYMOPT_DEFERRED_LOADS | \
|
|
SYMOPT_LOAD_LINES | SYMOPT_IGNORE_CVREC | SYMOPT_LOAD_ANYTHING | \
|
|
SYMOPT_EXACT_SYMBOLS)
|
|
|
|
ULONG g_SymOptions = SYMOPT_CASE_INSENSITIVE | SYMOPT_UNDNAME |
|
|
SYMOPT_NO_CPP | SYMOPT_OMAP_FIND_NEAREST |
|
|
SYMOPT_DEFERRED_LOADS;
|
|
|
|
CHAR g_SymBuffer[SYM_BUFFER_SIZE];
|
|
CHAR g_SymStartBuffer[SYM_BUFFER_SIZE];
|
|
PIMAGEHLP_SYMBOL64 g_Sym = (PIMAGEHLP_SYMBOL64) g_SymBuffer;
|
|
PIMAGEHLP_SYMBOL64 g_SymStart = (PIMAGEHLP_SYMBOL64) g_SymStartBuffer;
|
|
|
|
ULONG g_NumUnloadedModules;
|
|
|
|
PSTR g_DmtNameDescs[DMT_NAME_COUNT] =
|
|
{
|
|
"Loaded symbol image file", "Symbol file", "Mapped memory image file",
|
|
"Image path",
|
|
};
|
|
|
|
DEBUG_SCOPE g_ScopeBuffer;
|
|
|
|
void
|
|
RefreshAllModules(void)
|
|
{
|
|
PPROCESS_INFO Process;
|
|
|
|
// Force all loaded symbols to be unloaded so that symbols will
|
|
// be reloaded with any updated settings.
|
|
for (Process = g_ProcessHead;
|
|
Process != NULL;
|
|
Process = Process->Next)
|
|
{
|
|
PDEBUG_IMAGE_INFO Image;
|
|
|
|
for (Image = Process->ImageHead;
|
|
Image != NULL;
|
|
Image = Image->Next)
|
|
{
|
|
SymUnloadModule64(Process->Handle, Image->BaseOfImage);
|
|
ClearStoredTypes(Image->BaseOfImage);
|
|
if (!SymLoadModule64(Process->Handle,
|
|
Image->File,
|
|
PrepareImagePath(Image->ImagePath),
|
|
Image->ModuleName,
|
|
Image->BaseOfImage,
|
|
Image->SizeOfImage))
|
|
{
|
|
ErrOut("Unable to reload %s\n", Image->ModuleName);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void
|
|
SetSymOptions(ULONG Options)
|
|
{
|
|
ULONG OldOptions = g_SymOptions;
|
|
|
|
g_SymOptions = Options;
|
|
SymSetOptions(g_SymOptions);
|
|
NotifyChangeSymbolState(DEBUG_CSS_SYMBOL_OPTIONS, g_SymOptions, NULL);
|
|
|
|
if ((OldOptions ^ g_SymOptions) & RELOAD_SYM_OPTIONS)
|
|
{
|
|
RefreshAllModules();
|
|
}
|
|
}
|
|
|
|
BOOL
|
|
IsImageMachineType64(DWORD MachineType)
|
|
{
|
|
switch (MachineType)
|
|
{
|
|
case IMAGE_FILE_MACHINE_AXP64:
|
|
case IMAGE_FILE_MACHINE_IA64:
|
|
case IMAGE_FILE_MACHINE_AMD64:
|
|
return TRUE;
|
|
default:
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
ULONG64
|
|
GetRegValIA64(
|
|
PCROSS_PLATFORM_CONTEXT Context,
|
|
PDEBUG_STACK_FRAME Frame,
|
|
ULONG RegID
|
|
)
|
|
{
|
|
|
|
ULONGLONG Registers[96+2];
|
|
ULONGLONG RegisterHome = Frame->FrameOffset;
|
|
ULONG RegisterCount;
|
|
ULONG RegisterNumber;
|
|
ULONG ReadLength;
|
|
ULONG i;
|
|
|
|
if (Frame->FrameNumber = 0)
|
|
{
|
|
RegisterCount = (ULONG) Context->IA64Context.StIFS & 0x7f;
|
|
}
|
|
else
|
|
{
|
|
RegisterCount = 96;
|
|
}
|
|
|
|
// Sanity.
|
|
|
|
if (RegisterCount > 96)
|
|
{
|
|
return g_Machine->GetReg64(RegID);
|
|
}
|
|
|
|
if (RegisterHome & 3)
|
|
{
|
|
return g_Machine->GetReg64(RegID);
|
|
}
|
|
|
|
if ((RegID >= INTR32) && (RegID < INTR32 + RegisterCount))
|
|
{
|
|
//
|
|
// Read only what we have to
|
|
//
|
|
RegisterCount = RegID - INTR32 + 1;
|
|
|
|
//
|
|
// Calculate the number of registers to read from the
|
|
// RSE stack. For every 63 registers there will be at
|
|
// at least one NaT collection register, depending on
|
|
// where we start, there may be another one.
|
|
//
|
|
// First, starting at the current BSP, if we cross a 64 (0x40)
|
|
// boundry, then we have an extra.
|
|
//
|
|
|
|
ReadLength = (((((ULONG)Frame->FrameOffset) >> 3) & 0x1f) +
|
|
RegisterCount) >> 6;
|
|
|
|
//
|
|
// Add 1 for every 63 registers.
|
|
//
|
|
|
|
ReadLength = (RegisterCount / 63) + RegisterCount;
|
|
ReadLength *= sizeof(ULONGLONG);
|
|
|
|
//
|
|
// Read the registers for this frame.
|
|
//
|
|
|
|
if (!SwReadMemory(g_CurrentProcess->Handle,
|
|
RegisterHome,
|
|
Registers,
|
|
ReadLength,
|
|
&i))
|
|
{
|
|
//
|
|
// This shouldn't have happened.
|
|
//
|
|
return g_Machine->GetReg64(RegID);
|
|
}
|
|
|
|
|
|
return Registers[RegID - INTR32];
|
|
}
|
|
else
|
|
{
|
|
return g_Machine->GetReg64(RegID);
|
|
}
|
|
|
|
if (RegisterCount == 0)
|
|
{
|
|
//
|
|
// Not much point doing anything in this case.
|
|
//
|
|
|
|
return g_Machine->GetReg64(RegID);
|
|
}
|
|
|
|
//
|
|
// Note: the following code should be altered to understand
|
|
// NaTs as they come from the register stack (currently
|
|
// it ignores them).
|
|
//
|
|
|
|
RegisterNumber = 32;
|
|
}
|
|
|
|
ULONG64
|
|
GetScopeRegVal(ULONG RegId)
|
|
{
|
|
PDEBUG_SCOPE Scope = GetCurrentScope();
|
|
|
|
if (g_EffMachine == IMAGE_FILE_MACHINE_IA64)
|
|
{
|
|
switch (RegId)
|
|
{
|
|
case INTSP:
|
|
return Scope->Frame.StackOffset;
|
|
case RSBSP:
|
|
return Scope->Frame.FrameOffset;
|
|
default:
|
|
// continue
|
|
;
|
|
}
|
|
|
|
CROSS_PLATFORM_CONTEXT Context;
|
|
|
|
Context = g_Machine->m_Context;
|
|
|
|
return GetRegValIA64(&Context, &Scope->Frame, RegId);
|
|
}
|
|
else if (g_EffMachine == IMAGE_FILE_MACHINE_I386)
|
|
{
|
|
switch (RegId)
|
|
{
|
|
case X86_EBP:
|
|
if (Scope->Frame.FuncTableEntry)
|
|
{
|
|
PFPO_DATA FpoData = (PFPO_DATA)Scope->Frame.FuncTableEntry;
|
|
if (FpoData->cbFrame == FRAME_FPO)
|
|
{
|
|
//
|
|
// Get EBP from FPO data, if available
|
|
//
|
|
if (SAVE_EBP(&Scope->Frame))
|
|
{
|
|
return SAVE_EBP(&Scope->Frame);
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Guess the ebp value, in most cases for FPO frames its
|
|
// a DWORD off frameoffset
|
|
//
|
|
return Scope->Frame.FrameOffset + sizeof(DWORD);
|
|
}
|
|
}
|
|
}
|
|
return Scope->Frame.FrameOffset;
|
|
case X86_ESP:
|
|
return Scope->Frame.StackOffset;
|
|
default:
|
|
// continue
|
|
;
|
|
}
|
|
}
|
|
return g_Machine->GetReg64(RegId);
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
* TranslateAddress
|
|
* Flags Flags returned by dbghelp
|
|
* Address IN Address returned by dbghelp
|
|
* OUT Address of symbol
|
|
* Value Value of the symbol if its in register
|
|
*
|
|
*/
|
|
BOOL
|
|
TranslateAddress(
|
|
IN ULONG Flags,
|
|
IN ULONG RegId,
|
|
IN OUT PULONG64 Address,
|
|
OUT PULONG64 Value
|
|
)
|
|
{
|
|
PCROSS_PLATFORM_CONTEXT ScopeContext = GetCurrentScopeContext();
|
|
if (ScopeContext)
|
|
{
|
|
g_Machine->PushContext(ScopeContext);
|
|
}
|
|
|
|
if (Flags & SYMF_REGREL)
|
|
{
|
|
ULONG64 RegContent;
|
|
|
|
if (RegId)
|
|
{
|
|
RegContent = GetScopeRegVal(RegId);
|
|
}
|
|
else if (Value)
|
|
{
|
|
//
|
|
// *Value has RegID and *Address has Offset
|
|
//
|
|
RegContent = GetScopeRegVal(RegId = (ULONG) *Value);
|
|
}
|
|
else
|
|
{
|
|
DBG_ASSERT(FALSE);
|
|
if (ScopeContext)
|
|
{
|
|
g_Machine->PopContext();
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
*Address = RegContent + ((LONG64) (LONG) (ULONG) *Address);
|
|
#if 0
|
|
// This is now adjusted in GetScopeRegVal
|
|
if (g_EffMachine == IMAGE_FILE_MACHINE_I386 &&
|
|
RegId == X86_EBP)
|
|
{
|
|
PDEBUG_SCOPE Scope = GetCurrentScope();
|
|
PFPO_DATA pFpoData = (PFPO_DATA)Scope->Frame.FuncTableEntry;
|
|
if (pFpoData &&
|
|
(pFpoData->cbFrame == FRAME_FPO ||
|
|
pFpoData->cbFrame == FRAME_TRAP))
|
|
{
|
|
// Compensate for FPO's not having ebp
|
|
*Address += sizeof(DWORD);
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
else if (Flags & SYMF_REGISTER)
|
|
{
|
|
if (Value)
|
|
{
|
|
if (RegId)
|
|
{
|
|
*Value = GetScopeRegVal(RegId);
|
|
}
|
|
else
|
|
{
|
|
*Value = GetScopeRegVal((ULONG) *Address);
|
|
}
|
|
}
|
|
}
|
|
else if (Flags & SYMF_FRAMEREL)
|
|
{
|
|
PDEBUG_SCOPE Scope = GetCurrentScope();
|
|
if (Scope->Frame.FrameOffset)
|
|
{
|
|
*Address += Scope->Frame.FrameOffset;
|
|
|
|
PFPO_DATA pFpoData = (PFPO_DATA)Scope->Frame.FuncTableEntry;
|
|
if (g_EffMachine == IMAGE_FILE_MACHINE_I386 &&
|
|
pFpoData &&
|
|
(pFpoData->cbFrame == FRAME_FPO ||
|
|
pFpoData->cbFrame == FRAME_TRAP))
|
|
{
|
|
// Compensate for FPO's not having ebp
|
|
*Address += sizeof(DWORD);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
ADDR FP;
|
|
|
|
g_Machine->GetFP(&FP);
|
|
FP.flat = (LONG64) FP.flat + *Address;
|
|
*Address = FP.flat;
|
|
}
|
|
}
|
|
if (ScopeContext)
|
|
{
|
|
g_Machine->PopContext();
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
void
|
|
GetSymbolStdCall(ULONG64 Offset,
|
|
PCHAR Buffer,
|
|
ULONG BufferLen,
|
|
PULONG64 Displacement,
|
|
PUSHORT StdCallParams
|
|
)
|
|
{
|
|
IMAGEHLP_MODULE64 Mod;
|
|
|
|
// Assert that we have at least a minimum amount of space.
|
|
DBG_ASSERT(BufferLen >= sizeof(Mod.ModuleName));
|
|
Buffer[BufferLen - 1] = 0;
|
|
|
|
// In the past symbolic information would report the
|
|
// size of stdcall arguments, thus the StdCallParams argument
|
|
// could be filled out. Nowadays we do not have access
|
|
// to this information so just set it to 0xffff, which
|
|
// means unknown. In the future perhaps this can be
|
|
// turned back on.
|
|
if (StdCallParams != NULL)
|
|
{
|
|
*StdCallParams = 0xffff;
|
|
}
|
|
|
|
Mod.SizeOfStruct = sizeof(Mod);
|
|
// SymGetModuleInfo does special things with a -1 offset,
|
|
// so just assume there's never a symbol there and skip the call.
|
|
if (Offset != -1 &&
|
|
SymGetModuleInfo64(g_CurrentProcess->Handle, Offset, &Mod))
|
|
{
|
|
if (SymGetSymFromAddr64(g_CurrentProcess->Handle, Offset,
|
|
Displacement, g_Sym))
|
|
{
|
|
if (*Displacement == (ULONG64)-1)
|
|
{
|
|
// In some BBT cases dbghelp can tell that an offset
|
|
// is associated with a particular symbol but it
|
|
// doesn't have a valid offset. Present the symbol
|
|
// but in a way that makes it clear that it's
|
|
// this special case.
|
|
_snprintf(Buffer, BufferLen - 1,
|
|
"%s!%s <PERF> (%s+0x%I64x)",
|
|
Mod.ModuleName, g_Sym->Name,
|
|
Mod.ModuleName, (Offset - Mod.BaseOfImage));
|
|
*Displacement = 0;
|
|
}
|
|
else
|
|
{
|
|
_snprintf(Buffer, BufferLen - 1,
|
|
"%s!%s", Mod.ModuleName, g_Sym->Name);
|
|
}
|
|
return;
|
|
}
|
|
else
|
|
{
|
|
if (Offset >= Mod.BaseOfImage &&
|
|
Offset <= Mod.BaseOfImage + Mod.ImageSize)
|
|
{
|
|
strcpy(Buffer, Mod.ModuleName);
|
|
*Displacement = Offset - Mod.BaseOfImage;
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
ULONG64 FscBase;
|
|
|
|
// XXX drewb - Temporary hack so that stack traces
|
|
// show meaningful symbols for the fast system call
|
|
// code stuck in the shared user data area.
|
|
switch(IsInFastSyscall(Offset, &FscBase))
|
|
{
|
|
case FSC_FOUND:
|
|
strcpy(Buffer, "*SharedUserSystemCall");
|
|
*Displacement = Offset - FscBase;
|
|
return;
|
|
}
|
|
|
|
*Buffer = 0;
|
|
*Displacement = Offset;
|
|
}
|
|
|
|
BOOL
|
|
GetNearSymbol(
|
|
ULONG64 Offset,
|
|
PSTR Buffer,
|
|
ULONG BufferLen,
|
|
PULONG64 Disp,
|
|
LONG Delta
|
|
)
|
|
{
|
|
IMAGEHLP_MODULE64 Mod;
|
|
|
|
// Assert that we have at least a minimum amount of space.
|
|
DBG_ASSERT(BufferLen >= sizeof(Mod.ModuleName));
|
|
Buffer[BufferLen - 1] = 0;
|
|
|
|
Mod.SizeOfStruct = sizeof(Mod);
|
|
// SymGetModuleInfo does special things with a -1 offset,
|
|
// so just assume there's never a symbol there and skip the call.
|
|
if (Offset != -1 &&
|
|
SymGetModuleInfo64(g_CurrentProcess->Handle, Offset, &Mod))
|
|
{
|
|
if (SymGetSymFromAddr64(g_CurrentProcess->Handle, Offset, Disp, g_Sym))
|
|
{
|
|
if (Delta < 0)
|
|
{
|
|
while (Delta++ < 0)
|
|
{
|
|
if (!SymGetSymPrev(g_CurrentProcess->Handle, g_Sym))
|
|
{
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
if (Disp != NULL)
|
|
{
|
|
*Disp = Offset - g_Sym->Address;
|
|
}
|
|
}
|
|
else if (Delta > 0)
|
|
{
|
|
while (Delta-- > 0)
|
|
{
|
|
if (!SymGetSymNext(g_CurrentProcess->Handle, g_Sym))
|
|
{
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
if (Disp != NULL)
|
|
{
|
|
*Disp = g_Sym->Address - Offset;
|
|
}
|
|
}
|
|
|
|
_snprintf(Buffer, BufferLen - 1,
|
|
"%s!%s", Mod.ModuleName, g_Sym->Name);
|
|
return TRUE;
|
|
}
|
|
else if (Delta == 0 &&
|
|
Offset >= Mod.BaseOfImage &&
|
|
Offset <= Mod.BaseOfImage + Mod.ImageSize)
|
|
{
|
|
strcpy(Buffer, Mod.ModuleName);
|
|
if (Disp != NULL)
|
|
{
|
|
*Disp = Offset - Mod.BaseOfImage;
|
|
}
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
PDEBUG_IMAGE_INFO
|
|
GetImageByIndex(PPROCESS_INFO Process, ULONG Index)
|
|
{
|
|
PDEBUG_IMAGE_INFO Image = Process->ImageHead;
|
|
while (Index > 0 && Image != NULL)
|
|
{
|
|
Index--;
|
|
Image = Image->Next;
|
|
}
|
|
return Image;
|
|
}
|
|
|
|
PDEBUG_IMAGE_INFO
|
|
GetImageByOffset(PPROCESS_INFO Process, ULONG64 Offset)
|
|
{
|
|
PDEBUG_IMAGE_INFO Image = Process->ImageHead;
|
|
while (Image != NULL &&
|
|
(Offset < Image->BaseOfImage ||
|
|
Offset >= Image->BaseOfImage + Image->SizeOfImage))
|
|
{
|
|
Image = Image->Next;
|
|
}
|
|
return Image;
|
|
}
|
|
|
|
PDEBUG_IMAGE_INFO
|
|
GetImageByName(PPROCESS_INFO Process, PCSTR Name, INAME Which)
|
|
{
|
|
PDEBUG_IMAGE_INFO Image = Process->ImageHead;
|
|
while (Image != NULL)
|
|
{
|
|
PCSTR WhichStr;
|
|
|
|
switch(Which)
|
|
{
|
|
case INAME_IMAGE_PATH:
|
|
WhichStr = Image->ImagePath;
|
|
break;
|
|
case INAME_IMAGE_PATH_TAIL:
|
|
WhichStr = PathTail(Image->ImagePath);
|
|
break;
|
|
case INAME_MODULE:
|
|
if (Image->OriginalModuleName[0] &&
|
|
!_stricmp(Image->OriginalModuleName, Name))
|
|
{
|
|
return Image;
|
|
}
|
|
WhichStr = Image->ModuleName;
|
|
break;
|
|
}
|
|
|
|
if (_stricmp(WhichStr, Name) == 0)
|
|
{
|
|
break;
|
|
}
|
|
|
|
Image = Image->Next;
|
|
}
|
|
return Image;
|
|
}
|
|
|
|
#define IMAGE_IS_PATTERN ((PDEBUG_IMAGE_INFO)-1)
|
|
|
|
PDEBUG_IMAGE_INFO
|
|
ParseModuleName(PBOOL ModSpecified)
|
|
{
|
|
PSTR CmdSaved = g_CurCmd;
|
|
CHAR Name[MAX_MODULE];
|
|
PSTR Dst = Name;
|
|
CHAR ch;
|
|
BOOL HasWild = FALSE;
|
|
|
|
// first, parse out a possible module name, either a '*' or
|
|
// a string of 'A'-'Z', 'a'-'z', '0'-'9', '_', '~' (or null)
|
|
|
|
ch = PeekChar();
|
|
g_CurCmd++;
|
|
|
|
while ((ch >= 'A' && ch <= 'Z') ||
|
|
(ch >= 'a' && ch <= 'z') ||
|
|
(ch >= '0' && ch <= '9') ||
|
|
ch == '_' || ch == '~' || ch == '*' || ch == '?')
|
|
{
|
|
if (ch == '*' || ch == '?')
|
|
{
|
|
HasWild = TRUE;
|
|
}
|
|
|
|
*Dst++ = ch;
|
|
ch = *g_CurCmd++;
|
|
}
|
|
*Dst = '\0';
|
|
g_CurCmd--;
|
|
|
|
// if no '!' after name and white space, then no module specified
|
|
// restore text pointer and treat as null module (PC current)
|
|
|
|
if (PeekChar() == '!')
|
|
{
|
|
g_CurCmd++;
|
|
}
|
|
else
|
|
{
|
|
g_CurCmd = CmdSaved;
|
|
Name[0] = '\0';
|
|
}
|
|
|
|
// Name either has: '*' for all modules,
|
|
// '\0' for current module,
|
|
// nonnull string for module name.
|
|
*ModSpecified = Name[0] != 0;
|
|
if (HasWild)
|
|
{
|
|
return IMAGE_IS_PATTERN;
|
|
}
|
|
else if (Name[0])
|
|
{
|
|
return GetImageByName(g_CurrentProcess, Name, INAME_MODULE);
|
|
}
|
|
else
|
|
{
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
BOOL CALLBACK
|
|
ParseExamineSymbolInfo(
|
|
PSYMBOL_INFO SymInfo,
|
|
ULONG Size,
|
|
PVOID ExamineInfoArg
|
|
)
|
|
{
|
|
PEXAMINE_INFO ExamineInfo = (PEXAMINE_INFO)ExamineInfoArg;
|
|
ULONG64 Address = SymInfo->Address;
|
|
PDEBUG_IMAGE_INFO Image;
|
|
|
|
Image = GetImageByOffset(g_CurrentProcess, SymInfo->ModBase);
|
|
if (Image && ((SymInfo->Flags & IMAGEHLP_SYMBOL_INFO_LOCAL) == 0))
|
|
{
|
|
dprintf( "%s %s!%s",
|
|
FormatAddr64(Address),
|
|
Image->ModuleName,
|
|
SymInfo->Name);
|
|
}
|
|
else
|
|
{
|
|
ULONG64 Value = 0;
|
|
|
|
TranslateAddress(SymInfo->Flags, SymInfo->Register,
|
|
&Address, &Value);
|
|
dprintf( "%s %s",
|
|
FormatAddr64(Address),
|
|
SymInfo->Name);
|
|
}
|
|
|
|
if (ExamineInfo->Verbose)
|
|
{
|
|
dprintf(" ");
|
|
ShowSymbolInfo(SymInfo);
|
|
}
|
|
|
|
dprintf("\n");
|
|
|
|
return !CheckUserInterrupt();
|
|
}
|
|
|
|
/*** ParseExamine - parse and execute examine command
|
|
*
|
|
* Purpose:
|
|
* Parse the current command string and examine the symbol
|
|
* table to display the appropriate entries. The entries
|
|
* are displayed in increasing string order. This function
|
|
* accepts underscores, alphabetic, and numeric characters
|
|
* to match as well as the special characters '?', '*', '['-']'.
|
|
*
|
|
* Input:
|
|
* g_CurCmd - pointer to current command string
|
|
*
|
|
* Output:
|
|
* offset and string name of symbols displayed
|
|
*
|
|
*************************************************************************/
|
|
|
|
void
|
|
ParseExamine(void)
|
|
{
|
|
CHAR StringBuf[MAX_SYMBOL_LEN];
|
|
UCHAR ch;
|
|
PSTR String = StringBuf;
|
|
PSTR Start;
|
|
PSTR ModEnd;
|
|
BOOL ModSpecified;
|
|
ULONG64 Base = 0;
|
|
ULONG Count;
|
|
PDEBUG_IMAGE_INFO Image;
|
|
EXAMINE_INFO ExamineInfo;
|
|
|
|
// Get module pointer from name in command line (<string>!).
|
|
|
|
PeekChar();
|
|
Start = g_CurCmd;
|
|
|
|
Image = ParseModuleName(&ModSpecified);
|
|
|
|
ModEnd = g_CurCmd;
|
|
ch = PeekChar();
|
|
|
|
// Special case the command "x <pattern>!" to dump out the module table.
|
|
if (Image == IMAGE_IS_PATTERN &&
|
|
(ch == ';' || ch == '\0'))
|
|
{
|
|
*(ModEnd - 1) = 0;
|
|
_strupr(Start);
|
|
DumpModuleTable(DMT_STANDARD, Start);
|
|
return;
|
|
}
|
|
|
|
if (ModSpecified)
|
|
{
|
|
if (Image == NULL)
|
|
{
|
|
// The user specified a module that doesn't exist.
|
|
error(VARDEF);
|
|
}
|
|
else if (Image == IMAGE_IS_PATTERN)
|
|
{
|
|
// The user gave a pattern string for the module
|
|
// so we need to pass it on for dbghelp to scan with.
|
|
memcpy(String, Start, (ModEnd - Start));
|
|
String += ModEnd - Start;
|
|
}
|
|
else
|
|
{
|
|
// A specific image was given and found so
|
|
// confine the search to that one image.
|
|
Base = Image->BaseOfImage;
|
|
}
|
|
}
|
|
|
|
g_CurCmd++;
|
|
|
|
// Condense leading underscores into a "_#"
|
|
// that will match zero or more underscores. This causes all
|
|
// underscore-prefixed symbols to match the base symbol name
|
|
// when the pattern is prefixed by an underscore.
|
|
if (ch == '_')
|
|
{
|
|
*String++ = '_';
|
|
*String++ = '#';
|
|
do
|
|
{
|
|
ch = *g_CurCmd++;
|
|
} while (ch == '_');
|
|
}
|
|
|
|
ch = (UCHAR)toupper(ch);
|
|
while (ch && ch != ';' && ch != ' ')
|
|
{
|
|
*String++ = ch;
|
|
ch = (CHAR)toupper(*g_CurCmd);
|
|
g_CurCmd++;
|
|
}
|
|
*String = '\0';
|
|
g_CurCmd--;
|
|
|
|
ExamineInfo.Verbose = TRUE;
|
|
|
|
// We nee the scope for all cases since param values are displayed for
|
|
// function in scope
|
|
RequireCurrentScope();
|
|
|
|
SymEnumSymbols(g_CurrentProcess->Handle,
|
|
Base,
|
|
StringBuf,
|
|
ParseExamineSymbolInfo,
|
|
&ExamineInfo);
|
|
}
|
|
|
|
/*** fnListNear - function to list symbols near an address
|
|
*
|
|
* Purpose:
|
|
* from the address specified, access the symbol table to
|
|
* find the closest symbolic addresses both before and after
|
|
* it. output these on one line (if spaces permits).
|
|
*
|
|
* Input:
|
|
* addrstart - address to base listing
|
|
*
|
|
* Output:
|
|
* symbolic and absolute addresses of variable on or before
|
|
* and after the specified address
|
|
*
|
|
*************************************************************************/
|
|
|
|
void
|
|
fnListNear(ULONG64 AddrStart)
|
|
{
|
|
ULONG64 Displacement;
|
|
IMAGEHLP_MODULE64 Mod;
|
|
|
|
if (g_SrcOptions & SRCOPT_LIST_LINE)
|
|
{
|
|
OutputLineAddr(AddrStart);
|
|
}
|
|
|
|
if (SymGetSymFromAddr64(g_CurrentProcess->Handle, AddrStart,
|
|
&Displacement, g_Sym))
|
|
{
|
|
Mod.SizeOfStruct = sizeof(Mod);
|
|
if (!SymGetModuleInfo64(g_CurrentProcess->Handle, AddrStart, &Mod))
|
|
{
|
|
return;
|
|
}
|
|
|
|
dprintf("(%s) %s!%s",
|
|
FormatAddr64(g_Sym->Address),
|
|
Mod.ModuleName,
|
|
g_Sym->Name);
|
|
|
|
if (Displacement)
|
|
{
|
|
dprintf("+0x%s ", FormatDisp64(Displacement));
|
|
}
|
|
else
|
|
{
|
|
dprintf(" ");
|
|
}
|
|
|
|
if (SymGetSymNext64(g_CurrentProcess->Handle, g_Sym))
|
|
{
|
|
dprintf("| (%s) %s!%s",
|
|
FormatAddr64(g_Sym->Address),
|
|
Mod.ModuleName,
|
|
g_Sym->Name);
|
|
}
|
|
dprintf("\n");
|
|
}
|
|
}
|
|
|
|
void
|
|
DumpModuleTable(ULONG Flags, PSTR Pattern)
|
|
{
|
|
PDEBUG_IMAGE_INFO Image;
|
|
IMAGEHLP_MODULE64 mi;
|
|
DBH_MODSYMINFO SymFile;
|
|
ULONG i;
|
|
|
|
if (g_TargetMachine->m_Ptr64)
|
|
{
|
|
dprintf("start end module name\n");
|
|
}
|
|
else
|
|
{
|
|
dprintf("start end module name\n");
|
|
}
|
|
|
|
Image = g_CurrentProcess->ImageHead;
|
|
while (Image)
|
|
{
|
|
ULONG PrimaryName;
|
|
PSTR Names[DMT_NAME_COUNT];
|
|
|
|
if (Pattern != NULL &&
|
|
!MatchPattern(Image->ModuleName, Pattern))
|
|
{
|
|
Image = Image->Next;
|
|
continue;
|
|
}
|
|
|
|
mi.SizeOfStruct = sizeof(mi);
|
|
if (!SymGetModuleInfo64( g_CurrentProcess->Handle,
|
|
Image->BaseOfImage, &mi ))
|
|
{
|
|
mi.SymType = SymNone;
|
|
}
|
|
|
|
if (Flags & (DMT_SYM_FILE_NAME | DMT_VERBOSE))
|
|
{
|
|
SymFile.function = dbhModSymInfo;
|
|
SymFile.sizeofstruct = sizeof(SymFile);
|
|
SymFile.addr = Image->BaseOfImage;
|
|
if (!dbghelp(g_CurrentProcess->Handle, &SymFile))
|
|
{
|
|
sprintf(SymFile.file, "<Error: %s>",
|
|
FormatStatusCode(WIN32_LAST_STATUS()));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
SymFile.file[0] = 0;
|
|
}
|
|
|
|
Names[DMT_NAME_SYM_IMAGE] = mi.LoadedImageName;
|
|
Names[DMT_NAME_SYM_FILE] = SymFile.file;
|
|
Names[DMT_NAME_MAPPED_IMAGE] = Image->MappedImagePath;
|
|
Names[DMT_NAME_IMAGE_PATH] = Image->ImagePath;
|
|
|
|
if (Flags & DMT_SYM_FILE_NAME)
|
|
{
|
|
PrimaryName = DMT_NAME_SYM_FILE;
|
|
}
|
|
else if (Flags & DMT_MAPPED_IMAGE_NAME)
|
|
{
|
|
PrimaryName = DMT_NAME_MAPPED_IMAGE;
|
|
}
|
|
else if (Flags & DMT_IMAGE_PATH_NAME)
|
|
{
|
|
PrimaryName = DMT_NAME_IMAGE_PATH;
|
|
}
|
|
else
|
|
{
|
|
PrimaryName = DMT_NAME_SYM_IMAGE;
|
|
}
|
|
|
|
//
|
|
// Skip modules filtered by flags
|
|
//
|
|
if ((Flags & DMT_ONLY_LOADED_SYMBOLS) &&
|
|
(mi.SymType == SymDeferred))
|
|
{
|
|
Image = Image->Next;
|
|
continue;
|
|
}
|
|
|
|
if (IS_KERNEL_TARGET())
|
|
{
|
|
if ((Flags & DMT_ONLY_USER_SYMBOLS) &&
|
|
(Image->BaseOfImage >= g_SystemRangeStart))
|
|
{
|
|
Image = Image->Next;
|
|
continue;
|
|
}
|
|
|
|
if ((Flags & DMT_ONLY_KERNEL_SYMBOLS) &&
|
|
(Image->BaseOfImage <= g_SystemRangeStart))
|
|
{
|
|
Image = Image->Next;
|
|
continue;
|
|
}
|
|
}
|
|
|
|
_strlwr( Image->ModuleName );
|
|
|
|
dprintf( "%s %s %-8s ",
|
|
FormatAddr64(Image->BaseOfImage),
|
|
FormatAddr64(Image->BaseOfImage + Image->SizeOfImage),
|
|
Image->ModuleName
|
|
);
|
|
|
|
if (Flags & DMT_NO_SYMBOL_OUTPUT)
|
|
{
|
|
goto SkipSymbolOutput;
|
|
}
|
|
if (PrimaryName == DMT_NAME_MAPPED_IMAGE ||
|
|
PrimaryName == DMT_NAME_IMAGE_PATH)
|
|
{
|
|
dprintf(" %s\n",
|
|
*Names[PrimaryName] ? Names[PrimaryName] : "<none>");
|
|
goto SkipSymbolOutput;
|
|
}
|
|
|
|
switch (Image->GoodCheckSum)
|
|
{
|
|
case DII_GOOD_CHECKSUM:
|
|
dprintf( " " );
|
|
break;
|
|
case DII_UNKNOWN_TIMESTAMP:
|
|
dprintf( "T " );
|
|
break;
|
|
case DII_UNKNOWN_CHECKSUM:
|
|
dprintf( "C " );
|
|
break;
|
|
case DII_BAD_CHECKSUM:
|
|
dprintf( "# " );
|
|
break;
|
|
}
|
|
|
|
if (mi.SymType == SymDeferred)
|
|
{
|
|
dprintf( "(deferred) " );
|
|
}
|
|
else if (mi.SymType == SymNone)
|
|
{
|
|
dprintf( "(no symbolic information) " );
|
|
}
|
|
else
|
|
{
|
|
switch ( mi.SymType )
|
|
{
|
|
case SymCoff:
|
|
dprintf( "(coff symbols) " );
|
|
break;
|
|
|
|
case SymCv:
|
|
dprintf( "(codeview symbols) " );
|
|
break;
|
|
|
|
case SymPdb:
|
|
dprintf( "(pdb symbols) " );
|
|
break;
|
|
|
|
case SymExport:
|
|
dprintf( "(export symbols) " );
|
|
break;
|
|
}
|
|
|
|
dprintf("%s", *Names[PrimaryName] ? Names[PrimaryName] : "<none>");
|
|
}
|
|
|
|
dprintf("\n");
|
|
|
|
SkipSymbolOutput:
|
|
if (Flags & DMT_VERBOSE)
|
|
{
|
|
|
|
for (i = 0; i < DMT_NAME_COUNT; i++)
|
|
{
|
|
if (i != PrimaryName && *Names[i])
|
|
{
|
|
dprintf(" %s: %s\n", g_DmtNameDescs[i], Names[i]);
|
|
}
|
|
}
|
|
}
|
|
if (Flags & (DMT_VERBOSE | DMT_IMAGE_TIMESTAMP))
|
|
{
|
|
LPSTR TimeDateStr = TimeToStr(Image->TimeDateStamp);
|
|
dprintf(" Checksum: %08X Timestamp: %s (%08X)\n",
|
|
Image->CheckSum, TimeDateStr, Image->TimeDateStamp);
|
|
|
|
}
|
|
if (Flags & DMT_VERBOSE)
|
|
{
|
|
|
|
VS_FIXEDFILEINFO FixedVer;
|
|
|
|
if (g_Target->GetImageVersionInformation
|
|
(Image->ImagePath, Image->BaseOfImage, "\\",
|
|
&FixedVer, sizeof(FixedVer), NULL) == S_OK)
|
|
{
|
|
char Item[64];
|
|
char VerString[128];
|
|
|
|
dprintf(" File version: %d.%d.%d.%d"
|
|
" Product version: %d.%d.%d.%d\n",
|
|
FixedVer.dwFileVersionMS >> 16,
|
|
FixedVer.dwFileVersionMS & 0xFFFF,
|
|
FixedVer.dwFileVersionLS >> 16,
|
|
FixedVer.dwFileVersionLS & 0xFFFF,
|
|
FixedVer.dwProductVersionMS >> 16,
|
|
FixedVer.dwProductVersionMS & 0xFFFF,
|
|
FixedVer.dwProductVersionLS >> 16,
|
|
FixedVer.dwProductVersionLS & 0xFFFF);
|
|
dprintf(" File flags: %X (Mask %X) File OS: %X "
|
|
"File type: %X.%X\n",
|
|
FixedVer.dwFileFlags & FixedVer.dwFileFlagsMask,
|
|
FixedVer.dwFileFlagsMask, FixedVer.dwFileOS,
|
|
FixedVer.dwFileType, FixedVer.dwFileSubtype);
|
|
dprintf(" File date: %08X.%08X\n",
|
|
FixedVer.dwFileDateMS, FixedVer.dwFileDateLS);
|
|
|
|
sprintf(Item, "\\StringFileInfo\\%04x%04x\\FileVersion",
|
|
VER_VERSION_TRANSLATION);
|
|
if (SUCCEEDED(g_Target->GetImageVersionInformation
|
|
(Image->ImagePath, Image->BaseOfImage, Item,
|
|
VerString, sizeof(VerString), NULL)))
|
|
{
|
|
dprintf(" Version string: %s\n", VerString);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (CheckUserInterrupt())
|
|
{
|
|
break;
|
|
}
|
|
|
|
Image = Image->Next;
|
|
}
|
|
|
|
UnloadedModuleInfo* Unl;
|
|
|
|
if ((Flags & (DMT_ONLY_LOADED_SYMBOLS | DMT_ONLY_USER_SYMBOLS)) == 0)
|
|
{
|
|
ULONG LumFlags = LUM_OUTPUT;
|
|
|
|
LumFlags |= ((Flags & DMT_VERBOSE) ? LUM_OUTPUT_VERBOSE : 0);
|
|
LumFlags |= ((Flags & DMT_IMAGE_TIMESTAMP) ? LUM_OUTPUT_TIMESTAMP : 0);
|
|
dprintf("\n");
|
|
ListUnloadedModules(LumFlags, Pattern);
|
|
}
|
|
}
|
|
|
|
void
|
|
ParseDumpModuleTable(void)
|
|
{
|
|
ULONG Flags = DMT_STANDARD;
|
|
char Pattern[MAX_MODULE];
|
|
PSTR Pat = NULL;
|
|
|
|
g_CurCmd++;
|
|
|
|
for (;;)
|
|
{
|
|
// skip white space
|
|
while (isspace(*g_CurCmd))
|
|
{
|
|
g_CurCmd++;
|
|
}
|
|
|
|
if (*g_CurCmd == 'f')
|
|
{
|
|
Flags = (Flags & ~DMT_NAME_FLAGS) | DMT_IMAGE_PATH_NAME;
|
|
g_CurCmd++;
|
|
}
|
|
else if (*g_CurCmd == 'i')
|
|
{
|
|
Flags = (Flags & ~DMT_NAME_FLAGS) | DMT_SYM_IMAGE_FILE_NAME;
|
|
g_CurCmd++;
|
|
}
|
|
else if (*g_CurCmd == 'l')
|
|
{
|
|
Flags |= DMT_ONLY_LOADED_SYMBOLS;
|
|
g_CurCmd++;
|
|
}
|
|
else if (*g_CurCmd == 'm')
|
|
{
|
|
g_CurCmd++;
|
|
// skip white space
|
|
while (isspace(*g_CurCmd))
|
|
{
|
|
g_CurCmd++;
|
|
}
|
|
Pat = Pattern;
|
|
while (*g_CurCmd && !isspace(*g_CurCmd))
|
|
{
|
|
if ((Pat - Pattern) < sizeof(Pattern) - 1)
|
|
{
|
|
*Pat++ = *g_CurCmd;
|
|
}
|
|
|
|
g_CurCmd++;
|
|
}
|
|
*Pat = 0;
|
|
Pat = Pattern;
|
|
_strupr(Pat);
|
|
}
|
|
else if (*g_CurCmd == 'p')
|
|
{
|
|
Flags = (Flags & ~DMT_NAME_FLAGS) | DMT_MAPPED_IMAGE_NAME;
|
|
g_CurCmd++;
|
|
}
|
|
else if (*g_CurCmd == 't')
|
|
{
|
|
Flags = (Flags & ~(DMT_NAME_FLAGS)) | DMT_NAME_SYM_IMAGE | DMT_IMAGE_TIMESTAMP
|
|
| DMT_NO_SYMBOL_OUTPUT;
|
|
g_CurCmd++;
|
|
}
|
|
else if (*g_CurCmd == 'v')
|
|
{
|
|
Flags |= DMT_VERBOSE;
|
|
g_CurCmd++;
|
|
}
|
|
else if (IS_KERNEL_TARGET())
|
|
{
|
|
if (*g_CurCmd == 'u')
|
|
{
|
|
Flags |= DMT_ONLY_USER_SYMBOLS;
|
|
g_CurCmd++;
|
|
}
|
|
else if (*g_CurCmd == 'k')
|
|
{
|
|
Flags |= DMT_ONLY_KERNEL_SYMBOLS;
|
|
g_CurCmd++;
|
|
}
|
|
else
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
DumpModuleTable(Flags, Pat);
|
|
}
|
|
|
|
void
|
|
GetCurrentMemoryOffsets (
|
|
PULONG64 pMemoryLow,
|
|
PULONG64 pMemoryHigh
|
|
)
|
|
{
|
|
*pMemoryLow = (ULONG64)(LONG64)-1; // default value for no source
|
|
}
|
|
|
|
|
|
ULONG
|
|
ReadImageData(
|
|
ULONG64 Address,
|
|
HANDLE hFile,
|
|
LPVOID Buffer,
|
|
ULONG Size
|
|
)
|
|
{
|
|
if (hFile)
|
|
{
|
|
ULONG Result;
|
|
|
|
if (!SetFilePointer( hFile, (ULONG)Address, NULL, FILE_BEGIN ))
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
if (!ReadFile( hFile, Buffer, Size, &Result, NULL))
|
|
{
|
|
return 0;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
ULONG Result;
|
|
|
|
if (g_Target->ReadVirtual(Address, Buffer, Size, &Result) != S_OK ||
|
|
Result < Size)
|
|
{
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
return Size;
|
|
}
|
|
|
|
BOOL
|
|
GetModnameFromImageInternal(ULONG64 BaseOfDll,
|
|
HANDLE hFile,
|
|
LPSTR lpName,
|
|
ULONG NameSize
|
|
)
|
|
{
|
|
IMAGE_DEBUG_DIRECTORY DebugDir;
|
|
PIMAGE_DEBUG_MISC pMisc;
|
|
PIMAGE_DEBUG_MISC pT;
|
|
DWORD rva;
|
|
int nDebugDirs;
|
|
int i;
|
|
int j;
|
|
int l;
|
|
BOOL rVal = FALSE;
|
|
PVOID pExeName;
|
|
IMAGE_DOS_HEADER dh;
|
|
USHORT NumberOfSections;
|
|
USHORT Characteristics;
|
|
ULONG64 address;
|
|
DWORD sig;
|
|
PIMAGE_SECTION_HEADER pSH = NULL;
|
|
DWORD cb;
|
|
NTSTATUS Status;
|
|
ULONG Result;
|
|
IMAGE_NT_HEADERS64 nh64;
|
|
PIMAGE_NT_HEADERS32 pnh32 = (PIMAGE_NT_HEADERS32) &nh64;
|
|
BOOL fCheckDllExtensionInExportTable = FALSE;
|
|
|
|
|
|
lpName[0] = 0;
|
|
|
|
if (hFile)
|
|
{
|
|
BaseOfDll = 0;
|
|
}
|
|
|
|
address = BaseOfDll;
|
|
|
|
ReadImageData( address, hFile, &dh, sizeof(dh) );
|
|
|
|
if (dh.e_magic == IMAGE_DOS_SIGNATURE)
|
|
{
|
|
address += dh.e_lfanew;
|
|
}
|
|
|
|
ReadImageData( address, hFile, &sig, sizeof(sig) );
|
|
|
|
if (sig != IMAGE_NT_SIGNATURE)
|
|
{
|
|
IMAGE_FILE_HEADER fh;
|
|
IMAGE_ROM_OPTIONAL_HEADER rom;
|
|
|
|
ReadImageData( address, hFile, &fh, sizeof(IMAGE_FILE_HEADER) );
|
|
address += sizeof(IMAGE_FILE_HEADER);
|
|
ReadImageData( address, hFile, &rom, sizeof(rom) );
|
|
address += sizeof(rom);
|
|
|
|
if (rom.Magic == IMAGE_ROM_OPTIONAL_HDR_MAGIC)
|
|
{
|
|
NumberOfSections = fh.NumberOfSections;
|
|
Characteristics = fh.Characteristics;
|
|
nDebugDirs = rva = 0;
|
|
}
|
|
else
|
|
{
|
|
goto Finish;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// read the head as a 64 bit header and cast it appropriately.
|
|
//
|
|
|
|
ReadImageData( address, hFile, &nh64, sizeof(nh64) );
|
|
|
|
if (IsImageMachineType64(pnh32->FileHeader.Machine))
|
|
{
|
|
address += sizeof(IMAGE_NT_HEADERS64);
|
|
NumberOfSections = nh64.FileHeader.NumberOfSections;
|
|
Characteristics = nh64.FileHeader.Characteristics;
|
|
nDebugDirs = nh64.OptionalHeader.
|
|
DataDirectory[IMAGE_DIRECTORY_ENTRY_DEBUG].Size /
|
|
sizeof(IMAGE_DEBUG_DIRECTORY);
|
|
rva = nh64.OptionalHeader.
|
|
DataDirectory[IMAGE_DIRECTORY_ENTRY_DEBUG].VirtualAddress;
|
|
}
|
|
else
|
|
{
|
|
address += sizeof(IMAGE_NT_HEADERS32);
|
|
NumberOfSections = pnh32->FileHeader.NumberOfSections;
|
|
Characteristics = pnh32->FileHeader.Characteristics;
|
|
nDebugDirs = pnh32->OptionalHeader.
|
|
DataDirectory[IMAGE_DIRECTORY_ENTRY_DEBUG].Size /
|
|
sizeof(IMAGE_DEBUG_DIRECTORY);
|
|
rva = pnh32->OptionalHeader.
|
|
DataDirectory[IMAGE_DIRECTORY_ENTRY_DEBUG].VirtualAddress;
|
|
}
|
|
}
|
|
|
|
|
|
// After this point, none of the image datastructures have changed between
|
|
// 32bit NT and 64bit NT.
|
|
|
|
cb = NumberOfSections * IMAGE_SIZEOF_SECTION_HEADER;
|
|
pSH = (PIMAGE_SECTION_HEADER)malloc( cb );
|
|
if (!pSH)
|
|
{
|
|
goto Finish;
|
|
}
|
|
|
|
if (!ReadImageData( address, hFile, pSH, cb ))
|
|
{
|
|
goto Finish;
|
|
}
|
|
|
|
if (!nDebugDirs)
|
|
{
|
|
goto CheckExportTable;
|
|
}
|
|
|
|
for (i = 0; i < NumberOfSections; i++)
|
|
{
|
|
if (rva >= pSH[i].VirtualAddress &&
|
|
rva < pSH[i].VirtualAddress + pSH[i].SizeOfRawData)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (i >= NumberOfSections)
|
|
{
|
|
goto CheckExportTable;
|
|
}
|
|
|
|
rva = rva - pSH[i].VirtualAddress;
|
|
if (hFile)
|
|
{
|
|
rva += pSH[i].PointerToRawData;
|
|
}
|
|
else
|
|
{
|
|
rva += pSH[i].VirtualAddress;
|
|
}
|
|
|
|
for (j = 0; j < nDebugDirs; j++)
|
|
{
|
|
ReadImageData( rva + (sizeof(DebugDir) * j) + BaseOfDll,
|
|
hFile, &DebugDir, sizeof(DebugDir) );
|
|
|
|
if (DebugDir.Type == IMAGE_DEBUG_TYPE_MISC)
|
|
{
|
|
l = DebugDir.SizeOfData;
|
|
pMisc = pT = (PIMAGE_DEBUG_MISC)malloc(l);
|
|
|
|
if (!pMisc)
|
|
{
|
|
break;
|
|
}
|
|
|
|
if (!hFile &&
|
|
((ULONG)DebugDir.AddressOfRawData < pSH[i].VirtualAddress ||
|
|
(ULONG)DebugDir.AddressOfRawData >=
|
|
pSH[i].VirtualAddress + pSH[i].SizeOfRawData))
|
|
{
|
|
//
|
|
// the misc debug data MUST be in the .rdata section
|
|
// otherwise the debugger cannot access it as it is not
|
|
// mapped in.
|
|
//
|
|
break;
|
|
}
|
|
|
|
if (hFile)
|
|
{
|
|
address = DebugDir.PointerToRawData;
|
|
}
|
|
else
|
|
{
|
|
address = DebugDir.AddressOfRawData + BaseOfDll;
|
|
}
|
|
|
|
ReadImageData( address, hFile, pMisc, l );
|
|
|
|
while (l > 0)
|
|
{
|
|
if (pMisc->DataType != IMAGE_DEBUG_MISC_EXENAME)
|
|
{
|
|
//
|
|
// beware corrupt images:
|
|
//
|
|
if (pMisc->Length == 0 ||
|
|
pMisc->Length > (ULONG)l)
|
|
{
|
|
break;
|
|
}
|
|
l -= pMisc->Length;
|
|
pMisc = (PIMAGE_DEBUG_MISC)
|
|
(((LPSTR)pMisc) + pMisc->Length);
|
|
}
|
|
else
|
|
{
|
|
pExeName = (PVOID)&pMisc->Data[ 0 ];
|
|
|
|
if (!pMisc->Unicode)
|
|
{
|
|
strncat(lpName, (LPSTR)pExeName, NameSize - 1);
|
|
rVal = TRUE;
|
|
}
|
|
else
|
|
{
|
|
WideCharToMultiByte(CP_ACP,
|
|
0,
|
|
(LPWSTR)pExeName,
|
|
-1,
|
|
lpName,
|
|
NameSize,
|
|
NULL,
|
|
NULL);
|
|
rVal = TRUE;
|
|
}
|
|
|
|
//
|
|
// Undo stevewo's error
|
|
//
|
|
|
|
if (_stricmp(&lpName[strlen(lpName) - 4], ".DBG") == 0)
|
|
{
|
|
char rgchPath[MAX_IMAGE_PATH];
|
|
char rgchBase[_MAX_FNAME];
|
|
|
|
_splitpath(lpName, NULL, rgchPath, rgchBase, NULL);
|
|
if (strlen(rgchPath) == 4)
|
|
{
|
|
rgchPath[strlen(rgchPath) - 1] = 0;
|
|
strcpy(lpName, rgchBase);
|
|
strcat(lpName, ".");
|
|
strcat(lpName, rgchPath);
|
|
}
|
|
else if (Characteristics & IMAGE_FILE_DLL)
|
|
{
|
|
strcpy(lpName, rgchBase);
|
|
strcat(lpName, ".dll");
|
|
}
|
|
else
|
|
{
|
|
strcpy(lpName, rgchBase);
|
|
strcat(lpName, ".exe");
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
free(pT);
|
|
|
|
break;
|
|
}
|
|
else if ((DebugDir.Type == IMAGE_DEBUG_TYPE_CODEVIEW) &&
|
|
((!hFile && DebugDir.AddressOfRawData) ||
|
|
(hFile && DebugDir.PointerToRawData)) &&
|
|
(DebugDir.SizeOfData > sizeof(NB10IH)))
|
|
{
|
|
DWORD Signature;
|
|
char rgchPath[MAX_IMAGE_PATH];
|
|
char rgchBase[_MAX_FNAME];
|
|
|
|
// Mapped CV info. Read the data and see what the content is.
|
|
|
|
if (hFile)
|
|
{
|
|
address = DebugDir.PointerToRawData;
|
|
}
|
|
else
|
|
{
|
|
address = DebugDir.AddressOfRawData + BaseOfDll;
|
|
}
|
|
|
|
if (!ReadImageData( address, hFile, &Signature,
|
|
sizeof(Signature) ))
|
|
{
|
|
break;
|
|
}
|
|
|
|
// NB10 or PDB7 signature?
|
|
if (Signature == NB10_SIG ||
|
|
Signature == RSDS_SIG)
|
|
{
|
|
ULONG HdrSize = Signature == NB10_SIG ?
|
|
sizeof(NB10IH) : sizeof(RSDSIH);
|
|
|
|
address += HdrSize;
|
|
|
|
if ((DebugDir.SizeOfData - sizeof(HdrSize)) > MAX_PATH)
|
|
{
|
|
// Something's wrong here. The record should only contain
|
|
// a MAX_PATH path name.
|
|
break;
|
|
}
|
|
|
|
if (DebugDir.SizeOfData - HdrSize > NameSize)
|
|
{
|
|
break;
|
|
}
|
|
if (!ReadImageData(address, hFile, lpName,
|
|
DebugDir.SizeOfData - HdrSize))
|
|
{
|
|
break;
|
|
}
|
|
|
|
_splitpath(lpName, NULL, rgchPath, rgchBase, NULL);
|
|
|
|
// Files are sometimes generated with .pdb appended
|
|
// to the image name rather than replacing the extension
|
|
// of the image name, such as foo.exe.pdb.
|
|
// splitpath only takes off the outermost extension,
|
|
// so check and see if the base already has an extension
|
|
// we recognize.
|
|
PSTR Ext = strrchr(rgchBase, '.');
|
|
if (Ext != NULL &&
|
|
(!strcmp(Ext, ".exe") || !strcmp(Ext, ".dll") ||
|
|
!strcmp(Ext, ".sys")))
|
|
{
|
|
// The base already has an extension so use
|
|
// it as-is.
|
|
strcpy(lpName, rgchBase);
|
|
fCheckDllExtensionInExportTable = !strcmp(Ext, ".dll");
|
|
}
|
|
else if (Characteristics & IMAGE_FILE_DLL)
|
|
{
|
|
strcpy(lpName, rgchBase);
|
|
strcat(lpName, ".dll");
|
|
fCheckDllExtensionInExportTable = TRUE;
|
|
}
|
|
else
|
|
{
|
|
strcpy(lpName, rgchBase);
|
|
strcat(lpName, ".exe");
|
|
}
|
|
|
|
rVal = TRUE;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!rVal || fCheckDllExtensionInExportTable)
|
|
{
|
|
CHAR Char;
|
|
ULONG64 ExportNameRva;
|
|
char FileName[MAX_IMAGE_PATH];
|
|
int x;
|
|
|
|
ExportNameRva = 0;
|
|
|
|
CheckExportTable:
|
|
|
|
// No luck wandering the debug info. Try the export table.
|
|
|
|
if (IsImageMachineType64(pnh32->FileHeader.Machine))
|
|
{
|
|
rva = nh64.OptionalHeader.
|
|
DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress;
|
|
}
|
|
else
|
|
{
|
|
rva = pnh32->OptionalHeader.
|
|
DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress;
|
|
}
|
|
|
|
if (!rva)
|
|
{
|
|
goto Finish;
|
|
}
|
|
|
|
for (i = 0; i < NumberOfSections; i++)
|
|
{
|
|
if (rva >= pSH[i].VirtualAddress &&
|
|
rva < pSH[i].VirtualAddress + pSH[i].SizeOfRawData)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (i >= NumberOfSections)
|
|
{
|
|
goto Finish;
|
|
}
|
|
|
|
if (hFile)
|
|
{
|
|
rva = rva - pSH[i].VirtualAddress + pSH[i].PointerToRawData;
|
|
}
|
|
|
|
if (!ReadImageData( rva + offsetof(IMAGE_EXPORT_DIRECTORY, Name) +
|
|
BaseOfDll, hFile, &ExportNameRva, sizeof(DWORD)))
|
|
{
|
|
goto Finish;
|
|
}
|
|
|
|
if (hFile)
|
|
{
|
|
ExportNameRva = ExportNameRva - pSH[i].VirtualAddress +
|
|
pSH[i].PointerToRawData;
|
|
}
|
|
|
|
ExportNameRva += BaseOfDll;
|
|
|
|
rVal = TRUE;
|
|
x = 0;
|
|
do
|
|
{
|
|
if (!ReadImageData( ExportNameRva,
|
|
hFile, &Char, sizeof(Char)))
|
|
{
|
|
rVal = FALSE;
|
|
break;
|
|
}
|
|
ExportNameRva++;
|
|
FileName[x] = Char;
|
|
x++;
|
|
} while (Char && (x < sizeof(FileName)));
|
|
|
|
if (fCheckDllExtensionInExportTable)
|
|
{
|
|
char rgchExtFromExportTable[_MAX_EXT];
|
|
|
|
_splitpath(FileName, NULL, NULL, NULL, rgchExtFromExportTable);
|
|
if (_stricmp(rgchExtFromExportTable, ".dll"))
|
|
{
|
|
// Export table has something different.
|
|
// Use it with our base name.
|
|
strcpy(lpName + strlen(lpName) - 4,
|
|
rgchExtFromExportTable);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
lpName[0] = 0;
|
|
strncat(lpName, FileName, NameSize - 1);
|
|
}
|
|
}
|
|
|
|
Finish:
|
|
if (pSH)
|
|
{
|
|
free(pSH);
|
|
}
|
|
|
|
return rVal;
|
|
}
|
|
|
|
BOOL
|
|
GetModnameFromImage(ULONG64 BaseOfDll,
|
|
HANDLE hFile,
|
|
LPSTR lpName,
|
|
ULONG NameSize)
|
|
{
|
|
BOOL Status = GetModnameFromImageInternal( BaseOfDll, NULL,
|
|
lpName, NameSize );
|
|
if (!Status && hFile != NULL)
|
|
{
|
|
Status = GetModnameFromImageInternal( BaseOfDll, hFile,
|
|
lpName, NameSize );
|
|
}
|
|
return Status;
|
|
}
|
|
|
|
|
|
BOOL
|
|
GetHeaderInfo(
|
|
IN ULONG64 BaseOfDll,
|
|
OUT LPDWORD CheckSum,
|
|
OUT LPDWORD TimeDateStamp,
|
|
OUT LPDWORD SizeOfImage
|
|
)
|
|
{
|
|
IMAGE_NT_HEADERS32 nh32;
|
|
IMAGE_DOS_HEADER dh;
|
|
ULONG64 address;
|
|
DWORD sig;
|
|
|
|
|
|
address = BaseOfDll;
|
|
|
|
ReadImageData( address, NULL, &dh, sizeof(dh) );
|
|
|
|
if (dh.e_magic == IMAGE_DOS_SIGNATURE) {
|
|
address += dh.e_lfanew;
|
|
}
|
|
|
|
ReadImageData( address, NULL, &sig, sizeof(sig) );
|
|
|
|
if (sig != IMAGE_NT_SIGNATURE) {
|
|
|
|
IMAGE_FILE_HEADER fh;
|
|
ReadImageData( address, NULL, &fh, sizeof(IMAGE_FILE_HEADER) );
|
|
|
|
*CheckSum = 0;
|
|
*TimeDateStamp = fh.TimeDateStamp;
|
|
*SizeOfImage = 0;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
// Attempt to read as a 32bit header, then reread if the image type is 64bit.
|
|
// This works because IMAGE_FILE_HEADER, which is at the start of the IMAGE_NT_HEADERS,
|
|
// is the same on 32bit NT and 64bit NT and IMAGE_NT_HEADER32 <= IMAGE_NT_HEADER64.
|
|
ReadImageData( address, NULL, &nh32, sizeof(nh32) );
|
|
|
|
if (IsImageMachineType64(nh32.FileHeader.Machine)) {
|
|
|
|
// Image is 64bit. Reread as a 64bit structure.
|
|
IMAGE_NT_HEADERS64 nh64;
|
|
|
|
ReadImageData( address, NULL, &nh64, sizeof(nh64) );
|
|
|
|
*CheckSum = nh64.OptionalHeader.CheckSum;
|
|
*TimeDateStamp = nh64.FileHeader.TimeDateStamp;
|
|
*SizeOfImage = nh64.OptionalHeader.SizeOfImage;
|
|
}
|
|
|
|
else {
|
|
*CheckSum = nh32.OptionalHeader.CheckSum;
|
|
*TimeDateStamp = nh32.FileHeader.TimeDateStamp;
|
|
*SizeOfImage = nh32.OptionalHeader.SizeOfImage;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
PCSTR
|
|
PrependPrefixToSymbol( char PrefixedString[],
|
|
PCSTR pString,
|
|
PCSTR *RegString
|
|
)
|
|
{
|
|
if ( RegString )
|
|
{
|
|
*RegString = NULL;
|
|
}
|
|
|
|
PCSTR bangPtr;
|
|
int bang = '!';
|
|
PCSTR Tail;
|
|
|
|
bangPtr = strchr( pString, bang );
|
|
if ( bangPtr )
|
|
{
|
|
Tail = bangPtr + 1;
|
|
}
|
|
else
|
|
{
|
|
Tail = pString;
|
|
}
|
|
|
|
if ( strncmp( Tail, g_Machine->m_SymPrefix, g_Machine->m_SymPrefixLen ) )
|
|
{
|
|
ULONG Loc = (ULONG)(Tail - pString);
|
|
if (Loc > 0)
|
|
{
|
|
memcpy( PrefixedString, pString, Loc );
|
|
}
|
|
memcpy( PrefixedString + Loc, g_Machine->m_SymPrefix,
|
|
g_Machine->m_SymPrefixLen );
|
|
if ( RegString )
|
|
{
|
|
*RegString = &PrefixedString[Loc];
|
|
}
|
|
Loc += g_Machine->m_SymPrefixLen;
|
|
strcpy( &PrefixedString[Loc], Tail );
|
|
return PrefixedString;
|
|
}
|
|
else
|
|
{
|
|
return pString;
|
|
}
|
|
}
|
|
|
|
BOOL
|
|
ForceSymbolCodeAddress(PSYMBOL_INFO Symbol, MachineInfo* Machine)
|
|
{
|
|
ULONG64 Code = Symbol->Address;
|
|
|
|
if (Symbol->Flags & SYMF_FORWARDER)
|
|
{
|
|
char Fwd[2 * MAX_PATH];
|
|
ULONG Read;
|
|
PSTR Sep;
|
|
|
|
// The address of a forwarder entry points to the
|
|
// string name of the function that things are forwarded
|
|
// to. Look up that name and try to get the address
|
|
// from it.
|
|
if (g_Target->ReadVirtual(Symbol->Address, Fwd, sizeof(Fwd),
|
|
&Read) != S_OK ||
|
|
Read < 2)
|
|
{
|
|
ErrOut("Unable to read forwarder string\n");
|
|
return FALSE;
|
|
}
|
|
|
|
Fwd[sizeof(Fwd) - 1] = 0;
|
|
if (!(Sep = strchr(Fwd, '.')))
|
|
{
|
|
ErrOut("Unable to read forwarder string\n");
|
|
return FALSE;
|
|
}
|
|
|
|
*Sep = '!';
|
|
if (GetOffsetFromSym(Fwd, &Code, NULL) != 1)
|
|
{
|
|
ErrOut("Unable to get address of forwarder '%s'\n", Fwd);
|
|
return FALSE;
|
|
}
|
|
}
|
|
else if (Machine->m_ExecTypes[0] == IMAGE_FILE_MACHINE_IA64 &&
|
|
(Symbol->Flags & SYMF_EXPORT))
|
|
{
|
|
// On IA64 the export entries contain the address
|
|
// of the plabel. We want the actual code address
|
|
// so resolve the plabel to its code.
|
|
if (!Machine->GetPrefixedSymbolOffset(Symbol->Address,
|
|
GETPREF_VERBOSE,
|
|
&Code))
|
|
{
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
Symbol->Address = Code;
|
|
return TRUE;
|
|
}
|
|
|
|
/*** GetOffsetFromSym - return offset from symbol specified
|
|
*
|
|
* Purpose:
|
|
* external routine.
|
|
* With the specified symbol, set the pointer to
|
|
* its offset. The variable chSymbolSuffix may
|
|
* be used to append a character to repeat the search
|
|
* if it first fails.
|
|
*
|
|
* Input:
|
|
* pString - pointer to input symbol
|
|
*
|
|
* Output:
|
|
* pOffset - pointer to offset to be set
|
|
*
|
|
* Returns:
|
|
* BOOL value of success
|
|
*
|
|
*************************************************************************/
|
|
|
|
#ifndef _DBGHELP_USER_GENERATED_SYMBOLS_NOTSUPPORTED
|
|
|
|
extern "C" {
|
|
|
|
BOOL
|
|
IMAGEAPI
|
|
SymSetSymWithAddr64(
|
|
IN HANDLE hProcess,
|
|
IN DWORD64 qwAddr,
|
|
IN LPSTR SymString,
|
|
OUT PIMAGEHLP_SYMBOL64 Symbol
|
|
);
|
|
|
|
}
|
|
|
|
#endif // ! _DBGHELP_USER_GENERATED_SYMBOLS_NOTSUPPORTED
|
|
|
|
BOOL
|
|
GetOffsetFromMod(
|
|
PCSTR pString,
|
|
PULONG64 pOffset
|
|
)
|
|
{
|
|
if (!strchr(pString, '!'))
|
|
{
|
|
// Could be a module name
|
|
PDEBUG_IMAGE_INFO pImage = g_CurrentProcess->ImageHead;
|
|
|
|
while (pImage)
|
|
{
|
|
if (!_stricmp(pString, &pImage->ModuleName[0]) ||
|
|
(pImage->OriginalModuleName[0] &&
|
|
!_stricmp(pString, &pImage->OriginalModuleName[0])))
|
|
{
|
|
*pOffset = pImage->BaseOfImage;
|
|
return TRUE;
|
|
}
|
|
pImage = pImage->Next;
|
|
}
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
BOOL
|
|
IgnoreEnumeratedSymbol(class MachineInfo* Machine,
|
|
PSYMBOL_INFO SymInfo)
|
|
{
|
|
ULONG64 Func;
|
|
|
|
//
|
|
// IA64 plabels are publics with the same name
|
|
// as the function they refer to. This causes
|
|
// ambiguity problems as we end up with two
|
|
// hits. The plabel is rarely interesting, though,
|
|
// so just filter them out here so that expressions
|
|
// always evaluate to the function itself.
|
|
//
|
|
|
|
if (Machine->m_ExecTypes[0] != IMAGE_FILE_MACHINE_IA64 ||
|
|
SymInfo->Scope != SymTagPublicSymbol ||
|
|
SymInfo->Flags & SYMF_FUNCTION ||
|
|
!Machine->GetPrefixedSymbolOffset(SymInfo->Address, 0, &Func))
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
PSTR FuncSym;
|
|
|
|
__try
|
|
{
|
|
FuncSym = (PSTR)alloca(MAX_SYMBOL_LEN * 2);
|
|
}
|
|
__except(EXCEPTION_EXECUTE_HANDLER)
|
|
{
|
|
FuncSym = NULL;
|
|
}
|
|
|
|
if (FuncSym == NULL)
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
SYMBOL_INFO LocalSymInfo;
|
|
|
|
// We have to save and restore the original data as
|
|
// dbghelp always uses a single buffer to store all
|
|
// symbol information. The incoming symbol info
|
|
// is going to be wiped out when we
|
|
// call GetSymbolStdCall.
|
|
LocalSymInfo = *SymInfo;
|
|
strcpy(FuncSym + MAX_SYMBOL_LEN, SymInfo->Name);
|
|
|
|
ULONG64 FuncSymDisp;
|
|
|
|
GetSymbolStdCall(Func, FuncSym, MAX_SYMBOL_LEN,
|
|
&FuncSymDisp, NULL);
|
|
|
|
*SymInfo = LocalSymInfo;
|
|
strcpy(SymInfo->Name, FuncSym + MAX_SYMBOL_LEN);
|
|
return FuncSymDisp == 0 && strstr(FuncSym, SymInfo->Name);
|
|
}
|
|
|
|
struct COUNT_SYMBOL_MATCHES
|
|
{
|
|
MachineInfo* Machine;
|
|
SYMBOL_INFO ReturnSymInfo;
|
|
CHAR SymbolNameOverflowBuffer[MAX_SYMBOL_LEN];
|
|
ULONG Matches;
|
|
};
|
|
|
|
BOOL CALLBACK
|
|
CountSymbolMatches(
|
|
PSYMBOL_INFO SymInfo,
|
|
ULONG Size,
|
|
PVOID UserContext
|
|
)
|
|
{
|
|
COUNT_SYMBOL_MATCHES* Context =
|
|
(COUNT_SYMBOL_MATCHES*)UserContext;
|
|
|
|
if (IgnoreEnumeratedSymbol(Context->Machine, SymInfo))
|
|
{
|
|
return TRUE;
|
|
}
|
|
|
|
if (Context->Matches == 1)
|
|
{
|
|
// We already have one match, check if we got a duplicate.
|
|
if ((SymInfo->Address == Context->ReturnSymInfo.Address) &&
|
|
!strcmp(SymInfo->Name, Context->ReturnSymInfo.Name))
|
|
{
|
|
// Looks like the same symbol, ignore it.
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
Context->ReturnSymInfo = *SymInfo;
|
|
if (SymInfo->NameLen < MAX_SYMBOL_LEN)
|
|
{
|
|
strcpy(Context->ReturnSymInfo.Name, SymInfo->Name);
|
|
}
|
|
Context->Matches++;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
ULONG
|
|
MultiSymFromName(IN HANDLE Process,
|
|
IN LPSTR Name,
|
|
IN ULONG64 ImageBase,
|
|
IN MachineInfo* Machine,
|
|
OUT PSYMBOL_INFO Symbol)
|
|
{
|
|
ULONG Matches;
|
|
|
|
RequireCurrentScope();
|
|
|
|
if (ImageBase == 0)
|
|
{
|
|
if (!SymFromName(Process, Name, Symbol))
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
Matches = 1;
|
|
}
|
|
else
|
|
{
|
|
COUNT_SYMBOL_MATCHES Context;
|
|
ULONG MaxName = Symbol->MaxNameLen;
|
|
|
|
Context.Machine = Machine;
|
|
Context.ReturnSymInfo = *Symbol;
|
|
if (Symbol->NameLen < MAX_SYMBOL_LEN)
|
|
{
|
|
strcpy(Context.ReturnSymInfo.Name, Symbol->Name);
|
|
}
|
|
Context.Matches = 0;
|
|
SymEnumSymbols(Process, ImageBase, Name,
|
|
CountSymbolMatches, &Context);
|
|
*Symbol = Context.ReturnSymInfo;
|
|
Symbol->MaxNameLen = MaxName;
|
|
if (Symbol->MaxNameLen > Context.ReturnSymInfo.NameLen)
|
|
{
|
|
strcpy(Symbol->Name, Context.ReturnSymInfo.Name);
|
|
}
|
|
|
|
Matches = Context.Matches;
|
|
}
|
|
|
|
if (Matches == 1 &&
|
|
!ForceSymbolCodeAddress(Symbol, Machine))
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
return Matches;
|
|
}
|
|
|
|
ULONG
|
|
GetOffsetFromSym(PCSTR String,
|
|
PULONG64 Offset,
|
|
PDEBUG_IMAGE_INFO* Image)
|
|
{
|
|
CHAR ModifiedString[MAX_SYMBOL_LEN + 64];
|
|
CHAR Suffix[2];
|
|
SYMBOL_INFO SymInfo = {0};
|
|
ULONG Count;
|
|
|
|
if (Image != NULL)
|
|
{
|
|
*Image = NULL;
|
|
}
|
|
|
|
//
|
|
// We can't do anything without a current process.
|
|
//
|
|
|
|
if (g_CurrentProcess == NULL)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
if ( strlen(String) == 0 )
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
if (GetOffsetFromMod(String, Offset))
|
|
{
|
|
return 1;
|
|
}
|
|
|
|
//
|
|
// If a module name was given look up the module
|
|
// and determine the processor type so that the
|
|
// appropriate machine is used for the following
|
|
// machine-specific operations.
|
|
//
|
|
|
|
PDEBUG_IMAGE_INFO StrImage;
|
|
ULONG64 ImageBase;
|
|
PCSTR ModSep = strchr(String, '!');
|
|
if (ModSep != NULL)
|
|
{
|
|
ULONG Len = (ULONG)(ModSep - String);
|
|
memcpy(ModifiedString, String, Len);
|
|
ModifiedString[Len] = 0;
|
|
StrImage = GetImageByName(g_CurrentProcess, ModifiedString,
|
|
INAME_MODULE);
|
|
if (Image != NULL)
|
|
{
|
|
*Image = StrImage;
|
|
}
|
|
ImageBase = StrImage ? StrImage->BaseOfImage : 0;
|
|
}
|
|
else
|
|
{
|
|
StrImage = NULL;
|
|
ImageBase = 0;
|
|
}
|
|
|
|
MachineInfo* Machine = g_Machine;
|
|
|
|
if (StrImage != NULL)
|
|
{
|
|
Machine = MachineTypeInfo(ModuleMachineType(g_CurrentProcess,
|
|
StrImage->BaseOfImage));
|
|
if (Machine == NULL)
|
|
{
|
|
Machine = g_Machine;
|
|
}
|
|
}
|
|
|
|
if ( g_PrefixSymbols && Machine->m_SymPrefix != NULL )
|
|
{
|
|
PCSTR PreString;
|
|
PCSTR RegString;
|
|
|
|
PreString = PrependPrefixToSymbol( ModifiedString, String,
|
|
&RegString );
|
|
if ( Count =
|
|
MultiSymFromName( g_CurrentProcess->Handle, (PSTR)PreString,
|
|
ImageBase, Machine, &SymInfo ) )
|
|
{
|
|
*Offset = SymInfo.Address;
|
|
goto GotOffsetSuccess;
|
|
}
|
|
if ( (PreString != String) &&
|
|
(Count =
|
|
MultiSymFromName( g_CurrentProcess->Handle, (PSTR)String,
|
|
ImageBase, Machine, &SymInfo ) ) )
|
|
{
|
|
// Ambiguous plabels shouldn't be further resolved,
|
|
// so just return the information for the plabel.
|
|
if (Count > 1)
|
|
{
|
|
*Offset = SymInfo.Address;
|
|
goto GotOffsetSuccess;
|
|
}
|
|
|
|
if (Machine->GetPrefixedSymbolOffset(SymInfo.Address,
|
|
GETPREF_VERBOSE,
|
|
Offset))
|
|
{
|
|
#ifndef _DBGHELP_USER_GENERATED_SYMBOLS_NOTSUPPORTED
|
|
if ( ! SymSetSymWithAddr64( g_CurrentProcess->Handle,
|
|
*Offset, (PSTR)RegString,
|
|
g_Sym ) )
|
|
{
|
|
DWORD LastError = GetLastError();
|
|
|
|
if ( LastError != ERROR_ALREADY_EXISTS )
|
|
{
|
|
ErrOut("GetOffsetFromSym: "
|
|
"%s registration in dbghelp: "
|
|
"FAILED!!!, lerr:0x%lx\n",
|
|
RegString, LastError );
|
|
}
|
|
#endif // !_DBGHELP_USER_GENERATED_SYMBOLS_NOTSUPPORTED
|
|
}
|
|
else
|
|
{
|
|
// This symbol doesn't appear to actually
|
|
// be a plabel so just use the symbol address.
|
|
*Offset = SymInfo.Address;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
*Offset = SymInfo.Address;
|
|
}
|
|
goto GotOffsetSuccess;
|
|
}
|
|
}
|
|
else if (Count =
|
|
MultiSymFromName( g_CurrentProcess->Handle, (PSTR)String,
|
|
ImageBase, Machine, &SymInfo ))
|
|
{
|
|
*Offset = SymInfo.Address;
|
|
goto GotOffsetSuccess;
|
|
}
|
|
|
|
if (g_SymbolSuffix != 'n')
|
|
{
|
|
strcpy( ModifiedString, String );
|
|
Suffix[0] = g_SymbolSuffix;
|
|
Suffix[1] = '\0';
|
|
strcat( ModifiedString, Suffix );
|
|
if (Count =
|
|
MultiSymFromName( g_CurrentProcess->Handle, ModifiedString,
|
|
ImageBase, Machine, &SymInfo ))
|
|
{
|
|
*Offset = SymInfo.Address;
|
|
goto GotOffsetSuccess;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
|
|
GotOffsetSuccess:
|
|
TranslateAddress(SymInfo.Flags, SymInfo.Register, Offset, &SymInfo.Value);
|
|
if (SymInfo.Flags & SYMF_REGISTER)
|
|
{
|
|
*Offset = SymInfo.Value;
|
|
}
|
|
return Count;
|
|
}
|
|
|
|
void
|
|
CreateModuleNameFromPath(LPSTR ImagePath, LPSTR ModuleName)
|
|
{
|
|
PSTR Scan;
|
|
|
|
ModuleName[0] = 0;
|
|
strncat( ModuleName, PathTail(ImagePath), MAX_MODULE - 1 );
|
|
Scan = strchr( ModuleName, '.' );
|
|
if (Scan != NULL)
|
|
{
|
|
*Scan = '\0';
|
|
}
|
|
}
|
|
|
|
void
|
|
GetAdjacentSymOffsets(
|
|
ULONG64 addrStart,
|
|
PULONG64 prevOffset,
|
|
PULONG64 nextOffset
|
|
)
|
|
{
|
|
DWORD64 Displacement;
|
|
|
|
//
|
|
// assume failure
|
|
//
|
|
*prevOffset = 0;
|
|
*nextOffset = (ULONG64) -1;
|
|
|
|
//
|
|
// get the symbol for the initial address
|
|
//
|
|
if (!SymGetSymFromAddr64( g_CurrentProcess->Handle, addrStart, &Displacement, g_SymStart )) {
|
|
return;
|
|
}
|
|
|
|
*prevOffset = g_SymStart->Address;
|
|
|
|
if (SymGetSymNext64( g_CurrentProcess->Handle, g_SymStart )) {
|
|
*nextOffset = g_SymStart->Address;
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
BOOL
|
|
SymbolCallbackFunction(
|
|
HANDLE hProcess,
|
|
ULONG ActionCode,
|
|
ULONG64 CallbackData,
|
|
ULONG64 UserContext
|
|
)
|
|
{
|
|
PIMAGEHLP_DEFERRED_SYMBOL_LOAD64 idsl;
|
|
PIMAGEHLP_CBA_READ_MEMORY prm;
|
|
PIMAGEHLP_CBA_EVENT evt;
|
|
PDEBUG_IMAGE_INFO pImage;
|
|
IMAGEHLP_MODULE64 mi;
|
|
PUCHAR p;
|
|
ULONG i;
|
|
ULONG OldSymOptions;
|
|
|
|
idsl = (PIMAGEHLP_DEFERRED_SYMBOL_LOAD64) CallbackData;
|
|
|
|
switch ( ActionCode )
|
|
{
|
|
case CBA_DEBUG_INFO:
|
|
assert(CallbackData && *(LPSTR)CallbackData);
|
|
dprintf("%s", (LPSTR)CallbackData);
|
|
break;
|
|
|
|
case CBA_EVENT:
|
|
evt = (PIMAGEHLP_CBA_EVENT)CallbackData;
|
|
assert(evt);
|
|
if (evt->desc && *evt->desc)
|
|
dprintf("%s", evt->desc);
|
|
break;
|
|
|
|
case CBA_DEFERRED_SYMBOL_LOAD_CANCEL:
|
|
if (g_EngStatus & (ENG_STATUS_USER_INTERRUPT |
|
|
ENG_STATUS_PENDING_BREAK_IN))
|
|
{
|
|
return TRUE;
|
|
}
|
|
break;
|
|
|
|
case CBA_DEFERRED_SYMBOL_LOAD_START:
|
|
pImage = g_CurrentProcess->ImageHead;
|
|
while (pImage)
|
|
{
|
|
if (idsl->BaseOfImage == pImage->BaseOfImage)
|
|
{
|
|
_strlwr( idsl->FileName );
|
|
VerbOut( "Loading symbols for %s %16s -> ",
|
|
FormatAddr64(idsl->BaseOfImage),
|
|
idsl->FileName
|
|
);
|
|
return TRUE;
|
|
}
|
|
pImage = pImage->Next;
|
|
}
|
|
break;
|
|
|
|
case CBA_DEFERRED_SYMBOL_LOAD_FAILURE:
|
|
if (IS_KERNEL_TARGET() &&
|
|
idsl->SizeOfStruct >= FIELD_OFFSET(IMAGEHLP_DEFERRED_SYMBOL_LOAD,
|
|
Reparse))
|
|
{
|
|
i = 0;
|
|
|
|
if (strncmp(idsl->FileName, "dump_", sizeof("dump_")-1) == 0)
|
|
{
|
|
i = sizeof("dump_")-1;
|
|
}
|
|
|
|
if (strncmp(idsl->FileName, "hiber_", sizeof("hiber_")-1) == 0)
|
|
{
|
|
i = sizeof("hiber_")-1;
|
|
}
|
|
|
|
if (i)
|
|
{
|
|
if (_stricmp (idsl->FileName+i, "scsiport.sys") == 0)
|
|
{
|
|
strcpy (idsl->FileName, "diskdump.sys");
|
|
}
|
|
else
|
|
{
|
|
strcpy(idsl->FileName, idsl->FileName+i);
|
|
}
|
|
|
|
idsl->Reparse = TRUE;
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
if (idsl->FileName && *idsl->FileName)
|
|
{
|
|
VerbOut( "*** Error: could not load symbols for %s\n",
|
|
idsl->FileName );
|
|
}
|
|
else
|
|
{
|
|
VerbOut( "*** Error: could not load symbols [MODNAME UNKNOWN]\n");
|
|
}
|
|
break;
|
|
|
|
case CBA_DEFERRED_SYMBOL_LOAD_COMPLETE:
|
|
pImage = g_CurrentProcess->ImageHead;
|
|
|
|
// Do not load unqualified symbols in this callback sine this
|
|
// could result in stack owerflow
|
|
OldSymOptions = SymGetOptions();
|
|
SymSetOptions(OldSymOptions | SYMOPT_NO_UNQUALIFIED_LOADS);
|
|
|
|
while (pImage)
|
|
{
|
|
if ((idsl->BaseOfImage == pImage->BaseOfImage) ||
|
|
(pImage->BaseOfImage == 0))
|
|
{
|
|
VerbOut( "%s\n", idsl->FileName );
|
|
pImage->GoodCheckSum = DII_GOOD_CHECKSUM;
|
|
|
|
//
|
|
// If we had a 0 timestamp for the image, try to update it
|
|
// from the image since for NT4 - XP, the kernel
|
|
// does not report timestamps in the initial symbol load
|
|
// module
|
|
//
|
|
|
|
if (pImage->BaseOfImage &&
|
|
(pImage->TimeDateStamp == 0))
|
|
{
|
|
DWORD CheckSum;
|
|
DWORD TimeDateStamp;
|
|
DWORD SizeOfImage;
|
|
if (GetHeaderInfo(pImage->BaseOfImage,
|
|
&CheckSum,
|
|
&TimeDateStamp,
|
|
&SizeOfImage))
|
|
{
|
|
pImage->TimeDateStamp = TimeDateStamp;
|
|
}
|
|
}
|
|
|
|
if ((idsl->TimeDateStamp == 0) ||
|
|
(pImage->TimeDateStamp == 0) ||
|
|
(pImage->TimeDateStamp == UNKNOWN_TIMESTAMP))
|
|
{
|
|
dprintf( "*** WARNING: Unable to verify "
|
|
"timestamp for %s\n", idsl->FileName );
|
|
pImage->GoodCheckSum = DII_UNKNOWN_TIMESTAMP;
|
|
}
|
|
else
|
|
{
|
|
if ((idsl->CheckSum == 0) ||
|
|
(pImage->CheckSum == 0) ||
|
|
(pImage->CheckSum == UNKNOWN_CHECKSUM))
|
|
{
|
|
dprintf( "*** WARNING: Unable to verify "
|
|
"checksum for %s\n", idsl->FileName );
|
|
pImage->GoodCheckSum = DII_UNKNOWN_CHECKSUM;
|
|
}
|
|
else if (idsl->CheckSum != pImage->CheckSum)
|
|
{
|
|
pImage->GoodCheckSum = DII_BAD_CHECKSUM;
|
|
|
|
if (g_TargetMachineType == IMAGE_FILE_MACHINE_I386)
|
|
{
|
|
if (IS_USER_TARGET() ||
|
|
g_TargetNumberProcessors == 1)
|
|
{
|
|
//
|
|
// See if this is an MP image with the
|
|
// lock table removed by setup. If
|
|
// it is and the timestamps match, don't
|
|
// print the invalid checksum warning.
|
|
//
|
|
|
|
char szFileName[_MAX_FNAME];
|
|
_splitpath(idsl->FileName, NULL, NULL,
|
|
szFileName, NULL);
|
|
|
|
if ((!_stricmp(szFileName, "kernel32") ||
|
|
(IS_KERNEL_TARGET() &&
|
|
!_stricmp(szFileName, "win32k")) ||
|
|
!_stricmp(szFileName, "wow32") ||
|
|
!_stricmp(szFileName, "ntvdm") ||
|
|
!_stricmp(szFileName, "ntdll")) &&
|
|
(pImage->TimeDateStamp ==
|
|
idsl->TimeDateStamp))
|
|
{
|
|
pImage->GoodCheckSum = DII_GOOD_CHECKSUM;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (pImage->GoodCheckSum == DII_BAD_CHECKSUM)
|
|
{
|
|
//
|
|
// Only print the message if the timestamps
|
|
// are wrong.
|
|
//
|
|
|
|
if (pImage->TimeDateStamp != idsl->TimeDateStamp)
|
|
{
|
|
dprintf("*** WARNING: symbols timestamp "
|
|
"is wrong 0x%08x 0x%08x for %s\n",
|
|
pImage->TimeDateStamp,
|
|
idsl->TimeDateStamp,
|
|
idsl->FileName);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
mi.SizeOfStruct = sizeof(mi);
|
|
if (SymGetModuleInfo64( g_CurrentProcess->Handle,
|
|
idsl->BaseOfImage, &mi ))
|
|
{
|
|
if (mi.SymType == SymExport)
|
|
{
|
|
WarnOut("*** ERROR: Symbol file could not be found."
|
|
" Defaulted to export symbols for %s - \n",
|
|
idsl->FileName);
|
|
}
|
|
if (mi.SymType == SymNone)
|
|
{
|
|
WarnOut("*** ERROR: Module load completed but "
|
|
"symbols could not be loaded for %s\n",
|
|
idsl->FileName);
|
|
}
|
|
}
|
|
NotifyChangeSymbolState(DEBUG_CSS_LOADS,
|
|
idsl->BaseOfImage, g_CurrentProcess);
|
|
SymSetOptions(OldSymOptions);
|
|
return TRUE;
|
|
}
|
|
pImage = pImage->Next;
|
|
}
|
|
VerbOut( "\n" );
|
|
NotifyChangeSymbolState(DEBUG_CSS_LOADS,
|
|
idsl->BaseOfImage,
|
|
g_CurrentProcess);
|
|
SymSetOptions(OldSymOptions);
|
|
break;
|
|
|
|
case CBA_SYMBOLS_UNLOADED:
|
|
VerbOut( "Symbols unloaded for %s %s\n",
|
|
FormatAddr64(idsl->BaseOfImage),
|
|
idsl->FileName
|
|
);
|
|
break;
|
|
|
|
case CBA_READ_MEMORY:
|
|
prm = (PIMAGEHLP_CBA_READ_MEMORY)CallbackData;
|
|
return g_Target->ReadVirtual(prm->addr,
|
|
prm->buf,
|
|
prm->bytes,
|
|
prm->bytesread) == S_OK;
|
|
|
|
case CBA_SET_OPTIONS:
|
|
// Symbol options are set through the interface
|
|
// so the debugger generally knows about them
|
|
// already. The only flag that we want to check
|
|
// here is the debug flag since it can be changed
|
|
// through !sym. There is no need to notify
|
|
// about this as it's only an internal flag.
|
|
g_SymOptions = (g_SymOptions & ~SYMOPT_DEBUG) |
|
|
(*(PULONG)CallbackData & SYMOPT_DEBUG);
|
|
break;
|
|
|
|
default:
|
|
return FALSE;
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
BOOL
|
|
ValidatePathComponent(PCSTR Part)
|
|
{
|
|
if (strlen(Part) == 0)
|
|
{
|
|
return FALSE;
|
|
}
|
|
else if (!_strnicmp(Part, "SYMSRV*", 7) ||
|
|
IsUrlPathComponent(Part))
|
|
{
|
|
// No easy way to validate symbol server or URL paths.
|
|
// They're virtually always network references,
|
|
// so just disallow all such usage when net
|
|
// access isn't allowed.
|
|
if (g_EngOptions & DEBUG_ENGOPT_DISALLOW_NETWORK_PATHS)
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
else
|
|
{
|
|
DWORD Attrs;
|
|
DWORD OldMode;
|
|
char Expand[MAX_PATH];
|
|
|
|
// Otherwise make sure this is a valid directory.
|
|
if (!ExpandEnvironmentStrings(Part, Expand, sizeof(Expand)))
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
if (g_EngOptions & DEBUG_ENGOPT_DISALLOW_NETWORK_PATHS)
|
|
{
|
|
// Don't call GetFileAttributes when network paths
|
|
// are disabled as net operations may cause deadlocks.
|
|
if (NetworkPathCheck(Expand) != ERROR_SUCCESS)
|
|
{
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
// We can still get to this point when debugging CSR
|
|
// if the user has explicitly allowed net paths.
|
|
// This check isn't important enough to risk a hang.
|
|
if (SYSTEM_PROCESSES())
|
|
{
|
|
return TRUE;
|
|
}
|
|
|
|
OldMode = SetErrorMode(SEM_FAILCRITICALERRORS);
|
|
|
|
Attrs = GetFileAttributes(Expand);
|
|
|
|
SetErrorMode(OldMode);
|
|
return Attrs != 0xffffffff && (Attrs & FILE_ATTRIBUTE_DIRECTORY);
|
|
}
|
|
}
|
|
|
|
VOID
|
|
FeedSymPath(
|
|
LPSTR pSymbolSearchPath,
|
|
LPSTR raw
|
|
)
|
|
{
|
|
DWORD dw;
|
|
LPSTR rawbuf;
|
|
LPSTR p;
|
|
BOOL bAppend;
|
|
|
|
if (!raw)
|
|
{
|
|
return;
|
|
}
|
|
|
|
p = strtok(raw, ";");
|
|
while (p)
|
|
{
|
|
bAppend = FALSE;
|
|
|
|
// Check and see if this string is already in the path.
|
|
// If it is, don't add it again.
|
|
PSTR Dup = strstr(pSymbolSearchPath, p);
|
|
if (Dup != NULL)
|
|
{
|
|
PSTR DupEnd = Dup + strlen(p);
|
|
if ((Dup == pSymbolSearchPath || Dup[-1] == ';') &&
|
|
(*DupEnd == 0 || *DupEnd == ';'))
|
|
{
|
|
p = strtok(NULL, ";");
|
|
continue;
|
|
}
|
|
}
|
|
|
|
bAppend = ValidatePathComponent(p);
|
|
if (bAppend)
|
|
{
|
|
if (*pSymbolSearchPath)
|
|
{
|
|
strcat(pSymbolSearchPath, ";");
|
|
}
|
|
strcat(pSymbolSearchPath, p);
|
|
}
|
|
else
|
|
{
|
|
WarnOut("WARNING: %s is not accessible, ignoring\n", p);
|
|
}
|
|
|
|
p = strtok(NULL, ";");
|
|
}
|
|
}
|
|
|
|
void
|
|
SetSymbolSearchPath(PPROCESS_INFO Process)
|
|
{
|
|
LPSTR lpExePathEnv;
|
|
size_t cbExePath;
|
|
|
|
LPSTR lpSymPathEnv;
|
|
LPSTR lpAltSymPathEnv;
|
|
LPSTR lpSymPath;
|
|
size_t cbSymPath;
|
|
LPSTR NewMem;
|
|
|
|
//
|
|
// Load the Binary path (needed for triage dumps)
|
|
//
|
|
|
|
// No clue why this or the next is 18 ...
|
|
cbExePath = 18;
|
|
|
|
if (g_ExecutableImageSearchPath)
|
|
{
|
|
cbExePath += strlen(g_ExecutableImageSearchPath) + 1;
|
|
}
|
|
|
|
if (lpExePathEnv = getenv("_NT_EXECUTABLE_IMAGE_PATH"))
|
|
{
|
|
cbExePath += strlen(lpExePathEnv) + 1;
|
|
}
|
|
|
|
NewMem = (char*)realloc(g_ExecutableImageSearchPath, cbExePath);
|
|
if (!NewMem)
|
|
{
|
|
ErrOut("Not enough memory to allocate/initialize "
|
|
"ExecutableImageSearchPath");
|
|
return;
|
|
}
|
|
if (!g_ExecutableImageSearchPath)
|
|
{
|
|
*NewMem = 0;
|
|
}
|
|
g_ExecutableImageSearchPath = NewMem;
|
|
|
|
FeedSymPath(g_ExecutableImageSearchPath, lpExePathEnv);
|
|
|
|
//
|
|
// Load symbol Path
|
|
//
|
|
|
|
cbSymPath = 18;
|
|
if (g_SymbolSearchPath)
|
|
{
|
|
cbSymPath += strlen(g_SymbolSearchPath) + 1;
|
|
}
|
|
if (lpSymPathEnv = getenv("_NT_SYMBOL_PATH"))
|
|
{
|
|
cbSymPath += strlen(lpSymPathEnv) + 1;
|
|
}
|
|
if (lpAltSymPathEnv = getenv("_NT_ALT_SYMBOL_PATH"))
|
|
{
|
|
cbSymPath += strlen(lpAltSymPathEnv) + 1;
|
|
}
|
|
|
|
NewMem = (char*)realloc(g_SymbolSearchPath, cbSymPath);
|
|
if (!NewMem)
|
|
{
|
|
ErrOut("Not enough memory to allocate/initialize "
|
|
"SymbolSearchPath");
|
|
return;
|
|
}
|
|
if (!g_SymbolSearchPath)
|
|
{
|
|
*NewMem = 0;
|
|
}
|
|
g_SymbolSearchPath = NewMem;
|
|
|
|
FeedSymPath(g_SymbolSearchPath, lpAltSymPathEnv);
|
|
FeedSymPath(g_SymbolSearchPath, lpSymPathEnv);
|
|
|
|
SymSetSearchPath( Process->Handle, g_SymbolSearchPath );
|
|
|
|
dprintf("Symbol search path is: %s\n",
|
|
*g_SymbolSearchPath ?
|
|
g_SymbolSearchPath :
|
|
"*** Invalid *** : Verify _NT_SYMBOL_PATH setting" );
|
|
|
|
if (g_ExecutableImageSearchPath)
|
|
{
|
|
dprintf("Executable search path is: %s\n",
|
|
g_ExecutableImageSearchPath);
|
|
}
|
|
}
|
|
|
|
BOOL
|
|
SetCurrentScope(
|
|
IN PDEBUG_STACK_FRAME ScopeFrame,
|
|
IN OPTIONAL PVOID ScopeContext,
|
|
IN ULONG ScopeContextSize
|
|
)
|
|
{
|
|
BOOL ScopeChanged;
|
|
PDEBUG_SCOPE Scope = &g_ScopeBuffer;
|
|
|
|
if (Scope->State == ScopeDefaultLazy)
|
|
{
|
|
// Its not a lazy scope now
|
|
Scope->State = ScopeDefault;
|
|
}
|
|
|
|
ScopeChanged = SymSetContext(g_CurrentProcess->Handle,
|
|
(PIMAGEHLP_STACK_FRAME) ScopeFrame,
|
|
ScopeContext);
|
|
|
|
if (ScopeContext && (sizeof(Scope->Context) >= ScopeContextSize))
|
|
{
|
|
memcpy(&Scope->Context, ScopeContext, ScopeContextSize);
|
|
Scope->ContextState = MCTX_FULL;
|
|
Scope->State = ScopeFromContext;
|
|
NotifyChangeDebuggeeState(DEBUG_CDS_REGISTERS, DEBUG_ANY_ID);
|
|
}
|
|
|
|
Scope->LocalsChanged = ScopeChanged;
|
|
|
|
if (ScopeChanged ||
|
|
(ScopeFrame->FrameOffset != Scope->Frame.FrameOffset))
|
|
{
|
|
Scope->Frame = *ScopeFrame;
|
|
Scope->LocalsChanged = TRUE;
|
|
if (ScopeFrame->FuncTableEntry)
|
|
{
|
|
// Cache the FPO data since the pointer is only temporary
|
|
Scope->CachedFpo =
|
|
*((PFPO_DATA) ScopeFrame->FuncTableEntry);
|
|
Scope->Frame.FuncTableEntry =
|
|
(ULONG64) &Scope->CachedFpo;
|
|
}
|
|
NotifyChangeSymbolState(DEBUG_CSS_SCOPE, 0, g_CurrentProcess);
|
|
}
|
|
else
|
|
{
|
|
Scope->Frame = *ScopeFrame;
|
|
if (ScopeFrame->FuncTableEntry)
|
|
{
|
|
// Cache the FPO data since the pointer is only temporary
|
|
Scope->CachedFpo =
|
|
*((PFPO_DATA) ScopeFrame->FuncTableEntry);
|
|
Scope->Frame.FuncTableEntry =
|
|
(ULONG64) &Scope->CachedFpo;
|
|
}
|
|
}
|
|
|
|
return ScopeChanged;
|
|
}
|
|
|
|
BOOL
|
|
ResetCurrentScopeLazy(void)
|
|
{
|
|
PDEBUG_SCOPE Scope = &g_ScopeBuffer;
|
|
if (Scope->State == ScopeFromContext)
|
|
{
|
|
NotifyChangeDebuggeeState(DEBUG_CDS_REGISTERS, DEBUG_ANY_ID);
|
|
}
|
|
|
|
Scope->State = ScopeDefaultLazy;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
BOOL
|
|
ResetCurrentScope(void)
|
|
{
|
|
DEBUG_STACK_FRAME LocalFrame;
|
|
PDEBUG_SCOPE Scope = &g_ScopeBuffer;
|
|
|
|
if (Scope->State == ScopeFromContext)
|
|
{
|
|
NotifyChangeDebuggeeState(DEBUG_CDS_REGISTERS, DEBUG_ANY_ID);
|
|
}
|
|
|
|
Scope->State = ScopeDefault;
|
|
|
|
ZeroMemory(&LocalFrame, sizeof(LocalFrame));
|
|
|
|
// At the initial kernel load the system is only partially
|
|
// initialized and is very sensitive to bad memory reads.
|
|
// Stack traces can cause reads through unusual memory areas
|
|
// so it's best to avoid them at this time. This isn't
|
|
// much of a problem since users don't usually expect a locals
|
|
// context at this point.
|
|
if ((IS_USER_TARGET() ||
|
|
(g_EngStatus & ENG_STATUS_AT_INITIAL_MODULE_LOAD) == 0) &&
|
|
IS_CONTEXT_ACCESSIBLE())
|
|
{
|
|
if (!StackTrace(0, 0, 0, &LocalFrame, 1, 0, 0, TRUE))
|
|
{
|
|
ADDR Addr;
|
|
g_Machine->GetPC(&Addr);
|
|
LocalFrame.InstructionOffset = Addr.off;
|
|
}
|
|
}
|
|
|
|
return SetCurrentScope(&LocalFrame, NULL, 0);
|
|
}
|
|
|
|
void
|
|
ListUnloadedModules(ULONG Flags, PSTR Pattern)
|
|
{
|
|
UnloadedModuleInfo* Unl;
|
|
|
|
g_NumUnloadedModules = 0;
|
|
if (!IS_KERNEL_TARGET())
|
|
{
|
|
return;
|
|
}
|
|
|
|
Unl = g_Target->GetUnloadedModuleInfo();
|
|
if (Unl == NULL || Unl->Initialize() != S_OK)
|
|
{
|
|
ErrOut("Unable to examine unloaded module list\n");
|
|
return;
|
|
}
|
|
|
|
char UnlName[MAX_UNLOADED_NAME_LENGTH / sizeof(WCHAR) + 1];
|
|
DEBUG_MODULE_PARAMETERS Params;
|
|
|
|
if (Flags & LUM_OUTPUT)
|
|
{
|
|
dprintf("Unloaded modules:\n");
|
|
}
|
|
|
|
while (Unl->GetEntry(UnlName, &Params) == S_OK)
|
|
{
|
|
g_NumUnloadedModules++;
|
|
|
|
if (Pattern != NULL &&
|
|
!MatchPattern(UnlName, Pattern))
|
|
{
|
|
continue;
|
|
}
|
|
|
|
if (Flags & LUM_OUTPUT_TERSE)
|
|
{
|
|
dprintf(".");
|
|
continue;
|
|
}
|
|
|
|
if (Flags & LUM_OUTPUT)
|
|
{
|
|
dprintf("%s %s %-8s",
|
|
FormatAddr64(Params.Base),
|
|
FormatAddr64(Params.Base + Params.Size),
|
|
UnlName);
|
|
}
|
|
|
|
if (Flags & ( LUM_OUTPUT_VERBOSE | LUM_OUTPUT_TIMESTAMP))
|
|
{
|
|
PSTR TimeDateStr = TimeToStr(Params.TimeDateStamp);
|
|
|
|
dprintf(" Timestamp: %s (%08X)",
|
|
TimeDateStr, Params.TimeDateStamp);
|
|
}
|
|
|
|
dprintf("\n");
|
|
}
|
|
|
|
dprintf("\n");
|
|
}
|
|
|
|
ULONG
|
|
ModuleMachineType(PPROCESS_INFO Process, ULONG64 Offset)
|
|
{
|
|
ULONG64 Base = SymGetModuleBase64(Process->Handle, Offset);
|
|
if (Base == 0)
|
|
{
|
|
return IMAGE_FILE_MACHINE_UNKNOWN;
|
|
}
|
|
|
|
PPROCESS_INFO OldCur = g_CurrentProcess;
|
|
g_CurrentProcess = Process;
|
|
|
|
ULONG Machine = IMAGE_FILE_MACHINE_UNKNOWN;
|
|
IMAGE_DOS_HEADER DosHdr;
|
|
IMAGE_NT_HEADERS64 NtHdr;
|
|
ULONG Done;
|
|
|
|
if (g_Target->ReadVirtual(Base, &DosHdr, sizeof(DosHdr), &Done) == S_OK &&
|
|
Done == sizeof(DosHdr) &&
|
|
DosHdr.e_magic == IMAGE_DOS_SIGNATURE &&
|
|
g_Target->ReadVirtual(Base + DosHdr.e_lfanew, &NtHdr,
|
|
FIELD_OFFSET(IMAGE_NT_HEADERS64,
|
|
FileHeader.NumberOfSections),
|
|
&Done) == S_OK &&
|
|
Done == FIELD_OFFSET(IMAGE_NT_HEADERS64,
|
|
FileHeader.NumberOfSections) &&
|
|
NtHdr.Signature == IMAGE_NT_SIGNATURE &&
|
|
MachineTypeIndex(NtHdr.FileHeader.Machine) != MACHIDX_COUNT)
|
|
{
|
|
Machine = NtHdr.FileHeader.Machine;
|
|
}
|
|
|
|
g_CurrentProcess = OldCur;
|
|
return Machine;
|
|
}
|
|
|
|
ULONG
|
|
IsInFastSyscall(ULONG64 Addr, PULONG64 Base)
|
|
{
|
|
if (g_TargetMachineType != IMAGE_FILE_MACHINE_I386 ||
|
|
g_TargetPlatformId != VER_PLATFORM_WIN32_NT ||
|
|
g_SystemVersion < NT_SVER_W2K_WHISTLER)
|
|
{
|
|
return FSC_NONE;
|
|
}
|
|
|
|
ULONG64 FastBase = g_TargetBuildNumber >= 2412 ?
|
|
X86_SHARED_SYSCALL_BASE_GTE2412 :
|
|
X86_SHARED_SYSCALL_BASE_LT2412;
|
|
|
|
if (Addr >= FastBase &&
|
|
Addr < (FastBase + X86_SHARED_SYSCALL_SIZE))
|
|
{
|
|
*Base = FastBase;
|
|
return FSC_FOUND;
|
|
}
|
|
|
|
return FSC_NONE;
|
|
}
|
|
|
|
BOOL
|
|
ShowFunctionParameters(PDEBUG_STACK_FRAME StackFrame,
|
|
PSTR SymBuf, ULONG64 Displacement)
|
|
{
|
|
SYM_DUMP_PARAM_EX SymFunction = {0};
|
|
ULONG Status = 0;
|
|
PDEBUG_SCOPE Scope = GetCurrentScope();
|
|
DEBUG_SCOPE SavScope = *Scope;
|
|
|
|
SymFunction.size = sizeof(SYM_DUMP_PARAM_EX);
|
|
// SymFunction.sName = (PUCHAR) SymBuf;
|
|
SymFunction.addr = StackFrame->InstructionOffset;
|
|
SymFunction.Options = DBG_DUMP_COMPACT_OUT | DBG_DUMP_FUNCTION_FORMAT;
|
|
|
|
// SetCurrentScope to this function
|
|
SymSetContext(g_CurrentProcess->Handle,
|
|
(PIMAGEHLP_STACK_FRAME) StackFrame, NULL);
|
|
Scope->Frame = *StackFrame;
|
|
if (StackFrame->FuncTableEntry)
|
|
{
|
|
// Cache the FPO data since the pointer is only temporary
|
|
Scope->CachedFpo = *((PFPO_DATA) StackFrame->FuncTableEntry);
|
|
Scope->Frame.FuncTableEntry =
|
|
(ULONG64) &Scope->CachedFpo;
|
|
}
|
|
|
|
if (!SymbolTypeDumpNew(&SymFunction, &Status) &&
|
|
!Status)
|
|
{
|
|
Status = TRUE;
|
|
}
|
|
|
|
g_ScopeBuffer = SavScope;
|
|
SymSetContext(g_CurrentProcess->Handle,
|
|
(PIMAGEHLP_STACK_FRAME) &Scope->Frame, NULL);
|
|
|
|
return !Status;
|
|
}
|