Mercurial > hg > CbC > CbC_gcc
diff libgcc/generic-morestack.c @ 111:04ced10e8804
gcc 7
author | kono |
---|---|
date | Fri, 27 Oct 2017 22:46:09 +0900 |
parents | 561a7518be6b |
children | 84e7813d76e9 |
line wrap: on
line diff
--- a/libgcc/generic-morestack.c Sun Aug 21 07:07:55 2011 +0900 +++ b/libgcc/generic-morestack.c Fri Oct 27 22:46:09 2017 +0900 @@ -1,5 +1,5 @@ /* Library support for -fsplit-stack. */ -/* Copyright (C) 2009, 2010, 2011 Free Software Foundation, Inc. +/* Copyright (C) 2009-2017 Free Software Foundation, Inc. Contributed by Ian Lance Taylor <iant@google.com>. This file is part of GCC. @@ -23,10 +23,14 @@ 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 effect is that people will not be able to use -fsplit-stack. That @@ -40,12 +44,15 @@ #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. */ @@ -87,14 +94,58 @@ __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. */ @@ -137,8 +188,13 @@ /* 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. @@ -325,7 +381,7 @@ { void *guard; -#ifdef STACK_GROWS_DOWNWARD +#ifdef __LIBGCC_STACK_GROWS_DOWNWARD__ guard = space; space = (char *) space + pagesize; #else @@ -338,18 +394,13 @@ 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; } @@ -448,7 +499,7 @@ 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); @@ -459,8 +510,8 @@ 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. */ @@ -501,6 +552,7 @@ char *to; void *ret; size_t i; + size_t aligned; current = __morestack_current_segment; @@ -512,7 +564,11 @@ current = *pp; if (current == NULL) - current = allocate_segment (frame_size); + { + current = allocate_segment (frame_size + param_size); + current->prev = __morestack_current_segment; + *pp = current; + } current->old_stack = old_stack; @@ -528,15 +584,19 @@ *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 @@ -571,7 +631,7 @@ 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; @@ -582,7 +642,7 @@ 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 @@ -613,7 +673,9 @@ 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 @@ -626,7 +688,9 @@ 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); @@ -717,7 +781,7 @@ && (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; @@ -726,7 +790,11 @@ } /* 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 @@ -795,13 +863,16 @@ 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; @@ -813,7 +884,7 @@ 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; @@ -825,8 +896,8 @@ 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; @@ -835,7 +906,7 @@ } if (segment->prev == NULL) - *next_segment = (void *) 1; + *next_segment = (void *) (uintptr_type) 1; else *next_segment = segment->prev; @@ -856,17 +927,30 @@ 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 @@ -877,4 +961,221 @@ 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 */