install-reloc: Support multi-binary installation.
[gnulib.git] / lib / c-stack.c
index b502710..03db242 100644 (file)
@@ -1,11 +1,11 @@
 /* Stack overflow handling.
 
-   Copyright (C) 2002 Free Software Foundation, Inc.
+   Copyright (C) 2002, 2004, 2006, 2008-2013 Free Software Foundation, Inc.
 
-   This program is free software; you can redistribute it and/or modify
+   This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
-   the Free Software Foundation; either version 2, or (at your option)
-   any later version.
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
 
    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
@@ -13,8 +13,7 @@
    GNU General Public License for more details.
 
    You should have received a copy of the GNU General Public License
-   along with this program; if not, write to the Free Software Foundation,
-   Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
 
 /* Written by Paul Eggert.  */
 
    (e.g., with malloc).  Yes, this is a pain, but we don't know of any
    better solution that is portable.
 
-   No attempt has been made to deal with multithreaded applications.
+   No attempt has been made to deal with multithreaded applications.  */
 
-   If ! HAVE_XSI_STACK_OVERFLOW_HEURISTIC, the current implementation
-   assumes that, if the RLIMIT_STACK limit changes during execution,
-   then c_stack_action is invoked immediately afterwards.  */
-
-#if HAVE_CONFIG_H
-# include <config.h>
-#endif
+#include <config.h>
 
 #ifndef __attribute__
-# if __GNUC__ < 3 || __STRICT_ANSI__
+# if __GNUC__ < 3
 #  define __attribute__(x)
 # endif
 #endif
 #define _(msgid) gettext (msgid)
 
 #include <errno.h>
-#ifndef ENOTSUP
-# define ENOTSUP EINVAL
-#endif
-#ifndef EOVERFLOW
-# define EOVERFLOW EINVAL
-#endif
 
 #include <signal.h>
 #if ! HAVE_STACK_T && ! defined stack_t
 typedef struct sigaltstack stack_t;
 #endif
+#ifndef SIGSTKSZ
+# define SIGSTKSZ 16384
+#elif HAVE_LIBSIGSEGV && SIGSTKSZ < 16384
+/* libsigsegv 2.6 through 2.8 have a bug where some architectures use
+   more than the Linux default of an 8k alternate stack when deciding
+   if a fault was caused by stack overflow.  */
+# undef SIGSTKSZ
+# define SIGSTKSZ 16384
+#endif
 
 #include <stdlib.h>
 #include <string.h>
 
-#if HAVE_SYS_RESOURCE_H
-/* Include sys/time.h here, because...
-   SunOS-4.1.x <sys/resource.h> fails to include <sys/time.h>.
-   This gives "incomplete type" errors for ru_utime and tu_stime.  */
-# if HAVE_SYS_TIME_H
-#  include <sys/time.h>
-# endif
-# include <sys/resource.h>
-#endif
-
+/* Posix 2001 declares ucontext_t in <ucontext.h>, Posix 200x in
+   <signal.h>.  */
 #if HAVE_UCONTEXT_H
 # include <ucontext.h>
 #endif
 
-#if HAVE_UNISTD_H
-# include <unistd.h>
-#endif
-#ifndef STDERR_FILENO
-# define STDERR_FILENO 2
-#endif
+#include <unistd.h>
 
-#if DEBUG
-# include <stdio.h>
+#if HAVE_LIBSIGSEGV
+# include <sigsegv.h>
 #endif
 
 #include "c-stack.h"
 #include "exitfail.h"
+#include "ignore-value.h"
+
+#if defined SA_ONSTACK && defined SA_SIGINFO
+# define SIGINFO_WORKS 1
+#else
+# define SIGINFO_WORKS 0
+# ifndef SA_ONSTACK
+#  define SA_ONSTACK 0
+# endif
+#endif
 
 extern char *program_name;
 
@@ -111,325 +104,227 @@ static char const * volatile stack_overflow_message;
    appears to have been a stack overflow, or with a core dump
    otherwise.  This function is async-signal-safe.  */
 
