diff options
author | ian <ian@138bc75d-0d04-0410-961f-82ee72b054a4> | 2011-12-21 22:24:47 +0000 |
---|---|---|
committer | ian <ian@138bc75d-0d04-0410-961f-82ee72b054a4> | 2011-12-21 22:24:47 +0000 |
commit | dfcc5de0612ab74bb3329e22f5d6487de84c40a4 (patch) | |
tree | b581b11b17c97821c10249103de061834481481d /libgo | |
parent | 88f9060eb8b19564912ebca431808943c33ac6b5 (diff) | |
download | gcc-dfcc5de0612ab74bb3329e22f5d6487de84c40a4.tar.gz |
runtime: Catch signals on altstack, disable splitstack signal blocking.
git-svn-id: svn+ssh://gcc.gnu.org/svn/gcc/trunk@182607 138bc75d-0d04-0410-961f-82ee72b054a4
Diffstat (limited to 'libgo')
-rw-r--r-- | libgo/runtime/go-panic.c | 8 | ||||
-rw-r--r-- | libgo/runtime/go-signal.c | 415 | ||||
-rw-r--r-- | libgo/runtime/mem.c | 10 | ||||
-rw-r--r-- | libgo/runtime/proc.c | 21 | ||||
-rw-r--r-- | libgo/runtime/runtime.h | 17 | ||||
-rw-r--r-- | libgo/runtime/sigqueue.goc | 2 |
6 files changed, 340 insertions, 133 deletions
diff --git a/libgo/runtime/go-panic.c b/libgo/runtime/go-panic.c index 2e4a7883c18..24dc74bd285 100644 --- a/libgo/runtime/go-panic.c +++ b/libgo/runtime/go-panic.c @@ -24,13 +24,13 @@ __printpanics (struct __go_panic_stack *p) if (p->__next != NULL) { __printpanics (p->__next); - printf ("\t"); + fprintf (stderr, "\t"); } - printf ("panic: "); + fprintf (stderr, "panic: "); printany (p->__arg); if (p->__was_recovered) - printf (" [recovered]"); - putchar ('\n'); + fprintf (stderr, " [recovered]"); + fputc ('\n', stderr); } /* This implements __go_panic which is used for the panic diff --git a/libgo/runtime/go-signal.c b/libgo/runtime/go-signal.c index eb624097b36..e5a790aae52 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); diff --git a/libgo/runtime/mem.c b/libgo/runtime/mem.c index 4e1103e130a..72ab4d695fa 100644 --- a/libgo/runtime/mem.c +++ b/libgo/runtime/mem.c @@ -47,7 +47,7 @@ runtime_SysAlloc(uintptr n) if (dev_zero == -1) { dev_zero = open("/dev/zero", O_RDONLY); if (dev_zero < 0) { - printf("open /dev/zero: errno=%d\n", errno); + runtime_printf("open /dev/zero: errno=%d\n", errno); exit(2); } } @@ -57,8 +57,8 @@ runtime_SysAlloc(uintptr n) p = runtime_mmap(nil, n, PROT_READ|PROT_WRITE|PROT_EXEC, MAP_ANON|MAP_PRIVATE, fd, 0); if (p == MAP_FAILED) { if(errno == EACCES) { - printf("runtime: mmap: access denied\n"); - printf("if you're running SELinux, enable execmem for this process.\n"); + runtime_printf("runtime: mmap: access denied\n"); + runtime_printf("if you're running SELinux, enable execmem for this process.\n"); exit(2); } return nil; @@ -97,7 +97,7 @@ runtime_SysReserve(void *v, uintptr n) if (dev_zero == -1) { dev_zero = open("/dev/zero", O_RDONLY); if (dev_zero < 0) { - printf("open /dev/zero: errno=%d\n", errno); + runtime_printf("open /dev/zero: errno=%d\n", errno); exit(2); } } @@ -123,7 +123,7 @@ runtime_SysMap(void *v, uintptr n) if (dev_zero == -1) { dev_zero = open("/dev/zero", O_RDONLY); if (dev_zero < 0) { - printf("open /dev/zero: errno=%d\n", errno); + runtime_printf("open /dev/zero: errno=%d\n", errno); exit(2); } } diff --git a/libgo/runtime/proc.c b/libgo/runtime/proc.c index 34566fbf9a0..9225f825b88 100644 --- a/libgo/runtime/proc.c +++ b/libgo/runtime/proc.c @@ -29,6 +29,11 @@ extern void * __splitstack_resetcontext(void *context[10], size_t *); extern void *__splitstack_find(void *, void *, size_t *, void **, void **, void **); +extern void __splitstack_block_signals (int *, int *); + +extern void __splitstack_block_signals_context (void *context[10], int *, + int *); + #endif #if defined(USING_SPLIT_STACK) && defined(LINKER_SUPPORTS_SPLIT_STACK) @@ -862,6 +867,14 @@ runtime_mstart(void* mp) *(int*)0x21 = 0x21; } runtime_minit(); + +#ifdef USING_SPLIT_STACK + { + int dont_block_signals = 0; + __splitstack_block_signals(&dont_block_signals, nil); + } +#endif + schedule(nil); return nil; } @@ -1142,9 +1155,13 @@ runtime_malg(int32 stacksize, byte** ret_stack, size_t* ret_stacksize) newg = runtime_malloc(sizeof(G)); if(stacksize >= 0) { #if USING_SPLIT_STACK + int dont_block_signals = 0; + *ret_stack = __splitstack_makecontext(stacksize, &newg->stack_context[0], ret_stacksize); + __splitstack_block_signals_context(&newg->stack_context[0], + &dont_block_signals, nil); #else *ret_stack = runtime_mallocgc(stacksize, FlagNoProfiling|FlagNoGC, 0, 0); *ret_stacksize = stacksize; @@ -1186,8 +1203,12 @@ __go_go(void (*fn)(void*), void* arg) if((newg = gfget()) != nil){ #ifdef USING_SPLIT_STACK + int dont_block_signals = 0; + sp = __splitstack_resetcontext(&newg->stack_context[0], &spsize); + __splitstack_block_signals_context(&newg->stack_context[0], + &dont_block_signals, nil); #else sp = newg->gcinitial_sp; spsize = newg->gcstack_size; diff --git a/libgo/runtime/runtime.h b/libgo/runtime/runtime.h index 101a2e91038..ed626efe8a2 100644 --- a/libgo/runtime/runtime.h +++ b/libgo/runtime/runtime.h @@ -52,6 +52,7 @@ typedef struct G G; typedef union Lock Lock; typedef struct M M; typedef union Note Note; +typedef struct SigTab SigTab; typedef struct MCache MCache; typedef struct FixAlloc FixAlloc; typedef struct Hchan Hchan; @@ -179,6 +180,20 @@ struct M uint32 waitsemalock; }; +struct SigTab +{ + int32 sig; + int32 flags; +}; +enum +{ + SigCatch = 1<<0, + SigIgnore = 1<<1, + SigRestart = 1<<2, + SigQueue = 1<<3, + SigPanic = 1<<4, +}; + /* Macros. */ #ifdef __WINDOWS__ @@ -251,7 +266,7 @@ void runtime_args(int32, byte**); void runtime_osinit(); void runtime_goargs(void); void runtime_goenvs(void); -void runtime_throw(const char*); +void runtime_throw(const char*) __attribute__ ((noreturn)); void runtime_panicstring(const char*) __attribute__ ((noreturn)); void* runtime_mal(uintptr); void runtime_schedinit(void); diff --git a/libgo/runtime/sigqueue.goc b/libgo/runtime/sigqueue.goc index 502dc442c83..e91571902c0 100644 --- a/libgo/runtime/sigqueue.goc +++ b/libgo/runtime/sigqueue.goc @@ -110,6 +110,6 @@ func Signame(sig int32) (name String) { } func Siginit() { - runtime_initsig(1); + runtime_initsig(SigQueue); sig.inuse = true; // enable reception of signals; cannot disable } |