diff options
Diffstat (limited to 'src/runtime/signal_windows.go')
| -rw-r--r-- | src/runtime/signal_windows.go | 101 |
1 files changed, 97 insertions, 4 deletions
diff --git a/src/runtime/signal_windows.go b/src/runtime/signal_windows.go index 37986cd6b5..0686be4635 100644 --- a/src/runtime/signal_windows.go +++ b/src/runtime/signal_windows.go @@ -22,10 +22,11 @@ func disableWER() { stdcall1(_SetErrorMode, uintptr(errormode)|SEM_FAILCRITICALERRORS|SEM_NOGPFAULTERRORBOX|SEM_NOOPENFILEERRORBOX) } -// in sys_windows_386.s and sys_windows_amd64.s +// in sys_windows_386.s, sys_windows_amd64.s, sys_windows_arm.s, and sys_windows_arm64.s func exceptiontramp() func firstcontinuetramp() func lastcontinuetramp() +func sigresume() func initExceptionHandler() { stdcall2(_AddVectoredExceptionHandler, 1, abi.FuncPCABI0(exceptiontramp)) @@ -88,13 +89,105 @@ func isgoexception(info *exceptionrecord, r *context) bool { return true } +const ( + callbackVEH = iota + callbackFirstVCH + callbackLastVCH +) + +// sigFetchGSafe is like getg() but without panicking +// when TLS is not set. +// Only implemented on windows/386, which is the only +// arch that loads TLS when calling getg(). Others +// use a dedicated register. +func sigFetchGSafe() *g + +func sigFetchG() *g { + if GOARCH == "386" { + return sigFetchGSafe() + } + return getg() +} + +// sigtrampgo is called from the exception handler function, sigtramp, +// written in assembly code. +// Return EXCEPTION_CONTINUE_EXECUTION if the exception is handled, +// else return EXCEPTION_CONTINUE_SEARCH. +// +// It is nosplit for the same reason as exceptionhandler. +// +//go:nosplit +func sigtrampgo(ep *exceptionpointers, kind int) int32 { + gp := sigFetchG() + if gp == nil { + return _EXCEPTION_CONTINUE_SEARCH + } + + var fn func(info *exceptionrecord, r *context, gp *g) int32 + switch kind { + case callbackVEH: + fn = exceptionhandler + case callbackFirstVCH: + fn = firstcontinuehandler + case callbackLastVCH: + fn = lastcontinuehandler + default: + throw("unknown sigtramp callback") + } + + // Check if we are running on g0 stack, and if we are, + // call fn directly instead of creating the closure. + // for the systemstack argument. + // + // A closure can't be marked as nosplit, so it might + // call morestack if we are at the g0 stack limit. + // If that happens, the runtime will call abort + // and end up in sigtrampgo again. + // TODO: revisit this workaround if/when closures + // can be compiled as nosplit. + // + // Note that this scenario should only occur on + // TestG0StackOverflow. Any other occurrence should + // be treated as a bug. + var ret int32 + if gp != gp.m.g0 { + systemstack(func() { + ret = fn(ep.record, ep.context, gp) + }) + } else { + ret = fn(ep.record, ep.context, gp) + } + if ret == _EXCEPTION_CONTINUE_SEARCH { + return ret + } + + // Check if we need to set up the control flow guard workaround. + // On Windows, the stack pointer in the context must lie within + // system stack limits when we resume from exception. + // Store the resume SP and PC in alternate registers + // and return to sigresume on the g0 stack. + // sigresume makes no use of the stack at all, + // loading SP from RX and jumping to RY, being RX and RY two scratch registers. + // Note that blindly smashing RX and RY is only safe because we know sigpanic + // will not actually return to the original frame, so the registers + // are effectively dead. But this does mean we can't use the + // same mechanism for async preemption. + if ep.context.ip() == abi.FuncPCABI0(sigresume) { + // sigresume has already been set up by a previous exception. + return ret + } + prepareContextForSigResume(ep.context) + ep.context.set_sp(gp.m.g0.sched.sp) + ep.context.set_ip(abi.FuncPCABI0(sigresume)) + return ret +} + // Called by sigtramp from Windows VEH handler. // Return value signals whether the exception has been handled (EXCEPTION_CONTINUE_EXECUTION) // or should be made available to other handlers in the chain (EXCEPTION_CONTINUE_SEARCH). // -// This is the first entry into Go code for exception handling. This -// is nosplit to avoid growing the stack until we've checked for -// _EXCEPTION_BREAKPOINT, which is raised if we overflow the g0 stack, +// This is nosplit to avoid growing the stack until we've checked for +// _EXCEPTION_BREAKPOINT, which is raised by abort() if we overflow the g0 stack. // //go:nosplit func exceptionhandler(info *exceptionrecord, r *context, gp *g) int32 { |
