about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--ChangeLog4
-rw-r--r--linuxthreads/ChangeLog41
-rw-r--r--linuxthreads/Makeconfig3
-rw-r--r--linuxthreads/Makefile45
-rw-r--r--linuxthreads/internals.h8
-rw-r--r--linuxthreads/old_pthread_atfork.c27
-rw-r--r--linuxthreads/ptfork.c99
-rw-r--r--linuxthreads/pthread.c4
-rw-r--r--linuxthreads/pthread_atfork.c46
-rw-r--r--linuxthreads/ptlongjmp.c17
-rw-r--r--linuxthreads/sysdeps/pthread/list.h116
-rw-r--r--linuxthreads/sysdeps/unix/sysv/linux/Makefile3
-rw-r--r--linuxthreads/sysdeps/unix/sysv/linux/Versions5
-rw-r--r--linuxthreads/sysdeps/unix/sysv/linux/fork.c40
-rw-r--r--linuxthreads/sysdeps/unix/sysv/linux/fork.h59
-rw-r--r--linuxthreads/sysdeps/unix/sysv/linux/ia64/fork.h25
-rw-r--r--linuxthreads/sysdeps/unix/sysv/linux/jmp-unwind.c32
-rw-r--r--linuxthreads/sysdeps/unix/sysv/linux/register-atfork.c87
-rw-r--r--linuxthreads/sysdeps/unix/sysv/linux/sparc/fork.h34
-rw-r--r--linuxthreads/sysdeps/unix/sysv/linux/unregister-atfork.c49
-rw-r--r--malloc/thread-m.h8
-rw-r--r--nptl/ChangeLog3
-rw-r--r--nptl/sysdeps/pthread/pt-initfini.c5
23 files changed, 679 insertions, 81 deletions
diff --git a/ChangeLog b/ChangeLog
index cc7830c53d..b6f7abd5c3 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,7 @@
+2002-12-30  Ulrich Drepper  <drepper@redhat.com>
+
+	* malloc/thread-m.h (thread_atfork): Define using __register_atfork.
+
 2002-12-30  Roland McGrath  <roland@redhat.com>
 
 	* Rules (generated): Target removed.
diff --git a/linuxthreads/ChangeLog b/linuxthreads/ChangeLog
index 6927282eba..16e26d8950 100644
--- a/linuxthreads/ChangeLog
+++ b/linuxthreads/ChangeLog
@@ -1,3 +1,44 @@
+2002-12-31  Jakub Jelinek  <jakub@redhat.com>
+
+	* sysdeps/pthread/list.h: New file.
+	* sysdeps/unix/sysv/linux/jmp-unwind.c: New file.
+	* sysdeps/unix/sysv/linux/fork.c: New file.
+	* sysdeps/unix/sysv/linux/fork.h: New file.
+	* sysdeps/unix/sysv/linux/ia64/fork.h: New file.
+	* sysdeps/unix/sysv/linux/sparc/fork.h: New file.
+	* sysdeps/unix/sysv/linux/register-atfork.c: New file.
+	* sysdeps/unix/sysv/linux/unregister-atfork.c: New file.
+	* sysdeps/unix/sysv/linux/Makefile: New file.
+	* sysdeps/unix/sysv/linux/Versions: New file.
+	* ptlongjmp.c (pthread_cleanup_upto): Rename to...
+	(__pthread_cleanup_upto): ...this. Add targetframe argument,
+	use it instead of currentframe.  No longer static.
+	(siglongjmp, longjmp): Remove pthread_cleanup_upto calls.
+	* internals.h (__pthread_cleanup_upto, __pthread_fork): New prototypes.
+	(struct pthread_functions): Add ptr_pthread_fork,
+	ptr_pthread_cleanup_upto.
+	* pthread.c (pthread_functions): Initialize ptr_pthread_fork and
+	ptr_pthread_cleanup_upto.
+	* ptfork.c: Include fork.h.
+	(struct handler_list, struct handler_list_block): Remove.
+	(pthread_atfork_lock, pthread_atfork_prepare, pthread_atfork_parent,
+	pthread_atfork_child): Remove.
+	(pthread_insert_list, __pthread_atfork, pthread_call_handlers): Remove.
+	(__pthread_fork): New function.
+	(__fork, __vfork): Call __libc_fork.
+	* Makefile (libpthread-routines): Add old_pthread_atfork.
+	(libpthread-nonshared): Add pthread_atfork.
+	(others): Depend on $(objpfx)libpthread_nonshared.a.
+	($(objpfx)libpthread_nonshared.a): New rule.
+	(install): Depend on $(inst_libdir)/libpthread.so.
+	($(inst_libdir)/libpthread.so, $(inst_libdir)/libpthread_nonshared.a):
+	New rules.
+	(tests): Depend on libpthread_nonshared.a too.
+	* old_pthread_atfork.c: New file.
+	* pthread_atfork.c: New file.
+	* Makeconfig (shared-thread-library): Include libpthread_nonshared.a
+	too.
+
 2002-12-30  Jakub Jelinek  <jakub@redhat.com>
 
 	* forward.c: Make all functions available by default again.  It
