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 ../testcshared/main4.c.
7
8#include <setjmp.h>
9#include <signal.h>
10#include <stdarg.h>
11#include <stddef.h>
12#include <stdio.h>
13#include <stdint.h>
14#include <stdlib.h>
15#include <string.h>
16#include <sys/types.h>
17#include <unistd.h>
18#include <sched.h>
19#include <time.h>
20#include <errno.h>
21
22#include "libgo2.h"
23
24static void die(const char* msg) {
25 perror(msg);
26 exit(EXIT_FAILURE);
27}
28
29static volatile sig_atomic_t sigioSeen;
30static volatile sig_atomic_t sigpipeSeen;
31
32// Use up some stack space.
33static void recur(int i, char *p) {
34 char a[1024];
35
36 *p = '\0';
37 if (i > 0) {
38 recur(i - 1, a);
39 }
40}
41
42static void pipeHandler(int signo, siginfo_t* info, void* ctxt) {
43 sigpipeSeen = 1;
44}
45
46// Signal handler that uses up more stack space than a goroutine will have.
47static void ioHandler(int signo, siginfo_t* info, void* ctxt) {
48 char a[1024];
49
50 recur(4, a);
51 sigioSeen = 1;
52}
53
54static jmp_buf jmp;
55static char* nullPointer;
56
57// An arbitrary function which requires proper stack alignment; see
58// http://golang.org/issue/17641.
59static void callWithVarargs(void* dummy, ...) {
60 va_list args;
61 va_start(args, dummy);
62 va_end(args);
63}
64
65// Signal handler for SIGSEGV on a C thread.
66static void segvHandler(int signo, siginfo_t* info, void* ctxt) {
67 sigset_t mask;
68 int i;
69
70 // Call an arbitrary function that requires the stack to be properly aligned.
71 callWithVarargs("dummy arg", 3.1415);
72
73 if (sigemptyset(&mask) < 0) {
74 die("sigemptyset");
75 }
76 if (sigaddset(&mask, SIGSEGV) < 0) {
77 die("sigaddset");
78 }
79 i = sigprocmask(SIG_UNBLOCK, &mask, NULL);
80 if (i != 0) {
81 fprintf(stderr, "sigprocmask: %s\n", strerror(i));
82 exit(EXIT_FAILURE);
83 }
84
85 // Don't try this at home.
86 longjmp(jmp, signo);
87
88 // We should never get here.
89 abort();
90}
91
92// Set up the signal handlers in a high priority constructor,
93// so that they are installed before the Go code starts.
94
95static void init(void) __attribute__ ((constructor (200)));
96
97static void init() {
98 struct sigaction sa;
99
100 memset(&sa, 0, sizeof sa);
101 sa.sa_sigaction = ioHandler;
102 if (sigemptyset(&sa.sa_mask) < 0) {
103 die("sigemptyset");
104 }
105 sa.sa_flags = SA_SIGINFO;
106 if (sigaction(SIGIO, &sa, NULL) < 0) {
107 die("sigaction");
108 }
109
110 sa.sa_sigaction = segvHandler;
111 if (sigaction(SIGSEGV, &sa, NULL) < 0 || sigaction(SIGBUS, &sa, NULL) < 0) {
112 die("sigaction");
113 }
114
115 sa.sa_sigaction = pipeHandler;
116 if (sigaction(SIGPIPE, &sa, NULL) < 0) {
117 die("sigaction");
118 }
119}
120
121int main(int argc, char** argv) {
122 int verbose;
123 sigset_t mask;
124 int i;
125 struct timespec ts;
126 int darwin;
127
128 darwin = atoi(argv[1]);
129
130 verbose = argc > 2;
131
132 setvbuf(stdout, NULL, _IONBF, 0);
133
134 // Call setsid so that we can use kill(0, SIGIO) below.
135 // Don't check the return value so that this works both from
136 // a job control shell and from a shell script.
137 setsid();
138
139 if (verbose) {
140 printf("calling RunGoroutines\n");
141 }
142
143 RunGoroutines();
144
145 // Block SIGIO in this thread to make it more likely that it
146 // will be delivered to a goroutine.
147
148 if (verbose) {
149 printf("calling pthread_sigmask\n");
150 }
151
152 if (sigemptyset(&mask) < 0) {
153 die("sigemptyset");
154 }
155 if (sigaddset(&mask, SIGIO) < 0) {
156 die("sigaddset");
157 }
158 i = pthread_sigmask(SIG_BLOCK, &mask, NULL);
159 if (i != 0) {
160 fprintf(stderr, "pthread_sigmask: %s\n", strerror(i));
161 exit(EXIT_FAILURE);
162 }
163
164 if (verbose) {
165 printf("calling kill\n");
166 }
167
168 if (kill(0, SIGIO) < 0) {
169 die("kill");
170 }
171
172 if (verbose) {
173 printf("waiting for sigioSeen\n");
174 }
175
176 // Wait until the signal has been delivered.
177 i = 0;
178 while (!sigioSeen) {
179 ts.tv_sec = 0;
180 ts.tv_nsec = 1000000;
181 nanosleep(&ts, NULL);
182 i++;
183 if (i > 5000) {
184 fprintf(stderr, "looping too long waiting for SIGIO\n");
185 exit(EXIT_FAILURE);
186 }
187 }
188
189 if (verbose) {
190 printf("provoking SIGPIPE\n");
191 }
192
193 // SIGPIPE is never forwarded on Darwin, see golang.org/issue/33384.
194 if (!darwin) {
195 GoRaiseSIGPIPE();
196
197 if (verbose) {
198 printf("waiting for sigpipeSeen\n");
199 }
200
201 // Wait until the signal has been delivered.
202 i = 0;
203 while (!sigpipeSeen) {
204 ts.tv_sec = 0;
205 ts.tv_nsec = 1000000;
206 nanosleep(&ts, NULL);
207 i++;
208 if (i > 5000) {
209 fprintf(stderr, "looping too long waiting for SIGPIPE\n");
210 exit(EXIT_FAILURE);
211 }
212 }
213 }
214
215 if (verbose) {
216 printf("calling setjmp\n");
217 }
218
219 // Test that a SIGSEGV on this thread is delivered to us.
220 if (setjmp(jmp) == 0) {
221 if (verbose) {
222 printf("triggering SIGSEGV\n");
223 }
224
225 *nullPointer = '\0';
226
227 fprintf(stderr, "continued after address error\n");
228 exit(EXIT_FAILURE);
229 }
230
231 if (verbose) {
232 printf("calling TestSEGV\n");
233 }
234
235 TestSEGV();
236
237 printf("PASS\n");
238 return 0;
239}
View as plain text