generic-morestack.c: Include <string.h>.

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.

From-SVN: r181234
This commit is contained in:
Ian Lance Taylor 2011-11-09 20:06:31 +00:00 committed by Ian Lance Taylor
parent 8ee9fac21e
commit e14304eff5
7 changed files with 527 additions and 21 deletions

View File

@ -1,3 +1,9 @@
2011-11-09 Ian Lance Taylor <iant@google.com>
* lib/target-supports.exp (check_effective_target_ucontext_h): New
procedure.
* gcc.dg/split-5.c: New test.
2011-11-09 Patrick Marlier <patrick.marlier@gmail.com>
* gcc.dg/tm/memopt-1.c: Adjust regexp.

View File

@ -0,0 +1,171 @@
/* { dg-do run } */
/* { dg-require-effective-target split_stack } */
/* { dg-require-effective-target pthread_h } */
/* { dg-require-effective-target ucontext_h } */
/* { dg-options "-pthread -fsplit-stack" } */
#include <stdlib.h>
#include <pthread.h>
#include <ucontext.h>
extern void __splitstack_getcontext (void *context[10]);
extern void __splitstack_setcontext (void *context[10]);
extern void *__splitstack_makecontext (size_t, void *context[10], size_t *);
extern void __splitstack_block_signals (int *, int *);
extern void __splitstack_block_signals_context (void *context[10], int *,
int *);
extern void *__splitstack_find (void *, void *, size_t *, void **, void **,
void **);
extern void *__splitstack_find_context (void *context[10], size_t *, void **,
void **, void **);
static ucontext_t c1;
static void *s1[10];
static ucontext_t c2;
static void *s2[10];
static void swap (ucontext_t *, void *fs[10], ucontext_t *, void *ts[10])
__attribute__ ((no_split_stack));
static void
swap (ucontext_t *fu, void *fs[10], ucontext_t *tu, void *ts[10])
{
__splitstack_getcontext (fs);
__splitstack_setcontext (ts);
swapcontext (fu, tu);
__splitstack_setcontext (fs);
}
/* Use a noinline function to ensure that the buffer is not removed
from the stack. */
static void use_buffer (char *buf) __attribute__ ((noinline));
static void
use_buffer (char *buf)
{
buf[0] = '\0';
}
static void
down (int i, const char *msg, ucontext_t *me, void *mes[10],
ucontext_t *other, void *others[10])
{
char buf[10000];
if (i > 0)
{
use_buffer (buf);
swap (me, mes, other, others);
down (i - 1, msg, me, mes, other, others);
}
else
{
int c = 0;
void *stack;
size_t stack_size;
void *next_segment = NULL;
void *next_sp = NULL;
void *initial_sp = NULL;
stack = __splitstack_find_context (mes, &stack_size, &next_segment,
&next_sp, &initial_sp);
if (stack != NULL)
{
++c;
while (__splitstack_find (next_segment, next_sp, &stack_size,
&next_segment, &next_sp, &initial_sp)
!= NULL)
++c;
}
}
}
static void
go1 (void)
{
down (1000, "go1", &c1, s1, &c2, s2);
pthread_exit (NULL);
}
static void
go2 (void)
{
down (1000, "go2", &c2, s2, &c1, s1);
pthread_exit (NULL);
}
struct thread_context
{
ucontext_t *u;
void **s;
};
static void *start_thread (void *) __attribute__ ((no_split_stack));
static void *
start_thread (void *context)
{
struct thread_context *tc = (struct thread_context *) context;
int block;
block = 0;
__splitstack_block_signals (&block, NULL);
__splitstack_setcontext (tc->s);
setcontext (tc->u);
abort ();
}
int
main (int argc __attribute__ ((unused)), char **argv __attribute__ ((unused)))
{
pthread_t tid;
int err;
size_t size;
struct thread_context tc;
int block;
if (getcontext (&c1) < 0)
abort ();
c2 = c1;
c1.uc_stack.ss_sp = __splitstack_makecontext (8192, &s1[0], &size);
if (c1.uc_stack.ss_sp == NULL)
abort ();
c1.uc_stack.ss_flags = 0;
c1.uc_stack.ss_size = size;
c1.uc_link = NULL;
block = 0;
__splitstack_block_signals_context (&s1[0], &block, NULL);
makecontext (&c1, go1, 0);
c2.uc_stack.ss_sp = __splitstack_makecontext (8192, &s2[0], &size);
if (c2.uc_stack.ss_sp == NULL)
abort ();
c2.uc_stack.ss_flags = 0;
c2.uc_stack.ss_size = size;
c2.uc_link = NULL;
__splitstack_block_signals_context (&s2[0], &block, NULL);
makecontext (&c2, go2, 0);
block = 0;
__splitstack_block_signals (&block, NULL);
tc.u = &c1;
tc.s = &s1[0];
err = pthread_create (&tid, NULL, start_thread, &tc);
if (err != 0)
abort ();
err = pthread_join (tid, NULL);
if (err != 0)
abort ();
return 0;
}