diff --git a/linuxthreads/Makeconfig b/linuxthreads/Makeconfig
index 4b1c0c3414..2dfd58c99e 100644
--- a/linuxthreads/Makeconfig
+++ b/linuxthreads/Makeconfig
@@ -3,7 +3,8 @@
 
 have-thread-library = yes
 
-shared-thread-library = $(common-objpfx)linuxthreads/libpthread.so
+shared-thread-library = $(common-objpfx)linuxthreads/libpthread.so \
+			$(common-objpfx)linuxthreads/libpthread_nonshared.a
 static-thread-library = $(common-objpfx)linuxthreads/libpthread.a
 bounded-thread-library = $(common-objpfx)linuxthreads/libpthread_b.a
 
diff --git a/linuxthreads/Makefile b/linuxthreads/Makefile
index a86aae7d53..9a6745fe17 100644
--- a/linuxthreads/Makefile
+++ b/linuxthreads/Makefile
@@ -46,12 +46,14 @@ libpthread-routines := attr cancel condvar join manager mutex ptfork \
 		       ptw-lseek64 ptw-llseek ptw-msync ptw-nanosleep \
 		       ptw-open ptw-open64 ptw-pause ptw-pread ptw-pread64 \
 		       ptw-pwrite ptw-pwrite64 ptw-tcdrain ptw-wait \
-		       ptw-waitpid pt-system
+		       ptw-waitpid pt-system old_pthread_atfork
 # Don't generate deps for calls with no sources.  See sysdeps/unix/Makefile.
 omit-deps = $(unix-syscalls:%=ptw-%)
 
 libpthread-shared-only-routines = pt-allocrtsig
 
+libpthread-nonshared = pthread_atfork
+
 nodelete-yes = -Wl,--enable-new-dtags,-z,nodelete
 initfirst-yes = -Wl,--enable-new-dtags,-z,initfirst
 LDFLAGS-pthread.so = $(nodelete-$(have-z-nodelete)) \
@@ -65,6 +67,14 @@ CFLAGS-tst-cancel.c = -fno-inline -fno-inline-functions
 include ../Makeconfig
 
 ifeq ($(build-shared),yes)
+others: $(objpfx)libpthread_nonshared.a
+endif
+
+$(objpfx)libpthread_nonshared.a: $(addprefix $(objpfx),$(addsuffix .os,$(libpthread-nonshared)))
+	$(AR) $(ARFLAGS) $@ $^
+
+ifeq ($(build-shared),yes)
+
 extra-objs += crti.o crtn.o
 omit-deps += crti crtn
 
@@ -92,6 +102,31 @@ endif
 
 include ../Rules
 
+# 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.
+# The libpthread.so object is not complete since some functions are only defined
+# in libpthread_nonshared.a.
+# 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),\
+							$(libprefix)pthread) \
+			      $(+force)
+	(echo '/* GNU ld script';\
+	 echo '   Use the shared library, but some functions are only in';\
+	 echo '   the static library, so try that secondarily.  */';\
+	 cat $<; \
+	 echo 'GROUP ( $(slibdir)/libpthread.so$(libpthread.so-version)' \
+	      '$(libdir)/$(patsubst %,$(libtype.oS),$(libprefix)pthread)'\
+	      ')' \
+	) > $@.new
+	mv -f $@.new $@
+$(inst_libdir)/libpthread_nonshared.a: $(objpfx)libpthread_nonshared.a
+	$(do-install)
+
+
 extra-B-pthread.so = -B$(common-objpfx)linuxthreads/
 $(objpfx)libpthread.so: $(objpfx)crti.o $(objpfx)crtn.o
 $(objpfx)libpthread.so: +preinit += $(objpfx)crti.o
@@ -154,14 +189,16 @@ $(objpfx)libpthread.so: $(libc-link.so) $(common-objpfx)libc_nonshared.a
 ifeq ($(build-shared),yes)
 $(addprefix $(objpfx), \
   $(filter-out $(tests-static) $(tests-reverse) unload, \
-    $(tests) $(test-srcs))): $(objpfx)libpthread.so
+    $(tests) $(test-srcs))): $(objpfx)libpthread.so \
+			     $(objpfx)libpthread_nonshared.a
 # $(objpfx)../libc.so is used instead of $(common-objpfx)libc.so,
 # since otherwise libpthread.so comes before libc.so when linking.
 $(addprefix $(objpfx), $(tests-reverse)): \
