/* $Id: kDbgModWinDbgHelp.cpp 3531 2007-08-20 03:52:19Z bird $ */ /** @file * kDbg - The Debug Info Reader, DbgHelp Based Reader. */ /* * Copyright (c) 2006-2007 knut st. osmundsen * * This file is part of kLIBC. * * kLIBC is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * kLIBC is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with kLIBC; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * */ /******************************************************************************* * Header Files * *******************************************************************************/ #include #define _IMAGEHLP64 #include #include /* alloca */ #include "kDbgInternal.h" /******************************************************************************* * Global Variables * *******************************************************************************/ /** The dbghelp.dll module handle. */ static HMODULE g_hDbgHelp = NULL; /** Pointer to the dbhelp.dll SymInitialize function. */ static BOOL (WINAPI *g_pfnSymInitialize)(IN HANDLE,IN LPSTR,IN BOOL); /** Pointer to the dbhelp.dll SymCleanup function. */ static BOOL (WINAPI *g_pfnSymCleanup)(IN HANDLE); /** Pointer to the dbhelp.dll SymSetOptions function. */ static DWORD (WINAPI *g_pfnSymSetOptions)(IN DWORD); /** Pointer to the dbhelp.dll SymLoadModule64 function. */ static DWORD64 (WINAPI *g_pfnSymLoadModule64)(IN HANDLE, IN HANDLE, IN PCSTR, IN PCSTR ModuleName, IN DWORD64, IN DWORD); /** Pointer to the dbhelp.dll SymFromAddr function. */ static DWORD (WINAPI *g_pfnSymFromAddr)(IN HANDLE, IN DWORD64, OUT PDWORD64, OUT PSYMBOL_INFO); /** Pointer to the dbhelp.dll SymGetLineFromAddr64 function. */ static DWORD (WINAPI *g_pfnSymGetLineFromAddr64)(IN HANDLE, IN DWORD64, OUT PDWORD64, OUT PIMAGEHLP_LINE64); /******************************************************************************* * Structures and Typedefs * *******************************************************************************/ /** * A dbghelp based PE debug reader. */ typedef struct KDBGMODDBGHELP { /** The common module core. */ KDBGMOD Core; /** The image base. */ DWORD64 ImageBase; /** The "process" handle we present dbghelp. */ HANDLE hSymInst; /** The image size. */ uint32_t cbImage; /** The number of sections. (We've added the implicit header section.) */ int32_t cSections; /** The section headers (variable size). The first section is the * implicit header section.*/ IMAGE_SECTION_HEADER aSections[1]; } KDBGMODDBGHELP, *PKDBGMODDBGHELP; /** * Convers a Windows error to kDbg error code. * * @returns kDbg status code. * @param rc The Windows error. */ static int kdbgModDHConvWinError(DWORD rc) { switch (rc) { case 0: return 0; default: return KDBG_ERR_GENERAL_FAILURE; } } /** * Calcs the RVA for a segment:offset address. * * @returns IPRT status code. * * @param pModDH The PE debug module instance. * @param iSegment The segment number. Special segments are dealt with as well. * @param off The segment offset. * @param puRVA Where to store the RVA on success. */ static int kdbgModDHSegOffToRVA(PKDBGMODDBGHELP pModDH, int32_t iSegment, KDBGADDR off, uint32_t *puRVA) { if (iSegment >= 0) { kDbgAssertMsgReturn(iSegment < pModDH->cSections, ("iSegment=%x cSections=%x\n", iSegment, pModDH->cSections), KDBG_ERR_INVALID_ADDRESS); kDbgAssertMsgReturn(off < pModDH->aSections[iSegment].Misc.VirtualSize, ("off=" PRI_KDBGADDR " VirtualSize=%x\n", off, pModDH->aSections[iSegment].Misc.VirtualSize), KDBG_ERR_INVALID_ADDRESS); *puRVA = pModDH->aSections[iSegment].VirtualAddress + (uint32_t)off; return 0; } if (iSegment == KDBGSEG_RVA) { kDbgAssertMsgReturn(off < pModDH->cbImage, ("off=" PRI_KDBGADDR ", cbImage=%x\n", off, pModDH->cbImage), KDBG_ERR_INVALID_ADDRESS); *puRVA = (uint32_t)off; return 0; } kDbgAssertMsgFailedReturn(("iSegment=%d\n", iSegment), KDBG_ERR_INVALID_ADDRESS); } /** * Calcs the segment:offset address for a RVA. * * @returns IPRT status code. * * @param pModDH The PE debug module instance. * @param uRVA The RVA. * @param piSegment Where to store the segment number. * @param poff Where to store the segment offset. */ static int kdbgModDHRVAToSegOff(PKDBGMODDBGHELP pModDH, uint32_t uRVA, int32_t *piSegment, KDBGADDR *poff) { kDbgAssertMsgReturn(uRVA < pModDH->cbImage, ("uRVA=%x, cbImage=%x\n", uRVA, pModDH->cbImage), KDBG_ERR_INVALID_ADDRESS); for (int32_t iSegment = 0; iSegment < pModDH->cSections; iSegment++) { /** @todo should probably be less strict about address in the alignment gaps. */ uint32_t off = uRVA - pModDH->aSections[iSegment].VirtualAddress; if (off < pModDH->aSections[iSegment].Misc.VirtualSize) { *poff = off; *piSegment = iSegment; return 0; } } kDbgAssertMsgFailedReturn(("uRVA=%x\n", uRVA), KDBG_ERR_INVALID_ADDRESS); } /** * @copydoc KDBGMODOPS::pfnQueryLine */ static int kdbgModDHQueryLine(PKDBGMOD pMod, int32_t iSegment, KDBGADDR off, PKDBGLINE pLine) { PKDBGMODDBGHELP pModDH = (PKDBGMODDBGHELP)pMod; /* * Translate the address to an RVA. */ uint32_t uRVA; int rc = kdbgModDHSegOffToRVA(pModDH, iSegment, off, &uRVA); if (!rc) { DWORD64 off; IMAGEHLP_LINE64 Line; Line.SizeOfStruct = sizeof(Line); if (g_pfnSymGetLineFromAddr64(pModDH->hSymInst, pModDH->ImageBase + uRVA, &off, &Line)) { pLine->RVA = (KDBGADDR)(Line.Address - pModDH->ImageBase); rc = kdbgModDHRVAToSegOff(pModDH, (uint32_t)pLine->RVA, &pLine->iSegment, &pLine->offSegment); pLine->iLine = Line.LineNumber; size_t cchFile = strlen(Line.FileName); pLine->cchFile = cchFile < sizeof(pLine->szFile) ? (uint16_t)cchFile : (uint16_t)sizeof(pLine->szFile) - 1; memcpy(pLine->szFile, Line.FileName, pLine->cchFile); pLine->szFile[pLine->cchFile] = '\0'; } else { DWORD Err = GetLastError(); rc = kdbgModDHConvWinError(Err); } } return rc; } /** * @copydoc KDBGMODOPS::pfnQuerySymbol */ static int kdbgModDHQuerySymbol(PKDBGMOD pMod, int32_t iSegment, KDBGADDR off, PKDBGSYMBOL pSym) { PKDBGMODDBGHELP pModDH = (PKDBGMODDBGHELP)pMod; /* * Translate the address to an RVA. */ uint32_t uRVA; int rc = kdbgModDHSegOffToRVA(pModDH, iSegment, off, &uRVA); if (!rc) { DWORD64 off; union { SYMBOL_INFO Sym; char achBuffer[sizeof(SYMBOL_INFO) + KDBG_SYMBOL_MAX]; } Buf; Buf.Sym.SizeOfStruct = sizeof(SYMBOL_INFO); Buf.Sym.MaxNameLen = KDBG_SYMBOL_MAX; if (g_pfnSymFromAddr(pModDH->hSymInst, pModDH->ImageBase + uRVA, &off, &Buf.Sym)) { pSym->cb = Buf.Sym.Size; pSym->fFlags = 0; if (Buf.Sym.Flags & SYMFLAG_FUNCTION) pSym->fFlags |= KDBGSYM_FLAGS_CODE; else if (Buf.Sym.Flags & SYMFLAG_CONSTANT) pSym->fFlags |= KDBGSYM_FLAGS_ABS; /** @todo SYMFLAG_CONSTANT must be tested - documentation is too brief to say what is really meant here.*/ else pSym->fFlags |= KDBGSYM_FLAGS_DATA; if (Buf.Sym.Flags & SYMFLAG_EXPORT) pSym->fFlags |= KDBGSYM_FLAGS_EXPORTED; if ((Buf.Sym.Flags & (SYMFLAG_VALUEPRESENT | SYMFLAG_CONSTANT)) == (SYMFLAG_VALUEPRESENT | SYMFLAG_CONSTANT)) { pSym->iSegment = KDBGSEG_ABS; pSym->offSegment = (KDBGADDR)Buf.Sym.Value; pSym->RVA = (KDBGADDR)Buf.Sym.Value; } else { pSym->RVA = (KDBGADDR)(Buf.Sym.Address - pModDH->ImageBase); rc = kdbgModDHRVAToSegOff(pModDH, (uint32_t)pSym->RVA, &pSym->iSegment, &pSym->offSegment); } pSym->cchName = (uint16_t)Buf.Sym.NameLen; if (pSym->cchName >= sizeof(pSym->szName)) pSym->cchName = sizeof(pSym->szName) - 1; memcpy(pSym->szName, Buf.Sym.Name, Buf.Sym.NameLen); pSym->szName[Buf.Sym.NameLen] = '\0'; } else { DWORD Err = GetLastError(); rc = kdbgModDHConvWinError(Err); } } return rc; } /** * @copydoc KDBGMODOPS::pfnClose */ static int kdbgModDHClose(PKDBGMOD pMod) { PKDBGMODDBGHELP pModDH = (PKDBGMODDBGHELP)pMod; if (g_pfnSymCleanup(pModDH->hSymInst)) return 0; DWORD Err = GetLastError(); int rc = kdbgModDHConvWinError(Err); kDbgAssertMsgFailed(("SymInitialize failed: Err=%d rc=%Rrc\n", Err, rc)); return rc; } /** * Methods for a PE module. */ static const KDBGMODOPS g_kdbgModDHOps = { "PE (dbghelp)", kdbgModDHClose, kdbgModDHQuerySymbol, kdbgModDHQueryLine }; /** * Checks if the specified dbghelp.dll is usable. * * @returns IPRT status code. * * @param pszPath the path to the dbghelp.dll. */ static int kdbgModDHTryDbgHelp(const char *pszPath, uint32_t *pu32FileVersionMS, uint32_t *pu32FileVersionLS) { int rc; DWORD dwHandle = 0; DWORD cb = GetFileVersionInfoSize(pszPath, &dwHandle); if (cb > 0) { void *pvBuf = alloca(cb); if (GetFileVersionInfo(pszPath, dwHandle, cb, pvBuf)) { UINT cbValue = 0; VS_FIXEDFILEINFO *pFileInfo; if (VerQueryValue(pvBuf, "\\", (void **)&pFileInfo, &cbValue)) { if ( *pu32FileVersionMS < pFileInfo->dwFileVersionMS || ( *pu32FileVersionMS == pFileInfo->dwFileVersionMS && *pu32FileVersionLS > pFileInfo->dwFileVersionLS)) { *pu32FileVersionMS = pFileInfo->dwFileVersionMS; *pu32FileVersionLS = pFileInfo->dwFileVersionLS; } if (pFileInfo->dwFileVersionMS >= 0x60004) rc = 0; else rc = KDBG_ERR_DBGHLP_VERSION_MISMATCH; } else rc = KDBG_ERR_GENERAL_FAILURE; } else rc = kdbgModDHConvWinError(GetLastError()); } else rc = kdbgModDHConvWinError(GetLastError()); return rc; } /** * Find the dbghelp.dll */ static int kdbgModDHFindDbgHelp(char *pszPath, size_t cchPath) { /* * Try the current directory. */ uint32_t FileVersionMS = 0; uint32_t FileVersionLS = 0; int rc = KDBG_ERR_GENERAL_FAILURE; static char s_szDbgHelp[] = "\\dbghelp.dll"; if (GetCurrentDirectory((DWORD)(cchPath - sizeof(s_szDbgHelp) + 1), pszPath)) { strcat(pszPath, s_szDbgHelp); int rc2 = kdbgModDHTryDbgHelp(pszPath, &FileVersionMS, &FileVersionLS); if (!rc2) return rc2; if (rc != KDBG_ERR_DBGHLP_VERSION_MISMATCH) rc = rc2; } /* * Try the application directory. */ if (GetModuleFileName(NULL, pszPath, (DWORD)(cchPath - sizeof(s_szDbgHelp) + 1))) { strcat(strrchr(pszPath, '\\'), s_szDbgHelp); int rc2 = kdbgModDHTryDbgHelp(pszPath, &FileVersionMS, &FileVersionLS); if (!rc) return rc2; if (rc != KDBG_ERR_DBGHLP_VERSION_MISMATCH) rc = rc2; } /* * Try the windows directory. */ if (GetSystemDirectory(pszPath, (DWORD)(cchPath - sizeof(s_szDbgHelp) + 1))) { strcat(pszPath, s_szDbgHelp); int rc2 = kdbgModDHTryDbgHelp(pszPath, &FileVersionMS, &FileVersionLS); if (!rc2) return rc2; if (rc != KDBG_ERR_DBGHLP_VERSION_MISMATCH) rc = rc2; } /* * Try the windows directory. */ if (GetWindowsDirectory(pszPath, (DWORD)(cchPath - sizeof(s_szDbgHelp) + 1))) { strcat(pszPath, s_szDbgHelp); int rc2 = kdbgModDHTryDbgHelp(pszPath, &FileVersionMS, &FileVersionLS); if (!rc2) return rc2; if (rc != KDBG_ERR_DBGHLP_VERSION_MISMATCH) rc = rc2; } /* * Try the path. */ /** @todo find the actual path specs, I'm probably not doing this 100% correctly here. */ DWORD cb = GetEnvironmentVariable("PATH", NULL, 0) + 64; char *pszSearchPath = (char *) alloca(cb); if (GetEnvironmentVariable("PATH", pszSearchPath, cb) < cb) { char *psz = pszSearchPath; while (*psz) { /* find the end of the path. */ char *pszEnd = strchr(psz, ';'); if (!pszEnd) pszEnd = strchr(psz, '\0'); if (pszEnd != psz) { /* construct filename and try it out */ memcpy(pszPath, psz, pszEnd - psz); memcpy(&pszPath[pszEnd - psz], s_szDbgHelp, sizeof(s_szDbgHelp)); int rc2 = kdbgModDHTryDbgHelp(pszPath, &FileVersionMS, &FileVersionLS); if (!rc2) return rc2; if (rc != KDBG_ERR_DBGHLP_VERSION_MISMATCH) rc = rc2; } /* next path */ if (!*pszEnd) break; psz = pszEnd + 1; } } if (rc == KDBG_ERR_DBGHLP_VERSION_MISMATCH) kDbgAssertMsgFailed(("dbghelp.dll found, but it was ancient! The highest file version found was 0x%08x'%08x.\n" "This program require a file version of at least 0x00060004'00000000. Please download\n" "the latest windbg and use the dbghelp.dll from that package. Just put it somewhere in\n" "the PATH and we'll find it.\n", FileVersionMS, FileVersionLS)); else kDbgAssertMsgFailed(("dbghelp.dll was not found! Download the latest windbg and use the dbghelp.dll\n" "from that package - just put it somewhere in the PATH and we'll find it.\n")); return rc; } /** * Loads the dbghelp.dll, check that it's the right version, and * resolves all the symbols we need. * * @returns IPRT status code. */ static int kdbgModDHLoadDbgHelp(void) { if (g_hDbgHelp) return 0; /* primitive locking - make some useful API for this kind of spinning! */ static volatile uint32_t s_u32Lock = 0; while (!InterlockedCompareExchange((long volatile *)&s_u32Lock, 1, 0)) while (s_u32Lock) Sleep(1); if (g_hDbgHelp) { InterlockedExchange((long volatile *)&s_u32Lock, 0); return 0; } /* * Load it - try current dir first. */ char szPath[260]; int rc = kdbgModDHFindDbgHelp(szPath, sizeof(szPath)); if (rc) { InterlockedExchange((volatile long *)&s_u32Lock, 0); return rc; } HMODULE hmod = LoadLibraryEx(szPath, NULL, LOAD_WITH_ALTERED_SEARCH_PATH); if (!hmod) { DWORD Err = GetLastError(); int rc = kdbgModDHConvWinError(Err); kDbgAssertMsgFailedReturn(("Failed to load '%s', Err=%d rc=%Rrc\n", szPath, Err, rc), rc); } /* * Check the API version (too). */ LPAPI_VERSION (WINAPI *pfnImagehlpApiVersion)(VOID); FARPROC *ppfn = (FARPROC *)&pfnImagehlpApiVersion; *ppfn = GetProcAddress(hmod, "ImagehlpApiVersion"); if (*ppfn) { LPAPI_VERSION pVersion = pfnImagehlpApiVersion(); if ( pVersion && ( pVersion->MajorVersion > 4 || (pVersion->MajorVersion == 4 && pVersion->MinorVersion > 0) || (pVersion->MajorVersion == 4 && pVersion->MinorVersion == 0 && pVersion->Revision >= 5) ) ) { /* * Resolve the entrypoints we need. */ static const struct { const char *pszName; FARPROC *ppfn; } s_aFunctions[] = { { "SymInitialize", (FARPROC *)&g_pfnSymInitialize }, { "SymCleanup", (FARPROC *)&g_pfnSymCleanup }, { "SymSetOptions", (FARPROC *)&g_pfnSymSetOptions }, { "SymLoadModule64", (FARPROC *)&g_pfnSymLoadModule64 }, { "SymFromAddr", (FARPROC *)&g_pfnSymFromAddr }, { "SymFromAddr", (FARPROC *)&g_pfnSymFromAddr }, { "SymGetLineFromAddr64", (FARPROC *)&g_pfnSymGetLineFromAddr64 }, }; for (unsigned i = 0; i < KDBG_ELEMENTS(s_aFunctions); i++) { FARPROC pfn = GetProcAddress(hmod, s_aFunctions[i].pszName); if (!pfn) { DWORD Err = GetLastError(); rc = kdbgModDHConvWinError(Err); kDbgAssertMsgFailed(("Failed to resolve %s in dbghelp, Err=%d rc=%Rrc\n", s_aFunctions[i].pszName, Err, rc)); break; } *s_aFunctions[i].ppfn = pfn; } if (!rc) { //InterlockedExchange(&g_hDbgHelp, hmod); g_hDbgHelp = hmod; Sleep(1); InterlockedExchange((volatile long *)&s_u32Lock, 0); return 0; } } else { rc = KDBG_ERR_DBGHLP_VERSION_MISMATCH; kDbgAssertMsgFailed(("ImagehlpApiVersion -> %p and MajorVersion=%d.\n", pVersion, pVersion ? pVersion->MajorVersion : 0)); } } else { DWORD Err = GetLastError(); rc = kdbgModDHConvWinError(Err); kDbgAssertMsgFailed(("Failed to resolve ImagehlpApiVersionEx in dbghelp, Err=%d rc=%Rrc\n", Err, rc)); } FreeLibrary(hmod); InterlockedExchange((long volatile *)&s_u32Lock, 0); return rc; } /** * Opens the debug info for a PE image using the windows dbghelp library. * * @returns IPRT status code. * * @param pFile The handle to the module. * @param offHdr The offset of the PE header. * @param pszModulePath The path to the module. * @param ppDbgMod Where to store the module handle. * */ int kdbgModWinDbgHelpOpen(PKDBGHLPFILE pFile, int64_t offHdr, const char *pszModulePath, PKDBGMOD *ppDbgMod) { /* * We need to read the section headers and get the image size. */ IMAGE_FILE_HEADER FHdr; int rc = kDbgHlpReadAt(pFile, offHdr + KDBG_OFFSETOF(IMAGE_NT_HEADERS32, FileHeader), &FHdr, sizeof(FHdr)); kDbgAssertRCReturn(rc, rc); uint32_t cbImage; if (FHdr.SizeOfOptionalHeader == sizeof(IMAGE_OPTIONAL_HEADER32)) rc = kDbgHlpReadAt(pFile, offHdr + KDBG_OFFSETOF(IMAGE_NT_HEADERS32, OptionalHeader.SizeOfImage), &cbImage, sizeof(cbImage)); else if (FHdr.SizeOfOptionalHeader == sizeof(IMAGE_OPTIONAL_HEADER64)) rc = kDbgHlpReadAt(pFile, offHdr + KDBG_OFFSETOF(IMAGE_NT_HEADERS64, OptionalHeader.SizeOfImage), &cbImage, sizeof(cbImage)); else kDbgAssertFailedReturn(KDBG_ERR_BAD_EXE_FORMAT); kDbgAssertRCReturn(rc, rc); /* * Load dbghelp.dll. */ rc = kdbgModDHLoadDbgHelp(); if (rc) return rc; /* * Allocate the module and read/construct the section headers. */ PKDBGMODDBGHELP pModDH = (PKDBGMODDBGHELP)kDbgHlpAlloc(KDBG_OFFSETOF(KDBGMODDBGHELP, aSections[FHdr.NumberOfSections + 2])); kDbgAssertReturn(pModDH, KDBG_ERR_NO_MEMORY); pModDH->Core.u32Magic = KDBGMOD_MAGIC; pModDH->Core.pOps = &g_kdbgModDHOps; pModDH->Core.pFile = pFile; pModDH->cbImage = cbImage; pModDH->cSections = 1 + FHdr.NumberOfSections; rc = kDbgHlpReadAt(pFile, offHdr + KDBG_OFFSETOF(IMAGE_NT_HEADERS32, OptionalHeader) + FHdr.SizeOfOptionalHeader, &pModDH->aSections[1], sizeof(pModDH->aSections[0]) * FHdr.NumberOfSections); if (!rc) { PIMAGE_SECTION_HEADER pSH = &pModDH->aSections[0]; memcpy(pSH->Name, "headers", sizeof(pSH->Name)); pSH->Misc.VirtualSize = pModDH->aSections[1].VirtualAddress; pSH->VirtualAddress = 0; pSH->SizeOfRawData = pSH->Misc.VirtualSize; pSH->PointerToRawData = 0; pSH->PointerToRelocations = 0; pSH->PointerToLinenumbers = 0; pSH->NumberOfRelocations = 0; pSH->NumberOfLinenumbers = 0; pSH->Characteristics = IMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_ALIGN_4BYTES | IMAGE_SCN_MEM_READ; uint32_t uTheEnd = pModDH->aSections[FHdr.NumberOfSections].VirtualAddress + pModDH->aSections[FHdr.NumberOfSections].Misc.VirtualSize; if (uTheEnd < cbImage) { pSH = &pModDH->aSections[pModDH->cSections++]; memcpy(pSH->Name, "tail\0\0\0", sizeof(pSH->Name)); pSH->Misc.VirtualSize = cbImage - uTheEnd; pSH->VirtualAddress = uTheEnd; pSH->SizeOfRawData = pSH->Misc.VirtualSize; pSH->PointerToRawData = 0; pSH->PointerToRelocations = 0; pSH->PointerToLinenumbers = 0; pSH->NumberOfRelocations = 0; pSH->NumberOfLinenumbers = 0; pSH->Characteristics = IMAGE_SCN_CNT_UNINITIALIZED_DATA | IMAGE_SCN_ALIGN_1BYTES | IMAGE_SCN_MEM_READ; } /* * Find a new dbghelp handle. * * We assume 4GB of handles outlast most debugging sessions, or in anyways that * when we start reusing handles they are no longer in use. :-) */ static volatile uint32_t s_u32LastHandle = 1; HANDLE hSymInst = (HANDLE)InterlockedIncrement((long *)&s_u32LastHandle); while ( hSymInst == INVALID_HANDLE_VALUE || hSymInst == (HANDLE)0 || hSymInst == GetCurrentProcess()) hSymInst = (HANDLE)InterlockedIncrement((long *)&s_u32LastHandle); /* * Initialize dbghelp and try open the specified module. */ if (g_pfnSymInitialize(hSymInst, NULL, FALSE)) { g_pfnSymSetOptions(SYMOPT_LOAD_LINES | SYMOPT_AUTO_PUBLICS | SYMOPT_ALLOW_ABSOLUTE_SYMBOLS); kDbgHlpSeek(pFile, 0); /* don't know if this is required or not... */ DWORD64 ImageBase = g_pfnSymLoadModule64(hSymInst, (HANDLE)kDbgHlpNativeFileHandle(pFile), pszModulePath, NULL, 0x00400000, 0); if (ImageBase) { pModDH->hSymInst = hSymInst; pModDH->ImageBase = ImageBase; *ppDbgMod = &pModDH->Core; return rc; } DWORD Err = GetLastError(); rc = kdbgModDHConvWinError(Err); kDbgAssertMsgFailed(("SymLoadModule64 failed: Err=%d rc=%Rrc\n", Err, rc)); g_pfnSymCleanup(hSymInst); } else { DWORD Err = GetLastError(); rc = kdbgModDHConvWinError(Err); kDbgAssertMsgFailed(("SymInitialize failed: Err=%d rc=%Rrc\n", Err, rc)); } } else kDbgAssertRC(rc); kDbgHlpFree(pModDH); return rc; }