summaryrefslogtreecommitdiff
path: root/rts/RtsMain.c
diff options
context:
space:
mode:
authorTamar Christina <tamar@zhox.com>2015-03-03 07:20:55 -0600
committerAustin Seipp <austin@well-typed.com>2015-03-03 07:20:56 -0600
commit5200bdeb26c5ec98739b14b10fc8907296bceeb9 (patch)
tree7964b84458f70f7e1544b9ade6d953040f53290a /rts/RtsMain.c
parentf6609b0ed031135b8402fd179b3370958698ecb2 (diff)
downloadhaskell-5200bdeb26c5ec98739b14b10fc8907296bceeb9.tar.gz
Replaced SEH handles with VEH handlers which should work uniformly across x86 and x64
Summary: On Windows, the default action for things like division by zero and segfaults is to pop up a Dr. Watson error reporting dialog if the exception is unhandled by the user code. This is a pain when we are SSHed into a Windows machine, or when we want to debug a problem with gdb (gdb will get a first and second chance to handle the exception, but if it doesn't the pop-up will show). veh_excn provides two macros, `BEGIN_CATCH` and `END_CATCH`, which will catch such exceptions in the entire process and die by printing a message and calling `stg_exit(1)`. Previously this code was handled using SEH (Structured Exception Handlers) however each compiler and platform have different ways of dealing with SEH. `MSVC` compilers have the keywords `__try`, `__catch` and `__except` to have the compiler generate the appropriate SEH handler code for you. `MinGW` compilers have no such keywords and require you to manually set the SEH Handlers, however because SEH is implemented differently in x86 and x64 the methods to use them in GCC differs. `x86`: SEH is based on the stack, the SEH handlers are available at `FS[0]`. On startup one would only need to add a new handler there. This has a number of issues such as hard to share handlers and it can be exploited. `x64`: In order to fix the issues with the way SEH worked in x86, on x64 SEH handlers are statically compiled and added to the .pdata section by the compiler. Instead of being thread global they can now be Image global since you have to specify the `RVA` of the region of code that the handlers govern. You can on x64 Dynamically allocate SEH handlers, but it seems that (based on experimentation and it's very under-documented) that the dynamic calls cannot override static SEH handlers in the .pdata section. Because of this and because GHC no longer needs to support < windows XP, the better alternative for handling errors would be using the in XP introduced VEH. The bonus is because VEH (Vectored Exception Handler) are a runtime construct the API is the same for both x86 and x64 (note that the Context object does contain CPU specific structures) and the calls are the same cross compilers. Which means this file can be simplified quite a bit. Using VEH also means we don't have to worry about the dynamic code generated by GHCi. Test Plan: Prior to this diff the tests for `derefnull` and `divbyzero` seem to have been disabled for windows. To reproduce the issue on x64: 1) open ghci 2) import GHC.Base 3) run: 1 `divInt` 0 which should lead to ghci crashing an a watson error box displaying. After applying the patch, run: make TEST="derefnull divbyzero" on both x64 and x86 builds of ghc to verify fix. Reviewers: simonmar, austin Reviewed By: austin Subscribers: thomie Differential Revision: https://phabricator.haskell.org/D691 GHC Trac Issues: #6079
Diffstat (limited to 'rts/RtsMain.c')
-rw-r--r--rts/RtsMain.c121
1 files changed, 49 insertions, 72 deletions
diff --git a/rts/RtsMain.c b/rts/RtsMain.c
index 3cf4f54cec..667c9e4ae2 100644
--- a/rts/RtsMain.c
+++ b/rts/RtsMain.c
@@ -8,6 +8,7 @@
#define COMPILING_RTS_MAIN
+#include "Excn.h"
#include "PosixSource.h"
#include "Rts.h"
#include "RtsAPI.h"
@@ -15,105 +16,81 @@
#include "RtsUtils.h"
#include "Prelude.h"
#include "Task.h"
-#if defined(mingw32_HOST_OS)
-#include "win32/seh_excn.h"
-#endif
#ifdef DEBUG
# include "Printer.h" /* for printing */
#endif
-#ifdef HAVE_WINDOWS_H
-# include <windows.h>
-#endif
+// Hack: we assume that we're building a batch-mode system unless
+// INTERPRETER is set
+
+#ifndef INTERPRETER /* Hack */
-/* Annoying global vars for passing parameters to real_main() below
- * This is to get around problem with Windows SEH, see hs_main(). */
-static int progargc;
-static char **progargv;
-static StgClosure *progmain_closure; /* This will be ZCMain_main_closure */
-static RtsConfig rtsconfig;
+// 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
-/* Hack: we assume that we're building a batch-mode system unless
- * INTERPRETER is set
- */
-#ifndef INTERPRETER /* Hack */
-static void real_main(void) GNUC3_ATTRIBUTE(__noreturn__);
-static void real_main(void)
+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;
- hs_init_ghc(&progargc, &progargv, rtsconfig);
-
- /* 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;
-
- the same for GranSim (we have only one instance of this code)
+ hs_init_ghc(&argc, &argv, rts_config);
- 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)
- */
+ // 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;
+ //
+ // the same for GranSim (we have only one instance of this code)
+ //
+ // 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)
- /* ToDo: want to start with a larger stack size */
+ // ToDo: want to start with a larger stack size
{
Capability *cap = rts_lock();
- rts_evalLazyIO(&cap,progmain_closure, NULL);
+ rts_evalLazyIO(&cap, main_closure, NULL);
status = rts_getSchedStatus(cap);
rts_unlock(cap);
}
- /* check the status of the entire Haskell computation */
+ // check the status of the entire Haskell computation
switch (status) {
case Killed:
- errorBelch("main thread exited (uncaught exception)");
- exit_status = EXIT_KILLED;
- break;
+ errorBelch("main thread exited (uncaught exception)");
+ exit_status = EXIT_KILLED;
+ break;
case Interrupted:
- errorBelch("interrupted");
- exit_status = EXIT_INTERRUPTED;
- break;
+ errorBelch("interrupted");
+ exit_status = EXIT_INTERRUPTED;
+ break;
case HeapExhausted:
- exit_status = EXIT_HEAPOVERFLOW;
- break;
+ exit_status = EXIT_HEAPOVERFLOW;
+ break;
case Success:
- exit_status = EXIT_SUCCESS;
- break;
+ exit_status = EXIT_SUCCESS;
+ break;
default:
- barf("main thread completed with invalid status");
+ barf("main thread completed with invalid status");
}
- shutdownHaskellAndExit(exit_status, 0 /* !fastExit */);
-}
-/* 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.
- */
-int hs_main (int argc, char *argv[], // program args
- StgClosure *main_closure, // closure for Main.main
- RtsConfig rts_config) // RTS configuration
-{
- /* We do this dance with argc and argv as otherwise the SEH exception
- stuff (the BEGIN/END CATCH below) on Windows gets confused */
- progargc = argc;
- progargv = argv;
- progmain_closure = main_closure;
- rtsconfig = rts_config;
+ END_WINDOWS_VEH_HANDLER
-#if defined(mingw32_HOST_OS) && defined(i386_HOST_ARCH)
- BEGIN_CATCH
-#endif
- real_main();
-#if defined(mingw32_HOST_OS) && defined(i386_HOST_ARCH)
- END_CATCH
-#endif
+ shutdownHaskellAndExit(exit_status, 0 /* !fastExit */);
+ // No code beyond this point. Dead code elimination will remove it
}
# endif /* BATCH_MODE */