]> Cypherpunks repositories - gostls13.git/commitdiff
runtime: signal forwarding
authorSrdjan Petrovic <spetrovic@google.com>
Thu, 9 Apr 2015 18:12:12 +0000 (11:12 -0700)
committerIan Lance Taylor <iant@golang.org>
Fri, 24 Apr 2015 05:19:39 +0000 (05:19 +0000)
Forward signals to signal handlers installed before Go installs its own,
under certain circumstances.  In particular, as iant@ suggests, signals are
forwarded iff:
   (1) a non-SIG_DFL signal handler existed before Go, and
   (2) signal is synchronous (i.e., one of SIGSEGV, SIGBUS, SIGFPE), and
    (3a) signal occured on a non-Go thread, or
    (3b) signal occurred on a Go thread but in CGo code.

Supported only on Linux, for now.

Change-Id: I403219ee47b26cf65da819fb86cf1ec04d3e25f5
Reviewed-on: https://go-review.googlesource.com/8712
Reviewed-by: Ian Lance Taylor <iant@golang.org>
misc/cgo/testsigfwd/main.go [new file with mode: 0644]
src/cmd/dist/test.go
src/runtime/os_linux.go
src/runtime/signal1_unix.go
src/runtime/signal_linux.go
src/runtime/sys_linux_386.s
src/runtime/sys_linux_amd64.s
src/runtime/sys_linux_arm.s
src/runtime/sys_linux_arm64.s
src/runtime/sys_linux_ppc64x.s

