1 /**********************************************************************
2  *
3  * StackWalker.cpp
4  * https://github.com/JochenKalmbach/StackWalker
5  *
6  * Old location: http://stackwalker.codeplex.com/
7  *
8  *
9  * History:
10  *  2005-07-27   v1    - First public release on http://www.codeproject.com/
11  *                       http://www.codeproject.com/threads/StackWalker.asp
12  *  2005-07-28   v2    - Changed the params of the constructor and ShowCallstack
13  *                       (to simplify the usage)
14  *  2005-08-01   v3    - Changed to use 'CONTEXT_FULL' instead of CONTEXT_ALL
15  *                       (should also be enough)
16  *                     - Changed to compile correctly with the PSDK of VC7.0
17  *                       (GetFileVersionInfoSizeA and GetFileVersionInfoA is wrongly defined:
18  *                        it uses LPSTR instead of LPCSTR as first parameter)
19  *                     - Added declarations to support VC5/6 without using 'dbghelp.h'
20  *                     - Added a 'pUserData' member to the ShowCallstack function and the
21  *                       PReadProcessMemoryRoutine declaration (to pass some user-defined data,
22  *                       which can be used in the readMemoryFunction-callback)
23  *  2005-08-02   v4    - OnSymInit now also outputs the OS-Version by default
24  *                     - Added example for doing an exception-callstack-walking in main.cpp
25  *                       (thanks to owillebo: http://www.codeproject.com/script/profile/whos_who.asp?id=536268)
26  *  2005-08-05   v5    - Removed most Lint (http://www.gimpel.com/) errors... thanks to Okko Willeboordse!
27  *  2008-08-04   v6    - Fixed Bug: Missing LEAK-end-tag
28  *                       http://www.codeproject.com/KB/applications/leakfinder.aspx?msg=2502890#xx2502890xx
29  *                       Fixed Bug: Compiled with "WIN32_LEAN_AND_MEAN"
30  *                       http://www.codeproject.com/KB/applications/leakfinder.aspx?msg=1824718#xx1824718xx
31  *                       Fixed Bug: Compiling with "/Wall"
32  *                       http://www.codeproject.com/KB/threads/StackWalker.aspx?msg=2638243#xx2638243xx
33  *                       Fixed Bug: Now checking SymUseSymSrv
34  *                       http://www.codeproject.com/KB/threads/StackWalker.aspx?msg=1388979#xx1388979xx
35  *                       Fixed Bug: Support for recursive function calls
36  *                       http://www.codeproject.com/KB/threads/StackWalker.aspx?msg=1434538#xx1434538xx
37  *                       Fixed Bug: Missing FreeLibrary call in "GetModuleListTH32"
38  *                       http://www.codeproject.com/KB/threads/StackWalker.aspx?msg=1326923#xx1326923xx
39  *                       Fixed Bug: SymDia is number 7, not 9!
40  *  2008-09-11   v7      For some (undocumented) reason, dbhelp.h is needing a packing of 8!
41  *                       Thanks to Teajay which reported the bug...
42  *                       http://www.codeproject.com/KB/applications/leakfinder.aspx?msg=2718933#xx2718933xx
43  *  2008-11-27   v8      Debugging Tools for Windows are now stored in a different directory
44  *                       Thanks to Luiz Salamon which reported this "bug"...
45  *                       http://www.codeproject.com/KB/threads/StackWalker.aspx?msg=2822736#xx2822736xx
46  *  2009-04-10   v9      License slightly corrected (<ORGANIZATION> replaced)
47  *  2009-11-01   v10     Moved to http://stackwalker.codeplex.com/
48  *  2009-11-02   v11     Now try to use IMAGEHLP_MODULE64_V3 if available
49  *  2010-04-15   v12     Added support for VS2010 RTM
50  *  2010-05-25   v13     Now using secure MyStrcCpy. Thanks to luke.simon:
51  *                       http://www.codeproject.com/KB/applications/leakfinder.aspx?msg=3477467#xx3477467xx
52  *  2013-01-07   v14     Runtime Check Error VS2010 Debug Builds fixed:
53  *                       http://stackwalker.codeplex.com/workitem/10511
54  *
55  *
56  * LICENSE (http://www.opensource.org/licenses/bsd-license.php)
57  *
58  *   Copyright (c) 2005-2013, Jochen Kalmbach
59  *   All rights reserved.
60  *
61  *   Redistribution and use in source and binary forms, with or without modification,
62  *   are permitted provided that the following conditions are met:
63  *
64  *   Redistributions of source code must retain the above copyright notice,
65  *   this list of conditions and the following disclaimer.
66  *   Redistributions in binary form must reproduce the above copyright notice,
67  *   this list of conditions and the following disclaimer in the documentation
68  *   and/or other materials provided with the distribution.
69  *   Neither the name of Jochen Kalmbach nor the names of its contributors may be
70  *   used to endorse or promote products derived from this software without
71  *   specific prior written permission.
72  *   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
73  *   AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
74  *   THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
75  *   ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
76  *   FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
77  *   (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
78  *   LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
79  *   ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
80  *   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
81  *   SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
82  *
83  **********************************************************************/
84 
85 #include "StackWalker.h"
86 
87 #include <stdio.h>
88 #include <stdlib.h>
89 #include <tchar.h>
90 #include <windows.h>
91 #pragma comment(lib, "version.lib") // for "VerQueryValue"
92 #pragma warning(disable : 4826)
93 
94 
95 // If VC7 and later, then use the shipped 'dbghelp.h'-file
96 #pragma pack(push, 8)
97 #if _MSC_VER >= 1300
98 #include <dbghelp.h>
99 #else
100 // inline the important dbghelp.h-declarations...
101 typedef enum
102 {
103   SymNone = 0,
104   SymCoff,
105   SymCv,
106   SymPdb,
107   SymExport,
108   SymDeferred,
109   SymSym,
110   SymDia,
111   SymVirtual,
112   NumSymTypes
113 } SYM_TYPE;
114 typedef struct _IMAGEHLP_LINE64
115 {
116   DWORD   SizeOfStruct; // set to sizeof(IMAGEHLP_LINE64)
117   PVOID   Key;          // internal
118   DWORD   LineNumber;   // line number in file
119   PCHAR   FileName;     // full filename
120   DWORD64 Address;      // first instruction of line
121 } IMAGEHLP_LINE64, *PIMAGEHLP_LINE64;
122 typedef struct _IMAGEHLP_MODULE64
123 {
124   DWORD    SizeOfStruct;         // set to sizeof(IMAGEHLP_MODULE64)
125   DWORD64  BaseOfImage;          // base load address of module
126   DWORD    ImageSize;            // virtual size of the loaded module
127   DWORD    TimeDateStamp;        // date/time stamp from pe header
128   DWORD    CheckSum;             // checksum from the pe header
129   DWORD    NumSyms;              // number of symbols in the symbol table
130   SYM_TYPE SymType;              // type of symbols loaded
131   CHAR     ModuleName[32];       // module name
132   CHAR     ImageName[256];       // image name
133   CHAR     LoadedImageName[256]; // symbol file name
134 } IMAGEHLP_MODULE64, *PIMAGEHLP_MODULE64;
135 typedef struct _IMAGEHLP_SYMBOL64
136 {
137   DWORD   SizeOfStruct;  // set to sizeof(IMAGEHLP_SYMBOL64)
138   DWORD64 Address;       // virtual address including dll base address
139   DWORD   Size;          // estimated size of symbol, can be zero
140   DWORD   Flags;         // info about the symbols, see the SYMF defines
141   DWORD   MaxNameLength; // maximum size of symbol name in 'Name'
142   CHAR    Name[1];       // symbol name (null terminated string)
143 } IMAGEHLP_SYMBOL64, *PIMAGEHLP_SYMBOL64;
144 typedef enum
145 {
146   AddrMode1616,
147   AddrMode1632,
148   AddrModeReal,
149   AddrModeFlat
150 } ADDRESS_MODE;
151 typedef struct _tagADDRESS64
152 {
153   DWORD64      Offset;
154   WORD         Segment;
155   ADDRESS_MODE Mode;
156 } ADDRESS64, *LPADDRESS64;
157 typedef struct _KDHELP64
158 {
159   DWORD64 Thread;
160   DWORD   ThCallbackStack;
161   DWORD   ThCallbackBStore;
162   DWORD   NextCallback;
163   DWORD   FramePointer;
164   DWORD64 KiCallUserMode;
165   DWORD64 KeUserCallbackDispatcher;
166   DWORD64 SystemRangeStart;
167   DWORD64 Reserved[8];
168 } KDHELP64, *PKDHELP64;
169 typedef struct _tagSTACKFRAME64
170 {
171   ADDRESS64 AddrPC;         // program counter
172   ADDRESS64 AddrReturn;     // return address
173   ADDRESS64 AddrFrame;      // frame pointer
174   ADDRESS64 AddrStack;      // stack pointer
175   ADDRESS64 AddrBStore;     // backing store pointer
176   PVOID     FuncTableEntry; // pointer to pdata/fpo or NULL
177   DWORD64   Params[4];      // possible arguments to the function
178   BOOL      Far;            // WOW far call
179   BOOL      Virtual;        // is this a virtual frame?
180   DWORD64   Reserved[3];
181   KDHELP64  KdHelp;
182 } STACKFRAME64, *LPSTACKFRAME64;
183 typedef BOOL(__stdcall* PREAD_PROCESS_MEMORY_ROUTINE64)(HANDLE  hProcess,
184                                                         DWORD64 qwBaseAddress,
185                                                         PVOID   lpBuffer,
186                                                         DWORD   nSize,
187                                                         LPDWORD lpNumberOfBytesRead);
188 typedef PVOID(__stdcall* PFUNCTION_TABLE_ACCESS_ROUTINE64)(HANDLE hProcess, DWORD64 AddrBase);
189 typedef DWORD64(__stdcall* PGET_MODULE_BASE_ROUTINE64)(HANDLE hProcess, DWORD64 Address);
190 typedef DWORD64(__stdcall* PTRANSLATE_ADDRESS_ROUTINE64)(HANDLE      hProcess,
191                                                          HANDLE      hThread,
192                                                          LPADDRESS64 lpaddr);
193 
194 // clang-format off
195 #define SYMOPT_CASE_INSENSITIVE         0x00000001
196 #define SYMOPT_UNDNAME                  0x00000002
197 #define SYMOPT_DEFERRED_LOADS           0x00000004
198 #define SYMOPT_NO_CPP                   0x00000008
199 #define SYMOPT_LOAD_LINES               0x00000010
200 #define SYMOPT_OMAP_FIND_NEAREST        0x00000020
201 #define SYMOPT_LOAD_ANYTHING            0x00000040
202 #define SYMOPT_IGNORE_CVREC             0x00000080
203 #define SYMOPT_NO_UNQUALIFIED_LOADS     0x00000100
204 #define SYMOPT_FAIL_CRITICAL_ERRORS     0x00000200
205 #define SYMOPT_EXACT_SYMBOLS            0x00000400
206 #define SYMOPT_ALLOW_ABSOLUTE_SYMBOLS   0x00000800
207 #define SYMOPT_IGNORE_NT_SYMPATH        0x00001000
208 #define SYMOPT_INCLUDE_32BIT_MODULES    0x00002000
209 #define SYMOPT_PUBLICS_ONLY             0x00004000
210 #define SYMOPT_NO_PUBLICS               0x00008000
211 #define SYMOPT_AUTO_PUBLICS             0x00010000
212 #define SYMOPT_NO_IMAGE_SEARCH          0x00020000
213 #define SYMOPT_SECURE                   0x00040000
214 #define SYMOPT_DEBUG                    0x80000000
215 #define UNDNAME_COMPLETE                 (0x0000) // Enable full undecoration
216 #define UNDNAME_NAME_ONLY                (0x1000) // Crack only the name for primary declaration;
217 // clang-format on
218 
219 #endif // _MSC_VER < 1300
220 #pragma pack(pop)
221 
222 // Some missing defines (for VC5/6):
223 #ifndef INVALID_FILE_ATTRIBUTES
224 #define INVALID_FILE_ATTRIBUTES ((DWORD)-1)
225 #endif
226 
227 // secure-CRT_functions are only available starting with VC8
228 #if _MSC_VER < 1400
229 #define strcpy_s(dst, len, src) strcpy(dst, src)
230 #define strncpy_s(dst, len, src, maxLen) strncpy(dst, len, src)
231 #define strcat_s(dst, len, src) strcat(dst, src)
232 #define _snprintf_s _snprintf
233 #define _tcscat_s _tcscat
234 #endif
235 
MyStrCpy(char * szDest,size_t nMaxDestSize,const char * szSrc)236 static void MyStrCpy(char* szDest, size_t nMaxDestSize, const char* szSrc)
237 {
238   if (nMaxDestSize <= 0)
239     return;
240   strncpy_s(szDest, nMaxDestSize, szSrc, _TRUNCATE);
241   // INFO: _TRUNCATE will ensure that it is null-terminated;
242   // but with older compilers (<1400) it uses "strncpy" and this does not!)
243   szDest[nMaxDestSize - 1] = 0;
244 } // MyStrCpy
245 
246 // Normally it should be enough to use 'CONTEXT_FULL' (better would be 'CONTEXT_ALL')
247 #define USED_CONTEXT_FLAGS CONTEXT_FULL
248 
249 class StackWalkerInternal
250 {
251 public:
StackWalkerInternal(StackWalker * parent,HANDLE hProcess)252   StackWalkerInternal(StackWalker* parent, HANDLE hProcess)
253   {
254     m_parent = parent;
255     m_hDbhHelp = NULL;
256     pSC = NULL;
257     m_hProcess = hProcess;
258     m_szSymPath = NULL;
259     pSFTA = NULL;
260     pSGLFA = NULL;
261     pSGMB = NULL;
262     pSGMI = NULL;
263     pSGO = NULL;
264     pSGSFA = NULL;
265     pSI = NULL;
266     pSLM = NULL;
267     pSSO = NULL;
268     pSW = NULL;
269     pUDSN = NULL;
270     pSGSP = NULL;
271   }
~StackWalkerInternal()272   ~StackWalkerInternal()
273   {
274     if (pSC != NULL)
275       pSC(m_hProcess); // SymCleanup
276     if (m_hDbhHelp != NULL)
277       FreeLibrary(m_hDbhHelp);
278     m_hDbhHelp = NULL;
279     m_parent = NULL;
280     if (m_szSymPath != NULL)
281       free(m_szSymPath);
282     m_szSymPath = NULL;
283   }
Init(LPCSTR szSymPath)284   BOOL Init(LPCSTR szSymPath)
285   {
286     if (m_parent == NULL)
287       return FALSE;
288     // Dynamically load the Entry-Points for dbghelp.dll:
289     // First try to load the newest one from
290     TCHAR szTemp[4096];
291     // But before we do this, we first check if the ".local" file exists
292     if (GetModuleFileName(NULL, szTemp, 4096) > 0)
293     {
294       _tcscat_s(szTemp, _T(".local"));
295       if (GetFileAttributes(szTemp) == INVALID_FILE_ATTRIBUTES)
296       {
297         // ".local" file does not exist, so we can try to load the dbghelp.dll from the "Debugging Tools for Windows"
298         // Ok, first try the new path according to the architecture:
299 #ifdef _M_IX86
300         if ((m_hDbhHelp == NULL) && (GetEnvironmentVariable(_T("ProgramFiles"), szTemp, 4096) > 0))
301         {
302           _tcscat_s(szTemp, _T("\\Debugging Tools for Windows (x86)\\dbghelp.dll"));
303           // now check if the file exists:
304           if (GetFileAttributes(szTemp) != INVALID_FILE_ATTRIBUTES)
305           {
306             m_hDbhHelp = LoadLibrary(szTemp);
307           }
308         }
309 #elif _M_X64
310         if ((m_hDbhHelp == NULL) && (GetEnvironmentVariable(_T("ProgramFiles"), szTemp, 4096) > 0))
311         {
312           _tcscat_s(szTemp, _T("\\Debugging Tools for Windows (x64)\\dbghelp.dll"));
313           // now check if the file exists:
314           if (GetFileAttributes(szTemp) != INVALID_FILE_ATTRIBUTES)
315           {
316             m_hDbhHelp = LoadLibrary(szTemp);
317           }
318         }
319 #elif _M_IA64
320         if ((m_hDbhHelp == NULL) && (GetEnvironmentVariable(_T("ProgramFiles"), szTemp, 4096) > 0))
321         {
322           _tcscat_s(szTemp, _T("\\Debugging Tools for Windows (ia64)\\dbghelp.dll"));
323           // now check if the file exists:
324           if (GetFileAttributes(szTemp) != INVALID_FILE_ATTRIBUTES)
325           {
326             m_hDbhHelp = LoadLibrary(szTemp);
327           }
328         }
329 #endif
330         // If still not found, try the old directories...
331         if ((m_hDbhHelp == NULL) && (GetEnvironmentVariable(_T("ProgramFiles"), szTemp, 4096) > 0))
332         {
333           _tcscat_s(szTemp, _T("\\Debugging Tools for Windows\\dbghelp.dll"));
334           // now check if the file exists:
335           if (GetFileAttributes(szTemp) != INVALID_FILE_ATTRIBUTES)
336           {
337             m_hDbhHelp = LoadLibrary(szTemp);
338           }
339         }
340 #if defined _M_X64 || defined _M_IA64
341         // Still not found? Then try to load the (old) 64-Bit version:
342         if ((m_hDbhHelp == NULL) && (GetEnvironmentVariable(_T("ProgramFiles"), szTemp, 4096) > 0))
343         {
344           _tcscat_s(szTemp, _T("\\Debugging Tools for Windows 64-Bit\\dbghelp.dll"));
345           if (GetFileAttributes(szTemp) != INVALID_FILE_ATTRIBUTES)
346           {
347             m_hDbhHelp = LoadLibrary(szTemp);
348           }
349         }
350 #endif
351       }
352     }
353     if (m_hDbhHelp == NULL) // if not already loaded, try to load a default-one
354       m_hDbhHelp = LoadLibrary(_T("dbghelp.dll"));
355     if (m_hDbhHelp == NULL)
356       return FALSE;
357     pSI = (tSI)GetProcAddress(m_hDbhHelp, "SymInitialize");
358     pSC = (tSC)GetProcAddress(m_hDbhHelp, "SymCleanup");
359 
360     pSW = (tSW)GetProcAddress(m_hDbhHelp, "StackWalk64");
361     pSGO = (tSGO)GetProcAddress(m_hDbhHelp, "SymGetOptions");
362     pSSO = (tSSO)GetProcAddress(m_hDbhHelp, "SymSetOptions");
363 
364     pSFTA = (tSFTA)GetProcAddress(m_hDbhHelp, "SymFunctionTableAccess64");
365     pSGLFA = (tSGLFA)GetProcAddress(m_hDbhHelp, "SymGetLineFromAddr64");
366     pSGMB = (tSGMB)GetProcAddress(m_hDbhHelp, "SymGetModuleBase64");
367     pSGMI = (tSGMI)GetProcAddress(m_hDbhHelp, "SymGetModuleInfo64");
368     pSGSFA = (tSGSFA)GetProcAddress(m_hDbhHelp, "SymGetSymFromAddr64");
369     pUDSN = (tUDSN)GetProcAddress(m_hDbhHelp, "UnDecorateSymbolName");
370     pSLM = (tSLM)GetProcAddress(m_hDbhHelp, "SymLoadModule64");
371     pSGSP = (tSGSP)GetProcAddress(m_hDbhHelp, "SymGetSearchPath");
372 
373     if (pSC == NULL || pSFTA == NULL || pSGMB == NULL || pSGMI == NULL || pSGO == NULL ||
374         pSGSFA == NULL || pSI == NULL || pSSO == NULL || pSW == NULL || pUDSN == NULL ||
375         pSLM == NULL)
376     {
377       FreeLibrary(m_hDbhHelp);
378       m_hDbhHelp = NULL;
379       pSC = NULL;
380       return FALSE;
381     }
382 
383     // SymInitialize
384     if (szSymPath != NULL)
385       m_szSymPath = _strdup(szSymPath);
386     if (this->pSI(m_hProcess, m_szSymPath, FALSE) == FALSE)
387       this->m_parent->OnDbgHelpErr("SymInitialize", GetLastError(), 0);
388 
389     DWORD symOptions = this->pSGO(); // SymGetOptions
390     symOptions |= SYMOPT_LOAD_LINES;
391     symOptions |= SYMOPT_FAIL_CRITICAL_ERRORS;
392     //symOptions |= SYMOPT_NO_PROMPTS;
393     // SymSetOptions
394     symOptions = this->pSSO(symOptions);
395 
396     char buf[StackWalker::STACKWALK_MAX_NAMELEN] = {0};
397     if (this->pSGSP != NULL)
398     {
399       if (this->pSGSP(m_hProcess, buf, StackWalker::STACKWALK_MAX_NAMELEN) == FALSE)
400         this->m_parent->OnDbgHelpErr("SymGetSearchPath", GetLastError(), 0);
401     }
402     char  szUserName[1024] = {0};
403     DWORD dwSize = 1024;
404     GetUserNameA(szUserName, &dwSize);
405     this->m_parent->OnSymInit(buf, symOptions, szUserName);
406 
407     return TRUE;
408   }
409 
410   StackWalker* m_parent;
411 
412   HMODULE m_hDbhHelp;
413   HANDLE  m_hProcess;
414   LPSTR   m_szSymPath;
415 
416 #pragma pack(push, 8)
417   typedef struct IMAGEHLP_MODULE64_V3
418   {
419     DWORD    SizeOfStruct;         // set to sizeof(IMAGEHLP_MODULE64)
420     DWORD64  BaseOfImage;          // base load address of module
421     DWORD    ImageSize;            // virtual size of the loaded module
422     DWORD    TimeDateStamp;        // date/time stamp from pe header
423     DWORD    CheckSum;             // checksum from the pe header
424     DWORD    NumSyms;              // number of symbols in the symbol table
425     SYM_TYPE SymType;              // type of symbols loaded
426     CHAR     ModuleName[32];       // module name
427     CHAR     ImageName[256];       // image name
428     CHAR     LoadedImageName[256]; // symbol file name
429     // new elements: 07-Jun-2002
430     CHAR  LoadedPdbName[256];   // pdb file name
431     DWORD CVSig;                // Signature of the CV record in the debug directories
432     CHAR  CVData[MAX_PATH * 3]; // Contents of the CV record
433     DWORD PdbSig;               // Signature of PDB
434     GUID  PdbSig70;             // Signature of PDB (VC 7 and up)
435     DWORD PdbAge;               // DBI age of pdb
436     BOOL  PdbUnmatched;         // loaded an unmatched pdb
437     BOOL  DbgUnmatched;         // loaded an unmatched dbg
438     BOOL  LineNumbers;          // we have line number information
439     BOOL  GlobalSymbols;        // we have internal symbol information
440     BOOL  TypeInfo;             // we have type information
441     // new elements: 17-Dec-2003
442     BOOL SourceIndexed; // pdb supports source server
443     BOOL Publics;       // contains public symbols
444   };
445 
446   typedef struct IMAGEHLP_MODULE64_V2
447   {
448     DWORD    SizeOfStruct;         // set to sizeof(IMAGEHLP_MODULE64)
449     DWORD64  BaseOfImage;          // base load address of module
450     DWORD    ImageSize;            // virtual size of the loaded module
451     DWORD    TimeDateStamp;        // date/time stamp from pe header
452     DWORD    CheckSum;             // checksum from the pe header
453     DWORD    NumSyms;              // number of symbols in the symbol table
454     SYM_TYPE SymType;              // type of symbols loaded
455     CHAR     ModuleName[32];       // module name
456     CHAR     ImageName[256];       // image name
457     CHAR     LoadedImageName[256]; // symbol file name
458   };
459 #pragma pack(pop)
460 
461   // SymCleanup()
462   typedef BOOL(__stdcall* tSC)(IN HANDLE hProcess);
463   tSC pSC;
464 
465   // SymFunctionTableAccess64()
466   typedef PVOID(__stdcall* tSFTA)(HANDLE hProcess, DWORD64 AddrBase);
467   tSFTA pSFTA;
468 
469   // SymGetLineFromAddr64()
470   typedef BOOL(__stdcall* tSGLFA)(IN HANDLE hProcess,
471                                   IN DWORD64 dwAddr,
472                                   OUT PDWORD pdwDisplacement,
473                                   OUT PIMAGEHLP_LINE64 Line);
474   tSGLFA pSGLFA;
475 
476   // SymGetModuleBase64()
477   typedef DWORD64(__stdcall* tSGMB)(IN HANDLE hProcess, IN DWORD64 dwAddr);
478   tSGMB pSGMB;
479 
480   // SymGetModuleInfo64()
481   typedef BOOL(__stdcall* tSGMI)(IN HANDLE hProcess,
482                                  IN DWORD64 dwAddr,
483                                  OUT IMAGEHLP_MODULE64_V3* ModuleInfo);
484   tSGMI pSGMI;
485 
486   // SymGetOptions()
487   typedef DWORD(__stdcall* tSGO)(VOID);
488   tSGO pSGO;
489 
490   // SymGetSymFromAddr64()
491   typedef BOOL(__stdcall* tSGSFA)(IN HANDLE hProcess,
492                                   IN DWORD64 dwAddr,
493                                   OUT PDWORD64 pdwDisplacement,
494                                   OUT PIMAGEHLP_SYMBOL64 Symbol);
495   tSGSFA pSGSFA;
496 
497   // SymInitialize()
498   typedef BOOL(__stdcall* tSI)(IN HANDLE hProcess, IN PSTR UserSearchPath, IN BOOL fInvadeProcess);
499   tSI pSI;
500 
501   // SymLoadModule64()
502   typedef DWORD64(__stdcall* tSLM)(IN HANDLE hProcess,
503                                    IN HANDLE hFile,
504                                    IN PSTR ImageName,
505                                    IN PSTR ModuleName,
506                                    IN DWORD64 BaseOfDll,
507                                    IN DWORD SizeOfDll);
508   tSLM pSLM;
509 
510   // SymSetOptions()
511   typedef DWORD(__stdcall* tSSO)(IN DWORD SymOptions);
512   tSSO pSSO;
513 
514   // StackWalk64()
515   typedef BOOL(__stdcall* tSW)(DWORD                            MachineType,
516                                HANDLE                           hProcess,
517                                HANDLE                           hThread,
518                                LPSTACKFRAME64                   StackFrame,
519                                PVOID                            ContextRecord,
520                                PREAD_PROCESS_MEMORY_ROUTINE64   ReadMemoryRoutine,
521                                PFUNCTION_TABLE_ACCESS_ROUTINE64 FunctionTableAccessRoutine,
522                                PGET_MODULE_BASE_ROUTINE64       GetModuleBaseRoutine,
523                                PTRANSLATE_ADDRESS_ROUTINE64     TranslateAddress);
524   tSW pSW;
525 
526   // UnDecorateSymbolName()
527   typedef DWORD(__stdcall WINAPI* tUDSN)(PCSTR DecoratedName,
528                                          PSTR  UnDecoratedName,
529                                          DWORD UndecoratedLength,
530                                          DWORD Flags);
531   tUDSN pUDSN;
532 
533   typedef BOOL(__stdcall WINAPI* tSGSP)(HANDLE hProcess, PSTR SearchPath, DWORD SearchPathLength);
534   tSGSP pSGSP;
535 
536 private:
537 // **************************************** ToolHelp32 ************************
538 #define MAX_MODULE_NAME32 255
539 #define TH32CS_SNAPMODULE 0x00000008
540 #pragma pack(push, 8)
541   typedef struct tagMODULEENTRY32
542   {
543     DWORD   dwSize;
544     DWORD   th32ModuleID;  // This module
545     DWORD   th32ProcessID; // owning process
546     DWORD   GlblcntUsage;  // Global usage count on the module
547     DWORD   ProccntUsage;  // Module usage count in th32ProcessID's context
548     BYTE*   modBaseAddr;   // Base address of module in th32ProcessID's context
549     DWORD   modBaseSize;   // Size in bytes of module starting at modBaseAddr
550     HMODULE hModule;       // The hModule of this module in th32ProcessID's context
551     char    szModule[MAX_MODULE_NAME32 + 1];
552     char    szExePath[MAX_PATH];
553   } MODULEENTRY32;
554   typedef MODULEENTRY32* PMODULEENTRY32;
555   typedef MODULEENTRY32* LPMODULEENTRY32;
556 #pragma pack(pop)
557 
GetModuleListTH32(HANDLE hProcess,DWORD pid)558   BOOL GetModuleListTH32(HANDLE hProcess, DWORD pid)
559   {
560     // CreateToolhelp32Snapshot()
561     typedef HANDLE(__stdcall * tCT32S)(DWORD dwFlags, DWORD th32ProcessID);
562     // Module32First()
563     typedef BOOL(__stdcall * tM32F)(HANDLE hSnapshot, LPMODULEENTRY32 lpme);
564     // Module32Next()
565     typedef BOOL(__stdcall * tM32N)(HANDLE hSnapshot, LPMODULEENTRY32 lpme);
566 
567     // try both dlls...
568     const TCHAR* dllname[] = {_T("kernel32.dll"), _T("tlhelp32.dll")};
569     HINSTANCE    hToolhelp = NULL;
570     tCT32S       pCT32S = NULL;
571     tM32F        pM32F = NULL;
572     tM32N        pM32N = NULL;
573 
574     HANDLE        hSnap;
575     MODULEENTRY32 me;
576     me.dwSize = sizeof(me);
577     BOOL   keepGoing;
578     size_t i;
579 
580     for (i = 0; i < (sizeof(dllname) / sizeof(dllname[0])); i++)
581     {
582       hToolhelp = LoadLibrary(dllname[i]);
583       if (hToolhelp == NULL)
584         continue;
585       pCT32S = (tCT32S)GetProcAddress(hToolhelp, "CreateToolhelp32Snapshot");
586       pM32F = (tM32F)GetProcAddress(hToolhelp, "Module32First");
587       pM32N = (tM32N)GetProcAddress(hToolhelp, "Module32Next");
588       if ((pCT32S != NULL) && (pM32F != NULL) && (pM32N != NULL))
589         break; // found the functions!
590       FreeLibrary(hToolhelp);
591       hToolhelp = NULL;
592     }
593 
594     if (hToolhelp == NULL)
595       return FALSE;
596 
597     hSnap = pCT32S(TH32CS_SNAPMODULE, pid);
598     if (hSnap == (HANDLE)-1)
599     {
600       FreeLibrary(hToolhelp);
601       return FALSE;
602     }
603 
604     keepGoing = !!pM32F(hSnap, &me);
605     int cnt = 0;
606     while (keepGoing)
607     {
608       this->LoadModule(hProcess, me.szExePath, me.szModule, (DWORD64)me.modBaseAddr,
609                        me.modBaseSize);
610       cnt++;
611       keepGoing = !!pM32N(hSnap, &me);
612     }
613     CloseHandle(hSnap);
614     FreeLibrary(hToolhelp);
615     if (cnt <= 0)
616       return FALSE;
617     return TRUE;
618   } // GetModuleListTH32
619 
620   // **************************************** PSAPI ************************
621   typedef struct _MODULEINFO
622   {
623     LPVOID lpBaseOfDll;
624     DWORD  SizeOfImage;
625     LPVOID EntryPoint;
626   } MODULEINFO, *LPMODULEINFO;
627 
GetModuleListPSAPI(HANDLE hProcess)628   BOOL GetModuleListPSAPI(HANDLE hProcess)
629   {
630     // EnumProcessModules()
631     typedef BOOL(__stdcall * tEPM)(HANDLE hProcess, HMODULE * lphModule, DWORD cb,
632                                    LPDWORD lpcbNeeded);
633     // GetModuleFileNameEx()
634     typedef DWORD(__stdcall * tGMFNE)(HANDLE hProcess, HMODULE hModule, LPSTR lpFilename,
635                                       DWORD nSize);
636     // GetModuleBaseName()
637     typedef DWORD(__stdcall * tGMBN)(HANDLE hProcess, HMODULE hModule, LPSTR lpFilename,
638                                      DWORD nSize);
639     // GetModuleInformation()
640     typedef BOOL(__stdcall * tGMI)(HANDLE hProcess, HMODULE hModule, LPMODULEINFO pmi, DWORD nSize);
641 
642     HINSTANCE hPsapi;
643     tEPM      pEPM;
644     tGMFNE    pGMFNE;
645     tGMBN     pGMBN;
646     tGMI      pGMI;
647 
648     DWORD i;
649     //ModuleEntry e;
650     DWORD        cbNeeded;
651     MODULEINFO   mi;
652     HMODULE*     hMods = NULL;
653     char*        tt = NULL;
654     char*        tt2 = NULL;
655     const SIZE_T TTBUFLEN = 8096;
656     int          cnt = 0;
657 
658     hPsapi = LoadLibrary(_T("psapi.dll"));
659     if (hPsapi == NULL)
660       return FALSE;
661 
662     pEPM = (tEPM)GetProcAddress(hPsapi, "EnumProcessModules");
663     pGMFNE = (tGMFNE)GetProcAddress(hPsapi, "GetModuleFileNameExA");
664     pGMBN = (tGMFNE)GetProcAddress(hPsapi, "GetModuleBaseNameA");
665     pGMI = (tGMI)GetProcAddress(hPsapi, "GetModuleInformation");
666     if ((pEPM == NULL) || (pGMFNE == NULL) || (pGMBN == NULL) || (pGMI == NULL))
667     {
668       // we couldn't find all functions
669       FreeLibrary(hPsapi);
670       return FALSE;
671     }
672 
673     hMods = (HMODULE*)malloc(sizeof(HMODULE) * (TTBUFLEN / sizeof(HMODULE)));
674     tt = (char*)malloc(sizeof(char) * TTBUFLEN);
675     tt2 = (char*)malloc(sizeof(char) * TTBUFLEN);
676     if ((hMods == NULL) || (tt == NULL) || (tt2 == NULL))
677       goto cleanup;
678 
679     if (!pEPM(hProcess, hMods, TTBUFLEN, &cbNeeded))
680     {
681       //_ftprintf(fLogFile, _T("%lu: EPM failed, GetLastError = %lu\n"), g_dwShowCount, gle );
682       goto cleanup;
683     }
684 
685     if (cbNeeded > TTBUFLEN)
686     {
687       //_ftprintf(fLogFile, _T("%lu: More than %lu module handles. Huh?\n"), g_dwShowCount, lenof( hMods ) );
688       goto cleanup;
689     }
690 
691     for (i = 0; i < cbNeeded / sizeof(hMods[0]); i++)
692     {
693       // base address, size
694       pGMI(hProcess, hMods[i], &mi, sizeof(mi));
695       // image file name
696       tt[0] = 0;
697       pGMFNE(hProcess, hMods[i], tt, TTBUFLEN);
698       // module name
699       tt2[0] = 0;
700       pGMBN(hProcess, hMods[i], tt2, TTBUFLEN);
701 
702       DWORD dwRes = this->LoadModule(hProcess, tt, tt2, (DWORD64)mi.lpBaseOfDll, mi.SizeOfImage);
703       if (dwRes != ERROR_SUCCESS)
704         this->m_parent->OnDbgHelpErr("LoadModule", dwRes, 0);
705       cnt++;
706     }
707 
708   cleanup:
709     if (hPsapi != NULL)
710       FreeLibrary(hPsapi);
711     if (tt2 != NULL)
712       free(tt2);
713     if (tt != NULL)
714       free(tt);
715     if (hMods != NULL)
716       free(hMods);
717 
718     return cnt != 0;
719   } // GetModuleListPSAPI
720 
LoadModule(HANDLE hProcess,LPCSTR img,LPCSTR mod,DWORD64 baseAddr,DWORD size)721   DWORD LoadModule(HANDLE hProcess, LPCSTR img, LPCSTR mod, DWORD64 baseAddr, DWORD size)
722   {
723     CHAR* szImg = _strdup(img);
724     CHAR* szMod = _strdup(mod);
725     DWORD result = ERROR_SUCCESS;
726     if ((szImg == NULL) || (szMod == NULL))
727       result = ERROR_NOT_ENOUGH_MEMORY;
728     else
729     {
730       if (pSLM(hProcess, 0, szImg, szMod, baseAddr, size) == 0)
731         result = GetLastError();
732     }
733     ULONGLONG fileVersion = 0;
734     if ((m_parent != NULL) && (szImg != NULL))
735     {
736       // try to retrieve the file-version:
737       if ((this->m_parent->m_options & StackWalker::RetrieveFileVersion) != 0)
738       {
739         VS_FIXEDFILEINFO* fInfo = NULL;
740         DWORD             dwHandle;
741         DWORD             dwSize = GetFileVersionInfoSizeA(szImg, &dwHandle);
742         if (dwSize > 0)
743         {
744           LPVOID vData = malloc(dwSize);
745           if (vData != NULL)
746           {
747             if (GetFileVersionInfoA(szImg, dwHandle, dwSize, vData) != 0)
748             {
749               UINT  len;
750               TCHAR szSubBlock[] = _T("\\");
751               if (VerQueryValue(vData, szSubBlock, (LPVOID*)&fInfo, &len) == 0)
752                 fInfo = NULL;
753               else
754               {
755                 fileVersion =
756                     ((ULONGLONG)fInfo->dwFileVersionLS) + ((ULONGLONG)fInfo->dwFileVersionMS << 32);
757               }
758             }
759             free(vData);
760           }
761         }
762       }
763 
764       // Retrieve some additional-infos about the module
765       IMAGEHLP_MODULE64_V3 Module;
766       const char*          szSymType = "-unknown-";
767       if (this->GetModuleInfo(hProcess, baseAddr, &Module) != FALSE)
768       {
769         switch (Module.SymType)
770         {
771           case SymNone:
772             szSymType = "-nosymbols-";
773             break;
774           case SymCoff: // 1
775             szSymType = "COFF";
776             break;
777           case SymCv: // 2
778             szSymType = "CV";
779             break;
780           case SymPdb: // 3
781             szSymType = "PDB";
782             break;
783           case SymExport: // 4
784             szSymType = "-exported-";
785             break;
786           case SymDeferred: // 5
787             szSymType = "-deferred-";
788             break;
789           case SymSym: // 6
790             szSymType = "SYM";
791             break;
792           case 7: // SymDia:
793             szSymType = "DIA";
794             break;
795           case 8: //SymVirtual:
796             szSymType = "Virtual";
797             break;
798         }
799       }
800       LPCSTR pdbName = Module.LoadedImageName;
801       if (Module.LoadedPdbName[0] != 0)
802         pdbName = Module.LoadedPdbName;
803       this->m_parent->OnLoadModule(img, mod, baseAddr, size, result, szSymType, pdbName,
804                                    fileVersion);
805     }
806     if (szImg != NULL)
807       free(szImg);
808     if (szMod != NULL)
809       free(szMod);
810     return result;
811   }
812 
813 public:
LoadModules(HANDLE hProcess,DWORD dwProcessId)814   BOOL LoadModules(HANDLE hProcess, DWORD dwProcessId)
815   {
816     // first try toolhelp32
817     if (GetModuleListTH32(hProcess, dwProcessId))
818       return true;
819     // then try psapi
820     return GetModuleListPSAPI(hProcess);
821   }
822 
GetModuleInfo(HANDLE hProcess,DWORD64 baseAddr,IMAGEHLP_MODULE64_V3 * pModuleInfo)823   BOOL GetModuleInfo(HANDLE hProcess, DWORD64 baseAddr, IMAGEHLP_MODULE64_V3* pModuleInfo)
824   {
825     memset(pModuleInfo, 0, sizeof(IMAGEHLP_MODULE64_V3));
826     if (this->pSGMI == NULL)
827     {
828       SetLastError(ERROR_DLL_INIT_FAILED);
829       return FALSE;
830     }
831     // First try to use the larger ModuleInfo-Structure
832     pModuleInfo->SizeOfStruct = sizeof(IMAGEHLP_MODULE64_V3);
833     void* pData = malloc(
834         4096); // reserve enough memory, so the bug in v6.3.5.1 does not lead to memory-overwrites...
835     if (pData == NULL)
836     {
837       SetLastError(ERROR_NOT_ENOUGH_MEMORY);
838       return FALSE;
839     }
840     memcpy(pData, pModuleInfo, sizeof(IMAGEHLP_MODULE64_V3));
841     static bool s_useV3Version = true;
842     if (s_useV3Version)
843     {
844       if (this->pSGMI(hProcess, baseAddr, (IMAGEHLP_MODULE64_V3*)pData) != FALSE)
845       {
846         // only copy as much memory as is reserved...
847         memcpy(pModuleInfo, pData, sizeof(IMAGEHLP_MODULE64_V3));
848         pModuleInfo->SizeOfStruct = sizeof(IMAGEHLP_MODULE64_V3);
849         free(pData);
850         return TRUE;
851       }
852       s_useV3Version = false; // to prevent unnecessary calls with the larger struct...
853     }
854 
855     // could not retrieve the bigger structure, try with the smaller one (as defined in VC7.1)...
856     pModuleInfo->SizeOfStruct = sizeof(IMAGEHLP_MODULE64_V2);
857     memcpy(pData, pModuleInfo, sizeof(IMAGEHLP_MODULE64_V2));
858     if (this->pSGMI(hProcess, baseAddr, (IMAGEHLP_MODULE64_V3*)pData) != FALSE)
859     {
860       // only copy as much memory as is reserved...
861       memcpy(pModuleInfo, pData, sizeof(IMAGEHLP_MODULE64_V2));
862       pModuleInfo->SizeOfStruct = sizeof(IMAGEHLP_MODULE64_V2);
863       free(pData);
864       return TRUE;
865     }
866     free(pData);
867     SetLastError(ERROR_DLL_INIT_FAILED);
868     return FALSE;
869   }
870 };
871 
872 // #############################################################
StackWalker(DWORD dwProcessId,HANDLE hProcess)873 StackWalker::StackWalker(DWORD dwProcessId, HANDLE hProcess)
874 {
875   this->m_options = OptionsAll;
876   this->m_modulesLoaded = FALSE;
877   this->m_hProcess = hProcess;
878   this->m_sw = new StackWalkerInternal(this, this->m_hProcess);
879   this->m_dwProcessId = dwProcessId;
880   this->m_szSymPath = NULL;
881   this->m_MaxRecursionCount = 1000;
882 }
StackWalker(int options,LPCSTR szSymPath,DWORD dwProcessId,HANDLE hProcess)883 StackWalker::StackWalker(int options, LPCSTR szSymPath, DWORD dwProcessId, HANDLE hProcess)
884 {
885   this->m_options = options;
886   this->m_modulesLoaded = FALSE;
887   this->m_hProcess = hProcess;
888   this->m_sw = new StackWalkerInternal(this, this->m_hProcess);
889   this->m_dwProcessId = dwProcessId;
890   if (szSymPath != NULL)
891   {
892     this->m_szSymPath = _strdup(szSymPath);
893     this->m_options |= SymBuildPath;
894   }
895   else
896     this->m_szSymPath = NULL;
897   this->m_MaxRecursionCount = 1000;
898 }
899 
~StackWalker()900 StackWalker::~StackWalker()
901 {
902   if (m_szSymPath != NULL)
903     free(m_szSymPath);
904   m_szSymPath = NULL;
905   if (this->m_sw != NULL)
906     delete this->m_sw;
907   this->m_sw = NULL;
908 }
909 
LoadModules()910 BOOL StackWalker::LoadModules()
911 {
912   if (this->m_sw == NULL)
913   {
914     SetLastError(ERROR_DLL_INIT_FAILED);
915     return FALSE;
916   }
917   if (m_modulesLoaded != FALSE)
918     return TRUE;
919 
920   // Build the sym-path:
921   char* szSymPath = NULL;
922   if ((this->m_options & SymBuildPath) != 0)
923   {
924     const size_t nSymPathLen = 4096;
925     szSymPath = (char*)malloc(nSymPathLen);
926     if (szSymPath == NULL)
927     {
928       SetLastError(ERROR_NOT_ENOUGH_MEMORY);
929       return FALSE;
930     }
931     szSymPath[0] = 0;
932     // Now first add the (optional) provided sympath:
933     if (this->m_szSymPath != NULL)
934     {
935       strcat_s(szSymPath, nSymPathLen, this->m_szSymPath);
936       strcat_s(szSymPath, nSymPathLen, ";");
937     }
938 
939     strcat_s(szSymPath, nSymPathLen, ".;");
940 
941     const size_t nTempLen = 1024;
942     char         szTemp[nTempLen];
943     // Now add the current directory:
944     if (GetCurrentDirectoryA(nTempLen, szTemp) > 0)
945     {
946       szTemp[nTempLen - 1] = 0;
947       strcat_s(szSymPath, nSymPathLen, szTemp);
948       strcat_s(szSymPath, nSymPathLen, ";");
949     }
950 
951     // Now add the path for the main-module:
952     if (GetModuleFileNameA(NULL, szTemp, nTempLen) > 0)
953     {
954       szTemp[nTempLen - 1] = 0;
955       for (char* p = (szTemp + strlen(szTemp) - 1); p >= szTemp; --p)
956       {
957         // locate the rightmost path separator
958         if ((*p == '\\') || (*p == '/') || (*p == ':'))
959         {
960           *p = 0;
961           break;
962         }
963       } // for (search for path separator...)
964       if (strlen(szTemp) > 0)
965       {
966         strcat_s(szSymPath, nSymPathLen, szTemp);
967         strcat_s(szSymPath, nSymPathLen, ";");
968       }
969     }
970     if (GetEnvironmentVariableA("_NT_SYMBOL_PATH", szTemp, nTempLen) > 0)
971     {
972       szTemp[nTempLen - 1] = 0;
973       strcat_s(szSymPath, nSymPathLen, szTemp);
974       strcat_s(szSymPath, nSymPathLen, ";");
975     }
976     if (GetEnvironmentVariableA("_NT_ALTERNATE_SYMBOL_PATH", szTemp, nTempLen) > 0)
977     {
978       szTemp[nTempLen - 1] = 0;
979       strcat_s(szSymPath, nSymPathLen, szTemp);
980       strcat_s(szSymPath, nSymPathLen, ";");
981     }
982     if (GetEnvironmentVariableA("SYSTEMROOT", szTemp, nTempLen) > 0)
983     {
984       szTemp[nTempLen - 1] = 0;
985       strcat_s(szSymPath, nSymPathLen, szTemp);
986       strcat_s(szSymPath, nSymPathLen, ";");
987       // also add the "system32"-directory:
988       strcat_s(szTemp, nTempLen, "\\system32");
989       strcat_s(szSymPath, nSymPathLen, szTemp);
990       strcat_s(szSymPath, nSymPathLen, ";");
991     }
992 
993     if ((this->m_options & SymUseSymSrv) != 0)
994     {
995       if (GetEnvironmentVariableA("SYSTEMDRIVE", szTemp, nTempLen) > 0)
996       {
997         szTemp[nTempLen - 1] = 0;
998         strcat_s(szSymPath, nSymPathLen, "SRV*");
999         strcat_s(szSymPath, nSymPathLen, szTemp);
1000         strcat_s(szSymPath, nSymPathLen, "\\websymbols");
1001         strcat_s(szSymPath, nSymPathLen, "*https://msdl.microsoft.com/download/symbols;");
1002       }
1003       else
1004         strcat_s(szSymPath, nSymPathLen,
1005                  "SRV*c:\\websymbols*https://msdl.microsoft.com/download/symbols;");
1006     }
1007   } // if SymBuildPath
1008 
1009   // First Init the whole stuff...
1010   BOOL bRet = this->m_sw->Init(szSymPath);
1011   if (szSymPath != NULL)
1012     free(szSymPath);
1013   szSymPath = NULL;
1014   if (bRet == FALSE)
1015   {
1016     this->OnDbgHelpErr("Error while initializing dbghelp.dll", 0, 0);
1017     SetLastError(ERROR_DLL_INIT_FAILED);
1018     return FALSE;
1019   }
1020 
1021   bRet = this->m_sw->LoadModules(this->m_hProcess, this->m_dwProcessId);
1022   if (bRet != FALSE)
1023     m_modulesLoaded = TRUE;
1024   return bRet;
1025 }
1026 
1027 // The following is used to pass the "userData"-Pointer to the user-provided readMemoryFunction
1028 // This has to be done due to a problem with the "hProcess"-parameter in x64...
1029 // Because this class is in no case multi-threading-enabled (because of the limitations
1030 // of dbghelp.dll) it is "safe" to use a static-variable
1031 static StackWalker::PReadProcessMemoryRoutine s_readMemoryFunction = NULL;
1032 static LPVOID                                 s_readMemoryFunction_UserData = NULL;
1033 
ShowCallstack(HANDLE hThread,const CONTEXT * context,PReadProcessMemoryRoutine readMemoryFunction,LPVOID pUserData)1034 BOOL StackWalker::ShowCallstack(HANDLE                    hThread,
1035                                 const CONTEXT*            context,
1036                                 PReadProcessMemoryRoutine readMemoryFunction,
1037                                 LPVOID                    pUserData)
1038 {
1039   CONTEXT                                   c;
1040   CallstackEntry                            csEntry;
1041   IMAGEHLP_SYMBOL64*                        pSym = NULL;
1042   StackWalkerInternal::IMAGEHLP_MODULE64_V3 Module;
1043   IMAGEHLP_LINE64                           Line;
1044   int                                       frameNum;
1045   bool                                      bLastEntryCalled = true;
1046   int                                       curRecursionCount = 0;
1047 
1048   if (m_modulesLoaded == FALSE)
1049     this->LoadModules(); // ignore the result...
1050 
1051   if (this->m_sw->m_hDbhHelp == NULL)
1052   {
1053     SetLastError(ERROR_DLL_INIT_FAILED);
1054     return FALSE;
1055   }
1056 
1057   s_readMemoryFunction = readMemoryFunction;
1058   s_readMemoryFunction_UserData = pUserData;
1059 
1060   if (context == NULL)
1061   {
1062     // If no context is provided, capture the context
1063     // See: https://stackwalker.codeplex.com/discussions/446958
1064 #if _WIN32_WINNT <= 0x0501
1065     // If we need to support XP, we need to use the "old way", because "GetThreadId" is not available!
1066     if (hThread == GetCurrentThread())
1067 #else
1068     if (GetThreadId(hThread) == GetCurrentThreadId())
1069 #endif
1070     {
1071       GET_CURRENT_CONTEXT_STACKWALKER_CODEPLEX(c, USED_CONTEXT_FLAGS);
1072     }
1073     else
1074     {
1075       SuspendThread(hThread);
1076       memset(&c, 0, sizeof(CONTEXT));
1077       c.ContextFlags = USED_CONTEXT_FLAGS;
1078 
1079       // TODO: Detect if you want to get a thread context of a different process, which is running a different processor architecture...
1080       // This does only work if we are x64 and the target process is x64 or x86;
1081       // It cannot work, if this process is x64 and the target process is x64... this is not supported...
1082       // See also: http://www.howzatt.demon.co.uk/articles/DebuggingInWin64.html
1083       if (GetThreadContext(hThread, &c) == FALSE)
1084       {
1085         ResumeThread(hThread);
1086         return FALSE;
1087       }
1088     }
1089   }
1090   else
1091     c = *context;
1092 
1093   // init STACKFRAME for first call
1094   STACKFRAME64 s; // in/out stackframe
1095   memset(&s, 0, sizeof(s));
1096   DWORD imageType;
1097 #ifdef _M_IX86
1098   // normally, call ImageNtHeader() and use machine info from PE header
1099   imageType = IMAGE_FILE_MACHINE_I386;
1100   s.AddrPC.Offset = c.Eip;
1101   s.AddrPC.Mode = AddrModeFlat;
1102   s.AddrFrame.Offset = c.Ebp;
1103   s.AddrFrame.Mode = AddrModeFlat;
1104   s.AddrStack.Offset = c.Esp;
1105   s.AddrStack.Mode = AddrModeFlat;
1106 #elif _M_X64
1107   imageType = IMAGE_FILE_MACHINE_AMD64;
1108   s.AddrPC.Offset = c.Rip;
1109   s.AddrPC.Mode = AddrModeFlat;
1110   s.AddrFrame.Offset = c.Rsp;
1111   s.AddrFrame.Mode = AddrModeFlat;
1112   s.AddrStack.Offset = c.Rsp;
1113   s.AddrStack.Mode = AddrModeFlat;
1114 #elif _M_IA64
1115   imageType = IMAGE_FILE_MACHINE_IA64;
1116   s.AddrPC.Offset = c.StIIP;
1117   s.AddrPC.Mode = AddrModeFlat;
1118   s.AddrFrame.Offset = c.IntSp;
1119   s.AddrFrame.Mode = AddrModeFlat;
1120   s.AddrBStore.Offset = c.RsBSP;
1121   s.AddrBStore.Mode = AddrModeFlat;
1122   s.AddrStack.Offset = c.IntSp;
1123   s.AddrStack.Mode = AddrModeFlat;
1124 #else
1125 #error "Platform not supported!"
1126 #endif
1127 
1128   pSym = (IMAGEHLP_SYMBOL64*)malloc(sizeof(IMAGEHLP_SYMBOL64) + STACKWALK_MAX_NAMELEN);
1129   if (!pSym)
1130     goto cleanup; // not enough memory...
1131   memset(pSym, 0, sizeof(IMAGEHLP_SYMBOL64) + STACKWALK_MAX_NAMELEN);
1132   pSym->SizeOfStruct = sizeof(IMAGEHLP_SYMBOL64);
1133   pSym->MaxNameLength = STACKWALK_MAX_NAMELEN;
1134 
1135   memset(&Line, 0, sizeof(Line));
1136   Line.SizeOfStruct = sizeof(Line);
1137 
1138   memset(&Module, 0, sizeof(Module));
1139   Module.SizeOfStruct = sizeof(Module);
1140 
1141   for (frameNum = 0;; ++frameNum)
1142   {
1143     // get next stack frame (StackWalk64(), SymFunctionTableAccess64(), SymGetModuleBase64())
1144     // if this returns ERROR_INVALID_ADDRESS (487) or ERROR_NOACCESS (998), you can
1145     // assume that either you are done, or that the stack is so hosed that the next
1146     // deeper frame could not be found.
1147     // CONTEXT need not to be supplied if imageTyp is IMAGE_FILE_MACHINE_I386!
1148     if (!this->m_sw->pSW(imageType, this->m_hProcess, hThread, &s, &c, myReadProcMem,
1149                          this->m_sw->pSFTA, this->m_sw->pSGMB, NULL))
1150     {
1151       // INFO: "StackWalk64" does not set "GetLastError"...
1152       this->OnDbgHelpErr("StackWalk64", 0, s.AddrPC.Offset);
1153       break;
1154     }
1155 
1156     csEntry.offset = s.AddrPC.Offset;
1157     csEntry.name[0] = 0;
1158     csEntry.undName[0] = 0;
1159     csEntry.undFullName[0] = 0;
1160     csEntry.offsetFromSmybol = 0;
1161     csEntry.offsetFromLine = 0;
1162     csEntry.lineFileName[0] = 0;
1163     csEntry.lineNumber = 0;
1164     csEntry.loadedImageName[0] = 0;
1165     csEntry.moduleName[0] = 0;
1166     if (s.AddrPC.Offset == s.AddrReturn.Offset)
1167     {
1168       if ((this->m_MaxRecursionCount > 0) && (curRecursionCount > m_MaxRecursionCount))
1169       {
1170         this->OnDbgHelpErr("StackWalk64-Endless-Callstack!", 0, s.AddrPC.Offset);
1171         break;
1172       }
1173       curRecursionCount++;
1174     }
1175     else
1176       curRecursionCount = 0;
1177     if (s.AddrPC.Offset != 0)
1178     {
1179       // we seem to have a valid PC
1180       // show procedure info (SymGetSymFromAddr64())
1181       if (this->m_sw->pSGSFA(this->m_hProcess, s.AddrPC.Offset, &(csEntry.offsetFromSmybol),
1182                              pSym) != FALSE)
1183       {
1184         MyStrCpy(csEntry.name, STACKWALK_MAX_NAMELEN, pSym->Name);
1185         // UnDecorateSymbolName()
1186         this->m_sw->pUDSN(pSym->Name, csEntry.undName, STACKWALK_MAX_NAMELEN, UNDNAME_NAME_ONLY);
1187         this->m_sw->pUDSN(pSym->Name, csEntry.undFullName, STACKWALK_MAX_NAMELEN, UNDNAME_COMPLETE);
1188       }
1189       else
1190       {
1191         this->OnDbgHelpErr("SymGetSymFromAddr64", GetLastError(), s.AddrPC.Offset);
1192       }
1193 
1194       // show line number info, NT5.0-method (SymGetLineFromAddr64())
1195       if (this->m_sw->pSGLFA != NULL)
1196       { // yes, we have SymGetLineFromAddr64()
1197         if (this->m_sw->pSGLFA(this->m_hProcess, s.AddrPC.Offset, &(csEntry.offsetFromLine),
1198                                &Line) != FALSE)
1199         {
1200           csEntry.lineNumber = Line.LineNumber;
1201           MyStrCpy(csEntry.lineFileName, STACKWALK_MAX_NAMELEN, Line.FileName);
1202         }
1203         else
1204         {
1205           this->OnDbgHelpErr("SymGetLineFromAddr64", GetLastError(), s.AddrPC.Offset);
1206         }
1207       } // yes, we have SymGetLineFromAddr64()
1208 
1209       // show module info (SymGetModuleInfo64())
1210       if (this->m_sw->GetModuleInfo(this->m_hProcess, s.AddrPC.Offset, &Module) != FALSE)
1211       { // got module info OK
1212         switch (Module.SymType)
1213         {
1214           case SymNone:
1215             csEntry.symTypeString = "-nosymbols-";
1216             break;
1217           case SymCoff:
1218             csEntry.symTypeString = "COFF";
1219             break;
1220           case SymCv:
1221             csEntry.symTypeString = "CV";
1222             break;
1223           case SymPdb:
1224             csEntry.symTypeString = "PDB";
1225             break;
1226           case SymExport:
1227             csEntry.symTypeString = "-exported-";
1228             break;
1229           case SymDeferred:
1230             csEntry.symTypeString = "-deferred-";
1231             break;
1232           case SymSym:
1233             csEntry.symTypeString = "SYM";
1234             break;
1235 #if API_VERSION_NUMBER >= 9
1236           case SymDia:
1237             csEntry.symTypeString = "DIA";
1238             break;
1239 #endif
1240           case 8: //SymVirtual:
1241             csEntry.symTypeString = "Virtual";
1242             break;
1243           default:
1244             //_snprintf( ty, sizeof(ty), "symtype=%ld", (long) Module.SymType );
1245             csEntry.symTypeString = NULL;
1246             break;
1247         }
1248 
1249         MyStrCpy(csEntry.moduleName, STACKWALK_MAX_NAMELEN, Module.ModuleName);
1250         csEntry.baseOfImage = Module.BaseOfImage;
1251         MyStrCpy(csEntry.loadedImageName, STACKWALK_MAX_NAMELEN, Module.LoadedImageName);
1252       } // got module info OK
1253       else
1254       {
1255         this->OnDbgHelpErr("SymGetModuleInfo64", GetLastError(), s.AddrPC.Offset);
1256       }
1257     } // we seem to have a valid PC
1258 
1259     CallstackEntryType et = nextEntry;
1260     if (frameNum == 0)
1261       et = firstEntry;
1262     bLastEntryCalled = false;
1263     this->OnCallstackEntry(et, csEntry);
1264 
1265     if (s.AddrReturn.Offset == 0)
1266     {
1267       bLastEntryCalled = true;
1268       this->OnCallstackEntry(lastEntry, csEntry);
1269       SetLastError(ERROR_SUCCESS);
1270       break;
1271     }
1272   } // for ( frameNum )
1273 
1274 cleanup:
1275   if (pSym)
1276     free(pSym);
1277 
1278   if (bLastEntryCalled == false)
1279     this->OnCallstackEntry(lastEntry, csEntry);
1280 
1281   if (context == NULL)
1282     ResumeThread(hThread);
1283 
1284   return TRUE;
1285 }
1286 
ShowObject(LPVOID pObject)1287 BOOL StackWalker::ShowObject(LPVOID pObject)
1288 {
1289   // Load modules if not done yet
1290   if (m_modulesLoaded == FALSE)
1291     this->LoadModules(); // ignore the result...
1292 
1293   // Verify that the DebugHelp.dll was actually found
1294   if (this->m_sw->m_hDbhHelp == NULL)
1295   {
1296     SetLastError(ERROR_DLL_INIT_FAILED);
1297     return FALSE;
1298   }
1299 
1300   // SymGetSymFromAddr64() is required
1301   if (this->m_sw->pSGSFA == NULL)
1302     return FALSE;
1303 
1304   // Show object info (SymGetSymFromAddr64())
1305   DWORD64            dwAddress = DWORD64(pObject);
1306   DWORD64            dwDisplacement = 0;
1307   const SIZE_T       symSize = sizeof(IMAGEHLP_SYMBOL64) + STACKWALK_MAX_NAMELEN;
1308   IMAGEHLP_SYMBOL64* pSym = (IMAGEHLP_SYMBOL64*) malloc(symSize);
1309   if (!pSym)
1310     return FALSE;
1311   memset(pSym, 0, symSize);
1312   pSym->SizeOfStruct = sizeof(IMAGEHLP_SYMBOL64);
1313   pSym->MaxNameLength = STACKWALK_MAX_NAMELEN;
1314   if (this->m_sw->pSGSFA(this->m_hProcess, dwAddress, &dwDisplacement, pSym) == FALSE)
1315   {
1316     this->OnDbgHelpErr("SymGetSymFromAddr64", GetLastError(), dwAddress);
1317     return FALSE;
1318   }
1319   // Object name output
1320   this->OnOutput(pSym->Name);
1321 
1322   free(pSym);
1323   return TRUE;
1324 };
1325 
myReadProcMem(HANDLE hProcess,DWORD64 qwBaseAddress,PVOID lpBuffer,DWORD nSize,LPDWORD lpNumberOfBytesRead)1326 BOOL __stdcall StackWalker::myReadProcMem(HANDLE  hProcess,
1327                                           DWORD64 qwBaseAddress,
1328                                           PVOID   lpBuffer,
1329                                           DWORD   nSize,
1330                                           LPDWORD lpNumberOfBytesRead)
1331 {
1332   if (s_readMemoryFunction == NULL)
1333   {
1334     SIZE_T st;
1335     BOOL   bRet = ReadProcessMemory(hProcess, (LPVOID)qwBaseAddress, lpBuffer, nSize, &st);
1336     *lpNumberOfBytesRead = (DWORD)st;
1337     //printf("ReadMemory: hProcess: %p, baseAddr: %p, buffer: %p, size: %d, read: %d, result: %d\n", hProcess, (LPVOID) qwBaseAddress, lpBuffer, nSize, (DWORD) st, (DWORD) bRet);
1338     return bRet;
1339   }
1340   else
1341   {
1342     return s_readMemoryFunction(hProcess, qwBaseAddress, lpBuffer, nSize, lpNumberOfBytesRead,
1343                                 s_readMemoryFunction_UserData);
1344   }
1345 }
1346 
OnLoadModule(LPCSTR img,LPCSTR mod,DWORD64 baseAddr,DWORD size,DWORD result,LPCSTR symType,LPCSTR pdbName,ULONGLONG fileVersion)1347 void StackWalker::OnLoadModule(LPCSTR    img,
1348                                LPCSTR    mod,
1349                                DWORD64   baseAddr,
1350                                DWORD     size,
1351                                DWORD     result,
1352                                LPCSTR    symType,
1353                                LPCSTR    pdbName,
1354                                ULONGLONG fileVersion)
1355 {
1356   CHAR   buffer[STACKWALK_MAX_NAMELEN];
1357   size_t maxLen = STACKWALK_MAX_NAMELEN;
1358 #if _MSC_VER >= 1400
1359   maxLen = _TRUNCATE;
1360 #endif
1361   if (fileVersion == 0)
1362     _snprintf_s(buffer, maxLen, "%s:%s (%p), size: %d (result: %d), SymType: '%s', PDB: '%s'\n",
1363                 img, mod, (LPVOID)baseAddr, size, result, symType, pdbName);
1364   else
1365   {
1366     DWORD v4 = (DWORD)(fileVersion & 0xFFFF);
1367     DWORD v3 = (DWORD)((fileVersion >> 16) & 0xFFFF);
1368     DWORD v2 = (DWORD)((fileVersion >> 32) & 0xFFFF);
1369     DWORD v1 = (DWORD)((fileVersion >> 48) & 0xFFFF);
1370     _snprintf_s(
1371         buffer, maxLen,
1372         "%s:%s (%p), size: %d (result: %d), SymType: '%s', PDB: '%s', fileVersion: %d.%d.%d.%d\n",
1373         img, mod, (LPVOID)baseAddr, size, result, symType, pdbName, v1, v2, v3, v4);
1374   }
1375   buffer[STACKWALK_MAX_NAMELEN - 1] = 0; // be sure it is NULL terminated
1376   OnOutput(buffer);
1377 }
1378 
OnCallstackEntry(CallstackEntryType eType,CallstackEntry & entry)1379 void StackWalker::OnCallstackEntry(CallstackEntryType eType, CallstackEntry& entry)
1380 {
1381   CHAR   buffer[STACKWALK_MAX_NAMELEN];
1382   size_t maxLen = STACKWALK_MAX_NAMELEN;
1383 #if _MSC_VER >= 1400
1384   maxLen = _TRUNCATE;
1385 #endif
1386   if ((eType != lastEntry) && (entry.offset != 0))
1387   {
1388     if (entry.name[0] == 0)
1389       MyStrCpy(entry.name, STACKWALK_MAX_NAMELEN, "(function-name not available)");
1390     if (entry.undName[0] != 0)
1391       MyStrCpy(entry.name, STACKWALK_MAX_NAMELEN, entry.undName);
1392     if (entry.undFullName[0] != 0)
1393       MyStrCpy(entry.name, STACKWALK_MAX_NAMELEN, entry.undFullName);
1394     if (entry.lineFileName[0] == 0)
1395     {
1396       MyStrCpy(entry.lineFileName, STACKWALK_MAX_NAMELEN, "(filename not available)");
1397       if (entry.moduleName[0] == 0)
1398         MyStrCpy(entry.moduleName, STACKWALK_MAX_NAMELEN, "(module-name not available)");
1399       _snprintf_s(buffer, maxLen, "%p (%s): %s: %s\n", (LPVOID)entry.offset, entry.moduleName,
1400                   entry.lineFileName, entry.name);
1401     }
1402     else
1403       _snprintf_s(buffer, maxLen, "%s (%d): %s\n", entry.lineFileName, entry.lineNumber,
1404                   entry.name);
1405     buffer[STACKWALK_MAX_NAMELEN - 1] = 0;
1406     OnOutput(buffer);
1407   }
1408 }
1409 
OnDbgHelpErr(LPCSTR szFuncName,DWORD gle,DWORD64 addr)1410 void StackWalker::OnDbgHelpErr(LPCSTR szFuncName, DWORD gle, DWORD64 addr)
1411 {
1412   CHAR   buffer[STACKWALK_MAX_NAMELEN];
1413   size_t maxLen = STACKWALK_MAX_NAMELEN;
1414 #if _MSC_VER >= 1400
1415   maxLen = _TRUNCATE;
1416 #endif
1417   _snprintf_s(buffer, maxLen, "ERROR: %s, GetLastError: %d (Address: %p)\n", szFuncName, gle,
1418               (LPVOID)addr);
1419   buffer[STACKWALK_MAX_NAMELEN - 1] = 0;
1420   OnOutput(buffer);
1421 }
1422 
OnSymInit(LPCSTR szSearchPath,DWORD symOptions,LPCSTR szUserName)1423 void StackWalker::OnSymInit(LPCSTR szSearchPath, DWORD symOptions, LPCSTR szUserName)
1424 {
1425   CHAR   buffer[STACKWALK_MAX_NAMELEN];
1426   size_t maxLen = STACKWALK_MAX_NAMELEN;
1427 #if _MSC_VER >= 1400
1428   maxLen = _TRUNCATE;
1429 #endif
1430   _snprintf_s(buffer, maxLen, "SymInit: Symbol-SearchPath: '%s', symOptions: %d, UserName: '%s'\n",
1431               szSearchPath, symOptions, szUserName);
1432   buffer[STACKWALK_MAX_NAMELEN - 1] = 0;
1433   OnOutput(buffer);
1434   // Also display the OS-version
1435 #if _MSC_VER <= 1200
1436   OSVERSIONINFOA ver;
1437   ZeroMemory(&ver, sizeof(OSVERSIONINFOA));
1438   ver.dwOSVersionInfoSize = sizeof(ver);
1439   if (GetVersionExA(&ver) != FALSE)
1440   {
1441     _snprintf_s(buffer, maxLen, "OS-Version: %d.%d.%d (%s)\n", ver.dwMajorVersion,
1442                 ver.dwMinorVersion, ver.dwBuildNumber, ver.szCSDVersion);
1443     buffer[STACKWALK_MAX_NAMELEN - 1] = 0;
1444     OnOutput(buffer);
1445   }
1446 #else
1447   OSVERSIONINFOEXA ver;
1448   ZeroMemory(&ver, sizeof(OSVERSIONINFOEXA));
1449   ver.dwOSVersionInfoSize = sizeof(ver);
1450 #if _MSC_VER >= 1900
1451 #pragma warning(push)
1452 #pragma warning(disable : 4996)
1453 #endif
1454   if (GetVersionExA((OSVERSIONINFOA*)&ver) != FALSE)
1455   {
1456     _snprintf_s(buffer, maxLen, "OS-Version: %d.%d.%d (%s) 0x%x-0x%x\n", ver.dwMajorVersion,
1457                 ver.dwMinorVersion, ver.dwBuildNumber, ver.szCSDVersion, ver.wSuiteMask,
1458                 ver.wProductType);
1459     buffer[STACKWALK_MAX_NAMELEN - 1] = 0;
1460     OnOutput(buffer);
1461   }
1462 #if _MSC_VER >= 1900
1463 #pragma warning(pop)
1464 #endif
1465 #endif
1466 }
1467 
OnOutput(LPCSTR buffer)1468 void StackWalker::OnOutput(LPCSTR buffer)
1469 {
1470   OutputDebugStringA(buffer);
1471 }
1472