]> git.ipfire.org Git - thirdparty/gcc.git/blobdiff - libgcc/generic-morestack.c
Work around Solaris ld bug linking __tls_get_addr on 64-bit x86
[thirdparty/gcc.git] / libgcc / generic-morestack.c
index 00a3b1c3bf064ee8d79414e8b53137ac03bbd269..0f6f0005f994f384468d5b60eed7067a0eb5fac7 100644 (file)
@@ -1,5 +1,5 @@
 /* Library support for -fsplit-stack.  */
-/* Copyright (C) 2009, 2010, 2011 Free Software Foundation, Inc.
+/* Copyright (C) 2009-2019 Free Software Foundation, Inc.
    Contributed by Ian Lance Taylor <iant@google.com>.
 
 This file is part of GCC.
@@ -23,13 +23,16 @@ a copy of the GCC Runtime Library Exception along with this program;
 see the files COPYING3 and COPYING.RUNTIME respectively.  If not, see
 <http://www.gnu.org/licenses/>.  */
 
+/* powerpc 32-bit not supported.  */
+#if !defined __powerpc__ || defined __powerpc64__
+
 #include "tconfig.h"
 #include "tsystem.h"
 #include "coretypes.h"
 #include "tm.h"
 #include "libgcc_tm.h"
 
-/* If inhibit_libc is defined, we can not compile this file.  The
+/* If inhibit_libc is defined, we cannot compile this file.  The
    effect is that people will not be able to use -fsplit-stack.  That
    is much better than failing the build particularly since people
    will want to define inhibit_libc while building a compiler which
@@ -115,6 +118,14 @@ extern void *
 __splitstack_makecontext (size_t, void *context[10], size_t *)
   __attribute__ ((visibility ("default")));
 
+extern void *
+__splitstack_resetcontext (void *context[10], size_t *)
+  __attribute__ ((visibility ("default")));
+
+extern void
+__splitstack_releasecontext (void *context[10])
+  __attribute__ ((visibility ("default")));
+
 extern void
 __splitstack_block_signals_context (void *context[10], int *, int *)
   __attribute__ ((visibility ("default")));
@@ -232,6 +243,12 @@ __thread struct initial_sp __morestack_initial_sp
 
 static sigset_t __morestack_fullmask;
 
+/* Page size, as returned from getpagesize(). Set on startup. */
+static unsigned int static_pagesize;
+
+/* Set on startup to non-zero value if SPLIT_STACK_GUARD env var is set. */
+static int use_guard_page;
+
 /* Convert an integer to a decimal string without using much stack
    space.  Return a pointer to the part of the buffer to use.  We this
    instead of sprintf because sprintf will require too much stack
@@ -309,8 +326,6 @@ __morestack_fail (const char *msg, size_t len, int err)
 static struct stack_segment *
 allocate_segment (size_t frame_size)
 {
-  static unsigned int static_pagesize;
-  static int use_guard_page;
   unsigned int pagesize;
   unsigned int overhead;
   unsigned int allocate;
@@ -318,27 +333,6 @@ allocate_segment (size_t frame_size)
   struct stack_segment *pss;
 
   pagesize = static_pagesize;
-  if (pagesize == 0)
-    {
-      unsigned int p;
-
-      pagesize = getpagesize ();
-
-#ifdef __GCC_HAVE_SYNC_COMPARE_AND_SWAP_4
-      p = __sync_val_compare_and_swap (&static_pagesize, 0, pagesize);
-#else
-      /* Just hope this assignment is atomic.  */
-      static_pagesize = pagesize;
-      p = 0;
-#endif
-
-      use_guard_page = getenv ("SPLIT_STACK_GUARD") != 0;
-
-      /* FIXME: I'm not sure this assert should be in the released
-        code.  */
-      assert (p == 0 || p == pagesize);
-    }
-
   overhead = sizeof (struct stack_segment);
 
   allocate = pagesize;
