rts: fix filename case for mingw32 target
[ghc.git] / rts / win32 / veh_excn.c
1 /* -----------------------------------------------------------------------------
2 *
3 * (c) The GHC Team 1998-2000
4 *
5 * Error Handling implementations for windows
6 *
7 * ---------------------------------------------------------------------------*/
8 #define UNICODE 1
9 #include "Rts.h"
10 #include "ghcconfig.h"
11 #include "veh_excn.h"
12 #include "LinkerInternals.h"
13 #include <assert.h>
14 #include <stdbool.h>
15 #include <dbghelp.h>
16 #include <shellapi.h>
17 #include <shlobj.h>
18 #include <wchar.h>
19 #include <windows.h>
20 #include <stdio.h>
21 #include <excpt.h>
22 #include <inttypes.h>
23 #include <dbghelp.h>
24
25 /////////////////////////////////
26 // Exception / signal handlers.
27 /////////////////////////////////
28
29 /*
30 SEH (Structured Error Handler) on Windows is quite tricky. On x86 SEHs are
31 stack based and are stored in FS[0] of each thread. Which means every time we
32 spawn an OS thread we'd have to set up the error handling. However on x64 it's
33 table based and memory region based. e.g. you register a handler for a
34 particular memory range. This means that we'd have to register handlers for
35 each block of code we load externally or generate internally ourselves.
36
37 In Windows XP VEH (Vectored Exception Handler) and VCH (Vectored Continue
38 Handler) were added. Both of these are global/process wide handlers, the
39 former handling all exceptions and the latter handling only exceptions which
40 we're trying to recover from, e.g. a handler returned
41 EXCEPTION_CONTINUE_EXECUTION.
42
43 And lastly you have top level exception filters, which are also process global
44 but the problem here is that you can only have one, and setting this removes
45 the previous ones. The chain of exception handling looks like
46
47 [ Vectored Exception Handler ]
48 |
49 [ Structured Exception Handler ]
50 |
51 [ Exception Filters ]
52 |
53 [ Vectored Continue Handler ]
54
55 To make things more tricky, the exception handlers handle both hardware and
56 software exceptions Which means previously when we registered VEH handlers
57 we would also trap software exceptions. Which means when haskell code was
58 loaded in a C++ or C# context we would swallow exceptions and terminate in
59 contexes that normally the runtime should be able to continue on, e.g. you
60 could be handling the segfault in your C++ code, or the div by 0.
61
62 We could not handle these exceptions, but GHCi would just die a horrible death
63 then on normal Haskell only code when such an exception occurs.
64
65 So instead, we'll move to Continue handler, to run as late as possible, and
66 also register a filter which calls any existing filter, and then runs the
67 continue handlers, we then also only run as the last continue handler so we
68 don't supercede any other VCH handlers.
69
70 Lastly we'll also provide a way for users to disable the exception handling
71 entirely so even if the new approach doesn't solve the issue they can work
72 around it. After all, I don't expect any interpreted code if you are running
73 a haskell dll.
74
75 For a detailed analysis see
76 https://reverseengineering.stackexchange.com/questions/14992/what-are-the-vectored-continue-handlers
77 and https://www.gamekiller.net/threads/vectored-exception-handler.3237343/
78 */
79
80 // Define some values for the ordering of VEH Handlers:
81 // - CALL_FIRST means call this exception handler first
82 // - CALL_LAST means call this exception handler last
83 #define CALL_FIRST 1
84 #define CALL_LAST 0
85
86 // this should be in <excpt.h>, but it's been removed from MinGW distributions
87 #if !defined(EH_UNWINDING)
88 #define EH_UNWINDING 0x02
89 #endif /* EH_UNWINDING */
90
91 // Registered exception handler
92 PVOID __hs_handle = NULL;
93 LPTOP_LEVEL_EXCEPTION_FILTER oldTopFilter = NULL;
94 bool crash_dump = false;
95 bool filter_called = false;
96
97 long WINAPI __hs_exception_handler(struct _EXCEPTION_POINTERS *exception_data)
98 {
99 if (!crash_dump && filter_called)
100 return EXCEPTION_CONTINUE_EXECUTION;
101
102 long action = EXCEPTION_CONTINUE_SEARCH;
103 ULONG_PTR what;
104 fprintf (stderr, "\n");
105
106 // When the system unwinds the VEH stack after having handled an excn,
107 // return immediately.
108 if ((exception_data->ExceptionRecord->ExceptionFlags & EH_UNWINDING) == 0)
109 {
110 // Error handling cases covered by this implementation.
111 switch (exception_data->ExceptionRecord->ExceptionCode) {
112 case EXCEPTION_FLT_DIVIDE_BY_ZERO:
113 case EXCEPTION_INT_DIVIDE_BY_ZERO:
114 fprintf(stderr, "divide by zero\n");
115 action = EXCEPTION_CONTINUE_EXECUTION;
116 break;
117 case EXCEPTION_STACK_OVERFLOW:
118 fprintf(stderr, "C stack overflow in generated code\n");
119 action = EXCEPTION_CONTINUE_EXECUTION;
120 break;
121 case EXCEPTION_ACCESS_VIOLATION:
122 what = exception_data->ExceptionRecord->ExceptionInformation[0];
123 fprintf(stderr, "Access violation in generated code"
124 " when %s 0x%" PRIxPTR "\n"
125 , what == 0 ? "reading"
126 : what == 1 ? "writing"
127 : what == 8 ? "executing data at"
128 : "?"
129 , (uintptr_t) exception_data
130 ->ExceptionRecord
131 ->ExceptionInformation[1]
132 );
133 action = EXCEPTION_CONTINUE_EXECUTION;
134 break;
135 default:;
136 }
137
138 // If an error has occurred and we've decided to continue execution
139 // then we've done so to prevent something else from handling the error.
140 // But the correct action is still to exit as fast as possible.
141 if (EXCEPTION_CONTINUE_EXECUTION == action)
142 {
143 fflush(stderr);
144 generateStack (exception_data);
145 generateDump (exception_data);
146 stg_exit(EXIT_FAILURE);
147 }
148 }
149
150 return action;
151 }
152
153 long WINAPI __hs_exception_filter(struct _EXCEPTION_POINTERS *exception_data)
154 {
155 filter_called = true;
156 long result = EXCEPTION_CONTINUE_EXECUTION;
157 if (oldTopFilter)
158 {
159 result = (*oldTopFilter)(exception_data);
160 if (EXCEPTION_CONTINUE_SEARCH == result)
161 result = EXCEPTION_CONTINUE_EXECUTION;
162 return result;
163 }
164
165 crash_dump = true;
166 return result;
167 }
168
169 void __register_hs_exception_handler( void )
170 {
171 if (!RtsFlags.MiscFlags.install_seh_handlers)
172 return;
173
174 // Allow the VCH handler to be registered only once.
175 if (NULL == __hs_handle)
176 {
177 // Be the last one to run, We can then be sure we didn't interfere with
178 // anything else.
179 __hs_handle = AddVectoredContinueHandler(CALL_LAST,
180 __hs_exception_handler);
181 // should the handler not be registered this will return a null.
182 assert(__hs_handle);
183
184 // Register for an exception filter to ensure the continue handler gets
185 // hit if no one handled the exception.
186 oldTopFilter = SetUnhandledExceptionFilter (__hs_exception_filter);
187 }
188 else
189 {
190 errorBelch("There is no need to call __register_hs_exception_handler()"
191 " twice, VEH handlers are global per process.");
192 }
193 }
194
195 void __unregister_hs_exception_handler( void )
196 {
197 if (!RtsFlags.MiscFlags.install_seh_handlers)
198 return;
199
200 if (__hs_handle != NULL)
201 {
202 // Should the return value be checked? we're terminating anyway.
203 RemoveVectoredContinueHandler(__hs_handle);
204 __hs_handle = NULL;
205 }
206 else
207 {
208 errorBelch("__unregister_hs_exception_handler() called without having"
209 "called __register_hs_exception_handler() first.");
210 }
211 }
212
213 // Generate a crash dump, however in order for these to generate undecorated
214 // names we really need to be able to generate PDB files.
215 void generateDump (EXCEPTION_POINTERS* pExceptionPointers)
216 {
217 if (!RtsFlags.MiscFlags.generate_dump_file)
218 return;
219
220 WCHAR szPath[MAX_PATH];
221 WCHAR szFileName[MAX_PATH];
222 WCHAR const *const szAppName = L"ghc";
223 WCHAR const *const szVersion = L"";
224 DWORD dwBufferSize = MAX_PATH;
225 HANDLE hDumpFile;
226 SYSTEMTIME stLocalTime;
227 MINIDUMP_EXCEPTION_INFORMATION ExpParam;
228
229 GetLocalTime (&stLocalTime);
230 GetTempPathW (dwBufferSize, szPath);
231
232 swprintf (szFileName, MAX_PATH,
233 L"%ls%ls%ls-%04d%02d%02d-%02d%02d%02d-%ld-%ld.dmp",
234 szPath, szAppName, szVersion,
235 stLocalTime.wYear, stLocalTime.wMonth, stLocalTime.wDay,
236 stLocalTime.wHour, stLocalTime.wMinute, stLocalTime.wSecond,
237 GetCurrentProcessId(), GetCurrentThreadId());
238 hDumpFile = CreateFileW (szFileName, GENERIC_READ|GENERIC_WRITE,
239 FILE_SHARE_WRITE|FILE_SHARE_READ, 0, CREATE_ALWAYS, 0, 0);
240
241 ExpParam.ThreadId = GetCurrentThreadId();
242 ExpParam.ExceptionPointers = pExceptionPointers;
243 ExpParam.ClientPointers = TRUE;
244
245 MiniDumpWriteDump(GetCurrentProcess(), GetCurrentProcessId(),
246 hDumpFile, MiniDumpNormal | MiniDumpWithDataSegs |
247 MiniDumpWithThreadInfo | MiniDumpWithCodeSegs,
248 &ExpParam, NULL, NULL);
249
250 fprintf (stderr, "Crash dump created. Dump written to:\n\t%ls", szFileName);
251 }
252
253 // Generate stack trace information, we can piggy back on information we know
254 // about in the runtime linker to resolve symbols. So this is a good opportunity
255 // to make the output more useful.
256 void generateStack (EXCEPTION_POINTERS* pExceptionPointers)
257 {
258 if (!RtsFlags.MiscFlags.generate_stack_trace)
259 return;
260
261 PCONTEXT context = pExceptionPointers->ContextRecord;
262 STACKFRAME64 stackFrame = {0};
263 DWORD machineType;
264
265 #if defined(x86_64_HOST_ARCH)
266 machineType = IMAGE_FILE_MACHINE_AMD64;
267 stackFrame.AddrPC.Offset = context->Rip;
268 stackFrame.AddrPC.Mode = AddrModeFlat;
269
270 stackFrame.AddrFrame.Offset = context->Rbp;
271 stackFrame.AddrFrame.Mode = AddrModeFlat;
272
273 stackFrame.AddrStack.Offset = context->Rsp;
274 stackFrame.AddrStack.Mode = AddrModeFlat;
275 #else
276 machineType = IMAGE_FILE_MACHINE_I386;
277 stackFrame.AddrPC.Offset = context->Eip;
278 stackFrame.AddrPC.Mode = AddrModeFlat;
279
280 stackFrame.AddrFrame.Offset = context->Ebp;
281 stackFrame.AddrFrame.Mode = AddrModeFlat;
282
283 stackFrame.AddrStack.Offset = context->Esp;
284 stackFrame.AddrStack.Mode = AddrModeFlat;
285 #endif
286 fprintf (stderr, "\n Attempting to reconstruct a stack trace...\n\n");
287 if (!SymInitialize (GetCurrentProcess (), NULL, true))
288 fprintf (stderr, " \nNOTE: Symbols could not be loaded. Addresses may"
289 " be unresolved.\n\n");
290
291 /* Maximum amount of stack frames to show. */
292 /* Phyx: I'm not sure if I should make this configurable or not. Would a
293 longer stack really be more useful? usually you only care about the top
294 few. */
295 int max_frames = 35;
296
297 fprintf (stderr, " Frame\tCode address\n");
298 DWORD64 lastBp = 0; /* Prevent loops with optimized stackframes. */
299 while (StackWalk64 (machineType, GetCurrentProcess(), GetCurrentThread(),
300 &stackFrame, context, NULL, SymFunctionTableAccess64,
301 SymGetModuleBase64, NULL) && max_frames > 0)
302 {
303 if (stackFrame.AddrPC.Offset == 0)
304 {
305 fprintf (stderr, "Null address\n");
306 break;
307 }
308 wchar_t buffer[1024];
309 uintptr_t topSp = 0;
310 fprintf (stderr, " * 0x%" PRIxPTR "\t%ls\n",
311 (uintptr_t)stackFrame.AddrFrame.Offset,
312 resolveSymbolAddr ((wchar_t*)&buffer, 1024,
313 (SymbolAddr*)stackFrame.AddrPC.Offset,
314 &topSp));
315 if (lastBp >= stackFrame.AddrFrame.Offset)
316 {
317 fprintf (stderr, "Stack frame out of sequence...\n");
318 break;
319 }
320 lastBp = stackFrame.AddrFrame.Offset;
321
322 max_frames--;
323 if (max_frames ==0)
324 {
325 fprintf (stderr, "\n ... (maximum recursion depth reached.)\n");
326 }
327 }
328 fprintf (stderr, "\n");
329 fflush(stderr);
330 }