-static void die (int) __attribute__ ((noreturn));
-static void
+static _Noreturn void
 die (int signo)
 {
-  char const *message =
-    signo ? program_error_message : stack_overflow_message;
+  char const *message;
+#if !SIGINFO_WORKS && !HAVE_LIBSIGSEGV
+  /* We can't easily determine whether it is a stack overflow; so
+     assume that the rest of our program is perfect (!) and that
+     this segmentation violation is a stack overflow.  */
+  signo = 0;
+#endif /* !SIGINFO_WORKS && !HAVE_LIBSIGSEGV */
   segv_action (signo);
-  write (STDERR_FILENO, program_name, strlen (program_name));
-  write (STDERR_FILENO, ": ", 2);
-  write (STDERR_FILENO, message, strlen (message));
-  write (STDERR_FILENO, "\n", 1);
+  message = signo ? program_error_message : stack_overflow_message;
+  ignore_value (write (STDERR_FILENO, program_name, strlen (program_name)));
+  ignore_value (write (STDERR_FILENO, ": ", 2));
+  ignore_value (write (STDERR_FILENO, message, strlen (message)));
+  ignore_value (write (STDERR_FILENO, "\n", 1));
   if (! signo)
     _exit (exit_failure);
-  kill (getpid (), signo);
+  raise (signo);
   abort ();
 }
 
-#if HAVE_SIGALTSTACK && HAVE_DECL_SIGALTSTACK
+#if (HAVE_SIGALTSTACK && HAVE_DECL_SIGALTSTACK \
+     && HAVE_STACK_OVERFLOW_HANDLING) || HAVE_LIBSIGSEGV
 
-/* Direction of the C runtime stack.  This function is
-   async-signal-safe.  */
+/* Storage for the alternate signal stack.  */
+static union
+{
+  char buffer[SIGSTKSZ];
 
-# if STACK_DIRECTION
-#  define find_stack_direction(ptr) STACK_DIRECTION
-# else
-static int
-find_stack_direction (char const *addr)
+  /* These other members are for proper alignment.  There's no
+     standard way to guarantee stack alignment, but this seems enough
+     in practice.  */
+  long double ld;
+  long l;
+  void *p;
+} alternate_signal_stack;
+
+static void
+null_action (int signo __attribute__ ((unused)))
 {
-  char dummy;
-  return ! addr ? find_stack_direction (&dummy) : addr < &dummy ? 1 : -1;
 }
-# endif
-
-# if HAVE_XSI_STACK_OVERFLOW_HEURISTIC
-#  define get_stack_location(argv) 0
-# else
 
-#  if defined RLIMIT_STACK && defined _SC_PAGESIZE
+#endif /* SIGALTSTACK || LIBSIGSEGV */
 
-/* Return the minimum machine address deducible from ARGV.  This
-   includes the addresses of all the strings that ARGV points at, as
-   well as the address of ARGV itself.  */
+/* Only use libsigsegv if we need it; platforms like Solaris can
+   detect stack overflow without the overhead of an external
+   library.  */
+#if HAVE_LIBSIGSEGV && ! HAVE_XSI_STACK_OVERFLOW_HEURISTIC
 
-static char const *
-min_address_from_argv (char * const *argv)
-{
-  char const *min = (char const *) argv;
-  char const *p;
-  while ((p = *argv++))
-    if (p < min)
-      min = p;
-  return min;
-}
+/* Nonzero if general segv handler could not be installed.  */
+static volatile int segv_handler_missing;
 
-/* Return the maximum machine address deducible from ARGV.  */
+/* Handle a segmentation violation and exit if it cannot be stack
+   overflow.  This function is async-signal-safe.  */
 
-static char const *
-max_address_from_argv (char * const *argv)
+static int segv_handler (void *address __attribute__ ((unused)),
+                         int serious)
 {
-  char const *max = *argv;
-  char const *max1;
-  char const *p;
-  while ((p = *argv++))
-    if (max < p)
-      max = p;
-  max1 = (char const *) (argv + 1);
-  return max && max1 < max ? max + strlen (max) + 1 : max1;
-}
-#  endif
+# if DEBUG
+  {
+    char buf[1024];
+    sprintf (buf, "segv_handler serious=%d\n", serious);
+    write (STDERR_FILENO, buf, strlen (buf));
+  }
+# endif
 