-  $(objpfx)../libc.so $(objpfx)libpthread.so
+  $(objpfx)../libc.so $(objpfx)libpthread.so \
+  $(objpfx)libpthread_nonshared.a
 $(addprefix $(objpfx),$(librt-tests)): $(common-objpfx)rt/librt.so
 $(objpfx)unload: $(common-objpfx)dlfcn/libdl.so
-$(objpfx)unload.out: $(objpfx)libpthread.so
+$(objpfx)unload.out: $(objpfx)libpthread.so $(objpfx)libpthread_nonshared.a
 else
 $(addprefix $(objpfx),$(tests) $(test-srcs)): $(objpfx)libpthread.a
 $(addprefix $(objpfx),$(librt-tests)): $(common-objpfx)rt/librt.a
diff --git a/linuxthreads/internals.h b/linuxthreads/internals.h
index 8c4de222b4..872bc5c047 100644
--- a/linuxthreads/internals.h
+++ b/linuxthreads/internals.h
@@ -463,6 +463,11 @@ extern int __libc_enable_asynccancel (void) attribute_hidden;
 extern void __libc_disable_asynccancel (int oldtype)
   internal_function attribute_hidden;
 
+extern void __pthread_cleanup_upto (__jmp_buf target,
+				    char *targetframe) attribute_hidden;
+struct fork_block;
+extern pid_t __pthread_fork (struct fork_block *b) attribute_hidden;
+
 #if !defined NOT_IN_libc
 # define LIBC_CANCEL_ASYNC() \
   __libc_enable_asynccancel ()
@@ -482,6 +487,7 @@ extern void __libc_disable_asynccancel (int oldtype)
    the thread functions.  */
 struct pthread_functions
 {
+  pid_t (*ptr_pthread_fork) (struct fork_block *);
   int (*ptr_pthread_attr_destroy) (pthread_attr_t *);
   int (*ptr___pthread_attr_init_2_0) (pthread_attr_t *);
   int (*ptr___pthread_attr_init_2_1) (pthread_attr_t *);
@@ -519,6 +525,8 @@ struct pthread_functions
   int (*ptr_pthread_setcancelstate) (int, int *);
   int (*ptr_pthread_setcanceltype) (int, int *);
   void (*ptr_pthread_do_exit) (void *retval, char *currentframe);
+  void (*ptr_pthread_cleanup_upto) (__jmp_buf target,
+				    char *targetframe);
   pthread_descr (*ptr_pthread_thread_self) (void);
   int (*ptr_pthread_internal_tsd_set) (int key, const void * pointer);
   void * (*ptr_pthread_internal_tsd_get) (int key);
diff --git a/linuxthreads/old_pthread_atfork.c b/linuxthreads/old_pthread_atfork.c
new file mode 100644
index 0000000000..768e6876c4
--- /dev/null
+++ b/linuxthreads/old_pthread_atfork.c
@@ -0,0 +1,27 @@
+/* Copyright (C) 2002 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+   Contributed by Ulrich Drepper <drepper@redhat.com>, 2002.
+
+   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 <shlib-compat.h>
+
+#if SHLIB_COMPAT (libpthread, GLIBC_2_0, GLIBC_2_3)
+# define __pthread_atfork __dyn_pthread_atfork
+# include "pthread_atfork.c"
+# undef __pthread_atfork
+compat_symbol (libpthread, __dyn_pthread_atfork, pthread_atfork, GLIBC_2_0);
+#endif
diff --git a/linuxthreads/ptfork.c b/linuxthreads/ptfork.c
index 564f8d7c75..195ffcd1b0 100644
--- a/linuxthreads/ptfork.c
+++ b/linuxthreads/ptfork.c
@@ -21,96 +21,71 @@
 #include "pthread.h"
 #include "internals.h"
 #include <bits/libc-lock.h>
+#include "fork.h"
 
-struct handler_list {
-  void (*handler)(void);
-  struct handler_list * next;
-};
+extern int __libc_fork (void);
 
-static pthread_mutex_t pthread_atfork_lock = PTHREAD_MUTEX_INITIALIZER;
-static struct handler_list * pthread_atfork_prepare = NULL;
-static struct handler_list * pthread_atfork_parent = NULL;
-static struct handler_list * pthread_atfork_child = NULL;
-
-static void pthread_insert_list(struct handler_list ** list,
-                                void (*handler)(void),
-                                struct handler_list * newlist,
-                                int at_end)
-{
-  if (handler == NULL) return;
-  if (at_end) {
-    while(*list != NULL) list = &((*list)->next);
-  }
-  newlist->handler = handler;
-  newlist->next = *list;
-  *list = newlist;
-}
-
-struct handler_list_block {
-  struct handler_list prepare, parent, child;
-};
-
-int __pthread_atfork(void (*prepare)(void),
-		     void (*parent)(void),
-		     void (*child)(void))
-{
-  struct handler_list_block * block =
-    (struct handler_list_block *) malloc(sizeof(struct handler_list_block));
-  if (block == NULL) return ENOMEM;
-  pthread_mutex_lock(&pthread_atfork_lock);
-  /* "prepare" handlers are called in LIFO */
-  pthread_insert_list(&pthread_atfork_prepare, prepare, &block->prepare, 0);
-  /* "parent" handlers are called in FIFO */
-  pthread_insert_list(&pthread_atfork_parent, parent, &block->parent, 1);
-  /* "child" handlers are called in FIFO */
-  pthread_insert_list(&pthread_atfork_child, child, &block->child, 1);
-  pthread_mutex_unlock(&pthread_atfork_lock);
-  return 0;
-}
-strong_alias (__pthread_atfork, pthread_atfork)
-
-static inline void pthread_call_handlers(struct handler_list * list)
-{
-  for (/*nothing*/; list != NULL; list = list->next) (list->handler)();
-}
-
-extern int __libc_fork(void);
-
-pid_t __fork(void)
+pid_t __pthread_fork (struct fork_block *b)
 {
   pid_t pid;
+  list_t *runp;
+
+  __libc_lock_lock (b->lock);
 
-  pthread_mutex_lock(&pthread_atfork_lock);
+  /* Run all the registered preparation handlers.  In reverse order.  */
+  list_for_each_prev (runp, &b->prepare_list)
+    {
+      struct fork_handler *curp;
+      curp = list_entry (runp, struct fork_handler, list);
+      curp->handler ();
+    }
 
-  pthread_call_handlers(pthread_atfork_prepare);
   __pthread_once_fork_prepare();
   __flockfilelist();
 
-  pid = __libc_fork();
+  pid = ARCH_FORK ();
 
   if (pid == 0) {
     __pthread_reset_main_thread();
 
     __fresetlockfiles();
     __pthread_once_fork_child();
-    pthread_call_handlers(pthread_atfork_child);
 
-    pthread_mutex_init(&pthread_atfork_lock, NULL);
+    /* Run the handlers registered for the child.  */
+    list_for_each (runp, &b->child_list)
+      {
+	struct fork_handler *curp;
+	curp = list_entry (runp, struct fork_handler, list);
+	curp->handler ();
+      }
+
+    __libc_lock_init (b->lock);
   } else {
     __funlockfilelist();
     __pthread_once_fork_parent();
-    pthread_call_handlers(pthread_atfork_parent);
 
-    pthread_mutex_unlock(&pthread_atfork_lock);
+    /* Run the handlers registered for the parent.  */
+    list_for_each (runp, &b->parent_list)
+      {
+	struct fork_handler *curp;
+	curp = list_entry (runp, struct fork_handler, list);
+	curp->handler ();
+      }
+
+    __libc_lock_unlock (b->lock);
   }
 
   return pid;
 }
 
