Skip to content

Commit

Permalink
runtime: for c-archive/c-shared, install signal handlers synchronously
Browse files Browse the repository at this point in the history
The previous behaviour of installing the signal handlers in a separate
thread meant that Go initialization raced with non-Go initialization if
the non-Go initialization also wanted to install signal handlers.  Make
installing signal handlers synchronous so that the process-wide behavior
is predictable.

Update #9896.

Change-Id: Ice24299877ec46f8518b072a381932d273096a32
Reviewed-on: https://go-review.googlesource.com/18150
Run-TryBot: Ian Lance Taylor <iant@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: David Crawshaw <crawshaw@golang.org>
  • Loading branch information
ianlancetaylor committed Jan 9, 2016
1 parent 0b3807a commit 21b4f23
Show file tree
Hide file tree
Showing 23 changed files with 200 additions and 12 deletions.
39 changes: 39 additions & 0 deletions misc/cgo/testcarchive/main.c
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,44 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

#include <signal.h>
#include <stdint.h>
#include <stdio.h>
#include <string.h>

#include "p.h"
#include "libgo.h"

static void (*oldHandler)(int, siginfo_t*, void*);

static void handler(int signo, siginfo_t* info, void* ctxt) {
if (oldHandler) {
oldHandler(signo, info, ctxt);
}
}

