about summary refs log tree commit diff
path: root/nptl
diff options
context:
space:
mode:
Diffstat (limited to 'nptl')
-rw-r--r--nptl/ChangeLog42
-rw-r--r--nptl/Makefile19
-rw-r--r--nptl/Versions5
-rw-r--r--nptl/sysdeps/pthread/bits/libc-lock.h13
-rwxr-xr-xnptl/sysdeps/pthread/configure55
-rw-r--r--nptl/sysdeps/pthread/configure.in13
-rw-r--r--nptl/sysdeps/pthread/pthread-functions.h2
-rw-r--r--nptl/sysdeps/pthread/pthread.h103
-rw-r--r--nptl/sysdeps/unix/sysv/linux/i386/bits/pthreadtypes.h3
-rw-r--r--nptl/sysdeps/unix/sysv/linux/i386/sysdep-cancel.h263
-rw-r--r--nptl/unwind.c112
-rw-r--r--nptl/version.c6
12 files changed, 596 insertions, 40 deletions
diff --git a/nptl/ChangeLog b/nptl/ChangeLog
index 6ffe62314e..bbda8c591e 100644
--- a/nptl/ChangeLog
+++ b/nptl/ChangeLog
@@ -1,3 +1,45 @@
+2003-04-11  Ulrich Drepper  <drepper@redhat.com>
+
+	* pthread.h: Define new data structure for cleanup buffer.  Declare
+	new cleanup handler interfaces.
+	* descr.h: Include <unwind.h> if necessary.  Define pthread_unwind_buf.
+	(struct pthread): Add cleanup_jmp_buf pointer.  Define
+	HAVE_CLEANUP_JMP_BUF and not HAVE_CANCELBUF.
+	* pthreadP.h: Declare __pthread_unwind.  Define __do_cancel to use
+	it.  Declare old cleanup handler installation functions.
+	* cleanup.c: Rewrite.  Install handler for unwind-based cleanup
+	handling.
+	* cleanup_defer.c: Likewise.
+	* cleanup_compat.c: New file.  Old cleanup code.
+	* cleanup_def_compat.c: New file.  Old cleanup code.
+	* pthread_create.c (start_thread): Initialize cleanup_jmp_buf element
+	if own thread descriptor.
+	* unwind.c: New file.
+	* forward.c: Add __pthread_unwind.
+	* init.c (pthread_functions): Add __pthread_unwind.
+	* sysdeps/pthread/pthread-functions.s (struct pthread_functions):
+	Add ptr___pthread_unwind.
+	* Versions [GLIBC_2.3.3] (libpthread): Export new cleanup handling
+	and unwind function.
+	* Makefile (libpthread-routines): Add cleanup_compat,
+	cleanup_def_compat, and unwind.  Define CFLAGS to enable unwind
+	table generation if necessary.
+	* version.c: Record whether unwind support is compiled in.
+	* sysdeps/pthread/configure.in: Add checks for unwind unterfaces.
+	* sysdeps/pthread/bits/libc-lock.h: Add prototypes of the old cleanup
+	handler interfaces.
+	* sysdeps/unix/sysv/linux/i386/sysdep-cancel.h: Add quite a bit of
+	complication to generate unwind information for syscall wrappers.
+	* sysdeps/unix/sysv/linux/i386/bits/pthreadtypes.h: Define
+	__cleanup_fct_attribute.
+
+	* Makefile: Add rules to build and run tst-cleanup0.
+	* tst-cleanup0.c: New file.
+	* tst-cleanup0.expect: New file.
+
+	* pthread_create.c (deallocate_tsd): Don't take parameter.  Adjust
+	caller.  Optimize to avoid often unecessary local variable.
+
 2003-04-11  Roland McGrath  <roland@redhat.com>
 
 	* Makefile ($(objpfx)multidir.mk): New target, generated makefile that
diff --git a/nptl/Makefile b/nptl/Makefile
index cd8ad98c38..79d28c9a30 100644
--- a/nptl/Makefile
+++ b/nptl/Makefile
@@ -98,7 +98,8 @@ libpthread-routines = init events version \
 		      sem_open sem_close sem_unlink \
 		      sem_getvalue \
 		      sem_wait sem_trywait sem_timedwait sem_post \
-		      cleanup cleanup_defer \
+		      cleanup cleanup_defer cleanup_compat \
+		      cleanup_defer_compat unwind \
 		      pt-longjmp \
 		      cancellation \
 		      lowlevellock lowlevelmutex \
@@ -122,6 +123,10 @@ libpthread-static-only-routines = pthread_atfork
 libpthread-nonshared = pthread_atfork
 
 CFLAGS-pthread_atfork.c = -DNOT_IN_libc
+CFLAGS-init.c = -fexceptions -fasynchronous-unwind-tables
+CFLAGS-unwind.c = -fexceptions
+CFLAGS-cancellation.c = -fasynchronous-unwind-tables
+CFLAGS-libc-cancellation.c = -fasynchronous-unwind-tables
 
 # Don't generate deps for calls with no sources.  See sysdeps/unix/Makefile.
 omit-deps = $(unix-syscalls:%=ptw-%)
