diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/runtime/defs_windows.go | 2 | ||||
| -rw-r--r-- | src/runtime/export_windows_test.go | 1 | ||||
| -rw-r--r-- | src/runtime/extern.go | 1 | ||||
| -rw-r--r-- | src/runtime/nonwindows_stub.go (renamed from src/runtime/relax_stub.go) | 4 | ||||
| -rw-r--r-- | src/runtime/os_windows.go | 10 | ||||
| -rw-r--r-- | src/runtime/runtime1.go | 7 | ||||
| -rw-r--r-- | src/runtime/signal_windows.go | 83 | ||||
| -rw-r--r-- | src/runtime/syscall_windows_test.go | 23 |
8 files changed, 95 insertions, 36 deletions
diff --git a/src/runtime/defs_windows.go b/src/runtime/defs_windows.go index 60f20a5c2c..56698fa56c 100644 --- a/src/runtime/defs_windows.go +++ b/src/runtime/defs_windows.go @@ -67,7 +67,7 @@ type exceptionrecord struct { exceptioncode uint32 exceptionflags uint32 exceptionrecord *exceptionrecord - exceptionaddress *byte + exceptionaddress uintptr numberparameters uint32 exceptioninformation [15]uintptr } diff --git a/src/runtime/export_windows_test.go b/src/runtime/export_windows_test.go index d4b1e1fad3..332136b586 100644 --- a/src/runtime/export_windows_test.go +++ b/src/runtime/export_windows_test.go @@ -11,7 +11,6 @@ import "unsafe" const MaxArgs = maxArgs var ( - TestingWER = &testingWER OsYield = osyield TimeBeginPeriodRetValue = &timeBeginPeriodRetValue ) diff --git a/src/runtime/extern.go b/src/runtime/extern.go index 03d593906e..189b4d4bb9 100644 --- a/src/runtime/extern.go +++ b/src/runtime/extern.go @@ -217,6 +217,7 @@ and shows goroutines created internally by the run-time. GOTRACEBACK=crash is like “system” but crashes in an operating system-specific manner instead of exiting. For example, on Unix systems, the crash raises SIGABRT to trigger a core dump. +GOTRACEBACK=wer is like “crash” but doesn't disable Windows Error Reporting (WER). For historical reasons, the GOTRACEBACK settings 0, 1, and 2 are synonyms for none, all, and system, respectively. The runtime/debug package's SetTraceback function allows increasing the diff --git a/src/runtime/relax_stub.go b/src/runtime/nonwindows_stub.go index e507702fc1..033f026c42 100644 --- a/src/runtime/relax_stub.go +++ b/src/runtime/nonwindows_stub.go @@ -15,3 +15,7 @@ const osRelaxMinNS = 0 // osRelax is called by the scheduler when transitioning to and from // all Ps being idle. func osRelax(relax bool) {} + +// enableWER is called by setTraceback("wer"). +// Windows Error Reporting (WER) is only supported on Windows. +func enableWER() {} diff --git a/src/runtime/os_windows.go b/src/runtime/os_windows.go index 41f8f77848..10b445837e 100644 --- a/src/runtime/os_windows.go +++ b/src/runtime/os_windows.go @@ -30,6 +30,7 @@ const ( //go:cgo_import_dynamic runtime._GetConsoleMode GetConsoleMode%2 "kernel32.dll" //go:cgo_import_dynamic runtime._GetCurrentThreadId GetCurrentThreadId%0 "kernel32.dll" //go:cgo_import_dynamic runtime._GetEnvironmentStringsW GetEnvironmentStringsW%0 "kernel32.dll" +//go:cgo_import_dynamic runtime._GetErrorMode GetErrorMode%0 "kernel32.dll" //go:cgo_import_dynamic runtime._GetProcAddress GetProcAddress%2 "kernel32.dll" //go:cgo_import_dynamic runtime._GetProcessAffinityMask GetProcessAffinityMask%3 "kernel32.dll" //go:cgo_import_dynamic runtime._GetQueuedCompletionStatusEx GetQueuedCompletionStatusEx%6 "kernel32.dll" @@ -41,6 +42,7 @@ const ( //go:cgo_import_dynamic runtime._LoadLibraryExW LoadLibraryExW%3 "kernel32.dll" //go:cgo_import_dynamic runtime._LoadLibraryW LoadLibraryW%1 "kernel32.dll" //go:cgo_import_dynamic runtime._PostQueuedCompletionStatus PostQueuedCompletionStatus%4 "kernel32.dll" +//go:cgo_import_dynamic runtime._RaiseFailFastException RaiseFailFastException%3 "kernel32.dll" //go:cgo_import_dynamic runtime._ResumeThread ResumeThread%1 "kernel32.dll" //go:cgo_import_dynamic runtime._SetConsoleCtrlHandler SetConsoleCtrlHandler%2 "kernel32.dll" //go:cgo_import_dynamic runtime._SetErrorMode SetErrorMode%1 "kernel32.dll" @@ -57,6 +59,8 @@ const ( //go:cgo_import_dynamic runtime._VirtualQuery VirtualQuery%3 "kernel32.dll" //go:cgo_import_dynamic runtime._WaitForSingleObject WaitForSingleObject%2 "kernel32.dll" //go:cgo_import_dynamic runtime._WaitForMultipleObjects WaitForMultipleObjects%4 "kernel32.dll" +//go:cgo_import_dynamic runtime._WerGetFlags WerGetFlags%2 "kernel32.dll" +//go:cgo_import_dynamic runtime._WerSetFlags WerSetFlags%1 "kernel32.dll" //go:cgo_import_dynamic runtime._WriteConsoleW WriteConsoleW%5 "kernel32.dll" //go:cgo_import_dynamic runtime._WriteFile WriteFile%5 "kernel32.dll" @@ -80,6 +84,7 @@ var ( _GetConsoleMode, _GetCurrentThreadId, _GetEnvironmentStringsW, + _GetErrorMode, _GetProcAddress, _GetProcessAffinityMask, _GetQueuedCompletionStatusEx, @@ -94,6 +99,7 @@ var ( _PostQueuedCompletionStatus, _QueryPerformanceCounter, _QueryPerformanceFrequency, + _RaiseFailFastException, _ResumeThread, _SetConsoleCtrlHandler, _SetErrorMode, @@ -110,6 +116,8 @@ var ( _VirtualQuery, _WaitForSingleObject, _WaitForMultipleObjects, + _WerGetFlags, + _WerSetFlags, _WriteConsoleW, _WriteFile, _ stdFunction @@ -519,7 +527,7 @@ func osinit() { loadOptionalSyscalls() - disableWER() + preventErrorDialogs() initExceptionHandler() diff --git a/src/runtime/runtime1.go b/src/runtime/runtime1.go index 68a090a3c7..98c5c84c01 100644 --- a/src/runtime/runtime1.go +++ b/src/runtime/runtime1.go @@ -513,6 +513,13 @@ func setTraceback(level string) { t = 2<<tracebackShift | tracebackAll case "crash": t = 2<<tracebackShift | tracebackAll | tracebackCrash + case "wer": + if GOOS == "windows" { + t = 2<<tracebackShift | tracebackAll | tracebackCrash + enableWER() + break + } + fallthrough default: t = tracebackAll if n, ok := atoi(level); ok && n == int(uint32(n)) { diff --git a/src/runtime/signal_windows.go b/src/runtime/signal_windows.go index e4258f01b0..59c261ac19 100644 --- a/src/runtime/signal_windows.go +++ b/src/runtime/signal_windows.go @@ -10,16 +10,34 @@ 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, sys_windows_amd64.s, sys_windows_arm.s, and sys_windows_arm64.s @@ -259,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. // @@ -274,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 @@ -333,7 +346,7 @@ func winthrow(info *exceptionrecord, r *context, gp *g) { } if docrash { - crash() + dieFromException(info, r) } exit(2) @@ -396,14 +409,36 @@ func signame(sig uint32) string { //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. diff --git a/src/runtime/syscall_windows_test.go b/src/runtime/syscall_windows_test.go index b49da32384..8686d3f7f8 100644 --- a/src/runtime/syscall_windows_test.go +++ b/src/runtime/syscall_windows_test.go @@ -648,21 +648,26 @@ func TestZeroDivisionException(t *testing.T) { } func TestWERDialogue(t *testing.T) { - if os.Getenv("TESTING_WER_DIALOGUE") == "1" { - defer os.Exit(0) - - *runtime.TestingWER = true + const exitcode = 0xbad + if os.Getenv("TEST_WER_DIALOGUE") == "1" { const EXCEPTION_NONCONTINUABLE = 1 mod := syscall.MustLoadDLL("kernel32.dll") proc := mod.MustFindProc("RaiseException") - proc.Call(0xbad, EXCEPTION_NONCONTINUABLE, 0, 0) - println("RaiseException should not return") + proc.Call(exitcode, EXCEPTION_NONCONTINUABLE, 0, 0) + t.Fatal("RaiseException should not return") return } - cmd := exec.Command(os.Args[0], "-test.run=TestWERDialogue") - cmd.Env = []string{"TESTING_WER_DIALOGUE=1"} + cmd := testenv.CleanCmdEnv(exec.Command(os.Args[0], "-test.run=TestWERDialogue")) + cmd.Env = append(cmd.Env, "TEST_WER_DIALOGUE=1", "GOTRACEBACK=wer") // Child process should not open WER dialogue, but return immediately instead. - cmd.CombinedOutput() + _, err := cmd.CombinedOutput() + if err == nil { + t.Error("test program succeeded unexpectedly") + } else if ee, ok := err.(*exec.ExitError); !ok { + t.Errorf("error (%v) has type %T; expected exec.ExitError", err, err) + } else if got := ee.ExitCode(); got != exitcode { + t.Fatalf("got exit code %d; want %d", got, exitcode) + } } func TestWindowsStackMemory(t *testing.T) { |