-/* The base and size of the stack, determined at startup.  */
-static char const * volatile stack_base;
-static size_t volatile stack_size;
+  /* If this fault is not serious, return 0 to let the stack overflow
+     handler take a shot at it.  */
+  if (!serious)
+    return 0;
+  die (SIGSEGV);
+}
 
-/* Store the base and size of the stack into the static variables
-   STACK_BASE and STACK_SIZE.  The base is the numerically lowest
-   address in the stack.  Return -1 (setting errno) if this cannot be
-   done.  */
+/* Handle a segmentation violation that is likely to be a stack
+   overflow and exit.  This function is async-signal-safe.  */
 
-static int
-get_stack_location (char * const *argv)
+static _Noreturn void
+overflow_handler (int emergency,
+                  stackoverflow_context_t context __attribute__ ((unused)))
 {
-#  if ! (defined RLIMIT_STACK && defined _SC_PAGESIZE)
+# if DEBUG
+  {
+    char buf[1024];
+    sprintf (buf, "overflow_handler emergency=%d segv_handler_missing=%d\n",
+             emergency, segv_handler_missing);
+    write (STDERR_FILENO, buf, strlen (buf));
+  }
+# endif
 
-  errno = ENOTSUP;
-  return -1;
+  die ((!emergency || segv_handler_missing) ? 0 : SIGSEGV);
+}
 
-#  else
+int
+c_stack_action (void (*action) (int))
+{
+  segv_action = action ? action : null_action;
+  program_error_message = _("program error");
+  stack_overflow_message = _("stack overflow");
 
-  struct rlimit rlimit;
-  int r = getrlimit (RLIMIT_STACK, &rlimit);
-  if (r == 0)
+  /* Always install the overflow handler.  */
+  if (stackoverflow_install_handler (overflow_handler,
+                                     alternate_signal_stack.buffer,
+                                     sizeof alternate_signal_stack.buffer))
     {
-      char const *base;
-      size_t size = rlimit.rlim_cur;
-      extern char **environ;
-      size_t page_size = sysconf (_SC_PAGESIZE);
-      int stack_direction = find_stack_direction (0);
-
-#   if HAVE_GETCONTEXT && HAVE_DECL_GETCONTEXT
-      ucontext_t context;
-      if (getcontext (&context) == 0)
-       {
-         base = context.uc_stack.ss_sp;
-         if (stack_direction < 0)
-           base -= size - context.uc_stack.ss_size;
-       }
-      else
-#   endif
-       {
-         if (stack_direction < 0)
-           {
-             char const *a = max_address_from_argv (argv);
-             char const *b = max_address_from_argv (environ);
-             base = (a < b ? b : a) - size;
-             base += - (size_t) base % page_size;
-           }
-         else
-           {
-             char const *a = min_address_from_argv (argv);
-             char const *b = min_address_from_argv (environ);
-             base = a < b ? a : b;
-             base -= (size_t) base % page_size;
-           }
-       }
-
-      if (size != rlimit.rlim_cur
-         || rlimit.rlim_cur < 0
-         || base + size < base
-#   ifdef RLIM_SAVED_CUR
-         || rlimit.rlim_cur == RLIM_SAVED_CUR
-#   endif
-#   ifdef RLIM_SAVED_MAX
-         || rlimit.rlim_cur == RLIM_SAVED_MAX
-#   endif
-#   ifdef RLIM_INFINITY
-         || rlimit.rlim_cur == RLIM_INFINITY
-#   endif
-         )
-       {
-         errno = EOVERFLOW;
-         return -1;
-       }
-
-      stack_base = base;
-      stack_size = size;
-
-#   if DEBUG
-      fprintf (stderr, "get_stack_location base=%p size=%lx\n",
-              base, (unsigned long) size);
-#   endif
+      errno = ENOTSUP;
+      return -1;
     }
-
-  return r;
-
-#  endif
+  /* Try installing a general handler; if it fails, then treat all
+     segv as stack overflow.  */
+  segv_handler_missing = sigsegv_install_handler (segv_handler);
+  return 0;
 }
-# endif
 
