diff options
Diffstat (limited to 'src/runtime/signal_windows.go')
-rw-r--r-- | src/runtime/signal_windows.go | 208 |
1 files changed, 159 insertions, 49 deletions
diff --git a/src/runtime/signal_windows.go b/src/runtime/signal_windows.go index 37986cd6b5..8e0e39cb26 100644 --- a/src/runtime/signal_windows.go +++ b/src/runtime/signal_windows.go @@ -10,22 +10,41 @@ import ( "unsafe" ) -func disableWER() { - // do not display Windows Error Reporting dialogue - const ( - SEM_FAILCRITICALERRORS = 0x0001 - SEM_NOGPFAULTERRORBOX = 0x0002 - SEM_NOALIGNMENTFAULTEXCEPT = 0x0004 - SEM_NOOPENFILEERRORBOX = 0x8000 - ) - errormode := uint32(stdcall1(_SetErrorMode, SEM_NOGPFAULTERRORBOX)) - stdcall1(_SetErrorMode, uintptr(errormode)|SEM_FAILCRITICALERRORS|SEM_NOGPFAULTERRORBOX|SEM_NOOPENFILEERRORBOX) +const ( + _SEM_FAILCRITICALERRORS = 0x0001 + _SEM_NOGPFAULTERRORBOX = 0x0002 + _SEM_NOOPENFILEERRORBOX = 0x8000 + + _WER_FAULT_REPORTING_NO_UI = 0x0020 +) + +func preventErrorDialogs() { + errormode := stdcall0(_GetErrorMode) + stdcall1(_SetErrorMode, errormode|_SEM_FAILCRITICALERRORS|_SEM_NOGPFAULTERRORBOX|_SEM_NOOPENFILEERRORBOX) + + // Disable WER fault reporting UI. + // Do this even if WER is disabled as a whole, + // as WER might be enabled later with setTraceback("wer") + // and we still want the fault reporting UI to be disabled if this happens. + var werflags uintptr + stdcall2(_WerGetFlags, currentProcess, uintptr(unsafe.Pointer(&werflags))) + stdcall1(_WerSetFlags, werflags|_WER_FAULT_REPORTING_NO_UI) +} + +// enableWER re-enables Windows error reporting without fault reporting UI. +func enableWER() { + // re-enable Windows Error Reporting + errormode := stdcall0(_GetErrorMode) + if errormode&_SEM_NOGPFAULTERRORBOX != 0 { + stdcall1(_SetErrorMode, errormode^_SEM_NOGPFAULTERRORBOX) + } } -// 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)) @@ -75,6 +94,7 @@ func isgoexception(info *exceptionrecord, r *context) bool { default: return false case _EXCEPTION_ACCESS_VIOLATION: + case _EXCEPTION_IN_PAGE_ERROR: case _EXCEPTION_INT_DIVIDE_BY_ZERO: case _EXCEPTION_INT_OVERFLOW: case _EXCEPTION_FLT_DENORMAL_OPERAND: @@ -88,13 +108,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 { @@ -165,8 +277,6 @@ func firstcontinuehandler(info *exceptionrecord, r *context, gp *g) int32 { return _EXCEPTION_CONTINUE_EXECUTION } -var testingWER bool - // lastcontinuehandler is reached, because runtime cannot handle // current exception. lastcontinuehandler will print crash info and exit. // @@ -180,9 +290,6 @@ func lastcontinuehandler(info *exceptionrecord, r *context, gp *g) int32 { // should not take responsibility of crashing the process. return _EXCEPTION_CONTINUE_SEARCH } - if testingWER { - return _EXCEPTION_CONTINUE_SEARCH - } // VEH is called before SEH, but arm64 MSVC DLLs use SEH to trap // illegal instructions during runtime initialization to determine @@ -214,7 +321,7 @@ func winthrow(info *exceptionrecord, r *context, gp *g) { // g0 stack bounds so we have room to print the traceback. If // this somehow overflows the stack, the OS will trap it. g0.stack.lo = 0 - g0.stackguard0 = g0.stack.lo + _StackGuard + g0.stackguard0 = g0.stack.lo + stackGuard g0.stackguard1 = g0.stackguard0 print("Exception ", hex(info.exceptioncode), " ", hex(info.exceptioninformation[0]), " ", hex(info.exceptioninformation[1]), " ", hex(r.ip()), "\n") @@ -239,7 +346,7 @@ func winthrow(info *exceptionrecord, r *context, gp *g) { } if docrash { - crash() + dieFromException(info, r) } exit(2) @@ -252,7 +359,7 @@ func sigpanic() { } switch gp.sig { - case _EXCEPTION_ACCESS_VIOLATION: + case _EXCEPTION_ACCESS_VIOLATION, _EXCEPTION_IN_PAGE_ERROR: if gp.sigcode1 < 0x1000 { panicmem() } @@ -282,19 +389,6 @@ func sigpanic() { throw("fault") } -var ( - badsignalmsg [100]byte - badsignallen int32 -) - -func setBadSignalMsg() { - const msg = "runtime: signal received on thread not created by Go.\n" - for i, c := range msg { - badsignalmsg[i] = byte(c) - badsignallen++ - } -} - // Following are not implemented. func initsig(preinit bool) { @@ -309,26 +403,42 @@ func sigdisable(sig uint32) { func sigignore(sig uint32) { } -func badsignal2() - -func raisebadsignal(sig uint32) { - badsignal2() -} - func signame(sig uint32) string { return "" } //go:nosplit func crash() { - // TODO: This routine should do whatever is needed - // to make the Windows program abort/crash as it - // would if Go was not intercepting signals. - // On Unix the routine would remove the custom signal - // handler and then raise a signal (like SIGABRT). - // Something like that should happen here. - // It's okay to leave this empty for now: if crash returns - // the ordinary exit-after-panic happens. + dieFromException(nil, nil) +} + +// dieFromException raises an exception that bypasses all exception handlers. +// This provides the expected exit status for the shell. +// +//go:nosplit +func dieFromException(info *exceptionrecord, r *context) { + if info == nil { + gp := getg() + if gp.sig != 0 { + // Try to reconstruct an exception record from + // the exception information stored in gp. + info = &exceptionrecord{ + exceptionaddress: gp.sigpc, + exceptioncode: gp.sig, + numberparameters: 2, + } + info.exceptioninformation[0] = gp.sigcode0 + info.exceptioninformation[1] = gp.sigcode1 + } else { + // By default, a failing Go application exits with exit code 2. + // Use this value when gp does not contain exception info. + info = &exceptionrecord{ + exceptioncode: 2, + } + } + } + const FAIL_FAST_GENERATE_EXCEPTION_ADDRESS = 0x1 + stdcall3(_RaiseFailFastException, uintptr(unsafe.Pointer(info)), uintptr(unsafe.Pointer(r)), FAIL_FAST_GENERATE_EXCEPTION_ADDRESS) } // gsignalStack is unused on Windows. |