summary refs log tree commit diff stats
path: root/lib/pure/segfaults.nim
diff options
context:
space:
mode:
Diffstat (limited to 'lib/pure/segfaults.nim')
-rw-r--r--lib/pure/segfaults.nim88
1 files changed, 88 insertions, 0 deletions
diff --git a/lib/pure/segfaults.nim b/lib/pure/segfaults.nim
new file mode 100644
index 000000000..65b059e86
--- /dev/null
+++ b/lib/pure/segfaults.nim
@@ -0,0 +1,88 @@
+#
+#
+#            Nim's Runtime Library
+#        (c) Copyright 2017 Andreas Rumpf
+#
+#    See the file "copying.txt", included in this
+#    distribution, for details about the copyright.
+#
+
+## This modules registers a signal handler that turns access violations /
+## segfaults into a ``NilAccessDefect`` exception. To be able to catch
+## a NilAccessDefect all you have to do is to import this module.
+##
+## Tested on these OSes: Linux, Windows, OSX
+
+# xxx possibly broken on arm64, see bug #17178
+
+{.used.}
+
+# do allocate memory upfront:
+var se: ref NilAccessDefect
+new(se)
+se.name = "NilAccessDefect"
+se.msg = "Could not access value because it is nil."
+
+when defined(windows):
+  include "../system/ansi_c"
+
+  import std/winlean
+
+  const
+    EXCEPTION_ACCESS_VIOLATION = DWORD(0xc0000005'i32)
+    EXCEPTION_CONTINUE_SEARCH = LONG(0)
+
+  type
+    PEXCEPTION_RECORD = ptr object
+      exceptionCode: DWORD # other fields left out
+
+    PEXCEPTION_POINTERS = ptr object
+      exceptionRecord: PEXCEPTION_RECORD
+      contextRecord: pointer
+
+    VectoredHandler = proc (p: PEXCEPTION_POINTERS): LONG {.stdcall.}
+  proc addVectoredExceptionHandler(firstHandler: ULONG,
+                                   handler: VectoredHandler): pointer {.
+    importc: "AddVectoredExceptionHandler", stdcall, dynlib: "kernel32.dll".}
+
+  {.push stackTrace: off.}
+  proc segfaultHandler(p: PEXCEPTION_POINTERS): LONG {.stdcall.} =
+    if p.exceptionRecord.exceptionCode == EXCEPTION_ACCESS_VIOLATION:
+      {.gcsafe.}:
+        raise se
+    else:
+      result = EXCEPTION_CONTINUE_SEARCH
+  {.pop.}
+
+  discard addVectoredExceptionHandler(0, segfaultHandler)
+
+  when false:
+    {.push stackTrace: off.}
+    proc segfaultHandler(sig: cint) {.noconv.} =
+      {.gcsafe.}:
+        rawRaise se
+    {.pop.}
+    c_signal(SIGSEGV, segfaultHandler)
+
+else:
+  import std/posix
+
+  var sa: Sigaction
+
+  var SEGV_MAPERR {.importc, header: "<signal.h>".}: cint
+
+  {.push stackTrace: off.}
+  proc segfaultHandler(sig: cint, y: ptr SigInfo, z: pointer) {.noconv.} =
+    if y.si_code == SEGV_MAPERR:
+      {.gcsafe.}:
+        raise se
+    else:
+      quit(1)
+  {.pop.}
+
+  discard sigemptyset(sa.sa_mask)
+
+  sa.sa_sigaction = segfaultHandler
+  sa.sa_flags = SA_SIGINFO or SA_NODEFER
+
+  discard sigaction(SIGSEGV, sa)