-/* Storage for the alternate signal stack.  */
-static union
-{
-  char buffer[SIGSTKSZ];
+#elif HAVE_SIGALTSTACK && HAVE_DECL_SIGALTSTACK && HAVE_STACK_OVERFLOW_HANDLING
 
-  /* These other members are for proper alignment.  There's no
-     standard way to guarantee stack alignment, but this seems enough
-     in practice.  */
-  long double ld;
-  long l;
-  void *p;
-} alternate_signal_stack;
-
-# if defined SA_ONSTACK && defined SA_SIGINFO && defined _SC_PAGESIZE
+# if SIGINFO_WORKS
 
 /* Handle a segmentation violation and exit.  This function is
    async-signal-safe.  */
 
-static void segv_handler (int, siginfo_t *, void *) __attribute__((noreturn));
-static void
+static _Noreturn void
 segv_handler (int signo, siginfo_t *info,
-             void *context __attribute__ ((unused)))
+              void *context __attribute__ ((unused)))
 {
   /* Clear SIGNO if it seems to have been a stack overflow.  */
+#  if ! HAVE_XSI_STACK_OVERFLOW_HEURISTIC
+  /* We can't easily determine whether it is a stack overflow; so
+     assume that the rest of our program is perfect (!) and that
+     this segmentation violation is a stack overflow.
+
+     Note that although both Linux and Solaris provide
+     sigaltstack, SA_ONSTACK, and SA_SIGINFO, currently only
+     Solaris satisfies the XSI heuristic.  This is because
+     Solaris populates uc_stack with the details of the
+     interrupted stack, while Linux populates it with the details
+     of the current stack.  */
+  signo = 0;
+#  else
   if (0 < info->si_code)
     {
       /* If the faulting address is within the stack, or within one
-        page of the stack end, assume that it is a stack
-        overflow.  */
-#  if HAVE_XSI_STACK_OVERFLOW_HEURISTIC
+         page of the stack, assume that it is a stack overflow.  */
       ucontext_t const *user_context = context;
       char const *stack_base = user_context->uc_stack.ss_sp;
       size_t stack_size = user_context->uc_stack.ss_size;
-#  endif
       char const *faulting_address = info->si_addr;
-      size_t s = faulting_address - stack_base;
       size_t page_size = sysconf (_SC_PAGESIZE);
-      if (find_stack_direction (0) < 0)
-       s += page_size;
-      if (s < stack_size + page_size)
-       signo = 0;
+      size_t s = faulting_address - stack_base + page_size;
+      if (s < stack_size + 2 * page_size)
+        signo = 0;
 
-#  if DEBUG
+#   if DEBUG
       {
-       char buf[1024];
-       sprintf (buf,
-                "segv_handler fault=%p base=%p size=%lx page=%lx signo=%d\n",
-                faulting_address, stack_base, (unsigned long) stack_size,
-                (unsigned long) page_size, signo);
-       write (STDERR_FILENO, buf, strlen (buf));
+        char buf[1024];
+        sprintf (buf,
+                 "segv_handler fault=%p base=%p size=%lx page=%lx signo=%d\n",
+                 faulting_address, stack_base, (unsigned long) stack_size,
+                 (unsigned long) page_size, signo);
+        write (STDERR_FILENO, buf, strlen (buf));
       }
-#  endif
+#   endif
     }
+#  endif
 
   die (signo);
 }
 # endif
 
-static void
-null_action (int signo __attribute__ ((unused)))
-{
-}
-
-/* Assuming ARGV is the argument vector of `main', set up ACTION so
-   that it is invoked on C stack overflow.  Return -1 (setting errno)
-   if this cannot be done.
-
-   When ACTION is called, it is passed an argument equal to SIGSEGV
-   for a segmentation violation that does not appear related to stack
-   overflow, and is passed zero otherwise.
-
-   A null ACTION acts like an action that does nothing.
-
-   ACTION must be async-signal-safe.  ACTION together with its callees
-   must not require more than SIGSTKSZ bytes of stack space.  */
-
 int
