summaryrefslogtreecommitdiff
path: root/libgo
diff options
context:
space:
mode:
authorian <ian@138bc75d-0d04-0410-961f-82ee72b054a4>2011-12-21 22:24:47 +0000
committerian <ian@138bc75d-0d04-0410-961f-82ee72b054a4>2011-12-21 22:24:47 +0000
commitdfcc5de0612ab74bb3329e22f5d6487de84c40a4 (patch)
treeb581b11b17c97821c10249103de061834481481d /libgo
parent88f9060eb8b19564912ebca431808943c33ac6b5 (diff)
downloadgcc-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.c8
-rw-r--r--libgo/runtime/go-signal.c415
-rw-r--r--libgo/runtime/mem.c10
-rw-r--r--libgo/runtime/proc.c21
-rw-r--r--libgo/runtime/runtime.h17
-rw-r--r--libgo/runtime/sigqueue.goc2
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
}