@@ -153,7 +158,7 @@ tests = tst-attr1 tst-attr2 \
 	tst-cancel1 tst-cancel2 tst-cancel3 tst-cancel4 tst-cancel5 \
 	tst-cancel6 tst-cancel7 tst-cancel8 tst-cancel9 tst-cancel10 \
 	tst-cancel11 \
-	tst-cleanup1 tst-cleanup2 tst-cleanup3 \
+	tst-cleanup0 tst-cleanup1 tst-cleanup2 tst-cleanup3 \
 	tst-flock1 tst-flock2 \
 	tst-signal1 tst-signal2 tst-signal3 tst-signal4 tst-signal5 \
 	tst-exec1 tst-exec2 tst-exec3 \
@@ -208,6 +213,9 @@ CFLAGS-flockfile.c = -D_IO_MTSAFE_IO
 CFLAGS-ftrylockfile.c = -D_IO_MTSAFE_IO
 CFLAGS-funlockfile.c = -D_IO_MTSAFE_IO
 
+# Ugly, ugly.  We have to link with libgcc_eh but how?
+link-libc-static := $(common-objpfx)libc.a $(gnulib) -lgcc_eh $(common-objpfx)libc.a
+
 ifeq ($(build-static),yes)
 tests-static += tst-locale1 tst-locale2
 endif
@@ -220,7 +228,6 @@ ifeq (yes,$(build-shared))
 # Make sure these things are built in the `make lib' pass so they can be used
 # to run programs during the `make others' pass.
 lib-noranlib: $(addprefix $(objpfx),$(extra-objs))
-endif
 
 # What we install as libpthread.so for programs to link against is in fact a
 # link script.  It contains references for the various libraries we need.
@@ -229,6 +236,7 @@ endif
 # We need to use absolute paths since otherwise local copies (if they exist)
 # of the files are taken by the linker.
 install: $(inst_libdir)/libpthread.so
+
 $(inst_libdir)/libpthread.so: $(common-objpfx)format.lds \
 			      $(objpfx)libpthread.so$(libpthread.so-version) \
 			      $(inst_libdir)/$(patsubst %,$(libtype.oS),\
@@ -245,6 +253,8 @@ $(inst_libdir)/libpthread.so: $(common-objpfx)format.lds \
 	mv -f $@.new $@
 $(inst_libdir)/libpthread_nonshared.a: $(objpfx)libpthread_nonshared.a
 	$(do-install)
+endif
+
 
 # 'pthread_self' is a simple memory or register load.  Setting up the
 # stack frame is more work than the actual operation.  Disable the
@@ -313,6 +323,9 @@ $(objpfx)pt-initfini.s: pt-initfini.c
 	$(compile.c) -S $(CFLAGS-pt-initfini.s) -finhibit-size-directive \
 		$(patsubst -f%,-fno-%,$(exceptions)) -o $@
 
+$(objpfx)tst-cleanup0.out: /dev/null $(objpfx)tst-cleanup0
+	$(make-test-out) 2>&1 | cmp - tst-cleanup0.expect >& $@
+
 # We only have one kind of startup code files.  Static binaries and
 # shared libraries are build using the PIC version.
 $(objpfx)crti.S: $(objpfx)pt-initfini.s
diff --git a/nptl/Versions b/nptl/Versions
index 5824108426..4f52eb66f3 100644
--- a/nptl/Versions
+++ b/nptl/Versions
@@ -211,6 +211,11 @@ libpthread {
 
     # Proposed API extensions.
     pthread_tryjoin_np; pthread_timedjoin_np;
+
+    # New cancellation cleanup handling.
+    __pthread_register_cancel; __pthread_unregister_cancel;
+    __pthread_register_cancel_defer; __pthread_unregister_cancel_restore;
+    __pthread_unwind_next;
   }
 
   GLIBC_PRIVATE {
diff --git a/nptl/sysdeps/pthread/bits/libc-lock.h b/nptl/sysdeps/pthread/bits/libc-lock.h
index 3a3d3cc6d3..945a81cb82 100644
--- a/nptl/sysdeps/pthread/bits/libc-lock.h
+++ b/nptl/sysdeps/pthread/bits/libc-lock.h
@@ -345,6 +345,19 @@ typedef pthread_key_t __libc_key_t;
   } while (0)
 
 
+/* Note that for I/O cleanup handling we are using the old-style
+   cancel handling.  It does not have to be integrated with C++ snce
+   no C++ code is called in the middle.  The old-style handling is
+   faster and the support is not going away.  */
+extern void _pthread_cleanup_push (struct _pthread_cleanup_buffer *buffer,
+                                   void (*routine) (void *), void *arg);
+extern void _pthread_cleanup_pop (struct _pthread_cleanup_buffer *buffer,
+                                  int execute);
+extern void _pthread_cleanup_push_defer (struct _pthread_cleanup_buffer *buffer,
+                                         void (*routine) (void *), void *arg);
+extern void _pthread_cleanup_pop_restore (struct _pthread_cleanup_buffer *buffer,
+                                          int execute);
+
 /* Start critical region with cleanup.  */
 #define __libc_cleanup_region_start(DOIT, FCT, ARG) \
   { struct _pthread_cleanup_buffer _buffer;				      \
diff --git a/nptl/sysdeps/pthread/configure b/nptl/sysdeps/pthread/configure
index 8999d37e5a..50293a4f1c 100755
--- a/nptl/sysdeps/pthread/configure
+++ b/nptl/sysdeps/pthread/configure
@@ -5,3 +5,58 @@ if test "x$libc_cv_gcc___thread" != xyes; then
 echo "$as_me: error: compiler support for __thread is required" >&2;}
    { (exit 1); exit 1; }; }
 fi
