Commit 5c8fbc6f authored by Srdjan Petrovic's avatar Srdjan Petrovic Committed by Ian Lance Taylor

runtime: signal forwarding

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/8712Reviewed-by: default avatarIan Lance Taylor <iant@golang.org>
parent b075d1fc
// 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()
}
...@@ -284,6 +284,9 @@ func (t *tester) registerTests() { ...@@ -284,6 +284,9 @@ func (t *tester) registerTests() {
if t.hasBash() && t.goos != "android" && !iOS && t.gohostos != "windows" { if t.hasBash() && t.goos != "android" && !iOS && t.gohostos != "windows" {
t.registerTest("cgo_errors", "../misc/cgo/errors", "./test.bash") 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 { if t.hasBash() && t.goos != "nacl" && t.goos != "android" && !iOS {
t.registerTest("doc_progs", "../doc/progs", "time", "go", "run", "run.go") t.registerTest("doc_progs", "../doc/progs", "time", "go", "run", "run.go")
......
...@@ -18,6 +18,9 @@ func rt_sigaction(sig uintptr, new, old *sigactiont, size uintptr) int32 ...@@ -18,6 +18,9 @@ func rt_sigaction(sig uintptr, new, old *sigactiont, size uintptr) int32
//go:noescape //go:noescape
func sigaltstack(new, old *sigaltstackt) func sigaltstack(new, old *sigaltstackt)
//go:noescape
func sigfwd(fn uintptr, sig uint32, info *siginfo, ctx unsafe.Pointer)
//go:noescape //go:noescape
func setitimer(mode int32, new, old *itimerval) func setitimer(mode int32, new, old *itimerval)
......
...@@ -11,6 +11,14 @@ const ( ...@@ -11,6 +11,14 @@ const (
_SIG_IGN uintptr = 1 _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() { func initsig() {
// _NSIG is the number of signals on this operating system. // _NSIG is the number of signals on this operating system.
// sigtable should describe what to do for all the possible signals. // sigtable should describe what to do for all the possible signals.
...@@ -25,7 +33,7 @@ func initsig() { ...@@ -25,7 +33,7 @@ func initsig() {
if t.flags == 0 || t.flags&_SigDefault != 0 { if t.flags == 0 || t.flags&_SigDefault != 0 {
continue continue
} }
fwdSig[i] = getsig(i)
// For some signals, we respect an inherited SIG_IGN handler // For some signals, we respect an inherited SIG_IGN handler
// rather than insist on installing our own default handler. // rather than insist on installing our own default handler.
// Even these signals can be fetched using the os/signal package. // Even these signals can be fetched using the os/signal package.
......
...@@ -4,6 +4,8 @@ ...@@ -4,6 +4,8 @@
package runtime package runtime
import "unsafe"
type sigTabT struct { type sigTabT struct {
flags int32 flags int32
name string name string
...@@ -76,3 +78,53 @@ var sigtable = [...]sigTabT{ ...@@ -76,3 +78,53 @@ var sigtable = [...]sigTabT{
/* 63 */ {_SigNotify, "signal 63"}, /* 63 */ {_SigNotify, "signal 63"},
/* 64 */ {_SigNotify, "signal 64"}, /* 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)
}
...@@ -191,43 +191,25 @@ TEXT runtime·rt_sigaction(SB),NOSPLIT,$0 ...@@ -191,43 +191,25 @@ TEXT runtime·rt_sigaction(SB),NOSPLIT,$0
MOVL AX, ret+16(FP) MOVL AX, ret+16(FP)
RET RET
TEXT runtime·sigtramp(SB),NOSPLIT,$44 TEXT runtime·sigfwd(SB),NOSPLIT,$12-16
get_tls(CX) MOVL sig+4(FP), AX
MOVL AX, 0(SP)
// check that g exists MOVL info+8(FP), AX
MOVL g(CX), DI MOVL AX, 4(SP)
CMPL DI, $0 MOVL ctx+12(FP), AX
JNE 6(PC) MOVL AX, 8(SP)
MOVL sig+0(FP), BX MOVL fn+0(FP), AX
MOVL BX, 0(SP)
MOVL $runtime·badsignal(SB), AX
CALL AX CALL AX
RET RET
// save g TEXT runtime·sigtramp(SB),NOSPLIT,$12
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
MOVL sig+0(FP), BX MOVL sig+0(FP), BX
MOVL BX, 0(SP) MOVL BX, 0(SP)
MOVL info+4(FP), BX MOVL info+4(FP), BX
MOVL BX, 4(SP) MOVL BX, 4(SP)
MOVL context+8(FP), BX MOVL context+8(FP), BX
MOVL BX, 8(SP) MOVL BX, 8(SP)
MOVL DI, 12(SP) CALL runtime·sigtrampgo(SB)
CALL runtime·sighandler(SB)
// restore g
get_tls(CX)
MOVL 20(SP), BX
MOVL BX, g(CX)
RET RET
TEXT runtime·sigreturn(SB),NOSPLIT,$0 TEXT runtime·sigreturn(SB),NOSPLIT,$0
......
...@@ -212,37 +212,20 @@ TEXT runtime·rt_sigaction(SB),NOSPLIT,$0-36 ...@@ -212,37 +212,20 @@ TEXT runtime·rt_sigaction(SB),NOSPLIT,$0-36
MOVL AX, ret+32(FP) MOVL AX, ret+32(FP)
RET RET
TEXT runtime·sigtramp(SB),NOSPLIT,$64 TEXT runtime·sigfwd(SB),NOSPLIT,$0-32
get_tls(BX) MOVQ sig+8(FP), DI
MOVQ info+16(FP), SI
// check that g exists MOVQ ctx+24(FP), DX
MOVQ g(BX), R10 MOVQ fn+0(FP), AX
CMPQ R10, $0
JNE 5(PC)
MOVQ DI, 0(SP)
MOVQ $runtime·badsignal(SB), AX
CALL AX CALL AX
RET RET
// save g TEXT runtime·sigtramp(SB),NOSPLIT,$24
MOVQ R10, 40(SP) MOVQ DI, 0(SP) // signum
MOVQ SI, 8(SP) // info
// g = m->gsignal MOVQ DX, 16(SP) // ctx
MOVQ g_m(R10), AX MOVQ $runtime·sigtrampgo(SB), AX
MOVQ m_gsignal(AX), AX CALL 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)
RET RET
TEXT runtime·sigreturn(SB),NOSPLIT,$0 TEXT runtime·sigreturn(SB),NOSPLIT,$0
......
...@@ -327,7 +327,15 @@ TEXT runtime·sigaltstack(SB),NOSPLIT,$0 ...@@ -327,7 +327,15 @@ TEXT runtime·sigaltstack(SB),NOSPLIT,$0
MOVW.HI R8, (R8) MOVW.HI R8, (R8)
RET 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, // this might be called in external code context,
// where g is not set. // where g is not set.
// first save R0, because runtime·load_g will clobber it // first save R0, because runtime·load_g will clobber it
...@@ -336,32 +344,10 @@ TEXT runtime·sigtramp(SB),NOSPLIT,$24 ...@@ -336,32 +344,10 @@ TEXT runtime·sigtramp(SB),NOSPLIT,$24
CMP $0, R0 CMP $0, R0
BL.NE runtime·load_g(SB) 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 R1, 8(R13)
MOVW R2, 12(R13) MOVW R2, 12(R13)
MOVW R3, 16(R13) MOVW $runtime·sigtrampgo(SB), R11
BL (R11)
BL runtime·sighandler(SB)
// restore g
MOVW 20(R13), g
RET RET
TEXT runtime·rtsigprocmask(SB),NOSPLIT,$0 TEXT runtime·rtsigprocmask(SB),NOSPLIT,$0
......
...@@ -215,7 +215,15 @@ TEXT runtime·rt_sigaction(SB),NOSPLIT,$-8-36 ...@@ -215,7 +215,15 @@ TEXT runtime·rt_sigaction(SB),NOSPLIT,$-8-36
MOVW R0, ret+32(FP) MOVW R0, ret+32(FP)
RET 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, // this might be called in external code context,
// where g is not set. // where g is not set.
// first save R0, because runtime·load_g will clobber it // first save R0, because runtime·load_g will clobber it
...@@ -225,31 +233,10 @@ TEXT runtime·sigtramp(SB),NOSPLIT,$64 ...@@ -225,31 +233,10 @@ TEXT runtime·sigtramp(SB),NOSPLIT,$64
BEQ 2(PC) BEQ 2(PC)
BL runtime·load_g(SB) 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 R1, 16(RSP)
MOVD R2, 24(RSP) MOVD R2, 24(RSP)
MOVD R6, 32(RSP) MOVD $runtime·sigtrampgo(SB), R0
BL (R0)
BL runtime·sighandler(SB)
// restore g
MOVD 40(RSP), g
RET RET
TEXT runtime·mmap(SB),NOSPLIT,$-8 TEXT runtime·mmap(SB),NOSPLIT,$-8
......
...@@ -196,6 +196,15 @@ TEXT runtime·rt_sigaction(SB),NOSPLIT,$-8-36 ...@@ -196,6 +196,15 @@ TEXT runtime·rt_sigaction(SB),NOSPLIT,$-8-36
MOVW R3, ret+32(FP) MOVW R3, ret+32(FP)
RETURN 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 #ifdef GOARCH_ppc64le
// ppc64le doesn't need function descriptors // ppc64le doesn't need function descriptors
TEXT runtime·sigtramp(SB),NOSPLIT,$64 TEXT runtime·sigtramp(SB),NOSPLIT,$64
...@@ -217,33 +226,12 @@ TEXT runtime·_sigtramp(SB),NOSPLIT,$64 ...@@ -217,33 +226,12 @@ TEXT runtime·_sigtramp(SB),NOSPLIT,$64
BEQ 2(PC) BEQ 2(PC)
BL runtime·load_g(SB) 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) MOVW R3, 8(R1)
MOVD R4, 16(R1) MOVD R4, 16(R1)
MOVD R5, 24(R1) MOVD R5, 24(R1)
MOVD R6, 32(R1) MOVD $runtime·sigtrampgo(SB), R31
MOVD R31, CTR
BL runtime·sighandler(SB) BL (CTR)
// restore g
MOVD 40(R1), g
RETURN RETURN
TEXT runtime·mmap(SB),NOSPLIT,$-8 TEXT runtime·mmap(SB),NOSPLIT,$-8
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment