From 7ac3b0fa0b22cb531667995099afd5eea8607884 Mon Sep 17 00:00:00 2001 From: Mattes D Date: Mon, 19 Jun 2017 11:05:19 +0200 Subject: Removed the LeakFinder for Windows. (#3777) --- src/OSSupport/WinStackWalker.cpp | 1257 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 1257 insertions(+) create mode 100644 src/OSSupport/WinStackWalker.cpp (limited to 'src/OSSupport/WinStackWalker.cpp') diff --git a/src/OSSupport/WinStackWalker.cpp b/src/OSSupport/WinStackWalker.cpp new file mode 100644 index 000000000..6d732bd61 --- /dev/null +++ b/src/OSSupport/WinStackWalker.cpp @@ -0,0 +1,1257 @@ +// WinStackWalker.cpp + +// Implements the stack walking for Windows binaries + + + +/* +LICENSE (http://www.opensource.org/licenses/bsd-license.php) + +Copyright (c) 2005-2010, Jochen Kalmbach +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + +Redistributions of source code must retain the above copyright notice, +this list of conditions and the following disclaimer. +Redistributions in binary form must reproduce the above copyright notice, +this list of conditions and the following disclaimer in the documentation +and / or other materials provided with the distribution. +Neither the name of Jochen Kalmbach nor the names of its contributors may be +used to endorse or promote products derived from this software without +specific prior written permission. +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + +NOTE: 2017-06-16 madmaxoft: Removed the legacy VS code and generally cleaned up +*/ + + + + + +#include "Globals.h" + +#ifdef _WIN32 + +#include +#pragma comment(lib, "version.lib") // for "VerQueryValue" +#pragma warning(disable:4826) +#pragma warning(disable:4996) // "GetVersionEx" was declared deprecated + +#include "WinStackWalker.h" + + +#pragma pack(push, 8) + #include +#pragma pack(pop) + + + + + +// Copy szSrc to szDest, but at most nMaxDestSize bytes. Always NUL-terminate +template +static void MyStrCpy(CHAR (&szDest)[COUNT], const CHAR * szSrc) +{ + auto len = strlen(szSrc); + if (len < COUNT) + { + memcpy(szDest, szSrc, len + 1); + } + else + { + memcpy(szDest, szSrc, COUNT - 1); + szDest[COUNT - 1] = 0; + } +} + + + + + +// Normally it should be enough to use 'CONTEXT_FULL' (better would be 'CONTEXT_ALL') +#define USED_CONTEXT_FLAGS CONTEXT_FULL + + + + + +class StackWalkerInternal +{ +public: + StackWalkerInternal(WinStackWalker * parent, HANDLE hProcess) + { + m_parent = parent; + m_hDbhHelp = nullptr; + pSC = nullptr; + m_hProcess = hProcess; + m_szSymPath = nullptr; + pSFTA = nullptr; + pSGLFA = nullptr; + pSGMB = nullptr; + pSGMI = nullptr; + pSGO = nullptr; + pSGSFA = nullptr; + pSI = nullptr; + pSLM = nullptr; + pSSO = nullptr; + pSW = nullptr; + pUDSN = nullptr; + pSGSP = nullptr; + } + + + ~StackWalkerInternal() + { + if (pSC != nullptr) + { + pSC(m_hProcess); // SymCleanup + } + if (m_hDbhHelp != nullptr) + { + FreeLibrary(m_hDbhHelp); + } + m_hDbhHelp = nullptr; + m_parent = nullptr; + if (m_szSymPath != nullptr) + { + free(m_szSymPath); + } + m_szSymPath = nullptr; + } + + + BOOL Init(LPCSTR szSymPath) + { + if (m_parent == nullptr) + { + return FALSE; + } + // Dynamically load the Entry-Points for dbghelp.dll: + // First try to load the newsest one from + TCHAR szTemp[4096]; + // But before wqe do this, we first check if the ".local" file exists + if (GetModuleFileName(nullptr, szTemp, sizeof(szTemp)) > 0) + { + _tcscat_s(szTemp, _T(".local")); + if (GetFileAttributes(szTemp) == INVALID_FILE_ATTRIBUTES) + { + // ".local" file does not exist, so we can try to load the dbghelp.dll from the "Debugging Tools for Windows" + // Ok, first try the new path according to the archtitecture: + #ifdef _M_IX86 + if ((m_hDbhHelp == nullptr) && (GetEnvironmentVariable(_T("ProgramFiles"), szTemp, ARRAYCOUNT(szTemp)) > 0)) + { + _tcscat_s(szTemp, _T("\\Debugging Tools for Windows (x86)\\dbghelp.dll")); + // now check if the file exists: + if (GetFileAttributes(szTemp) != INVALID_FILE_ATTRIBUTES) + { + m_hDbhHelp = LoadLibrary(szTemp); + } + } + #elif _M_X64 + if ((m_hDbhHelp == nullptr) && (GetEnvironmentVariable(_T("ProgramFiles"), szTemp, ARRAYCOUNT(szTemp)) > 0)) + { + _tcscat_s(szTemp, _T("\\Debugging Tools for Windows (x64)\\dbghelp.dll")); + // now check if the file exists: + if (GetFileAttributes(szTemp) != INVALID_FILE_ATTRIBUTES) + { + m_hDbhHelp = LoadLibrary(szTemp); + } + } + #elif _M_IA64 + if ((m_hDbhHelp == nullptr) && (GetEnvironmentVariable(_T("ProgramFiles"), szTemp, ARRAYCOUNT(szTemp)) > 0)) + { + _tcscat_s(szTemp, _T("\\Debugging Tools for Windows (ia64)\\dbghelp.dll")); + // now check if the file exists: + if (GetFileAttributes(szTemp) != INVALID_FILE_ATTRIBUTES) + { + m_hDbhHelp = LoadLibrary(szTemp); + } + } + #endif + // If still not found, try the old directories... + if ((m_hDbhHelp == nullptr) && (GetEnvironmentVariable(_T("ProgramFiles"), szTemp, ARRAYCOUNT(szTemp)) > 0)) + { + _tcscat_s(szTemp, _T("\\Debugging Tools for Windows\\dbghelp.dll")); + // now check if the file exists: + if (GetFileAttributes(szTemp) != INVALID_FILE_ATTRIBUTES) + { + m_hDbhHelp = LoadLibrary(szTemp); + } + } + #if defined _M_X64 || defined _M_IA64 + // Still not found? Then try to load the (old) 64-Bit version: + if ((m_hDbhHelp == nullptr) && (GetEnvironmentVariable(_T("ProgramFiles"), szTemp, ARRAYCOUNT(szTemp)) > 0)) + { + _tcscat_s(szTemp, _T("\\Debugging Tools for Windows 64-Bit\\dbghelp.dll")); + if (GetFileAttributes(szTemp) != INVALID_FILE_ATTRIBUTES) + { + m_hDbhHelp = LoadLibrary(szTemp); + } + } + #endif + } + } + if (m_hDbhHelp == nullptr) // if not already loaded, try to load a default-one + { + m_hDbhHelp = LoadLibrary(_T("dbghelp.dll")); + } + if (m_hDbhHelp == nullptr) + { + return FALSE; + } + pSI = (tSI)GetProcAddress(m_hDbhHelp, "SymInitialize"); + pSC = (tSC)GetProcAddress(m_hDbhHelp, "SymCleanup"); + + pSW = (tSW)GetProcAddress(m_hDbhHelp, "StackWalk64"); + pSGO = (tSGO)GetProcAddress(m_hDbhHelp, "SymGetOptions"); + pSSO = (tSSO)GetProcAddress(m_hDbhHelp, "SymSetOptions"); + + pSFTA = (tSFTA)GetProcAddress(m_hDbhHelp, "SymFunctionTableAccess64"); + pSGLFA = (tSGLFA)GetProcAddress(m_hDbhHelp, "SymGetLineFromAddr64"); + pSGMB = (tSGMB)GetProcAddress(m_hDbhHelp, "SymGetModuleBase64"); + pSGMI = (tSGMI)GetProcAddress(m_hDbhHelp, "SymGetModuleInfo64"); + // pSGMI_V3 = (tSGMI_V3) GetProcAddress(m_hDbhHelp, "SymGetModuleInfo64"); + pSGSFA = (tSGSFA)GetProcAddress(m_hDbhHelp, "SymGetSymFromAddr64"); + pUDSN = (tUDSN)GetProcAddress(m_hDbhHelp, "UnDecorateSymbolName"); + pSLM = (tSLM)GetProcAddress(m_hDbhHelp, "SymLoadModule64"); + pSGSP = (tSGSP)GetProcAddress(m_hDbhHelp, "SymGetSearchPath"); + + if ( + (pSC == nullptr) || (pSFTA == nullptr) || (pSGMB == nullptr) || (pSGMI == nullptr) || + (pSGO == nullptr) || (pSGSFA == nullptr) || (pSI == nullptr) || (pSSO == nullptr) || + (pSW == nullptr) || (pUDSN == nullptr) || (pSLM == nullptr) + ) + { + FreeLibrary(m_hDbhHelp); + m_hDbhHelp = nullptr; + pSC = nullptr; + return FALSE; + } + + // SymInitialize + if (szSymPath != nullptr) + { + m_szSymPath = _strdup(szSymPath); + } + if (this->pSI(m_hProcess, m_szSymPath, FALSE) == FALSE) + { + this->m_parent->OnDbgHelpErr("SymInitialize", GetLastError(), 0); + } + + DWORD symOptions = this->pSGO(); // SymGetOptions + symOptions |= SYMOPT_LOAD_LINES; + symOptions |= SYMOPT_FAIL_CRITICAL_ERRORS; + symOptions = this->pSSO(symOptions); + + char buf[WinStackWalker::STACKWALK_MAX_NAMELEN] = { 0 }; + if (this->pSGSP != nullptr) + { + if (this->pSGSP(m_hProcess, buf, static_cast(sizeof(buf))) == FALSE) + { + this->m_parent->OnDbgHelpErr("SymGetSearchPath", GetLastError(), 0); + } + } + char szUserName[1024] = { 0 }; + DWORD dwSize = static_cast(sizeof(szUserName)); + GetUserNameA(szUserName, &dwSize); + this->m_parent->OnSymInit(buf, symOptions, szUserName); + + return TRUE; + } + + + + + + WinStackWalker * m_parent; + + HMODULE m_hDbhHelp; + HANDLE m_hProcess; + LPSTR m_szSymPath; + + #pragma pack(push, 8) + struct IMAGEHLP_MODULE64_V2 + { + DWORD SizeOfStruct; // set to sizeof(IMAGEHLP_MODULE64) + DWORD64 BaseOfImage; // base load address of module + DWORD ImageSize; // virtual size of the loaded module + DWORD TimeDateStamp; // date / time stamp from pe header + DWORD CheckSum; // checksum from the pe header + DWORD NumSyms; // number of symbols in the symbol table + SYM_TYPE SymType; // type of symbols loaded + CHAR ModuleName[32]; // module name + CHAR ImageName[256]; // image name + CHAR LoadedImageName[256]; // symbol file name + }; + #pragma pack(pop) + + + // SymCleanup() + typedef BOOL(__stdcall *tSC)(IN HANDLE hProcess); + tSC pSC; + + // SymFunctionTableAccess64() + typedef PVOID(__stdcall *tSFTA)(HANDLE hProcess, DWORD64 AddrBase); + tSFTA pSFTA; + + // SymGetLineFromAddr64() + typedef BOOL(__stdcall *tSGLFA)(IN HANDLE hProcess, IN DWORD64 dwAddr, OUT PDWORD pdwDisplacement, OUT PIMAGEHLP_LINE64 Line); + tSGLFA pSGLFA; + + // SymGetModuleBase64() + typedef DWORD64(__stdcall *tSGMB)(IN HANDLE hProcess, IN DWORD64 dwAddr); + tSGMB pSGMB; + + // SymGetModuleInfo64() + typedef BOOL(__stdcall *tSGMI)(IN HANDLE hProcess, IN DWORD64 dwAddr, OUT IMAGEHLP_MODULE64_V2 *ModuleInfo); + tSGMI pSGMI; + + // // SymGetModuleInfo64() + // typedef BOOL (__stdcall *tSGMI_V3)( IN HANDLE hProcess, IN DWORD64 dwAddr, OUT IMAGEHLP_MODULE64_V3 *ModuleInfo); + // tSGMI_V3 pSGMI_V3; + + // SymGetOptions() + typedef DWORD(__stdcall *tSGO)(VOID); + tSGO pSGO; + + // SymGetSymFromAddr64() + typedef BOOL(__stdcall *tSGSFA)(IN HANDLE hProcess, IN DWORD64 dwAddr, OUT PDWORD64 pdwDisplacement, OUT PIMAGEHLP_SYMBOL64 Symbol); + tSGSFA pSGSFA; + + // SymInitialize() + typedef BOOL(__stdcall *tSI)(IN HANDLE hProcess, IN PSTR UserSearchPath, IN BOOL fInvadeProcess); + tSI pSI; + + // SymLoadModule64() + typedef DWORD64(__stdcall *tSLM)(IN HANDLE hProcess, IN HANDLE hFile, IN PSTR ImageName, IN PSTR ModuleName, IN DWORD64 BaseOfDll, IN DWORD SizeOfDll); + tSLM pSLM; + + // SymSetOptions() + typedef DWORD(__stdcall *tSSO)(IN DWORD SymOptions); + tSSO pSSO; + + // StackWalk64() + typedef BOOL(__stdcall *tSW)( + DWORD MachineType, + HANDLE hProcess, + HANDLE hThread, + LPSTACKFRAME64 StackFrame, + PVOID ContextRecord, + PREAD_PROCESS_MEMORY_ROUTINE64 ReadMemoryRoutine, + PFUNCTION_TABLE_ACCESS_ROUTINE64 FunctionTableAccessRoutine, + PGET_MODULE_BASE_ROUTINE64 GetModuleBaseRoutine, + PTRANSLATE_ADDRESS_ROUTINE64 TranslateAddress); + tSW pSW; + + // UnDecorateSymbolName() + typedef DWORD(__stdcall WINAPI *tUDSN)(PCSTR DecoratedName, PSTR UnDecoratedName, DWORD UndecoratedLength, DWORD Flags); + tUDSN pUDSN; + + typedef BOOL(__stdcall WINAPI *tSGSP)(HANDLE hProcess, PSTR SearchPath, DWORD SearchPathLength); + tSGSP pSGSP; + + +private: + // **************************************** ToolHelp32 ************************ + #define MAX_MODULE_NAME32 255 + #define TH32CS_SNAPMODULE 0x00000008 + + #pragma pack(push, 8) + typedef struct tagMODULEENTRY32 + { + DWORD dwSize; + DWORD th32ModuleID; // This module + DWORD th32ProcessID; // owning process + DWORD GlblcntUsage; // Global usage count on the module + DWORD ProccntUsage; // Module usage count in th32ProcessID's context + BYTE * modBaseAddr; // Base address of module in th32ProcessID's context + DWORD modBaseSize; // Size in bytes of module starting at modBaseAddr + HMODULE hModule; // The hModule of this module in th32ProcessID's context + char szModule[MAX_MODULE_NAME32 + 1]; + char szExePath[MAX_PATH]; + } MODULEENTRY32; + typedef MODULEENTRY32 * PMODULEENTRY32; + typedef MODULEENTRY32 * LPMODULEENTRY32; + #pragma pack(pop) + + BOOL GetModuleListTH32(HANDLE hProcess, DWORD pid) + { + // CreateToolhelp32Snapshot() + typedef HANDLE(__stdcall *tCT32S)(DWORD dwFlags, DWORD th32ProcessID); + // Module32First() + typedef BOOL(__stdcall *tM32F)(HANDLE hSnapshot, LPMODULEENTRY32 lpme); + // Module32Next() + typedef BOOL(__stdcall *tM32N)(HANDLE hSnapshot, LPMODULEENTRY32 lpme); + + // try both dlls... + const TCHAR *dllname[] = { _T("kernel32.dll"), _T("tlhelp32.dll") }; + HINSTANCE hToolhelp = nullptr; + tCT32S pCT32S = nullptr; + tM32F pM32F = nullptr; + tM32N pM32N = nullptr; + + HANDLE hSnap; + MODULEENTRY32 me; + me.dwSize = sizeof(me); + BOOL keepGoing; + + for (size_t i = 0; i < ARRAYCOUNT(dllname); i++) + { + hToolhelp = LoadLibrary(dllname[i]); + if (hToolhelp == nullptr) + { + continue; + } + pCT32S = (tCT32S)GetProcAddress(hToolhelp, "CreateToolhelp32Snapshot"); + pM32F = (tM32F)GetProcAddress(hToolhelp, "Module32First"); + pM32N = (tM32N)GetProcAddress(hToolhelp, "Module32Next"); + if ((pCT32S != nullptr) && (pM32F != nullptr) && (pM32N != nullptr)) + { + break; // found the functions! + } + FreeLibrary(hToolhelp); + hToolhelp = nullptr; + } + + if (hToolhelp == nullptr) + { + return FALSE; + } + + hSnap = pCT32S(TH32CS_SNAPMODULE, pid); + if (hSnap == (HANDLE)-1) + { + FreeLibrary(hToolhelp); + return FALSE; + } + + keepGoing = !!pM32F(hSnap, &me); + int cnt = 0; + while (keepGoing) + { + this->LoadModule(hProcess, me.szExePath, me.szModule, (DWORD64)me.modBaseAddr, me.modBaseSize); + cnt++; + keepGoing = !!pM32N(hSnap, &me); + } + CloseHandle(hSnap); + FreeLibrary(hToolhelp); + if (cnt <= 0) + { + return FALSE; + } + return TRUE; + } // GetModuleListTH32 + + + + + + // **************************************** PSAPI ************************ + typedef struct _MODULEINFO + { + LPVOID lpBaseOfDll; + DWORD SizeOfImage; + LPVOID EntryPoint; + } MODULEINFO, *LPMODULEINFO; + + BOOL GetModuleListPSAPI(HANDLE hProcess) + { + // EnumProcessModules() + typedef BOOL(__stdcall *tEPM)(HANDLE hProcess, HMODULE *lphModule, DWORD cb, LPDWORD lpcbNeeded); + // GetModuleFileNameEx() + typedef DWORD(__stdcall *tGMFNE)(HANDLE hProcess, HMODULE hModule, LPSTR lpFilename, DWORD nSize); + // GetModuleBaseName() + typedef DWORD(__stdcall *tGMBN)(HANDLE hProcess, HMODULE hModule, LPSTR lpFilename, DWORD nSize); + // GetModuleInformation() + typedef BOOL(__stdcall *tGMI)(HANDLE hProcess, HMODULE hModule, LPMODULEINFO pmi, DWORD nSize); + + HINSTANCE hPsapi; + tEPM pEPM; + tGMFNE pGMFNE; + tGMBN pGMBN; + tGMI pGMI; + + DWORD i; + DWORD cbNeeded; + MODULEINFO mi; + HMODULE *hMods = 0; + char *tt = nullptr; + char *tt2 = nullptr; + const SIZE_T TTBUFLEN = 8096; + int cnt = 0; + + hPsapi = LoadLibrary(_T("psapi.dll")); + if (hPsapi == nullptr) + { + return FALSE; + } + + pEPM = (tEPM)GetProcAddress(hPsapi, "EnumProcessModules"); + pGMFNE = (tGMFNE)GetProcAddress(hPsapi, "GetModuleFileNameExA"); + pGMBN = (tGMFNE)GetProcAddress(hPsapi, "GetModuleBaseNameA"); + pGMI = (tGMI)GetProcAddress(hPsapi, "GetModuleInformation"); + if ((pEPM == nullptr) || (pGMFNE == nullptr) || (pGMBN == nullptr) || (pGMI == nullptr)) + { + // we couldnīt find all functions + FreeLibrary(hPsapi); + return FALSE; + } + + hMods = (HMODULE*)malloc(sizeof(HMODULE) * (TTBUFLEN / sizeof HMODULE)); + tt = (char*)malloc(sizeof(char) * TTBUFLEN); + tt2 = (char*)malloc(sizeof(char) * TTBUFLEN); + if ((hMods == nullptr) || (tt == nullptr) || (tt2 == nullptr)) + { + goto cleanup; + } + + if (!pEPM(hProcess, hMods, TTBUFLEN, &cbNeeded)) + { + goto cleanup; + } + + if (cbNeeded > TTBUFLEN) + { + goto cleanup; + } + + for (i = 0; i < cbNeeded / sizeof hMods[0]; i++) + { + // base address, size + pGMI(hProcess, hMods[i], &mi, sizeof mi); + // image file name + tt[0] = 0; + pGMFNE(hProcess, hMods[i], tt, TTBUFLEN); + // module name + tt2[0] = 0; + pGMBN(hProcess, hMods[i], tt2, TTBUFLEN); + + DWORD dwRes = this->LoadModule(hProcess, tt, tt2, (DWORD64)mi.lpBaseOfDll, mi.SizeOfImage); + if (dwRes != ERROR_SUCCESS) + { + this->m_parent->OnDbgHelpErr("LoadModule", dwRes, 0); + } + cnt++; + } + + cleanup: + if (hPsapi != nullptr) + { + FreeLibrary(hPsapi); + } + if (tt2 != nullptr) + { + free(tt2); + } + if (tt != nullptr) + { + free(tt); + } + if (hMods != nullptr) + { + free(hMods); + } + + return cnt != 0; + } // GetModuleListPSAPI + + + + + + DWORD LoadModule(HANDLE hProcess, LPCSTR img, LPCSTR mod, DWORD64 baseAddr, DWORD size) + { + CHAR *szImg = _strdup(img); + CHAR *szMod = _strdup(mod); + DWORD result = ERROR_SUCCESS; + if ((szImg == nullptr) || (szMod == nullptr)) + { + result = ERROR_NOT_ENOUGH_MEMORY; + } + else + { + if (pSLM(hProcess, 0, szImg, szMod, baseAddr, size) == 0) + { + result = GetLastError(); + } + } + ULONGLONG fileVersion = 0; + if ((m_parent != nullptr) && (szImg != nullptr)) + { + // try to retrive the file-version: + if ((this->m_parent->m_options & WinStackWalker::RetrieveFileVersion) != 0) + { + VS_FIXEDFILEINFO *fInfo = nullptr; + DWORD dwHandle; + DWORD dwSize = GetFileVersionInfoSizeA(szImg, &dwHandle); + if (dwSize > 0) + { + LPVOID vData = malloc(dwSize); + if (vData != nullptr) + { + if (GetFileVersionInfoA(szImg, dwHandle, dwSize, vData) != 0) + { + UINT len; + TCHAR szSubBlock[] = _T("\\"); + if (VerQueryValue(vData, szSubBlock, (LPVOID*)&fInfo, &len) == 0) + { + fInfo = nullptr; + } + else + { + fileVersion = ((ULONGLONG)fInfo->dwFileVersionLS) + ((ULONGLONG)fInfo->dwFileVersionMS << 32); + } + } + free(vData); + } + } + } + + // Retrive some additional-infos about the module + IMAGEHLP_MODULE64_V2 Module; + const char *szSymType = "-unknown-"; + if (this->GetModuleInfo(hProcess, baseAddr, &Module) != FALSE) + { + switch (Module.SymType) + { + case SymNone: szSymType = "-nosymbols-"; break; + case SymCoff: szSymType = "COFF"; break; + case SymCv: szSymType = "CV"; break; + case SymPdb: szSymType = "PDB"; break; + case SymExport: szSymType = "-exported-"; break; + case SymDeferred: szSymType = "-deferred-"; break; + case SymSym: szSymType = "SYM"; break; + case 7: szSymType = "DIA"; break; + case 8: szSymType = "Virtual"; break; + } + } + this->m_parent->OnLoadModule(img, mod, baseAddr, size, result, szSymType, Module.LoadedImageName, fileVersion); + } + if (szImg != nullptr) + { + free(szImg); + } + if (szMod != nullptr) + { + free(szMod); + } + return result; + } + + + + + +public: + BOOL LoadModules(HANDLE hProcess, DWORD dwProcessId) + { + // first try toolhelp32 + if (GetModuleListTH32(hProcess, dwProcessId)) + { + return true; + } + // then try psapi + return GetModuleListPSAPI(hProcess); + } + + + BOOL GetModuleInfo(HANDLE hProcess, DWORD64 baseAddr, IMAGEHLP_MODULE64_V2 *pModuleInfo) + { + if (this->pSGMI == nullptr) + { + SetLastError(ERROR_DLL_INIT_FAILED); + return FALSE; + } + pModuleInfo->SizeOfStruct = sizeof(IMAGEHLP_MODULE64_V2); + void * pData = malloc(4096); // reserve enough memory, so the bug in v6.3.5.1 does not lead to memory-overwrites... + if (pData == nullptr) + { + SetLastError(ERROR_NOT_ENOUGH_MEMORY); + return FALSE; + } + memcpy(pData, pModuleInfo, sizeof(IMAGEHLP_MODULE64_V2)); + if (this->pSGMI(hProcess, baseAddr, (IMAGEHLP_MODULE64_V2*)pData) != FALSE) + { + // only copy as much memory as is reserved... + memcpy(pModuleInfo, pData, sizeof(IMAGEHLP_MODULE64_V2)); + pModuleInfo->SizeOfStruct = sizeof(IMAGEHLP_MODULE64_V2); + free(pData); + return TRUE; + } + free(pData); + SetLastError(ERROR_DLL_INIT_FAILED); + return FALSE; + } +}; + + + + + +// ############################################################# +WinStackWalker::WinStackWalker(DWORD dwProcessId, HANDLE hProcess) +{ + this->m_options = OptionsAll; + this->m_modulesLoaded = FALSE; + this->m_hProcess = hProcess; + this->m_sw = new StackWalkerInternal(this, this->m_hProcess); + this->m_dwProcessId = dwProcessId; + this->m_szSymPath = nullptr; + this->m_MaxRecursionCount = 1000; +} + + + + +WinStackWalker::WinStackWalker(int options, LPCSTR szSymPath, DWORD dwProcessId, HANDLE hProcess) +{ + this->m_options = options; + this->m_modulesLoaded = FALSE; + this->m_hProcess = hProcess; + this->m_sw = new StackWalkerInternal(this, this->m_hProcess); + this->m_dwProcessId = dwProcessId; + if (szSymPath != nullptr) + { + this->m_szSymPath = _strdup(szSymPath); + this->m_options |= SymBuildPath; + } + else + { + this->m_szSymPath = nullptr; + } + this->m_MaxRecursionCount = 1000; +} + + + + + +WinStackWalker::~WinStackWalker() +{ + if (m_szSymPath != nullptr) + { + free(m_szSymPath); + } + m_szSymPath = nullptr; + if (this->m_sw != nullptr) + { + delete this->m_sw; + } + this->m_sw = nullptr; +} + + + + + +BOOL WinStackWalker::LoadModules() +{ + if (this->m_sw == nullptr) + { + SetLastError(ERROR_DLL_INIT_FAILED); + return FALSE; + } + if (m_modulesLoaded != FALSE) + { + return TRUE; + } + + // Build the sym-path: + char *szSymPath = nullptr; + if ((this->m_options & SymBuildPath) != 0) + { + const size_t nSymPathLen = 4096; + szSymPath = (char*)malloc(nSymPathLen); + if (szSymPath == nullptr) + { + SetLastError(ERROR_NOT_ENOUGH_MEMORY); + return FALSE; + } + szSymPath[0] = 0; + // Now first add the (optional) provided sympath: + if (this->m_szSymPath != nullptr) + { + strcat_s(szSymPath, nSymPathLen, this->m_szSymPath); + strcat_s(szSymPath, nSymPathLen, ";"); + } + + strcat_s(szSymPath, nSymPathLen, ".;"); + + const DWORD nTempLen = 1024; + char szTemp[nTempLen]; + // Now add the current directory: + if (GetCurrentDirectoryA(nTempLen, szTemp) > 0) + { + szTemp[nTempLen - 1] = 0; + strcat_s(szSymPath, nSymPathLen, szTemp); + strcat_s(szSymPath, nSymPathLen, ";"); + } + + // Now add the path for the main-module: + if (GetModuleFileNameA(nullptr, szTemp, nTempLen) > 0) + { + szTemp[nTempLen - 1] = 0; + for (char *p = (szTemp + strlen(szTemp) - 1); p >= szTemp; --p) + { + // locate the rightmost path separator + if ((*p == '\\') || (*p == '/') || (*p == ':')) + { + *p = 0; + break; + } + } // for (search for path separator...) + + if (szTemp[0] != '\0') + { + strcat_s(szSymPath, nSymPathLen, szTemp); + strcat_s(szSymPath, nSymPathLen, ";"); + } + } + if (GetEnvironmentVariableA("_NT_SYMBOL_PATH", szTemp, nTempLen) > 0) + { + szTemp[nTempLen - 1] = 0; + strcat_s(szSymPath, nSymPathLen, szTemp); + strcat_s(szSymPath, nSymPathLen, ";"); + } + if (GetEnvironmentVariableA("_NT_ALTERNATE_SYMBOL_PATH", szTemp, nTempLen) > 0) + { + szTemp[nTempLen - 1] = 0; + strcat_s(szSymPath, nSymPathLen, szTemp); + strcat_s(szSymPath, nSymPathLen, ";"); + } + if (GetEnvironmentVariableA("SYSTEMROOT", szTemp, nTempLen) > 0) + { + szTemp[nTempLen - 1] = 0; + strcat_s(szSymPath, nSymPathLen, szTemp); + strcat_s(szSymPath, nSymPathLen, ";"); + // also add the "system32"-directory: + strcat_s(szTemp, nTempLen, "\\system32"); + strcat_s(szSymPath, nSymPathLen, szTemp); + strcat_s(szSymPath, nSymPathLen, ";"); + } + + if ((this->m_options & SymUseSymSrv) != 0) + { + if (GetEnvironmentVariableA("SYSTEMDRIVE", szTemp, nTempLen) > 0) + { + szTemp[nTempLen - 1] = 0; + strcat_s(szSymPath, nSymPathLen, "SRV*"); + strcat_s(szSymPath, nSymPathLen, szTemp); + strcat_s(szSymPath, nSymPathLen, "\\websymbols"); + strcat_s(szSymPath, nSymPathLen, "*http://msdl.microsoft.com/download/symbols;"); + } + else + { + strcat_s(szSymPath, nSymPathLen, "SRV*c:\\websymbols*http://msdl.microsoft.com/download/symbols;"); + } + } + } // if SymBuildPath + + // First Init the whole stuff... + BOOL bRet = this->m_sw->Init(szSymPath); + if (szSymPath != nullptr) + { + free(szSymPath); + szSymPath = nullptr; + } + if (bRet == FALSE) + { + this->OnDbgHelpErr("Error while initializing dbghelp.dll", 0, 0); + SetLastError(ERROR_DLL_INIT_FAILED); + return FALSE; + } + + bRet = this->m_sw->LoadModules(this->m_hProcess, this->m_dwProcessId); + if (bRet != FALSE) + { + m_modulesLoaded = TRUE; + } + return bRet; +} + + + + + +// The following is used to pass the "userData"-Pointer to the user-provided readMemoryFunction +// This has to be done due to a problem with the "hProcess"-parameter in x64 +// Because this class is in no case multi-threading-enabled (because of the limitations +// of dbghelp.dll) it is "safe" to use a static-variable +static WinStackWalker::PReadProcessMemoryRoutine s_readMemoryFunction = nullptr; +static LPVOID s_readMemoryFunction_UserData = nullptr; + +BOOL WinStackWalker::ShowCallstack(HANDLE hThread, const CONTEXT *context, PReadProcessMemoryRoutine readMemoryFunction, LPVOID pUserData) +{ + CONTEXT c; + CallstackEntry csEntry; + IMAGEHLP_SYMBOL64 *pSym = nullptr; + StackWalkerInternal::IMAGEHLP_MODULE64_V2 Module; + IMAGEHLP_LINE64 Line; + int frameNum; + bool bLastEntryCalled = true; + int curRecursionCount = 0; + + if (!m_modulesLoaded) + { + this->LoadModules(); // ignore the result... + } + + if (this->m_sw->m_hDbhHelp == nullptr) + { + SetLastError(ERROR_DLL_INIT_FAILED); + return FALSE; + } + + s_readMemoryFunction = readMemoryFunction; + s_readMemoryFunction_UserData = pUserData; + + if (context == nullptr) + { + // If no context is provided, capture the context + if (hThread == GetCurrentThread()) + { + GET_CURRENT_CONTEXT(c, USED_CONTEXT_FLAGS); + } + else + { + SuspendThread(hThread); + memset(&c, 0, sizeof(CONTEXT)); + c.ContextFlags = USED_CONTEXT_FLAGS; + if (GetThreadContext(hThread, &c) == FALSE) + { + ResumeThread(hThread); + return FALSE; + } + } + } + else + { + c = *context; + } + + // init STACKFRAME for first call + STACKFRAME64 s; // in / out stackframe + memset(&s, 0, sizeof(s)); + DWORD imageType; + #ifdef _M_IX86 + // normally, call ImageNtHeader() and use machine info from PE header + imageType = IMAGE_FILE_MACHINE_I386; + s.AddrPC.Offset = c.Eip; + s.AddrPC.Mode = AddrModeFlat; + s.AddrFrame.Offset = c.Ebp; + s.AddrFrame.Mode = AddrModeFlat; + s.AddrStack.Offset = c.Esp; + s.AddrStack.Mode = AddrModeFlat; + #elif _M_X64 + imageType = IMAGE_FILE_MACHINE_AMD64; + s.AddrPC.Offset = c.Rip; + s.AddrPC.Mode = AddrModeFlat; + s.AddrFrame.Offset = c.Rsp; + s.AddrFrame.Mode = AddrModeFlat; + s.AddrStack.Offset = c.Rsp; + s.AddrStack.Mode = AddrModeFlat; + #elif _M_IA64 + imageType = IMAGE_FILE_MACHINE_IA64; + s.AddrPC.Offset = c.StIIP; + s.AddrPC.Mode = AddrModeFlat; + s.AddrFrame.Offset = c.IntSp; + s.AddrFrame.Mode = AddrModeFlat; + s.AddrBStore.Offset = c.RsBSP; + s.AddrBStore.Mode = AddrModeFlat; + s.AddrStack.Offset = c.IntSp; + s.AddrStack.Mode = AddrModeFlat; + #else + #error "Platform not supported!" + #endif + + pSym = (IMAGEHLP_SYMBOL64 *)malloc(sizeof(IMAGEHLP_SYMBOL64) + STACKWALK_MAX_NAMELEN); + if (!pSym) + { + goto cleanup; // not enough memory... + } + memset(pSym, 0, sizeof(IMAGEHLP_SYMBOL64) + STACKWALK_MAX_NAMELEN); + pSym->SizeOfStruct = sizeof(IMAGEHLP_SYMBOL64); + pSym->MaxNameLength = STACKWALK_MAX_NAMELEN; + + memset(&Line, 0, sizeof(Line)); + Line.SizeOfStruct = sizeof(Line); + + memset(&Module, 0, sizeof(Module)); + Module.SizeOfStruct = sizeof(Module); + + for (frameNum = 0; ; ++frameNum) + { + // get next stack frame (StackWalk64(), SymFunctionTableAccess64(), SymGetModuleBase64()) + // if this returns ERROR_INVALID_ADDRESS (487) or ERROR_NOACCESS (998), you can + // assume that either you are done, or that the stack is so hosed that the next + // deeper frame could not be found. + // CONTEXT need not to be suplied if imageTyp is IMAGE_FILE_MACHINE_I386! + if (!this->m_sw->pSW(imageType, this->m_hProcess, hThread, &s, &c, myReadProcMem, this->m_sw->pSFTA, this->m_sw->pSGMB, nullptr)) + { + // INFO: "StackWalk64" does not set "GetLastError"... + this->OnDbgHelpErr("StackWalk64", 0, s.AddrPC.Offset); + break; + } + + csEntry.offset = s.AddrPC.Offset; + csEntry.name[0] = 0; + csEntry.undName[0] = 0; + csEntry.undFullName[0] = 0; + csEntry.offsetFromSmybol = 0; + csEntry.offsetFromLine = 0; + csEntry.lineFileName[0] = 0; + csEntry.lineNumber = 0; + csEntry.loadedImageName[0] = 0; + csEntry.moduleName[0] = 0; + if (s.AddrPC.Offset == s.AddrReturn.Offset) + { + if ((this->m_MaxRecursionCount > 0) && (curRecursionCount > m_MaxRecursionCount)) + { + this->OnDbgHelpErr("StackWalk64-Endless-Callstack!", 0, s.AddrPC.Offset); + break; + } + curRecursionCount++; + } + else + { + curRecursionCount = 0; + } + if (s.AddrPC.Offset != 0) + { + // we seem to have a valid PC + // show procedure info (SymGetSymFromAddr64()) + if (this->m_sw->pSGSFA(this->m_hProcess, s.AddrPC.Offset, &(csEntry.offsetFromSmybol), pSym) != FALSE) + { + MyStrCpy(csEntry.name, pSym->Name); + // UnDecorateSymbolName() + this->m_sw->pUDSN(pSym->Name, csEntry.undName, static_cast(sizeof(csEntry.undName)), UNDNAME_NAME_ONLY); + this->m_sw->pUDSN(pSym->Name, csEntry.undFullName, static_cast(sizeof(csEntry.undFullName)), UNDNAME_COMPLETE); + } + else + { + this->OnDbgHelpErr("SymGetSymFromAddr64", GetLastError(), s.AddrPC.Offset); + } + + // show line number info, NT5.0-method (SymGetLineFromAddr64()) + if (this->m_sw->pSGLFA != nullptr) + { + if (this->m_sw->pSGLFA(this->m_hProcess, s.AddrPC.Offset, &(csEntry.offsetFromLine), &Line) != FALSE) + { + csEntry.lineNumber = Line.LineNumber; + MyStrCpy(csEntry.lineFileName, Line.FileName); + } + else + { + this->OnDbgHelpErr("SymGetLineFromAddr64", GetLastError(), s.AddrPC.Offset); + } + } + + // show module info (SymGetModuleInfo64()) + if (this->m_sw->GetModuleInfo(this->m_hProcess, s.AddrPC.Offset, &Module) != FALSE) + { + switch (Module.SymType) + { + case SymNone: csEntry.symTypeString = "-nosymbols-"; break; + case SymCoff: csEntry.symTypeString = "COFF"; break; + case SymCv: csEntry.symTypeString = "CV"; break; + case SymPdb: csEntry.symTypeString = "PDB"; break; + case SymExport: csEntry.symTypeString = "-exported-"; break; + case SymDeferred: csEntry.symTypeString = "-deferred-"; break; + case SymSym: csEntry.symTypeString = "SYM"; break; + case SymDia: csEntry.symTypeString = "DIA"; break; + case 8: csEntry.symTypeString = "Virtual"; break; + default: csEntry.symTypeString = nullptr; break; + } + + MyStrCpy(csEntry.moduleName, Module.ModuleName); + csEntry.baseOfImage = Module.BaseOfImage; + MyStrCpy(csEntry.loadedImageName, Module.LoadedImageName); + } + else + { + this->OnDbgHelpErr("SymGetModuleInfo64", GetLastError(), s.AddrPC.Offset); + } + } + + CallstackEntryType et = nextEntry; + if (frameNum == 0) + { + et = firstEntry; + } + bLastEntryCalled = false; + this->OnCallstackEntry(et, csEntry); + + if (s.AddrReturn.Offset == 0) + { + bLastEntryCalled = true; + this->OnCallstackEntry(lastEntry, csEntry); + SetLastError(ERROR_SUCCESS); + break; + } + } // for (frameNum) + +cleanup: + if (pSym) + { + free(pSym); + } + + if (bLastEntryCalled == false) + { + this->OnCallstackEntry(lastEntry, csEntry); + } + + if (context == nullptr) + { + ResumeThread(hThread); + } + + return TRUE; +} + + + + + +BOOL __stdcall WinStackWalker::myReadProcMem( + HANDLE hProcess, + DWORD64 qwBaseAddress, + PVOID lpBuffer, + DWORD nSize, + LPDWORD lpNumberOfBytesRead +) +{ + if (s_readMemoryFunction == nullptr) + { + SIZE_T st; + BOOL bRet = ReadProcessMemory(hProcess, (LPVOID)qwBaseAddress, lpBuffer, nSize, &st); + *lpNumberOfBytesRead = (DWORD)st; + // printf("ReadMemory: hProcess: %p, baseAddr: %p, buffer: %p, size: %d, read: %d, result: %d\n", hProcess, (LPVOID) qwBaseAddress, lpBuffer, nSize, (DWORD) st, (DWORD) bRet); + return bRet; + } + else + { + return s_readMemoryFunction(hProcess, qwBaseAddress, lpBuffer, nSize, lpNumberOfBytesRead, s_readMemoryFunction_UserData); + } +} + + + + + +void WinStackWalker::OnLoadModule(LPCSTR img, LPCSTR mod, DWORD64 baseAddr, DWORD size, DWORD result, LPCSTR symType, LPCSTR pdbName, ULONGLONG fileVersion) +{ + char buffer[STACKWALK_MAX_NAMELEN]; + if (fileVersion == 0) + { + _snprintf_s(buffer, sizeof(buffer), "%s:%s (%p), size: %d (result: %d), SymType: '%s', PDB: '%s'\n", img, mod, (LPVOID)baseAddr, size, result, symType, pdbName); + } + else + { + DWORD v4 = (DWORD)fileVersion & 0xFFFF; + DWORD v3 = (DWORD)(fileVersion >> 16) & 0xFFFF; + DWORD v2 = (DWORD)(fileVersion >> 32) & 0xFFFF; + DWORD v1 = (DWORD)(fileVersion >> 48) & 0xFFFF; + _snprintf_s(buffer, sizeof(buffer), "%s:%s (%p), size: %d (result: %d), SymType: '%s', PDB: '%s', fileVersion: %d.%d.%d.%d\n", img, mod, (LPVOID)baseAddr, size, result, symType, pdbName, v1, v2, v3, v4); + } + OnOutput(buffer); +} + + + + + +void WinStackWalker::OnCallstackEntry(CallstackEntryType eType, CallstackEntry &entry) +{ + CHAR buffer[STACKWALK_MAX_NAMELEN + MAX_PATH + 100]; + if ((eType == lastEntry) || (entry.offset == 0)) + { + return; + } + if (entry.undFullName[0] != 0) + { + MyStrCpy(entry.name, entry.undFullName); + } + else if (entry.undName[0] != 0) + { + MyStrCpy(entry.name, entry.undName); + } + else if (entry.name[0] == 0) + { + MyStrCpy(entry.name, "(function-name not available)"); + } + if (entry.lineFileName[0] == 0) + { + MyStrCpy(entry.lineFileName, "(filename not available)"); + if (entry.moduleName[0] == 0) + { + MyStrCpy(entry.moduleName, "(module-name not available)"); + } + _snprintf_s(buffer, sizeof(buffer), "%p (%s): %s: %s\n", (LPVOID)entry.offset, entry.moduleName, entry.lineFileName, entry.name); + } + else + { + _snprintf_s(buffer, sizeof(buffer), "%s (%d): %s\n", entry.lineFileName, entry.lineNumber, entry.name); + } + OnOutput(buffer); +} + + + + + +void WinStackWalker::OnDbgHelpErr(LPCSTR szFuncName, DWORD gle, DWORD64 addr) +{ + CHAR buffer[STACKWALK_MAX_NAMELEN]; + _snprintf_s(buffer, sizeof(buffer), "ERROR: %s, GetLastError: %d (Address: %p)\n", szFuncName, gle, (LPVOID)addr); + OnOutput(buffer); +} + + + + + +void WinStackWalker::OnSymInit(LPCSTR szSearchPath, DWORD symOptions, LPCSTR szUserName) +{ + CHAR buffer[STACKWALK_MAX_NAMELEN]; + _snprintf_s(buffer, sizeof(buffer), "SymInit: Symbol-SearchPath: '%s', symOptions: %d, UserName: '%s'\n", szSearchPath, symOptions, szUserName); + OnOutput(buffer); + // Also display the OS-version + OSVERSIONINFOEXA ver; + ZeroMemory(&ver, sizeof(OSVERSIONINFOEXA)); + ver.dwOSVersionInfoSize = sizeof(ver); + if (GetVersionExA((OSVERSIONINFOA*)&ver) != FALSE) + { + _snprintf_s(buffer, sizeof(buffer), "OS-Version: %d.%d.%d (%s) 0x%x-0x%x\n", + ver.dwMajorVersion, ver.dwMinorVersion, ver.dwBuildNumber, + ver.szCSDVersion, ver.wSuiteMask, ver.wProductType + ); + OnOutput(buffer); + } +} + + + + + +void WinStackWalker::OnOutput(LPCSTR buffer) +{ + OutputDebugStringA(buffer); +} + + + + +#endif // _WIN32 -- cgit v1.2.3