-c_stack_action (char * const *argv __attribute__ ((unused)),
-               void (*action) (int))
+c_stack_action (void (*action) (int))
 {
-  int r = get_stack_location (argv);
+  int r;
+  stack_t st;
+  struct sigaction act;
+  st.ss_flags = 0;
+# if SIGALTSTACK_SS_REVERSED
+  /* Irix mistakenly treats ss_sp as the upper bound, rather than
+     lower bound, of the alternate stack.  */
+  st.ss_sp = alternate_signal_stack.buffer + SIGSTKSZ - sizeof (void *);
+  st.ss_size = sizeof alternate_signal_stack.buffer - sizeof (void *);
+# else
+  st.ss_sp = alternate_signal_stack.buffer;
+  st.ss_size = sizeof alternate_signal_stack.buffer;
+# endif
+  r = sigaltstack (&st, NULL);
   if (r != 0)
     return r;
 
-  {
-    stack_t st;
-    st.ss_flags = 0;
-    st.ss_sp = alternate_signal_stack.buffer;
-    st.ss_size = sizeof alternate_signal_stack.buffer;
-    r = sigaltstack (&st, 0);
-    if (r != 0)
-      return r;
-  }
-
   segv_action = action ? action : null_action;
   program_error_message = _("program error");
   stack_overflow_message = _("stack overflow");
 
-  {
-# if ! (defined SA_ONSTACK && defined SA_SIGINFO && defined _SC_PAGESIZE)
-    return signal (SIGSEGV, die) == SIG_ERR ? -1 : 0;
-# else
-    struct sigaction act;
-    sigemptyset (&act.sa_mask);
-
-    /* POSIX 1003.1-2001 says SA_RESETHAND implies SA_NODEFER, but
-       this is not true on Solaris 8 at least.  It doesn't hurt to use
-       SA_NODEFER here, so leave it in.  */
-    act.sa_flags = SA_NODEFER | SA_ONSTACK | SA_RESETHAND | SA_SIGINFO;
+  sigemptyset (&act.sa_mask);
 
-    act.sa_sigaction = segv_handler;
+# if SIGINFO_WORKS
+  /* POSIX 1003.1-2001 says SA_RESETHAND implies SA_NODEFER, but
+     this is not true on Solaris 8 at least.  It doesn't hurt to use
+     SA_NODEFER here, so leave it in.  */
+  act.sa_flags = SA_NODEFER | SA_ONSTACK | SA_RESETHAND | SA_SIGINFO;
+  act.sa_sigaction = segv_handler;
+# else
+  act.sa_flags = SA_NODEFER | SA_ONSTACK | SA_RESETHAND;
+  act.sa_handler = die;
+# endif
 
-    return sigaction (SIGSEGV, &act, 0);
+# if FAULT_YIELDS_SIGBUS
+  if (sigaction (SIGBUS, &act, NULL) < 0)
+    return -1;
 # endif
-  }
+  return sigaction (SIGSEGV, &act, NULL);
 }
 
-#else /* ! (HAVE_SIGALTSTACK && HAVE_DECL_SIGALTSTACK) */
+#else /* ! ((HAVE_SIGALTSTACK && HAVE_DECL_SIGALTSTACK
+             && HAVE_STACK_OVERFLOW_HANDLING) || HAVE_LIBSIGSEGV) */
 
 int
-c_stack_action (char * const *argv __attribute__ ((unused)),
-               void (*action) (int)  __attribute__ ((unused)))
+c_stack_action (void (*action) (int)  __attribute__ ((unused)))
 {
   errno = ENOTSUP;
   return -1;
 }
 
 #endif
-
-\f
-
-#if DEBUG
-
-int volatile exit_failure;
-
-static long
-recurse (char *p)
-{
-  char array[500];
-  array[0] = 1;
-  return *p + recurse (array);
-}
-
-char *program_name;
-
-int
-main (int argc __attribute__ ((unused)), char **argv)
-{
-  program_name = argv[0];
-  fprintf (stderr, "The last line of output should be \"stack overflow\".\n");
-  if (c_stack_action (argv, 0) == 0)
-    return recurse ("\1");
-  perror ("c_stack_action");
-  return 1;
-}
-
-#endif /* DEBUG */
-\f
-/*
-Local Variables:
-compile-command: "gcc -DDEBUG -DHAVE_CONFIG_H -I.. -g -O -Wall -W c-stack.c"
-End:
-*/