Skip to content

Commit fbdfa99

Browse files
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 <[email protected]> Run-TryBot: Ian Lance Taylor <[email protected]> TryBot-Result: Gobot Gobot <[email protected]>
1 parent 32cf985 commit fbdfa99

File tree

12 files changed

+635
-21
lines changed

12 files changed

+635
-21
lines changed

misc/cgo/testcarchive/main2.c

+185
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,185 @@
1+
// Copyright 2015 The Go Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style
3+
// license that can be found in the LICENSE file.
4+
5+
// Test installing a signal handler before the Go code starts.
6+
// This is a lot like misc/cgo/testcshared/main4.c.
7+
8+
#include <setjmp.h>
9+
#include <signal.h>
10+
#include <stddef.h>
11+
#include <stdio.h>
12+
#include <stdlib.h>
13+
#include <string.h>
14+
#include <sys/types.h>
15+
#include <unistd.h>
16+
#include <sched.h>
17+
#include <time.h>
18+
19+
#include "libgo2.h"
20+
21+
static void die(const char* msg) {
22+
perror(msg);
23+
exit(EXIT_FAILURE);
24+
}
25+
26+
static volatile sig_atomic_t sigioSeen;
27+
28+
// Use up some stack space.
29+
static void recur(int i, char *p) {
30+
char a[1024];
31+
32+
*p = '\0';
33+
if (i > 0) {
34+
recur(i - 1, a);
35+
}
36+
}
37+
38+
// Signal handler that uses up more stack space than a goroutine will have.
39+
static void ioHandler(int signo, siginfo_t* info, void* ctxt) {
40+
char a[1024];
41+
42+
recur(4, a);
43+
sigioSeen = 1;
44+
}
45+
46+
static jmp_buf jmp;
47+
static char* nullPointer;
48+
49+
// Signal handler for SIGSEGV on a C thread.
50+
static void segvHandler(int signo, siginfo_t* info, void* ctxt) {
51+
sigset_t mask;
52+
int i;
53+
54+
if (sigemptyset(&mask) < 0) {
55+
die("sigemptyset");
56+
}
57+
if (sigaddset(&mask, SIGSEGV) < 0) {
58+
die("sigaddset");
59+
}
60+
i = sigprocmask(SIG_UNBLOCK, &mask, NULL);
61+
if (i != 0) {
62+
fprintf(stderr, "sigprocmask: %s\n", strerror(i));
63+
exit(EXIT_FAILURE);
64+
}
65+
66+
// Don't try this at home.
67+
longjmp(jmp, signo);
68+
69+
// We should never get here.
70+
abort();
71+
}
72+
73+
// Set up the signal handlers in a high priority constructor,
74+
// so that they are installed before the Go code starts.
75+
76+
static void init(void) __attribute__ ((constructor (200)));
77+
78+
static void init() {
79+
struct sigaction sa;
80+
81+
memset(&sa, 0, sizeof sa);
82+
sa.sa_sigaction = ioHandler;
83+
if (sigemptyset(&sa.sa_mask) < 0) {
84+
die("sigemptyset");
85+
}
86+
sa.sa_flags = SA_SIGINFO;
87+
if (sigaction(SIGIO, &sa, NULL) < 0) {
88+
die("sigaction");
89+
}
90+
91+
sa.sa_sigaction = segvHandler;
92+
if (sigaction(SIGSEGV, &sa, NULL) < 0 || sigaction(SIGBUS, &sa, NULL) < 0) {
93+
die("sigaction");
94+
}
95+
96+
}
97+
98+
int main(int argc, char** argv) {
99+
int verbose;
100+
sigset_t mask;
101+
int i;
102+
103+
verbose = argc > 1;
104+
setvbuf(stdout, NULL, _IONBF, 0);
105+
106+
// Call setsid so that we can use kill(0, SIGIO) below.
107+
// Don't check the return value so that this works both from
108+
// a job control shell and from a shell script.
109+
setsid();
110+
111+
if (verbose) {
112+
printf("calling RunGoroutines\n");
113+
}
114+
115+
RunGoroutines();
116+
117+
// Block SIGIO in this thread to make it more likely that it
118+
// will be delivered to a goroutine.
119+
120+
if (verbose) {
121+
printf("calling pthread_sigmask\n");
122+
}
123+
124+
if (sigemptyset(&mask) < 0) {
125+
die("sigemptyset");
126+
}
127+
if (sigaddset(&mask, SIGIO) < 0) {
128+
die("sigaddset");
129+
}
130+
i = pthread_sigmask(SIG_BLOCK, &mask, NULL);
131+
if (i != 0) {
132+
fprintf(stderr, "pthread_sigmask: %s\n", strerror(i));
133+
exit(EXIT_FAILURE);
134+
}
135+
136+
if (verbose) {
137+
printf("calling kill\n");
138+
}
139+
140+
if (kill(0, SIGIO) < 0) {
141+
die("kill");
142+
}
143+
144+
if (verbose) {
145+
printf("waiting for sigioSeen\n");
146+
}
147+
148+
// Wait until the signal has been delivered.
149+
i = 0;
150+
while (!sigioSeen) {
151+
if (sched_yield() < 0) {
152+
perror("sched_yield");
153+
}
154+
i++;
155+
if (i > 10000) {
156+
fprintf(stderr, "looping too long waiting for signal\n");
157+
exit(EXIT_FAILURE);
158+
}
159+
}
160+
161+
if (verbose) {
162+
printf("calling setjmp\n");
163+
}
164+
165+
// Test that a SIGSEGV on this thread is delivered to us.
166+
if (setjmp(jmp) == 0) {
167+
if (verbose) {
168+
printf("triggering SIGSEGV\n");
169+
}
170+
171+
*nullPointer = '\0';
172+
173+
fprintf(stderr, "continued after address error\n");
174+
exit(EXIT_FAILURE);
175+
}
176+
177+
if (verbose) {
178+
printf("calling TestSEGV\n");
179+
}
180+
181+
TestSEGV();
182+
183+
printf("PASS\n");
184+
return 0;
185+
}
+45
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
// Copyright 2015 The Go Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style
3+
// license that can be found in the LICENSE file.
4+
5+
package main
6+
7+
import "C"
8+
9+
import (
10+
"fmt"
11+
"os"
12+
"runtime"
13+
)
14+
15+
// RunGoroutines starts some goroutines that don't do anything.
16+
// The idea is to get some threads going, so that a signal will be delivered
17+
// to a thread started by Go.
18+
//export RunGoroutines
19+
func RunGoroutines() {
20+
for i := 0; i < 4; i++ {
21+
go func() {
22+
runtime.LockOSThread()
23+
select {}
24+
}()
25+
}
26+
}
27+
28+
var P *byte
29+
30+
// TestSEGV makes sure that an invalid address turns into a run-time Go panic.
31+
//export TestSEGV
32+
func TestSEGV() {
33+
defer func() {
34+
if recover() == nil {
35+
fmt.Fprintln(os.Stderr, "no panic from segv")
36+
os.Exit(1)
37+
}
38+
}()
39+
*P = 0
40+
fmt.Fprintln(os.Stderr, "continued after segv")
41+
os.Exit(1)
42+
}
43+
44+
func main() {
45+
}

