Commit 53ca4118bd

emekoi <emekankurumeh@outlook.com>
2019-07-03 17:16:52
added segfault handler support for windows
1 parent a1359ac
Changed files (4)
std/os/windows/bits.zig
@@ -539,3 +539,25 @@ pub const FORMAT_MESSAGE_FROM_STRING = 0x00000400;
 pub const FORMAT_MESSAGE_FROM_SYSTEM = 0x00001000;
 pub const FORMAT_MESSAGE_IGNORE_INSERTS = 0x00000200;
 pub const FORMAT_MESSAGE_MAX_WIDTH_MASK = 0x000000FF;
+
+pub const EXCEPTION_DATATYPE_MISALIGNMENT = 0x80000002;
+pub const EXCEPTION_ACCESS_VIOLATION = 0xc0000005;
+pub const EXCEPTION_ILLEGAL_INSTRUCTION = 0xc000001d;
+pub const EXCEPTION_STACK_OVERFLOW = 0xc00000fd;
+pub const EXCEPTION_CONTINUE_SEARCH = 0;
+
+pub const EXCEPTION_RECORD = extern struct {
+    ExceptionCode: u32,
+    ExceptionFlags: u32,
+    ExceptionRecord: *EXCEPTION_RECORD,
+    ExceptionAddress: *c_void,
+    NumberParameters: u32,
+    ExceptionInformation: [15]usize,
+};
+
+pub const EXCEPTION_POINTERS = extern struct {
+    ExceptionRecord: *EXCEPTION_RECORD,
+    ContextRecord: *c_void,
+};
+
+pub const VECTORED_EXCEPTION_HANDLER = stdcallcc fn (ExceptionInfo: *EXCEPTION_POINTERS) c_long;
std/os/windows/kernel32.zig
@@ -1,5 +1,7 @@
 usingnamespace @import("bits.zig");
 
+pub extern "kernel32" stdcallcc fn AddVectoredExceptionHandler(First: c_ulong, Handler: ?VECTORED_EXCEPTION_HANDLER) ?*c_void;
+
 pub extern "kernel32" stdcallcc fn CancelIoEx(hFile: HANDLE, lpOverlapped: LPOVERLAPPED) BOOL;
 
 pub extern "kernel32" stdcallcc fn CloseHandle(hObject: HANDLE) BOOL;
std/special/start.zig
@@ -24,6 +24,16 @@ comptime {
     }
 }
 
+fn enableSegfaultHandler() void {
+    const enable_segfault_handler: bool = if (@hasDecl(root, "enable_segfault_handler"))
+        root.enable_segfault_handler
+    else
+        std.debug.runtime_safety and std.debug.have_segfault_handling_support;
+    if (enable_segfault_handler) {
+        std.debug.attachSegfaultHandler();
+    }
+}
+
 extern fn wasm_freestanding_start() void {
     _ = callMain();
 }
@@ -61,6 +71,9 @@ extern fn WinMainCRTStartup() noreturn {
     if (!builtin.single_threaded) {
         _ = @import("start_windows_tls.zig");
     }
+
+    enableSegfaultHandler();
+    
     std.os.windows.kernel32.ExitProcess(callMain());
 }
 
@@ -100,13 +113,7 @@ inline fn callMainWithArgs(argc: usize, argv: [*][*]u8, envp: [][*]u8) u8 {
     std.os.argv = argv[0..argc];
     std.os.environ = envp;
 
-    const enable_segfault_handler: bool = if (@hasDecl(root, "enable_segfault_handler"))
-        root.enable_segfault_handler
-    else
-        std.debug.runtime_safety and std.debug.have_segfault_handling_support;
-    if (enable_segfault_handler) {
-        std.debug.attachSegfaultHandler();
-    }
+    enableSegfaultHandler();
 
     return callMain();
 }
std/debug.zig
@@ -2311,23 +2311,31 @@ fn getDebugInfoAllocator() *mem.Allocator {
 }
 
 /// Whether or not the current target can print useful debug information when a segfault occurs.
-pub const have_segfault_handling_support = builtin.arch == .x86_64 and builtin.os == .linux;
+pub const have_segfault_handling_support = (builtin.arch == .x86_64 and builtin.os == .linux) or builtin.os == .windows;
 
 /// Attaches a global SIGSEGV handler which calls @panic("segmentation fault");
 pub fn attachSegfaultHandler() void {
     if (!have_segfault_handling_support) {
         @compileError("segfault handler not supported for this target");
     }
-    var act = os.Sigaction{
-        .sigaction = handleSegfault,
-        .mask = os.empty_sigset,
-        .flags = (os.SA_SIGINFO | os.SA_RESTART | os.SA_RESETHAND),
-    };
+    switch (builtin.os) {
+        .linux => {
+            var act = os.Sigaction{
+                .sigaction = handleSegfaultLinux,
+                .mask = os.empty_sigset,
+                .flags = (os.SA_SIGINFO | os.SA_RESTART | os.SA_RESETHAND),
+            };
 
-    os.sigaction(os.SIGSEGV, &act, null);
+            os.sigaction(os.SIGSEGV, &act, null);
+        },
+        .windows => {
+            _ = windows.kernel32.AddVectoredExceptionHandler(0, handleSegfaultWindows);
+        },
+        else => unreachable,
+    }
 }
 
-extern fn handleSegfault(sig: i32, info: *const os.siginfo_t, ctx_ptr: *const c_void) noreturn {
+extern fn handleSegfaultLinux(sig: i32, info: *const os.siginfo_t, ctx_ptr: *const c_void) noreturn {
     // Reset to the default handler so that if a segfault happens in this handler it will crash
     // the process. Also when this handler returns, the original instruction will be repeated
     // and the resulting segfault will crash the process rather than continually dump stack traces.
@@ -2350,3 +2358,14 @@ extern fn handleSegfault(sig: i32, info: *const os.siginfo_t, ctx_ptr: *const c_
     // the segfault. So we simply abort here.
     os.abort();
 }
+
+stdcallcc fn handleSegfaultWindows(info: *windows.EXCEPTION_POINTERS) c_long {
+    const exception_address = @ptrToInt(info.ExceptionRecord.ExceptionAddress);
+    switch (info.ExceptionRecord.ExceptionCode) {
+        windows.EXCEPTION_DATATYPE_MISALIGNMENT => panicExtra(null, exception_address, "Unaligned Memory Access"),
+        windows.EXCEPTION_ACCESS_VIOLATION => panicExtra(null, exception_address, "Segmentation fault at address 0x{x}", info.ExceptionRecord.ExceptionInformation[1]),
+        windows.EXCEPTION_ILLEGAL_INSTRUCTION => panicExtra(null, exception_address, "Illegal Instruction"),
+        windows.EXCEPTION_STACK_OVERFLOW => panicExtra(null, exception_address, "Stack Overflow"),
+        else => return windows.EXCEPTION_CONTINUE_SEARCH,
+    }
+}