int main(void) {
struct sigaction sa;
struct sigaction osa;
int32_t res;

// Install our own signal handler.
memset(&sa, 0, sizeof sa);
sa.sa_sigaction = handler;
sigemptyset(&sa.sa_mask);
sa.sa_flags = SA_ONSTACK | SA_SIGINFO;
memset(&osa, 0, sizeof osa);
sigemptyset(&osa.sa_mask);
if (sigaction(SIGSEGV, &sa, &osa) < 0) {
perror("sigaction");
return 2;
}
if (osa.sa_handler == SIG_DFL || (osa.sa_flags&SA_ONSTACK) == 0) {
fprintf(stderr, "Go runtime did not install signal handler\n");
return 2;
}
oldHandler = osa.sa_sigaction;

if (!DidInitRun()) {
fprintf(stderr, "ERROR: buildmode=c-archive init should run\n");
return 2;
Expand All @@ -21,6 +50,16 @@ int main(void) {
return 2;
}

// Make sure our signal handler is still the one in use.
if (sigaction(SIGSEGV, NULL, &sa) < 0) {
perror("sigaction check");
return 2;
}
if (sa.sa_sigaction != handler) {
fprintf(stderr, "ERROR: wrong signal handler: %p != %p\n", sa.sa_sigaction, handler);
return 2;
}

res = FromPkg();
if (res != 1024) {
fprintf(stderr, "ERROR: FromPkg()=%d, want 1024\n", res);
Expand Down
5 changes: 4 additions & 1 deletion src/os/signal/doc.go
Original file line number Diff line number Diff line change
Expand Up @@ -172,7 +172,10 @@ When Go code is built with options like -buildmode=c-shared, it will
be run as part of an existing non-Go program. The non-Go code may
have already installed signal handlers when the Go code starts (that
may also happen in unusual cases when using cgo or SWIG; in that case,
the discussion here applies).
the discussion here applies). For -buildmode=c-archive the Go runtime
will initialize signals at global constructor time. For
-buildmode=c-shared the Go runtime will initialize signals when the
shared library is loaded.
If the Go runtime sees an existing signal handler for the SIGCANCEL or
SIGSETXID signals (which are used only on GNU/Linux), it will turn on
Expand Down
17 changes: 17 additions & 0 deletions src/runtime/os1_darwin.go
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,15 @@ func newosproc0(stacksize uintptr, fn unsafe.Pointer, fnarg uintptr) {
var failallocatestack = []byte("runtime: failed to allocate stack for the new OS thread\n")
var failthreadcreate = []byte("runtime: failed to create new OS thread\n")

// Called to do synchronous initialization of Go code built with
// -buildmode=c-archive or -buildmode=c-shared.
// None of the Go runtime is initialized.
//go:nosplit
//go:nowritebarrierrec
func libpreinit() {
initsig(true)
}

// Called to initialize a new m (including the bootstrap m).
// Called on the parent thread (main thread in case of bootstrap), can allocate memory.
func mpreinit(mp *m) {
Expand Down Expand Up @@ -459,6 +468,8 @@ func memlimit() uintptr {
return 0
}

//go:nosplit
//go:nowritebarrierrec
func setsig(i int32, fn uintptr, restart bool) {
var sa sigactiont
sa.sa_flags = _SA_SIGINFO | _SA_ONSTACK
Expand All @@ -471,6 +482,8 @@ func setsig(i int32, fn uintptr, restart bool) {
sigaction(uint32(i), &sa, nil)
}

//go:nosplit
//go:nowritebarrierrec
func setsigstack(i int32) {
var osa usigactiont
sigaction(uint32(i), nil, &osa)
Expand All @@ -486,6 +499,8 @@ func setsigstack(i int32) {
sigaction(uint32(i), &sa, nil)
}

//go:nosplit
//go:nowritebarrierrec
func getsig(i int32) uintptr {
var sa usigactiont
sigaction(uint32(i), nil, &sa)
Expand All @@ -505,6 +520,8 @@ func signalstack(s *stack) {
sigaltstack(&st, nil)
}

//go:nosplit
//go:nowritebarrierrec
func updatesigmask(m sigmask) {
s := sigset(m[0])
sigprocmask(_SIG_SETMASK, &s, nil)
Expand Down
8 changes: 8 additions & 0 deletions src/runtime/os1_dragonfly.go
Original file line number Diff line number Diff line change
Expand Up @@ -213,6 +213,8 @@ type sigactiont struct {
sa_mask sigset
}

//go:nosplit
//go:nowritebarrierrec
func setsig(i int32, fn uintptr, restart bool) {
var sa sigactiont
sa.sa_flags = _SA_SIGINFO | _SA_ONSTACK
Expand All @@ -227,10 +229,14 @@ func setsig(i int32, fn uintptr, restart bool) {
sigaction(i, &sa, nil)
}

//go:nosplit
//go:nowritebarrierrec
func setsigstack(i int32) {
throw("setsigstack")
}

//go:nosplit
//go:nowritebarrierrec
func getsig(i int32) uintptr {
var sa sigactiont
sigaction(i, nil, &sa)
Expand All @@ -253,6 +259,8 @@ func signalstack(s *stack) {
sigaltstack(&st, nil)
}

//go:nosplit
//go:nowritebarrierrec
func updatesigmask(m sigmask) {
var mask sigset
copy(mask.__bits[:], m[:])
Expand Down
8 changes: 8 additions & 0 deletions src/runtime/os1_freebsd.go
Original file line number Diff line number Diff line change
Expand Up @@ -220,6 +220,8 @@ type sigactiont struct {
sa_mask sigset
}

//go:nosplit
//go:nowritebarrierrec
func setsig(i int32, fn uintptr, restart bool) {
var sa sigactiont
sa.sa_flags = _SA_SIGINFO | _SA_ONSTACK
Expand All @@ -234,10 +236,14 @@ func setsig(i int32, fn uintptr, restart bool) {
sigaction(i, &sa, nil)
}

//go:nosplit
//go:nowritebarrierrec
func setsigstack(i int32) {
throw("setsigstack")
}

//go:nosplit
//go:nowritebarrierrec
func getsig(i int32) uintptr {
var sa sigactiont
sigaction(i, nil, &sa)
Expand All @@ -260,6 +266,8 @@ func signalstack(s *stack) {
sigaltstack(&st, nil)
}

//go:nosplit
//go:nowritebarrierrec
func updatesigmask(m [(_NSIG + 31) / 32]uint32) {
var mask sigset
copy(mask.__bits[:], m[:])
Expand Down
22 changes: 18 additions & 4 deletions src/runtime/os1_linux.go
Original file line number Diff line number Diff line change
Expand Up @@ -190,6 +190,15 @@ func goenvs() {
goenvs_unix()
}

// Called to do synchronous initialization of Go code built with
// -buildmode=c-archive or -buildmode=c-shared.
// None of the Go runtime is initialized.
//go:nosplit
//go:nowritebarrierrec
func libpreinit() {
initsig(true)
}

// Called to initialize a new m (including the bootstrap m).
// Called on the parent thread (main thread in case of bootstrap), can allocate memory.
func mpreinit(mp *m) {
Expand Down Expand Up @@ -298,6 +307,8 @@ func memlimit() uintptr {
func sigreturn()
func sigtramp()

//go:nosplit
//go:nowritebarrierrec
func setsig(i int32, fn uintptr, restart bool) {
var sa sigactiont
memclr(unsafe.Pointer(&sa), unsafe.Sizeof(sa))
Expand All @@ -316,12 +327,11 @@ func setsig(i int32, fn uintptr, restart bool) {
fn = funcPC(sigtramp)
}
sa.sa_handler = fn
// Qemu rejects rt_sigaction of SIGRTMAX (64).
if rt_sigaction(uintptr(i), &sa, nil, unsafe.Sizeof(sa.sa_mask)) != 0 && i != 64 {
throw("rt_sigaction failure")
}
rt_sigaction(uintptr(i), &sa, nil, unsafe.Sizeof(sa.sa_mask))
}

//go:nosplit
//go:nowritebarrierrec
func setsigstack(i int32) {
var sa sigactiont
if rt_sigaction(uintptr(i), nil, &sa, unsafe.Sizeof(sa.sa_mask)) != 0 {
Expand All @@ -336,6 +346,8 @@ func setsigstack(i int32) {
}
}

//go:nosplit
//go:nowritebarrierrec
func getsig(i int32) uintptr {
var sa sigactiont

Expand All @@ -362,6 +374,8 @@ func signalstack(s *stack) {
sigaltstack(&st, nil)
}

//go:nosplit
//go:nowritebarrierrec
func updatesigmask(m sigmask) {
var mask sigset
sigcopyset(&mask, m)
Expand Down
2 changes: 1 addition & 1 deletion src/runtime/os1_nacl.go
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ func goenvs() {
goenvs_unix()
}

func initsig() {
func initsig(preinit bool) {
}

//go:nosplit
Expand Down
8 changes: 8 additions & 0 deletions src/runtime/os1_netbsd.go
Original file line number Diff line number Diff line change
Expand Up @@ -206,6 +206,8 @@ type sigactiont struct {
sa_flags int32
}

//go:nosplit
//go:nowritebarrierrec
func setsig(i int32, fn uintptr, restart bool) {
var sa sigactiont
sa.sa_flags = _SA_SIGINFO | _SA_ONSTACK
Expand All @@ -220,10 +222,14 @@ func setsig(i int32, fn uintptr, restart bool) {
sigaction(i, &sa, nil)
}

//go:nosplit
//go:nowritebarrierrec
func setsigstack(i int32) {
throw("setsigstack")
}

//go:nosplit
//go:nowritebarrierrec
func getsig(i int32) uintptr {
var sa sigactiont
sigaction(i, nil, &sa)
Expand All @@ -246,6 +252,8 @@ func signalstack(s *stack) {
sigaltstack(&st, nil)
}

//go:nosplit
//go:nowritebarrierrec
func updatesigmask(m sigmask) {
var mask sigset
copy(mask.__bits[:], m[:])
Expand Down
8 changes: 8 additions & 0 deletions src/runtime/os1_openbsd.go
Original file line number Diff line number Diff line change
Expand Up @@ -220,6 +220,8 @@ type sigactiont struct {
sa_flags int32
}

//go:nosplit
//go:nowritebarrierrec
func setsig(i int32, fn uintptr, restart bool) {
var sa sigactiont
sa.sa_flags = _SA_SIGINFO | _SA_ONSTACK
Expand All @@ -234,10 +236,14 @@ func setsig(i int32, fn uintptr, restart bool) {
sigaction(i, &sa, nil)
}

//go:nosplit
//go:nowritebarrierrec
func setsigstack(i int32) {
throw("setsigstack")
}

//go:nosplit
//go:nowritebarrierrec
func getsig(i int32) uintptr {
var sa sigactiont
sigaction(i, nil, &sa)
Expand All @@ -260,6 +266,8 @@ func signalstack(s *stack) {
sigaltstack(&st, nil)
}

//go:nosplit
//go:nowritebarrierrec
func updatesigmask(m sigmask) {
sigprocmask(_SIG_SETMASK, sigset(m[0]))
}
Expand Down
2 changes: 1 addition & 1 deletion src/runtime/os1_plan9.go
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,7 @@ func getRandomData(r []byte) {
func goenvs() {
}

func initsig() {
func initsig(preinit bool) {
}

//go:nosplit
Expand Down
13 changes: 13 additions & 0 deletions src/runtime/os3_solaris.go
Original file line number Diff line number Diff line change
Expand Up @@ -279,6 +279,8 @@ func memlimit() uintptr {

func sigtramp()

//go:nosplit
//go:nowritebarrierrec
func setsig(i int32, fn uintptr, restart bool) {
var sa sigactiont

Expand All @@ -295,6 +297,8 @@ func setsig(i int32, fn uintptr, restart bool) {
sigaction(i, &sa, nil)
}

//go:nosplit
//go:nowritebarrierrec
func setsigstack(i int32) {
var sa sigactiont
sigaction(i, nil, &sa)
Expand All @@ -306,6 +310,8 @@ func setsigstack(i int32) {
sigaction(i, &sa, nil)
}

//go:nosplit
//go:nowritebarrierrec
func getsig(i int32) uintptr {
var sa sigactiont
sigaction(i, nil, &sa)
Expand All @@ -328,6 +334,8 @@ func signalstack(s *stack) {
sigaltstack(&st, nil)
}

//go:nosplit
//go:nowritebarrierrec
func updatesigmask(m sigmask) {
var mask sigset
copy(mask.__sigbits[:], m[:])
Expand Down Expand Up @@ -478,6 +486,8 @@ func pthread_create(thread *pthread, attr *pthreadattr, fn uintptr, arg unsafe.P
return int32(sysvicall4(&libc_pthread_create, uintptr(unsafe.Pointer(thread)), uintptr(unsafe.Pointer(attr)), uintptr(fn), uintptr(arg)))
}

//go:nosplit
//go:nowritebarrierrec
func raise(sig int32) /* int32 */ {
sysvicall1(&libc_raise, uintptr(sig))
}
Expand Down Expand Up @@ -516,6 +526,8 @@ func setitimer(which int32, value *itimerval, ovalue *itimerval) /* int32 */ {
sysvicall3(&libc_setitimer, uintptr(which), uintptr(unsafe.Pointer(value)), uintptr(unsafe.Pointer(ovalue)))
}

//go:nosplit
//go:nowritebarrierrec
func sigaction(sig int32, act *sigactiont, oact *sigactiont) /* int32 */ {
sysvicall3(&libc_sigaction, uintptr(sig), uintptr(unsafe.Pointer(act)), uintptr(unsafe.Pointer(oact)))
}
Expand All @@ -527,6 +539,7 @@ func sigaltstack(ss *sigaltstackt, oss *sigaltstackt) /* int32 */ {
}

//go:nosplit
//go:nowritebarrierrec
func sigprocmask(how int32, set *sigset, oset *sigset) /* int32 */ {
sysvicall3(&libc_sigprocmask, uintptr(how), uintptr(unsafe.Pointer(set)), uintptr(unsafe.Pointer(oset)))
}
Expand Down
Loading

0 comments on commit 21b4f23

Please sign in to comment.