diff options
Diffstat (limited to 'libpthread/nptl/sysdeps/unix/sysv/linux/arm')
22 files changed, 2093 insertions, 0 deletions
| diff --git a/libpthread/nptl/sysdeps/unix/sysv/linux/arm/Makefile b/libpthread/nptl/sysdeps/unix/sysv/linux/arm/Makefile new file mode 100644 index 000000000..43a6fad84 --- /dev/null +++ b/libpthread/nptl/sysdeps/unix/sysv/linux/arm/Makefile @@ -0,0 +1,13 @@ +# Makefile for uClibc NPTL +# +# Copyright (C) 2005 Steven J. Hill <sjhill@uclibc.org> +# +# Licensed under the LGPL v2.1, see the file COPYING.LIB in this tarball. +# + +top_srcdir=../../../../../../../ +top_builddir=../../../../../../../ +all: objs +include $(top_builddir)Rules.mak +include Makefile.arch +include $(top_srcdir)Makerules diff --git a/libpthread/nptl/sysdeps/unix/sysv/linux/arm/Makefile.arch b/libpthread/nptl/sysdeps/unix/sysv/linux/arm/Makefile.arch new file mode 100644 index 000000000..2256d6b2e --- /dev/null +++ b/libpthread/nptl/sysdeps/unix/sysv/linux/arm/Makefile.arch @@ -0,0 +1,56 @@ +# Makefile for uClibc NPTL +# +# Copyright (C) 2006 Steven J. Hill <sjhill@uclibc.org> +# +# Licensed under the LGPL v2.1, see the file COPYING.LIB in this tarball. +# + +libpthread_SSRC = pt-vfork.S +libpthread_CSRC = pthread_once.c lowlevellock.c \ +                  pt-__syscall_rt_sigaction.c pt-__syscall_error.c + +libc_a_CSRC = fork.c +libc_a_SSRC = clone.S vfork.S + +ifeq ($(UCLIBC_HAS_STDIO_FUTEXES),y) +CFLAGS-fork.c = -D__USE_STDIO_FUTEXES__ +endif +CFLAGS-pthread_once.c = -DNOT_IN_libc=1 -DIS_IN_libpthread=1 +CFLAGS-lowlevellock.c = -DNOT_IN_libc=1 -DIS_IN_libpthread=1 + +ASFLAGS-pt-vfork.S = -DNOT_IN_libc=1 -DIS_IN_libpthread=1 -marm +# We always compile it in arm mode because of SAVE_PID macro +# This macro should be alternatively implemented in THUMB +# assembly. +ASFLAGS-vfork.S = -marm + +CFLAGS += $(SSP_ALL_CFLAGS) +#CFLAGS:=$(CFLAGS:-O1=-O2) + +LINUX_ARCH_DIR:=$(top_srcdir)libpthread/nptl/sysdeps/unix/sysv/linux/arm +LINUX_ARCH_OUT:=$(top_builddir)libpthread/nptl/sysdeps/unix/sysv/linux/arm + +LINUX_ARCH_OBJ:=$(patsubst %.S,$(LINUX_ARCH_OUT)/%.o,$(libpthread_SSRC)) +LINUX_ARCH_OBJ+=$(patsubst %.c,$(LINUX_ARCH_OUT)/%.o,$(libpthread_CSRC)) + +ifeq ($(DOPIC),y) +libpthread-a-y += $(LINUX_ARCH_OBJ:.o=.os) +else +libpthread-a-y += $(LINUX_ARCH_OBJ) +endif +libpthread-so-y += $(LINUX_ARCH_OBJ:.o=.oS) + +libpthread-nomulti-y+=$(LINUX_ARCH_OBJ) + +LIBC_LINUX_ARCH_OBJ:=$(patsubst %.c,$(LINUX_ARCH_OUT)/%.o,$(libc_a_CSRC)) +LIBC_LINUX_ARCH_OBJ+=$(patsubst %.S,$(LINUX_ARCH_OUT)/%.o,$(libc_a_SSRC)) + +libc-static-y+=$(LIBC_LINUX_ARCH_OBJ) +libc-shared-y+=$(LIBC_LINUX_ARCH_OBJ:.o=.oS) + +libc-nomulti-y+=$(LIBC_LINUX_ARCH_OBJ) + +objclean-y+=pthread_linux_arch_objclean + +pthread_linux_arch_objclean: +	$(RM) $(LINUX_ARCH_OUT)/*.{o,os,oS} diff --git a/libpthread/nptl/sysdeps/unix/sysv/linux/arm/bits/atomic.h b/libpthread/nptl/sysdeps/unix/sysv/linux/arm/bits/atomic.h new file mode 100644 index 000000000..49a935a52 --- /dev/null +++ b/libpthread/nptl/sysdeps/unix/sysv/linux/arm/bits/atomic.h @@ -0,0 +1,136 @@ +/* Copyright (C) 2002, 2003, 2004, 2005 Free Software Foundation, Inc. +   This file is part of the GNU C Library. + +   The GNU C Library is free software; you can redistribute it and/or +   modify it under the terms of the GNU Lesser General Public +   License as published by the Free Software Foundation; either +   version 2.1 of the License, or (at your option) any later version. + +   The GNU C Library is distributed in the hope that it will be useful, +   but WITHOUT ANY WARRANTY; without even the implied warranty of +   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU +   Lesser General Public License for more details. + +   You should have received a copy of the GNU Lesser General Public +   License along with the GNU C Library; if not, write to the Free +   Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA +   02111-1307 USA.  */ + +#include <stdint.h> +#include <sysdep.h> + + +typedef int8_t atomic8_t; +typedef uint8_t uatomic8_t; +typedef int_fast8_t atomic_fast8_t; +typedef uint_fast8_t uatomic_fast8_t; + +typedef int32_t atomic32_t; +typedef uint32_t uatomic32_t; +typedef int_fast32_t atomic_fast32_t; +typedef uint_fast32_t uatomic_fast32_t; + +typedef intptr_t atomicptr_t; +typedef uintptr_t uatomicptr_t; +typedef intmax_t atomic_max_t; +typedef uintmax_t uatomic_max_t; + +void __arm_link_error (void); + +#ifdef __thumb__ + +/* Note that to allow efficient implementation the arguemtns are reversed +   relative to atomic_exchange_acq.  */ +int __thumb_swpb (int newvalue, void *mem) +  attribute_hidden; +unsigned int __thumb_swp (unsigned int newvalue, void *mem) +  attribute_hidden; +unsigned int __thumb_cmpxchg (unsigned int oldval, unsigned int newval, void *mem) +  attribute_hidden; + +#define atomic_exchange_acq(mem, newvalue)				      \ +  ({ __typeof (*mem) result;						      \ +     if (sizeof (*mem) == 1)						      \ +       result = __thumb_swpb (newvalue, mem);				      \ +     else if (sizeof (*mem) == 4)					      \ +       result = __thumb_swp (newvalue, mem);				      \ +     else								      \ +       {								      \ +	 result = 0;							      \ +	 abort ();							      \ +       }								      \ +     result; }) + +#define __arch_compare_and_exchange_val_8_acq(mem, newval, oldval) \ +  ({ __arm_link_error (); oldval; }) + +#define __arch_compare_and_exchange_val_16_acq(mem, newval, oldval) \ +  ({ __arm_link_error (); oldval; }) + +#define __arch_compare_and_exchange_val_32_acq(mem, newval, oldval) \ +  ((__typeof (oldval)) __thumb_cmpxchg (oldval, newval, mem)) + +#define __arch_compare_and_exchange_val_64_acq(mem, newval, oldval) \ +  ({ __arm_link_error (); oldval; }) + +#else +/* ARM mode.  */ + +#define atomic_exchange_acq(mem, newvalue)				      \ +  ({ __typeof (*mem) _xchg_result;					      \ +     if (sizeof (*mem) == 1)						      \ +       __asm__ __volatile__ ("swpb %0, %1, [%2]"			      \ +			     : "=&r,&r" (_xchg_result)			      \ +			     : "r,0" (newvalue), "r,r" (mem) : "memory");     \ +     else if (sizeof (*mem) == 4)					      \ +       __asm__ __volatile__ ("swp %0, %1, [%2]"				      \ +			     : "=&r,&r" (_xchg_result)			      \ +			     : "r,0" (newvalue), "r,r" (mem) : "memory");     \ +     else								      \ +       {								      \ +	 _xchg_result = 0;						      \ +	 abort ();							      \ +       }								      \ +     _xchg_result; }) + +/* Atomic compare and exchange.  This sequence relies on the kernel to +   provide a compare and exchange operation which is atomic on the +   current architecture, either via cleverness on pre-ARMv6 or via +   ldrex / strex on ARMv6.  */ + +#define __arch_compare_and_exchange_val_8_acq(mem, newval, oldval) \ +  ({ __arm_link_error (); oldval; }) + +#define __arch_compare_and_exchange_val_16_acq(mem, newval, oldval) \ +  ({ __arm_link_error (); oldval; }) + +/* It doesn't matter what register is used for a_oldval2, but we must +   specify one to work around GCC PR rtl-optimization/21223.  Otherwise +   it may cause a_oldval or a_tmp to be moved to a different register.  */ + +#define __arch_compare_and_exchange_val_32_acq(mem, newval, oldval) \ +  ({ register __typeof (oldval) a_oldval asm ("r0");			      \ +     register __typeof (oldval) a_newval asm ("r1") = (newval);		      \ +     register __typeof (mem) a_ptr asm ("r2") = (mem);			      \ +     register __typeof (oldval) a_tmp asm ("r3");			      \ +     register __typeof (oldval) a_oldval2 asm ("r4") = (oldval);	      \ +     __asm__ __volatile__						      \ +	     ("0:\tldr\t%1,[%3]\n\t"					      \ +	      "cmp\t%1, %4\n\t"						      \ +	      "bne\t1f\n\t"						      \ +	      "mov\t%0, %4\n\t"						      \ +	      "mov\t%1, #0xffff0fff\n\t"				      \ +	      "mov\tlr, pc\n\t"						      \ +	      "add\tpc, %1, #(0xffff0fc0 - 0xffff0fff)\n\t"		      \ +	      "bcc\t0b\n\t"						      \ +	      "mov\t%1, %4\n\t"						      \ +	      "1:"							      \ +	      : "=&r" (a_oldval), "=&r" (a_tmp)				      \ +	      : "r" (a_newval), "r" (a_ptr), "r" (a_oldval2)		      \ +	      : "ip", "lr", "cc", "memory");				      \ +     a_tmp; }) + +#define __arch_compare_and_exchange_val_64_acq(mem, newval, oldval) \ +  ({ __arm_link_error (); oldval; }) + +#endif /* __thumb__ */ diff --git a/libpthread/nptl/sysdeps/unix/sysv/linux/arm/bits/pthreadtypes.h b/libpthread/nptl/sysdeps/unix/sysv/linux/arm/bits/pthreadtypes.h new file mode 100644 index 000000000..ea8d6a2f0 --- /dev/null +++ b/libpthread/nptl/sysdeps/unix/sysv/linux/arm/bits/pthreadtypes.h @@ -0,0 +1,167 @@ +/* Copyright (C) 2002, 2003, 2004, 2005, 2006 Free Software Foundation, Inc. +   This file is part of the GNU C Library. + +   The GNU C Library is free software; you can redistribute it and/or +   modify it under the terms of the GNU Lesser General Public +   License as published by the Free Software Foundation; either +   version 2.1 of the License, or (at your option) any later version. + +   The GNU C Library is distributed in the hope that it will be useful, +   but WITHOUT ANY WARRANTY; without even the implied warranty of +   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU +   Lesser General Public License for more details. + +   You should have received a copy of the GNU Lesser General Public +   License along with the GNU C Library; if not, write to the Free +   Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA +   02111-1307 USA.  */ + +#ifndef _BITS_PTHREADTYPES_H +#define _BITS_PTHREADTYPES_H	1 + +#define __SIZEOF_PTHREAD_ATTR_T 36 +#define __SIZEOF_PTHREAD_MUTEX_T 24 +#define __SIZEOF_PTHREAD_MUTEXATTR_T 4 +#define __SIZEOF_PTHREAD_COND_T 48 +#define __SIZEOF_PTHREAD_COND_COMPAT_T 12 +#define __SIZEOF_PTHREAD_CONDATTR_T 4 +#define __SIZEOF_PTHREAD_RWLOCK_T 32 +#define __SIZEOF_PTHREAD_RWLOCKATTR_T 8 +#define __SIZEOF_PTHREAD_BARRIER_T 20 +#define __SIZEOF_PTHREAD_BARRIERATTR_T 4 + + +/* Thread identifiers.  The structure of the attribute type is not +   exposed on purpose.  */ +typedef unsigned long int pthread_t; + + +typedef union +{ +  char __size[__SIZEOF_PTHREAD_ATTR_T]; +  long int __align; +} pthread_attr_t; + + +typedef struct __pthread_internal_slist +{ +  struct __pthread_internal_slist *__next; +} __pthread_slist_t; + + +/* Data structures for mutex handling.  The structure of the attribute +   type is not exposed on purpose.  */ +typedef union +{ +  struct __pthread_mutex_s +  { +    int __lock; +    unsigned int __count; +    int __owner; +    /* KIND must stay at this position in the structure to maintain +       binary compatibility.  */ +    int __kind; +    unsigned int __nusers; +    __extension__ union +    { +      int __spins; +      __pthread_slist_t __list; +    }; +  } __data; +  char __size[__SIZEOF_PTHREAD_MUTEX_T]; +  long int __align; +} pthread_mutex_t; + +typedef union +{ +  char __size[__SIZEOF_PTHREAD_MUTEXATTR_T]; +  long int __align; +} pthread_mutexattr_t; + + +/* Data structure for conditional variable handling.  The structure of +   the attribute type is not exposed on purpose.  */ +typedef union +{ +  struct +  { +    int __lock; +    unsigned int __futex; +    __extension__ unsigned long long int __total_seq; +    __extension__ unsigned long long int __wakeup_seq; +    __extension__ unsigned long long int __woken_seq; +    void *__mutex; +    unsigned int __nwaiters; +    unsigned int __broadcast_seq; +  } __data; +  char __size[__SIZEOF_PTHREAD_COND_T]; +  __extension__ long long int __align; +} pthread_cond_t; + +typedef union +{ +  char __size[__SIZEOF_PTHREAD_CONDATTR_T]; +  long int __align; +} pthread_condattr_t; + + +/* Keys for thread-specific data */ +typedef unsigned int pthread_key_t; + + +/* Once-only execution */ +typedef int pthread_once_t; + + +#if defined __USE_UNIX98 || defined __USE_XOPEN2K +/* Data structure for read-write lock variable handling.  The +   structure of the attribute type is not exposed on purpose.  */ +typedef union +{ +  struct +  { +    int __lock; +    unsigned int __nr_readers; +    unsigned int __readers_wakeup; +    unsigned int __writer_wakeup; +    unsigned int __nr_readers_queued; +    unsigned int __nr_writers_queued; +    /* FLAGS must stay at this position in the structure to maintain +       binary compatibility.  */ +    unsigned int __flags; +    int __writer; +  } __data; +  char __size[__SIZEOF_PTHREAD_RWLOCK_T]; +  long int __align; +} pthread_rwlock_t; + +typedef union +{ +  char __size[__SIZEOF_PTHREAD_RWLOCKATTR_T]; +  long int __align; +} pthread_rwlockattr_t; +#endif + + +#ifdef __USE_XOPEN2K +/* POSIX spinlock data type.  */ +typedef volatile int pthread_spinlock_t; + + +/* POSIX barriers data type.  The structure of the type is +   deliberately not exposed.  */ +typedef union +{ +  char __size[__SIZEOF_PTHREAD_BARRIER_T]; +  long int __align; +} pthread_barrier_t; + +typedef union +{ +  char __size[__SIZEOF_PTHREAD_BARRIERATTR_T]; +  int __align; +} pthread_barrierattr_t; +#endif + + +#endif	/* bits/pthreadtypes.h */ diff --git a/libpthread/nptl/sysdeps/unix/sysv/linux/arm/bits/semaphore.h b/libpthread/nptl/sysdeps/unix/sysv/linux/arm/bits/semaphore.h new file mode 100644 index 000000000..3fc647d31 --- /dev/null +++ b/libpthread/nptl/sysdeps/unix/sysv/linux/arm/bits/semaphore.h @@ -0,0 +1,38 @@ +/* Copyright (C) 2002, 2005 Free Software Foundation, Inc. +   This file is part of the GNU C Library. + +   The GNU C Library is free software; you can redistribute it and/or +   modify it under the terms of the GNU Lesser General Public +   License as published by the Free Software Foundation; either +   version 2.1 of the License, or (at your option) any later version. + +   The GNU C Library is distributed in the hope that it will be useful, +   but WITHOUT ANY WARRANTY; without even the implied warranty of +   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU +   Lesser General Public License for more details. + +   You should have received a copy of the GNU Lesser General Public +   License along with the GNU C Library; if not, write to the Free +   Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA +   02111-1307 USA.  */ + +#ifndef _SEMAPHORE_H +# error "Never use <bits/semaphore.h> directly; include <semaphore.h> instead." +#endif + + +#define __SIZEOF_SEM_T	16 + + +/* Value returned if `sem_open' failed.  */ +#define SEM_FAILED      ((sem_t *) 0) + +/* Maximum value the semaphore can have.  */ +#define SEM_VALUE_MAX   ((int) ((~0u) >> 1)) + + +typedef union +{ +  char __size[__SIZEOF_SEM_T]; +  long int __align; +} sem_t; diff --git a/libpthread/nptl/sysdeps/unix/sysv/linux/arm/clone.S b/libpthread/nptl/sysdeps/unix/sysv/linux/arm/clone.S new file mode 100644 index 000000000..23227eb24 --- /dev/null +++ b/libpthread/nptl/sysdeps/unix/sysv/linux/arm/clone.S @@ -0,0 +1,3 @@ +#define RESET_PID +#include <tcb-offsets.h> +#include "../../../../../../../libc/sysdeps/linux/arm/clone.S" diff --git a/libpthread/nptl/sysdeps/unix/sysv/linux/arm/createthread.c b/libpthread/nptl/sysdeps/unix/sysv/linux/arm/createthread.c new file mode 100644 index 000000000..2d4355980 --- /dev/null +++ b/libpthread/nptl/sysdeps/unix/sysv/linux/arm/createthread.c @@ -0,0 +1,23 @@ +/* Copyright (C) 2005 Free Software Foundation, Inc. +   This file is part of the GNU C Library. + +   The GNU C Library is free software; you can redistribute it and/or +   modify it under the terms of the GNU Lesser General Public +   License as published by the Free Software Foundation; either +   version 2.1 of the License, or (at your option) any later version. + +   The GNU C Library is distributed in the hope that it will be useful, +   but WITHOUT ANY WARRANTY; without even the implied warranty of +   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.	 See the GNU +   Lesser General Public License for more details. + +   You should have received a copy of the GNU Lesser General Public +   License along with the GNU C Library; if not, write to the Free +   Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA +   02111-1307 USA.  */ + +/* Value passed to 'clone' for initialization of the thread register.  */ +#define TLS_VALUE (pd + 1) + +/* Get the real implementation.	 */ +#include <sysdeps/pthread/createthread.c> diff --git a/libpthread/nptl/sysdeps/unix/sysv/linux/arm/fork.c b/libpthread/nptl/sysdeps/unix/sysv/linux/arm/fork.c new file mode 100644 index 000000000..1c8f4c43a --- /dev/null +++ b/libpthread/nptl/sysdeps/unix/sysv/linux/arm/fork.c @@ -0,0 +1,31 @@ +/* Copyright (C) 2005 Free Software Foundation, Inc. +   This file is part of the GNU C Library. +   Contributed by Phil Blundell <pb@nexus.co.uk>, 2005 + +   The GNU C Library is free software; you can redistribute it and/or +   modify it under the terms of the GNU Lesser General Public +   License as published by the Free Software Foundation; either +   version 2.1 of the License, or (at your option) any later version. + +   The GNU C Library is distributed in the hope that it will be useful, +   but WITHOUT ANY WARRANTY; without even the implied warranty of +   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU +   Lesser General Public License for more details. + +   You should have received a copy of the GNU Lesser General Public +   License along with the GNU C Library; if not, write to the Free +   Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA +   02111-1307 USA.  */ + +#include <sched.h> +#include <signal.h> +#include <sysdep.h> +#include <tls.h> + + +#define ARCH_FORK()							\ +  INLINE_SYSCALL (clone, 5,						\ +		  CLONE_CHILD_SETTID | CLONE_CHILD_CLEARTID | SIGCHLD,	\ +		  NULL, NULL, NULL, &THREAD_SELF->tid) + +#include "../fork.c" diff --git a/libpthread/nptl/sysdeps/unix/sysv/linux/arm/lowlevellock.c b/libpthread/nptl/sysdeps/unix/sysv/linux/arm/lowlevellock.c new file mode 100644 index 000000000..74be18855 --- /dev/null +++ b/libpthread/nptl/sysdeps/unix/sysv/linux/arm/lowlevellock.c @@ -0,0 +1,118 @@ +/* low level locking for pthread library.  Generic futex-using version. +   Copyright (C) 2003, 2005 Free Software Foundation, Inc. +   This file is part of the GNU C Library. + +   The GNU C Library is free software; you can redistribute it and/or +   modify it under the terms of the GNU Lesser General Public +   License as published by the Free Software Foundation; either +   version 2.1 of the License, or (at your option) any later version. + +   The GNU C Library is distributed in the hope that it will be useful, +   but WITHOUT ANY WARRANTY; without even the implied warranty of +   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.	 See the GNU +   Lesser General Public License for more details. + +   You should have received a copy of the GNU Lesser General Public +   License along with the GNU C Library; if not, write to the Free +   Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA +   02111-1307 USA.  */ + +#include <errno.h> +#include <sysdep.h> +#include <lowlevellock.h> +#include <sys/time.h> + +int +__lll_timedlock_wait (int *futex, const struct timespec *abstime) +{ +  struct timespec rt; + +  /* Reject invalid timeouts.  */ +  if (abstime->tv_nsec < 0 || abstime->tv_nsec >= 1000000000) +    return EINVAL; + +  /* Upgrade the lock.  */ +  if (atomic_exchange_acq (futex, 2) == 0) +    return 0; + +  do +    { +      struct timeval tv; + +      /* Get the current time.  */ +      (void) gettimeofday (&tv, NULL); + +      /* Compute relative timeout.  */ +      rt.tv_sec = abstime->tv_sec - tv.tv_sec; +      rt.tv_nsec = abstime->tv_nsec - tv.tv_usec * 1000; +      if (rt.tv_nsec < 0) +	{ +	  rt.tv_nsec += 1000000000; +	  --rt.tv_sec; +	} + +      /* Already timed out?  */ +      if (rt.tv_sec < 0) +	return ETIMEDOUT; + +      lll_futex_timed_wait (futex, 2, &rt); +    } +  while (atomic_exchange_acq (futex, 2) != 0); + +  return 0; +} + + +/* These don't get included in libc.so  */ +#ifdef IS_IN_libpthread +int +lll_unlock_wake_cb (int *futex) +{ +  int val = atomic_exchange_rel (futex, 0); + +  if (__builtin_expect (val > 1, 0)) +    lll_futex_wake (futex, 1); + +  return 0; +} + + +int +__lll_timedwait_tid (int *tidp, const struct timespec *abstime) +{ +  int tid; + +  if (abstime->tv_nsec < 0 || abstime->tv_nsec >= 1000000000) +    return EINVAL; + +  /* Repeat until thread terminated.  */ +  while ((tid = *tidp) != 0) +    { +      struct timeval tv; +      struct timespec rt; + +      /* Get the current time.  */ +      (void) gettimeofday (&tv, NULL); + +      /* Compute relative timeout.  */ +      rt.tv_sec = abstime->tv_sec - tv.tv_sec; +      rt.tv_nsec = abstime->tv_nsec - tv.tv_usec * 1000; +      if (rt.tv_nsec < 0) +	{ +	  rt.tv_nsec += 1000000000; +	  --rt.tv_sec; +	} + +      /* Already timed out?  */ +      if (rt.tv_sec < 0) +	return ETIMEDOUT; + +      /* Wait until thread terminates.  */ +      if (lll_futex_timed_wait (tidp, tid, &rt) == -ETIMEDOUT) +	return ETIMEDOUT; +    } + +  return 0; +} + +#endif diff --git a/libpthread/nptl/sysdeps/unix/sysv/linux/arm/lowlevellock.h b/libpthread/nptl/sysdeps/unix/sysv/linux/arm/lowlevellock.h new file mode 100644 index 000000000..79f3ddeca --- /dev/null +++ b/libpthread/nptl/sysdeps/unix/sysv/linux/arm/lowlevellock.h @@ -0,0 +1,339 @@ +/* Copyright (C) 2005, 2006 Free Software Foundation, Inc. +   This file is part of the GNU C Library. + +   The GNU C Library is free software; you can redistribute it and/or +   modify it under the terms of the GNU Lesser General Public +   License as published by the Free Software Foundation; either +   version 2.1 of the License, or (at your option) any later version. + +   The GNU C Library is distributed in the hope that it will be useful, +   but WITHOUT ANY WARRANTY; without even the implied warranty of +   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.	 See the GNU +   Lesser General Public License for more details. + +   You should have received a copy of the GNU Lesser General Public +   License along with the GNU C Libr	\ary; if not, write to the Free +   Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA +   02111-1307 USA.  */ + +#ifndef _LOWLEVELLOCK_H +#define _LOWLEVELLOCK_H	1 + +#include <time.h> +#include <sys/param.h> +#include <bits/pthreadtypes.h> +#include <atomic.h> +#include <sysdep.h> + +#define FUTEX_WAIT		0 +#define FUTEX_WAKE		1 +#define FUTEX_REQUEUE		3 +#define FUTEX_CMP_REQUEUE	4 +#define FUTEX_WAKE_OP		5 +#define FUTEX_OP_CLEAR_WAKE_IF_GT_ONE	((4 << 24) | 1) + +/* Initializer for compatibility lock.	*/ +#define LLL_MUTEX_LOCK_INITIALIZER (0) + +#define lll_futex_wait(futexp, val) \ +  ({									      \ +    INTERNAL_SYSCALL_DECL (__err);					      \ +    long int __ret;							      \ +    __ret = INTERNAL_SYSCALL (futex, __err, 4,				      \ +			      (futexp), FUTEX_WAIT, (val), 0);		      \ +    __ret;								      \ +  }) + +#define lll_futex_timed_wait(futexp, val, timespec) \ +  ({									      \ +    INTERNAL_SYSCALL_DECL (__err);					      \ +    long int __ret;							      \ +    __ret = INTERNAL_SYSCALL (futex, __err, 4,				      \ +			      (futexp), FUTEX_WAIT, (val), (timespec));	      \ +    __ret;								      \ +  }) + +#define lll_futex_wake(futexp, nr) \ +  ({									      \ +    INTERNAL_SYSCALL_DECL (__err);					      \ +    long int __ret;							      \ +    __ret = INTERNAL_SYSCALL (futex, __err, 4,				      \ +			      (futexp), FUTEX_WAKE, (nr), 0);		      \ +    __ret;								      \ +  }) + +#define lll_robust_mutex_dead(futexv) \ +  do									      \ +    {									      \ +      int *__futexp = &(futexv);					      \ +      atomic_or (__futexp, FUTEX_OWNER_DIED);				      \ +      lll_futex_wake (__futexp, 1);					      \ +    }									      \ +  while (0) + +/* Returns non-zero if error happened, zero if success.  */ +#define lll_futex_requeue(futexp, nr_wake, nr_move, mutex, val) \ +  ({									      \ +    INTERNAL_SYSCALL_DECL (__err);					      \ +    long int __ret;							      \ +    __ret = INTERNAL_SYSCALL (futex, __err, 6,				      \ +			      (futexp), FUTEX_CMP_REQUEUE, (nr_wake),	      \ +			      (nr_move), (mutex), (val));		      \ +    __ret;								      \ +  }) + + +/* Returns non-zero if error happened, zero if success.  */ +#define lll_futex_wake_unlock(futexp, nr_wake, nr_wake2, futexp2) \ +  ({									      \ +    INTERNAL_SYSCALL_DECL (__err);					      \ +    long int __ret;							      \ +    __ret = INTERNAL_SYSCALL (futex, __err, 6,				      \ +			      (futexp), FUTEX_WAKE_OP, (nr_wake),	      \ +			      (nr_wake2), (futexp2),			      \ +			      FUTEX_OP_CLEAR_WAKE_IF_GT_ONE);		      \ +    __ret;								      \ +  }) + + +static inline int __attribute__((always_inline)) +__lll_mutex_trylock (int *futex) +{ +  int flag = 1, old; +#ifdef __thumb__ +  old = atomic_exchange_acq (futex, flag); +  if (old < 1) +    flag = 0; +  else if (old > 1) +    flag = atomic_exchange_acq (futex, old); +#else +  __asm__ __volatile__ ( +    "\tswp	%[old], %[flag], [%[futex]]	@ try to take the lock\n" +    "\tcmp	%[old], #1			@ check old lock value\n" +    "\tmovlo	%[flag], #0			@ if we got it, return 0\n" +    "\tswphi	%[flag], %[old], [%[futex]]	@ if it was contested,\n" +    "						@ restore the contested flag,\n" +    "						@ and check whether that won." +    : [futex] "+&r" (futex), [flag] "+&r" (flag), [old] "=&r" (old) +    : : "memory" ); +#endif + +  return flag; +} +#define lll_mutex_trylock(lock)	__lll_mutex_trylock (&(lock)) + + +static inline int __attribute__((always_inline)) +__lll_mutex_cond_trylock (int *futex) +{ +  int flag = 2, old; +#ifdef __thumb__ +  old = atomic_exchange_acq (futex, flag); +  if (old < 1) +    flag = 0; +  else if (old > 1) +    flag = atomic_exchange_acq (futex, old); +#else +  __asm__ __volatile__ ( +    "\tswp	%[old], %[flag], [%[futex]]	@ try to take the lock\n" +    "\tcmp	%[old], #1			@ check old lock value\n" +    "\tmovlo	%[flag], #0			@ if we got it, return 0\n" +    "\tswphi	%[flag], %[old], [%[futex]]	@ if it was contested,\n" +    "						@ restore the contested flag,\n" +    "						@ and check whether that won." +    : [futex] "+&r" (futex), [flag] "+&r" (flag), [old] "=&r" (old) +    : : "memory" ); +#endif + +  return flag; +} +#define lll_mutex_cond_trylock(lock)	__lll_mutex_cond_trylock (&(lock)) + + +static inline int __attribute__((always_inline)) +__lll_robust_mutex_trylock(int *futex, int id) +{ +  return atomic_compare_and_exchange_val_acq (futex, id, 0) != 0; +} +#define lll_robust_mutex_trylock(lock, id) \ +  __lll_robust_mutex_trylock (&(lock), id) + +extern int __lll_robust_lock_wait (int *futex) attribute_hidden; + +static inline void __attribute__((always_inline)) +__lll_mutex_lock (int *futex) +{ +  int val = atomic_exchange_acq (futex, 1); + +  if (__builtin_expect (val != 0, 0)) +    { +      while (atomic_exchange_acq (futex, 2) != 0) +	lll_futex_wait (futex, 2); +    } +} +#define lll_mutex_lock(futex) __lll_mutex_lock (&(futex)) + + +static inline int __attribute__ ((always_inline)) +__lll_robust_mutex_lock (int *futex, int id) +{ +  int result = 0; +  if (atomic_compare_and_exchange_bool_acq (futex, id, 0) != 0) +    result = __lll_robust_lock_wait (futex); +  return result; +} +#define lll_robust_mutex_lock(futex, id) \ +  __lll_robust_mutex_lock (&(futex), id) + + +static inline void __attribute__ ((always_inline)) +__lll_mutex_cond_lock (int *futex) +{ +  int val = atomic_exchange_acq (futex, 2); + +  if (__builtin_expect (val != 0, 0)) +    { +      while (atomic_exchange_acq (futex, 2) != 0) +	lll_futex_wait (futex, 2); +    } +} +#define lll_mutex_cond_lock(futex) __lll_mutex_cond_lock (&(futex)) + + +#define lll_robust_mutex_cond_lock(futex, id) \ +  __lll_robust_mutex_lock (&(futex), (id) | FUTEX_WAITERS) + + +extern int __lll_timedlock_wait (int *futex, const struct timespec *) +	attribute_hidden; +extern int __lll_robust_timedlock_wait (int *futex, const struct timespec *) +	attribute_hidden; + +static inline int __attribute__ ((always_inline)) +__lll_mutex_timedlock (int *futex, const struct timespec *abstime) +{ +  int result = 0; +  int val = atomic_exchange_acq (futex, 1); + +  if (__builtin_expect (val != 0, 0)) +    result = __lll_timedlock_wait (futex, abstime); +  return result; +} +#define lll_mutex_timedlock(futex, abstime) \ +  __lll_mutex_timedlock (&(futex), abstime) + + +static inline int __attribute__ ((always_inline)) +__lll_robust_mutex_timedlock (int *futex, const struct timespec *abstime, +			      int id) +{ +  int result = 0; +  if (atomic_compare_and_exchange_bool_acq (futex, id, 0) != 0) +    result = __lll_robust_timedlock_wait (futex, abstime); +  return result; +} +#define lll_robust_mutex_timedlock(futex, abstime, id) \ +  __lll_robust_mutex_timedlock (&(futex), abstime, id) + + +static inline void __attribute__ ((always_inline)) +__lll_mutex_unlock (int *futex) +{ +  int val = atomic_exchange_rel (futex, 0); +  if (__builtin_expect (val > 1, 0)) +    lll_futex_wake (futex, 1); +} +#define lll_mutex_unlock(futex) __lll_mutex_unlock(&(futex)) + + +static inline void __attribute__ ((always_inline)) +__lll_robust_mutex_unlock (int *futex, int mask) +{ +  int val = atomic_exchange_rel (futex, 0); +  if (__builtin_expect (val & mask, 0)) +    lll_futex_wake (futex, 1); +} +#define lll_robust_mutex_unlock(futex) \ +  __lll_robust_mutex_unlock(&(futex), FUTEX_WAITERS) + + +static inline void __attribute__ ((always_inline)) +__lll_mutex_unlock_force (int *futex) +{ +  (void) atomic_exchange_rel (futex, 0); +  lll_futex_wake (futex, 1); +} +#define lll_mutex_unlock_force(futex) __lll_mutex_unlock_force(&(futex)) + + +#define lll_mutex_islocked(futex) \ +  (futex != 0) + + +/* Our internal lock implementation is identical to the binary-compatible +   mutex implementation. */ + +/* Type for lock object.  */ +typedef int lll_lock_t; + +/* Initializers for lock.  */ +#define LLL_LOCK_INITIALIZER		(0) +#define LLL_LOCK_INITIALIZER_LOCKED	(1) + +extern int lll_unlock_wake_cb (int *__futex) attribute_hidden; + +/* The states of a lock are: +    0  -  untaken +    1  -  taken by one user +   >1  -  taken by more users */ + +#define lll_trylock(lock)	lll_mutex_trylock (lock) +#define lll_lock(lock)		lll_mutex_lock (lock) +#define lll_unlock(lock)	lll_mutex_unlock (lock) +#define lll_islocked(lock)	lll_mutex_islocked (lock) + +/* The kernel notifies a process which uses CLONE_CLEARTID via futex +   wakeup when the clone terminates.  The memory location contains the +   thread ID while the clone is running and is reset to zero +   afterwards.	*/ +#define lll_wait_tid(tid) \ +  do {					\ +    __typeof (tid) __tid;		\ +    while ((__tid = (tid)) != 0)	\ +      lll_futex_wait (&(tid), __tid);	\ +  } while (0) + +extern int __lll_timedwait_tid (int *, const struct timespec *) +     attribute_hidden; + +#define lll_timedwait_tid(tid, abstime) \ +  ({							\ +    int __res = 0;					\ +    if ((tid) != 0)					\ +      __res = __lll_timedwait_tid (&(tid), (abstime));	\ +    __res;						\ +  }) + + +/* Conditional variable handling.  */ + +extern void __lll_cond_wait (pthread_cond_t *cond) +     attribute_hidden; +extern int __lll_cond_timedwait (pthread_cond_t *cond, +				 const struct timespec *abstime) +     attribute_hidden; +extern void __lll_cond_wake (pthread_cond_t *cond) +     attribute_hidden; +extern void __lll_cond_broadcast (pthread_cond_t *cond) +     attribute_hidden; + +#define lll_cond_wait(cond) \ +  __lll_cond_wait (cond) +#define lll_cond_timedwait(cond, abstime) \ +  __lll_cond_timedwait (cond, abstime) +#define lll_cond_wake(cond) \ +  __lll_cond_wake (cond) +#define lll_cond_broadcast(cond) \ +  __lll_cond_broadcast (cond) + +#endif	/* lowlevellock.h */ diff --git a/libpthread/nptl/sysdeps/unix/sysv/linux/arm/nptl-aeabi_unwind_cpp_pr1.c b/libpthread/nptl/sysdeps/unix/sysv/linux/arm/nptl-aeabi_unwind_cpp_pr1.c new file mode 100644 index 000000000..7b8352243 --- /dev/null +++ b/libpthread/nptl/sysdeps/unix/sysv/linux/arm/nptl-aeabi_unwind_cpp_pr1.c @@ -0,0 +1 @@ +#include <aeabi_unwind_cpp_pr1.c> diff --git a/libpthread/nptl/sysdeps/unix/sysv/linux/arm/pt-__syscall_error.c b/libpthread/nptl/sysdeps/unix/sysv/linux/arm/pt-__syscall_error.c new file mode 100644 index 000000000..5a48a9b2e --- /dev/null +++ b/libpthread/nptl/sysdeps/unix/sysv/linux/arm/pt-__syscall_error.c @@ -0,0 +1 @@ +#include <../../../../../../../libc/sysdeps/linux/arm/__syscall_error.c> diff --git a/libpthread/nptl/sysdeps/unix/sysv/linux/arm/pt-__syscall_rt_sigaction.c b/libpthread/nptl/sysdeps/unix/sysv/linux/arm/pt-__syscall_rt_sigaction.c new file mode 100644 index 000000000..50137c84a --- /dev/null +++ b/libpthread/nptl/sysdeps/unix/sysv/linux/arm/pt-__syscall_rt_sigaction.c @@ -0,0 +1 @@ +#include <../../../../../../../libc/sysdeps/linux/common/__syscall_rt_sigaction.c> diff --git a/libpthread/nptl/sysdeps/unix/sysv/linux/arm/pt-gettimeofday.c b/libpthread/nptl/sysdeps/unix/sysv/linux/arm/pt-gettimeofday.c new file mode 100644 index 000000000..08710f1c8 --- /dev/null +++ b/libpthread/nptl/sysdeps/unix/sysv/linux/arm/pt-gettimeofday.c @@ -0,0 +1,5 @@ +#include <sys/syscall.h> +#include <sys/time.h> + +int gettimeofday (struct timeval *, struct timezone *) attribute_hidden; +_syscall2(int, gettimeofday, struct timeval *, tv, struct timezone *, tz); diff --git a/libpthread/nptl/sysdeps/unix/sysv/linux/arm/pt-vfork.S b/libpthread/nptl/sysdeps/unix/sysv/linux/arm/pt-vfork.S new file mode 100644 index 000000000..9764e9ee2 --- /dev/null +++ b/libpthread/nptl/sysdeps/unix/sysv/linux/arm/pt-vfork.S @@ -0,0 +1,38 @@ +/* Copyright (C) 2005 Free Software Foundation, Inc. +   This file is part of the GNU C Library. + +   The GNU C Library is free software; you can redistribute it and/or +   modify it under the terms of the GNU Lesser General Public +   License as published by the Free Software Foundation; either +   version 2.1 of the License, or (at your option) any later version. + +   The GNU C Library is distributed in the hope that it will be useful, +   but WITHOUT ANY WARRANTY; without even the implied warranty of +   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU +   Lesser General Public License for more details. + +   You should have received a copy of the GNU Lesser General Public +   License along with the GNU C Library; if not, write to the Free +   Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA +   02111-1307 USA.  */ + +#include <tcb-offsets.h> + +/* Save the PID value.  */ +#define SAVE_PID \ +	str	lr, [sp, #-4]!;		/* Save LR.  */			\ +	mov	r0, #0xffff0fff;	/* Point to the high page.  */	\ +	mov	lr, pc;			/* Save our return address.  */	\ +	sub	pc, r0, #31;		/* Jump to the TLS entry.  */	\ +	ldr	lr, [sp], #4;		/* Restore LR.  */		\ +	mov	r2, r0;			/* Save the TLS addr in r2.  */	\ +	ldr	r3, [r2, #PID_OFFSET];	/* Load the saved PID.  */	\ +	rsb	r0, r3, #0;		/* Negate it.  */		\ +	str	r0, [r2, #PID_OFFSET]	/* Store the temporary PID.  */ + +/* Restore the old PID value in the parent.  */ +#define RESTORE_PID \ +	cmp	r0, #0;			/* If we are the parent... */	\ +	strne	r3, [r2, #PID_OFFSET]	/* ... restore the saved PID.  */ + +#INCLUDE <../../../../../../../LIBC/SYSDEPS/LINUX/ARM/VFORK.S> diff --git a/libpthread/nptl/sysdeps/unix/sysv/linux/arm/pthread_once.c b/libpthread/nptl/sysdeps/unix/sysv/linux/arm/pthread_once.c new file mode 100644 index 000000000..c8925810c --- /dev/null +++ b/libpthread/nptl/sysdeps/unix/sysv/linux/arm/pthread_once.c @@ -0,0 +1,99 @@ +/* Copyright (C) 2004, 2005 Free Software Foundation, Inc. +   This file is part of the GNU C Library. + +   The GNU C Library is free software; you can redistribute it and/or +   modify it under the terms of the GNU Lesser General Public +   License as published by the Free Software Foundation; either +   version 2.1 of the License, or (at your option) any later version. + +   The GNU C Library is distributed in the hope that it will be useful, +   but WITHOUT ANY WARRANTY; without even the implied warranty of +   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.	 See the GNU +   Lesser General Public License for more details. + +   You should have received a copy of the GNU Lesser General Public +   License along with the GNU C Library; if not, write to the Free +   Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA +   02111-1307 USA.  */ + +#include "pthreadP.h" +#include <lowlevellock.h> + +unsigned long int __fork_generation attribute_hidden; + +static void +clear_once_control (void *arg) +{ +  pthread_once_t *once_control = (pthread_once_t *) arg; + +  *once_control = 0; +  lll_futex_wake (once_control, INT_MAX); +} + +int +__pthread_once (pthread_once_t *once_control, void (*init_routine) (void)) +{ +  for (;;) +    { +      int oldval; +      int newval; + +      /* Pseudo code: +	 newval = __fork_generation | 1; +	 oldval = *once_control; +	 if ((oldval & 2) == 0) +	   *once_control = newval; +	 Do this atomically. +      */ +      do +	{ +	  newval = __fork_generation | 1; +	  oldval = *once_control; +	  if (oldval & 2) +	    break; +	} while (atomic_compare_and_exchange_val_acq (once_control, newval, oldval) != oldval); + +      /* Check if the initializer has already been done.  */ +      if ((oldval & 2) != 0) +	return 0; + +      /* Check if another thread already runs the initializer.	*/ +      if ((oldval & 1) == 0) +	break; + +      /* Check whether the initializer execution was interrupted by a fork.  */ +      if (oldval != newval) +	break; + +      /* Same generation, some other thread was faster. Wait.  */ +      lll_futex_wait (once_control, oldval); +    } + +  /* This thread is the first here.  Do the initialization. +     Register a cleanup handler so that in case the thread gets +     interrupted the initialization can be restarted.  */ +  pthread_cleanup_push (clear_once_control, once_control); + +  init_routine (); + +  pthread_cleanup_pop (0); + +  /* Say that the initialisation is done.  */ +  *once_control = __fork_generation | 2; + +  /* Wake up all other threads.  */ +  lll_futex_wake (once_control, INT_MAX); + +  return 0; +} +weak_alias (__pthread_once, pthread_once) +strong_alias (__pthread_once, __pthread_once_internal) + +#if defined(__USING_SJLJ_EXCEPTIONS__) && !defined(__PIC__) +/* When statically linked, if pthread_create is used, this file +   will be brought in.  The exception handling code in GCC assumes +   that if pthread_create is available, so are these.  */ +const void *include_pthread_getspecific attribute_hidden = pthread_getspecific; +const void *include_pthread_setspecific attribute_hidden = pthread_setspecific; +const void *include_pthread_key_create attribute_hidden = pthread_key_create; +#endif diff --git a/libpthread/nptl/sysdeps/unix/sysv/linux/arm/sysdep-cancel.h b/libpthread/nptl/sysdeps/unix/sysv/linux/arm/sysdep-cancel.h new file mode 100644 index 000000000..350d9af50 --- /dev/null +++ b/libpthread/nptl/sysdeps/unix/sysv/linux/arm/sysdep-cancel.h @@ -0,0 +1,153 @@ +/* Copyright (C) 2003, 2004, 2005 Free Software Foundation, Inc. +   This file is part of the GNU C Library. + +   The GNU C Library is free software; you can redistribute it and/or +   modify it under the terms of the GNU Lesser General Public +   License as published by the Free Software Foundation; either +   version 2.1 of the License, or (at your option) any later version. + +   The GNU C Library is distributed in the hope that it will be useful, +   but WITHOUT ANY WARRANTY; without even the implied warranty of +   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU +   Lesser General Public License for more details. + +   You should have received a copy of the GNU Lesser General Public +   License along with the GNU C Library; if not, write to the Free +   Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA +   02111-1307 USA.  */ + +#include <sysdep.h> +#include <tls.h> +#ifndef __ASSEMBLER__ +# include <pthreadP.h> +#endif + +#if !defined NOT_IN_libc || defined IS_IN_libpthread || defined IS_IN_librt + +/* NOTE: We do mark syscalls with unwind annotations, for the benefit of +   cancellation; but they're really only accurate at the point of the +   syscall.  The ARM unwind directives are not rich enough without adding +   a custom personality function.  */ + +# undef PSEUDO +# define PSEUDO(name, syscall_name, args)				\ +  .section ".text";							\ +    PSEUDO_PROLOGUE;							\ +  .type __##syscall_name##_nocancel,%function;				\ +  .globl __##syscall_name##_nocancel;					\ +  __##syscall_name##_nocancel:						\ +    DO_CALL (syscall_name, args);					\ +    PSEUDO_RET;								\ +  .size __##syscall_name##_nocancel,.-__##syscall_name##_nocancel;	\ +  ENTRY (name);								\ +    SINGLE_THREAD_P;							\ +    DOARGS_##args;							\ +    bne .Lpseudo_cancel;						\ +    DO_CALL (syscall_name, 0);						\ +    UNDOARGS_##args;							\ +    cmn r0, $4096;							\ +    PSEUDO_RET;								\ +  .Lpseudo_cancel:							\ +    .fnstart;								\ +    DOCARGS_##args;	/* save syscall args etc. around CENABLE.  */	\ +    CENABLE;								\ +    mov ip, r0;		/* put mask in safe place.  */			\ +    UNDOCARGS_##args;	/* restore syscall args.  */			\ +    ldr r7, =SYS_ify (syscall_name);					\ +    swi 0x0;		/* do the call.  */				\ +    .fnend;		/* Past here we can't easily unwind.  */	\ +    mov r7, r0;		/* save syscall return value.  */		\ +    mov r0, ip;		/* get mask back.  */				\ +    CDISABLE;								\ +    mov r0, r7;		/* retrieve return value.  */			\ +    RESTORE_LR_##args;							\ +    UNDOARGS_##args;							\ +    cmn r0, $4096; + +/* DOARGS pushes four bytes on the stack for five arguments, eight bytes for +   six arguments, and nothing for fewer.  In order to preserve doubleword +   alignment, sometimes we must save an extra register.  */ + +# define RESTART_UNWIND .fnend; .fnstart; .save {r7, lr} + +# define DOCARGS_0	stmfd sp!, {r7, lr}; .save {r7, lr} +# define UNDOCARGS_0 +# define RESTORE_LR_0	ldmfd sp!, {r7, lr}; + +# define DOCARGS_1	stmfd sp!, {r0, r1, r7, lr}; .save {r7, lr}; .pad #8 +# define UNDOCARGS_1	ldr r0, [sp], #8; RESTART_UNWIND +# define RESTORE_LR_1	RESTORE_LR_0 + +# define DOCARGS_2	stmfd sp!, {r0, r1, r7, lr}; .save {r7, lr}; .pad #8 +# define UNDOCARGS_2	ldmfd sp!, {r0, r1}; RESTART_UNWIND +# define RESTORE_LR_2	RESTORE_LR_0 + +# define DOCARGS_3	stmfd sp!, {r0, r1, r2, r3, r7, lr}; .save {r7, lr}; .pad #16 +# define UNDOCARGS_3	ldmfd sp!, {r0, r1, r2, r3}; RESTART_UNWIND +# define RESTORE_LR_3	RESTORE_LR_0 + +# define DOCARGS_4	stmfd sp!, {r0, r1, r2, r3, r7, lr}; .save {r7, lr}; .pad #16 +# define UNDOCARGS_4	ldmfd sp!, {r0, r1, r2, r3}; RESTART_UNWIND +# define RESTORE_LR_4	RESTORE_LR_0 + +# define DOCARGS_5	.save {r4}; stmfd sp!, {r0, r1, r2, r3, r4, r7, lr}; .save {r7, lr}; .pad #20 +# define UNDOCARGS_5	ldmfd sp!, {r0, r1, r2, r3}; .fnend; .fnstart; .save {r4}; .save {r7, lr}; .pad #4 +# define RESTORE_LR_5	ldmfd sp!, {r4, r7, lr} + +# define DOCARGS_6	.save {r4, r5}; stmfd sp!, {r0, r1, r2, r3, r7, lr}; .save {r7, lr}; .pad #20 +# define UNDOCARGS_6	ldmfd sp!, {r0, r1, r2, r3}; .fnend; .fnstart; .save {r4, r5}; .save {r7, lr} +# define RESTORE_LR_6	RESTORE_LR_0 + +# ifdef IS_IN_libpthread +#  define CENABLE	bl PLTJMP(__pthread_enable_asynccancel) +#  define CDISABLE	bl PLTJMP(__pthread_disable_asynccancel) +#  define __local_multiple_threads __pthread_multiple_threads +# elif !defined NOT_IN_libc +#  define CENABLE	bl PLTJMP(__libc_enable_asynccancel) +#  define CDISABLE	bl PLTJMP(__libc_disable_asynccancel) +#  define __local_multiple_threads __libc_multiple_threads +# elif defined IS_IN_librt +#  define CENABLE	bl PLTJMP(__librt_enable_asynccancel) +#  define CDISABLE	bl PLTJMP(__librt_disable_asynccancel) +# else +#  error Unsupported library +# endif + +# if defined IS_IN_libpthread || !defined NOT_IN_libc +#  ifndef __ASSEMBLER__ +extern int __local_multiple_threads attribute_hidden; +#   define SINGLE_THREAD_P __builtin_expect (__local_multiple_threads == 0, 1) +#  else +#   define SINGLE_THREAD_P						\ +  ldr ip, 1b;								\ +2:									\ +  ldr ip, [pc, ip];							\ +  teq ip, #0; +#   define PSEUDO_PROLOGUE						\ +  1:  .word __local_multiple_threads - 2f - 8; +#  endif +# else +/*  There is no __local_multiple_threads for librt, so use the TCB.  */ +#  ifndef __ASSEMBLER__ +#   define SINGLE_THREAD_P						\ +  __builtin_expect (THREAD_GETMEM (THREAD_SELF,				\ +				   header.multiple_threads) == 0, 1) +#  else +#   define PSEUDO_PROLOGUE +#   define SINGLE_THREAD_P						\ +  stmfd	sp!, {r0, lr};							\ +  bl	__aeabi_read_tp;						\ +  ldr	ip, [r0, #MULTIPLE_THREADS_OFFSET];				\ +  ldmfd	sp!, {r0, lr};							\ +  teq	ip, #0 +#   define SINGLE_THREAD_P_PIC(x) SINGLE_THREAD_P +#  endif +# endif + +#elif !defined __ASSEMBLER__ + +/* For rtld, et cetera.  */ +# define SINGLE_THREAD_P 1 +# define NO_CANCELLATION 1 + +#endif diff --git a/libpthread/nptl/sysdeps/unix/sysv/linux/arm/sysdep.h b/libpthread/nptl/sysdeps/unix/sysv/linux/arm/sysdep.h new file mode 100644 index 000000000..23c2bfc8c --- /dev/null +++ b/libpthread/nptl/sysdeps/unix/sysv/linux/arm/sysdep.h @@ -0,0 +1,334 @@ +/* Copyright (C) 1992, 93, 1995-2000, 2002, 2003, 2005, 2006 +   Free Software Foundation, Inc. +   This file is part of the GNU C Library. +   Contributed by Ulrich Drepper, <drepper@gnu.ai.mit.edu>, August 1995. +   ARM changes by Philip Blundell, <pjb27@cam.ac.uk>, May 1997. + +   The GNU C Library is free software; you can redistribute it and/or +   modify it under the terms of the GNU Lesser General Public +   License as published by the Free Software Foundation; either +   version 2.1 of the License, or (at your option) any later version. + +   The GNU C Library is distributed in the hope that it will be useful, +   but WITHOUT ANY WARRANTY; without even the implied warranty of +   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU +   Lesser General Public License for more details. + +   You should have received a copy of the GNU Lesser General Public +   License along with the GNU C Library; if not, write to the Free +   Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA +   02111-1307 USA.  */ + +#ifndef _LINUX_ARM_SYSDEP_H +#define _LINUX_ARM_SYSDEP_H 1 + +#include <sys/syscall.h> + +/* There is some commonality.  */ +#include <sysdeps/arm/sysdep.h> + +/* For Linux we can use the system call table in the header file +	/usr/include/asm/unistd.h +   of the kernel.  But these symbols do not follow the SYS_* syntax +   so we have to redefine the `SYS_ify' macro here.  */ +#undef SYS_ify +#define SWI_BASE  (0x900000) +#define SYS_ify(syscall_name)	(__NR_##syscall_name) + + +#ifdef __ASSEMBLER__ + +/* Linux uses a negative return value to indicate syscall errors, +   unlike most Unices, which use the condition codes' carry flag. + +   Since version 2.1 the return value of a system call might be +   negative even if the call succeeded.  E.g., the `lseek' system call +   might return a large offset.  Therefore we must not anymore test +   for < 0, but test for a real error by making sure the value in R0 +   is a real error number.  Linus said he will make sure the no syscall +   returns a value in -1 .. -4095 as a valid result so we can safely +   test with -4095.  */ + +#undef	PSEUDO +#define	PSEUDO(name, syscall_name, args)				      \ +  .text;								      \ +  ENTRY (name);								      \ +    DO_CALL (syscall_name, args);					      \ +    cmn r0, $4096; + +#define PSEUDO_RET							      \ +    RETINSTR(cc, lr);							      \ +    b PLTJMP(SYSCALL_ERROR) +#undef ret +#define ret PSEUDO_RET + +#undef	PSEUDO_END +#define	PSEUDO_END(name)						      \ +  SYSCALL_ERROR_HANDLER							      \ +  END (name) + +#undef	PSEUDO_NOERRNO +#define	PSEUDO_NOERRNO(name, syscall_name, args)			      \ +  .text;								      \ +  ENTRY (name);								      \ +    DO_CALL (syscall_name, args); + +#define PSEUDO_RET_NOERRNO						      \ +    DO_RET (lr); + +#undef ret_NOERRNO +#define ret_NOERRNO PSEUDO_RET_NOERRNO + +#undef	PSEUDO_END_NOERRNO +#define	PSEUDO_END_NOERRNO(name)					      \ +  END (name) + +/* The function has to return the error code.  */ +#undef	PSEUDO_ERRVAL +#define	PSEUDO_ERRVAL(name, syscall_name, args) \ +  .text;								      \ +  ENTRY (name)								      \ +    DO_CALL (syscall_name, args);					      \ +    rsb r0, r0, #0 + +#undef	PSEUDO_END_ERRVAL +#define	PSEUDO_END_ERRVAL(name) \ +  END (name) + +#define ret_ERRVAL PSEUDO_RET_NOERRNO + +#if NOT_IN_libc +# define SYSCALL_ERROR __local_syscall_error +# ifdef RTLD_PRIVATE_ERRNO +#  define SYSCALL_ERROR_HANDLER					\ +__local_syscall_error:						\ +       ldr     r1, 1f;						\ +       rsb     r0, r0, #0;					\ +0:     str     r0, [pc, r1];					\ +       mvn     r0, #0;						\ +       DO_RET(lr);						\ +1:     .word C_SYMBOL_NAME(rtld_errno) - 0b - 8; +# else +#  define SYSCALL_ERROR_HANDLER					\ +__local_syscall_error:						\ +	str	lr, [sp, #-4]!;					\ +	str	r0, [sp, #-4]!;					\ +	bl	PLTJMP(C_SYMBOL_NAME(__errno_location)); 	\ +	ldr	r1, [sp], #4;					\ +	rsb	r1, r1, #0;					\ +	str	r1, [r0];					\ +	mvn	r0, #0;						\ +	ldr	pc, [sp], #4; +# endif +#else +# define SYSCALL_ERROR_HANDLER	/* Nothing here; code in sysdep.S is used.  */ +# define SYSCALL_ERROR __syscall_error +#endif + +/* Linux takes system call args in registers: +	syscall number	in the SWI instruction +	arg 1		r0 +	arg 2		r1 +	arg 3		r2 +	arg 4		r3 +	arg 5		r4	(this is different from the APCS convention) +	arg 6		r5 +	arg 7		r6 + +   The compiler is going to form a call by coming here, through PSEUDO, with +   arguments +	syscall number	in the DO_CALL macro +	arg 1		r0 +	arg 2		r1 +	arg 3		r2 +	arg 4		r3 +	arg 5		[sp] +	arg 6		[sp+4] +	arg 7		[sp+8] + +   We need to shuffle values between R4..R6 and the stack so that the +   caller's v1..v3 and stack frame are not corrupted, and the kernel +   sees the right arguments. + +*/ + +#undef	DO_CALL +#if defined(__ARM_EABI__) +#define DO_CALL(syscall_name, args)		\ +    DOARGS_##args				\ +    mov ip, r7;					\ +    ldr r7, =SYS_ify (syscall_name);		\ +    swi 0x0;					\ +    mov r7, ip;					\ +    UNDOARGS_##args +#else +#define DO_CALL(syscall_name, args)		\ +    DOARGS_##args				\ +    swi SYS_ify (syscall_name); 		\ +    UNDOARGS_##args +#endif + +#define DOARGS_0 /* nothing */ +#define DOARGS_1 /* nothing */ +#define DOARGS_2 /* nothing */ +#define DOARGS_3 /* nothing */ +#define DOARGS_4 /* nothing */ +#define DOARGS_5 str r4, [sp, $-4]!; ldr r4, [sp, $4]; +#define DOARGS_6 mov ip, sp; stmfd sp!, {r4, r5}; ldmia ip, {r4, r5}; +#define DOARGS_7 mov ip, sp; stmfd sp!, {r4, r5, r6}; ldmia ip, {r4, r5, r6}; + +#define UNDOARGS_0 /* nothing */ +#define UNDOARGS_1 /* nothing */ +#define UNDOARGS_2 /* nothing */ +#define UNDOARGS_3 /* nothing */ +#define UNDOARGS_4 /* nothing */ +#define UNDOARGS_5 ldr r4, [sp], $4; +#define UNDOARGS_6 ldmfd sp!, {r4, r5}; +#define UNDOARGS_7 ldmfd sp!, {r4, r5, r6}; + +#else /* not __ASSEMBLER__ */ + +/* Define a macro which expands into the inline wrapper code for a system +   call.  */ +#undef INLINE_SYSCALL +#define INLINE_SYSCALL(name, nr, args...)				\ +  ({ unsigned int _sys_result = INTERNAL_SYSCALL (name, , nr, args);	\ +     if (__builtin_expect (INTERNAL_SYSCALL_ERROR_P (_sys_result, ), 0))	\ +       {								\ +	 __set_errno (INTERNAL_SYSCALL_ERRNO (_sys_result, ));		\ +	 _sys_result = (unsigned int) -1;				\ +       }								\ +     (int) _sys_result; }) + +#undef INTERNAL_SYSCALL_DECL +#define INTERNAL_SYSCALL_DECL(err) do { } while (0) + +#undef INTERNAL_SYSCALL_RAW +#if defined(__thumb__) +/* Hide the use of r7 from the compiler, this would be a lot + * easier but for the fact that the syscalls can exceed 255. + * For the moment the LOAD_ARG_7 is sacrificed. + * We can't use push/pop inside the asm because that breaks + * unwinding (ie. thread cancellation). + */ +#define INTERNAL_SYSCALL_RAW(name, err, nr, args...)		\ +  ({ unsigned int _sys_result;                                  \ +    {                                                           \ +      int _sys_buf[2];						\ +      register int _a1 __asm__ ("a1");                          \ +      register int *_v3 __asm__ ("v3") = _sys_buf;		\ +      LOAD_ARGS_##nr (args)                                     \ +      *_v3 = (int) (name);					\ +      __asm__ __volatile__ ("str	r7, [v3, #4]\n"		\ +                    "\tldr      r7, [v3]\n"                     \ +                    "\tswi      0       @ syscall " #name "\n"  \ +                    "\tldr      r7, [v3, #4]"                   \ +                   : "=r" (_a1)                                 \ +                    : "r" (_v3) ASM_ARGS_##nr                   \ +                    : "memory");                                \ +      _sys_result = _a1;                                        \ +    }                                                           \ +    (int) _sys_result; }) +#elif defined(__ARM_EABI__) +#define INTERNAL_SYSCALL_RAW(name, err, nr, args...)		\ +  ({unsigned int _sys_result;					\ +     {								\ +       register int _a1 __asm__ ("r0"), _nr __asm__ ("r7");	\ +       LOAD_ARGS_##nr (args)					\ +       _nr = name;						\ +       __asm__ __volatile__ ("swi	0x0 @ syscall " #name	\ +		     : "=r" (_a1)				\ +		     : "r" (_nr) ASM_ARGS_##nr			\ +		     : "memory");				\ +       _sys_result = _a1;					\ +     }								\ +     (int) _sys_result; }) +#else /* !defined(__ARM_EABI__) */ +#define INTERNAL_SYSCALL_RAW(name, err, nr, args...)		\ +  ({ unsigned int _sys_result;					\ +     {								\ +       register int _a1 __asm__ ("a1");				\ +       LOAD_ARGS_##nr (args)					\ +       __asm__ __volatile__ ("swi	%1 @ syscall " #name	\ +		     : "=r" (_a1)				\ +		     : "i" (name) ASM_ARGS_##nr			\ +		     : "memory");				\ +       _sys_result = _a1;					\ +     }								\ +     (int) _sys_result; }) +#endif + +#undef INTERNAL_SYSCALL +#define INTERNAL_SYSCALL(name, err, nr, args...)		\ +	INTERNAL_SYSCALL_RAW(SYS_ify(name), err, nr, args) + +#undef INTERNAL_SYSCALL_ARM +#define INTERNAL_SYSCALL_ARM(name, err, nr, args...)		\ +	INTERNAL_SYSCALL_RAW(__ARM_NR_##name, err, nr, args) + +#undef INTERNAL_SYSCALL_ERROR_P +#define INTERNAL_SYSCALL_ERROR_P(val, err) \ +  ((unsigned int) (val) >= 0xfffff001u) + +#undef INTERNAL_SYSCALL_ERRNO +#define INTERNAL_SYSCALL_ERRNO(val, err)	(-(val)) + +#define LOAD_ARGS_0() +#define ASM_ARGS_0 +#define LOAD_ARGS_1(a1)				\ +  _a1 = (int) (a1);				\ +  LOAD_ARGS_0 () +#define ASM_ARGS_1	ASM_ARGS_0, "r" (_a1) +#define LOAD_ARGS_2(a1, a2)			\ +  register int _a2 __asm__ ("a2") = (int) (a2);	\ +  LOAD_ARGS_1 (a1) +#define ASM_ARGS_2	ASM_ARGS_1, "r" (_a2) +#define LOAD_ARGS_3(a1, a2, a3)			\ +  register int _a3 __asm__ ("a3") = (int) (a3);	\ +  LOAD_ARGS_2 (a1, a2) +#define ASM_ARGS_3	ASM_ARGS_2, "r" (_a3) +#define LOAD_ARGS_4(a1, a2, a3, a4)		\ +  register int _a4 __asm__ ("a4") = (int) (a4);	\ +  LOAD_ARGS_3 (a1, a2, a3) +#define ASM_ARGS_4	ASM_ARGS_3, "r" (_a4) +#define LOAD_ARGS_5(a1, a2, a3, a4, a5)		\ +  register int _v1 __asm__ ("v1") = (int) (a5);	\ +  LOAD_ARGS_4 (a1, a2, a3, a4) +#define ASM_ARGS_5	ASM_ARGS_4, "r" (_v1) +#define LOAD_ARGS_6(a1, a2, a3, a4, a5, a6)	\ +  register int _v2 __asm__ ("v2") = (int) (a6);	\ +  LOAD_ARGS_5 (a1, a2, a3, a4, a5) +#define ASM_ARGS_6	ASM_ARGS_5, "r" (_v2) +#define LOAD_ARGS_7(a1, a2, a3, a4, a5, a6, a7)	\ +  register int _v3 __asm__ ("v3") = (int) (a7);	\ +  LOAD_ARGS_6 (a1, a2, a3, a4, a5, a6) +#define ASM_ARGS_7	ASM_ARGS_6, "r" (_v3) + +#if defined(__ARM_EABI__) +#undef INTERNAL_SYSCALL_NCS +#define INTERNAL_SYSCALL_NCS(number, err, nr, args...)		\ +	INTERNAL_SYSCALL_RAW(number, err, nr, args) +#else +/* We can't implement non-constant syscalls directly since the syscall +   number is normally encoded in the instruction.  So use SYS_syscall.  */ +#undef INTERNAL_SYSCALL_NCS +#define INTERNAL_SYSCALL_NCS(number, err, nr, args...)		\ +	INTERNAL_SYSCALL_NCS_##nr (number, err, args) + +#define INTERNAL_SYSCALL_NCS_0(number, err, args...)		\ +	INTERNAL_SYSCALL (syscall, err, 1, number, args) +#define INTERNAL_SYSCALL_NCS_1(number, err, args...)		\ +	INTERNAL_SYSCALL (syscall, err, 2, number, args) +#define INTERNAL_SYSCALL_NCS_2(number, err, args...)		\ +	INTERNAL_SYSCALL (syscall, err, 3, number, args) +#define INTERNAL_SYSCALL_NCS_3(number, err, args...)		\ +	INTERNAL_SYSCALL (syscall, err, 4, number, args) +#define INTERNAL_SYSCALL_NCS_4(number, err, args...)		\ +	INTERNAL_SYSCALL (syscall, err, 5, number, args) +#define INTERNAL_SYSCALL_NCS_5(number, err, args...)		\ +	INTERNAL_SYSCALL (syscall, err, 6, number, args) +#endif + +#endif	/* __ASSEMBLER__ */ + +#endif /* linux/arm/sysdep.h */ diff --git a/libpthread/nptl/sysdeps/unix/sysv/linux/arm/unwind-forcedunwind.c b/libpthread/nptl/sysdeps/unix/sysv/linux/arm/unwind-forcedunwind.c new file mode 100644 index 000000000..206202809 --- /dev/null +++ b/libpthread/nptl/sysdeps/unix/sysv/linux/arm/unwind-forcedunwind.c @@ -0,0 +1,129 @@ +/* Copyright (C) 2003, 2005 Free Software Foundation, Inc. +   This file is part of the GNU C Library. +   Contributed by Jakub Jelinek <jakub@redhat.com>. + +   The GNU C Library is free software; you can redistribute it and/or +   modify it under the terms of the GNU Lesser General Public License as +   published by the Free Software Foundation; either version 2.1 of the +   License, or (at your option) any later version. + +   The GNU C Library is distributed in the hope that it will be useful, +   but WITHOUT ANY WARRANTY; without even the implied warranty of +   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU +   Lesser General Public License for more details. + +   You should have received a copy of the GNU Lesser General Public +   License along with the GNU C Library; see the file COPYING.LIB.  If not, +   write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, +   Boston, MA 02111-1307, USA.  */ + +#include <dlfcn.h> +#include <string.h> +#include <unwind.h> +#include <unistd.h> +#include <pthreadP.h> + +#define __libc_dlopen(x)	dlopen(x, (RTLD_LOCAL | RTLD_LAZY)) +#define __libc_dlsym		dlsym + +static void (*libgcc_s_resume) (struct _Unwind_Exception *exc); +static _Unwind_Reason_Code (*libgcc_s_personality) +  (_Unwind_State, struct _Unwind_Exception *, struct _Unwind_Context *); +static _Unwind_Reason_Code (*libgcc_s_forcedunwind) +  (struct _Unwind_Exception *, _Unwind_Stop_Fn, void *); +static _Unwind_Word (*libgcc_s_getcfa) (struct _Unwind_Context *); + +void +pthread_cancel_init (void) +{ +  void *resume, *personality, *forcedunwind, *getcfa; +  void *handle; + +  if (__builtin_expect (libgcc_s_getcfa != NULL, 1)) +    return; + +  handle = __libc_dlopen ("libgcc_s.so.1"); + +  if (handle == NULL +      || (resume = __libc_dlsym (handle, "_Unwind_Resume")) == NULL +      || (personality = __libc_dlsym (handle, "__gcc_personality_v0")) == NULL +      || (forcedunwind = __libc_dlsym (handle, "_Unwind_ForcedUnwind")) +	 == NULL +      || (getcfa = __libc_dlsym (handle, "_Unwind_GetCFA")) == NULL +#ifdef ARCH_CANCEL_INIT +      || ARCH_CANCEL_INIT (handle) +#endif +      ) +    { +# define STR_N_LEN(str) str, strlen (str) +      INTERNAL_SYSCALL_DECL (err); +      INTERNAL_SYSCALL (write, err, 3, STDERR_FILENO, +			STR_N_LEN ("libgcc_s.so.1 must be installed for pthread_cancel to work\n")); +      abort (); +    } + +  libgcc_s_resume = resume; +  libgcc_s_personality = personality; +  libgcc_s_forcedunwind = forcedunwind; +  libgcc_s_getcfa = getcfa; +} + +/* It's vitally important that _Unwind_Resume not have a stack frame; the +   ARM unwinder relies on register state at entrance.  So we write this in +   assembly.  */ + +asm ( +#ifdef __thumb__ +"	.code 32" +#endif +"	.globl	_Unwind_Resume\n" +"	.type	_Unwind_Resume, %function\n" +"_Unwind_Resume:\n" +"	stmfd	sp!, {r4, r5, r6, lr}\n" +"	ldr	r4, 1f\n" +"	ldr	r5, 2f\n" +"3:	add	r4, pc, r4\n" +"	ldr	r3, [r4, r5]\n" +"	mov	r6, r0\n" +"	cmp	r3, #0\n" +"	beq	4f\n" +"5:	mov	r0, r6\n" +"	ldmfd	sp!, {r4, r5, r6, lr}\n" +"	bx	r3\n" +"4:	bl	pthread_cancel_init\n" +"	ldr	r3, [r4, r5]\n" +"	b	5b\n" +"1:	.word	_GLOBAL_OFFSET_TABLE_ - 3b - 8\n" +"2:	.word	libgcc_s_resume(GOTOFF)\n" +"	.size	_Unwind_Resume, .-_Unwind_Resume\n" +#ifdef __thumb__ +"	.code 16" +#endif +); + +_Unwind_Reason_Code +__gcc_personality_v0 (_Unwind_State state, +		      struct _Unwind_Exception *ue_header, +		      struct _Unwind_Context *context) +{ +  if (__builtin_expect (libgcc_s_personality == NULL, 0)) +    pthread_cancel_init (); +  return libgcc_s_personality (state, ue_header, context); +} + +_Unwind_Reason_Code +_Unwind_ForcedUnwind (struct _Unwind_Exception *exc, _Unwind_Stop_Fn stop, +		      void *stop_argument) +{ +  if (__builtin_expect (libgcc_s_forcedunwind == NULL, 0)) +    pthread_cancel_init (); +  return libgcc_s_forcedunwind (exc, stop, stop_argument); +} + +_Unwind_Word +_Unwind_GetCFA (struct _Unwind_Context *context) +{ +  if (__builtin_expect (libgcc_s_getcfa == NULL, 0)) +    pthread_cancel_init (); +  return libgcc_s_getcfa (context); +} diff --git a/libpthread/nptl/sysdeps/unix/sysv/linux/arm/unwind-resume.c b/libpthread/nptl/sysdeps/unix/sysv/linux/arm/unwind-resume.c new file mode 100644 index 000000000..99b15746a --- /dev/null +++ b/libpthread/nptl/sysdeps/unix/sysv/linux/arm/unwind-resume.c @@ -0,0 +1,95 @@ +/* Copyright (C) 2003, 2005 Free Software Foundation, Inc. +   This file is part of the GNU C Library. +   Contributed by Jakub Jelinek <jakub@redhat.com>. + +   The GNU C Library is free software; you can redistribute it and/or +   modify it under the terms of the GNU Lesser General Public License as +   published by the Free Software Foundation; either version 2.1 of the +   License, or (at your option) any later version. + +   The GNU C Library is distributed in the hope that it will be useful, +   but WITHOUT ANY WARRANTY; without even the implied warranty of +   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU +   Lesser General Public License for more details. + +   You should have received a copy of the GNU Lesser General Public +   License along with the GNU C Library; see the file COPYING.LIB.  If not, +   write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, +   Boston, MA 02111-1307, USA.  */ + +#include <dlfcn.h> +#include <stdio.h> +#include <stdlib.h> +#include <unwind.h> + +#define __libc_dlopen(x)	dlopen(x, (RTLD_LOCAL | RTLD_LAZY)) +#define __libc_dlsym		dlsym + +static void (*libgcc_s_resume) (struct _Unwind_Exception *exc); +static _Unwind_Reason_Code (*libgcc_s_personality) +  (_Unwind_State, struct _Unwind_Exception *, struct _Unwind_Context *); + +static void init (void) __attribute_used__; + +static void +init (void) +{ +  void *resume, *personality; +  void *handle; + +  handle = __libc_dlopen ("libgcc_s.so.1"); + +  if (handle == NULL +      || (resume = __libc_dlsym (handle, "_Unwind_Resume")) == NULL +      || (personality = __libc_dlsym (handle, "__gcc_personality_v0")) == NULL) { +    fprintf(stderr, "libgcc_s.so.1 must be installed for pthread_cancel to work\n"); +    abort (); +  } + +  libgcc_s_resume = resume; +  libgcc_s_personality = personality; +} + +/* It's vitally important that _Unwind_Resume not have a stack frame; the +   ARM unwinder relies on register state at entrance.  So we write this in +   assembly.  */ + +__asm__ ( +#ifdef __thumb__ +"       .code 32\n" +#endif +"	.globl	_Unwind_Resume\n" +"	.type	_Unwind_Resume, %function\n" +"_Unwind_Resume:\n" +"	stmfd	sp!, {r4, r5, r6, lr}\n" +"	ldr	r4, 1f\n" +"	ldr	r5, 2f\n" +"3:	add	r4, pc, r4\n" +"	ldr	r3, [r4, r5]\n" +"	mov	r6, r0\n" +"	cmp	r3, #0\n" +"	beq	4f\n" +"5:	mov	r0, r6\n" +"	ldmfd	sp!, {r4, r5, r6, lr}\n" +"	bx	r3\n" +"4:	bl	init\n" +"	ldr	r3, [r4, r5]\n" +"	b	5b\n" +"1:	.word	_GLOBAL_OFFSET_TABLE_ - 3b - 8\n" +"2:	.word	libgcc_s_resume(GOTOFF)\n" +"	.size	_Unwind_Resume, .-_Unwind_Resume\n" +#ifdef __thumb__ +"       .code 16\n" +#endif + +); + +_Unwind_Reason_Code +__gcc_personality_v0 (_Unwind_State state, +		      struct _Unwind_Exception *ue_header, +		      struct _Unwind_Context *context) +{ +  if (__builtin_expect (libgcc_s_personality == NULL, 0)) +    init (); +  return libgcc_s_personality (state, ue_header, context); +} diff --git a/libpthread/nptl/sysdeps/unix/sysv/linux/arm/unwind.h b/libpthread/nptl/sysdeps/unix/sysv/linux/arm/unwind.h new file mode 100644 index 000000000..d625fb288 --- /dev/null +++ b/libpthread/nptl/sysdeps/unix/sysv/linux/arm/unwind.h @@ -0,0 +1,274 @@ +/* Header file for the ARM EABI unwinder +   Copyright (C) 2003, 2004, 2005  Free Software Foundation, Inc. +   Contributed by Paul Brook + +   This file is free software; you can redistribute it and/or modify it +   under the terms of the GNU General Public License as published by the +   Free Software Foundation; either version 2, or (at your option) any +   later version. + +   In addition to the permissions in the GNU General Public License, the +   Free Software Foundation gives you unlimited permission to link the +   compiled version of this file into combinations with other programs, +   and to distribute those combinations without any restriction coming +   from the use of this file.  (The General Public License restrictions +   do apply in other respects; for example, they cover modification of +   the file, and distribution when not linked into a combine +   executable.) + +   This file is distributed in the hope that it will be useful, but +   WITHOUT ANY WARRANTY; without even the implied warranty of +   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU +   General Public License for more details. + +   You should have received a copy of the GNU General Public License +   along with this program; see the file COPYING.  If not, write to +   the Free Software Foundation, 51 Franklin Street, Fifth Floor, +   Boston, MA 02110-1301, USA.  */ + +/* Language-independent unwinder header public defines.  This contains both +   ABI defined objects, and GNU support routines.  */ + +#ifndef UNWIND_ARM_H +#define UNWIND_ARM_H + +#define __ARM_EABI_UNWINDER__ 1 + +#ifdef __cplusplus +extern "C" { +#endif +  typedef unsigned _Unwind_Word __attribute__((__mode__(__word__))); +  typedef signed _Unwind_Sword __attribute__((__mode__(__word__))); +  typedef unsigned _Unwind_Ptr __attribute__((__mode__(__pointer__))); +  typedef unsigned _Unwind_Internal_Ptr __attribute__((__mode__(__pointer__))); +  typedef _Unwind_Word _uw; +  typedef unsigned _uw64 __attribute__((mode(__DI__))); +  typedef unsigned _uw16 __attribute__((mode(__HI__))); +  typedef unsigned _uw8 __attribute__((mode(__QI__))); + +  typedef enum +    { +      _URC_OK = 0,       /* operation completed successfully */ +      _URC_FOREIGN_EXCEPTION_CAUGHT = 1, +      _URC_END_OF_STACK = 5, +      _URC_HANDLER_FOUND = 6, +      _URC_INSTALL_CONTEXT = 7, +      _URC_CONTINUE_UNWIND = 8, +      _URC_FAILURE = 9   /* unspecified failure of some kind */ +    } +  _Unwind_Reason_Code; + +  typedef enum +    { +      _US_VIRTUAL_UNWIND_FRAME = 0, +      _US_UNWIND_FRAME_STARTING = 1, +      _US_UNWIND_FRAME_RESUME = 2, +      _US_ACTION_MASK = 3, +      _US_FORCE_UNWIND = 8, +      _US_END_OF_STACK = 16 +    } +  _Unwind_State; + +  /* Provided only for for compatibility with existing code.  */ +  typedef int _Unwind_Action; +#define _UA_SEARCH_PHASE	1 +#define _UA_CLEANUP_PHASE	2 +#define _UA_HANDLER_FRAME	4 +#define _UA_FORCE_UNWIND	8 +#define _UA_END_OF_STACK	16 +#define _URC_NO_REASON 	_URC_OK + +  typedef struct _Unwind_Control_Block _Unwind_Control_Block; +  typedef struct _Unwind_Context _Unwind_Context; +  typedef _uw _Unwind_EHT_Header; + + +  /* UCB: */ + +  struct _Unwind_Control_Block +    { +#ifdef _LIBC +      /* For the benefit of code which assumes this is a scalar.  All +	 glibc ever does is clear it.  */ +      _uw64 exception_class; +#else +      char exception_class[8]; +#endif +      void (*exception_cleanup)(_Unwind_Reason_Code, _Unwind_Control_Block *); +      /* Unwinder cache, private fields for the unwinder's use */ +      struct +	{ +	  _uw reserved1;  /* Forced unwind stop fn, 0 if not forced */ +	  _uw reserved2;  /* Personality routine address */ +	  _uw reserved3;  /* Saved callsite address */ +	  _uw reserved4;  /* Forced unwind stop arg */ +	  _uw reserved5; +	} +      unwinder_cache; +      /* Propagation barrier cache (valid after phase 1): */ +      struct +	{ +	  _uw sp; +	  _uw bitpattern[5]; +	} +      barrier_cache; +      /* Cleanup cache (preserved over cleanup): */ +      struct +	{ +	  _uw bitpattern[4]; +	} +      cleanup_cache; +      /* Pr cache (for pr's benefit): */ +      struct +	{ +	  _uw fnstart;			/* function start address */ +	  _Unwind_EHT_Header *ehtp;	/* pointer to EHT entry header word */ +	  _uw additional;		/* additional data */ +	  _uw reserved1; +	} +      pr_cache; +      long long int :0;	/* Force alignment to 8-byte boundary */ +    }; + +  /* Virtual Register Set*/ + +  typedef enum +    { +      _UVRSC_CORE = 0,      /* integer register */ +      _UVRSC_VFP = 1,       /* vfp */ +      _UVRSC_FPA = 2,       /* fpa */ +      _UVRSC_WMMXD = 3,     /* Intel WMMX data register */ +      _UVRSC_WMMXC = 4      /* Intel WMMX control register */ +    } +  _Unwind_VRS_RegClass; + +  typedef enum +    { +      _UVRSD_UINT32 = 0, +      _UVRSD_VFPX = 1, +      _UVRSD_FPAX = 2, +      _UVRSD_UINT64 = 3, +      _UVRSD_FLOAT = 4, +      _UVRSD_DOUBLE = 5 +    } +  _Unwind_VRS_DataRepresentation; + +  typedef enum +    { +      _UVRSR_OK = 0, +      _UVRSR_NOT_IMPLEMENTED = 1, +      _UVRSR_FAILED = 2 +    } +  _Unwind_VRS_Result; + +  /* Frame unwinding state.  */ +  typedef struct +    { +      /* The current word (bytes packed msb first).  */ +      _uw data; +      /* Pointer to the next word of data.  */ +      _uw *next; +      /* The number of bytes left in this word.  */ +      _uw8 bytes_left; +      /* The number of words pointed to by ptr.  */ +      _uw8 words_left; +    } +  __gnu_unwind_state; + +  typedef _Unwind_Reason_Code (*personality_routine) (_Unwind_State, +      _Unwind_Control_Block *, _Unwind_Context *); + +  _Unwind_VRS_Result _Unwind_VRS_Set(_Unwind_Context *, _Unwind_VRS_RegClass, +                                     _uw, _Unwind_VRS_DataRepresentation, +                                     void *); + +  _Unwind_VRS_Result _Unwind_VRS_Get(_Unwind_Context *, _Unwind_VRS_RegClass, +                                     _uw, _Unwind_VRS_DataRepresentation, +                                     void *); + +  _Unwind_VRS_Result _Unwind_VRS_Pop(_Unwind_Context *, _Unwind_VRS_RegClass, +                                     _uw, _Unwind_VRS_DataRepresentation); + + +  /* Support functions for the PR.  */ +#define _Unwind_Exception _Unwind_Control_Block +  typedef char _Unwind_Exception_Class[8]; + +  void * _Unwind_GetLanguageSpecificData (_Unwind_Context *); +  _Unwind_Ptr _Unwind_GetRegionStart (_Unwind_Context *); + +  /* These two should never be used.  */ +  _Unwind_Ptr _Unwind_GetDataRelBase (_Unwind_Context *); +  _Unwind_Ptr _Unwind_GetTextRelBase (_Unwind_Context *); + +  /* Interface functions: */ +  _Unwind_Reason_Code _Unwind_RaiseException(_Unwind_Control_Block *ucbp); +  void __attribute__((noreturn)) _Unwind_Resume(_Unwind_Control_Block *ucbp); +  _Unwind_Reason_Code _Unwind_Resume_or_Rethrow (_Unwind_Control_Block *ucbp); + +  typedef _Unwind_Reason_Code (*_Unwind_Stop_Fn) +       (int, _Unwind_Action, _Unwind_Exception_Class, +	_Unwind_Control_Block *, struct _Unwind_Context *, void *); +  _Unwind_Reason_Code _Unwind_ForcedUnwind (_Unwind_Control_Block *, +					    _Unwind_Stop_Fn, void *); +  _Unwind_Word _Unwind_GetCFA (struct _Unwind_Context *); +  void _Unwind_Complete(_Unwind_Control_Block *ucbp); +  void _Unwind_DeleteException (_Unwind_Exception *); + +  _Unwind_Reason_Code __gnu_unwind_frame (_Unwind_Control_Block *, +					  _Unwind_Context *); +  _Unwind_Reason_Code __gnu_unwind_execute (_Unwind_Context *, +					    __gnu_unwind_state *); + +  /* Decode an R_ARM_TARGET2 relocation.  */ +  static inline _Unwind_Word +  _Unwind_decode_target2 (_Unwind_Word ptr) +    { +      _Unwind_Word tmp; + +      tmp = *(_Unwind_Word *) ptr; +      /* Zero values are always NULL.  */ +      if (!tmp) +	return 0; + +#if defined(linux) || defined(__NetBSD__) +      /* Pc-relative indirect.  */ +      tmp += ptr; +      tmp = *(_Unwind_Word *) tmp; +#elif defined(__symbian__) +      /* Absolute pointer.  Nothing more to do.  */ +#else +      /* Pc-relative pointer.  */ +      tmp += ptr; +#endif +      return tmp; +    } + +  static inline _Unwind_Word +  _Unwind_GetGR (_Unwind_Context *context, int regno) +    { +      _uw val; +      _Unwind_VRS_Get (context, _UVRSC_CORE, regno, _UVRSD_UINT32, &val); +      return val; +    } + +  /* Return the address of the instruction, not the actual IP value.  */ +#define _Unwind_GetIP(context) \ +  (_Unwind_GetGR (context, 15) & ~(_Unwind_Word)1) + +  static inline void +  _Unwind_SetGR (_Unwind_Context *context, int regno, _Unwind_Word val) +    { +      _Unwind_VRS_Set (context, _UVRSC_CORE, regno, _UVRSD_UINT32, &val); +    } + +  /* The dwarf unwinder doesn't understand arm/thumb state.  We assume the +     landing pad uses the same instruction set as the call site.  */ +#define _Unwind_SetIP(context, val) \ +  _Unwind_SetGR (context, 15, val | (_Unwind_GetGR (context, 15) & 1)) + +#ifdef __cplusplus +}   /* extern "C" */ +#endif + +#endif /* defined UNWIND_ARM_H */ diff --git a/libpthread/nptl/sysdeps/unix/sysv/linux/arm/vfork.S b/libpthread/nptl/sysdeps/unix/sysv/linux/arm/vfork.S new file mode 100644 index 000000000..935a4e904 --- /dev/null +++ b/libpthread/nptl/sysdeps/unix/sysv/linux/arm/vfork.S @@ -0,0 +1,39 @@ +/* Copyright (C) 2005 Free Software Foundation, Inc. +   This file is part of the GNU C Library. + +   The GNU C Library is free software; you can redistribute it and/or +   modify it under the terms of the GNU Lesser General Public +   License as published by the Free Software Foundation; either +   version 2.1 of the License, or (at your option) any later version. + +   The GNU C Library is distributed in the hope that it will be useful, +   but WITHOUT ANY WARRANTY; without even the implied warranty of +   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU +   Lesser General Public License for more details. + +   You should have received a copy of the GNU Lesser General Public +   License along with the GNU C Library; if not, write to the Free +   Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA +   02111-1307 USA.  */ + +#include <tcb-offsets.h> + +/* Save the PID value.  */ +#define SAVE_PID \ +	str	lr, [sp, #-4]!;		/* Save LR.  */			\ +	mov	r0, #0xffff0fff;	/* Point to the high page.  */	\ +	mov	lr, pc;			/* Save our return address.  */	\ +	sub	pc, r0, #31;		/* Jump to the TLS entry.  */	\ +	ldr	lr, [sp], #4;		/* Restore LR.  */		\ +	mov	r2, r0;			/* Save the TLS addr in r2.  */	\ +	ldr	r3, [r2, #PID_OFFSET];	/* Load the saved PID.  */	\ +	rsbs	r0, r3, #0;		/* Negate it.  */		\ +	moveq	r0, #0x80000000;	/* Use 0x80000000 if it was 0.  */ \ +	str	r0, [r2, #PID_OFFSET]	/* Store the temporary PID.  */ + +/* Restore the old PID value in the parent.  */ +#define RESTORE_PID \ +	cmp	r0, #0;			/* If we are the parent... */	\ +	strne	r3, [r2, #PID_OFFSET]	/* ... restore the saved PID.  */ + +#include "../../../../../../../libc/sysdeps/linux/arm/vfork.S" | 
