diff options
author | Ian Lance Taylor <ian@gcc.gnu.org> | 2011-12-21 22:24:47 +0000 |
---|---|---|
committer | Ian Lance Taylor <ian@gcc.gnu.org> | 2011-12-21 22:24:47 +0000 |
commit | a6dcb7d46549faa48f782e7b154c6103e0406b58 (patch) | |
tree | b581b11b17c97821c10249103de061834481481d /libgo/runtime/go-signal.c | |
parent | e0d2f0301635c6c3165c159e7d18e5bc07dc7325 (diff) | |
download | gcc-a6dcb7d46549faa48f782e7b154c6103e0406b58.zip gcc-a6dcb7d46549faa48f782e7b154c6103e0406b58.tar.gz gcc-a6dcb7d46549faa48f782e7b154c6103e0406b58.tar.bz2 |
runtime: Catch signals on altstack, disable splitstack signal blocking.
From-SVN: r182607
Diffstat (limited to 'libgo/runtime/go-signal.c')
-rw-r--r-- | libgo/runtime/go-signal.c | 415 |
1 files changed, 293 insertions, 122 deletions
diff --git a/libgo/runtime/go-signal.c b/libgo/runtime/go-signal.c index eb624097..e5a790a 100644 --- a/libgo/runtime/go-signal.c +++ b/libgo/runtime/go-signal.c @@ -17,109 +17,133 @@ #define SA_RESTART 0 #endif -/* What to do for a signal. */ +#ifdef USING_SPLIT_STACK -struct sigtab -{ - /* Signal number. */ - int sig; - /* Nonzero if the signal should be caught. */ - _Bool catch; - /* Nonzero if the signal should be queued. */ - _Bool queue; - /* Nonzero if the signal should be ignored. */ - _Bool ignore; - /* Nonzero if we should restart system calls. */ - _Bool restart; -}; +extern void __splitstack_getcontext(void *context[10]); -/* What to do for signals. */ +extern void __splitstack_setcontext(void *context[10]); -static struct sigtab signals[] = -{ - { SIGHUP, 0, 1, 0, 1 }, - { SIGINT, 0, 1, 0, 1 }, - { SIGQUIT, 0, 1, 0, 1 }, - { SIGALRM, 0, 1, 1, 1 }, - { SIGTERM, 0, 1, 0, 1 }, +#endif + +#define C SigCatch +#define I SigIgnore +#define R SigRestart +#define Q SigQueue +#define P SigPanic + +/* Signal actions. This collects the sigtab tables for several + different targets from the master library. SIGKILL, SIGCONT, and + SIGSTOP are not listed, as we don't want to set signal handlers for + them. */ + +SigTab runtime_sigtab[] = { +#ifdef SIGHUP + { SIGHUP, Q + R }, +#endif +#ifdef SIGINT + { SIGINT, Q + R }, +#endif +#ifdef SIGQUIT + { SIGQUIT, C }, +#endif #ifdef SIGILL - { SIGILL, 1, 0, 0, 0 }, + { SIGILL, C }, #endif #ifdef SIGTRAP - { SIGTRAP, 1, 0, 0, 0 }, + { SIGTRAP, C }, #endif #ifdef SIGABRT - { SIGABRT, 1, 0, 0, 0 }, + { SIGABRT, C }, #endif #ifdef SIGBUS - { SIGBUS, 1, 0, 0, 0 }, + { SIGBUS, C + P }, #endif #ifdef SIGFPE - { SIGFPE, 1, 0, 0, 0 }, + { SIGFPE, C + P }, #endif #ifdef SIGUSR1 - { SIGUSR1, 0, 1, 1, 1 }, + { SIGUSR1, Q + I + R }, #endif #ifdef SIGSEGV - { SIGSEGV, 1, 0, 0, 0 }, + { SIGSEGV, C + P }, #endif #ifdef SIGUSR2 - { SIGUSR2, 0, 1, 1, 1 }, + { SIGUSR2, Q + I + R }, #endif #ifdef SIGPIPE - { SIGPIPE, 0, 0, 1, 0 }, + { SIGPIPE, I }, +#endif +#ifdef SIGALRM + { SIGALRM, Q + I + R }, +#endif +#ifdef SIGTERM + { SIGTERM, Q + R }, #endif #ifdef SIGSTKFLT - { SIGSTKFLT, 1, 0, 0, 0 }, + { SIGSTKFLT, C }, #endif #ifdef SIGCHLD - { SIGCHLD, 0, 1, 1, 1 }, + { SIGCHLD, Q + I + R }, #endif #ifdef SIGTSTP - { SIGTSTP, 0, 1, 1, 1 }, + { SIGTSTP, Q + I + R }, #endif #ifdef SIGTTIN - { SIGTTIN, 0, 1, 1, 1 }, + { SIGTTIN, Q + I + R }, #endif #ifdef SIGTTOU - { SIGTTOU, 0, 1, 1, 1 }, + { SIGTTOU, Q + I + R }, #endif #ifdef SIGURG - { SIGURG, 0, 1, 1, 1 }, + { SIGURG, Q + I + R }, #endif #ifdef SIGXCPU - { SIGXCPU, 0, 1, 1, 1 }, + { SIGXCPU, Q + I + R }, #endif #ifdef SIGXFSZ - { SIGXFSZ, 0, 1, 1, 1 }, + { SIGXFSZ, Q + I + R }, #endif -#ifdef SIGVTARLM - { SIGVTALRM, 0, 1, 1, 1 }, +#ifdef SIGVTALRM + { SIGVTALRM, Q + I + R }, #endif #ifdef SIGPROF - { SIGPROF, 0, 1, 1, 1 }, + { SIGPROF, Q + I + R }, #endif #ifdef SIGWINCH - { SIGWINCH, 0, 1, 1, 1 }, + { SIGWINCH, Q + I + R }, #endif #ifdef SIGIO - { SIGIO, 0, 1, 1, 1 }, + { SIGIO, Q + I + R }, #endif #ifdef SIGPWR - { SIGPWR, 0, 1, 1, 1 }, + { SIGPWR, Q + I + R }, #endif #ifdef SIGSYS - { SIGSYS, 1, 0, 0, 0 }, + { SIGSYS, C }, #endif - { -1, 0, 0, 0, 0 } +#ifdef SIGEMT + { SIGEMT, C }, +#endif +#ifdef SIGINFO + { SIGINFO, Q + I + R }, +#endif +#ifdef SIGTHR + { SIGTHR, Q + I + R }, +#endif + { -1, 0 } }; +#undef C +#undef I +#undef R +#undef Q +#undef P -/* The Go signal handler. */ +/* Handle a signal, for cases where we don't panic. We can split the + stack here. */ static void -sighandler (int sig) +sig_handler (int sig) { - const char *msg; int i; #ifdef SIGPROF @@ -131,99 +155,223 @@ sighandler (int sig) } #endif - /* FIXME: Should check siginfo for more information when - available. */ - msg = NULL; - switch (sig) + for (i = 0; runtime_sigtab[i].sig != -1; ++i) { -#ifdef SIGILL - case SIGILL: - msg = "illegal instruction"; - break; -#endif + struct sigaction sa; -#ifdef SIGBUS - case SIGBUS: - msg = "invalid memory address or nil pointer dereference"; - break; -#endif + if (runtime_sigtab[i].sig != sig) + continue; -#ifdef SIGFPE - case SIGFPE: - msg = "integer divide by zero or floating point error"; - break; -#endif + if ((runtime_sigtab[i].flags & SigQueue) != 0) + { + if (__go_sigsend (sig) + || (runtime_sigtab[sig].flags & SigIgnore) != 0) + return; + runtime_exit (2); // SIGINT, SIGTERM, etc + } -#ifdef SIGSEGV - case SIGSEGV: - msg = "invalid memory address or nil pointer dereference"; - break; -#endif + if (runtime_panicking) + runtime_exit (2); + runtime_panicking = 1; + + /* We should do a stack backtrace here. Until we can do that, + we reraise the signal in order to get a slightly better + report from the shell. */ + + memset (&sa, 0, sizeof sa); - default: - break; + sa.sa_handler = SIG_DFL; + + i = sigemptyset (&sa.sa_mask); + __go_assert (i == 0); + + if (sigaction (sig, &sa, NULL) != 0) + abort (); + + raise (sig); + + runtime_exit (2); } - if (msg != NULL) + __builtin_unreachable (); +} + +/* The start of handling a signal which panics. */ + +static void +sig_panic_leadin (int sig) +{ + int i; + sigset_t clear; + + if (runtime_m ()->mallocing) { - sigset_t clear; + runtime_printf ("caught signal while mallocing: %d\n", sig); + runtime_throw ("caught signal while mallocing"); + } - if (runtime_m()->mallocing) - { - fprintf (stderr, "caught signal while mallocing: %s\n", msg); - __go_assert (0); - } + /* The signal handler blocked signals; unblock them. */ + i = sigfillset (&clear); + __go_assert (i == 0); + i = sigprocmask (SIG_UNBLOCK, &clear, NULL); + __go_assert (i == 0); +} - /* The signal handler blocked signals; unblock them. */ - i = sigfillset (&clear); - __go_assert (i == 0); - i = sigprocmask (SIG_UNBLOCK, &clear, NULL); - __go_assert (i == 0); +#ifdef SA_SIGINFO - runtime_panicstring (msg); +/* Signal dispatch for signals which panic, on systems which support + SA_SIGINFO. This is called on the thread stack, and as such it is + permitted to split the stack. */ + +static void +sig_panic_info_handler (int sig, siginfo_t *info, + void *context __attribute__ ((unused))) +{ + if (runtime_g () == NULL) + { + sig_handler (sig); + return; } - for (i = 0; signals[i].sig != -1; ++i) + sig_panic_leadin (sig); + + switch (sig) { - if (signals[i].sig == sig) +#ifdef SIGBUS + case SIGBUS: + if (info->si_code == BUS_ADRERR && (uintptr_t) info->si_addr < 0x1000) + runtime_panicstring ("invalid memory address or " + "nil pointer dereference"); + runtime_printf ("unexpected fault address %p\n", info->si_addr); + runtime_throw ("fault"); +#endif + +#ifdef SIGSEGV + case SIGSEGV: + if ((info->si_code == 0 + || info->si_code == SEGV_MAPERR + || info->si_code == SEGV_ACCERR) + && (uintptr_t) info->si_addr < 0x1000) + runtime_panicstring ("invalid memory address or " + "nil pointer dereference"); + runtime_printf ("unexpected fault address %p\n", info->si_addr); + runtime_throw ("fault"); +#endif + +#ifdef SIGFPE + case SIGFPE: + switch (info->si_code) { - struct sigaction sa; + case FPE_INTDIV: + runtime_panicstring ("integer divide by zero"); + case FPE_INTOVF: + runtime_panicstring ("integer overflow"); + } + runtime_panicstring ("floating point error"); +#endif + } - if (signals[i].queue) - { - if (__go_sigsend (sig) || signals[i].ignore) - return; - runtime_exit (2); // SIGINT, SIGTERM, etc - } + /* All signals with SigPanic should be in cases above, and this + handler should only be invoked for those signals. */ + __builtin_unreachable (); +} - if (runtime_panicking) - runtime_exit (2); - runtime_panicking = 1; +#else /* !defined (SA_SIGINFO) */ - memset (&sa, 0, sizeof sa); +static void +sig_panic_handler (int sig) +{ + if (runtime_g () == NULL) + { + sig_handler (sig); + return; + } - sa.sa_handler = SIG_DFL; + sig_panic_leadin (sig); - i = sigemptyset (&sa.sa_mask); - __go_assert (i == 0); + switch (sig) + { +#ifdef SIGBUS + case SIGBUS: + runtime_panicstring ("invalid memory address or " + "nil pointer dereference"); +#endif - if (sigaction (sig, &sa, NULL) != 0) - abort (); +#ifdef SIGSEGV + case SIGSEGV: + runtime_panicstring ("invalid memory address or " + "nil pointer dereference"); +#endif - raise (sig); - exit (2); - } +#ifdef SIGFPE + case SIGFPE: + runtime_panicstring ("integer divide by zero or floating point error"); +#endif } - abort (); + + /* All signals with SigPanic should be in cases above, and this + handler should only be invoked for those signals. */ + __builtin_unreachable (); } -/* Ignore a signal. */ +#endif /* !defined (SA_SIGINFO) */ + +/* Ignore a signal. This is called on the alternate signal stack so + it may not split the stack. */ + +static void sig_ignore (int) __attribute__ ((no_split_stack)); static void sig_ignore (int sig __attribute__ ((unused))) { } +/* A signal handler used for signals which are not going to panic. + This is called on the alternate signal stack so it may not split + the stack. */ + +static void +sig_tramp (int) __attribute__ ((no_split_stack)); + +static void +sig_tramp (int sig) +{ + G *gp; + M *mp; + + /* We are now running on the stack registered via sigaltstack. + (Actually there is a small span of time between runtime_siginit + and sigaltstack when the program starts.) */ + gp = runtime_g (); + mp = runtime_m (); + + if (gp != NULL) + __splitstack_getcontext (&gp->stack_context[0]); + + if (gp != NULL && mp->gsignal != NULL) + { + /* We are running on the signal stack. Set the split stack + context so that the stack guards are checked correctly. */ +#ifdef USING_SPLIT_STACK + __splitstack_setcontext (&mp->gsignal->stack_context[0]); +#endif + } + + sig_handler (sig); + + /* We are going to return back to the signal trampoline and then to + whatever we were doing before we got the signal. Restore the + split stack context so that stack guards are checked + correctly. */ + + if (gp != NULL) + { +#ifdef USING_SPLIT_STACK + __splitstack_setcontext (&gp->stack_context[0]); +#endif + } +} + /* Initialize signal handling for Go. This is called when the program starts. */ @@ -237,21 +385,44 @@ runtime_initsig (int32 queue) memset (&sa, 0, sizeof sa); - sa.sa_handler = sighandler; - i = sigfillset (&sa.sa_mask); __go_assert (i == 0); - for (i = 0; signals[i].sig != -1; ++i) + for (i = 0; runtime_sigtab[i].sig != -1; ++i) { - if (signals[i].queue != (queue ? 1 : 0)) + if (runtime_sigtab[i].flags == 0) + continue; + if ((runtime_sigtab[i].flags & SigQueue) != queue) continue; - if (signals[i].catch || signals[i].queue) - sa.sa_handler = sighandler; + + if ((runtime_sigtab[i].flags & (SigCatch | SigQueue)) != 0) + { + if ((runtime_sigtab[i].flags & SigPanic) == 0) + { + sa.sa_flags = SA_ONSTACK; + sa.sa_handler = sig_tramp; + } + else + { +#ifdef SA_SIGINFO + sa.sa_flags = SA_SIGINFO; + sa.sa_sigaction = sig_panic_info_handler; +#else + sa.sa_flags = 0; + sa.sa_handler = sig_panic_handler; +#endif + } + } else - sa.sa_handler = sig_ignore; - sa.sa_flags = signals[i].restart ? SA_RESTART : 0; - if (sigaction (signals[i].sig, &sa, NULL) != 0) + { + sa.sa_flags = SA_ONSTACK; + sa.sa_handler = sig_ignore; + } + + if ((runtime_sigtab[i].flags & SigRestart) != 0) + sa.sa_flags |= SA_RESTART; + + if (sigaction (runtime_sigtab[i].sig, &sa, NULL) != 0) __go_assert (0); } } @@ -281,7 +452,7 @@ runtime_resetcpuprofiler(int32 hz) } else { - sa.sa_handler = sighandler; + sa.sa_handler = sig_handler; sa.sa_flags = SA_RESTART; i = sigaction (SIGPROF, &sa, NULL); __go_assert (i == 0); |