1 /*
2  * PROJECT:     Dr. Watson crash reporter
3  * LICENSE:     GPL-2.0+ (https://spdx.org/licenses/GPL-2.0+)
4  * PURPOSE:     Print a stacktrace
5  * COPYRIGHT:   Copyright 2017 Mark Jansen (mark.jansen@reactos.org)
6  */
7 
8 #include "precomp.h"
9 #include <dbghelp.h>
10 
11 
12 void BeginStackBacktrace(DumpData& data)
13 {
14     DWORD symOptions = SymGetOptions();
15     symOptions |= SYMOPT_UNDNAME | SYMOPT_AUTO_PUBLICS | SYMOPT_DEFERRED_LOADS;
16     SymSetOptions(symOptions);
17     SymInitialize(data.ProcessHandle, NULL, TRUE);
18 }
19 
20 void EndStackBacktrace(DumpData& data)
21 {
22     SymCleanup(data.ProcessHandle);
23 }
24 
25 static char ToChar(UCHAR data)
26 {
27     if (data < 0xa)
28         return '0' + data;
29     else if (data <= 0xf)
30         return 'a' + data - 0xa;
31     return '?';
32 }
33 
34 void PrintStackBacktrace(FILE* output, DumpData& data, ThreadData& thread)
35 {
36     DWORD MachineType;
37     STACKFRAME64 StackFrame = { { 0 } };
38 
39 #ifdef _M_X64
40     MachineType = IMAGE_FILE_MACHINE_AMD64;
41     StackFrame.AddrPC.Offset = thread.Context.Rip;
42     StackFrame.AddrPC.Mode = AddrModeFlat;
43     StackFrame.AddrStack.Offset = thread.Context.Rsp;
44     StackFrame.AddrStack.Mode = AddrModeFlat;
45     StackFrame.AddrFrame.Offset = thread.Context.Rbp;
46     StackFrame.AddrFrame.Mode = AddrModeFlat;
47 #else
48     MachineType = IMAGE_FILE_MACHINE_I386;
49     StackFrame.AddrPC.Offset =  thread.Context.Eip;
50     StackFrame.AddrPC.Mode = AddrModeFlat;
51     StackFrame.AddrStack.Offset = thread.Context.Esp;
52     StackFrame.AddrStack.Mode = AddrModeFlat;
53     StackFrame.AddrFrame.Offset = thread.Context.Ebp;
54     StackFrame.AddrFrame.Mode = AddrModeFlat;
55 #endif
56 
57 
58 #define STACKWALK_MAX_NAMELEN   512
59     char buf[sizeof(SYMBOL_INFO) + STACKWALK_MAX_NAMELEN] = {0};
60     SYMBOL_INFO* sym = (SYMBOL_INFO *)buf;
61     IMAGEHLP_MODULE64 Module = { 0 };
62     sym->SizeOfStruct = sizeof(sym);
63 
64     /* FIXME: Disasm function! */
65 
66     xfprintf(output, NEWLINE "*----> Stack Back Trace <----*" NEWLINE NEWLINE);
67     bool first = true;
68     ULONG_PTR LastFrame = StackFrame.AddrFrame.Offset - 8;
69     while(StackWalk64(MachineType, data.ProcessHandle, thread.Handle, &StackFrame, &thread.Context,
70                          NULL, SymFunctionTableAccess64, SymGetModuleBase64, NULL))
71     {
72         if (!StackFrame.AddrPC.Offset)
73             break;
74 
75         if (LastFrame >= StackFrame.AddrFrame.Offset)
76             break;
77 
78         LastFrame = StackFrame.AddrFrame.Offset;
79 
80         if (first)
81         {
82             xfprintf(output, "FramePtr ReturnAd Param#1  Param#2  Param#3  Param#4  Function Name" NEWLINE);
83             first = false;
84         }
85 
86         Module.SizeOfStruct = sizeof(Module);
87         DWORD64 ModBase = SymGetModuleBase64(data.ProcessHandle, StackFrame.AddrPC.Offset);
88         if (!SymGetModuleInfo64(data.ProcessHandle, ModBase, &Module))
89             strcpy(Module.ModuleName, "<nomod>");
90 
91         memset(sym, '\0', sizeof(*sym) + STACKWALK_MAX_NAMELEN);
92         sym->SizeOfStruct = sizeof(*sym);
93         sym->MaxNameLen = STACKWALK_MAX_NAMELEN;
94         DWORD64 displacement;
95 
96         if (!SymFromAddr(data.ProcessHandle, StackFrame.AddrPC.Offset, &displacement, sym))
97             strcpy(sym->Name, "<nosymbols>");
98 
99         xfprintf(output, "%p %p %p %p %p %p %s!%s" NEWLINE,
100                  (ULONG_PTR)StackFrame.AddrFrame.Offset, (ULONG_PTR)StackFrame.AddrPC.Offset,
101                  (ULONG_PTR)StackFrame.Params[0], (ULONG_PTR)StackFrame.Params[1],
102                  (ULONG_PTR)StackFrame.Params[2], (ULONG_PTR)StackFrame.Params[3],
103                  Module.ModuleName, sym->Name);
104     }
105 
106     UCHAR stackData[0x10 * 10];
107     SIZE_T sizeRead;
108 #if defined(_M_IX86)
109     ULONG_PTR stackPointer = thread.Context.Esp;
110 #elif defined(_M_AMD64)
111     ULONG_PTR stackPointer = thread.Context.Rsp;
112 #else
113 #error Unknown architecture
114 #endif
115     if (!ReadProcessMemory(data.ProcessHandle, (PVOID)stackPointer, stackData, sizeof(stackData), &sizeRead))
116         return;
117 
118     xfprintf(output, NEWLINE "*----> Raw Stack Dump <----*" NEWLINE NEWLINE);
119     for (size_t n = 0; n < sizeof(stackData); n += 0x10)
120     {
121         char HexData1[] = "?? ?? ?? ?? ?? ?? ?? ??";
122         char HexData2[] = "?? ?? ?? ?? ?? ?? ?? ??";
123         char AsciiData1[] = "????????";
124         char AsciiData2[] = "????????";
125 
126         for (size_t j = 0; j < 8; ++j)
127         {
128             size_t idx = j + n;
129             if (idx < sizeRead)
130             {
131                 HexData1[j * 3] = ToChar(stackData[idx] >> 4);
132                 HexData1[j * 3 + 1] = ToChar(stackData[idx] & 0xf);
133                 AsciiData1[j] = isprint(stackData[idx]) ? stackData[idx] : '.';
134             }
135             idx += 8;
136             if (idx < sizeRead)
137             {
138                 HexData2[j * 3] = ToChar(stackData[idx] >> 4);
139                 HexData2[j * 3 + 1] = ToChar(stackData[idx] & 0xf);
140                 AsciiData2[j] = isprint(stackData[idx]) ? stackData[idx] : '.';
141             }
142         }
143 
144         xfprintf(output, "%p %s - %s  %s%s" NEWLINE, stackPointer+n, HexData1, HexData2, AsciiData1, AsciiData2);
145     }
146 }
147