+
+
+echo "$as_me:$LINENO: checking for forced unwind support" >&5
+echo $ECHO_N "checking for forced unwind support... $ECHO_C" >&6
+if test "${libc_cv_forced_unwind+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  cat >conftest.$ac_ext <<_ACEOF
+#line $LINENO "configure"
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+#include <unwind.h>
+int
+main ()
+{
+
+struct _Unwind_Exception exc;
+struct _Unwind_Context *context;
+_Unwind_GetCFA (context)
+  ;
+  return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5
+  (eval $ac_link) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } &&
+         { ac_try='test -s conftest$ac_exeext'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; }; then
+  libc_cv_forced_unwind=yes
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+libc_cv_forced_unwind=no
+fi
+rm -f conftest.$ac_objext conftest$ac_exeext conftest.$ac_ext
+fi
+echo "$as_me:$LINENO: result: $libc_cv_forced_unwind" >&5
+echo "${ECHO_T}$libc_cv_forced_unwind" >&6
+if test $libc_cv_forced_unwind = yes; then
+  cat >>confdefs.h <<\_ACEOF
+#define HAVE_FORCED_UNWIND 1
+_ACEOF
+
+fi
diff --git a/nptl/sysdeps/pthread/configure.in b/nptl/sysdeps/pthread/configure.in
index 8350f86dc0..e4ea6830d6 100644
--- a/nptl/sysdeps/pthread/configure.in
+++ b/nptl/sysdeps/pthread/configure.in
@@ -4,3 +4,16 @@ GLIBC_PROVIDES dnl See aclocal.m4 in the top level source directory.
 if test "x$libc_cv_gcc___thread" != xyes; then
   AC_MSG_ERROR(compiler support for __thread is required)
 fi
+
+dnl Iff <unwind.h> is available, make sure it is the right one and it
+dnl contains struct _Unwind_Exception.
+AC_CACHE_CHECK(dnl
+for forced unwind support, libc_cv_forced_unwind, [dnl
+AC_TRY_LINK([#include <unwind.h>], [
+struct _Unwind_Exception exc;
+struct _Unwind_Context *context;
+_Unwind_GetCFA (context)],
+libc_cv_forced_unwind=yes, libc_cv_forced_unwind=no)])
+if test $libc_cv_forced_unwind = yes; then
+  AC_DEFINE(HAVE_FORCED_UNWIND)
+fi
diff --git a/nptl/sysdeps/pthread/pthread-functions.h b/nptl/sysdeps/pthread/pthread-functions.h
index 9f38e34053..93ba089982 100644
--- a/nptl/sysdeps/pthread/pthread-functions.h
+++ b/nptl/sysdeps/pthread/pthread-functions.h
@@ -85,6 +85,8 @@ struct pthread_functions
 					    int);
 #define HAVE_PTR_NTHREADS
   int *ptr_nthreads;
+  void (*ptr___pthread_unwind) (__pthread_unwind_buf_t *)
+       __attribute ((noreturn)) __cleanup_fct_attribute;
 };
 
 /* Variable in libc.so.  */
diff --git a/nptl/sysdeps/pthread/pthread.h b/nptl/sysdeps/pthread/pthread.h
index 72673d11a6..5373b80dcf 100644
--- a/nptl/sysdeps/pthread/pthread.h
+++ b/nptl/sysdeps/pthread/pthread.h
@@ -26,6 +26,7 @@
 #define __need_sigset_t
 #include <signal.h>
 #include <bits/pthreadtypes.h>
+#include <bits/setjmp.h>
 
 
 /* Detach state.  */
@@ -380,6 +381,24 @@ extern int pthread_cancel (pthread_t __th) __THROW;
 extern void pthread_testcancel (void) __THROW;
 
 
