/* 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.
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
#include <errno.h>
#include <signal.h>
#include <stdlib.h>
+#include <string.h>
#include <unistd.h>
#include <sys/mman.h>
#include <sys/uio.h>
#include "generic-morestack.h"
+typedef unsigned uintptr_type __attribute__ ((mode (pointer)));
+
/* This file contains subroutines that are used by code compiled with
-fsplit-stack. */
__morestack_allocate_stack_space (size_t size)
__attribute__ ((visibility ("hidden")));
-/* This is a function which -fsplit-stack code can call to get a list
- of the stacks. Since it is not called only by the compiler, it is
- not hidden. */
+/* These are functions which -fsplit-stack code can call. These are
+ not called by the compiler, and are not hidden. FIXME: These
+ should be in some header file somewhere, somehow. */
extern void *
__splitstack_find (void *, void *, size_t *, void **, void **, void **)
__attribute__ ((visibility ("default")));
+extern void
+__splitstack_block_signals (int *, int *)
+ __attribute__ ((visibility ("default")));
+
+extern void
+__splitstack_getcontext (void *context[10])
+ __attribute__ ((no_split_stack, visibility ("default")));
+
+extern void
+__splitstack_setcontext (void *context[10])
+ __attribute__ ((no_split_stack, visibility ("default")));
+
+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")));
+
+extern void *
+__splitstack_find_context (void *context[10], size_t *, void **, void **,
+ void **)
+ __attribute__ ((visibility ("default")));
+
+/* These functions must be defined by the processor specific code. */
+
+extern void *__morestack_get_guard (void)
+ __attribute__ ((no_split_stack, visibility ("hidden")));
+
+extern void __morestack_set_guard (void *)
+ __attribute__ ((no_split_stack, visibility ("hidden")));
+
+extern void *__morestack_make_guard (void *, size_t)
+ __attribute__ ((no_split_stack, visibility ("hidden")));
+
/* When we allocate a stack segment we put this header at the
start. */
/* A signal mask, put here so that the thread can use it without
needing stack space. */
sigset_t mask;
+ /* Non-zero if we should not block signals. This is a reversed flag
+ so that the default zero value is the safe value. The type is
+ uintptr_type because it replaced one of the void * pointers in
+ extra. */
+ uintptr_type dont_block_signals;
/* Some extra space for later extensibility. */
- void *extra[5];
+ void *extra[4];
};
/* A list of memory blocks allocated by dynamic stack allocation.
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
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;
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;
{
void *guard;
-#ifdef STACK_GROWS_DOWNWARD
+#ifdef __LIBGCC_STACK_GROWS_DOWNWARD__
guard = space;
space = (char *) space + pagesize;
#else
pss = (struct stack_segment *) space;
- pss->prev = __morestack_current_segment;
+ pss->prev = NULL;
pss->next = NULL;
pss->size = allocate - overhead;
pss->dynamic_allocation = NULL;
pss->free_dynamic_allocation = NULL;
pss->extra = NULL;
- if (__morestack_current_segment != NULL)
- __morestack_current_segment->next = pss;
- else
- __morestack_segments = pss;
-
return pss;
}
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);
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. */
char *to;
void *ret;
size_t i;
+ size_t aligned;
current = __morestack_current_segment;
current = *pp;
if (current == NULL)
- current = allocate_segment (frame_size + param_size);
+ {
+ current = allocate_segment (frame_size + param_size);
+ current->prev = __morestack_current_segment;
+ *pp = current;
+ }
current->old_stack = old_stack;
*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
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;
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
void
__morestack_block_signals (void)
{
- if (pthread_sigmask)
+ if (__morestack_initial_sp.dont_block_signals)
+ ;
+ else if (pthread_sigmask)
pthread_sigmask (SIG_BLOCK, &__morestack_fullmask,
&__morestack_initial_sp.mask);
else
void
__morestack_unblock_signals (void)
{
- if (pthread_sigmask)
+ if (__morestack_initial_sp.dont_block_signals)
+ ;
+ else if (pthread_sigmask)
pthread_sigmask (SIG_SETMASK, &__morestack_initial_sp.mask, NULL);
else
sigprocmask (SIG_SETMASK, &__morestack_initial_sp.mask, NULL);
&& (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;
}
/* We have popped back to the original stack. */
-#ifdef STACK_GROWS_DOWNWARD
+
+ if (__morestack_initial_sp.sp == NULL)
+ return 0;
+
+#ifdef __LIBGCC_STACK_GROWS_DOWNWARD__
if ((char *) stack >= (char *) __morestack_initial_sp.sp)
used = 0;
else
/* 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)
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.
void *ret;
char *nsp;
- if (segment_arg == (void *) 1)
+ if (segment_arg == (void *) (uintptr_type) 1)
{
char *isp = (char *) *initial_sp;
- *next_segment = (void *) 2;
+ if (isp == NULL)
+ return NULL;
+
+ *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;
return (void *) isp;
#endif
}
- else if (segment_arg == (void *) 2)
+ else if (segment_arg == (void *) (uintptr_type) 2)
return NULL;
else if (segment_arg != NULL)
segment = (struct stack_segment *) segment_arg;
while (1)
{
if (segment == NULL)
- return __splitstack_find ((void *) 1, sp, len, next_segment,
- next_sp, initial_sp);
+ return __splitstack_find ((void *) (uintptr_type) 1, sp, len,
+ next_segment, next_sp, initial_sp);
if ((char *) sp >= (char *) (segment + 1)
&& (char *) sp <= (char *) (segment + 1) + segment->size)
break;
}
if (segment->prev == NULL)
- *next_segment = (void *) 1;
+ *next_segment = (void *) (uintptr_type) 1;
else
*next_segment = segment->prev;
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
return ret;
}
+/* Tell the split stack code whether it has to block signals while
+ manipulating the stack. This is for programs in which some threads
+ block all signals. If a thread already blocks signals, there is no
+ need for the split stack code to block them as well. If NEW is not
+ NULL, then if *NEW is non-zero signals will be blocked while
+ splitting the stack, otherwise they will not. If OLD is not NULL,
+ *OLD will be set to the old value. */
+
+void
+__splitstack_block_signals (int *new, int *old)
+{
+ if (old != NULL)
+ *old = __morestack_initial_sp.dont_block_signals ? 0 : 1;
+ if (new != NULL)
+ __morestack_initial_sp.dont_block_signals = *new ? 0 : 1;
+}
+
+/* The offsets into the arrays used by __splitstack_getcontext and
+ __splitstack_setcontext. */
+
+enum __splitstack_context_offsets
+{
+ MORESTACK_SEGMENTS = 0,
+ CURRENT_SEGMENT = 1,
+ CURRENT_STACK = 2,
+ STACK_GUARD = 3,
+ INITIAL_SP = 4,
+ INITIAL_SP_LEN = 5,
+ BLOCK_SIGNALS = 6,
+
+ NUMBER_OFFSETS = 10
+};
+
+/* Get the current split stack context. This may be used for
+ coroutine switching, similar to getcontext. The argument should
+ have at least 10 void *pointers for extensibility, although we
+ don't currently use all of them. This would normally be called
+ immediately before a call to getcontext or swapcontext or
+ setjmp. */
+
+void
+__splitstack_getcontext (void *context[NUMBER_OFFSETS])
+{
+ memset (context, 0, NUMBER_OFFSETS * sizeof (void *));
+ context[MORESTACK_SEGMENTS] = (void *) __morestack_segments;
+ context[CURRENT_SEGMENT] = (void *) __morestack_current_segment;
+ context[CURRENT_STACK] = (void *) &context;
+ context[STACK_GUARD] = __morestack_get_guard ();
+ context[INITIAL_SP] = (void *) __morestack_initial_sp.sp;
+ context[INITIAL_SP_LEN] = (void *) (uintptr_type) __morestack_initial_sp.len;
+ context[BLOCK_SIGNALS] = (void *) __morestack_initial_sp.dont_block_signals;
+}
+
+/* Set the current split stack context. The argument should be a
+ context previously passed to __splitstack_getcontext. This would
+ normally be called immediately after a call to getcontext or
+ swapcontext or setjmp if something jumped to it. */
+
+void
+__splitstack_setcontext (void *context[NUMBER_OFFSETS])
+{
+ __morestack_segments = (struct stack_segment *) context[MORESTACK_SEGMENTS];
+ __morestack_current_segment =
+ (struct stack_segment *) context[CURRENT_SEGMENT];
+ __morestack_set_guard (context[STACK_GUARD]);
+ __morestack_initial_sp.sp = context[INITIAL_SP];
+ __morestack_initial_sp.len = (size_t) context[INITIAL_SP_LEN];
+ __morestack_initial_sp.dont_block_signals =
+ (uintptr_type) context[BLOCK_SIGNALS];
+}
+
+/* Create a new split stack context. This will allocate a new stack
+ segment which may be used by a coroutine. STACK_SIZE is the
+ minimum size of the new stack. The caller is responsible for
+ actually setting the stack pointer. This would normally be called
+ before a call to makecontext, and the returned stack pointer and
+ size would be used to set the uc_stack field. A function called
+ via makecontext on a stack created by __splitstack_makecontext may
+ not return. Note that the returned pointer points to the lowest
+ address in the stack space, and thus may not be the value to which
+ to set the stack pointer. */
+
+void *
+__splitstack_makecontext (size_t stack_size, void *context[NUMBER_OFFSETS],
+ size_t *size)
+{
+ struct stack_segment *segment;
+ void *initial_sp;
+
+ memset (context, 0, NUMBER_OFFSETS * sizeof (void *));
+ segment = allocate_segment (stack_size);
+ context[MORESTACK_SEGMENTS] = segment;
+ context[CURRENT_SEGMENT] = segment;
+#ifdef __LIBGCC_STACK_GROWS_DOWNWARD__
+ initial_sp = (void *) ((char *) (segment + 1) + segment->size);
+#else
+ initial_sp = (void *) (segment + 1);
+#endif
+ context[STACK_GUARD] = __morestack_make_guard (initial_sp, segment->size);
+ context[INITIAL_SP] = NULL;
+ context[INITIAL_SP_LEN] = 0;
+ *size = segment->size;
+ 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. */
+
+void
+__splitstack_block_signals_context (void *context[NUMBER_OFFSETS], int *new,
+ int *old)
+{
+ if (old != NULL)
+ *old = ((uintptr_type) context[BLOCK_SIGNALS]) != 0 ? 0 : 1;
+ if (new != NULL)
+ context[BLOCK_SIGNALS] = (void *) (uintptr_type) (*new ? 0 : 1);
+}
+
+/* Find the stack segments associated with a split stack context.
+ This will return the address of the first stack segment and set
+ *STACK_SIZE to its size. It will set next_segment, next_sp, and
+ initial_sp which may be passed to __splitstack_find to find the
+ remaining segments. */
+
+void *
+__splitstack_find_context (void *context[NUMBER_OFFSETS], size_t *stack_size,
+ void **next_segment, void **next_sp,
+ void **initial_sp)
+{
+ void *sp;
+ struct stack_segment *segment;
+
+ *initial_sp = context[INITIAL_SP];
+
+ sp = context[CURRENT_STACK];
+ if (sp == NULL)
+ {
+ /* Most likely this context was created but was never used. The
+ value 2 is a code used by __splitstack_find to mean that we
+ have reached the end of the list of stacks. */
+ *next_segment = (void *) (uintptr_type) 2;
+ *next_sp = NULL;
+ *initial_sp = NULL;
+ return NULL;
+ }
+
+ segment = context[CURRENT_SEGMENT];
+ if (segment == NULL)
+ {
+ /* Most likely this context was saved by a thread which was not
+ created using __splistack_makecontext and which has never
+ split the stack. The value 1 is a code used by
+ __splitstack_find to look at the initial stack. */
+ segment = (struct stack_segment *) (uintptr_type) 1;
+ }
+
+ return __splitstack_find (segment, sp, stack_size, next_segment, next_sp,
+ initial_sp);
+}
+
#endif /* !defined (inhibit_libc) */
+#endif /* not powerpc 32-bit */