+pid_t __fork (void)
+{
+  return __libc_fork ();
+}
 weak_alias (__fork, fork);
 
 pid_t __vfork(void)
 {
-  return __fork();
+  return __libc_fork ();
 }
 weak_alias (__vfork, vfork);
diff --git a/linuxthreads/pthread.c b/linuxthreads/pthread.c
index 15de7e610f..444c0a0ed2 100644
--- a/linuxthreads/pthread.c
+++ b/linuxthreads/pthread.c
@@ -238,6 +238,7 @@ static struct pthread_functions pthread_functions =
     .ptr_pthread_internal_tsd_get = __pthread_internal_tsd_get,
     .ptr_pthread_internal_tsd_address = __pthread_internal_tsd_address,
 #endif
+    .ptr_pthread_fork = __pthread_fork,
     .ptr_pthread_attr_destroy = __pthread_attr_destroy,
 #if SHLIB_COMPAT(libpthread, GLIBC_2_0, GLIBC_2_1)
     .ptr___pthread_attr_init_2_0 = __pthread_attr_init_2_0,
@@ -273,7 +274,8 @@ static struct pthread_functions pthread_functions =
     .ptr_pthread_setcancelstate = __pthread_setcancelstate,
     .ptr_pthread_setcanceltype = __pthread_setcanceltype,
     .ptr_pthread_do_exit = __pthread_do_exit,
-    .ptr_pthread_thread_self = __pthread_thread_self
+    .ptr_pthread_thread_self = __pthread_thread_self,
+    .ptr_pthread_cleanup_upto = __pthread_cleanup_upto
   };
 # define ptr_pthread_functions &pthread_functions
 #else