+/* Cancellation handling with integration into exception handling.  */
+
+typedef struct
+{
+  void *__pad[16];
+  struct
+  {
+    __jmp_buf __cancel_jmp_buf;
+    int __mask_was_saved;
+  } __cancel_jmp_buf[1];
+} __pthread_unwind_buf_t __attribute__ ((__aligned__));
+
+/* No special attributes by default.  */
+#ifndef __cleanup_fct_attribute
+# define __cleanup_fct_attribute
+#endif
+
+
 /* Install a cleanup handler: ROUTINE will be called with arguments ARG
    when the thread is cancelled or calls pthread_exit.  ROUTINE will also
    be called with arguments ARG when the matching pthread_cleanup_pop
@@ -387,43 +406,83 @@ extern void pthread_testcancel (void) __THROW;
 
    pthread_cleanup_push and pthread_cleanup_pop are macros and must always
    be used in matching pairs at the same nesting level of braces.  */
-#define pthread_cleanup_push(routine,arg) \
-  { struct _pthread_cleanup_buffer _buffer;				      \
-    _pthread_cleanup_push (&_buffer, (routine), (arg));
-
-extern void _pthread_cleanup_push (struct _pthread_cleanup_buffer *__buffer,
-				   void (*__routine) (void *), void *__arg)
-     __THROW;
+#define pthread_cleanup_push(routine, arg) \
+  do {									      \
+    __pthread_unwind_buf_t __cancel_buf;				      \
+    void (*__cancel_routine) (void *) = (routine);			      \
+    void *__cancel_arg = (arg);						      \
+    int not_first_call = __sigsetjmp ((struct __jmp_buf_tag *)		      \
+				      __cancel_buf.__cancel_jmp_buf, 0);      \
+    if (__builtin_expect (not_first_call, 0))				      \
+      {									      \
+	__cancel_routine (__cancel_arg);				      \
+	__pthread_unwind_next (&__cancel_buf);				      \
+	/* NOTREACHED */						      \
+      }									      \
+									      \
+    __pthread_register_cancel (&__cancel_buf);				      \
+    do {
+extern void __pthread_register_cancel (__pthread_unwind_buf_t *__buf)
+     __cleanup_fct_attribute;
 
 /* Remove a cleanup handler installed by the matching pthread_cleanup_push.
    If EXECUTE is non-zero, the handler function is called. */
 #define pthread_cleanup_pop(execute) \
-    _pthread_cleanup_pop (&_buffer, (execute)); }
-
-extern void _pthread_cleanup_pop (struct _pthread_cleanup_buffer *__buffer,
-				  int __execute) __THROW;
+    } while (0);							      \
+    __pthread_unregister_cancel (&__cancel_buf);			      \
+    if (execute)							      \
+      __cancel_routine (__cancel_arg);					      \
+  } while (0)
+extern void __pthread_unregister_cancel (__pthread_unwind_buf_t *__buf)
+  __cleanup_fct_attribute;
 
 #ifdef __USE_GNU
 /* Install a cleanup handler as pthread_cleanup_push does, but also
    saves the current cancellation type and sets it to deferred
    cancellation.  */
-# define pthread_cleanup_push_defer_np(routine,arg) \
-  { struct _pthread_cleanup_buffer _buffer;				      \
-    _pthread_cleanup_push_defer (&_buffer, (routine), (arg));
-
-extern void _pthread_cleanup_push_defer (struct _pthread_cleanup_buffer *__buffer,
-					 void (*__routine) (void *),
-					 void *__arg) __THROW;
+# define pthread_cleanup_push_defer(routine, arg) \
+  do {									      \
+    __pthread_unwind_buf_t __cancel_buf;				      \
+    void (*__cancel_routine) (void *) = (routine);			      \
+    void *__cancel_arg = (arg);						      \
+    int not_first_call = __sigsetjmp ((struct __jmp_buf_tag *)		      \
+				      __cancel_buf.__cancel_jmp_buf, 0);      \
+    if (__builtin_expect (not_first_call, 0))				      \
+      {									      \
+	__cancel_routine (__cancel_arg);				      \
+	__pthread_unwind_next (&__cancel_buf);				      \
+	/* NOTREACHED */						      \
+      }									      \
+									      \
+    __pthread_register_cancel_defer (&__cancel_buf);			      \
+    do {
+extern void __pthread_register_cancel_defer (__pthread_unwind_buf_t *__buf)
+     __cleanup_fct_attribute;
 
 /* Remove a cleanup handler as pthread_cleanup_pop does, but also
    restores the cancellation type that was in effect when the matching
    pthread_cleanup_push_defer was called.  */
-# define pthread_cleanup_pop_restore_np(execute) \
-  _pthread_cleanup_pop_restore (&_buffer, (execute)); }
+# define pthread_cleanup_pop_cleanup(execute) \
+    } while (0);							      \
+    __pthread_unregister_cancel_restore (&__cancel_buf);		      \
+    if (execute)							      \
+      __cancel_routine (__cancel_arg);					      \
+  } while (0)
+extern void __pthread_unregister_cancel_restore (__pthread_unwind_buf_t *__buf)
+  __cleanup_fct_attribute;
+#endif
 
