1
0
mirror of https://github.com/golang/go synced 2024-09-25 01:20:13 -06:00

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/8712
Reviewed-by: Ian Lance Taylor <iant@golang.org>
This commit is contained in:
Srdjan Petrovic 2015-04-09 11:12:12 -07:00 committed by Ian Lance Taylor
parent b075d1fc2e
commit 5c8fbc6f1e
10 changed files with 180 additions and 130 deletions

View File

@ -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()
}

View 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")

View 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)

View 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.

View 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)
}

View 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

View 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

View 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

View 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

View 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