diff --git a/linuxthreads/pthread_atfork.c b/linuxthreads/pthread_atfork.c
new file mode 100644
index 0000000000..dabb26a726
--- /dev/null
+++ b/linuxthreads/pthread_atfork.c
@@ -0,0 +1,46 @@
+/* Copyright (C) 2002 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+   Contributed by Ulrich Drepper <drepper@redhat.com>, 2002.
+
+   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 "internals.h"
+#include <fork.h>
+
+/* This is defined by newer gcc version unique for each module.  */
+extern void *__dso_handle __attribute__ ((__weak__));
+
+
+/* Hide the symbol so that no definition but the one locally in the
+   executable or DSO is used.  */
+int
+#ifndef __pthread_atfork
+/* Don't mark the compatibility function as hidden.  */
+attribute_hidden
+#endif
+__pthread_atfork (prepare, parent, child)
+     void (*prepare) (void);
+     void (*parent) (void);
+     void (*child) (void);
+{
+  return __register_atfork (prepare, parent, child,
+			    &__dso_handle == NULL ? NULL : __dso_handle);
+}
+#ifndef __pthread_atfork
+extern int pthread_atfork (void (*prepare) (void), void (*parent) (void),
+			   void (*child) (void)) attribute_hidden;
+strong_alias (__pthread_atfork, pthread_atfork)
+#endif
diff --git a/linuxthreads/ptlongjmp.c b/linuxthreads/ptlongjmp.c
index 18592548ed..ece553141f 100644
--- a/linuxthreads/ptlongjmp.c
+++ b/linuxthreads/ptlongjmp.c
@@ -27,24 +27,23 @@ extern void __libc_longjmp (sigjmp_buf env, int val)
      __attribute__ ((noreturn));
 
 
