github.com/twelsh-aw/go/src@v0.0.0-20230516233729-a56fe86a7c81/runtime/signal_windows.go (about)

     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  	"internal/abi"
     9  	"runtime/internal/sys"
    10  	"unsafe"
    11  )
    12  
    13  const (
    14  	_SEM_FAILCRITICALERRORS = 0x0001
    15  	_SEM_NOGPFAULTERRORBOX  = 0x0002
    16  	_SEM_NOOPENFILEERRORBOX = 0x8000
    17  
    18  	_WER_FAULT_REPORTING_NO_UI = 0x0020
    19  )
    20  
    21  func preventErrorDialogs() {
    22  	errormode := stdcall0(_GetErrorMode)
    23  	stdcall1(_SetErrorMode, errormode|_SEM_FAILCRITICALERRORS|_SEM_NOGPFAULTERRORBOX|_SEM_NOOPENFILEERRORBOX)
    24  
    25  	// Disable WER fault reporting UI.
    26  	// Do this even if WER is disabled as a whole,
    27  	// as WER might be enabled later with setTraceback("wer")
    28  	// and we still want the fault reporting UI to be disabled if this happens.
    29  	var werflags uintptr
    30  	stdcall2(_WerGetFlags, currentProcess, uintptr(unsafe.Pointer(&werflags)))
    31  	stdcall1(_WerSetFlags, werflags|_WER_FAULT_REPORTING_NO_UI)
    32  }
    33  
    34  // enableWER re-enables Windows error reporting without fault reporting UI.
    35  func enableWER() {
    36  	// re-enable Windows Error Reporting
    37  	errormode := stdcall0(_GetErrorMode)
    38  	if errormode&_SEM_NOGPFAULTERRORBOX != 0 {
    39  		stdcall1(_SetErrorMode, errormode^_SEM_NOGPFAULTERRORBOX)
    40  	}
    41  }
    42  
    43  // in sys_windows_386.s, sys_windows_amd64.s, sys_windows_arm.s, and sys_windows_arm64.s
    44  func exceptiontramp()
    45  func firstcontinuetramp()
    46  func lastcontinuetramp()
    47  func sigresume()
    48  
    49  func initExceptionHandler() {
    50  	stdcall2(_AddVectoredExceptionHandler, 1, abi.FuncPCABI0(exceptiontramp))
    51  	if _AddVectoredContinueHandler == nil || GOARCH == "386" {
    52  		// use SetUnhandledExceptionFilter for windows-386 or
    53  		// if VectoredContinueHandler is unavailable.
    54  		// note: SetUnhandledExceptionFilter handler won't be called, if debugging.
    55  		stdcall1(_SetUnhandledExceptionFilter, abi.FuncPCABI0(lastcontinuetramp))
    56  	} else {
    57  		stdcall2(_AddVectoredContinueHandler, 1, abi.FuncPCABI0(firstcontinuetramp))
    58  		stdcall2(_AddVectoredContinueHandler, 0, abi.FuncPCABI0(lastcontinuetramp))
    59  	}
    60  }
    61  
    62  // isAbort returns true, if context r describes exception raised
    63  // by calling runtime.abort function.
    64  //
    65  //go:nosplit
    66  func isAbort(r *context) bool {
    67  	pc := r.ip()
    68  	if GOARCH == "386" || GOARCH == "amd64" || GOARCH == "arm" {
    69  		// In the case of an abort, the exception IP is one byte after
    70  		// the INT3 (this differs from UNIX OSes). Note that on ARM,
    71  		// this means that the exception IP is no longer aligned.
    72  		pc--
    73  	}
    74  	return isAbortPC(pc)
    75  }
    76  
    77  // isgoexception reports whether this exception should be translated
    78  // into a Go panic or throw.
    79  //
    80  // It is nosplit to avoid growing the stack in case we're aborting
    81  // because of a stack overflow.
    82  //
    83  //go:nosplit
    84  func isgoexception(info *exceptionrecord, r *context) bool {
    85  	// Only handle exception if executing instructions in Go binary
    86  	// (not Windows library code).
    87  	// TODO(mwhudson): needs to loop to support shared libs
    88  	if r.ip() < firstmoduledata.text || firstmoduledata.etext < r.ip() {
    89  		return false
    90  	}
    91  
    92  	// Go will only handle some exceptions.
    93  	switch info.exceptioncode {
    94  	default:
    95  		return false
    96  	case _EXCEPTION_ACCESS_VIOLATION:
    97  	case _EXCEPTION_IN_PAGE_ERROR:
    98  	case _EXCEPTION_INT_DIVIDE_BY_ZERO:
    99  	case _EXCEPTION_INT_OVERFLOW:
   100  	case _EXCEPTION_FLT_DENORMAL_OPERAND:
   101  	case _EXCEPTION_FLT_DIVIDE_BY_ZERO:
   102  	case _EXCEPTION_FLT_INEXACT_RESULT:
   103  	case _EXCEPTION_FLT_OVERFLOW:
   104  	case _EXCEPTION_FLT_UNDERFLOW:
   105  	case _EXCEPTION_BREAKPOINT:
   106  	case _EXCEPTION_ILLEGAL_INSTRUCTION: // breakpoint arrives this way on arm64
   107  	}
   108  	return true
   109  }
   110  
   111  const (
   112  	callbackVEH = iota
   113  	callbackFirstVCH
   114  	callbackLastVCH
   115  )
   116  
   117  // sigFetchGSafe is like getg() but without panicking
   118  // when TLS is not set.
   119  // Only implemented on windows/386, which is the only
   120  // arch that loads TLS when calling getg(). Others
   121  // use a dedicated register.
   122  func sigFetchGSafe() *g
   123  
   124  func sigFetchG() *g {
   125  	if GOARCH == "386" {
   126  		return sigFetchGSafe()
   127  	}
   128  	return getg()
   129  }
   130  
   131  // sigtrampgo is called from the exception handler function, sigtramp,
   132  // written in assembly code.
   133  // Return EXCEPTION_CONTINUE_EXECUTION if the exception is handled,
   134  // else return EXCEPTION_CONTINUE_SEARCH.
   135  //
   136  // It is nosplit for the same reason as exceptionhandler.
   137  //
   138  //go:nosplit
   139  func sigtrampgo(ep *exceptionpointers, kind int) int32 {
   140  	gp := sigFetchG()
   141  	if gp == nil {
   142  		return _EXCEPTION_CONTINUE_SEARCH
   143  	}
   144  
   145  	var fn func(info *exceptionrecord, r *context, gp *g) int32
   146  	switch kind {
   147  	case callbackVEH:
   148  		fn = exceptionhandler
   149  	case callbackFirstVCH:
   150  		fn = firstcontinuehandler
   151  	case callbackLastVCH:
   152  		fn = lastcontinuehandler
   153  	default:
   154  		throw("unknown sigtramp callback")
   155  	}
   156  
   157  	// Check if we are running on g0 stack, and if we are,
   158  	// call fn directly instead of creating the closure.
   159  	// for the systemstack argument.
   160  	//
   161  	// A closure can't be marked as nosplit, so it might
   162  	// call morestack if we are at the g0 stack limit.
   163  	// If that happens, the runtime will call abort
   164  	// and end up in sigtrampgo again.
   165  	// TODO: revisit this workaround if/when closures
   166  	// can be compiled as nosplit.
   167  	//
   168  	// Note that this scenario should only occur on
   169  	// TestG0StackOverflow. Any other occurrence should
   170  	// be treated as a bug.
   171  	var ret int32
   172  	if gp != gp.m.g0 {
   173  		systemstack(func() {
   174  			ret = fn(ep.record, ep.context, gp)
   175  		})
   176  	} else {
   177  		ret = fn(ep.record, ep.context, gp)
   178  	}
   179  	if ret == _EXCEPTION_CONTINUE_SEARCH {
   180  		return ret
   181  	}
   182  
   183  	// Check if we need to set up the control flow guard workaround.
   184  	// On Windows, the stack pointer in the context must lie within
   185  	// system stack limits when we resume from exception.
   186  	// Store the resume SP and PC in alternate registers
   187  	// and return to sigresume on the g0 stack.
   188  	// sigresume makes no use of the stack at all,
   189  	// loading SP from RX and jumping to RY, being RX and RY two scratch registers.
   190  	// Note that blindly smashing RX and RY is only safe because we know sigpanic
   191  	// will not actually return to the original frame, so the registers
   192  	// are effectively dead. But this does mean we can't use the
   193  	// same mechanism for async preemption.
   194  	if ep.context.ip() == abi.FuncPCABI0(sigresume) {
   195  		// sigresume has already been set up by a previous exception.
   196  		return ret
   197  	}
   198  	prepareContextForSigResume(ep.context)
   199  	ep.context.set_sp(gp.m.g0.sched.sp)
   200  	ep.context.set_ip(abi.FuncPCABI0(sigresume))
   201  	return ret
   202  }
   203  
   204  // Called by sigtramp from Windows VEH handler.
   205  // Return value signals whether the exception has been handled (EXCEPTION_CONTINUE_EXECUTION)
   206  // or should be made available to other handlers in the chain (EXCEPTION_CONTINUE_SEARCH).
   207  //
   208  // This is nosplit to avoid growing the stack until we've checked for
   209  // _EXCEPTION_BREAKPOINT, which is raised by abort() if we overflow the g0 stack.
   210  //
   211  //go:nosplit
   212  func exceptionhandler(info *exceptionrecord, r *context, gp *g) int32 {
   213  	if !isgoexception(info, r) {
   214  		return _EXCEPTION_CONTINUE_SEARCH
   215  	}
   216  
   217  	if gp.throwsplit || isAbort(r) {
   218  		// We can't safely sigpanic because it may grow the stack.
   219  		// Or this is a call to abort.
   220  		// Don't go through any more of the Windows handler chain.
   221  		// Crash now.
   222  		winthrow(info, r, gp)
   223  	}
   224  
   225  	// After this point, it is safe to grow the stack.
   226  
   227  	// Make it look like a call to the signal func.
   228  	// Have to pass arguments out of band since
   229  	// augmenting the stack frame would break
   230  	// the unwinding code.
   231  	gp.sig = info.exceptioncode
   232  	gp.sigcode0 = info.exceptioninformation[0]
   233  	gp.sigcode1 = info.exceptioninformation[1]
   234  	gp.sigpc = r.ip()
   235  
   236  	// Only push runtime·sigpanic if r.ip() != 0.
   237  	// If r.ip() == 0, probably panicked because of a
   238  	// call to a nil func. Not pushing that onto sp will
   239  	// make the trace look like a call to runtime·sigpanic instead.
   240  	// (Otherwise the trace will end at runtime·sigpanic and we
   241  	// won't get to see who faulted.)
   242  	// Also don't push a sigpanic frame if the faulting PC
   243  	// is the entry of asyncPreempt. In this case, we suspended
   244  	// the thread right between the fault and the exception handler
   245  	// starting to run, and we have pushed an asyncPreempt call.
   246  	// The exception is not from asyncPreempt, so not to push a
   247  	// sigpanic call to make it look like that. Instead, just
   248  	// overwrite the PC. (See issue #35773)
   249  	if r.ip() != 0 && r.ip() != abi.FuncPCABI0(asyncPreempt) {
   250  		sp := unsafe.Pointer(r.sp())
   251  		delta := uintptr(sys.StackAlign)
   252  		sp = add(sp, -delta)
   253  		r.set_sp(uintptr(sp))
   254  		if usesLR {
   255  			*((*uintptr)(sp)) = r.lr()
   256  			r.set_lr(r.ip())
   257  		} else {
   258  			*((*uintptr)(sp)) = r.ip()
   259  		}
   260  	}
   261  	r.set_ip(abi.FuncPCABI0(sigpanic0))
   262  	return _EXCEPTION_CONTINUE_EXECUTION
   263  }
   264  
   265  // It seems Windows searches ContinueHandler's list even
   266  // if ExceptionHandler returns EXCEPTION_CONTINUE_EXECUTION.
   267  // firstcontinuehandler will stop that search,
   268  // if exceptionhandler did the same earlier.
   269  //
   270  // It is nosplit for the same reason as exceptionhandler.
   271  //
   272  //go:nosplit
   273  func firstcontinuehandler(info *exceptionrecord, r *context, gp *g) int32 {
   274  	if !isgoexception(info, r) {
   275  		return _EXCEPTION_CONTINUE_SEARCH
   276  	}
   277  	return _EXCEPTION_CONTINUE_EXECUTION
   278  }
   279  
   280  // lastcontinuehandler is reached, because runtime cannot handle
   281  // current exception. lastcontinuehandler will print crash info and exit.
   282  //
   283  // It is nosplit for the same reason as exceptionhandler.
   284  //
   285  //go:nosplit
   286  func lastcontinuehandler(info *exceptionrecord, r *context, gp *g) int32 {
   287  	if islibrary || isarchive {
   288  		// Go DLL/archive has been loaded in a non-go program.
   289  		// If the exception does not originate from go, the go runtime
   290  		// should not take responsibility of crashing the process.
   291  		return _EXCEPTION_CONTINUE_SEARCH
   292  	}
   293  
   294  	// VEH is called before SEH, but arm64 MSVC DLLs use SEH to trap
   295  	// illegal instructions during runtime initialization to determine
   296  	// CPU features, so if we make it to the last handler and we're
   297  	// arm64 and it's an illegal instruction and this is coming from
   298  	// non-Go code, then assume it's this runtime probing happen, and
   299  	// pass that onward to SEH.
   300  	if GOARCH == "arm64" && info.exceptioncode == _EXCEPTION_ILLEGAL_INSTRUCTION &&
   301  		(r.ip() < firstmoduledata.text || firstmoduledata.etext < r.ip()) {
   302  		return _EXCEPTION_CONTINUE_SEARCH
   303  	}
   304  
   305  	winthrow(info, r, gp)
   306  	return 0 // not reached
   307  }
   308  
   309  // Always called on g0. gp is the G where the exception occurred.
   310  //
   311  //go:nosplit
   312  func winthrow(info *exceptionrecord, r *context, gp *g) {
   313  	g0 := getg()
   314  
   315  	if panicking.Load() != 0 { // traceback already printed
   316  		exit(2)
   317  	}
   318  	panicking.Store(1)
   319  
   320  	// In case we're handling a g0 stack overflow, blow away the
   321  	// g0 stack bounds so we have room to print the traceback. If
   322  	// this somehow overflows the stack, the OS will trap it.
   323  	g0.stack.lo = 0
   324  	g0.stackguard0 = g0.stack.lo + stackGuard
   325  	g0.stackguard1 = g0.stackguard0
   326  
   327  	print("Exception ", hex(info.exceptioncode), " ", hex(info.exceptioninformation[0]), " ", hex(info.exceptioninformation[1]), " ", hex(r.ip()), "\n")
   328  
   329  	print("PC=", hex(r.ip()), "\n")
   330  	if g0.m.incgo && gp == g0.m.g0 && g0.m.curg != nil {
   331  		if iscgo {
   332  			print("signal arrived during external code execution\n")
   333  		}
   334  		gp = g0.m.curg
   335  	}
   336  	print("\n")
   337  
   338  	g0.m.throwing = throwTypeRuntime
   339  	g0.m.caughtsig.set(gp)
   340  
   341  	level, _, docrash := gotraceback()
   342  	if level > 0 {
   343  		tracebacktrap(r.ip(), r.sp(), r.lr(), gp)
   344  		tracebackothers(gp)
   345  		dumpregs(r)
   346  	}
   347  
   348  	if docrash {
   349  		dieFromException(info, r)
   350  	}
   351  
   352  	exit(2)
   353  }
   354  
   355  func sigpanic() {
   356  	gp := getg()
   357  	if !canpanic() {
   358  		throw("unexpected signal during runtime execution")
   359  	}
   360  
   361  	switch gp.sig {
   362  	case _EXCEPTION_ACCESS_VIOLATION, _EXCEPTION_IN_PAGE_ERROR:
   363  		if gp.sigcode1 < 0x1000 {
   364  			panicmem()
   365  		}
   366  		if gp.paniconfault {
   367  			panicmemAddr(gp.sigcode1)
   368  		}
   369  		if inUserArenaChunk(gp.sigcode1) {
   370  			// We could check that the arena chunk is explicitly set to fault,
   371  			// but the fact that we faulted on accessing it is enough to prove
   372  			// that it is.
   373  			print("accessed data from freed user arena ", hex(gp.sigcode1), "\n")
   374  		} else {
   375  			print("unexpected fault address ", hex(gp.sigcode1), "\n")
   376  		}
   377  		throw("fault")
   378  	case _EXCEPTION_INT_DIVIDE_BY_ZERO:
   379  		panicdivide()
   380  	case _EXCEPTION_INT_OVERFLOW:
   381  		panicoverflow()
   382  	case _EXCEPTION_FLT_DENORMAL_OPERAND,
   383  		_EXCEPTION_FLT_DIVIDE_BY_ZERO,
   384  		_EXCEPTION_FLT_INEXACT_RESULT,
   385  		_EXCEPTION_FLT_OVERFLOW,
   386  		_EXCEPTION_FLT_UNDERFLOW:
   387  		panicfloat()
   388  	}
   389  	throw("fault")
   390  }
   391  
   392  // Following are not implemented.
   393  
   394  func initsig(preinit bool) {
   395  }
   396  
   397  func sigenable(sig uint32) {
   398  }
   399  
   400  func sigdisable(sig uint32) {
   401  }
   402  
   403  func sigignore(sig uint32) {
   404  }
   405  
   406  func signame(sig uint32) string {
   407  	return ""
   408  }
   409  
   410  //go:nosplit
   411  func crash() {
   412  	dieFromException(nil, nil)
   413  }
   414  
   415  // dieFromException raises an exception that bypasses all exception handlers.
   416  // This provides the expected exit status for the shell.
   417  //
   418  //go:nosplit
   419  func dieFromException(info *exceptionrecord, r *context) {
   420  	if info == nil {
   421  		gp := getg()
   422  		if gp.sig != 0 {
   423  			// Try to reconstruct an exception record from
   424  			// the exception information stored in gp.
   425  			info = &exceptionrecord{
   426  				exceptionaddress: gp.sigpc,
   427  				exceptioncode:    gp.sig,
   428  				numberparameters: 2,
   429  			}
   430  			info.exceptioninformation[0] = gp.sigcode0
   431  			info.exceptioninformation[1] = gp.sigcode1
   432  		} else {
   433  			// By default, a failing Go application exits with exit code 2.
   434  			// Use this value when gp does not contain exception info.
   435  			info = &exceptionrecord{
   436  				exceptioncode: 2,
   437  			}
   438  		}
   439  	}
   440  	const FAIL_FAST_GENERATE_EXCEPTION_ADDRESS = 0x1
   441  	stdcall3(_RaiseFailFastException, uintptr(unsafe.Pointer(info)), uintptr(unsafe.Pointer(r)), FAIL_FAST_GENERATE_EXCEPTION_ADDRESS)
   442  }
   443  
   444  // gsignalStack is unused on Windows.
   445  type gsignalStack struct{}