diff --git a/src/runtime/crash_cgo_test.go b/src/runtime/crash_cgo_test.go index a5cbbad69b..2869ac7687 100644 --- a/src/runtime/crash_cgo_test.go +++ b/src/runtime/crash_cgo_test.go @@ -411,3 +411,31 @@ func TestCgoNumGoroutine(t *testing.T) { t.Errorf("expected %q got %v", want, got) } } + +func TestCatchPanic(t *testing.T) { + t.Parallel() + switch runtime.GOOS { + case "plan9", "windows": + t.Skipf("no signals on %s", runtime.GOOS) + case "darwin": + if runtime.GOARCH == "amd64" { + t.Skipf("crash() on darwin/amd64 doesn't raise SIGABRT") + } + } + + testenv.MustHaveGoRun(t) + + exe, err := buildTestProg(t, "testprogcgo") + if err != nil { + t.Fatal(err) + } + + cmd := testEnv(exec.Command(exe, "CgoCatchPanic")) + // Make sure a panic results in a crash. + cmd.Env = append(cmd.Env, "GOTRACEBACK=crash") + // Tell testprogcgo to install an early signal handler for SIGABRT + cmd.Env = append(cmd.Env, "CGOCATCHPANIC_INSTALL_HANDLER=1") + if out, err := cmd.CombinedOutput(); err != nil { + t.Errorf("testprogcgo CgoCatchPanic failed: %v\n%s", err, out) + } +} diff --git a/src/runtime/signal_unix.go b/src/runtime/signal_unix.go index d9a18caa6f..e087e145aa 100644 --- a/src/runtime/signal_unix.go +++ b/src/runtime/signal_unix.go @@ -394,8 +394,11 @@ func sigpanic() { //go:nosplit //go:nowritebarrierrec func dieFromSignal(sig uint32) { - setsig(sig, _SIG_DFL) unblocksig(sig) + // First, try any signal handler installed before the runtime + // initialized. + fn := atomic.Loaduintptr(&fwdSig[sig]) + setsig(sig, fn) raise(sig) // That should have killed us. On some systems, though, raise @@ -407,6 +410,14 @@ func dieFromSignal(sig uint32) { osyield() osyield() + // If that didn't work, try _SIG_DFL. + setsig(sig, _SIG_DFL) + raise(sig) + + osyield() + osyield() + osyield() + // If we are still somehow running, just exit with the wrong status. exit(2) } diff --git a/src/runtime/testdata/testprogcgo/catchpanic.go b/src/runtime/testdata/testprogcgo/catchpanic.go new file mode 100644 index 0000000000..f03b6d3ea3 --- /dev/null +++ b/src/runtime/testdata/testprogcgo/catchpanic.go @@ -0,0 +1,39 @@ +// Copyright 2017 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. + +// +build !plan9,!windows + +package main + +/* +#include +#include +#include + +static void abrthandler(int signum) { + if (signum == SIGABRT) { + exit(0); // success + } +} + +static void __attribute__ ((constructor)) sigsetup(void) { + struct sigaction act; + + if (getenv("CGOCATCHPANIC_INSTALL_HANDLER") == NULL) + return; + memset(&act, 0, sizeof act); + act.sa_handler = abrthandler; + sigaction(SIGABRT, &act, NULL); +} +*/ +import "C" + +func init() { + register("CgoCatchPanic", CgoCatchPanic) +} + +// Test that the SIGABRT raised by panic can be caught by an early signal handler. +func CgoCatchPanic() { + panic("catch me") +}