aboutsummaryrefslogtreecommitdiff
path: root/libgcc/generic-morestack.c
diff options
context:
space:
mode:
authorian <ian@138bc75d-0d04-0410-961f-82ee72b054a4>2011-11-09 20:06:31 +0000
committerian <ian@138bc75d-0d04-0410-961f-82ee72b054a4>2011-11-09 20:06:31 +0000
commit23371076addef1c1a970ab4bb29c99ade876f289 (patch)
treeb16db4d1e1ec9a11ee2ee00a183a6daa8b383c59 /libgcc/generic-morestack.c
parentf3210a53394de39a8aa74ec9dcb23f2cc0551322 (diff)
libgcc/:
* generic-morestack.c: Include <string.h>. (uintptr_type): Define. (struct initial_sp): Add dont_block_signals field. Reduce size of extra array by 1. (allocate_segment): Set prev field to NULL. Don't set __morestack_current_segment or __morestack_segments. (__generic_morestack): Update current->prev and *pp after calling allocate_segment. (__morestack_block_signals): Don't do anything if dont_block_signals is set. (__morestack_unblock_signals): Likewise. (__generic_findstack): Check for initial_sp == NULL. Add casts to uintptr_type. (__splitstack_block_signals): New function. (enum __splitstack_content_offsets): Define. (__splitstack_getcontext, __splitstack_setcontext): New functions. (__splitstack_makecontext): New function. (__splitstack_block_signals_context): New function. (__splitstack_find_context): New function. * config/i386/morestack.S (__morestack_get_guard): New function. (__morestack_set_guard, __morestack_make_guard): New functions. * libgcc-std.ver.in: Add new functions to GCC_4.7.0. gcc/testsuite/: * lib/target-supports.exp (check_effective_target_ucontext_h): New procedure. * gcc.dg/split-5.c: New test. git-svn-id: svn+ssh://gcc.gnu.org/svn/gcc/trunk@181234 138bc75d-0d04-0410-961f-82ee72b054a4
Diffstat (limited to 'libgcc/generic-morestack.c')
-rw-r--r--libgcc/generic-morestack.c252
1 files changed, 233 insertions, 19 deletions
diff --git a/libgcc/generic-morestack.c b/libgcc/generic-morestack.c
index 07bc2a66073..00a3b1c3bf0 100644
--- a/libgcc/generic-morestack.c
+++ b/libgcc/generic-morestack.c
@@ -41,12 +41,15 @@ see the files COPYING3 and COPYING.RUNTIME respectively. If not, see
#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. */
@@ -88,14 +91,50 @@ extern void *
__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_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. */
@@ -138,8 +177,13 @@ struct initial_sp
/* 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.
@@ -339,18 +383,13 @@ allocate_segment (size_t frame_size)
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;
}
@@ -513,7 +552,11 @@ __generic_morestack (size_t *pframe_size, void *old_stack, size_t param_size)
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;
@@ -614,7 +657,9 @@ extern int pthread_sigmask (int, const sigset_t *, sigset_t *)
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
@@ -627,7 +672,9 @@ __morestack_block_signals (void)
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);
@@ -727,6 +774,10 @@ __generic_findstack (void *stack)
}
/* We have popped back to the original stack. */
+
+ if (__morestack_initial_sp.sp == NULL)
+ return 0;
+
#ifdef STACK_GROWS_DOWNWARD
if ((char *) stack >= (char *) __morestack_initial_sp.sp)
used = 0;
@@ -796,11 +847,14 @@ __splitstack_find (void *segment_arg, void *sp, size_t *len,
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
if ((char *) sp >= isp)
@@ -814,7 +868,7 @@ __splitstack_find (void *segment_arg, void *sp, size_t *len,
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;
@@ -826,8 +880,8 @@ __splitstack_find (void *segment_arg, void *sp, size_t *len,
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;
@@ -836,7 +890,7 @@ __splitstack_find (void *segment_arg, void *sp, size_t *len,
}
if (segment->prev == NULL)
- *next_segment = (void *) 1;
+ *next_segment = (void *) (uintptr_type) 1;
else
*next_segment = segment->prev;
@@ -878,4 +932,164 @@ __splitstack_find (void *segment_arg, void *sp, size_t *len,
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 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);
+}
+
+/* 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) */