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