...

Text file src/cmd/cgo/internal/testcshared/testdata/main4.c

Documentation: cmd/cgo/internal/testcshared/testdata

     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 that a signal handler that uses up stack space does not crash
     6// if the signal is delivered to a thread running a goroutine.
     7// This is a lot like ../testcarchive/main2.c.
     8
     9#include <setjmp.h>
    10#include <signal.h>
    11#include <stddef.h>
    12#include <stdio.h>
    13#include <stdlib.h>
    14#include <string.h>
    15#include <sys/types.h>
    16#include <unistd.h>
    17#include <sched.h>
    18#include <time.h>
    19#include <dlfcn.h>
    20
    21static void die(const char* msg) {
    22	perror(msg);
    23	exit(EXIT_FAILURE);
    24}
    25
    26static volatile sig_atomic_t sigioSeen;
    27
    28// Use up some stack space.
    29static 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.
    39static void ioHandler(int signo, siginfo_t* info, void* ctxt) {
    40	char a[1024];
    41
    42	recur(4, a);
    43	sigioSeen = 1;
    44}
    45
    46static jmp_buf jmp;
    47static char* nullPointer;
    48
    49// Signal handler for SIGSEGV on a C thread.
    50static 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
    73int main(int argc, char** argv) {
    74	int verbose;
    75	struct sigaction sa;
    76	void* handle;
    77	void (*fn)(void);
    78	sigset_t mask;
    79	int i;
    80	struct timespec ts;
    81
    82	verbose = argc > 2;
    83	setvbuf(stdout, NULL, _IONBF, 0);
    84
    85	// Call setsid so that we can use kill(0, SIGIO) below.
    86	// Don't check the return value so that this works both from
    87	// a job control shell and from a shell script.
    88	setsid();
    89
    90	if (verbose) {
    91		fprintf(stderr, "calling sigaction\n");
    92	}
    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	if (verbose) {
   110		fprintf(stderr, "calling dlopen\n");
   111	}
   112
   113	handle = dlopen(argv[1], RTLD_NOW | RTLD_GLOBAL);
   114	if (handle == NULL) {
   115		fprintf(stderr, "%s\n", dlerror());
   116		exit(EXIT_FAILURE);
   117	}
   118
   119	if (verbose) {
   120		fprintf(stderr, "calling dlsym\n");
   121	}
   122
   123	// Start some goroutines.
   124	fn = (void(*)(void))dlsym(handle, "RunGoroutines");
   125	if (fn == NULL) {
   126		fprintf(stderr, "%s\n", dlerror());
   127		exit(EXIT_FAILURE);
   128	}
   129
   130	if (verbose) {
   131		fprintf(stderr, "calling RunGoroutines\n");
   132	}
   133
   134	fn();
   135
   136	// Block SIGIO in this thread to make it more likely that it
   137	// will be delivered to a goroutine.
   138
   139	if (verbose) {
   140		fprintf(stderr, "calling pthread_sigmask\n");
   141	}
   142
   143	if (sigemptyset(&mask) < 0) {
   144		die("sigemptyset");
   145	}
   146	if (sigaddset(&mask, SIGIO) < 0) {
   147		die("sigaddset");
   148	}
   149	i = pthread_sigmask(SIG_BLOCK, &mask, NULL);
   150	if (i != 0) {
   151		fprintf(stderr, "pthread_sigmask: %s\n", strerror(i));
   152		exit(EXIT_FAILURE);
   153	}
   154
   155	if (verbose) {
   156		fprintf(stderr, "calling kill\n");
   157	}
   158
   159	if (kill(0, SIGIO) < 0) {
   160		die("kill");
   161	}
   162
   163	if (verbose) {
   164		fprintf(stderr, "waiting for sigioSeen\n");
   165	}
   166
   167	// Wait until the signal has been delivered.
   168	i = 0;
   169	while (!sigioSeen) {
   170		ts.tv_sec = 0;
   171		ts.tv_nsec = 1000000;
   172		nanosleep(&ts, NULL);
   173		i++;
   174		if (i > 5000) {
   175			fprintf(stderr, "looping too long waiting for signal\n");
   176			exit(EXIT_FAILURE);
   177		}
   178	}
   179
   180	if (verbose) {
   181		fprintf(stderr, "calling setjmp\n");
   182	}
   183
   184	// Test that a SIGSEGV on this thread is delivered to us.
   185	if (setjmp(jmp) == 0) {
   186		if (verbose) {
   187			fprintf(stderr, "triggering SIGSEGV\n");
   188		}
   189
   190		*nullPointer = '\0';
   191
   192		fprintf(stderr, "continued after address error\n");
   193		exit(EXIT_FAILURE);
   194	}
   195
   196	if (verbose) {
   197		fprintf(stderr, "calling dlsym\n");
   198	}
   199
   200	// Make sure that a SIGSEGV in Go causes a run-time panic.
   201	fn = (void (*)(void))dlsym(handle, "TestSEGV");
   202	if (fn == NULL) {
   203		fprintf(stderr, "%s\n", dlerror());
   204		exit(EXIT_FAILURE);
   205	}
   206
   207	if (verbose) {
   208		fprintf(stderr, "calling TestSEGV\n");
   209	}
   210
   211	fn();
   212
   213	printf("PASS\n");
   214	return 0;
   215}

View as plain text