-extern void _pthread_cleanup_pop_restore (struct _pthread_cleanup_buffer *__buffer,
-					  int __execute) __THROW;
+/* Internal interface to initiate cleanup.  */
+extern void __pthread_unwind_next (__pthread_unwind_buf_t *__buf)
+     __cleanup_fct_attribute __attribute ((__noreturn__))
+#ifndef SHARED
+     __attribute ((__weak__))
 #endif
+     ;
+
+/* Function used in the macros.  */
+struct __jmp_buf_tag;
+extern int __sigsetjmp (struct __jmp_buf_tag __env[1], int __savemask) __THROW;
 
 
 /* Mutex handling.  */
diff --git a/nptl/sysdeps/unix/sysv/linux/i386/bits/pthreadtypes.h b/nptl/sysdeps/unix/sysv/linux/i386/bits/pthreadtypes.h
index c14f1b1ef6..0834894c25 100644
--- a/nptl/sysdeps/unix/sysv/linux/i386/bits/pthreadtypes.h
+++ b/nptl/sysdeps/unix/sysv/linux/i386/bits/pthreadtypes.h
@@ -149,4 +149,7 @@ typedef union
 #endif
 
 
+/* Extra attributes for the cleanup functions.  */
+#define __cleanup_fct_attribute __attribute ((regparm (1)))
+
 #endif	/* bits/pthreadtypes.h */
diff --git a/nptl/sysdeps/unix/sysv/linux/i386/sysdep-cancel.h b/nptl/sysdeps/unix/sysv/linux/i386/sysdep-cancel.h
index 35f05e1cc2..b7009d753f 100644
--- a/nptl/sysdeps/unix/sysv/linux/i386/sysdep-cancel.h
+++ b/nptl/sysdeps/unix/sysv/linux/i386/sysdep-cancel.h
@@ -29,6 +29,7 @@
 # define PSEUDO(name, syscall_name, args)				      \
   .text;								      \
   ENTRY (name)								      \
