1 /*
2 * Copyright (C) 2005-2018 Team Kodi
3 * This file is part of Kodi - https://kodi.tv
4 *
5 * SPDX-License-Identifier: GPL-2.0-or-later
6 * See LICENSES/README.md for more information.
7 */
8
9 #include "Win32Exception.h"
10
11 #include "Util.h"
12 #include "WIN32Util.h"
13 #include "utils/StringUtils.h"
14 #include "utils/URIUtils.h"
15
16 #include "platform/win32/CharsetConverter.h"
17
18 #include <VersionHelpers.h>
19 #include <dbghelp.h>
20
21 typedef BOOL (WINAPI *MINIDUMPWRITEDUMP)(HANDLE hProcess, DWORD dwPid, HANDLE hFile, MINIDUMP_TYPE DumpType,
22 const PMINIDUMP_EXCEPTION_INFORMATION ExceptionParam,
23 const PMINIDUMP_USER_STREAM_INFORMATION UserStreamParam,
24 const PMINIDUMP_CALLBACK_INFORMATION CallbackParam);
25
26 // StackWalk64()
27 typedef BOOL (__stdcall *tSW)(
28 DWORD MachineType,
29 HANDLE hProcess,
30 HANDLE hThread,
31 LPSTACKFRAME64 StackFrame,
32 PVOID ContextRecord,
33 PREAD_PROCESS_MEMORY_ROUTINE64 ReadMemoryRoutine,
34 PFUNCTION_TABLE_ACCESS_ROUTINE64 FunctionTableAccessRoutine,
35 PGET_MODULE_BASE_ROUTINE64 GetModuleBaseRoutine,
36 PTRANSLATE_ADDRESS_ROUTINE64 TranslateAddress );
37
38 // SymInitialize()
39 typedef BOOL (__stdcall *tSI)( IN HANDLE hProcess, IN PSTR UserSearchPath, IN BOOL fInvadeProcess );
40 // SymCleanup()
41 typedef BOOL (__stdcall *tSC)( IN HANDLE hProcess );
42 // SymGetSymFromAddr64()
43 typedef BOOL (__stdcall *tSGSFA)( IN HANDLE hProcess, IN DWORD64 dwAddr, OUT PDWORD64 pdwDisplacement, OUT PIMAGEHLP_SYMBOL64 Symbol );
44 // UnDecorateSymbolName()
45 typedef DWORD (__stdcall WINAPI *tUDSN)( PCSTR DecoratedName, PSTR UnDecoratedName, DWORD UndecoratedLength, DWORD Flags );
46 // SymGetLineFromAddr64()
47 typedef BOOL (__stdcall *tSGLFA)( IN HANDLE hProcess, IN DWORD64 dwAddr, OUT PDWORD pdwDisplacement, OUT PIMAGEHLP_LINE64 Line );
48 // SymGetModuleBase64()
49 typedef DWORD64 (__stdcall *tSGMB)( IN HANDLE hProcess, IN DWORD64 dwAddr );
50 // SymFunctionTableAccess64()
51 typedef PVOID (__stdcall *tSFTA)( HANDLE hProcess, DWORD64 AddrBase );
52 // SymGetOptions()
53 typedef DWORD (__stdcall *tSGO)( VOID );
54 // SymSetOptions()
55 typedef DWORD (__stdcall *tSSO)( IN DWORD SymOptions );
56
57 // GetCurrentPackageFullName
58 typedef LONG (__stdcall *GCPFN)(UINT32*, PWSTR);
59
60 std::string win32_exception::mVersion;
61
write_minidump(EXCEPTION_POINTERS * pEp)62 bool win32_exception::write_minidump(EXCEPTION_POINTERS* pEp)
63 {
64 // Create the dump file where the xbmc.exe resides
65 bool returncode = false;
66 std::string dumpFileName;
67 std::wstring dumpFileNameW;
68 KODI::TIME::SystemTime stLocalTime;
69 KODI::TIME::GetLocalTime(&stLocalTime);
70
71 dumpFileName = StringUtils::Format(
72 "kodi_crashlog-%s-%04d%02d%02d-%02d%02d%02d.dmp", mVersion.c_str(), stLocalTime.year,
73 stLocalTime.month, stLocalTime.day, stLocalTime.hour, stLocalTime.minute, stLocalTime.second);
74
75 dumpFileName = CWIN32Util::SmbToUnc(URIUtils::AddFileToFolder(CWIN32Util::GetProfilePath(), CUtil::MakeLegalFileName(dumpFileName)));
76
77 dumpFileNameW = KODI::PLATFORM::WINDOWS::ToW(dumpFileName);
78 HANDLE hDumpFile = CreateFileW(dumpFileNameW.c_str(), GENERIC_WRITE, 0, 0, CREATE_ALWAYS, 0, 0);
79
80 HMODULE hDbgHelpDll = nullptr;
81
82 if (hDumpFile == INVALID_HANDLE_VALUE)
83 {
84 goto cleanup;
85 }
86
87 // Load the DBGHELP DLL
88 hDbgHelpDll = ::LoadLibrary(L"DBGHELP.DLL");
89 if (!hDbgHelpDll)
90 {
91 goto cleanup;
92 }
93
94 MINIDUMPWRITEDUMP pDump = (MINIDUMPWRITEDUMP)::GetProcAddress(hDbgHelpDll, "MiniDumpWriteDump");
95 if (!pDump)
96 {
97 goto cleanup;
98 }
99
100 // Initialize minidump structure
101 MINIDUMP_EXCEPTION_INFORMATION mdei;
102 mdei.ThreadId = GetCurrentThreadId();
103 mdei.ExceptionPointers = pEp;
104 mdei.ClientPointers = FALSE;
105
106 // Call the minidump api with normal dumping
107 // We can get more detail information by using other minidump types but the dump file will be
108 // extremely large.
109 BOOL bMiniDumpSuccessful = pDump(GetCurrentProcess(), GetCurrentProcessId(), hDumpFile, MiniDumpNormal, &mdei, 0, NULL);
110 if( !bMiniDumpSuccessful )
111 {
112 goto cleanup;
113 }
114
115 returncode = true;
116
117 cleanup:
118
119 if (hDumpFile != INVALID_HANDLE_VALUE)
120 CloseHandle(hDumpFile);
121
122 if (hDbgHelpDll)
123 FreeLibrary(hDbgHelpDll);
124
125 return returncode;
126 }
127
128 /* \brief Writes a simple stack trace to the XBMC user directory.
129 It needs a valid .pdb file to show the method, filename and
130 line number where the exception took place.
131 */
write_stacktrace(EXCEPTION_POINTERS * pEp)132 bool win32_exception::write_stacktrace(EXCEPTION_POINTERS* pEp)
133 {
134 #define STACKWALK_MAX_NAMELEN 1024
135
136 std::string dumpFileName, strOutput;
137 std::wstring dumpFileNameW;
138 CHAR cTemp[STACKWALK_MAX_NAMELEN];
139 DWORD dwBytes;
140 KODI::TIME::SystemTime stLocalTime;
141 KODI::TIME::GetLocalTime(&stLocalTime);
142 bool returncode = false;
143 STACKFRAME64 frame = { 0 };
144 HANDLE hCurProc = GetCurrentProcess();
145 IMAGEHLP_SYMBOL64* pSym = NULL;
146 HANDLE hDumpFile = INVALID_HANDLE_VALUE;
147 tSC pSC = NULL;
148
149 HMODULE hDbgHelpDll = ::LoadLibrary(L"DBGHELP.DLL");
150 if (!hDbgHelpDll)
151 {
152 goto cleanup;
153 }
154
155 tSI pSI = (tSI) GetProcAddress(hDbgHelpDll, "SymInitialize" );
156 tSGO pSGO = (tSGO) GetProcAddress(hDbgHelpDll, "SymGetOptions" );
157 tSSO pSSO = (tSSO) GetProcAddress(hDbgHelpDll, "SymSetOptions" );
158 pSC = (tSC) GetProcAddress(hDbgHelpDll, "SymCleanup" );
159 tSW pSW = (tSW) GetProcAddress(hDbgHelpDll, "StackWalk64" );
160 tSGSFA pSGSFA = (tSGSFA) GetProcAddress(hDbgHelpDll, "SymGetSymFromAddr64" );
161 tUDSN pUDSN = (tUDSN) GetProcAddress(hDbgHelpDll, "UnDecorateSymbolName" );
162 tSGLFA pSGLFA = (tSGLFA) GetProcAddress(hDbgHelpDll, "SymGetLineFromAddr64" );
163 tSFTA pSFTA = (tSFTA) GetProcAddress(hDbgHelpDll, "SymFunctionTableAccess64" );
164 tSGMB pSGMB = (tSGMB) GetProcAddress(hDbgHelpDll, "SymGetModuleBase64" );
165
166 if(pSI == NULL || pSGO == NULL || pSSO == NULL || pSC == NULL || pSW == NULL || pSGSFA == NULL || pUDSN == NULL || pSGLFA == NULL ||
167 pSFTA == NULL || pSGMB == NULL)
168 goto cleanup;
169
170 dumpFileName = StringUtils::Format(
171 "kodi_stacktrace-%s-%04d%02d%02d-%02d%02d%02d.txt", mVersion.c_str(), stLocalTime.year,
172 stLocalTime.month, stLocalTime.day, stLocalTime.hour, stLocalTime.minute, stLocalTime.second);
173
174 dumpFileName = CWIN32Util::SmbToUnc(URIUtils::AddFileToFolder(CWIN32Util::GetProfilePath(), CUtil::MakeLegalFileName(dumpFileName)));
175
176 dumpFileNameW = KODI::PLATFORM::WINDOWS::ToW(dumpFileName);
177 hDumpFile = CreateFileW(dumpFileNameW.c_str(), GENERIC_WRITE, 0, 0, CREATE_ALWAYS, 0, 0);
178
179 if (hDumpFile == INVALID_HANDLE_VALUE)
180 {
181 goto cleanup;
182 }
183
184 frame.AddrPC.Mode = AddrModeFlat; // Address mode for this pointer: flat 32 bit addressing
185 frame.AddrStack.Mode = AddrModeFlat; // Address mode for this pointer: flat 32 bit addressing
186 frame.AddrFrame.Mode = AddrModeFlat; // Address mode for this pointer: flat 32 bit addressing
187
188 #if defined(_X86_)
189 frame.AddrPC.Offset = pEp->ContextRecord->Eip; // Current location in program
190 frame.AddrStack.Offset = pEp->ContextRecord->Esp; // Stack pointers current value
191 frame.AddrFrame.Offset = pEp->ContextRecord->Ebp; // Value of register used to access local function variables.
192 #else
193 frame.AddrPC.Offset = pEp->ContextRecord->Rip; // Current location in program
194 frame.AddrStack.Offset = pEp->ContextRecord->Rsp; // Stack pointers current value
195 frame.AddrFrame.Offset = pEp->ContextRecord->Rbp; // Value of register used to access local function variables.
196 #endif
197
198 if(pSI(hCurProc, NULL, TRUE) == FALSE)
199 goto cleanup;
200
201 DWORD symOptions = pSGO();
202 symOptions |= SYMOPT_LOAD_LINES;
203 symOptions |= SYMOPT_FAIL_CRITICAL_ERRORS;
204 symOptions &= ~SYMOPT_UNDNAME;
205 symOptions &= ~SYMOPT_DEFERRED_LOADS;
206 symOptions = pSSO(symOptions);
207
208 pSym = (IMAGEHLP_SYMBOL64 *) malloc(sizeof(IMAGEHLP_SYMBOL64) + STACKWALK_MAX_NAMELEN);
209 if (!pSym)
210 goto cleanup;
211 memset(pSym, 0, sizeof(IMAGEHLP_SYMBOL64) + STACKWALK_MAX_NAMELEN);
212 pSym->SizeOfStruct = sizeof(IMAGEHLP_SYMBOL64);
213 pSym->MaxNameLength = STACKWALK_MAX_NAMELEN;
214
215 IMAGEHLP_LINE64 Line;
216 memset(&Line, 0, sizeof(Line));
217 Line.SizeOfStruct = sizeof(Line);
218
219 IMAGEHLP_MODULE64 Module;
220 memset(&Module, 0, sizeof(Module));
221 Module.SizeOfStruct = sizeof(Module);
222 int seq=0;
223
224 strOutput = StringUtils::Format("Thread %d (process %d)\r\n", GetCurrentThreadId(), GetCurrentProcessId());
225 WriteFile(hDumpFile, strOutput.c_str(), strOutput.size(), &dwBytes, NULL);
226
227 while(pSW(IMAGE_FILE_MACHINE_I386, hCurProc, GetCurrentThread(), &frame, pEp->ContextRecord, NULL, pSFTA, pSGMB, NULL))
228 {
229 if(frame.AddrPC.Offset != 0)
230 {
231 DWORD64 symoffset=0;
232 DWORD lineoffset=0;
233 strOutput = StringUtils::Format("#%2d", seq++);
234
235 if(pSGSFA(hCurProc, frame.AddrPC.Offset, &symoffset, pSym))
236 {
237 if(pUDSN(pSym->Name, cTemp, STACKWALK_MAX_NAMELEN, UNDNAME_COMPLETE)>0)
238 strOutput.append(StringUtils::Format(" %s", cTemp));
239 }
240 if(pSGLFA(hCurProc, frame.AddrPC.Offset, &lineoffset, &Line))
241 strOutput.append(StringUtils::Format(" at %s:%d", Line.FileName, Line.LineNumber));
242
243 strOutput.append("\r\n");
244 WriteFile(hDumpFile, strOutput.c_str(), strOutput.size(), &dwBytes, NULL);
245 }
246 }
247 returncode = true;
248
249 cleanup:
250 if (pSym)
251 free( pSym );
252
253 if (hDumpFile != INVALID_HANDLE_VALUE)
254 CloseHandle(hDumpFile);
255
256 if(pSC)
257 pSC(hCurProc);
258
259 if (hDbgHelpDll)
260 FreeLibrary(hDbgHelpDll);
261
262 return returncode;
263 }
264
ShouldHook()265 bool win32_exception::ShouldHook()
266 {
267 if (!IsWindows8OrGreater())
268 return true;
269
270 bool result = true;
271
272 auto module = ::LoadLibrary(L"kernel32.dll");
273 if (module)
274 {
275 auto func = reinterpret_cast<GCPFN>(::GetProcAddress(module, "GetCurrentPackageFullName"));
276 if (func)
277 {
278 UINT32 length = 0;
279 auto r = func(&length, nullptr);
280 result = r == APPMODEL_ERROR_NO_PACKAGE;
281 }
282
283 ::FreeLibrary(module);
284 }
285
286 return result;
287 }
288