summaryrefslogtreecommitdiffstats
path: root/src/runtime/signal_windows.go
diff options
context:
space:
mode:
Diffstat (limited to 'src/runtime/signal_windows.go')
-rw-r--r--src/runtime/signal_windows.go310
1 files changed, 310 insertions, 0 deletions
diff --git a/src/runtime/signal_windows.go b/src/runtime/signal_windows.go
new file mode 100644
index 0000000..3af2e39
--- /dev/null
+++ b/src/runtime/signal_windows.go
@@ -0,0 +1,310 @@
+// Copyright 2011 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package runtime
+
+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)
+}
+
+// in sys_windows_386.s and sys_windows_amd64.s
+func exceptiontramp()
+func firstcontinuetramp()
+func lastcontinuetramp()
+
+func initExceptionHandler() {
+ stdcall2(_AddVectoredExceptionHandler, 1, funcPC(exceptiontramp))
+ if _AddVectoredContinueHandler == nil || GOARCH == "386" {
+ // use SetUnhandledExceptionFilter for windows-386 or
+ // if VectoredContinueHandler is unavailable.
+ // note: SetUnhandledExceptionFilter handler won't be called, if debugging.
+ stdcall1(_SetUnhandledExceptionFilter, funcPC(lastcontinuetramp))
+ } else {
+ stdcall2(_AddVectoredContinueHandler, 1, funcPC(firstcontinuetramp))
+ stdcall2(_AddVectoredContinueHandler, 0, funcPC(lastcontinuetramp))
+ }
+}
+
+// isAbort returns true, if context r describes exception raised
+// by calling runtime.abort function.
+//
+//go:nosplit
+func isAbort(r *context) bool {
+ // In the case of an abort, the exception IP is one byte after
+ // the INT3 (this differs from UNIX OSes).
+ return isAbortPC(r.ip() - 1)
+}
+
+// isgoexception reports whether this exception should be translated
+// into a Go panic.
+//
+// It is nosplit to avoid growing the stack in case we're aborting
+// because of a stack overflow.
+//
+//go:nosplit
+func isgoexception(info *exceptionrecord, r *context) bool {
+ // Only handle exception if executing instructions in Go binary
+ // (not Windows library code).
+ // TODO(mwhudson): needs to loop to support shared libs
+ if r.ip() < firstmoduledata.text || firstmoduledata.etext < r.ip() {
+ return false
+ }
+
+ if isAbort(r) {
+ // Never turn abort into a panic.
+ return false
+ }
+
+ // Go will only handle some exceptions.
+ switch info.exceptioncode {
+ default:
+ return false
+ case _EXCEPTION_ACCESS_VIOLATION:
+ case _EXCEPTION_INT_DIVIDE_BY_ZERO:
+ case _EXCEPTION_INT_OVERFLOW:
+ case _EXCEPTION_FLT_DENORMAL_OPERAND:
+ case _EXCEPTION_FLT_DIVIDE_BY_ZERO:
+ case _EXCEPTION_FLT_INEXACT_RESULT:
+ case _EXCEPTION_FLT_OVERFLOW:
+ case _EXCEPTION_FLT_UNDERFLOW:
+ case _EXCEPTION_BREAKPOINT:
+ }
+ return true
+}
+
+// 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,
+//
+//go:nosplit
+func exceptionhandler(info *exceptionrecord, r *context, gp *g) int32 {
+ if !isgoexception(info, r) {
+ return _EXCEPTION_CONTINUE_SEARCH
+ }
+
+ // After this point, it is safe to grow the stack.
+
+ if gp.throwsplit {
+ // We can't safely sigpanic because it may grow the
+ // stack. Let it fall through.
+ return _EXCEPTION_CONTINUE_SEARCH
+ }
+
+ // Make it look like a call to the signal func.
+ // Have to pass arguments out of band since
+ // augmenting the stack frame would break
+ // the unwinding code.
+ gp.sig = info.exceptioncode
+ gp.sigcode0 = uintptr(info.exceptioninformation[0])
+ gp.sigcode1 = uintptr(info.exceptioninformation[1])
+ gp.sigpc = r.ip()
+
+ // Only push runtime·sigpanic if r.ip() != 0.
+ // If r.ip() == 0, probably panicked because of a
+ // call to a nil func. Not pushing that onto sp will
+ // make the trace look like a call to runtime·sigpanic instead.
+ // (Otherwise the trace will end at runtime·sigpanic and we
+ // won't get to see who faulted.)
+ // Also don't push a sigpanic frame if the faulting PC
+ // is the entry of asyncPreempt. In this case, we suspended
+ // the thread right between the fault and the exception handler
+ // starting to run, and we have pushed an asyncPreempt call.
+ // The exception is not from asyncPreempt, so not to push a
+ // sigpanic call to make it look like that. Instead, just
+ // overwrite the PC. (See issue #35773)
+ if r.ip() != 0 && r.ip() != funcPC(asyncPreempt) {
+ sp := unsafe.Pointer(r.sp())
+ sp = add(sp, ^(unsafe.Sizeof(uintptr(0)) - 1)) // sp--
+ r.set_sp(uintptr(sp))
+ switch GOARCH {
+ default:
+ panic("unsupported architecture")
+ case "386", "amd64":
+ *((*uintptr)(sp)) = r.ip()
+ case "arm":
+ *((*uintptr)(sp)) = r.lr()
+ r.set_lr(r.ip())
+ }
+ }
+ r.set_ip(funcPC(sigpanic))
+ return _EXCEPTION_CONTINUE_EXECUTION
+}
+
+// It seems Windows searches ContinueHandler's list even
+// if ExceptionHandler returns EXCEPTION_CONTINUE_EXECUTION.
+// firstcontinuehandler will stop that search,
+// if exceptionhandler did the same earlier.
+//
+// It is nosplit for the same reason as exceptionhandler.
+//
+//go:nosplit
+func firstcontinuehandler(info *exceptionrecord, r *context, gp *g) int32 {
+ if !isgoexception(info, r) {
+ return _EXCEPTION_CONTINUE_SEARCH
+ }
+ return _EXCEPTION_CONTINUE_EXECUTION
+}
+
+var testingWER bool
+
+// lastcontinuehandler is reached, because runtime cannot handle
+// current exception. lastcontinuehandler will print crash info and exit.
+//
+// It is nosplit for the same reason as exceptionhandler.
+//
+//go:nosplit
+func lastcontinuehandler(info *exceptionrecord, r *context, gp *g) int32 {
+ if islibrary || isarchive {
+ // Go DLL/archive has been loaded in a non-go program.
+ // If the exception does not originate from go, the go runtime
+ // should not take responsibility of crashing the process.
+ return _EXCEPTION_CONTINUE_SEARCH
+ }
+ if testingWER {
+ return _EXCEPTION_CONTINUE_SEARCH
+ }
+
+ _g_ := getg()
+
+ if panicking != 0 { // traceback already printed
+ exit(2)
+ }
+ panicking = 1
+
+ // In case we're handling a g0 stack overflow, blow away the
+ // g0 stack bounds so we have room to print the traceback. If
+ // this somehow overflows the stack, the OS will trap it.
+ _g_.stack.lo = 0
+ _g_.stackguard0 = _g_.stack.lo + _StackGuard
+ _g_.stackguard1 = _g_.stackguard0
+
+ print("Exception ", hex(info.exceptioncode), " ", hex(info.exceptioninformation[0]), " ", hex(info.exceptioninformation[1]), " ", hex(r.ip()), "\n")
+
+ print("PC=", hex(r.ip()), "\n")
+ if _g_.m.lockedg != 0 && _g_.m.ncgo > 0 && gp == _g_.m.g0 {
+ if iscgo {
+ print("signal arrived during external code execution\n")
+ }
+ gp = _g_.m.lockedg.ptr()
+ }
+ print("\n")
+
+ // TODO(jordanrh1): This may be needed for 386/AMD64 as well.
+ if GOARCH == "arm" {
+ _g_.m.throwing = 1
+ _g_.m.caughtsig.set(gp)
+ }
+
+ level, _, docrash := gotraceback()
+ if level > 0 {
+ tracebacktrap(r.ip(), r.sp(), r.lr(), gp)
+ tracebackothers(gp)
+ dumpregs(r)
+ }
+
+ if docrash {
+ crash()
+ }
+
+ exit(2)
+ return 0 // not reached
+}
+
+func sigpanic() {
+ g := getg()
+ if !canpanic(g) {
+ throw("unexpected signal during runtime execution")
+ }
+
+ switch g.sig {
+ case _EXCEPTION_ACCESS_VIOLATION:
+ if g.sigcode1 < 0x1000 {
+ panicmem()
+ }
+ if g.paniconfault {
+ panicmemAddr(g.sigcode1)
+ }
+ print("unexpected fault address ", hex(g.sigcode1), "\n")
+ throw("fault")
+ case _EXCEPTION_INT_DIVIDE_BY_ZERO:
+ panicdivide()
+ case _EXCEPTION_INT_OVERFLOW:
+ panicoverflow()
+ case _EXCEPTION_FLT_DENORMAL_OPERAND,
+ _EXCEPTION_FLT_DIVIDE_BY_ZERO,
+ _EXCEPTION_FLT_INEXACT_RESULT,
+ _EXCEPTION_FLT_OVERFLOW,
+ _EXCEPTION_FLT_UNDERFLOW:
+ panicfloat()
+ }
+ 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) {
+}
+
+func sigenable(sig uint32) {
+}
+
+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.
+}
+
+// gsignalStack is unused on Windows.
+type gsignalStack struct{}