summaryrefslogtreecommitdiff
path: root/rts
diff options
context:
space:
mode:
Diffstat (limited to 'rts')
-rw-r--r--rts/RtsFlags.c19
-rw-r--r--rts/RtsMain.c19
-rw-r--r--rts/win32/veh_excn.c91
-rw-r--r--rts/win32/veh_excn.h1
4 files changed, 112 insertions, 18 deletions
diff --git a/rts/RtsFlags.c b/rts/RtsFlags.c
index 4194aa0eec..5a5abb0d19 100644
--- a/rts/RtsFlags.c
+++ b/rts/RtsFlags.c
@@ -225,8 +225,9 @@ void initRtsFlagsDefaults(void)
RtsFlags.ConcFlags.ctxtSwitchTime = USToTime(20000); // 20ms
RtsFlags.MiscFlags.install_signal_handlers = true;
- RtsFlags.MiscFlags.machineReadable = false;
- RtsFlags.MiscFlags.linkerMemBase = 0;
+ RtsFlags.MiscFlags.install_seh_handlers = true;
+ RtsFlags.MiscFlags.machineReadable = false;
+ RtsFlags.MiscFlags.linkerMemBase = 0;
#if defined(THREADED_RTS)
RtsFlags.ParFlags.nCapabilities = 1;
@@ -426,6 +427,10 @@ usage_text[] = {
#endif
" --install-signal-handlers=<yes|no>",
" Install signal handlers (default: yes)",
+#if defined(mingw32_HOST_OS)
+" --install-seh-handlers=<yes|no>",
+" Install exception handlers (default: yes)",
+#endif
#if defined(THREADED_RTS)
" -e<n> Maximum number of outstanding local sparks (default: 4096)",
#endif
@@ -840,6 +845,16 @@ error = true;
OPTION_UNSAFE;
RtsFlags.MiscFlags.install_signal_handlers = false;
}
+ else if (strequal("install-seh-handlers=yes",
+ &rts_argv[arg][2])) {
+ OPTION_UNSAFE;
+ RtsFlags.MiscFlags.install_seh_handlers = true;
+ }
+ else if (strequal("install-seh-handlers=no",
+ &rts_argv[arg][2])) {
+ OPTION_UNSAFE;
+ RtsFlags.MiscFlags.install_seh_handlers = false;
+ }
else if (strequal("machine-readable",
&rts_argv[arg][2])) {
OPTION_UNSAFE;
diff --git a/rts/RtsMain.c b/rts/RtsMain.c
index 57c38742b6..21b8577cca 100644
--- a/rts/RtsMain.c
+++ b/rts/RtsMain.c
@@ -24,28 +24,26 @@
// Hack: we assume that we're building a batch-mode system unless
// INTERPRETER is set
-
+
#if !defined(INTERPRETER) /* Hack */
// The rts entry point from a compiled program using a Haskell main
// function. This gets called from a tiny main function generated by
// GHC and linked into each compiled Haskell program that uses a
// Haskell main function.
-//
+//
// We expect the caller to pass ZCMain_main_closure for
// main_closure. The reason we cannot refer to this symbol directly
// is because we're inside the rts and we do not know for sure that
// we'll be using a Haskell main function.
-//
+//
// NOTE: This function is marked as _noreturn_ in Main.h
int hs_main ( int argc, char *argv[], // program args
StgClosure *main_closure, // closure for Main.main
RtsConfig rts_config) // RTS configuration
-
-{
- BEGIN_WINDOWS_VEH_HANDLER
+{
int exit_status;
SchedulerStatus status;
@@ -56,15 +54,14 @@ int hs_main ( int argc, char *argv[], // program args
}
#endif
-
-
-
hs_init_ghc(&argc, &argv, rts_config);
+ BEGIN_WINDOWS_VEH_HANDLER
+
// kick off the computation by creating the main thread with a pointer
// to mainIO_closure representing the computation of the overall program;
// then enter the scheduler with this thread and off we go;
- //
+ //
// in a parallel setup, where we have many instances of this code
// running on different PEs, we should do this only for the main PE
// (IAmMainThread is set in startupHaskell)
@@ -100,6 +97,6 @@ int hs_main ( int argc, char *argv[], // program args
END_WINDOWS_VEH_HANDLER
shutdownHaskellAndExit(exit_status, 0 /* !fastExit */);
- // No code beyond this point. Dead code elimination will remove it
+ // No code beyond this point. Dead code elimination will remove it
}
# endif /* BATCH_MODE */
diff --git a/rts/win32/veh_excn.c b/rts/win32/veh_excn.c
index d925ad8919..e45ea2b49c 100644
--- a/rts/win32/veh_excn.c
+++ b/rts/win32/veh_excn.c
@@ -15,6 +15,57 @@
// Exception / signal handlers.
/////////////////////////////////
+/*
+ SEH (Structured Error Handler) on Windows is quite tricky. On x86 SEHs are
+ stack based and are stored in FS[0] of each thread. Which means every time we
+ spawn an OS thread we'd have to set up the error handling. However on x64 it's
+ table based and memory region based. e.g. you register a handler for a
+ particular memory range. This means that we'd have to register handlers for
+ each block of code we load externally or generate internally ourselves.
+
+ In Windows XP VEH (Vectored Exception Handler) and VCH (Vectored Continue
+ Handler) were added. Both of these are global/process wide handlers, the
+ former handling all exceptions and the latter handling only exceptions which
+ we're trying to recover from, e.g. a handler returned
+ EXCEPTION_CONTINUE_EXECUTION.
+
+ And lastly you have top level exception filters, which are also process global
+ but the problem here is that you can only have one, and setting this removes
+ the previous ones. The chain of exception handling looks like
+
+ [ Vectored Exception Handler ]
+ |
+ [ Structured Exception Handler ]
+ |
+ [ Exception Filters ]
+ |
+ [ Vectored Continue Handler ]
+
+ To make things more tricky, the exception handlers handle both hardware and
+ software exceptions Which means previously when we registered VEH handlers
+ we would also trap software exceptions. Which means when haskell code was
+ loaded in a C++ or C# context we would swallow exceptions and terminate in
+ contexes that normally the runtime should be able to continue on, e.g. you
+ could be handling the segfault in your C++ code, or the div by 0.
+
+ We could not handle these exceptions, but GHCi would just die a horrible death
+ then on normal Haskell only code when such an exception occurs.
+
+ So instead, we'll move to Continue handler, to run as late as possible, and
+ also register a filter which calls any existing filter, and then runs the
+ continue handlers, we then also only run as the last continue handler so we
+ don't supercede any other VCH handlers.
+
+ Lastly we'll also provide a way for users to disable the exception handling
+ entirely so even if the new approach doesn't solve the issue they can work
+ around it. After all, I don't expect any interpreted code if you are running
+ a haskell dll.
+
+ For a detailed analysis see
+ https://reverseengineering.stackexchange.com/questions/14992/what-are-the-vectored-continue-handlers
+ and https://www.gamekiller.net/threads/vectored-exception-handler.3237343/
+ */
+
// Define some values for the ordering of VEH Handlers:
// - CALL_FIRST means call this exception handler first
// - CALL_LAST means call this exception handler last
@@ -28,6 +79,7 @@
// Registered exception handler
PVOID __hs_handle = NULL;
+LPTOP_LEVEL_EXCEPTION_FILTER oldTopFilter = NULL;
long WINAPI __hs_exception_handler(struct _EXCEPTION_POINTERS *exception_data)
{
@@ -74,32 +126,61 @@ long WINAPI __hs_exception_handler(struct _EXCEPTION_POINTERS *exception_data)
return action;
}
+long WINAPI __hs_exception_filter(struct _EXCEPTION_POINTERS *exception_data)
+{
+ long result = EXCEPTION_CONTINUE_EXECUTION;
+ if (oldTopFilter)
+ {
+ result = (*oldTopFilter)(exception_data);
+ if (EXCEPTION_CONTINUE_SEARCH == result)
+ result = EXCEPTION_CONTINUE_EXECUTION;
+ return result;
+ }
+
+ return result;
+}
+
void __register_hs_exception_handler( void )
{
- // Allow the VEH handler to be registered only once.
+ if (!RtsFlags.MiscFlags.install_seh_handlers)
+ return;
+
+ // Allow the VCH handler to be registered only once.
if (NULL == __hs_handle)
{
- __hs_handle = AddVectoredExceptionHandler(CALL_FIRST, __hs_exception_handler);
+ // Be the last one to run, We can then be sure we didn't interfere with
+ // anything else.
+ __hs_handle = AddVectoredContinueHandler(CALL_LAST,
+ __hs_exception_handler);
// should the handler not be registered this will return a null.
assert(__hs_handle);
+
+ // Register for an exception filter to ensure the continue handler gets
+ // hit if no one handled the exception.
+ oldTopFilter = SetUnhandledExceptionFilter (__hs_exception_filter);
}
else
{
- errorBelch("There is no need to call __register_hs_exception_handler() twice, VEH handlers are global per process.");
+ errorBelch("There is no need to call __register_hs_exception_handler()"
+ " twice, VEH handlers are global per process.");
}
}
void __unregister_hs_exception_handler( void )
{
+ if (!RtsFlags.MiscFlags.install_seh_handlers)
+ return;
+
if (__hs_handle != NULL)
{
// Should the return value be checked? we're terminating anyway.
- RemoveVectoredExceptionHandler(__hs_handle);
+ RemoveVectoredContinueHandler(__hs_handle);
__hs_handle = NULL;
}
else
{
- errorBelch("__unregister_hs_exception_handler() called without having called __register_hs_exception_handler() first.");
+ errorBelch("__unregister_hs_exception_handler() called without having"
+ "called __register_hs_exception_handler() first.");
}
}
diff --git a/rts/win32/veh_excn.h b/rts/win32/veh_excn.h
index fda837f1f1..72a9967afd 100644
--- a/rts/win32/veh_excn.h
+++ b/rts/win32/veh_excn.h
@@ -63,6 +63,7 @@
// See https://msdn.microsoft.com/en-us/library/windows/desktop/ms681419(v=vs.85).aspx
//
long WINAPI __hs_exception_handler( struct _EXCEPTION_POINTERS *exception_data );
+long WINAPI __hs_exception_filter(struct _EXCEPTION_POINTERS *exception_data);
// prototypes to the functions doing the registration and unregistration of the VEH handlers
void __register_hs_exception_handler( void );