misc/cgo/testcarchive/test.bash

+24-3
Original file line numberDiff line numberDiff line change
@@ -23,22 +23,43 @@ fi
2323

2424
rm -rf libgo.a libgo.h testp pkg
2525

26+
status=0
27+
2628
# Installing first will create the header files we want.
2729

2830
GOPATH=$(pwd) go install -buildmode=c-archive libgo
2931
$(go env CC) $(go env GOGCCFLAGS) $ccargs -o testp main.c pkg/$(go env GOOS)_$(go env GOARCH)/libgo.a
30-
$bin arg1 arg2
32+
if ! $bin arg1 arg2; then
33+
echo "FAIL test1"
34+
status=1
35+
fi
3136
rm -f libgo.a libgo.h testp
3237

3338
# Test building libgo other than installing it.
3439
# Header files are now present.
3540

3641
GOPATH=$(pwd) go build -buildmode=c-archive src/libgo/libgo.go
3742
$(go env CC) $(go env GOGCCFLAGS) $ccargs -o testp main.c libgo.a
38-
$bin arg1 arg2
43+
if ! $bin arg1 arg2; then
44+
echo "FAIL test2"
45+
status=1
46+
fi
3947
rm -f libgo.a libgo.h testp
4048

4149
GOPATH=$(pwd) go build -buildmode=c-archive -o libgo.a libgo
4250
$(go env CC) $(go env GOGCCFLAGS) $ccargs -o testp main.c libgo.a
43-
$bin arg1 arg2
51+
if ! $bin arg1 arg2; then
52+
echo "FAIL test3"
53+
status=1
54+
fi
4455
rm -rf libgo.a libgo.h testp pkg
56+
57+
GOPATH=$(pwd) go build -buildmode=c-archive -o libgo2.a libgo2
58+
$(go env CC) $(go env GOGCCFLAGS) $ccargs -o testp main2.c libgo2.a
59+
if ! $bin; then
60+
echo "FAIL test4"
61+
status=1
62+
fi
63+
rm -rf libgo2.a libgo2.h testp pkg
64+
65+
exit $status

0 commit comments

Comments
 (0)