-static void pthread_cleanup_upto(__jmp_buf target)
+void __pthread_cleanup_upto (__jmp_buf target, char *targetframe)
 {
   pthread_descr self = thread_self();
   struct _pthread_cleanup_buffer * c;
-  char *currentframe = CURRENT_STACK_FRAME;
 
   for (c = THREAD_GETMEM(self, p_cleanup);
        c != NULL && _JMPBUF_UNWINDS(target, c);
        c = c->__prev)
     {
 #if _STACK_GROWS_DOWN
-      if ((char *) c <= currentframe)
+      if ((char *) c <= targetframe)
 	{
 	  c = NULL;
 	  break;
 	}
 #elif _STACK_GROWS_UP
-      if ((char *) c >= currentframe)
+      if ((char *) c >= targetframe)
 	{
 	  c = NULL;
 	  break;
@@ -60,14 +59,12 @@ static void pthread_cleanup_upto(__jmp_buf target)
     THREAD_SETMEM(self, p_in_sighandler, NULL);
 }
 
-void siglongjmp(sigjmp_buf env, int val)
+void siglongjmp (sigjmp_buf env, int val)
 {
-  pthread_cleanup_upto(env->__jmpbuf);
-  __libc_siglongjmp(env, val);
+  __libc_siglongjmp (env, val);
 }
 
-void longjmp(jmp_buf env, int val)
+void longjmp (jmp_buf env, int val)
 {
-  pthread_cleanup_upto(env->__jmpbuf);
-  __libc_longjmp(env, val);
+  __libc_longjmp (env, val);
 }
diff --git a/linuxthreads/sysdeps/pthread/list.h b/linuxthreads/sysdeps/pthread/list.h
new file mode 100644
index 0000000000..1d6a4cfa65
--- /dev/null
+++ b/linuxthreads/sysdeps/pthread/list.h
@@ -0,0 +1,116 @@
+/* Copyright (C) 2002 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+   Contributed by Ulrich Drepper <drepper@redhat.com>, 2002.
+
+   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 _LIST_H
+#define _LIST_H	1
+
+#include <assert.h>
+
+/* The definitions of this file are adopted from those which can be
+   found in the Linux kernel headers to enable people familiar with
+   the latter find their way in these sources as well.  */
+
+
+/* Basic type for the double-link list.  */
+typedef struct list_head
+{
+  struct list_head *next;
+  struct list_head *prev;
+} list_t;
+
+
+/* Define a variable with the head and tail of the list.  */
+#define LIST_HEAD(name) \
+  list_t name = { &(name), &(name) }
+
+/* Initialize a new list head.  */
+#define INIT_LIST_HEAD(ptr) \
+  (ptr)->next = (ptr)->prev = (ptr)
+
+
+/* Add new element at the head of the list.  */
+static inline void
+list_add (list_t *newp, list_t *head)
+{
+  head->next->prev = newp;
+  newp->next = head->next;
+  newp->prev = head;
+  head->next = newp;
+}
+
+
+/* Add new element at the tail of the list.  */
+static inline void
+list_add_tail (list_t *newp, list_t *head)
+{
+  head->prev->next = newp;
+  newp->next = head;
+  newp->prev = head->prev;
+  head->prev = newp;
+}
+
+
+/* Remove element from list.  */
+static inline void
+list_del (list_t *elem)
+{
+  elem->next->prev = elem->prev;
+  elem->prev->next = elem->next;
+}
+
+
+/* Join two lists.  */
+static inline void
+list_splice (list_t *add, list_t *head)
+{
+  /* Do nothing if the list which gets added is empty.  */
+  if (add != add->next)
+    {
+      add->next->prev = head;
+      add->prev->next = head->next;
+      head->next->prev = add->prev;
+      head->next = add->next;
+    }
+}
+
+
+/* Get typed element from list at a given position.  */
+#define list_entry(ptr, type, member) \
+  ((type *) ((char *) (ptr) - (unsigned long) (&((type *) 0)->member)))
+
+
+
+/* Iterate forward over the elements of the list.  */
+#define list_for_each(pos, head) \
+  for (pos = (head)->next; pos != (head); pos = pos->next)
+
+
+/* Iterate forward over the elements of the list.  */
+#define list_for_each_prev(pos, head) \
+  for (pos = (head)->prev; pos != (head); pos = pos->prev)
+
+
+/* Iterate backwards over the elements list.  The list elements can be
+   removed from the list while doing this.  */
+#define list_for_each_prev_safe(pos, p, head) \
+  for (pos = (head)->prev, p = pos->prev; \
+       pos != (head); \
+       pos = p, p = pos->prev)
+
+#endif	/* list.h */
diff --git a/linuxthreads/sysdeps/unix/sysv/linux/Makefile b/linuxthreads/sysdeps/unix/sysv/linux/Makefile
new file mode 100644
index 0000000000..38c6cbc1af
--- /dev/null
+++ b/linuxthreads/sysdeps/unix/sysv/linux/Makefile
@@ -0,0 +1,3 @@
+ifeq ($(subdir),linuxthreads)
+sysdep_routines += register-atfork unregister-atfork
+endif
diff --git a/linuxthreads/sysdeps/unix/sysv/linux/Versions b/linuxthreads/sysdeps/unix/sysv/linux/Versions
new file mode 100644
index 0000000000..6cd3dbe372
--- /dev/null
+++ b/linuxthreads/sysdeps/unix/sysv/linux/Versions
@@ -0,0 +1,5 @@
+libc {
+  GLIBC_2.3.2 {
+    __register_atfork;
+  }
+}
diff --git a/linuxthreads/sysdeps/unix/sysv/linux/fork.c b/linuxthreads/sysdeps/unix/sysv/linux/fork.c
new file mode 100644
index 0000000000..c519fa0677
--- /dev/null
+++ b/linuxthreads/sysdeps/unix/sysv/linux/fork.c
@@ -0,0 +1,40 @@
+/* Copyright (C) 2002 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+   Contributed by Jakub Jelinek <jakub@redhat.com>, 2002.
+
+   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 "fork.h"
+#include <bits/libc-lock.h>
+
+weak_extern (__pthread_fork);
+
+struct fork_block __fork_block =
+{
+  .lock = PTHREAD_MUTEX_INITIALIZER,
+  .prepare_list = { &__fork_block.prepare_list, &__fork_block.prepare_list },
+  .parent_list = { &__fork_block.parent_list, &__fork_block.parent_list },
+  .child_list = { &__fork_block.child_list, &__fork_block.child_list }
+};
+
+pid_t
+__libc_fork (void)
+{
+  return __libc_maybe_call2 (pthread_fork, (&__fork_block), ARCH_FORK ());
+}
+weak_alias (__libc_fork, __fork)
+weak_alias (__libc_fork, fork)
diff --git a/linuxthreads/sysdeps/unix/sysv/linux/fork.h b/linuxthreads/sysdeps/unix/sysv/linux/fork.h
new file mode 100644
index 0000000000..e84119a2ef
--- /dev/null
+++ b/linuxthreads/sysdeps/unix/sysv/linux/fork.h
@@ -0,0 +1,59 @@
+/* Copyright (C) 2002 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+   Contributed by Ulrich Drepper <drepper@redhat.com>, 2002.
+
+   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 <list.h>
+#include <bits/libc-lock.h>
+#include <sysdep.h>
+
+struct fork_block
+{
+  /* Lock to protect handling of fork handlers.  */
+  __libc_lock_define (, lock);
+
+  /* Lists of registered fork handlers.  */
+  list_t prepare_list;
+  list_t parent_list;
+  list_t child_list;
+};
+
+extern struct fork_block __fork_block attribute_hidden;
+
+/* Elements of the fork handler lists.  */
+struct fork_handler
+{
+  list_t list;
+  void (*handler) (void);
+  void *dso_handle;
+};
+
+
+/* Function to call to unregister fork handlers.  */
+extern void __unregister_atfork (void *dso_handle) attribute_hidden;
+#define UNREGISTER_ATFORK(dso_handle) __unregister_atfork (dso_handle)
+
+
+/* C library side function to register new fork handlers.  */
+extern int __register_atfork (void (*__prepare) (void),
+			      void (*__parent) (void),
+			      void (*__child) (void),
+			      void *dso_handle);
+
+#ifndef ARCH_FORK
+# define ARCH_FORK() INLINE_SYSCALL (fork, 0)
+#endif
diff --git a/linuxthreads/sysdeps/unix/sysv/linux/ia64/fork.h b/linuxthreads/sysdeps/unix/sysv/linux/ia64/fork.h
new file mode 100644
index 0000000000..30a0cc1918
--- /dev/null
+++ b/linuxthreads/sysdeps/unix/sysv/linux/ia64/fork.h
@@ -0,0 +1,25 @@
+/* Copyright (C) 2002 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+   Contributed by Jakub Jelinek <jakub@redhat.com>, 2002.
+
+   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 <signal.h>
+#include <sysdep.h>
+
+#define ARCH_FORK() INLINE_SYSCALL (clone, 2, SIGCHLD, 0)
+
+#include_next <fork.h>
diff --git a/linuxthreads/sysdeps/unix/sysv/linux/jmp-unwind.c b/linuxthreads/sysdeps/unix/sysv/linux/jmp-unwind.c
new file mode 100644
index 0000000000..37cc4c2c0b
--- /dev/null
+++ b/linuxthreads/sysdeps/unix/sysv/linux/jmp-unwind.c
@@ -0,0 +1,32 @@
+/* _longjmp_unwind -- Clean up stack frames unwound by longjmp.
+   Copyright (C) 2002 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 <setjmp.h>
+#include <stddef.h>
+#include <bits/libc-lock.h>
+
+weak_extern (__pthread_cleanup_upto);
+
+void
+_longjmp_unwind (jmp_buf env, int val)
+{
+  __libc_maybe_call2 (pthread_cleanup_upto,
+		      (env->__jmpbuf, __builtin_frame_address (0)),
+		      (void) 0);
+}
diff --git a/linuxthreads/sysdeps/unix/sysv/linux/register-atfork.c b/linuxthreads/sysdeps/unix/sysv/linux/register-atfork.c
new file mode 100644
index 0000000000..2dce262a38
--- /dev/null
+++ b/linuxthreads/sysdeps/unix/sysv/linux/register-atfork.c
@@ -0,0 +1,87 @@
+/* Copyright (C) 2002 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+   Contributed by Ulrich Drepper <drepper@redhat.com>, 2002.
+
+   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 <stdlib.h>
+#include "fork.h"
+
+
+int
+__register_atfork (prepare, parent, child, dso_handle)
+     void (*prepare) (void);
+     void (*parent) (void);
+     void (*child) (void);
+     void *dso_handle;
+{
+  struct fork_handler *new_prepare = NULL;
+  struct fork_handler *new_parent = NULL;
+  struct fork_handler *new_child = NULL;
+
+  if (prepare != NULL)
+    {
+      new_prepare = (struct fork_handler *) malloc (sizeof (*new_prepare));
+      if (new_prepare == NULL)
+	goto out1;
+
+      new_prepare->handler = prepare;
+      new_prepare->dso_handle = dso_handle;
+    }
+
+  if (parent != NULL)
+    {
+      new_parent = (struct fork_handler *) malloc (sizeof (*new_parent));
+      if (new_parent == NULL)
+	goto out2;
+
+      new_parent->handler = parent;
+      new_parent->dso_handle = dso_handle;
+    }
+
+  if (child != NULL)
+    {
+      new_child = (struct fork_handler *) malloc (sizeof (*new_child));
+      if (new_child == NULL)
+	{
+	  free (new_parent);
+	out2:
+	  free (new_prepare);
+	out1:
+	  return errno;
+	}
+
+      new_child->handler = child;
+      new_child->dso_handle = dso_handle;
+    }
+
+  /* Get the lock to not conflict with running forks.  */
+  __libc_lock_lock (__fork_block.lock);
+
+  /* Now that we have all the handlers allocate enqueue them.  */
+  if (new_prepare != NULL)
+    list_add_tail (&new_prepare->list, &__fork_block.prepare_list);
+  if (new_parent != NULL)
+    list_add_tail (&new_parent->list, &__fork_block.parent_list);
+  if (new_child != NULL)
+    list_add_tail (&new_child->list, &__fork_block.child_list);
+
+  /* Release the lock.  */
+  __libc_lock_unlock (__fork_block.lock);
+
+  return 0;
+}
diff --git a/linuxthreads/sysdeps/unix/sysv/linux/sparc/fork.h b/linuxthreads/sysdeps/unix/sysv/linux/sparc/fork.h
new file mode 100644
index 0000000000..793cb1d5f9
--- /dev/null
+++ b/linuxthreads/sysdeps/unix/sysv/linux/sparc/fork.h
@@ -0,0 +1,34 @@
+/* Copyright (C) 2002 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+   Contributed by Jakub Jelinek <jakub@redhat.com>, 2002.
+
+   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>
+
+#define ARCH_FORK() \
+({									\
+  register long __o0 __asm__ ("o0");					\
+  register long __o1 __asm__ ("o1");					\
+  register long __g1 __asm__ ("g1") = __NR_fork;			\
+  __asm __volatile (__SYSCALL_STRING					\
+		    : "=r" (__g1), "=r" (__o0), "=r" (__o1)		\
+		    : "0" (__g1)					\
+		    : __SYSCALL_CLOBBERS);				\
+  __o0 == -1 ? __o0 : (__o0 & (__o1 - 1));				\
+})
+
+#include_next <fork.h>
diff --git a/linuxthreads/sysdeps/unix/sysv/linux/unregister-atfork.c b/linuxthreads/sysdeps/unix/sysv/linux/unregister-atfork.c
new file mode 100644
index 0000000000..dad273fdf5
--- /dev/null
+++ b/linuxthreads/sysdeps/unix/sysv/linux/unregister-atfork.c
@@ -0,0 +1,49 @@
+/* Copyright (C) 2002 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+   Contributed by Ulrich Drepper <drepper@redhat.com>, 2002.
+
+   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 <stdlib.h>
+#include "fork.h"
+
+
+void
+__unregister_atfork (dso_handle)
+     void *dso_handle;
+{
+  /* Get the lock to not conflict with running forks.  */
+  __libc_lock_lock (__fork_block.lock);
+
+  list_t *runp;
+  list_t *prevp;
+
+  list_for_each_prev_safe (runp, prevp, &__fork_block.prepare_list)
+    if (list_entry (runp, struct fork_handler, list)->dso_handle == dso_handle)
+      list_del (runp);
+
+  list_for_each_prev_safe (runp, prevp, &__fork_block.parent_list)
+    if (list_entry (runp, struct fork_handler, list)->dso_handle == dso_handle)
+      list_del (runp);
+
+  list_for_each_prev_safe (runp, prevp, &__fork_block.child_list)
+    if (list_entry (runp, struct fork_handler, list)->dso_handle == dso_handle)
+      list_del (runp);
+
+  /* Release the lock.  */
+  __libc_lock_unlock (__fork_block.lock);
+}
diff --git a/malloc/thread-m.h b/malloc/thread-m.h
index 49db784c52..d3352d1fe5 100644
--- a/malloc/thread-m.h
+++ b/malloc/thread-m.h
@@ -72,8 +72,14 @@ __libc_lock_define (typedef, mutex_t)
 
 #endif
 
