1 /**
2 * UGENE - Integrated Bioinformatics Tools.
3 * Copyright (C) 2008-2021 UniPro <ugene@unipro.ru>
4 * http://ugene.net
5 *
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License
8 * as published by the Free Software Foundation; either version 2
9 * of the License, or (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
19 * MA 02110-1301, USA.
20 */
21
22 /**********************************************************************
23 * LICENSE (http://www.opensource.org/licenses/bsd-license.php)
24 *
25 * Copyright (c) 2005-2011, Jochen Kalmbach
26 * All rights reserved.
27 *
28 * Redistribution and use in source and binary forms, with or without modification,
29 * are permitted provided that the following conditions are met:
30 *
31 * Redistributions of source code must retain the above copyright notice,
32 * this list of conditions and the following disclaimer.
33 * Redistributions in binary form must reproduce the above copyright notice,
34 * this list of conditions and the following disclaimer in the documentation
35 * and/or other materials provided with the distribution.
36 * Neither the name of Jochen Kalmbach nor the names of its contributors may be
37 * used to endorse or promote products derived from this software without
38 * specific prior written permission.
39 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
40 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
41 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
42 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
43 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
44 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
45 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
46 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
47 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
48 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
49 *
50 **********************************************************************/
51
52 #include "StackWalker.h"
53 #if defined(Q_OS_WIN)
54
55 # pragma comment(lib, "version.lib") // for "VerQueryValue"
56
57 # include <dbghelp.h>
58 # include <fstream>
59 # include <stdio.h>
60 # include <tchar.h>
61
62 # include <QFile>
63 # include <QStringList>
64
65 # define USED_CONTEXT_FLAGS CONTEXT_ALL
66
67 namespace U2 {
68
69 class StackWalkerInternal {
70 public:
StackWalkerInternal(StackWalker * parent,HANDLE hProcess)71 StackWalkerInternal(StackWalker *parent, HANDLE hProcess) {
72 m_parent = parent;
73 m_hDbhHelp = nullptr;
74 pSC = nullptr;
75 m_hProcess = hProcess;
76 m_szSymPath = nullptr;
77 pSFTA = nullptr;
78 pSGLFA = nullptr;
79 pSGMB = nullptr;
80 pSGMI = nullptr;
81 pSGO = nullptr;
82 pSGSFA = nullptr;
83 pSI = nullptr;
84 pSLM = nullptr;
85 pSSO = nullptr;
86 pSW = nullptr;
87 pUDSN = nullptr;
88 pSGSP = nullptr;
89 }
~StackWalkerInternal()90 ~StackWalkerInternal() {
91 if (pSC != nullptr) {
92 pSC(m_hProcess); // SymCleanup
93 }
94 if (m_hDbhHelp != nullptr) {
95 FreeLibrary(m_hDbhHelp);
96 }
97 m_hDbhHelp = nullptr;
98 m_parent = nullptr;
99 if (m_szSymPath != nullptr) {
100 free(m_szSymPath);
101 }
102 m_szSymPath = nullptr;
103 }
Init(LPCSTR szSymPath)104 BOOL Init(LPCSTR szSymPath) {
105 if (m_parent == nullptr) {
106 return FALSE;
107 }
108 // Dynamically load the Entry-Points for dbghelp.dll:
109 // First try to load the newest one from
110 wchar_t szTemp[4096];
111 // But before we do this, we first check if the ".local" file exists
112 if (GetModuleFileName(nullptr, szTemp, 4096) > 0) {
113 wcscat_s(szTemp, TEXT(".local"));
114 if (GetFileAttributes(szTemp) == INVALID_FILE_ATTRIBUTES) {
115 // ".local" file does not exist, so we can try to load the dbghelp.dll from the "Debugging Tools for Windows"
116 if (GetEnvironmentVariable(TEXT("ProgramFiles"), szTemp, 4096) > 0) {
117 wcscat_s(szTemp, TEXT("\\Debugging Tools for Windows\\dbghelp.dll"));
118 // now check if the file exists:
119 if (GetFileAttributes(szTemp) != INVALID_FILE_ATTRIBUTES) {
120 m_hDbhHelp = LoadLibrary(szTemp);
121 }
122 }
123 // Still not found? Then try to load the 64-Bit version:
124 if ((m_hDbhHelp == nullptr) && (GetEnvironmentVariable(TEXT("ProgramFiles"), szTemp, 4096) > 0)) {
125 wcscat_s(szTemp, TEXT("\\Debugging Tools for Windows 64-Bit\\dbghelp.dll"));
126 if (GetFileAttributes(szTemp) != INVALID_FILE_ATTRIBUTES) {
127 m_hDbhHelp = LoadLibrary(szTemp);
128 }
129 }
130 }
131 }
132 if (m_hDbhHelp == nullptr) { // if not already loaded, try to load a default-one
133 m_hDbhHelp = LoadLibrary(TEXT("dbghelp.dll"));
134 }
135 if (m_hDbhHelp == nullptr) {
136 return FALSE;
137 }
138 pSI = (tSI)GetProcAddress(m_hDbhHelp, "SymInitialize");
139 pSC = (tSC)GetProcAddress(m_hDbhHelp, "SymCleanup");
140
141 pSW = (tSW)GetProcAddress(m_hDbhHelp, "StackWalk64");
142 pSGO = (tSGO)GetProcAddress(m_hDbhHelp, "SymGetOptions");
143 pSSO = (tSSO)GetProcAddress(m_hDbhHelp, "SymSetOptions");
144
145 pSFTA = (tSFTA)GetProcAddress(m_hDbhHelp, "SymFunctionTableAccess64");
146 pSGLFA = (tSGLFA)GetProcAddress(m_hDbhHelp, "SymGetLineFromAddr64");
147 pSGMB = (tSGMB)GetProcAddress(m_hDbhHelp, "SymGetModuleBase64");
148 pSGMI = (tSGMI)GetProcAddress(m_hDbhHelp, "SymGetModuleInfo64");
149 // pSGMI_V3 = (tSGMI_V3) GetProcAddress(m_hDbhHelp, "SymGetModuleInfo64" );
150 pSGSFA = (tSGSFA)GetProcAddress(m_hDbhHelp, "SymGetSymFromAddr64");
151 pUDSN = (tUDSN)GetProcAddress(m_hDbhHelp, "UnDecorateSymbolName");
152 pSLM = (tSLM)GetProcAddress(m_hDbhHelp, "SymLoadModule64");
153 pSGSP = (tSGSP)GetProcAddress(m_hDbhHelp, "SymGetSearchPath");
154
155 if (pSC == nullptr || pSFTA == nullptr || pSGMB == nullptr || pSGMI == nullptr ||
156 pSGO == nullptr || pSGSFA == nullptr || pSI == nullptr || pSSO == nullptr ||
157 pSW == nullptr || pUDSN == nullptr || pSLM == nullptr) {
158 FreeLibrary(m_hDbhHelp);
159 m_hDbhHelp = nullptr;
160 pSC = nullptr;
161 return FALSE;
162 }
163
164 // SymInitialize
165 if (szSymPath != nullptr) {
166 m_szSymPath = _strdup(szSymPath);
167 }
168 if (this->pSI(m_hProcess, m_szSymPath, FALSE) == FALSE) {
169 this->m_parent->OnDbgHelpErr("SymInitialize", GetLastError(), 0);
170 }
171
172 DWORD symOptions = this->pSGO(); // SymGetOptions
173 symOptions |= SYMOPT_LOAD_LINES;
174 symOptions |= SYMOPT_FAIL_CRITICAL_ERRORS;
175 // SymSetOptions
176 symOptions = this->pSSO(symOptions);
177
178 char buf[StackWalker::STACKWALK_MAX_NAMELEN] = {0};
179 if (this->pSGSP != nullptr) {
180 if (this->pSGSP(m_hProcess, buf, StackWalker::STACKWALK_MAX_NAMELEN) == FALSE) {
181 this->m_parent->OnDbgHelpErr("SymGetSearchPath", GetLastError(), 0);
182 }
183 }
184 char szUserName[1024] = {0};
185 DWORD dwSize = 1024;
186 GetUserNameA(szUserName, &dwSize);
187 this->m_parent->OnSymInit(buf, symOptions, szUserName);
188
189 return TRUE;
190 }
191
192 StackWalker *m_parent;
193
194 HMODULE m_hDbhHelp;
195 HANDLE m_hProcess;
196 LPSTR m_szSymPath;
197
198 typedef struct _IMAGEHLP_MODULE64_V2 {
199 DWORD SizeOfStruct; // set to sizeof(IMAGEHLP_MODULE64)
200 DWORD64 BaseOfImage; // base load address of module
201 DWORD ImageSize; // virtual size of the loaded module
202 DWORD TimeDateStamp; // date/time stamp from pe header
203 DWORD CheckSum; // checksum from the pe header
204 DWORD NumSyms; // number of symbols in the symbol table
205 SYM_TYPE SymType; // type of symbols loaded
206 CHAR ModuleName[32]; // module name
207 CHAR ImageName[256]; // image name
208 CHAR LoadedImageName[256]; // symbol file name
209 } IMAGEHLP_MODULE64_V2;
210
211 // SymCleanup()
212 typedef BOOL(__stdcall *tSC)(IN HANDLE hProcess);
213 tSC pSC;
214
215 // SymFunctionTableAccess64()
216 typedef PVOID(__stdcall *tSFTA)(HANDLE hProcess, DWORD64 AddrBase);
217 tSFTA pSFTA;
218
219 // SymGetLineFromAddr64()
220 typedef BOOL(__stdcall *tSGLFA)(IN HANDLE hProcess, IN DWORD64 dwAddr, OUT PDWORD pdwDisplacement, OUT PIMAGEHLP_LINE64 Line);
221 tSGLFA pSGLFA;
222
223 // SymGetModuleBase64()
224 typedef DWORD64(__stdcall *tSGMB)(IN HANDLE hProcess, IN DWORD64 dwAddr);
225 tSGMB pSGMB;
226
227 // SymGetModuleInfo64()
228 typedef BOOL(__stdcall *tSGMI)(IN HANDLE hProcess, IN DWORD64 dwAddr, OUT IMAGEHLP_MODULE64_V2 *ModuleInfo);
229 tSGMI pSGMI;
230
231 // SymGetOptions()
232 typedef DWORD(__stdcall *tSGO)(VOID);
233 tSGO pSGO;
234
235 // SymGetSymFromAddr64()
236 typedef BOOL(__stdcall *tSGSFA)(IN HANDLE hProcess, IN DWORD64 dwAddr, OUT PDWORD64 pdwDisplacement, OUT PIMAGEHLP_SYMBOL64 Symbol);
237 tSGSFA pSGSFA;
238
239 // SymInitialize()
240 typedef BOOL(__stdcall *tSI)(IN HANDLE hProcess, IN PSTR UserSearchPath, IN BOOL fInvadeProcess);
241 tSI pSI;
242
243 // SymLoadModule64()
244 typedef DWORD64(__stdcall *tSLM)(IN HANDLE hProcess, IN HANDLE hFile, IN PSTR ImageName, IN PSTR ModuleName, IN DWORD64 BaseOfDll, IN DWORD SizeOfDll);
245 tSLM pSLM;
246
247 // SymSetOptions()
248 typedef DWORD(__stdcall *tSSO)(IN DWORD SymOptions);
249 tSSO pSSO;
250
251 // StackWalk64()
252 typedef BOOL(__stdcall *tSW)(
253 DWORD MachineType,
254 HANDLE hProcess,
255 HANDLE hThread,
256 LPSTACKFRAME64 StackFrame,
257 PVOID ContextRecord,
258 PREAD_PROCESS_MEMORY_ROUTINE64 ReadMemoryRoutine,
259 PFUNCTION_TABLE_ACCESS_ROUTINE64 FunctionTableAccessRoutine,
260 PGET_MODULE_BASE_ROUTINE64 GetModuleBaseRoutine,
261 PTRANSLATE_ADDRESS_ROUTINE64 TranslateAddress);
262 tSW pSW;
263
264 // UnDecorateSymbolName()
265 typedef DWORD(__stdcall WINAPI *tUDSN)(PCSTR DecoratedName, PSTR UnDecoratedName, DWORD UndecoratedLength, DWORD Flags);
266 tUDSN pUDSN;
267
268 typedef BOOL(__stdcall WINAPI *tSGSP)(HANDLE hProcess, PSTR SearchPath, DWORD SearchPathLength);
269 tSGSP pSGSP;
270
271 private:
272 // **************************************** ToolHelp32 ************************
273 # define MAX_MODULE_NAME32 255
274 # define TH32CS_SNAPMODULE 0x00000008
275 # pragma pack(push, 8)
276 typedef struct tagMODULEENTRY32 {
277 DWORD dwSize;
278 DWORD th32ModuleID; // This module
279 DWORD th32ProcessID; // owning process
280 DWORD GlblcntUsage; // Global usage count on the module
281 DWORD ProccntUsage; // Module usage count in th32ProcessID's context
282 BYTE *modBaseAddr; // Base address of module in th32ProcessID's context
283 DWORD modBaseSize; // Size in bytes of module starting at modBaseAddr
284 HMODULE hModule; // The hModule of this module in th32ProcessID's context
285 char szModule[MAX_MODULE_NAME32 + 1];
286 char szExePath[MAX_PATH];
287 } MODULEENTRY32;
288 typedef MODULEENTRY32 *PMODULEENTRY32;
289 typedef MODULEENTRY32 *LPMODULEENTRY32;
290 # pragma pack(pop)
291
GetModuleListTH32(HANDLE hProcess,DWORD pid)292 BOOL GetModuleListTH32(HANDLE hProcess, DWORD pid) {
293 // CreateToolhelp32Snapshot()
294 typedef HANDLE(__stdcall * tCT32S)(DWORD dwFlags, DWORD th32ProcessID);
295 // Module32First()
296 typedef BOOL(__stdcall * tM32F)(HANDLE hSnapshot, LPMODULEENTRY32 lpme);
297 // Module32Next()
298 typedef BOOL(__stdcall * tM32N)(HANDLE hSnapshot, LPMODULEENTRY32 lpme);
299
300 // try both dlls...
301 const wchar_t *dllname[] = {TEXT("kernel32.dll"), TEXT("tlhelp32.dll")};
302 HINSTANCE hToolhelp = nullptr;
303 tCT32S pCT32S = nullptr;
304 tM32F pM32F = nullptr;
305 tM32N pM32N = nullptr;
306
307 HANDLE hSnap;
308 MODULEENTRY32 me;
309 me.dwSize = sizeof(me);
310 BOOL keepGoing;
311 size_t i;
312
313 for (i = 0; i < (sizeof(dllname) / sizeof(dllname[0])); i++) {
314 hToolhelp = LoadLibrary(dllname[i]);
315 if (hToolhelp == nullptr) {
316 continue;
317 }
318 pCT32S = (tCT32S)GetProcAddress(hToolhelp, "CreateToolhelp32Snapshot");
319 pM32F = (tM32F)GetProcAddress(hToolhelp, "Module32First");
320 pM32N = (tM32N)GetProcAddress(hToolhelp, "Module32Next");
321 if ((pCT32S != nullptr) && (pM32F != nullptr) && (pM32N != nullptr)) {
322 break; // found the functions!
323 }
324 FreeLibrary(hToolhelp);
325 hToolhelp = nullptr;
326 }
327
328 if (hToolhelp == nullptr) {
329 return FALSE;
330 }
331
332 hSnap = pCT32S(TH32CS_SNAPMODULE, pid);
333 if (hSnap == (HANDLE)-1) {
334 return FALSE;
335 }
336
337 keepGoing = !!pM32F(hSnap, &me);
338 int cnt = 0;
339 while (keepGoing) {
340 this->LoadModule(hProcess, me.szExePath, me.szModule, (DWORD64)me.modBaseAddr, me.modBaseSize);
341 cnt++;
342 keepGoing = !!pM32N(hSnap, &me);
343 }
344 CloseHandle(hSnap);
345 FreeLibrary(hToolhelp);
346 if (cnt <= 0) {
347 return FALSE;
348 }
349 return TRUE;
350 } // GetModuleListTH32
351
352 // **************************************** PSAPI ************************
353 typedef struct _MODULEINFO {
354 LPVOID lpBaseOfDll;
355 DWORD SizeOfImage;
356 LPVOID EntryPoint;
357 } MODULEINFO, *LPMODULEINFO;
358
GetModuleListPSAPI(HANDLE hProcess)359 BOOL GetModuleListPSAPI(HANDLE hProcess) {
360 // EnumProcessModules()
361 typedef BOOL(__stdcall * tEPM)(HANDLE hProcess, HMODULE * lphModule, DWORD cb, LPDWORD lpcbNeeded);
362 // GetModuleFileNameEx()
363 typedef DWORD(__stdcall * tGMFNE)(HANDLE hProcess, HMODULE hModule, LPSTR lpFilename, DWORD nSize);
364 // GetModuleBaseName()
365 typedef DWORD(__stdcall * tGMBN)(HANDLE hProcess, HMODULE hModule, LPSTR lpFilename, DWORD nSize);
366 // GetModuleInformation()
367 typedef BOOL(__stdcall * tGMI)(HANDLE hProcess, HMODULE hModule, LPMODULEINFO pmi, DWORD nSize);
368
369 HINSTANCE hPsapi;
370 tEPM pEPM;
371 tGMFNE pGMFNE;
372 tGMBN pGMBN;
373 tGMI pGMI;
374
375 DWORD i;
376 // ModuleEntry e;
377 DWORD cbNeeded;
378 MODULEINFO mi;
379 HMODULE *hMods = 0;
380 char *tt = nullptr;
381 char *tt2 = nullptr;
382 const SIZE_T TTBUFLEN = 8096;
383 int cnt = 0;
384
385 hPsapi = LoadLibrary(TEXT("psapi.dll"));
386 if (hPsapi == nullptr) {
387 return FALSE;
388 }
389
390 pEPM = (tEPM)GetProcAddress(hPsapi, "EnumProcessModules");
391 pGMFNE = (tGMFNE)GetProcAddress(hPsapi, "GetModuleFileNameExA");
392 pGMBN = (tGMFNE)GetProcAddress(hPsapi, "GetModuleBaseNameA");
393 pGMI = (tGMI)GetProcAddress(hPsapi, "GetModuleInformation");
394 if ((pEPM == nullptr) || (pGMFNE == nullptr) || (pGMBN == nullptr) || (pGMI == nullptr)) {
395 // we couldn't find all functions
396 FreeLibrary(hPsapi);
397 return FALSE;
398 }
399
400 hMods = (HMODULE *)malloc(sizeof(HMODULE) * (TTBUFLEN / sizeof HMODULE));
401 tt = (char *)malloc(sizeof(char) * TTBUFLEN);
402 tt2 = (char *)malloc(sizeof(char) * TTBUFLEN);
403 if (!(((hMods == nullptr) || (tt == nullptr) || (tt2 == nullptr)) || !pEPM(hProcess, hMods, TTBUFLEN, &cbNeeded) || cbNeeded > TTBUFLEN)) {
404 for (i = 0; i < cbNeeded / sizeof hMods[0]; i++) {
405 // base address, size
406 pGMI(hProcess, hMods[i], &mi, sizeof mi);
407 // image file name
408 tt[0] = 0;
409 pGMFNE(hProcess, hMods[i], tt, TTBUFLEN);
410 // module name
411 tt2[0] = 0;
412 pGMBN(hProcess, hMods[i], tt2, TTBUFLEN);
413
414 DWORD dwRes = this->LoadModule(hProcess, tt, tt2, (DWORD64)mi.lpBaseOfDll, mi.SizeOfImage);
415 if (dwRes != ERROR_SUCCESS)
416 this->m_parent->OnDbgHelpErr("LoadModule", dwRes, 0);
417 cnt++;
418 }
419 }
420
421 if (hPsapi != nullptr)
422 FreeLibrary(hPsapi);
423 if (tt2 != nullptr)
424 free(tt2);
425 if (tt != nullptr)
426 free(tt);
427 if (hMods != nullptr)
428 free(hMods);
429
430 return cnt != 0;
431 } // GetModuleListPSAPI
432
LoadModule(HANDLE hProcess,LPCSTR img,LPCSTR mod,DWORD64 baseAddr,DWORD size)433 DWORD LoadModule(HANDLE hProcess, LPCSTR img, LPCSTR mod, DWORD64 baseAddr, DWORD size) {
434 CHAR *szImg = _strdup(img);
435 CHAR *szMod = _strdup(mod);
436 DWORD result = ERROR_SUCCESS;
437 if ((szImg == nullptr) || (szMod == nullptr))
438 result = ERROR_NOT_ENOUGH_MEMORY;
439 else {
440 if (pSLM(hProcess, 0, szImg, szMod, baseAddr, size) == 0)
441 result = GetLastError();
442 }
443 ULONGLONG fileVersion = 0;
444 if ((m_parent != nullptr) && (szImg != nullptr)) {
445 // try to retrive the file-version:
446 if ((this->m_parent->m_options & StackWalker::RetrieveFileVersion) != 0) {
447 VS_FIXEDFILEINFO *fInfo = nullptr;
448 DWORD dwHandle;
449 DWORD dwSize = GetFileVersionInfoSizeA(szImg, &dwHandle);
450 if (dwSize > 0) {
451 LPVOID vData = malloc(dwSize);
452 if (vData != nullptr) {
453 if (GetFileVersionInfoA(szImg, dwHandle, dwSize, vData) != 0) {
454 UINT len;
455 TCHAR szSubBlock[] = TEXT("\\");
456 if (VerQueryValue(vData, szSubBlock, (LPVOID *)&fInfo, &len) == 0)
457 fInfo = nullptr;
458 else {
459 fileVersion = ((ULONGLONG)fInfo->dwFileVersionLS) + ((ULONGLONG)fInfo->dwFileVersionMS << 32);
460 }
461 }
462 free(vData);
463 }
464 }
465 }
466
467 // Retrive some additional-infos about the module
468 IMAGEHLP_MODULE64_V2 Module;
469 const char *szSymType = "-unknown-";
470 if (this->GetModuleInfo(hProcess, baseAddr, &Module) != FALSE) {
471 switch (Module.SymType) {
472 case SymNone:
473 szSymType = "-nosymbols-";
474 break;
475 case SymCoff:
476 szSymType = "COFF";
477 break;
478 case SymCv:
479 szSymType = "CV";
480 break;
481 case SymPdb:
482 szSymType = "PDB";
483 break;
484 case SymExport:
485 szSymType = "-exported-";
486 break;
487 case SymDeferred:
488 szSymType = "-deferred-";
489 break;
490 case SymSym:
491 szSymType = "SYM";
492 break;
493 case 8: // SymVirtual:
494 szSymType = "Virtual";
495 break;
496 case 9: // SymDia:
497 szSymType = "DIA";
498 break;
499 }
500 }
501 this->m_parent->OnLoadModule(img, mod, baseAddr, size, result, szSymType, Module.LoadedImageName, fileVersion);
502 }
503 if (szImg != nullptr)
504 free(szImg);
505 if (szMod != nullptr)
506 free(szMod);
507 return result;
508 }
509
510 public:
LoadModules(HANDLE hProcess,DWORD dwProcessId)511 BOOL LoadModules(HANDLE hProcess, DWORD dwProcessId) {
512 // first try toolhelp32
513 if (GetModuleListTH32(hProcess, dwProcessId)) {
514 return true;
515 }
516 // then try psapi
517 return GetModuleListPSAPI(hProcess);
518 }
519
GetModuleInfo(HANDLE hProcess,DWORD64 baseAddr,IMAGEHLP_MODULE64_V2 * pModuleInfo)520 bool GetModuleInfo(HANDLE hProcess, DWORD64 baseAddr, IMAGEHLP_MODULE64_V2 *pModuleInfo) {
521 if (this->pSGMI == nullptr) {
522 SetLastError(ERROR_DLL_INIT_FAILED);
523 return FALSE;
524 }
525 pModuleInfo->SizeOfStruct = sizeof(IMAGEHLP_MODULE64_V2);
526 void *pData = malloc(4096); // reserve enough memory, so the bug in v6.3.5.1 does not lead to memory-overwrites...
527 if (pData == nullptr) {
528 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
529 return FALSE;
530 }
531 memcpy(pData, pModuleInfo, sizeof(IMAGEHLP_MODULE64_V2));
532 if (this->pSGMI(hProcess, baseAddr, (IMAGEHLP_MODULE64_V2 *)pData) != FALSE) {
533 // only copy as much memory as is reserved...
534 memcpy(pModuleInfo, pData, sizeof(IMAGEHLP_MODULE64_V2));
535 pModuleInfo->SizeOfStruct = sizeof(IMAGEHLP_MODULE64_V2);
536 free(pData);
537 return TRUE;
538 }
539 free(pData);
540 SetLastError(ERROR_DLL_INIT_FAILED);
541 return FALSE;
542 }
543 };
544
545 // #############################################################
StackWalker(DWORD dwProcessId,HANDLE hProcess)546 StackWalker::StackWalker(DWORD dwProcessId, HANDLE hProcess) {
547 this->m_options = OptionsAll;
548 this->m_modulesLoaded = FALSE;
549 this->m_hProcess = hProcess;
550 this->m_sw = new StackWalkerInternal(this, this->m_hProcess);
551 this->m_dwProcessId = dwProcessId;
552 this->m_szSymPath = nullptr;
553 }
StackWalker(int options,LPCSTR szSymPath,DWORD dwProcessId,HANDLE hProcess)554 StackWalker::StackWalker(int options, LPCSTR szSymPath, DWORD dwProcessId, HANDLE hProcess) {
555 this->m_options = options;
556 this->m_modulesLoaded = FALSE;
557 this->m_hProcess = hProcess;
558 this->m_sw = new StackWalkerInternal(this, this->m_hProcess);
559 this->m_dwProcessId = dwProcessId;
560 if (szSymPath != nullptr) {
561 this->m_szSymPath = _strdup(szSymPath);
562 this->m_options |= SymBuildPath;
563 } else {
564 this->m_szSymPath = nullptr;
565 }
566 }
567
~StackWalker()568 StackWalker::~StackWalker() {
569 if (m_szSymPath != nullptr) {
570 free(m_szSymPath);
571 }
572 m_szSymPath = nullptr;
573 if (this->m_sw != nullptr) {
574 delete this->m_sw;
575 }
576 this->m_sw = nullptr;
577 }
578
LoadModules()579 BOOL StackWalker::LoadModules() {
580 if (this->m_sw == nullptr) {
581 SetLastError(ERROR_DLL_INIT_FAILED);
582 return FALSE;
583 }
584 if (m_modulesLoaded != FALSE) {
585 return TRUE;
586 }
587
588 // Build the sym-path:
589 char *szSymPath = nullptr;
590 if ((this->m_options & SymBuildPath) != 0) {
591 const size_t nSymPathLen = 4096;
592 szSymPath = (char *)malloc(nSymPathLen);
593 if (szSymPath == nullptr) {
594 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
595 return FALSE;
596 }
597 szSymPath[0] = 0;
598 // Now first add the (optional) provided sympath:
599 if (this->m_szSymPath != nullptr) {
600 strcat_s(szSymPath, nSymPathLen, this->m_szSymPath);
601 strcat_s(szSymPath, nSymPathLen, ";");
602 }
603
604 strcat_s(szSymPath, nSymPathLen, ".;");
605
606 const size_t nTempLen = 1024;
607 char szTemp[nTempLen];
608 // Now add the current folder:
609 if (GetCurrentDirectoryA(nTempLen, szTemp) > 0) {
610 szTemp[nTempLen - 1] = 0;
611 strcat_s(szSymPath, nSymPathLen, szTemp);
612 strcat_s(szSymPath, nSymPathLen, ";");
613 }
614
615 // Now add the path for the main-module:
616 if (GetModuleFileNameA(nullptr, szTemp, nTempLen) > 0) {
617 szTemp[nTempLen - 1] = 0;
618 for (char *p = (szTemp + strlen(szTemp) - 1); p >= szTemp; --p) {
619 // locate the rightmost path separator
620 if ((*p == '\\') || (*p == '/') || (*p == ':')) {
621 *p = 0;
622 break;
623 }
624 } // for (search for path separator...)
625 if (strlen(szTemp) > 0) {
626 strcat_s(szSymPath, nSymPathLen, szTemp);
627 strcat_s(szSymPath, nSymPathLen, ";");
628 }
629 }
630 if (GetEnvironmentVariableA("_NT_SYMBOL_PATH", szTemp, nTempLen) > 0) {
631 szTemp[nTempLen - 1] = 0;
632 strcat_s(szSymPath, nSymPathLen, szTemp);
633 strcat_s(szSymPath, nSymPathLen, ";");
634 }
635 if (GetEnvironmentVariableA("_NT_ALTERNATE_SYMBOL_PATH", szTemp, nTempLen) > 0) {
636 szTemp[nTempLen - 1] = 0;
637 strcat_s(szSymPath, nSymPathLen, szTemp);
638 strcat_s(szSymPath, nSymPathLen, ";");
639 }
640 if (GetEnvironmentVariableA("SYSTEMROOT", szTemp, nTempLen) > 0) {
641 szTemp[nTempLen - 1] = 0;
642 strcat_s(szSymPath, nSymPathLen, szTemp);
643 strcat_s(szSymPath, nSymPathLen, ";");
644 // also add the "system32"-folder:
645 strcat_s(szTemp, nTempLen, "\\system32");
646 strcat_s(szSymPath, nSymPathLen, szTemp);
647 strcat_s(szSymPath, nSymPathLen, ";");
648 }
649
650 if ((this->m_options & SymBuildPath) != 0) {
651 if (GetEnvironmentVariableA("SYSTEMDRIVE", szTemp, nTempLen) > 0) {
652 szTemp[nTempLen - 1] = 0;
653 strcat_s(szSymPath, nSymPathLen, "SRV*");
654 strcat_s(szSymPath, nSymPathLen, szTemp);
655 strcat_s(szSymPath, nSymPathLen, "\\websymbols");
656 strcat_s(szSymPath, nSymPathLen, "*http://msdl.microsoft.com/download/symbols;");
657 } else {
658 strcat_s(szSymPath, nSymPathLen, "SRV*c:\\websymbols*http://msdl.microsoft.com/download/symbols;");
659 }
660 }
661 }
662
663 // First Init the whole stuff...
664 BOOL bRet = this->m_sw->Init(szSymPath);
665 if (szSymPath != nullptr) {
666 free(szSymPath);
667 }
668 szSymPath = nullptr;
669 if (bRet == FALSE) {
670 this->OnDbgHelpErr("Error while initializing dbghelp.dll", 0, 0);
671 SetLastError(ERROR_DLL_INIT_FAILED);
672 return FALSE;
673 }
674
675 bRet = this->m_sw->LoadModules(this->m_hProcess, this->m_dwProcessId);
676 if (bRet != FALSE) {
677 m_modulesLoaded = TRUE;
678 }
679 return bRet;
680 }
681
682 // The following is used to pass the "userData"-Pointer to the user-provided readMemoryFunction
683 // This has to be done due to a problem with the "hProcess"-parameter in x64...
684 // Because this class is in no case multi-threading-enabled (because of the limitations
685 // of dbghelp.dll) it is "safe" to use a static-variable
686 static StackWalker::PReadProcessMemoryRoutine s_readMemoryFunction = nullptr;
687 static LPVOID s_readMemoryFunction_UserData = nullptr;
688
ShowCallstack(HANDLE hThread,const CONTEXT * context,PReadProcessMemoryRoutine readMemoryFunction,LPVOID pUserData)689 bool StackWalker::ShowCallstack(HANDLE hThread, const CONTEXT *context, PReadProcessMemoryRoutine readMemoryFunction, LPVOID pUserData) {
690 CONTEXT c;
691 CallstackEntry csEntry;
692 IMAGEHLP_SYMBOL64 *pSym = nullptr;
693 StackWalkerInternal::IMAGEHLP_MODULE64_V2 Module;
694 IMAGEHLP_LINE64 Line;
695 int frameNum;
696
697 if (m_modulesLoaded == FALSE)
698 this->LoadModules(); // ignore the result...
699
700 if (this->m_sw->m_hDbhHelp == nullptr) {
701 SetLastError(ERROR_DLL_INIT_FAILED);
702 return FALSE;
703 }
704
705 s_readMemoryFunction = readMemoryFunction;
706 s_readMemoryFunction_UserData = pUserData;
707
708 c = *context;
709
710 // init STACKFRAME for first call
711 STACKFRAME64 s; // in/out stackframe
712 memset(&s, 0, sizeof(s));
713 DWORD imageType;
714 # ifdef _M_IX86
715 // normally, call ImageNtHeader() and use machine info from PE header
716 imageType = IMAGE_FILE_MACHINE_I386;
717 s.AddrPC.Offset = c.Eip;
718 s.AddrPC.Mode = AddrModeFlat;
719 s.AddrFrame.Offset = c.Ebp;
720 s.AddrFrame.Mode = AddrModeFlat;
721 s.AddrStack.Offset = c.Esp;
722 s.AddrStack.Mode = AddrModeFlat;
723 # elif _M_X64
724 imageType = IMAGE_FILE_MACHINE_AMD64;
725 s.AddrPC.Offset = c.Rip;
726 s.AddrPC.Mode = AddrModeFlat;
727 s.AddrFrame.Offset = c.Rsp;
728 s.AddrFrame.Mode = AddrModeFlat;
729 s.AddrStack.Offset = c.Rsp;
730 s.AddrStack.Mode = AddrModeFlat;
731 # elif _M_IA64
732 imageType = IMAGE_FILE_MACHINE_IA64;
733 s.AddrPC.Offset = c.StIIP;
734 s.AddrPC.Mode = AddrModeFlat;
735 s.AddrFrame.Offset = c.IntSp;
736 s.AddrFrame.Mode = AddrModeFlat;
737 s.AddrBStore.Offset = c.RsBSP;
738 s.AddrBStore.Mode = AddrModeFlat;
739 s.AddrStack.Offset = c.IntSp;
740 s.AddrStack.Mode = AddrModeFlat;
741 # else
742 # error "Platform not supported!"
743 # endif
744
745 pSym = (IMAGEHLP_SYMBOL64 *)malloc(sizeof(IMAGEHLP_SYMBOL64) + STACKWALK_MAX_NAMELEN);
746 if (!pSym)
747 goto cleanup; // not enough memory...
748 memset(pSym, 0, sizeof(IMAGEHLP_SYMBOL64) + STACKWALK_MAX_NAMELEN);
749 pSym->SizeOfStruct = sizeof(IMAGEHLP_SYMBOL64);
750 pSym->MaxNameLength = STACKWALK_MAX_NAMELEN;
751
752 memset(&Line, 0, sizeof(Line));
753 Line.SizeOfStruct = sizeof(Line);
754
755 memset(&Module, 0, sizeof(Module));
756 Module.SizeOfStruct = sizeof(Module);
757
758 for (frameNum = 0;; ++frameNum) {
759 // get next stack frame (StackWalk64(), SymFunctionTableAccess64(), SymGetModuleBase64())
760 // if this returns ERROR_INVALID_ADDRESS (487) or ERROR_NOACCESS (998), you can
761 // assume that either you are done, or that the stack is so hosed that the next
762 // deeper frame could not be found.
763 // CONTEXT need not to be suplied if imageTyp is IMAGE_FILE_MACHINE_I386!
764 if (!this->m_sw->pSW(imageType, this->m_hProcess, hThread, &s, &c, myReadProcMem, this->m_sw->pSFTA, this->m_sw->pSGMB, nullptr)) {
765 this->OnDbgHelpErr("StackWalk64", GetLastError(), s.AddrPC.Offset);
766 break;
767 }
768
769 csEntry.offset = s.AddrPC.Offset;
770 csEntry.name[0] = 0;
771 csEntry.undName[0] = 0;
772 csEntry.undFullName[0] = 0;
773 csEntry.offsetFromSmybol = 0;
774 csEntry.offsetFromLine = 0;
775 csEntry.lineFileName[0] = 0;
776 csEntry.lineNumber = 0;
777 csEntry.loadedImageName[0] = 0;
778 csEntry.moduleName[0] = 0;
779 if (s.AddrPC.Offset == s.AddrReturn.Offset) {
780 this->OnDbgHelpErr("StackWalk64-Endless-Callstack!", 0, s.AddrPC.Offset);
781 break;
782 }
783 if (s.AddrPC.Offset != 0) {
784 // we seem to have a valid PC
785 // show procedure info (SymGetSymFromAddr64())
786 if (this->m_sw->pSGSFA(this->m_hProcess, s.AddrPC.Offset, &(csEntry.offsetFromSmybol), pSym) != FALSE) {
787 // TODO: Mache dies sicher...!
788 strcpy_s(csEntry.name, pSym->Name);
789 // UnDecorateSymbolName()
790 this->m_sw->pUDSN(pSym->Name, csEntry.undName, STACKWALK_MAX_NAMELEN, UNDNAME_NAME_ONLY);
791 this->m_sw->pUDSN(pSym->Name, csEntry.undFullName, STACKWALK_MAX_NAMELEN, UNDNAME_COMPLETE);
792 } else {
793 this->OnDbgHelpErr("SymGetSymFromAddr64", GetLastError(), s.AddrPC.Offset);
794 }
795
796 // show line number info, NT5.0-method (SymGetLineFromAddr64())
797 if (this->m_sw->pSGLFA != nullptr) { // yes, we have SymGetLineFromAddr64()
798 if (this->m_sw->pSGLFA(this->m_hProcess, s.AddrPC.Offset, &(csEntry.offsetFromLine), &Line) != FALSE) {
799 csEntry.lineNumber = Line.LineNumber;
800 // TODO: Mache dies sicher...!
801 strcpy_s(csEntry.lineFileName, Line.FileName);
802 } else {
803 this->OnDbgHelpErr("SymGetLineFromAddr64", GetLastError(), s.AddrPC.Offset);
804 }
805 } // yes, we have SymGetLineFromAddr64()
806
807 // show module info (SymGetModuleInfo64())
808 if (this->m_sw->GetModuleInfo(this->m_hProcess, s.AddrPC.Offset, &Module) != FALSE) { // got module info OK
809 switch (Module.SymType) {
810 case SymNone:
811 csEntry.symTypeString = "-nosymbols-";
812 break;
813 case SymCoff:
814 csEntry.symTypeString = "COFF";
815 break;
816 case SymCv:
817 csEntry.symTypeString = "CV";
818 break;
819 case SymPdb:
820 csEntry.symTypeString = "PDB";
821 break;
822 case SymExport:
823 csEntry.symTypeString = "-exported-";
824 break;
825 case SymDeferred:
826 csEntry.symTypeString = "-deferred-";
827 break;
828 case SymSym:
829 csEntry.symTypeString = "SYM";
830 break;
831 # if API_VERSION_NUMBER >= 9
832 case SymDia:
833 csEntry.symTypeString = "DIA";
834 break;
835 # endif
836 case 8: // SymVirtual:
837 csEntry.symTypeString = "Virtual";
838 break;
839 default:
840 //_snprintf( ty, sizeof ty, "symtype=%ld", (long) Module.SymType );
841 csEntry.symTypeString = nullptr;
842 break;
843 }
844
845 // TODO: Mache dies sicher...!
846 strcpy_s(csEntry.moduleName, Module.ModuleName);
847 csEntry.baseOfImage = Module.BaseOfImage;
848 strcpy_s(csEntry.loadedImageName, Module.LoadedImageName);
849 } // got module info OK
850 else {
851 this->OnDbgHelpErr("SymGetModuleInfo64", GetLastError(), s.AddrPC.Offset);
852 }
853 } // we seem to have a valid PC
854
855 CallstackEntryType et = nextEntry;
856 if (frameNum == 0)
857 et = firstEntry;
858 this->OnCallstackEntry(et, csEntry);
859
860 if (s.AddrReturn.Offset == 0) {
861 this->OnCallstackEntry(lastEntry, csEntry);
862 SetLastError(ERROR_SUCCESS);
863 break;
864 }
865 } // for ( frameNum )
866
867 cleanup:
868 if (pSym)
869 free(pSym);
870
871 if (context == nullptr)
872 ResumeThread(hThread);
873
874 return TRUE;
875 }
876
myReadProcMem(HANDLE hProcess,DWORD64 qwBaseAddress,PVOID lpBuffer,DWORD nSize,LPDWORD lpNumberOfBytesRead)877 BOOL __stdcall StackWalker::myReadProcMem(
878 HANDLE hProcess,
879 DWORD64 qwBaseAddress,
880 PVOID lpBuffer,
881 DWORD nSize,
882 LPDWORD lpNumberOfBytesRead) {
883 if (s_readMemoryFunction == nullptr) {
884 SIZE_T st;
885 BOOL bRet = ReadProcessMemory(hProcess, (LPVOID)qwBaseAddress, lpBuffer, nSize, &st);
886 *lpNumberOfBytesRead = (DWORD)st;
887 return bRet;
888 } else {
889 return s_readMemoryFunction(hProcess, qwBaseAddress, lpBuffer, nSize, lpNumberOfBytesRead, s_readMemoryFunction_UserData);
890 }
891 }
892
OnLoadModule(LPCSTR img,LPCSTR mod,DWORD64 baseAddr,DWORD size,DWORD result,LPCSTR symType,LPCSTR pdbName,ULONGLONG fileVersion)893 void StackWalker::OnLoadModule(LPCSTR img, LPCSTR mod, DWORD64 baseAddr, DWORD size, DWORD result, LPCSTR symType, LPCSTR pdbName, ULONGLONG fileVersion) {
894 libraries[(LPVOID)baseAddr] = QString(img);
895 }
896
OnCallstackEntry(CallstackEntryType eType,CallstackEntry & entry)897 void StackWalker::OnCallstackEntry(CallstackEntryType eType, CallstackEntry &entry) {
898 QMap<LPVOID, QString>::iterator it = libraries.begin();
899 while (entry.offset > (DWORD)it.key() && it != libraries.end()) {
900 it++;
901 }
902 it--;
903 QString library = it.value();
904 DWORD libAddr = (DWORD)it.key();
905 QString binaryName = library.split("\\").last();
906 library.replace("dll", "map");
907 library.replace("exe", "map");
908 QFile file(library);
909 if (!file.exists()) {
910 QString str;
911 buffer += str.sprintf("%p", entry.offset) + ": (" + binaryName + ") " + entry.name + "\n";
912 return;
913 }
914 file.open(QIODevice::ReadOnly);
915 DWORD preferredAddress = 0x10000000;
916
917 QString name;
918 DWORD offset = 0;
919 while (!file.readLine().contains("Rva+Base") && !file.atEnd())
920 ;
921 file.readLine();
922 while (!file.atEnd()) {
923 QString line = file.readLine();
924 QStringList list = line.split(" ", QString::SkipEmptyParts);
925 if (list.size() > 3) {
926 DWORD addr = list[2].toInt(nullptr, 16);
927 DWORD actualAddress = preferredAddress + entry.offset - libAddr;
928 if (actualAddress < addr) {
929 break;
930 }
931 name = list[1];
932 offset = actualAddress - addr;
933 }
934 }
935 if (file.atEnd()) {
936 name = "Some static function";
937 }
938 file.close();
939 QString str;
940 buffer += str.sprintf("%p", entry.offset) + ": (" + binaryName + ") " + name.replace("@", ":") + " + " + str.sprintf("%p", offset) + "\n";
941 }
942
OnDbgHelpErr(LPCSTR szFuncName,DWORD gle,DWORD64 addr)943 void StackWalker::OnDbgHelpErr(LPCSTR szFuncName, DWORD gle, DWORD64 addr) {
944 // CHAR buffer[STACKWALK_MAX_NAMELEN];
945 //_snprintf_s(buffer, STACKWALK_MAX_NAMELEN, "ERROR: %s, GetLastError: %d (Address: %p)\n", szFuncName, gle, (LPVOID) addr);
946 // OnOutput("--");
947 }
948
OnSymInit(LPCSTR szSearchPath,DWORD symOptions,LPCSTR szUserName)949 void StackWalker::OnSymInit(LPCSTR szSearchPath, DWORD symOptions, LPCSTR szUserName) {
950 CHAR buffer[STACKWALK_MAX_NAMELEN];
951 _snprintf_s(buffer, STACKWALK_MAX_NAMELEN, "SymInit: Symbol-SearchPath: '%s', symOptions: %d, UserName: '%s'\n", szSearchPath, symOptions, szUserName);
952 // Also display the OS-version
953 OSVERSIONINFOEXA ver;
954 ZeroMemory(&ver, sizeof(OSVERSIONINFOEXA));
955 ver.dwOSVersionInfoSize = sizeof(ver);
956 if (GetVersionExA((OSVERSIONINFOA *)&ver) != FALSE) {
957 _snprintf_s(buffer, STACKWALK_MAX_NAMELEN, "OS-Version: %d.%d.%d (%s) 0x%x-0x%x\n", ver.dwMajorVersion, ver.dwMinorVersion, ver.dwBuildNumber, ver.szCSDVersion, ver.wSuiteMask, ver.wProductType);
958 OnOutput(buffer);
959 }
960 }
961
OnOutput(LPCSTR _buffer)962 void StackWalker::OnOutput(LPCSTR _buffer) {
963 buffer.append(_buffer);
964 }
965
966 } // namespace U2
967
968 #endif
969