OSDN Git Service

runtime: Catch signals on altstack, disable splitstack signal blocking.
authorian <ian@138bc75d-0d04-0410-961f-82ee72b054a4>
Wed, 21 Dec 2011 22:24:47 +0000 (22:24 +0000)
committerian <ian@138bc75d-0d04-0410-961f-82ee72b054a4>
Wed, 21 Dec 2011 22:24:47 +0000 (22:24 +0000)
git-svn-id: svn+ssh://gcc.gnu.org/svn/gcc/trunk@182607 138bc75d-0d04-0410-961f-82ee72b054a4

libgo/runtime/go-panic.c
libgo/runtime/go-signal.c
libgo/runtime/mem.c
libgo/runtime/proc.c
libgo/runtime/runtime.h
libgo/runtime/sigqueue.goc

index 2e4a788..24dc74b 100644 (file)
@@ -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
index eb62409..e5a790a 100644 (file)
   #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);
index 4e1103e..72ab4d6 100644 (file)
@@ -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);
                }
        }
index 34566fb..9225f82 100644 (file)
@@ -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;
index 101a2e9..ed626ef 100644 (file)
@@ -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);
index 502dc44..e915719 100644 (file)
@@ -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
 }