Skip to content

Commit cf92e2f

Browse files
committed
runtime: add Windows vectored exception handler for recoverable panics
Register a vectored exception handler at startup so Windows hardware exceptions can be translated into Go panics. Access violations become nil pointer panics, and integer divide-by-zero exceptions become divide by zero panics, allowing defer/recover to handle them like ordinary runtime panics.
1 parent 60f99d4 commit cf92e2f

3 files changed

Lines changed: 59 additions & 0 deletions

File tree

compileopts/target.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -485,6 +485,8 @@ func defaultTarget(options *Options) (*TargetSpec, error) {
485485
"--no-insert-timestamp",
486486
"--no-dynamicbase",
487487
)
488+
spec.ExtraFiles = append(spec.ExtraFiles,
489+
"src/runtime/runtime_windows.c")
488490
case "wasm", "wasip1", "wasip2":
489491
return nil, fmt.Errorf("GOOS=%s but GOARCH is unset. Please set GOARCH to wasm", options.GOOS)
490492
default:

src/runtime/runtime_windows.c

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
//go:build none
2+
3+
// This file is included on Windows (despite the //go:build line above).
4+
5+
#include <windows.h>
6+
#include <stdint.h>
7+
8+
void tinygo_sigpanic_windows(int32_t exception_code);
9+
10+
static LONG WINAPI tinygo_exception_handler(EXCEPTION_POINTERS *info) {
11+
DWORD code = info->ExceptionRecord->ExceptionCode;
12+
switch (code) {
13+
case EXCEPTION_ACCESS_VIOLATION:
14+
case EXCEPTION_IN_PAGE_ERROR:
15+
case EXCEPTION_INT_DIVIDE_BY_ZERO:
16+
case EXCEPTION_INT_OVERFLOW:
17+
tinygo_sigpanic_windows((int32_t)code);
18+
// If runtimePanic triggers longjmp, we never reach here.
19+
// If it doesn't (no defer frame), it will abort and we also
20+
// never reach here.
21+
return EXCEPTION_CONTINUE_SEARCH;
22+
default:
23+
return EXCEPTION_CONTINUE_SEARCH;
24+
}
25+
}
26+
27+
void tinygo_init_exception_handler(void) {
28+
AddVectoredExceptionHandler(1, tinygo_exception_handler);
29+
}

src/runtime/runtime_windows.go

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,10 @@ func mainCRTStartup() int {
6060
_QueryPerformanceFrequency(&performanceFrequency)
6161
}
6262

63+
// Register vectored exception handler so that access violations and
64+
// divide-by-zero exceptions can be recovered with defer/recover.
65+
tinygo_init_exception_handler()
66+
6367
// Obtain the initial stack pointer right before calling the run() function.
6468
// The run function has been moved to a separate (non-inlined) function so
6569
// that the correct stack pointer is read.
@@ -294,3 +298,27 @@ func hardwareRand() (n uint64, ok bool) {
294298
//
295299
//export SystemFunction036
296300
func _RtlGenRandom(buf unsafe.Pointer, len int) bool
301+
302+
const (
303+
_EXCEPTION_ACCESS_VIOLATION = 0xC0000005
304+
_EXCEPTION_IN_PAGE_ERROR = 0xC0000006
305+
_EXCEPTION_INT_DIVIDE_BY_ZERO = 0xC0000094
306+
_EXCEPTION_INT_OVERFLOW = 0xC0000095
307+
)
308+
309+
//export tinygo_init_exception_handler
310+
func tinygo_init_exception_handler()
311+
312+
//export tinygo_sigpanic_windows
313+
func tinygo_sigpanic_windows(exceptionCode int32) {
314+
switch uint32(exceptionCode) {
315+
case _EXCEPTION_ACCESS_VIOLATION, _EXCEPTION_IN_PAGE_ERROR:
316+
runtimePanic("nil pointer dereference")
317+
case _EXCEPTION_INT_DIVIDE_BY_ZERO:
318+
runtimePanic("divide by zero")
319+
case _EXCEPTION_INT_OVERFLOW:
320+
runtimePanic("integer overflow")
321+
default:
322+
runtimePanic("unknown exception")
323+
}
324+
}

0 commit comments

Comments
 (0)