@@ -370,7 +364,7 @@ allocate_segment (size_t frame_size)
     {
       void *guard;
 
-#ifdef STACK_GROWS_DOWNWARD
+#ifdef __LIBGCC_STACK_GROWS_DOWNWARD__
       guard = space;
       space = (char *) space + pagesize;
 #else
@@ -488,7 +482,7 @@ __generic_morestack_set_initial_sp (void *sp, size_t len)
      to the nearest 512 byte boundary.  It's not essential that we be
      precise here; getting it wrong will just leave some stack space
      unused.  */
-#ifdef STACK_GROWS_DOWNWARD
+#ifdef __LIBGCC_STACK_GROWS_DOWNWARD__
   sp = (void *) ((((__UINTPTR_TYPE__) sp + 511U) / 512U) * 512U);
 #else
   sp = (void *) ((((__UINTPTR_TYPE__) sp - 511U) / 512U) * 512U);
@@ -499,8 +493,8 @@ __generic_morestack_set_initial_sp (void *sp, size_t len)
   sigemptyset (&__morestack_initial_sp.mask);
 
   sigfillset (&__morestack_fullmask);
-#ifdef __linux__
-  /* On Linux, the first two real time signals are used by the NPTL
+#if defined(__GLIBC__) && defined(__linux__)
+  /* In glibc, the first two real time signals are used by the NPTL
      threading library.  By taking them out of the set of signals, we
      avoiding copying the signal mask in pthread_sigmask.  More
      importantly, pthread_sigmask uses less stack space on x86_64.  */
@@ -541,6 +535,7 @@ __generic_morestack (size_t *pframe_size, void *old_stack, size_t param_size)
   char *to;
   void *ret;
   size_t i;
+  size_t aligned;
 
   current = __morestack_current_segment;
 
@@ -572,15 +567,19 @@ __generic_morestack (size_t *pframe_size, void *old_stack, size_t param_size)
 
   *pframe_size = current->size - param_size;
 
-#ifdef STACK_GROWS_DOWNWARD
+  /* Align the returned stack to a 32-byte boundary.  */
+  aligned = (param_size + 31) & ~ (size_t) 31;
+
+#ifdef __LIBGCC_STACK_GROWS_DOWNWARD__
   {
     char *bottom = (char *) (current + 1) + current->size;
-    to = bottom - param_size;
-    ret = bottom - param_size;
+    to = bottom - aligned;
+    ret = bottom - aligned;
   }
 #else
   to = current + 1;
-  ret = (char *) (current + 1) + param_size;
+  to += aligned - param_size;
+  ret = (char *) (current + 1) + aligned;
 #endif
 
   /* We don't call memcpy to avoid worrying about the dynamic linker
@@ -615,7 +614,7 @@ __generic_releasestack (size_t *pavailable)
 
   if (current != NULL)
     {
-#ifdef STACK_GROWS_DOWNWARD
+#ifdef __LIBGCC_STACK_GROWS_DOWNWARD__
       *pavailable = (char *) old_stack - (char *) (current + 1);
 #else
       *pavailable = (char *) (current + 1) + current->size - (char *) old_stack;
@@ -626,7 +625,7 @@ __generic_releasestack (size_t *pavailable)
       size_t used;
 
       /* We have popped back to the original stack.  */
-#ifdef STACK_GROWS_DOWNWARD
+#ifdef __LIBGCC_STACK_GROWS_DOWNWARD__
       if ((char *) old_stack >= (char *) __morestack_initial_sp.sp)
        used = 0;
       else
@@ -765,7 +764,7 @@ __generic_findstack (void *stack)
          && (char *) pss + pss->size > (char *) stack)
        {
          __morestack_current_segment = pss;
-#ifdef STACK_GROWS_DOWNWARD
+#ifdef __LIBGCC_STACK_GROWS_DOWNWARD__
          return (char *) stack - (char *) (pss + 1);
 #else
          return (char *) (pss + 1) + pss->size - (char *) stack;
@@ -778,7 +777,7 @@ __generic_findstack (void *stack)
   if (__morestack_initial_sp.sp == NULL)
     return 0;
 
-#ifdef STACK_GROWS_DOWNWARD
+#ifdef __LIBGCC_STACK_GROWS_DOWNWARD__
   if ((char *) stack >= (char *) __morestack_initial_sp.sp)
     used = 0;
   else
@@ -799,7 +798,10 @@ __generic_findstack (void *stack)
 /* This function is called at program startup time to make sure that
    mmap, munmap, and getpagesize are resolved if linking dynamically.
    We want to resolve them while we have enough stack for them, rather
-   than calling into the dynamic linker while low on stack space.  */
+   than calling into the dynamic linker while low on stack space.
+   Similarly, invoke getenv here to check for split-stack related control
+   variables, since doing do as part of the __morestack path can result
+   in unwanted use of SSE/AVX registers (see GCC PR 86213). */
 
 void
 __morestack_load_mmap (void)
@@ -809,7 +811,12 @@ __morestack_load_mmap (void)
      TLS accessor function is resolved.  */
   mmap (__morestack_current_segment, 0, PROT_READ, MAP_ANONYMOUS, -1, 0);
   mprotect (NULL, 0, 0);
-  munmap (0, getpagesize ());
+  munmap (0, static_pagesize);
+
+  /* Initialize these values here, so as to avoid dynamic linker
+     activity as part of a __morestack call. */
+  static_pagesize = getpagesize();
+  use_guard_page = getenv ("SPLIT_STACK_GUARD") != 0;
 }
 
 /* This function may be used to iterate over the stack segments.
@@ -856,7 +863,7 @@ __splitstack_find (void *segment_arg, void *sp, size_t *len,
 
       *next_segment = (void *) (uintptr_type) 2;
       *next_sp = NULL;
-#ifdef STACK_GROWS_DOWNWARD
+#ifdef __LIBGCC_STACK_GROWS_DOWNWARD__
       if ((char *) sp >= isp)
        return NULL;
       *len = (char *) isp - (char *) sp;
@@ -911,17 +918,30 @@ __splitstack_find (void *segment_arg, void *sp, size_t *len,
 
   nsp = (char *) segment->old_stack;
 
+  if (nsp == NULL)
+    {
+      /* We've reached the top of the stack.  */
+      *next_segment = (void *) (uintptr_type) 2;
+    }
+  else
+    {
 #if defined (__x86_64__)
-  nsp -= 12 * sizeof (void *);
+      nsp -= 12 * sizeof (void *);
 #elif defined (__i386__)
-  nsp -= 6 * sizeof (void *);
+      nsp -= 6 * sizeof (void *);
+#elif defined __powerpc64__
+#elif defined __s390x__
+      nsp -= 2 * 160;
+#elif defined __s390__
+      nsp -= 2 * 96;
 #else
 #error "unrecognized target"
 #endif
 
-  *next_sp = (void *) nsp;
+      *next_sp = (void *) nsp;
+    }
 
