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:
parent
b075d1fc2e
commit
5c8fbc6f1e
58
misc/cgo/testsigfwd/main.go
Normal file
58
misc/cgo/testsigfwd/main.go
Normal 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()
|
||||
}
|
3
src/cmd/dist/test.go
vendored
3
src/cmd/dist/test.go
vendored
@ -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")
|
||||
|
@ -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)
|
||||
|
||||
|
@ -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.
|
||||
|
@ -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)
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user