mirror of
https://github.com/golang/go
synced 2024-11-23 05:00:07 -07:00
runtime: for c-archive/c-shared, don't install unnecessary signal handlers
Only install signal handlers for synchronous signals that become run-time panics. Set the SA_ONSTACK flag for other signal handlers as needed. Fixes #13028. Update #12465. Update #13034. Update #13042. Change-Id: I28375e70641f60630e10f3c86e24b6e4f8a35cc9 Reviewed-on: https://go-review.googlesource.com/17903 Reviewed-by: Russ Cox <rsc@golang.org> Run-TryBot: Ian Lance Taylor <iant@golang.org> TryBot-Result: Gobot Gobot <gobot@golang.org>
This commit is contained in:
parent
32cf985d1a
commit
fbdfa99246
185
misc/cgo/testcarchive/main2.c
Normal file
185
misc/cgo/testcarchive/main2.c
Normal file
@ -0,0 +1,185 @@
|
|||||||
|
// 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.
|
||||||
|
|
||||||
|
// Test installing a signal handler before the Go code starts.
|
||||||
|
// This is a lot like misc/cgo/testcshared/main4.c.
|
||||||
|
|
||||||
|
#include <setjmp.h>
|
||||||
|
#include <signal.h>
|
||||||
|
#include <stddef.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <sched.h>
|
||||||
|
#include <time.h>
|
||||||
|
|
||||||
|
#include "libgo2.h"
|
||||||
|
|
||||||
|
static void die(const char* msg) {
|
||||||
|
perror(msg);
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
|
||||||
|
static volatile sig_atomic_t sigioSeen;
|
||||||
|
|
||||||
|
// Use up some stack space.
|
||||||
|
static void recur(int i, char *p) {
|
||||||
|
char a[1024];
|
||||||
|
|
||||||
|
*p = '\0';
|
||||||
|
if (i > 0) {
|
||||||
|
recur(i - 1, a);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Signal handler that uses up more stack space than a goroutine will have.
|
||||||
|
static void ioHandler(int signo, siginfo_t* info, void* ctxt) {
|
||||||
|
char a[1024];
|
||||||
|
|
||||||
|
recur(4, a);
|
||||||
|
sigioSeen = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static jmp_buf jmp;
|
||||||
|
static char* nullPointer;
|
||||||
|
|
||||||
|
// Signal handler for SIGSEGV on a C thread.
|
||||||
|
static void segvHandler(int signo, siginfo_t* info, void* ctxt) {
|
||||||
|
sigset_t mask;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
if (sigemptyset(&mask) < 0) {
|
||||||
|
die("sigemptyset");
|
||||||
|
}
|
||||||
|
if (sigaddset(&mask, SIGSEGV) < 0) {
|
||||||
|
die("sigaddset");
|
||||||
|
}
|
||||||
|
i = sigprocmask(SIG_UNBLOCK, &mask, NULL);
|
||||||
|
if (i != 0) {
|
||||||
|
fprintf(stderr, "sigprocmask: %s\n", strerror(i));
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Don't try this at home.
|
||||||
|
longjmp(jmp, signo);
|
||||||
|
|
||||||
|
// We should never get here.
|
||||||
|
abort();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set up the signal handlers in a high priority constructor,
|
||||||
|
// so that they are installed before the Go code starts.
|
||||||
|
|
||||||
|
static void init(void) __attribute__ ((constructor (200)));
|
||||||
|
|
||||||
|
static void init() {
|
||||||
|
struct sigaction sa;
|
||||||
|
|
||||||
|
memset(&sa, 0, sizeof sa);
|
||||||
|
sa.sa_sigaction = ioHandler;
|
||||||
|
if (sigemptyset(&sa.sa_mask) < 0) {
|
||||||
|
die("sigemptyset");
|
||||||
|
}
|
||||||
|
sa.sa_flags = SA_SIGINFO;
|
||||||
|
if (sigaction(SIGIO, &sa, NULL) < 0) {
|
||||||
|
die("sigaction");
|
||||||
|
}
|
||||||
|
|
||||||
|
sa.sa_sigaction = segvHandler;
|
||||||
|
if (sigaction(SIGSEGV, &sa, NULL) < 0 || sigaction(SIGBUS, &sa, NULL) < 0) {
|
||||||
|
die("sigaction");
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc, char** argv) {
|
||||||
|
int verbose;
|
||||||
|
sigset_t mask;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
verbose = argc > 1;
|
||||||
|
setvbuf(stdout, NULL, _IONBF, 0);
|
||||||
|
|
||||||
|
// Call setsid so that we can use kill(0, SIGIO) below.
|
||||||
|
// Don't check the return value so that this works both from
|
||||||
|
// a job control shell and from a shell script.
|
||||||
|
setsid();
|
||||||
|
|
||||||
|
if (verbose) {
|
||||||
|
printf("calling RunGoroutines\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
RunGoroutines();
|
||||||
|
|
||||||
|
// Block SIGIO in this thread to make it more likely that it
|
||||||
|
// will be delivered to a goroutine.
|
||||||
|
|
||||||
|
if (verbose) {
|
||||||
|
printf("calling pthread_sigmask\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (sigemptyset(&mask) < 0) {
|
||||||
|
die("sigemptyset");
|
||||||
|
}
|
||||||
|
if (sigaddset(&mask, SIGIO) < 0) {
|
||||||
|
die("sigaddset");
|
||||||
|
}
|
||||||
|
i = pthread_sigmask(SIG_BLOCK, &mask, NULL);
|
||||||
|
if (i != 0) {
|
||||||
|
fprintf(stderr, "pthread_sigmask: %s\n", strerror(i));
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (verbose) {
|
||||||
|
printf("calling kill\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (kill(0, SIGIO) < 0) {
|
||||||
|
die("kill");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (verbose) {
|
||||||
|
printf("waiting for sigioSeen\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Wait until the signal has been delivered.
|
||||||
|
i = 0;
|
||||||
|
while (!sigioSeen) {
|
||||||
|
if (sched_yield() < 0) {
|
||||||
|
perror("sched_yield");
|
||||||
|
}
|
||||||
|
i++;
|
||||||
|
if (i > 10000) {
|
||||||
|
fprintf(stderr, "looping too long waiting for signal\n");
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (verbose) {
|
||||||
|
printf("calling setjmp\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test that a SIGSEGV on this thread is delivered to us.
|
||||||
|
if (setjmp(jmp) == 0) {
|
||||||
|
if (verbose) {
|
||||||
|
printf("triggering SIGSEGV\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
*nullPointer = '\0';
|
||||||
|
|
||||||
|
fprintf(stderr, "continued after address error\n");
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (verbose) {
|
||||||
|
printf("calling TestSEGV\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
TestSEGV();
|
||||||
|
|
||||||
|
printf("PASS\n");
|
||||||
|
return 0;
|
||||||
|
}
|
45
misc/cgo/testcarchive/src/libgo2/libgo2.go
Normal file
45
misc/cgo/testcarchive/src/libgo2/libgo2.go
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
// 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 "C"
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"runtime"
|
||||||
|
)
|
||||||
|
|
||||||
|
// RunGoroutines starts some goroutines that don't do anything.
|
||||||
|
// The idea is to get some threads going, so that a signal will be delivered
|
||||||
|
// to a thread started by Go.
|
||||||
|
//export RunGoroutines
|
||||||
|
func RunGoroutines() {
|
||||||
|
for i := 0; i < 4; i++ {
|
||||||
|
go func() {
|
||||||
|
runtime.LockOSThread()
|
||||||
|
select {}
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var P *byte
|
||||||
|
|
||||||
|
// TestSEGV makes sure that an invalid address turns into a run-time Go panic.
|
||||||
|
//export TestSEGV
|
||||||
|
func TestSEGV() {
|
||||||
|
defer func() {
|
||||||
|
if recover() == nil {
|
||||||
|
fmt.Fprintln(os.Stderr, "no panic from segv")
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
*P = 0
|
||||||
|
fmt.Fprintln(os.Stderr, "continued after segv")
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
}
|
@ -23,11 +23,16 @@ fi
|
|||||||
|
|
||||||
rm -rf libgo.a libgo.h testp pkg
|
rm -rf libgo.a libgo.h testp pkg
|
||||||
|
|
||||||
|
status=0
|
||||||
|
|
||||||
# Installing first will create the header files we want.
|
# Installing first will create the header files we want.
|
||||||
|
|
||||||
GOPATH=$(pwd) go install -buildmode=c-archive libgo
|
GOPATH=$(pwd) go install -buildmode=c-archive libgo
|
||||||
$(go env CC) $(go env GOGCCFLAGS) $ccargs -o testp main.c pkg/$(go env GOOS)_$(go env GOARCH)/libgo.a
|
$(go env CC) $(go env GOGCCFLAGS) $ccargs -o testp main.c pkg/$(go env GOOS)_$(go env GOARCH)/libgo.a
|
||||||
$bin arg1 arg2
|
if ! $bin arg1 arg2; then
|
||||||
|
echo "FAIL test1"
|
||||||
|
status=1
|
||||||
|
fi
|
||||||
rm -f libgo.a libgo.h testp
|
rm -f libgo.a libgo.h testp
|
||||||
|
|
||||||
# Test building libgo other than installing it.
|
# Test building libgo other than installing it.
|
||||||
@ -35,10 +40,26 @@ rm -f libgo.a libgo.h testp
|
|||||||
|
|
||||||
GOPATH=$(pwd) go build -buildmode=c-archive src/libgo/libgo.go
|
GOPATH=$(pwd) go build -buildmode=c-archive src/libgo/libgo.go
|
||||||
$(go env CC) $(go env GOGCCFLAGS) $ccargs -o testp main.c libgo.a
|
$(go env CC) $(go env GOGCCFLAGS) $ccargs -o testp main.c libgo.a
|
||||||
$bin arg1 arg2
|
if ! $bin arg1 arg2; then
|
||||||
|
echo "FAIL test2"
|
||||||
|
status=1
|
||||||
|
fi
|
||||||
rm -f libgo.a libgo.h testp
|
rm -f libgo.a libgo.h testp
|
||||||
|
|
||||||
GOPATH=$(pwd) go build -buildmode=c-archive -o libgo.a libgo
|
GOPATH=$(pwd) go build -buildmode=c-archive -o libgo.a libgo
|
||||||
$(go env CC) $(go env GOGCCFLAGS) $ccargs -o testp main.c libgo.a
|
$(go env CC) $(go env GOGCCFLAGS) $ccargs -o testp main.c libgo.a
|
||||||
$bin arg1 arg2
|
if ! $bin arg1 arg2; then
|
||||||
|
echo "FAIL test3"
|
||||||
|
status=1
|
||||||
|
fi
|
||||||
rm -rf libgo.a libgo.h testp pkg
|
rm -rf libgo.a libgo.h testp pkg
|
||||||
|
|
||||||
|
GOPATH=$(pwd) go build -buildmode=c-archive -o libgo2.a libgo2
|
||||||
|
$(go env CC) $(go env GOGCCFLAGS) $ccargs -o testp main2.c libgo2.a
|
||||||
|
if ! $bin; then
|
||||||
|
echo "FAIL test4"
|
||||||
|
status=1
|
||||||
|
fi
|
||||||
|
rm -rf libgo2.a libgo2.h testp pkg
|
||||||
|
|
||||||
|
exit $status
|
||||||
|
214
misc/cgo/testcshared/main4.c
Normal file
214
misc/cgo/testcshared/main4.c
Normal file
@ -0,0 +1,214 @@
|
|||||||
|
// 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.
|
||||||
|
|
||||||
|
// Test that a signal handler that uses up stack space does not crash
|
||||||
|
// if the signal is delivered to a thread running a goroutine.
|
||||||
|
// This is a lot like misc/cgo/testcarchive/main2.c.
|
||||||
|
|
||||||
|
#include <setjmp.h>
|
||||||
|
#include <signal.h>
|
||||||
|
#include <stddef.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <sched.h>
|
||||||
|
#include <time.h>
|
||||||
|
#include <dlfcn.h>
|
||||||
|
|
||||||
|
static void die(const char* msg) {
|
||||||
|
perror(msg);
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
|
||||||
|
static volatile sig_atomic_t sigioSeen;
|
||||||
|
|
||||||
|
// Use up some stack space.
|
||||||
|
static void recur(int i, char *p) {
|
||||||
|
char a[1024];
|
||||||
|
|
||||||
|
*p = '\0';
|
||||||
|
if (i > 0) {
|
||||||
|
recur(i - 1, a);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Signal handler that uses up more stack space than a goroutine will have.
|
||||||
|
static void ioHandler(int signo, siginfo_t* info, void* ctxt) {
|
||||||
|
char a[1024];
|
||||||
|
|
||||||
|
recur(4, a);
|
||||||
|
sigioSeen = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static jmp_buf jmp;
|
||||||
|
static char* nullPointer;
|
||||||
|
|
||||||
|
// Signal handler for SIGSEGV on a C thread.
|
||||||
|
static void segvHandler(int signo, siginfo_t* info, void* ctxt) {
|
||||||
|
sigset_t mask;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
if (sigemptyset(&mask) < 0) {
|
||||||
|
die("sigemptyset");
|
||||||
|
}
|
||||||
|
if (sigaddset(&mask, SIGSEGV) < 0) {
|
||||||
|
die("sigaddset");
|
||||||
|
}
|
||||||
|
i = sigprocmask(SIG_UNBLOCK, &mask, NULL);
|
||||||
|
if (i != 0) {
|
||||||
|
fprintf(stderr, "sigprocmask: %s\n", strerror(i));
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Don't try this at home.
|
||||||
|
longjmp(jmp, signo);
|
||||||
|
|
||||||
|
// We should never get here.
|
||||||
|
abort();
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc, char** argv) {
|
||||||
|
int verbose;
|
||||||
|
struct sigaction sa;
|
||||||
|
void* handle;
|
||||||
|
void (*fn)(void);
|
||||||
|
sigset_t mask;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
verbose = argc > 2;
|
||||||
|
setvbuf(stdout, NULL, _IONBF, 0);
|
||||||
|
|
||||||
|
// Call setsid so that we can use kill(0, SIGIO) below.
|
||||||
|
// Don't check the return value so that this works both from
|
||||||
|
// a job control shell and from a shell script.
|
||||||
|
setsid();
|
||||||
|
|
||||||
|
if (verbose) {
|
||||||
|
printf("calling sigaction\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
memset(&sa, 0, sizeof sa);
|
||||||
|
sa.sa_sigaction = ioHandler;
|
||||||
|
if (sigemptyset(&sa.sa_mask) < 0) {
|
||||||
|
die("sigemptyset");
|
||||||
|
}
|
||||||
|
sa.sa_flags = SA_SIGINFO;
|
||||||
|
if (sigaction(SIGIO, &sa, NULL) < 0) {
|
||||||
|
die("sigaction");
|
||||||
|
}
|
||||||
|
|
||||||
|
sa.sa_sigaction = segvHandler;
|
||||||
|
if (sigaction(SIGSEGV, &sa, NULL) < 0 || sigaction(SIGBUS, &sa, NULL) < 0) {
|
||||||
|
die("sigaction");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (verbose) {
|
||||||
|
printf("calling dlopen\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
handle = dlopen(argv[1], RTLD_NOW | RTLD_GLOBAL);
|
||||||
|
if (handle == NULL) {
|
||||||
|
fprintf(stderr, "%s\n", dlerror());
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (verbose) {
|
||||||
|
printf("calling dlsym\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Start some goroutines.
|
||||||
|
fn = (void(*)(void))dlsym(handle, "RunGoroutines");
|
||||||
|
if (fn == NULL) {
|
||||||
|
fprintf(stderr, "%s\n", dlerror());
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (verbose) {
|
||||||
|
printf("calling RunGoroutines\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
fn();
|
||||||
|
|
||||||
|
// Block SIGIO in this thread to make it more likely that it
|
||||||
|
// will be delivered to a goroutine.
|
||||||
|
|
||||||
|
if (verbose) {
|
||||||
|
printf("calling pthread_sigmask\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (sigemptyset(&mask) < 0) {
|
||||||
|
die("sigemptyset");
|
||||||
|
}
|
||||||
|
if (sigaddset(&mask, SIGIO) < 0) {
|
||||||
|
die("sigaddset");
|
||||||
|
}
|
||||||
|
i = pthread_sigmask(SIG_BLOCK, &mask, NULL);
|
||||||
|
if (i != 0) {
|
||||||
|
fprintf(stderr, "pthread_sigmask: %s\n", strerror(i));
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (verbose) {
|
||||||
|
printf("calling kill\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (kill(0, SIGIO) < 0) {
|
||||||
|
die("kill");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (verbose) {
|
||||||
|
printf("waiting for sigioSeen\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Wait until the signal has been delivered.
|
||||||
|
i = 0;
|
||||||
|
while (!sigioSeen) {
|
||||||
|
if (sched_yield() < 0) {
|
||||||
|
perror("sched_yield");
|
||||||
|
}
|
||||||
|
i++;
|
||||||
|
if (i > 10000) {
|
||||||
|
fprintf(stderr, "looping too long waiting for signal\n");
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (verbose) {
|
||||||
|
printf("calling setjmp\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test that a SIGSEGV on this thread is delivered to us.
|
||||||
|
if (setjmp(jmp) == 0) {
|
||||||
|
if (verbose) {
|
||||||
|
printf("triggering SIGSEGV\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
*nullPointer = '\0';
|
||||||
|
|
||||||
|
fprintf(stderr, "continued after address error\n");
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (verbose) {
|
||||||
|
printf("calling dlsym\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Make sure that a SIGSEGV in Go causes a run-time panic.
|
||||||
|
fn = (void (*)(void))dlsym(handle, "TestSEGV");
|
||||||
|
if (fn == NULL) {
|
||||||
|
fprintf(stderr, "%s\n", dlerror());
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (verbose) {
|
||||||
|
printf("calling TestSEGV\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
fn();
|
||||||
|
|
||||||
|
printf("PASS\n");
|
||||||
|
return 0;
|
||||||
|
}
|
45
misc/cgo/testcshared/src/libgo4/libgo4.go
Normal file
45
misc/cgo/testcshared/src/libgo4/libgo4.go
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
// 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 "C"
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"runtime"
|
||||||
|
)
|
||||||
|
|
||||||
|
// RunGoroutines starts some goroutines that don't do anything.
|
||||||
|
// The idea is to get some threads going, so that a signal will be delivered
|
||||||
|
// to a thread started by Go.
|
||||||
|
//export RunGoroutines
|
||||||
|
func RunGoroutines() {
|
||||||
|
for i := 0; i < 4; i++ {
|
||||||
|
go func() {
|
||||||
|
runtime.LockOSThread()
|
||||||
|
select {}
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var P *byte
|
||||||
|
|
||||||
|
// TestSEGV makes sure that an invalid address turns into a run-time Go panic.
|
||||||
|
//export TestSEGV
|
||||||
|
func TestSEGV() {
|
||||||
|
defer func() {
|
||||||
|
if recover() == nil {
|
||||||
|
fmt.Fprintln(os.Stderr, "no panic from segv")
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
*P = 0
|
||||||
|
fmt.Fprintln(os.Stderr, "continued after segv")
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
}
|
@ -28,9 +28,9 @@ fi
|
|||||||
androidpath=/data/local/tmp/testcshared-$$
|
androidpath=/data/local/tmp/testcshared-$$
|
||||||
|
|
||||||
function cleanup() {
|
function cleanup() {
|
||||||
rm -rf libgo.$libext libgo2.$libext libgo.h testp testp2 testp3 pkg
|
rm -f libgo.$libext libgo2.$libext libgo4.$libext libgo.h libgo4.h
|
||||||
|
rm -f testp testp2 testp3 testp4
|
||||||
rm -rf $(go env GOROOT)/${installdir}
|
rm -rf pkg $(go env GOROOT)/${installdir}
|
||||||
|
|
||||||
if [ "$goos" == "android" ]; then
|
if [ "$goos" == "android" ]; then
|
||||||
adb shell rm -rf $androidpath
|
adb shell rm -rf $androidpath
|
||||||
@ -93,6 +93,8 @@ if [ "$goos" == "android" ]; then
|
|||||||
GOGCCFLAGS="${GOGCCFLAGS} -pie"
|
GOGCCFLAGS="${GOGCCFLAGS} -pie"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
status=0
|
||||||
|
|
||||||
# test0: exported symbols in shared lib are accessible.
|
# test0: exported symbols in shared lib are accessible.
|
||||||
# TODO(iant): using _shared here shouldn't really be necessary.
|
# TODO(iant): using _shared here shouldn't really be necessary.
|
||||||
$(go env CC) ${GOGCCFLAGS} -I ${installdir} -o testp main0.c libgo.$libext
|
$(go env CC) ${GOGCCFLAGS} -I ${installdir} -o testp main0.c libgo.$libext
|
||||||
@ -101,7 +103,7 @@ binpush testp
|
|||||||
output=$(run LD_LIBRARY_PATH=. ./testp)
|
output=$(run LD_LIBRARY_PATH=. ./testp)
|
||||||
if [ "$output" != "PASS" ]; then
|
if [ "$output" != "PASS" ]; then
|
||||||
echo "FAIL test0 got ${output}"
|
echo "FAIL test0 got ${output}"
|
||||||
exit 1
|
status=1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# test1: shared library can be dynamically loaded and exported symbols are accessible.
|
# test1: shared library can be dynamically loaded and exported symbols are accessible.
|
||||||
@ -110,7 +112,7 @@ binpush testp
|
|||||||
output=$(run ./testp ./libgo.$libext)
|
output=$(run ./testp ./libgo.$libext)
|
||||||
if [ "$output" != "PASS" ]; then
|
if [ "$output" != "PASS" ]; then
|
||||||
echo "FAIL test1 got ${output}"
|
echo "FAIL test1 got ${output}"
|
||||||
exit 1
|
status=1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# test2: tests libgo2 which does not export any functions.
|
# test2: tests libgo2 which does not export any functions.
|
||||||
@ -125,7 +127,7 @@ binpush testp2
|
|||||||
output=$(run LD_LIBRARY_PATH=. ./testp2)
|
output=$(run LD_LIBRARY_PATH=. ./testp2)
|
||||||
if [ "$output" != "PASS" ]; then
|
if [ "$output" != "PASS" ]; then
|
||||||
echo "FAIL test2 got ${output}"
|
echo "FAIL test2 got ${output}"
|
||||||
exit 1
|
status=1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# test3: tests main.main is exported on android.
|
# test3: tests main.main is exported on android.
|
||||||
@ -135,7 +137,27 @@ if [ "$goos" == "android" ]; then
|
|||||||
output=$(run ./testp ./libgo.so)
|
output=$(run ./testp ./libgo.so)
|
||||||
if [ "$output" != "PASS" ]; then
|
if [ "$output" != "PASS" ]; then
|
||||||
echo "FAIL test3 got ${output}"
|
echo "FAIL test3 got ${output}"
|
||||||
exit 1
|
status=1
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
echo "ok"
|
|
||||||
|
# test4: tests signal handlers
|
||||||
|
GOPATH=$(pwd) go build -buildmode=c-shared $suffix -o libgo4.$libext libgo4
|
||||||
|
binpush libgo4.$libext
|
||||||
|
$(go env CC) ${GOGCCFLAGS} -pthread -o testp4 main4.c -ldl
|
||||||
|
binpush testp4
|
||||||
|
output=$(run ./testp4 ./libgo4.$libext 2>&1)
|
||||||
|
if test "$output" != "PASS"; then
|
||||||
|
echo "FAIL test4 got ${output}"
|
||||||
|
if test "$goos" != "android"; then
|
||||||
|
echo "re-running test4 in verbose mode"
|
||||||
|
./testp4 ./libgo4.$libext verbose
|
||||||
|
fi
|
||||||
|
status=1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if test $status = 0; then
|
||||||
|
echo "ok"
|
||||||
|
fi
|
||||||
|
|
||||||
|
exit $status
|
||||||
|
@ -7,9 +7,14 @@ package main
|
|||||||
import "fmt"
|
import "fmt"
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
#cgo CFLAGS: -pthread
|
||||||
|
#cgo LDFLAGS: -pthread
|
||||||
|
|
||||||
#include <signal.h>
|
#include <signal.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <pthread.h>
|
||||||
|
|
||||||
int *p;
|
int *p;
|
||||||
static void sigsegv() {
|
static void sigsegv() {
|
||||||
@ -18,16 +23,65 @@ static void sigsegv() {
|
|||||||
exit(2);
|
exit(2);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void sighandler(int signum) {
|
static void segvhandler(int signum) {
|
||||||
if (signum == SIGSEGV) {
|
if (signum == SIGSEGV) {
|
||||||
exit(0); // success
|
exit(0); // success
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static volatile sig_atomic_t sigioSeen;
|
||||||
|
|
||||||
|
// Use up some stack space.
|
||||||
|
static void recur(int i, char *p) {
|
||||||
|
char a[1024];
|
||||||
|
|
||||||
|
*p = '\0';
|
||||||
|
if (i > 0) {
|
||||||
|
recur(i - 1, a);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void iohandler(int signum) {
|
||||||
|
char a[1024];
|
||||||
|
|
||||||
|
recur(4, a);
|
||||||
|
sigioSeen = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void* sigioThread(void* arg __attribute__ ((unused))) {
|
||||||
|
raise(SIGIO);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void sigioOnThread() {
|
||||||
|
pthread_t tid;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
pthread_create(&tid, NULL, sigioThread, NULL);
|
||||||
|
pthread_join(tid, NULL);
|
||||||
|
|
||||||
|
// Wait until the signal has been delivered.
|
||||||
|
i = 0;
|
||||||
|
while (!sigioSeen) {
|
||||||
|
if (sched_yield() < 0) {
|
||||||
|
perror("sched_yield");
|
||||||
|
}
|
||||||
|
i++;
|
||||||
|
if (i > 10000) {
|
||||||
|
fprintf(stderr, "looping too long waiting for signal\n");
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static void __attribute__ ((constructor)) sigsetup(void) {
|
static void __attribute__ ((constructor)) sigsetup(void) {
|
||||||
struct sigaction act;
|
struct sigaction act;
|
||||||
act.sa_handler = &sighandler;
|
|
||||||
sigaction(SIGSEGV, &act, 0);
|
memset(&act, 0, sizeof act);
|
||||||
|
act.sa_handler = segvhandler;
|
||||||
|
sigaction(SIGSEGV, &act, NULL);
|
||||||
|
|
||||||
|
act.sa_handler = iohandler;
|
||||||
|
sigaction(SIGIO, &act, NULL);
|
||||||
}
|
}
|
||||||
*/
|
*/
|
||||||
import "C"
|
import "C"
|
||||||
|
@ -156,14 +156,19 @@ 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
|
SIGSETXID signals (which are used only on GNU/Linux), it will turn on
|
||||||
the SA_ONSTACK flag and otherwise keep the signal handler.
|
the SA_ONSTACK flag and otherwise keep the signal handler.
|
||||||
|
|
||||||
For other signals listed above, the Go runtime will install a signal
|
For the synchronous signals, the Go runtime will install a signal
|
||||||
handler. It will save any existing signal handler. If a synchronous
|
handler. It will save any existing signal handler. If a synchronous
|
||||||
signal arrives while executing non-Go code, the Go runtime will invoke
|
signal arrives while executing non-Go code, the Go runtime will invoke
|
||||||
the existing signal handler instead of the Go signal handler.
|
the existing signal handler instead of the Go signal handler.
|
||||||
|
|
||||||
If a signal is delivered to a non-Go thread, it will act as described
|
Go code built with -buildmode=c-archive or -buildmode=c-shared will
|
||||||
above, except that if there is an existing non-Go signal handler, that
|
not install any other signal handlers. TODO: Describe Notify behavior.
|
||||||
handler will be installed before raising the signal.
|
|
||||||
|
Go code built otherwise will install a signal handler for the
|
||||||
|
asynchronous signals listed above, and save any existing signal
|
||||||
|
handler. If a signal is delivered to a non-Go thread, it will act as
|
||||||
|
described above, except that if there is an existing non-Go signal
|
||||||
|
handler, that handler will be installed before raising the signal.
|
||||||
|
|
||||||
Windows
|
Windows
|
||||||
|
|
||||||
|
@ -65,6 +65,14 @@ func initsig() {
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// When built using c-archive or c-shared, only
|
||||||
|
// install signal handlers for synchronous signals.
|
||||||
|
// Set SA_ONSTACK for other signals if necessary.
|
||||||
|
if (isarchive || islibrary) && t.flags&_SigPanic == 0 {
|
||||||
|
setsigstack(i)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
t.flags |= _SigHandling
|
t.flags |= _SigHandling
|
||||||
setsig(i, funcPC(sighandler), true)
|
setsig(i, funcPC(sighandler), true)
|
||||||
}
|
}
|
||||||
|
@ -16,8 +16,6 @@ func sigfwd(fn uintptr, sig uint32, info *siginfo, ctx unsafe.Pointer)
|
|||||||
// signal was forwarded.
|
// signal was forwarded.
|
||||||
//go:nosplit
|
//go:nosplit
|
||||||
func sigfwdgo(sig uint32, info *siginfo, ctx unsafe.Pointer) bool {
|
func sigfwdgo(sig uint32, info *siginfo, ctx unsafe.Pointer) bool {
|
||||||
g := getg()
|
|
||||||
c := &sigctxt{info, ctx}
|
|
||||||
if sig >= uint32(len(sigtable)) {
|
if sig >= uint32(len(sigtable)) {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
@ -28,13 +26,22 @@ func sigfwdgo(sig uint32, info *siginfo, ctx unsafe.Pointer) bool {
|
|||||||
if fwdFn == _SIG_DFL {
|
if fwdFn == _SIG_DFL {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// If we aren't handling the signal, forward it.
|
||||||
|
if flags&_SigHandling == 0 {
|
||||||
|
sigfwd(fwdFn, sig, info, ctx)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
// Only forward synchronous signals.
|
// Only forward synchronous signals.
|
||||||
|
c := &sigctxt{info, ctx}
|
||||||
if c.sigcode() == _SI_USER || flags&_SigPanic == 0 {
|
if c.sigcode() == _SI_USER || flags&_SigPanic == 0 {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
// Determine if the signal occurred inside Go code. We test that:
|
// Determine if the signal occurred inside Go code. We test that:
|
||||||
// (1) we were in a goroutine (i.e., m.curg != nil), and
|
// (1) we were in a goroutine (i.e., m.curg != nil), and
|
||||||
// (2) we weren't in CGO (i.e., m.curg.syscallsp == 0).
|
// (2) we weren't in CGO (i.e., m.curg.syscallsp == 0).
|
||||||
|
g := getg()
|
||||||
if g != nil && g.m != nil && g.m.curg != nil && g.m.curg.syscallsp == 0 {
|
if g != nil && g.m != nil && g.m.curg != nil && g.m.curg.syscallsp == 0 {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
@ -214,10 +214,14 @@ TEXT runtime·sigaction(SB),NOSPLIT,$0-24
|
|||||||
|
|
||||||
TEXT runtime·sigfwd(SB),NOSPLIT,$0-32
|
TEXT runtime·sigfwd(SB),NOSPLIT,$0-32
|
||||||
MOVQ fn+0(FP), AX
|
MOVQ fn+0(FP), AX
|
||||||
MOVQ sig+8(FP), DI
|
MOVL sig+8(FP), DI
|
||||||
MOVQ info+16(FP), SI
|
MOVQ info+16(FP), SI
|
||||||
MOVQ ctx+24(FP), DX
|
MOVQ ctx+24(FP), DX
|
||||||
|
MOVQ SP, BP
|
||||||
|
SUBQ $64, SP
|
||||||
|
ANDQ $~15, SP // alignment for x86_64 ABI
|
||||||
CALL AX
|
CALL AX
|
||||||
|
MOVQ BP, SP
|
||||||
RET
|
RET
|
||||||
|
|
||||||
TEXT runtime·sigreturn(SB),NOSPLIT,$0-12
|
TEXT runtime·sigreturn(SB),NOSPLIT,$0-12
|
||||||
|
@ -339,7 +339,11 @@ TEXT runtime·sigfwd(SB),NOSPLIT,$0-16
|
|||||||
MOVW info+8(FP), R1
|
MOVW info+8(FP), R1
|
||||||
MOVW ctx+12(FP), R2
|
MOVW ctx+12(FP), R2
|
||||||
MOVW fn+0(FP), R11
|
MOVW fn+0(FP), R11
|
||||||
|
MOVW R13, R4
|
||||||
|
SUB $24, R13
|
||||||
|
BIC $0x7, R13 // alignment for ELF ABI
|
||||||
BL (R11)
|
BL (R11)
|
||||||
|
MOVW R4, R13
|
||||||
RET
|
RET
|
||||||
|
|
||||||
TEXT runtime·sigtramp(SB),NOSPLIT,$12
|
TEXT runtime·sigtramp(SB),NOSPLIT,$12
|
||||||
|
Loading…
Reference in New Issue
Block a user