-#ifdef STACK_GROWS_DOWNWARD
+#ifdef __LIBGCC_STACK_GROWS_DOWNWARD__
   *len = (char *) (segment + 1) + segment->size - (char *) sp;
   ret = (void *) sp;
 #else
@@ -1025,7 +1045,7 @@ __splitstack_makecontext (size_t stack_size, void *context[NUMBER_OFFSETS],
   segment = allocate_segment (stack_size);
   context[MORESTACK_SEGMENTS] = segment;
   context[CURRENT_SEGMENT] = segment;
-#ifdef STACK_GROWS_DOWNWARD
+#ifdef __LIBGCC_STACK_GROWS_DOWNWARD__
   initial_sp = (void *) ((char *) (segment + 1) + segment->size);
 #else
   initial_sp = (void *) (segment + 1);
@@ -1037,6 +1057,62 @@ __splitstack_makecontext (size_t stack_size, void *context[NUMBER_OFFSETS],
   return (void *) (segment + 1);
 }
 
+/* Given an existing split stack context, reset it back to the start
+   of the stack.  Return the stack pointer and size, appropriate for
+   use with makecontext.  This may be used if a coroutine exits, in
+   order to reuse the stack segments for a new coroutine.  */
+
+void *
+__splitstack_resetcontext (void *context[10], size_t *size)
+{
+  struct stack_segment *segment;
+  void *initial_sp;
+  size_t initial_size;
+  void *ret;
+
+  /* Reset the context assuming that MORESTACK_SEGMENTS, INITIAL_SP
+     and INITIAL_SP_LEN are correct.  */
+
+  segment = context[MORESTACK_SEGMENTS];
+  context[CURRENT_SEGMENT] = segment;
+  context[CURRENT_STACK] = NULL;
+  if (segment == NULL)
+    {
+      initial_sp = context[INITIAL_SP];
+      initial_size = (uintptr_type) context[INITIAL_SP_LEN];
+      ret = initial_sp;
+#ifdef __LIBGCC_STACK_GROWS_DOWNWARD__
+      ret = (void *) ((char *) ret - initial_size);
+#endif
+    }
+  else
+    {
+#ifdef __LIBGCC_STACK_GROWS_DOWNWARD__
+      initial_sp = (void *) ((char *) (segment + 1) + segment->size);
+#else
+      initial_sp = (void *) (segment + 1);
+#endif
+      initial_size = segment->size;
+      ret = (void *) (segment + 1);
+    }
+  context[STACK_GUARD] = __morestack_make_guard (initial_sp, initial_size);
+  context[BLOCK_SIGNALS] = NULL;
+  *size = initial_size;
+  return ret;
+}
+
+/* Release all the memory associated with a splitstack context.  This
+   may be used if a coroutine exits and the associated stack should be
+   freed.  */
+
+void
+__splitstack_releasecontext (void *context[10])
+{
+  __morestack_release_segments (((struct stack_segment **)
+                                &context[MORESTACK_SEGMENTS]),
+                               1);
+}
+
 /* Like __splitstack_block_signals, but operating on CONTEXT, rather
    than on the current state.  */
 
@@ -1093,3 +1169,4 @@ __splitstack_find_context (void *context[NUMBER_OFFSETS], size_t *stack_size,
 }
 
 #endif /* !defined (inhibit_libc) */
+#endif /* not powerpc 32-bit */