+/* This is defined by newer gcc version unique for each module.  */
+extern void *__dso_handle __attribute__ ((__weak__));
+
+#include <fork.h>
+
 #define thread_atfork(prepare, parent, child) \
-   (__pthread_atfork != NULL ? __pthread_atfork(prepare, parent, child) : 0)
+   __register_atfork (prepare, parent, child,				      \
+		      &__dso_handle == NULL ? NULL : __dso_handle)
 
 #elif defined(MUTEX_INITIALIZER)
 /* Assume hurd, with cthreads */
diff --git a/nptl/ChangeLog b/nptl/ChangeLog
index 3d527a959a..35ef11fba9 100644
--- a/nptl/ChangeLog
+++ b/nptl/ChangeLog
@@ -1,5 +1,8 @@
 2002-12-30  Ulrich Drepper  <drepper@redhat.com>
 
+	* sysdeps/pthread/pt-initfini.c (call_initialize_minimal): Mark
+	__pthread_initialize_minimal as hidden.
+
 	* init.c (__pthread_initialize_minimal_internal): Don't mark as
 	constructor.
 
diff --git a/nptl/sysdeps/pthread/pt-initfini.c b/nptl/sysdeps/pthread/pt-initfini.c
index 55d9f31562..7aa734d020 100644
--- a/nptl/sysdeps/pthread/pt-initfini.c
+++ b/nptl/sysdeps/pthread/pt-initfini.c
@@ -1,5 +1,5 @@
 /* Special .init and .fini section support.  Linuxthread version.
-   Copyright (C) 1995, 1996, 1997, 2000, 2001 Free Software Foundation, Inc.
+   Copyright (C) 1995,1996,1997,2000,2001,2002 Free Software Foundation, Inc.
    This file is part of the GNU C Library.
 
    The GNU C Library is free software; you can redistribute it
@@ -65,7 +65,8 @@ asm ("\n/*@_init_PROLOG_BEGINS*/");
 static void
 call_initialize_minimal (void)
 {
-  extern void __pthread_initialize_minimal (void);
+  extern void __pthread_initialize_minimal (void)
+    __attribute ((visibility ("hidden")));
 
   __pthread_initialize_minimal ();
 }