+  L(name##START):							      \
     cmpl $0, %gs:MULTIPLE_THREADS_OFFSET;				      \
     jne L(pseudo_cancel);						      \
     DO_CALL (syscall_name, args);					      \
@@ -38,29 +39,262 @@
   L(pseudo_cancel):							      \
     CENABLE								      \
     SAVE_OLDTYPE_##args							      \
-    PUSHARGS_##args							      \
+    PUSHCARGS_##args							      \
     DOCARGS_##args							      \
     movl $SYS_ify (syscall_name), %eax;					      \
-    ENTER_KERNEL							      \
-    POPARGS_##args;							      \
-    POPCARGS_##args							      \
+    /* Until we can handle unwinding from the sysenter page the kernel	      \
+       provides we cannot use ENTER_KERNEL here.  */			      \
+    int $0x80;								      \
+    POPCARGS_##args;							      \
+    POPSTATE_##args							      \
     cmpl $-4095, %eax;							      \
     jae SYSCALL_ERROR_LABEL;						      \
-  L(pseudo_end):
+  L(pseudo_end):							      \
+									      \
+  /* Create unwinding information for the syscall wrapper.  */		      \
+  .section .eh_frame,"a",@progbits;					      \
+  L(STARTFRAME):							      \
+    /* Length of the CIE.  */						      \
+    .long L(ENDCIE)-L(STARTCIE);					      \
+  L(STARTCIE):								      \
+    /* CIE ID.  */							      \
+    .long 0;								      \
+    /* Version number.  */						      \
+    .byte 1;								      \
+    /* NUL-terminated augmentation string.  Note "z" means there is an	      \
+       augmentation value later on.  */					      \
+    .string "zR";							      \
+    /* Code alignment factor.  */					      \
+    .uleb128 1;								      \
+    /* Data alignment factor.  */					      \
+    .sleb128 -4;							      \
+    /* Return address register column.  */				      \
+    .byte 8;								      \
+    /* Augmentation value length.  */					      \
+    .uleb128 1;								      \
+    /* Encoding: DW_EH_PE_pcrel + DW_EH_PE_sdata4.  */			      \
+    .byte 0x1b;								      \
+    /* Start of the table initialization.  */				      \
+    .byte 0xc;								      \
+    .uleb128 4;								      \
+    .uleb128 4;								      \
+    .byte 0x88;								      \
+    .uleb128 1;								      \
+    .align 4;								      \
+  L(ENDCIE):								      \
+    /* Length of the FDE.  */						      \
+    .long L(ENDFDE)-L(STARTFDE);					      \
+  L(STARTFDE):								      \
+    /* CIE pointer.  */							      \
+    .long L(STARTFDE)-L(STARTFRAME);					      \
+    /* PC-relative start address of the code.  */			      \
+    .long L(name##START)-.;						      \
+    /* Length of the code.  */						      \
+    .long L(name##END)-L(name##START);					      \
+    /* No augmentation data.  */					      \
+    .uleb128 0;								      \
+    /* The rest of the code depends on the number of parameters the syscall   \
+       takes.  */							      \
+    EH_FRAME_##args(name);						      \
+    .align 4;								      \
+  L(ENDFDE):								      \
+  .previous
+
+/* Callframe description for syscalls without parameters.  This is very
+   simple.  The only place the stack pointer is changed is when the old
+   cancellation state value is saved.  */
+# define EH_FRAME_0(name) \
+    .byte 4;								      \
+    .long L(PUSHSTATE)-name;						      \
+    .byte 14;								      \
+    .uleb128 8;								      \
+    .byte 4;								      \
+    .long L(POPSTATE)-L(PUSHSTATE);					      \
+    .byte 14;								      \
+    .uleb128 4
+
+/* For syscalls with one and two parameters the code is the same as for
+   those which take no parameter.  */
+# define EH_FRAME_1(name) EH_FRAME_0 (name)
+# define EH_FRAME_2(name) EH_FRAME_1 (name)
+
+/* For syscalls with three parameters the stack pointer is changed
+   also to save the content of the %ebx register.  */
+# define EH_FRAME_3(name) \
+    .byte 4;								      \
+    .long L(PUSHBX1)-name;						      \
+    .byte 14;								      \
+    .uleb128 8;								      \
+    .byte 4;								      \
+    .long L(POPBX1)-L(PUSHBX1);						      \
+    .byte 14;								      \
+    .uleb128 4;								      \
+    .byte 4;								      \
+    .long L(PUSHSTATE)-L(POPBX1);					      \
+    .byte 14;								      \
+    .uleb128 8;								      \
+    .byte 4;								      \
+    .long L(PUSHBX2)-L(PUSHSTATE);					      \
+    .byte 14;								      \
+    .uleb128 12;							      \
+    .byte 4;								      \
+    .long L(POPBX2)-L(PUSHBX2);						      \
+    .byte 14;								      \
+    .uleb128 8;								      \
+    .byte 4;								      \
+    .long L(POPSTATE)-L(POPBX2);					      \
+    .byte 14;								      \
+    .uleb128 4
+
+/* With four parameters the syscall wrappers have to save %ebx and %esi.  */
+# define EH_FRAME_4(name) \
+    .byte 4;								      \
+    .long L(PUSHSI1)-name;						      \
+    .byte 14;								      \
+    .uleb128 8;								      \
+    .byte 4;								      \
+    .long L(PUSHBX1)-L(PUSHSI1);					      \
+    .byte 14;								      \
+    .uleb128 12;							      \
+    .byte 4;								      \
+    .long L(POPBX1)-L(PUSHBX1);						      \
+    .byte 14;								      \
+    .uleb128 8;								      \
+    .byte 4;								      \
+    .long L(POPSI1)-L(POPBX1);						      \
+    .byte 14;								      \
+    .uleb128 4;								      \
+    .byte 4;								      \
+    .long L(PUSHSTATE)-L(POPSI1);					      \
+    .byte 14;								      \
+    .uleb128 8;								      \
+    .byte 4;								      \
+    .long L(PUSHSI2)-L(PUSHSTATE);					      \
+    .byte 14;								      \
+    .uleb128 12;							      \
+    .byte 4;								      \
+    .long L(PUSHBX2)-L(PUSHSI2);					      \
+    .byte 14;								      \
+    .uleb128 16;							      \
+    .byte 4;								      \
+    .long L(POPBX2)-L(PUSHBX2);						      \
+    .byte 14;								      \
+    .uleb128 12;							      \
+    .byte 4;								      \
+    .long L(POPSI2)-L(POPBX2);						      \
+    .byte 14;								      \
+    .uleb128 8;								      \
+    .byte 4;								      \
+    .long L(POPSTATE)-L(POPSI2);					      \
+    .byte 14;								      \
+    .uleb128 4
+
+/* With five parameters the syscall wrappers have to save %ebx, %esi,
+   and %edi.  */
+# define EH_FRAME_5(name) \
+    .byte 4;								      \
+    .long L(PUSHDI1)-name;						      \
+    .byte 14;								      \
+    .uleb128 8;								      \
+    .byte 4;								      \
+    .long L(PUSHSI1)-L(PUSHDI1);					      \
+    .byte 14;								      \
+    .uleb128 12;							      \
+    .byte 4;								      \
+    .long L(PUSHBX1)-L(PUSHSI1);					      \
+    .byte 14;								      \
+    .uleb128 16;							      \
+    .byte 4;								      \
+    .long L(POPBX1)-L(PUSHBX1);						      \
+    .byte 14;								      \
+    .uleb128 12;							      \
+    .byte 4;								      \
+    .long L(POPSI1)-L(POPBX1);						      \
+    .byte 14;								      \
+    .uleb128 8;								      \
+    .byte 4;								      \
+    .long L(POPDI1)-L(POPSI1);						      \
+    .byte 14;								      \
+    .uleb128 4;								      \
+    .byte 4;								      \
+    .long L(PUSHSTATE)-L(POPDI1);					      \
+    .byte 14;								      \
+    .uleb128 8;								      \
+    .byte 4;								      \
+    .long L(PUSHDI2)-L(PUSHSTATE);					      \
+    .byte 14;								      \
+    .uleb128 12;							      \
+    .byte 4;								      \
+    .long L(PUSHSI2)-L(PUSHDI2);					      \
+    .byte 14;								      \
+    .uleb128 16;							      \
+    .byte 4;								      \
+    .long L(PUSHBX2)-L(PUSHSI2);					      \
+    .byte 14;								      \
+    .uleb128 20;							      \
+    .byte 4;								      \
+    .long L(POPBX2)-L(PUSHBX2);						      \
+    .byte 14;								      \
+    .uleb128 16;							      \
+    .byte 4;								      \
+    .long L(POPSI2)-L(POPBX2);						      \
+    .byte 14;								      \
+    .uleb128 12;							      \
+    .byte 4;								      \
+    .long L(POPDI2)-L(POPSI2);						      \
+    .byte 14;								      \
+    .uleb128 8;								      \
+    .byte 4;								      \
+    .long L(POPSTATE)-L(POPDI2);					      \
+    .byte 14;								      \
+    .uleb128 4
+
+
+# undef ASM_SIZE_DIRECTIVE
+# define ASM_SIZE_DIRECTIVE(name) L(name##END): .size name,.-name;
 
 # define SAVE_OLDTYPE_0	movl %eax, %edx;
 # define SAVE_OLDTYPE_1	SAVE_OLDTYPE_0
-# define SAVE_OLDTYPE_2	pushl %eax;
+# define SAVE_OLDTYPE_2	pushl %eax; L(PUSHSTATE):
 # define SAVE_OLDTYPE_3	SAVE_OLDTYPE_2
 # define SAVE_OLDTYPE_4	SAVE_OLDTYPE_2
 # define SAVE_OLDTYPE_5	SAVE_OLDTYPE_2
 
-# define DOCARGS_0	DOARGS_0
-# define DOCARGS_1	DOARGS_1
+# define PUSHCARGS_0	/* No arguments to push.  */
+# define DOCARGS_0	/* No arguments to frob.  */
+# define POPCARGS_0	/* No arguments to pop.  */
+# define _PUSHCARGS_0	/* No arguments to push.  */
+# define _POPCARGS_0	/* No arguments to pop.  */
+
+# define PUSHCARGS_1	movl %ebx, %edx; PUSHCARGS_0
+# define DOCARGS_1	_DOARGS_1 (4)
+# define POPCARGS_1	POPCARGS_0; movl %edx, %ebx
+# define _PUSHCARGS_1	pushl %ebx; L(PUSHBX2): _PUSHCARGS_0
+# define _POPCARGS_1	_POPCARGS_0; popl %ebx; L(POPBX2):
+
+# define PUSHCARGS_2	PUSHCARGS_1
 # define DOCARGS_2	_DOARGS_2 (12)
+# define POPCARGS_2	POPCARGS_1
+# define _PUSHCARGS_2	_PUSHCARGS_1
+# define _POPCARGS_2	_POPCARGS_1
+
+# define PUSHCARGS_3	_PUSHCARGS_2
 # define DOCARGS_3	_DOARGS_3 (20)
+# define POPCARGS_3	_POPCARGS_3
+# define _PUSHCARGS_3	_PUSHCARGS_2
+# define _POPCARGS_3	_POPCARGS_2
+
+# define PUSHCARGS_4	_PUSHCARGS_4
 # define DOCARGS_4	_DOARGS_4 (28)
+# define POPCARGS_4	_POPCARGS_4
+# define _PUSHCARGS_4	pushl %esi; L(PUSHSI2): _PUSHCARGS_3
+# define _POPCARGS_4	_POPCARGS_3; popl %esi; L(POPSI2):
+
+# define PUSHCARGS_5	_PUSHCARGS_5
 # define DOCARGS_5	_DOARGS_5 (36)
+# define POPCARGS_5	_POPCARGS_5
+# define _PUSHCARGS_5	pushl %edi; L(PUSHDI2): _PUSHCARGS_4
+# define _POPCARGS_5	_POPCARGS_4; popl %edi; L(POPDI2):
 
 # ifdef IS_IN_libpthread
 #  define CENABLE	call __pthread_enable_asynccancel;
@@ -69,12 +303,13 @@
 #  define CENABLE	call __libc_enable_asynccancel;
 #  define CDISABLE	call __libc_disable_asynccancel
 # endif
-# define POPCARGS_0	pushl %eax; movl %ecx, %eax; CDISABLE; popl %eax;
-# define POPCARGS_1	POPCARGS_0
-# define POPCARGS_2	xchgl (%esp), %eax; CDISABLE; popl %eax;
-# define POPCARGS_3	POPCARGS_2
-# define POPCARGS_4	POPCARGS_2
-# define POPCARGS_5	POPCARGS_2
+# define POPSTATE_0 \
+ pushl %eax; L(PUSHSTATE): movl %ecx, %eax; CDISABLE; popl %eax; L(POPSTATE):
+# define POPSTATE_1	POPSTATE_0
+# define POPSTATE_2	xchgl (%esp), %eax; CDISABLE; popl %eax; L(POPSTATE):
+# define POPSTATE_3	POPSTATE_2
+# define POPSTATE_4	POPSTATE_3
+# define POPSTATE_5	POPSTATE_4
 
 # ifndef __ASSEMBLER__
 #  define SINGLE_THREAD_P \
diff --git a/nptl/unwind.c b/nptl/unwind.c
new file mode 100644
index 0000000000..46c896d5b3
--- /dev/null
+++ b/nptl/unwind.c
@@ -0,0 +1,112 @@
+/* Copyright (C) 2003 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+   Contributed by Ulrich Drepper <drepper@redhat.com>
+   and Richard Henderson <rth@redhat.com>, 2003.
+
+   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 <setjmp.h>
+#include <stdlib.h>
+#include "pthreadP.h"
+
+
+#ifdef HAVE_FORCED_UNWIND
+
+static _Unwind_Reason_Code
+unwind_stop (int version, _Unwind_Action actions,
+	     _Unwind_Exception_Class exc_class,
+	     struct _Unwind_Exception *exc_obj,
+	     struct _Unwind_Context *context, void *stop_parameter)
+{
+  struct pthread_unwind_buf *buf = stop_parameter;
+
+  /* Do longjmp if we're at "end of stack", aka "end of unwind data".
+     We assume there are only C frame without unwind data in between
+     here and the jmp_buf target.  Otherwise simply note that the CFA
+     of a function is NOT within it's stack frame; it's the SP of the
+     previous frame.  */
+  if ((actions & _UA_END_OF_STACK)
+      || ! _JMPBUF_UNWINDS  (buf->cancel_jmp_buf[0].jmp_buf,
+			     _Unwind_GetCFA (context)))
+    __libc_longjmp ((struct __jmp_buf_tag *) buf->cancel_jmp_buf, 1);
+
+  return _URC_NO_REASON;
+}
+
+
+static void
+unwind_cleanup (_Unwind_Reason_Code reason, struct _Unwind_Exception *exc)
+{
+  /* Nothing to do.  */
+}
+
+#endif	/* have forced unwind */
+
+
+void
+__cleanup_fct_attribute __attribute ((noreturn))
+__pthread_unwind (__pthread_unwind_buf_t *buf)
+{
+  struct pthread_unwind_buf *ibuf = (struct pthread_unwind_buf *) buf;
+  struct pthread *self = THREAD_SELF;
+
+  /* Handle the compatibility stuff first.  Execute all handlers
+     registered with the old method.  We don't execute them in order,
+     instead, they will run first.  */
+  struct _pthread_cleanup_buffer *oldp = ibuf->priv.data.cleanup;
+  struct _pthread_cleanup_buffer *curp = THREAD_GETMEM (self, cleanup);
+
+  while (curp != oldp)
+    {
+      /* Pointer to the next element.  */
+      struct _pthread_cleanup_buffer *nextp = curp->__prev;
+
+      /* Call the handler.  */
+      curp->__routine (curp->__arg);
+
+      /* To the next.  */
+      curp = nextp;
+    }
+
+  /* Mark the current element as handled.  */
+  THREAD_SETMEM (self, cleanup, curp);
+
+#ifdef HAVE_FORCED_UNWIND
+  /* This is not a catchable exception, so don't provide any details about
+     the exception type.  We do need to initialize the field though.  */
+  ibuf->priv.data.exc.exception_class = 0;
+  ibuf->priv.data.exc.exception_cleanup = unwind_cleanup;
+
+  _Unwind_ForcedUnwind (&ibuf->priv.data.exc, unwind_stop, ibuf);
+#else
+  /* We simply jump to the registered setjmp buffer.  */
+  __libc_longjmp ((struct __jmp_buf_tag *) ibuf->cancel_jmp_buf, 1);
+#endif
+  /* NOTREACHED */
+
+  /* We better do not get here.  */
+  abort ();
+}
+
+
+void
+__cleanup_fct_attribute __attribute ((noreturn))
+__pthread_unwind_next (__pthread_unwind_buf_t *buf)
+{
+  struct pthread_unwind_buf *ibuf = (struct pthread_unwind_buf *) buf;
+
+  __pthread_unwind (ibuf->priv.data.prev);
+}
diff --git a/nptl/version.c b/nptl/version.c
index d0658bac0c..f2fd25fd6d 100644
--- a/nptl/version.c
+++ b/nptl/version.c
@@ -26,7 +26,11 @@ static const char banner[] =
 "Copyright (C) 2003 Free Software Foundation, Inc.\n\
 This is free software; see the source for copying conditions.\n\
 There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A\n\
-PARTICULAR PURPOSE.\n";
+PARTICULAR PURPOSE.\n"
+#ifdef HAVE_FORCED_UNWIND
+"Forced unwind support included.\n"
+#endif
+;
 
 
 extern void __nptl_main (void) __attribute__ ((noreturn));