//---------------------------------------------------------------------------- // // Symbol-handling routines. // // Copyright (C) Microsoft Corporation, 1997-2001. // //---------------------------------------------------------------------------- #include "ntsdp.hpp" #include #include #include 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 (%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 (!). PeekChar(); Start = g_CurCmd; Image = ParseModuleName(&ModSpecified); ModEnd = g_CurCmd; ch = PeekChar(); // Special case the command "x !" 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, "", 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] : ""); 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] : ""); } 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; }