diff --git a/misc/cgo/testsigfwd/main.go b/misc/cgo/testsigfwd/main.go
new file mode 100644 (file)
index 0000000..6641c9d
--- /dev/null
@@ -0,0 +1,58 @@
+// Copyright 2015 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 main
+
+import "fmt"
+
+/*
+#include <signal.h>
+#include <stdlib.h>
+#include <stdio.h>
+
+int *p;
+static void sigsegv() {
+       *p = 1;
+       fprintf(stderr, "ERROR: C SIGSEGV not thrown on caught?.\n");
+       exit(2);
+}
+
+static void sighandler(int signum) {
+       if (signum == SIGSEGV) {
+               exit(0);  // success
+       }
+}
+
+static void __attribute__ ((constructor)) sigsetup(void) {
+       struct sigaction act;
+       act.sa_handler = &sighandler;
+       sigaction(SIGSEGV, &act, 0);
+}
+*/
+import "C"
+
+var p *byte
+
+func f() (ret bool) {
+       defer func() {
+               if recover() == nil {
+                       fmt.Errorf("ERROR: couldn't raise SIGSEGV in Go.")
+                       C.exit(2)
+               }
+               ret = true
+       }()
+       *p = 1
+       return false
+}
+
+func main() {
+       // Test that the signal originating in Go is handled (and recovered) by Go.
+       if !f() {
+               fmt.Errorf("couldn't recover from SIGSEGV in Go.")
+               C.exit(2)
+       }
+
+       // Test that the signal originating in C is handled by C.
+       C.sigsegv()
+}
index f4fef5655d2439999060e4b0c32509a56c5170d0..559e5aaf3a4d1d0cbf6f7c12cdea951357a05ffb 100644 (file)
@@ -284,6 +284,9 @@ func (t *tester) registerTests() {
                if t.hasBash() && t.goos != "android" && !iOS && t.gohostos != "windows" {
                        t.registerTest("cgo_errors", "../misc/cgo/errors", "./test.bash")
                }
+               if t.gohostos == "linux" && t.extLink() {
+                       t.registerTest("testsigfwd", "../misc/cgo/testsigfwd", "go", "run", "main.go")
+               }
        }
        if t.hasBash() && t.goos != "nacl" && t.goos != "android" && !iOS {
                t.registerTest("doc_progs", "../doc/progs", "time", "go", "run", "run.go")
index abea5d61aa8f38a2b20d3baeb36b061b217e73fc..523d28b2106f73fbef805e93c724d7b00887a8f6 100644 (file)
@@ -18,6 +18,9 @@ func rt_sigaction(sig uintptr, new, old *sigactiont, size uintptr) int32
 //go:noescape
 func sigaltstack(new, old *sigaltstackt)
 
+//go:noescape
+func sigfwd(fn uintptr, sig uint32, info *siginfo, ctx unsafe.Pointer)
+
 //go:noescape
 func setitimer(mode int32, new, old *itimerval)
 
index 184fd125fb8513e784b657f0ae0a42aac394ad0e..7577d43a64d2fde24e320825f5bfdad0cf26b0d8 100644 (file)
@@ -11,6 +11,14 @@ const (
        _SIG_IGN uintptr = 1
 )
 
+// Stores the signal handlers registered before Go installed its own.
+// These signal handlers will be invoked in cases where Go doesn't want to
+// handle a particular signal (e.g., signal occurred on a non-Go thread).
+// See sigfwdgo() for more information on when the signals are forwarded.
+//
+// Signal forwarding is currently available only on Linux.
+var fwdSig [_NSIG]uintptr
+
 func initsig() {
        // _NSIG is the number of signals on this operating system.
        // sigtable should describe what to do for all the possible signals.
@@ -25,7 +33,7 @@ func initsig() {
                if t.flags == 0 || t.flags&_SigDefault != 0 {
                        continue
                }
-
+               fwdSig[i] = getsig(i)
                // For some signals, we respect an inherited SIG_IGN handler
                // rather than insist on installing our own default handler.
                // Even these signals can be fetched using the os/signal package.
index c71e619b1ec3fc924d8ed40be585cbe5c426b43b..1ab4e9ec7156b5efd77785c6d1aaa5f483403c79 100644 (file)
@@ -4,6 +4,8 @@
 
 package runtime
 
+import "unsafe"
+
 type sigTabT struct {
        flags int32
        name  string
@@ -76,3 +78,53 @@ var sigtable = [...]sigTabT{
        /* 63 */ {_SigNotify, "signal 63"},
        /* 64 */ {_SigNotify, "signal 64"},
 }
+
+// Determines if the signal should be handled by Go and if not, forwards the
+// signal to the handler that was installed before Go's.  Returns whether the
+// signal was forwarded.
+//go:nosplit
+func sigfwdgo(sig uint32, info *siginfo, ctx unsafe.Pointer) bool {
+       g := getg()
+       c := &sigctxt{info, ctx}
+       if sig >= uint32(len(sigtable)) {
+               return false
+       }
+       fwdFn := fwdSig[sig]
+       flags := sigtable[sig].flags
+
+       // If there is no handler to forward to, no need to forward.
+       if fwdFn == _SIG_DFL {
+               return false
+       }
+       // Only forward synchronous signals.
+       if c.sigcode() == _SI_USER || flags&_SigPanic == 0 {
+               return false
+       }
+       // Determine if the signal occurred inside Go code.  We test that:
+       //   (1) we were in a goroutine (i.e., m.curg != nil), and
+       //   (2) we weren't in CGO (i.e., m.curg.syscallsp == 0).
+       if g != nil && g.m != nil && g.m.curg != nil && g.m.curg.syscallsp == 0 {
+               return false
+       }
+       // Signal not handled by Go, forward it.
+       if fwdFn != _SIG_IGN {
+               sigfwd(fwdFn, sig, info, ctx)
+       }
+       return true
+}
+
+// Continuation of the (assembly) sigtramp() logic.
+//go:nosplit
+func sigtrampgo(sig uint32, info *siginfo, ctx unsafe.Pointer) {
+       if sigfwdgo(sig, info, ctx) {
+               return
+       }
+       g := getg()
+       if g == nil {
+               badsignal(uintptr(sig))
+               return
+       }
+       setg(g.m.gsignal)
+       sighandler(sig, info, ctx, g)
+       setg(g)
+}
index 679a81d66da1eb1f3adf97cc2bd559273c8b9697..f5cfb644c96d62c680a36c6f0c289eff124c1fa2 100644 (file)
@@ -191,43 +191,25 @@ TEXT runtime·rt_sigaction(SB),NOSPLIT,$0
        MOVL    AX, ret+16(FP)
        RET
 
-TEXT runtime·sigtramp(SB),NOSPLIT,$44
-       get_tls(CX)
-
-       // check that g exists
-       MOVL    g(CX), DI
-       CMPL    DI, $0
-       JNE     6(PC)
-       MOVL    sig+0(FP), BX
-       MOVL    BX, 0(SP)
-       MOVL    $runtime·badsignal(SB), AX
+TEXT runtime·sigfwd(SB),NOSPLIT,$12-16
+       MOVL    sig+4(FP), AX
+       MOVL    AX, 0(SP)
+       MOVL    info+8(FP), AX
+       MOVL    AX, 4(SP)
+       MOVL    ctx+12(FP), AX
+       MOVL    AX, 8(SP)
+       MOVL    fn+0(FP), AX
        CALL    AX
        RET
 
-       // save g
-       MOVL    DI, 20(SP)
-
-       // g = m->gsignal
-       MOVL    g_m(DI), BX
-       MOVL    m_gsignal(BX), BX
-       MOVL    BX, g(CX)
-
-       // copy arguments for call to sighandler
+TEXT runtime·sigtramp(SB),NOSPLIT,$12
        MOVL    sig+0(FP), BX
        MOVL    BX, 0(SP)
        MOVL    info+4(FP), BX
        MOVL    BX, 4(SP)
        MOVL    context+8(FP), BX
        MOVL    BX, 8(SP)
-       MOVL    DI, 12(SP)
-
-       CALL    runtime·sighandler(SB)
-
-       // restore g
-       get_tls(CX)
-       MOVL    20(SP), BX
-       MOVL    BX, g(CX)
-
+       CALL    runtime·sigtrampgo(SB)
        RET
 
 TEXT runtime·sigreturn(SB),NOSPLIT,$0
index fa7fa164b5ef06ee18aaceb0c39ff660f71041f8..f36ac8493b06fad00e6c960de60f17c97b95e9bc 100644 (file)
@@ -212,37 +212,20 @@ TEXT runtime·rt_sigaction(SB),NOSPLIT,$0-36
        MOVL    AX, ret+32(FP)
        RET
 
-TEXT runtime·sigtramp(SB),NOSPLIT,$64
-       get_tls(BX)
-
-       // check that g exists
-       MOVQ    g(BX), R10
-       CMPQ    R10, $0
-       JNE     5(PC)
-       MOVQ    DI, 0(SP)
-       MOVQ    $runtime·badsignal(SB), AX
+TEXT runtime·sigfwd(SB),NOSPLIT,$0-32
+       MOVQ    sig+8(FP), DI
+       MOVQ    info+16(FP), SI
+       MOVQ    ctx+24(FP), DX
+       MOVQ    fn+0(FP), AX
        CALL    AX
        RET
 
-       // save g
-       MOVQ    R10, 40(SP)
-
-       // g = m->gsignal
-       MOVQ    g_m(R10), AX
-       MOVQ    m_gsignal(AX), AX
-       MOVQ    AX, g(BX)
-
-       MOVQ    DI, 0(SP)
-       MOVQ    SI, 8(SP)
-       MOVQ    DX, 16(SP)
-       MOVQ    R10, 24(SP)
-
-       CALL    runtime·sighandler(SB)
-
-       // restore g
-       get_tls(BX)
-       MOVQ    40(SP), R10
-       MOVQ    R10, g(BX)
+TEXT runtime·sigtramp(SB),NOSPLIT,$24
+       MOVQ    DI, 0(SP)   // signum
+       MOVQ    SI, 8(SP)   // info
+       MOVQ    DX, 16(SP)  // ctx
+       MOVQ    $runtime·sigtrampgo(SB), AX
+       CALL AX
        RET
 
 TEXT runtime·sigreturn(SB),NOSPLIT,$0
index d0c6d22f31f3f5fe2cbb34d1f920e8d77c51f7ef..3936cd93a87e8e163d7c11f09197c9b9704f891b 100644 (file)
@@ -327,7 +327,15 @@ TEXT runtime·sigaltstack(SB),NOSPLIT,$0
        MOVW.HI R8, (R8)
        RET
 
-TEXT runtime·sigtramp(SB),NOSPLIT,$24
+TEXT runtime·sigfwd(SB),NOSPLIT,$0-16
+       MOVW    sig+4(FP), R0
+       MOVW    info+8(FP), R1
+       MOVW    ctx+12(FP), R2
+       MOVW    fn+0(FP), R11
+       BL      (R11)
+       RET
+
+TEXT runtime·sigtramp(SB),NOSPLIT,$12
        // this might be called in external code context,
        // where g is not set.
        // first save R0, because runtime·load_g will clobber it
@@ -336,32 +344,10 @@ TEXT runtime·sigtramp(SB),NOSPLIT,$24
        CMP     $0, R0
        BL.NE   runtime·load_g(SB)
 
-       CMP     $0, g
-       BNE     4(PC)
-       // signal number is already prepared in 4(R13)
-       MOVW    $runtime·badsignal(SB), R11
-       BL      (R11)
-       RET
-
-       // save g
-       MOVW    g, R3
-       MOVW    g, 20(R13)
-
-       // g = m->gsignal
-       MOVW    g_m(g), R8
-       MOVW    m_gsignal(R8), g
-
-       // copy arguments for call to sighandler
-       // R0 is already saved above
        MOVW    R1, 8(R13)
        MOVW    R2, 12(R13)
-       MOVW    R3, 16(R13)
-
-       BL      runtime·sighandler(SB)
-
-       // restore g
-       MOVW    20(R13), g
-
+       MOVW    $runtime·sigtrampgo(SB), R11
+       BL      (R11)
        RET
 
 TEXT runtime·rtsigprocmask(SB),NOSPLIT,$0
index 389fea0ddec1640d0d0d2aa6491d765c75f66915..0aca3a2010742404797d47313ec59ce098555bb2 100644 (file)
@@ -215,7 +215,15 @@ TEXT runtime·rt_sigaction(SB),NOSPLIT,$-8-36
        MOVW    R0, ret+32(FP)
        RET
 
-TEXT runtime·sigtramp(SB),NOSPLIT,$64
+TEXT runtime·sigfwd(SB),NOSPLIT,$0-32
+       MOVW    sig+8(FP), R0
+       MOVD    info+16(FP), R1
+       MOVD    ctx+24(FP), R2
+       MOVD    fn+0(FP), R11
+       BL      (R11)
+       RET
+
+TEXT runtime·sigtramp(SB),NOSPLIT,$24
        // this might be called in external code context,
        // where g is not set.
        // first save R0, because runtime·load_g will clobber it
@@ -225,31 +233,10 @@ TEXT runtime·sigtramp(SB),NOSPLIT,$64
        BEQ     2(PC)
        BL      runtime·load_g(SB)
 
-       // check that g exists
-       CMP     g, ZR
-       BNE     ok
-       MOVD    $runtime·badsignal(SB), R0
-       BL      (R0)
-       RET
-
-ok:
-       // save g
-       MOVD    g, 40(RSP)
-       MOVD    g, R6
-
-       // g = m->gsignal
-       MOVD    g_m(g), R7
-       MOVD    m_gsignal(R7), g
-
-       // R0 is already saved above
        MOVD    R1, 16(RSP)
        MOVD    R2, 24(RSP)
-       MOVD    R6, 32(RSP)
-
-       BL      runtime·sighandler(SB)
-
-       // restore g
-       MOVD    40(RSP), g
+       MOVD    $runtime·sigtrampgo(SB), R0
+       BL      (R0)
        RET
 
 TEXT runtime·mmap(SB),NOSPLIT,$-8
index 44a22c9c7756b8d22dc47f09e9410531671a7fbe..232f299ac2ee148cdb3f838a9b552a2743307e0c 100644 (file)
@@ -196,6 +196,15 @@ TEXT runtime·rt_sigaction(SB),NOSPLIT,$-8-36
        MOVW    R3, ret+32(FP)
        RETURN
 
+TEXT runtime·sigfwd(SB),NOSPLIT,$0-32
+       MOVW    sig+8(FP), R3
+       MOVD    info+16(FP), R4
+       MOVD    ctx+24(FP), R5
+       MOVD    fn+0(FP), R31
+       MOVD    R31, CTR
+       BL      (CTR)
+       RETURN
+
 #ifdef GOARCH_ppc64le
 // ppc64le doesn't need function descriptors
 TEXT runtime·sigtramp(SB),NOSPLIT,$64
@@ -217,33 +226,12 @@ TEXT runtime·_sigtramp(SB),NOSPLIT,$64
        BEQ     2(PC)
        BL      runtime·load_g(SB)
 
-       // check that g exists
-       CMP     g, $0
-       BNE     6(PC)
-       MOVD    R3, 8(R1)
-       MOVD    $runtime·badsignal(SB), R31
-       MOVD    R31, CTR
-       BL      (CTR)
-       RETURN
-
-       // save g
-       MOVD    g, 40(R1)
-       MOVD    g, R6
-
-       // g = m->gsignal
-       MOVD    g_m(g), R7
-       MOVD    m_gsignal(R7), g
-
        MOVW    R3, 8(R1)
        MOVD    R4, 16(R1)
        MOVD    R5, 24(R1)
-       MOVD    R6, 32(R1)
-
-       BL      runtime·sighandler(SB)
-
-       // restore g
-       MOVD    40(R1), g
-
+       MOVD    $runtime·sigtrampgo(SB), R31
+       MOVD    R31, CTR
+       BL      (CTR)
        RETURN
 
 TEXT runtime·mmap(SB),NOSPLIT,$-8