Black Lives Matter. Support the Equal Justice Initiative.

Source file src/runtime/signal_windows.go

Documentation: runtime

     1  // Copyright 2011 The Go Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  package runtime
     6  
     7  import (
     8  	"runtime/internal/sys"
     9  	"unsafe"
    10  )
    11  
    12  func disableWER() {
    13  	// do not display Windows Error Reporting dialogue
    14  	const (
    15  		SEM_FAILCRITICALERRORS     = 0x0001
    16  		SEM_NOGPFAULTERRORBOX      = 0x0002
    17  		SEM_NOALIGNMENTFAULTEXCEPT = 0x0004
    18  		SEM_NOOPENFILEERRORBOX     = 0x8000
    19  	)
    20  	errormode := uint32(stdcall1(_SetErrorMode, SEM_NOGPFAULTERRORBOX))
    21  	stdcall1(_SetErrorMode, uintptr(errormode)|SEM_FAILCRITICALERRORS|SEM_NOGPFAULTERRORBOX|SEM_NOOPENFILEERRORBOX)
    22  }
    23  
    24  // in sys_windows_386.s and sys_windows_amd64.s
    25  func exceptiontramp()
    26  func firstcontinuetramp()
    27  func lastcontinuetramp()
    28  
    29  func initExceptionHandler() {
    30  	stdcall2(_AddVectoredExceptionHandler, 1, funcPC(exceptiontramp))
    31  	if _AddVectoredContinueHandler == nil || GOARCH == "386" {
    32  		// use SetUnhandledExceptionFilter for windows-386 or
    33  		// if VectoredContinueHandler is unavailable.
    34  		// note: SetUnhandledExceptionFilter handler won't be called, if debugging.
    35  		stdcall1(_SetUnhandledExceptionFilter, funcPC(lastcontinuetramp))
    36  	} else {
    37  		stdcall2(_AddVectoredContinueHandler, 1, funcPC(firstcontinuetramp))
    38  		stdcall2(_AddVectoredContinueHandler, 0, funcPC(lastcontinuetramp))
    39  	}
    40  }
    41  
    42  // isAbort returns true, if context r describes exception raised
    43  // by calling runtime.abort function.
    44  //
    45  //go:nosplit
    46  func isAbort(r *context) bool {
    47  	pc := r.ip()
    48  	if GOARCH == "386" || GOARCH == "amd64" || GOARCH == "arm" {
    49  		// In the case of an abort, the exception IP is one byte after
    50  		// the INT3 (this differs from UNIX OSes). Note that on ARM,
    51  		// this means that the exception IP is no longer aligned.
    52  		pc--
    53  	}
    54  	return isAbortPC(pc)
    55  }
    56  
    57  // isgoexception reports whether this exception should be translated
    58  // into a Go panic or throw.
    59  //
    60  // It is nosplit to avoid growing the stack in case we're aborting
    61  // because of a stack overflow.
    62  //
    63  //go:nosplit
    64  func isgoexception(info *exceptionrecord, r *context) bool {
    65  	// Only handle exception if executing instructions in Go binary
    66  	// (not Windows library code).
    67  	// TODO(mwhudson): needs to loop to support shared libs
    68  	if r.ip() < firstmoduledata.text || firstmoduledata.etext < r.ip() {
    69  		return false
    70  	}
    71  
    72  	// Go will only handle some exceptions.
    73  	switch info.exceptioncode {
    74  	default:
    75  		return false
    76  	case _EXCEPTION_ACCESS_VIOLATION:
    77  	case _EXCEPTION_INT_DIVIDE_BY_ZERO:
    78  	case _EXCEPTION_INT_OVERFLOW:
    79  	case _EXCEPTION_FLT_DENORMAL_OPERAND:
    80  	case _EXCEPTION_FLT_DIVIDE_BY_ZERO:
    81  	case _EXCEPTION_FLT_INEXACT_RESULT:
    82  	case _EXCEPTION_FLT_OVERFLOW:
    83  	case _EXCEPTION_FLT_UNDERFLOW:
    84  	case _EXCEPTION_BREAKPOINT:
    85  	case _EXCEPTION_ILLEGAL_INSTRUCTION: // breakpoint arrives this way on arm64
    86  	}
    87  	return true
    88  }
    89  
    90  // Called by sigtramp from Windows VEH handler.
    91  // Return value signals whether the exception has been handled (EXCEPTION_CONTINUE_EXECUTION)
    92  // or should be made available to other handlers in the chain (EXCEPTION_CONTINUE_SEARCH).
    93  //
    94  // This is the first entry into Go code for exception handling. This
    95  // is nosplit to avoid growing the stack until we've checked for
    96  // _EXCEPTION_BREAKPOINT, which is raised if we overflow the g0 stack,
    97  //
    98  //go:nosplit
    99  func exceptionhandler(info *exceptionrecord, r *context, gp *g) int32 {
   100  	if !isgoexception(info, r) {
   101  		return _EXCEPTION_CONTINUE_SEARCH
   102  	}
   103  
   104  	if gp.throwsplit || isAbort(r) {
   105  		// We can't safely sigpanic because it may grow the stack.
   106  		// Or this is a call to abort.
   107  		// Don't go through any more of the Windows handler chain.
   108  		// Crash now.
   109  		winthrow(info, r, gp)
   110  	}
   111  
   112  	// After this point, it is safe to grow the stack.
   113  
   114  	// Make it look like a call to the signal func.
   115  	// Have to pass arguments out of band since
   116  	// augmenting the stack frame would break
   117  	// the unwinding code.
   118  	gp.sig = info.exceptioncode
   119  	gp.sigcode0 = info.exceptioninformation[0]
   120  	gp.sigcode1 = info.exceptioninformation[1]
   121  	gp.sigpc = r.ip()
   122  
   123  	// Only push runtime·sigpanic if r.ip() != 0.
   124  	// If r.ip() == 0, probably panicked because of a
   125  	// call to a nil func. Not pushing that onto sp will
   126  	// make the trace look like a call to runtime·sigpanic instead.
   127  	// (Otherwise the trace will end at runtime·sigpanic and we
   128  	// won't get to see who faulted.)
   129  	// Also don't push a sigpanic frame if the faulting PC
   130  	// is the entry of asyncPreempt. In this case, we suspended
   131  	// the thread right between the fault and the exception handler
   132  	// starting to run, and we have pushed an asyncPreempt call.
   133  	// The exception is not from asyncPreempt, so not to push a
   134  	// sigpanic call to make it look like that. Instead, just
   135  	// overwrite the PC. (See issue #35773)
   136  	if r.ip() != 0 && r.ip() != funcPC(asyncPreempt) {
   137  		sp := unsafe.Pointer(r.sp())
   138  		delta := uintptr(sys.StackAlign)
   139  		sp = add(sp, -delta)
   140  		r.set_sp(uintptr(sp))
   141  		if usesLR {
   142  			*((*uintptr)(sp)) = r.lr()
   143  			r.set_lr(r.ip())
   144  		} else {
   145  			*((*uintptr)(sp)) = r.ip()
   146  		}
   147  	}
   148  	r.set_ip(funcPC(sigpanic0))
   149  	return _EXCEPTION_CONTINUE_EXECUTION
   150  }
   151  
   152  // It seems Windows searches ContinueHandler's list even
   153  // if ExceptionHandler returns EXCEPTION_CONTINUE_EXECUTION.
   154  // firstcontinuehandler will stop that search,
   155  // if exceptionhandler did the same earlier.
   156  //
   157  // It is nosplit for the same reason as exceptionhandler.
   158  //
   159  //go:nosplit
   160  func firstcontinuehandler(info *exceptionrecord, r *context, gp *g) int32 {
   161  	if !isgoexception(info, r) {
   162  		return _EXCEPTION_CONTINUE_SEARCH
   163  	}
   164  	return _EXCEPTION_CONTINUE_EXECUTION
   165  }
   166  
   167  var testingWER bool
   168  
   169  // lastcontinuehandler is reached, because runtime cannot handle
   170  // current exception. lastcontinuehandler will print crash info and exit.
   171  //
   172  // It is nosplit for the same reason as exceptionhandler.
   173  //
   174  //go:nosplit
   175  func lastcontinuehandler(info *exceptionrecord, r *context, gp *g) int32 {
   176  	if islibrary || isarchive {
   177  		// Go DLL/archive has been loaded in a non-go program.
   178  		// If the exception does not originate from go, the go runtime
   179  		// should not take responsibility of crashing the process.
   180  		return _EXCEPTION_CONTINUE_SEARCH
   181  	}
   182  	if testingWER {
   183  		return _EXCEPTION_CONTINUE_SEARCH
   184  	}
   185  
   186  	// VEH is called before SEH, but arm64 MSVC DLLs use SEH to trap
   187  	// illegal instructions during runtime initialization to determine
   188  	// CPU features, so if we make it to the last handler and we're
   189  	// arm64 and it's an illegal instruction and this is coming from
   190  	// non-Go code, then assume it's this runtime probing happen, and
   191  	// pass that onward to SEH.
   192  	if GOARCH == "arm64" && info.exceptioncode == _EXCEPTION_ILLEGAL_INSTRUCTION &&
   193  		(r.ip() < firstmoduledata.text || firstmoduledata.etext < r.ip()) {
   194  		return _EXCEPTION_CONTINUE_SEARCH
   195  	}
   196  
   197  	winthrow(info, r, gp)
   198  	return 0 // not reached
   199  }
   200  
   201  //go:nosplit
   202  func winthrow(info *exceptionrecord, r *context, gp *g) {
   203  	_g_ := getg()
   204  
   205  	if panicking != 0 { // traceback already printed
   206  		exit(2)
   207  	}
   208  	panicking = 1
   209  
   210  	// In case we're handling a g0 stack overflow, blow away the
   211  	// g0 stack bounds so we have room to print the traceback. If
   212  	// this somehow overflows the stack, the OS will trap it.
   213  	_g_.stack.lo = 0
   214  	_g_.stackguard0 = _g_.stack.lo + _StackGuard
   215  	_g_.stackguard1 = _g_.stackguard0
   216  
   217  	print("Exception ", hex(info.exceptioncode), " ", hex(info.exceptioninformation[0]), " ", hex(info.exceptioninformation[1]), " ", hex(r.ip()), "\n")
   218  
   219  	print("PC=", hex(r.ip()), "\n")
   220  	if _g_.m.lockedg != 0 && _g_.m.ncgo > 0 && gp == _g_.m.g0 {
   221  		if iscgo {
   222  			print("signal arrived during external code execution\n")
   223  		}
   224  		gp = _g_.m.lockedg.ptr()
   225  	}
   226  	print("\n")
   227  
   228  	_g_.m.throwing = 1
   229  	_g_.m.caughtsig.set(gp)
   230  
   231  	level, _, docrash := gotraceback()
   232  	if level > 0 {
   233  		tracebacktrap(r.ip(), r.sp(), r.lr(), gp)
   234  		tracebackothers(gp)
   235  		dumpregs(r)
   236  	}
   237  
   238  	if docrash {
   239  		crash()
   240  	}
   241  
   242  	exit(2)
   243  }
   244  
   245  func sigpanic() {
   246  	g := getg()
   247  	if !canpanic(g) {
   248  		throw("unexpected signal during runtime execution")
   249  	}
   250  
   251  	switch g.sig {
   252  	case _EXCEPTION_ACCESS_VIOLATION:
   253  		if g.sigcode1 < 0x1000 {
   254  			panicmem()
   255  		}
   256  		if g.paniconfault {
   257  			panicmemAddr(g.sigcode1)
   258  		}
   259  		print("unexpected fault address ", hex(g.sigcode1), "\n")
   260  		throw("fault")
   261  	case _EXCEPTION_INT_DIVIDE_BY_ZERO:
   262  		panicdivide()
   263  	case _EXCEPTION_INT_OVERFLOW:
   264  		panicoverflow()
   265  	case _EXCEPTION_FLT_DENORMAL_OPERAND,
   266  		_EXCEPTION_FLT_DIVIDE_BY_ZERO,
   267  		_EXCEPTION_FLT_INEXACT_RESULT,
   268  		_EXCEPTION_FLT_OVERFLOW,
   269  		_EXCEPTION_FLT_UNDERFLOW:
   270  		panicfloat()
   271  	}
   272  	throw("fault")
   273  }
   274  
   275  var (
   276  	badsignalmsg [100]byte
   277  	badsignallen int32
   278  )
   279  
   280  func setBadSignalMsg() {
   281  	const msg = "runtime: signal received on thread not created by Go.\n"
   282  	for i, c := range msg {
   283  		badsignalmsg[i] = byte(c)
   284  		badsignallen++
   285  	}
   286  }
   287  
   288  // Following are not implemented.
   289  
   290  func initsig(preinit bool) {
   291  }
   292  
   293  func sigenable(sig uint32) {
   294  }
   295  
   296  func sigdisable(sig uint32) {
   297  }
   298  
   299  func sigignore(sig uint32) {
   300  }
   301  
   302  func badsignal2()
   303  
   304  func raisebadsignal(sig uint32) {
   305  	badsignal2()
   306  }
   307  
   308  func signame(sig uint32) string {
   309  	return ""
   310  }
   311  
   312  //go:nosplit
   313  func crash() {
   314  	// TODO: This routine should do whatever is needed
   315  	// to make the Windows program abort/crash as it
   316  	// would if Go was not intercepting signals.
   317  	// On Unix the routine would remove the custom signal
   318  	// handler and then raise a signal (like SIGABRT).
   319  	// Something like that should happen here.
   320  	// It's okay to leave this empty for now: if crash returns
   321  	// the ordinary exit-after-panic happens.
   322  }
   323  
   324  // gsignalStack is unused on Windows.
   325  type gsignalStack struct{}
   326  

View as plain text