View File

@ -4401,3 +4401,11 @@ proc check_effective_target_non_strict_align {} {
void foo(void) { z = (c *) y; }
} "-Wcast-align"]
}
# Return 1 if the target has <ucontext.h>.
proc check_effective_target_ucontext_h { } {
return [check_no_compiler_messages ucontext_h assembly {
#include <ucontext.h>
}]
}

View File

@ -1,3 +1,28 @@
2011-11-09 Ian Lance Taylor <iant@google.com>
* 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.
2011-11-09 Rainer Orth <ro@CeBiTec.Uni-Bielefeld.DE>
* config.host (i[34567]86-*-cygwin*): Move i386/t-mingw-pthread ...

View File

@ -1,5 +1,5 @@
# x86/x86_64 support for -fsplit-stack.
# Copyright (C) 2009, 2010 Free Software Foundation, Inc.
# Copyright (C) 2009, 2010, 2011 Free Software Foundation, Inc.
# Contributed by Ian Lance Taylor <iant@google.com>.
# This file is part of GCC.
@ -620,6 +620,82 @@ __stack_split_initialize:
.size __stack_split_initialize, . - __stack_split_initialize
#endif
# Routines to get and set the guard, for __splitstack_getcontext,
# __splitstack_setcontext, and __splitstack_makecontext.
# void *__morestack_get_guard (void) returns the current stack guard.
.text
.global __morestack_get_guard
.hidden __morestack_get_guard
#ifdef __ELF__
.type __morestack_get_guard,@function
#endif
__morestack_get_guard:
#ifndef __x86_64__
movl %gs:0x30,%eax
#else
#ifdef __LP64__
movq %fs:0x70,%rax
#else
movl %fs:0x40,%eax
#endif
#endif
ret
#ifdef __ELF__
.size __morestack_get_guard, . - __morestack_get_guard
#endif
# void __morestack_set_guard (void *) sets the stack guard.
.global __morestack_set_guard
.hidden __morestack_set_guard
#ifdef __ELF__
.type __morestack_set_guard,@function
#endif
__morestack_set_guard:
#ifndef __x86_64__
movl 4(%esp),%eax
movl %eax,%gs:0x30
#else
X86_64_SAVE_NEW_STACK_BOUNDARY (di)
#endif
ret
#ifdef __ELF__
.size __morestack_set_guard, . - __morestack_set_guard
#endif
# void *__morestack_make_guard (void *, size_t) returns the stack
# guard value for a stack.
.global __morestack_make_guard
.hidden __morestack_make_guard
#ifdef __ELF__
.type __morestack_make_guard,@function
#endif
__morestack_make_guard:
#ifndef __x86_64__
movl 4(%esp),%eax
subl 8(%esp),%eax
addl $BACKOFF,%eax
#else
subq %rsi,%rdi
addq $BACKOFF,%rdi
movq %rdi,%rax
#endif
ret
#ifdef __ELF__
.size __morestack_make_guard, . - __morestack_make_guard
#endif
# Make __stack_split_initialize a high priority constructor. FIXME:
# This is ELF specific.

View File

@ -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) */

View File

@ -1,5 +1,5 @@
# Copyright (C) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007,
# 2008, 2010 Free Software Foundation, Inc.
# 2008, 2010, 2011 Free Software Foundation, Inc.
#
# This file is part of GCC.
#
@ -1926,4 +1926,10 @@ GCC_4.7.0 {
__PFX__clrsbsi2
__PFX__clrsbdi2
__PFX__clrsbti2
__splitstack_block_signals
__splitstack_getcontext
__splitstack_setcontext
__splitstack_makecontext
__splitstack_block_signals_context
__splitstack_find_context
}