#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
}
#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. */
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);
}
}
}
else
{
- sa.sa_handler = sighandler;
+ sa.sa_handler = sig_handler;
sa.sa_flags = SA_RESTART;
i = sigaction (SIGPROF, &sa, NULL);
__go_assert (i == 0);