about summary refs log tree commit diff
diff options
context:
space:
mode:
authorAndreas Jaeger <aj@suse.de>2005-03-28 09:26:46 +0000
committerAndreas Jaeger <aj@suse.de>2005-03-28 09:26:46 +0000
commitf850220be60d259b62f1f5c5d8b568c105c4ecf2 (patch)
treea339b223ea2c0ef1be9c757a1c90adf0dd795a63
parentabc85e9fea35a8e2eedfc9796c8d81ae5364bcf7 (diff)
downloadglibc-f850220be60d259b62f1f5c5d8b568c105c4ecf2.tar.gz
glibc-f850220be60d259b62f1f5c5d8b568c105c4ecf2.tar.xz
glibc-f850220be60d259b62f1f5c5d8b568c105c4ecf2.zip
Update.
	* sysdeps/mips/atomicity.h: Remove unused file.
	* sysdeps/mips/dl-machine.h (elf_machine_rel): Add TLS relocations.
	* sysdeps/mips/dl-tls.h: New file.
	* sysdeps/mips/libc-tls.c: New file.
	* sysdeps/mips/tls-macros.h: New file.
	* sysdeps/mips/bits/atomic.h: New file.
	* sysdeps/mips/bits/setjmp.h: Protect against multiple inclusion.
	* sysdeps/mips/elf/configure.in: New file.
	* sysdeps/mips/elf/configure: Generated.
	* sysdeps/mips/sys/asm.h: New file.
	* sysdeps/unix/sysv/linux/mips/vfork.S: New file.
	* sysdeps/unix/sysv/linux/mips/clone.S: Add NPTL and five-argument
	clone support.
	* sysdeps/unix/sysv/linux/mips/mips32/sysdep.h
	(INTERNAL_SYSCALL_NCS): New.
	(INTERNAL_SYSCALL): Update for non-constant support.
	(internal_syscall0): Likewise.
	(internal_syscall1): Likewise.
	(internal_syscall2): Likewise.
	(internal_syscall3): Likewise.
	(internal_syscall4): Likewise.
	(internal_syscall5): Likewise.
	(internal_syscall6): Likewise.
	(internal_syscall7): Likewise.
	* sysdeps/unix/sysv/linux/mips/bits/siginfo.h (SIGEV_THREAD):
	Update to match the kernel.
	(SIGEV_CALLBACK): Likewise.
	(SIGEV_THREAD_ID): Likewise.

2005-03-28  Daniel Jacobowitz  <dan@codesourcery.com>
-rw-r--r--ChangeLog31
-rw-r--r--linuxthreads/ChangeLog5
-rw-r--r--linuxthreads/sysdeps/mips/tls.h170
-rw-r--r--linuxthreads/sysdeps/unix/sysv/linux/mips/vfork.S104
-rw-r--r--nptl/ChangeLog21
-rw-r--r--nptl/sysdeps/mips/Makefile25
-rw-r--r--nptl/sysdeps/mips/jmpbuf-unwind.h30
-rw-r--r--nptl/sysdeps/mips/nptl-sysdep.S2
-rw-r--r--nptl/sysdeps/mips/pthread_spin_lock.S37
-rw-r--r--nptl/sysdeps/mips/pthread_spin_trylock.S41
-rw-r--r--nptl/sysdeps/mips/pthreaddef.h39
-rw-r--r--nptl/sysdeps/mips/tcb-offsets.sym11
-rw-r--r--nptl/sysdeps/mips/tls.h161
-rw-r--r--nptl/sysdeps/unix/sysv/linux/mips/bits/semaphore.h40
-rw-r--r--nptl/sysdeps/unix/sysv/linux/mips/clone.S2
-rw-r--r--nptl/sysdeps/unix/sysv/linux/mips/createthread.c24
-rw-r--r--nptl/sysdeps/unix/sysv/linux/mips/fork.c1
-rw-r--r--nptl/sysdeps/unix/sysv/linux/mips/lowlevellock.h216
-rw-r--r--nptl/sysdeps/unix/sysv/linux/mips/pt-vfork.S37
-rw-r--r--nptl/sysdeps/unix/sysv/linux/mips/pthread_once.c94
-rw-r--r--nptl/sysdeps/unix/sysv/linux/mips/sysdep-cancel.h170
-rw-r--r--nptl/sysdeps/unix/sysv/linux/mips/vfork.S42
-rw-r--r--sysdeps/mips/atomicity.h113
-rw-r--r--sysdeps/mips/bits/atomic.h303
-rw-r--r--sysdeps/mips/dl-tls.h46
-rw-r--r--sysdeps/mips/dl-trampoline.c2
-rw-r--r--sysdeps/mips/elf/configure46
-rw-r--r--sysdeps/mips/elf/configure.in35
-rw-r--r--sysdeps/mips/libc-tls.c37
-rw-r--r--sysdeps/mips/sys/asm.h19
-rw-r--r--sysdeps/mips/tls-macros.h88
-rw-r--r--sysdeps/unix/sysv/linux/mips/bits/siginfo.h9
-rw-r--r--sysdeps/unix/sysv/linux/mips/clone.S60
-rw-r--r--sysdeps/unix/sysv/linux/mips/mips32/sysdep.h285
-rw-r--r--sysdeps/unix/sysv/linux/mips/vfork.S98
35 files changed, 2186 insertions, 258 deletions
diff --git a/ChangeLog b/ChangeLog
index 48d7deab70..966cb8245c 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,36 @@
 2005-03-28  Daniel Jacobowitz  <dan@codesourcery.com>
 
+	* sysdeps/mips/atomicity.h: Remove unused file.
+	* sysdeps/mips/dl-machine.h (elf_machine_rel): Add TLS relocations.
+	* sysdeps/mips/dl-tls.h: New file.
+	* sysdeps/mips/libc-tls.c: New file.
+	* sysdeps/mips/tls-macros.h: New file.
+	* sysdeps/mips/bits/atomic.h: New file.
+	* sysdeps/mips/bits/setjmp.h: Protect against multiple inclusion.
+	* sysdeps/mips/elf/configure.in: New file.
+	* sysdeps/mips/elf/configure: Generated.
+	* sysdeps/mips/sys/asm.h: New file.
+	* sysdeps/unix/sysv/linux/mips/vfork.S: New file.
+	* sysdeps/unix/sysv/linux/mips/clone.S: Add NPTL and five-argument
+	clone support.
+	* sysdeps/unix/sysv/linux/mips/mips32/sysdep.h
+	(INTERNAL_SYSCALL_NCS): New.
+	(INTERNAL_SYSCALL): Update for non-constant support.
+	(internal_syscall0): Likewise.
+	(internal_syscall1): Likewise.
+	(internal_syscall2): Likewise.
+	(internal_syscall3): Likewise.
+	(internal_syscall4): Likewise.
+	(internal_syscall5): Likewise.
+	(internal_syscall6): Likewise.
+	(internal_syscall7): Likewise.
+	* sysdeps/unix/sysv/linux/mips/bits/siginfo.h (SIGEV_THREAD):
+	Update to match the kernel.
+	(SIGEV_CALLBACK): Likewise.
+	(SIGEV_THREAD_ID): Likewise.
+
+2005-03-28  Daniel Jacobowitz  <dan@codesourcery.com>
+
 	[BZ #783]
 	* elf/tst-auditmod1.c: Add MIPS support.
 	* sysdeps/generic/ldsodefs.h (La_mips_32_regs): New.
diff --git a/linuxthreads/ChangeLog b/linuxthreads/ChangeLog
index bf842818d7..dfecefc9ac 100644
--- a/linuxthreads/ChangeLog
+++ b/linuxthreads/ChangeLog
@@ -1,3 +1,8 @@
+2005-03-28  Daniel Jacobowitz  <dan@codesourcery.com>
+
+	* sysdeps/mips/tls.h: New file.
+	* sysdeps/unix/sysv/linux/mips/vfork.S: New file.
+
 2005-03-21  GOTO Masanori  <gotom@debian.or.jp>
 
 	* linuxthreads/sysdeps/m68k/Makefile: Remove to delete
diff --git a/linuxthreads/sysdeps/mips/tls.h b/linuxthreads/sysdeps/mips/tls.h
new file mode 100644
index 0000000000..be4478db43
--- /dev/null
+++ b/linuxthreads/sysdeps/mips/tls.h
@@ -0,0 +1,170 @@
+/* Definitions for thread-local data handling.  linuxthreads/MIPS version.
+   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.  */
+
+#ifndef _TLS_H
+#define _TLS_H
+
+#ifndef __ASSEMBLER__
+
+# include <stdbool.h>
+# include <pt-machine.h>
+# include <stddef.h>
+
+/* Type for the dtv.  */
+typedef union dtv
+{
+  size_t counter;
+  struct
+  {
+    void *val;
+    bool is_static;
+  } pointer;
+} dtv_t;
+
+# define READ_THREAD_POINTER() \
+    ({ void *__result;							      \
+       asm volatile (".set\tpush\n\t.set\tmips32r2\n\t"			      \
+		     "rdhwr\t%0, $29\n\t.set\tpop" : "=v" (__result));	      \
+       __result; })
+
+#else /* __ASSEMBLER__ */
+# include <tcb-offsets.h>
+
+/* Note: rd must be $v1 to be ABI-conformant.  */
+# define READ_THREAD_POINTER(rd) \
+	.set	push;							      \
+	.set	mips32r2;						      \
+	rdhwr	rd, $29;						      \
+	.set	pop
+#endif /* __ASSEMBLER__ */
+
+/* LinuxThreads can only use TLS if both floating stacks (in the MIPS case,
+   that means support for "rdhwr") and support from the tools are available.
+
+   We have to define USE_TLS consistently, or ldsodefs.h will lay out types
+   differently between an NPTL build and a LinuxThreads build.  It can be set
+   for libc.so and not libpthread.so, but only if we provide appropriate padding
+   in the _pthread_descr_struct.
+
+   Currently nothing defines FLOATING_STACKS.  We could assume this based on
+   kernel version once the TLS patches are available in kernel.org, but
+   it hardly seems worth it.  Use NPTL if you can.
+
+   To avoid bothering with the TLS support code at all, use configure
+   --without-tls.  */
+
+#if defined HAVE_TLS_SUPPORT \
+    && (defined FLOATING_STACKS || !defined IS_IN_libpthread)
+
+/* Signal that TLS support is available.  */
+# define USE_TLS	1
+
+/* Include padding in _pthread_descr_struct so that libc can find p_errno,
+   if libpthread will only include the padding because of the !IS_IN_libpthread
+   check.  */
+#ifndef FLOATING_STACKS
+# define INCLUDE_TLS_PADDING	1
+#endif
+
+# ifndef __ASSEMBLER__
+
+/* This layout is actually wholly private and not affected by the ABI.
+   Nor does it overlap the pthread data structure, so we need nothing
+   extra here at all.  */
+typedef struct
+{
+  dtv_t *dtv;
+  void *private;
+} tcbhead_t;
+
+/* This is the size of the initial TCB.  */
+#  define TLS_INIT_TCB_SIZE	0
+
+/* Alignment requirements for the initial TCB.  */
+#  define TLS_INIT_TCB_ALIGN	__alignof__ (struct _pthread_descr_struct)
+
+/* This is the size of the TCB.  */
+#  define TLS_TCB_SIZE		0
+
+/* Alignment requirements for the TCB.  */
+#  define TLS_TCB_ALIGN		__alignof__ (struct _pthread_descr_struct)
+
+/* This is the size we need before TCB.  */
+#  define TLS_PRE_TCB_SIZE \
+  (sizeof (struct _pthread_descr_struct)				      \
+   + ((sizeof (tcbhead_t) + TLS_TCB_ALIGN - 1) & ~(TLS_TCB_ALIGN - 1)))
+
+/* The thread pointer (in hardware register $29) points to the end of
+   the TCB + 0x7000, as for PowerPC.  The pthread_descr structure is
+   immediately in front of the TCB.  */
+#define TLS_TCB_OFFSET		0x7000
+
+/* The DTV is allocated at the TP; the TCB is placed elsewhere.  */
+/* This is not really true for powerpc64.  We are following alpha
+   where the DTV pointer is first doubleword in the TCB.  */
+#  define TLS_DTV_AT_TP 1
+
+/* Install the dtv pointer.  The pointer passed is to the element with
+   index -1 which contain the length.  */
+#  define INSTALL_DTV(TCBP, DTVP) \
+  (((tcbhead_t *) (TCBP))[-1].dtv = (DTVP) + 1)
+
+/* Install new dtv for current thread.  */
+#  define INSTALL_NEW_DTV(DTV) (THREAD_DTV() = (DTV))
+
+/* Return dtv of given thread descriptor.  */
+#  define GET_DTV(TCBP)	(((tcbhead_t *) (TCBP))[-1].dtv)
+
+/* Code to initially initialize the thread pointer.  This might need
+   special attention since 'errno' is not yet available and if the
+   operation can cause a failure 'errno' must not be touched.  */
+# define TLS_INIT_TP(tcbp, secondcall) \
+  ({ INTERNAL_SYSCALL_DECL (err);					\
+     long result_var;							\
+     result_var = INTERNAL_SYSCALL (set_thread_area, err, 1,		\
+				    (char *) (tcbp) + TLS_TCB_OFFSET);	\
+     INTERNAL_SYSCALL_ERROR_P (result_var, err)				\
+       ? "unknown error" : NULL; })
+
+/* Return the address of the dtv for the current thread.  */
+# define THREAD_DTV() \
+  (((tcbhead_t *) (READ_THREAD_POINTER () - TLS_TCB_OFFSET))[-1].dtv)
+
+/* Return the thread descriptor for the current thread.  */
+#  undef THREAD_SELF
+#  define THREAD_SELF \
+    ((pthread_descr) (READ_THREAD_POINTER () \
+		      - TLS_TCB_OFFSET - TLS_PRE_TCB_SIZE))
+
+/* Get the thread descriptor definition.  */
+#  include <linuxthreads/descr.h>
+
+/* l_tls_offset == 0 is perfectly valid on MIPS, so we have to use some
+   different value to mean unset l_tls_offset.  */
+#  define NO_TLS_OFFSET	-1
+
+/* Initializing the thread pointer requires a syscall which may not be
+   available, so don't do it if we don't need to.  */
+#  define TLS_INIT_TP_EXPENSIVE 1
+
+# endif /* __ASSEMBLER__ */
+
+#endif /* HAVE_TLS_SUPPORT */
+
+#endif	/* tls.h */
diff --git a/linuxthreads/sysdeps/unix/sysv/linux/mips/vfork.S b/linuxthreads/sysdeps/unix/sysv/linux/mips/vfork.S
new file mode 100644
index 0000000000..bf541439b5
--- /dev/null
+++ b/linuxthreads/sysdeps/unix/sysv/linux/mips/vfork.S
@@ -0,0 +1,104 @@
+/* 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.  */
+
+/* vfork() is just a special case of clone().  */
+
+#include <sys/asm.h>
+#include <sysdep.h>
+#include <asm/unistd.h>
+
+/* int vfork() */
+
+	.text
+LOCALSZ= 1
+FRAMESZ= (((NARGSAVE+LOCALSZ)*SZREG)+ALSZ)&ALMASK
+GPOFF= FRAMESZ-(1*SZREG)
+NESTED(__vfork,FRAMESZ,sp)
+#ifdef __PIC__
+	SETUP_GP
+#endif
+	PTR_SUBU sp, FRAMESZ
+	SETUP_GP64 (a5, __vfork)
+#ifdef __PIC__
+	SAVE_GP (GPOFF)
+#endif
+#ifdef PROF
+# if (_MIPS_SIM != _ABIO32)
+	PTR_S		a5, GPOFF(sp)
+# endif
+	.set		noat
+	move		$1, ra
+# if (_MIPS_SIM == _ABIO32)
+	subu		sp,sp,8
+# endif
+	jal		_mcount
+	.set		at
+# if (_MIPS_SIM != _ABIO32)
+	PTR_L		a5, GPOFF(sp)
+# endif
+#endif
+
+	/* If libpthread is loaded, we need to call fork instead.  */
+#ifdef SHARED
+	PTR_L		a0, __libc_pthread_functions
+#else
+	.weak		pthread_create
+	PTR_LA		a0, pthread_create
+#endif
+
+	PTR_ADDU	sp, FRAMESZ
+
+	bnez		a0, L(call_fork)
+
+	li		a0, 0x4112	/* CLONE_VM | CLONE_VFORK | SIGCHLD */
+	move		a1, sp
+
+	/* Do the system call */
+	li		v0,__NR_clone
+	syscall
+
+	bnez		a3,L(error)
+
+	/* Successful return from the parent or child.  */
+	RESTORE_GP64
+	ret
+
+	/* Something bad happened -- no child created.  */
+L(error):
+#ifdef __PIC__
+	PTR_LA		t9, __syscall_error
+	RESTORE_GP64
+	jr		t9
+#else
+	RESTORE_GP64
+	j		__syscall_error
+#endif
+
+L(call_fork):
+#ifdef __PIC__
+	PTR_LA		t9, fork
+	RESTORE_GP64
+	jr		t9
+#else
+	RESTORE_GP64
+	j		fork
+#endif
+	END(__vfork)
+
+libc_hidden_def(__vfork)
+weak_alias(__vfork, vfork)
diff --git a/nptl/ChangeLog b/nptl/ChangeLog
index 23b0f51a0f..82895d4785 100644
--- a/nptl/ChangeLog
+++ b/nptl/ChangeLog
@@ -1,3 +1,24 @@
+2005-03-28  Daniel Jacobowitz  <dan@codesourcery.com>
+
+	* sysdeps/mips/Makefile: New file.
+	* sysdeps/mips/nptl-sysdep.S: New file.
+	* sysdeps/mips/tcb-offsets.sym: New file.
+	* sysdeps/mips/pthread_spin_lock.S: New file.
+	* sysdeps/mips/pthread_spin_trylock.S: New file.
+	* sysdeps/mips/pthreaddef.h: New file.
+	* sysdeps/mips/tls.h: New file.
+	* sysdeps/mips/jmpbuf-unwind.h: New file.
+	* sysdeps/unix/sysv/linux/mips/lowlevellock.h: New file.
+	* sysdeps/unix/sysv/linux/mips/bits/pthreadtypes.h: New file.
+	* sysdeps/unix/sysv/linux/mips/bits/semaphore.h: New file.
+	* sysdeps/unix/sysv/linux/mips/pthread_once.c: New file.
+	* sysdeps/unix/sysv/linux/mips/fork.c: New file.
+	* sysdeps/unix/sysv/linux/mips/pt-vfork.S: New file.
+	* sysdeps/unix/sysv/linux/mips/vfork.S: New file.
+	* sysdeps/unix/sysv/linux/mips/clone.S: New file.
+	* sysdeps/unix/sysv/linux/mips/createthread.c: New file.
+	* sysdeps/unix/sysv/linux/mips/sysdep-cancel.h: New file.
+
 2005-03-23  Ulrich Drepper  <drepper@redhat.com>
 
 	* pthread_create.c (__pthread_create_2_1): Rename syscall error
diff --git a/nptl/sysdeps/mips/Makefile b/nptl/sysdeps/mips/Makefile
new file mode 100644
index 0000000000..d0c59a5091
--- /dev/null
+++ b/nptl/sysdeps/mips/Makefile
@@ -0,0 +1,25 @@
+# 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.
+
+ifeq ($(subdir),csu)
+gen-as-const-headers += tcb-offsets.sym
+endif
+
+ifeq ($(subdir),nptl)
+libpthread-sysdep_routines += nptl-sysdep
+endif
diff --git a/nptl/sysdeps/mips/jmpbuf-unwind.h b/nptl/sysdeps/mips/jmpbuf-unwind.h
new file mode 100644
index 0000000000..67cc969663
--- /dev/null
+++ b/nptl/sysdeps/mips/jmpbuf-unwind.h
@@ -0,0 +1,30 @@
+/* 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 <setjmp.h>
+#include <stdint.h>
+#include <unwind.h>
+
+#define _JMPBUF_CFA_UNWINDS_ADJ(_jmpbuf, _context, _adj) \
+  _JMPBUF_UNWINDS_ADJ (_jmpbuf, (void *) _Unwind_GetCFA (_context), _adj)
+
+#define _JMPBUF_UNWINDS_ADJ(_jmpbuf, _address, _adj) \
+  ((uintptr_t) (_address) - (_adj) < (uintptr_t) (_jmpbuf)[0].__sp - (_adj))
+
+/* We use the normal longjmp for unwinding.  */
+#define __libc_unwind_longjmp(buf, val) __libc_longjmp (buf, val)
diff --git a/nptl/sysdeps/mips/nptl-sysdep.S b/nptl/sysdeps/mips/nptl-sysdep.S
new file mode 100644
index 0000000000..3f5c2a364a
--- /dev/null
+++ b/nptl/sysdeps/mips/nptl-sysdep.S
@@ -0,0 +1,2 @@
+/* Pull in __syscall_error.  */
+#include <sysdep.S>
diff --git a/nptl/sysdeps/mips/pthread_spin_lock.S b/nptl/sysdeps/mips/pthread_spin_lock.S
new file mode 100644
index 0000000000..d5f2a72340
--- /dev/null
+++ b/nptl/sysdeps/mips/pthread_spin_lock.S
@@ -0,0 +1,37 @@
+/* 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 <sys/asm.h>
+#include <sysdep.h>
+#include <sgidefs.h>
+
+ENTRY (pthread_spin_lock)
+	.set	push
+#if _MIPS_SIM == _ABIO32
+	.set	mips2
+#endif
+1:	ll	a2, 0(a0)
+	li	a1, 1
+	bnez	a2, 1b
+	sc	a1, 0(a0)
+	beqz	a1, 1b
+	MIPS_SYNC
+	.set	pop
+	li	v0, 0
+	ret
+PSEUDO_END (pthread_spin_lock)
diff --git a/nptl/sysdeps/mips/pthread_spin_trylock.S b/nptl/sysdeps/mips/pthread_spin_trylock.S
new file mode 100644
index 0000000000..9c6e740f0f
--- /dev/null
+++ b/nptl/sysdeps/mips/pthread_spin_trylock.S
@@ -0,0 +1,41 @@
+/* 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 <sys/asm.h>
+#include <sysdep.h>
+#define _ERRNO_H 1
+#include <bits/errno.h>
+#include <sgidefs.h>
+
+ENTRY (pthread_spin_trylock)
+	.set	push
+#if _MIPS_SIM == _ABIO32
+	.set	mips2
+#endif
+	ll	a2, 0(a0)
+	li	a1, 1
+	bnez	a2, 1f
+	sc	a1, 0(a0)
+	beqz	a1, 1f
+	MIPS_SYNC
+	.set	pop
+	li	v0, 0
+	ret
+1:	li	v0, EBUSY
+	ret
+PSEUDO_END (pthread_spin_trylock)
diff --git a/nptl/sysdeps/mips/pthreaddef.h b/nptl/sysdeps/mips/pthreaddef.h
new file mode 100644
index 0000000000..e72b4bc58d
--- /dev/null
+++ b/nptl/sysdeps/mips/pthreaddef.h
@@ -0,0 +1,39 @@
+/* Copyright (C) 2002, 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.  */
+
+/* Default stack size.  */
+#define ARCH_STACK_DEFAULT_SIZE	(2 * 1024 * 1024)
+
+/* Required stack pointer alignment at beginning.  */
+#define STACK_ALIGN		16
+
+/* Minimal stack size after allocating thread descriptor and guard size.  */
+#define MINIMAL_REST_STACK	2048
+
+/* Alignment requirement for TCB.  */
+#define TCB_ALIGNMENT		16
+
+
+/* Location of current stack frame.  */
+#define CURRENT_STACK_FRAME	__builtin_frame_address (0)
+
+
+/* XXX Until we have a better place keep the definitions here.  */
+
+#define __exit_thread_inline(val) \
+  INLINE_SYSCALL (exit, 1, (val))
diff --git a/nptl/sysdeps/mips/tcb-offsets.sym b/nptl/sysdeps/mips/tcb-offsets.sym
new file mode 100644
index 0000000000..e0e71dc430
--- /dev/null
+++ b/nptl/sysdeps/mips/tcb-offsets.sym
@@ -0,0 +1,11 @@
+#include <sysdep.h>
+#include <tls.h>
+
+--
+
+-- Abuse tls.h macros to derive offsets relative to the thread register.
+#define thread_offsetof(mem)	(long)(offsetof(struct pthread, mem) - TLS_TCB_OFFSET - TLS_PRE_TCB_SIZE)
+
+MULTIPLE_THREADS_OFFSET		thread_offsetof (header.multiple_threads)
+PID_OFFSET			thread_offsetof (pid)
+TID_OFFSET			thread_offsetof (tid)
diff --git a/nptl/sysdeps/mips/tls.h b/nptl/sysdeps/mips/tls.h
new file mode 100644
index 0000000000..1cef161010
--- /dev/null
+++ b/nptl/sysdeps/mips/tls.h
@@ -0,0 +1,161 @@
+/* Definition for thread-local data handling.  NPTL/MIPS version.
+   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.  */
+
+#ifndef _TLS_H
+#define _TLS_H	1
+
+#include <dl-sysdep.h>
+
+#ifndef __ASSEMBLER__
+# include <stdbool.h>
+# include <stddef.h>
+# include <stdint.h>
+
+/* Type for the dtv.  */
+typedef union dtv
+{
+  size_t counter;
+  struct
+  {
+    void *val;
+    bool is_static;
+  } pointer;
+} dtv_t;
+
+/* Note: rd must be $v1 to be ABI-conformant.  */
+# define READ_THREAD_POINTER() \
+    ({ void *__result;							      \
+       asm volatile (".set\tpush\n\t.set\tmips32r2\n\t"			      \
+		     "rdhwr\t%0, $29\n\t.set\tpop" : "=v" (__result));	      \
+       __result; })
+
+#else /* __ASSEMBLER__ */
+# include <tcb-offsets.h>
+
+# define READ_THREAD_POINTER(rd) \
+	.set	push;							      \
+	.set	mips32r2;						      \
+	rdhwr	rd, $29;						      \
+	.set	pop
+#endif /* __ASSEMBLER__ */
+
+
+/* We require TLS support in the tools.  */
+#ifndef HAVE_TLS_SUPPORT
+# error "TLS support is required."
+#endif
+
+/* Signal that TLS support is available.  */
+#define USE_TLS	1
+
+#ifndef __ASSEMBLER__
+
+/* Get system call information.  */
+# include <sysdep.h>
+
+/* The TP points to the start of the thread blocks.  */
+# define TLS_DTV_AT_TP	1
+
+/* Get the thread descriptor definition.  */
+# include <nptl/descr.h>
+
+typedef struct
+{
+  dtv_t *dtv;
+  void *private;
+} tcbhead_t;
+
+/* This is the size of the initial TCB.  Because our TCB is before the thread
+   pointer, we don't need this.  */
+# define TLS_INIT_TCB_SIZE	0
+
+/* Alignment requirements for the initial TCB.  */
+# define TLS_INIT_TCB_ALIGN	__alignof__ (struct pthread)
+
+/* This is the size of the TCB.  Because our TCB is before the thread
+   pointer, we don't need this.  */
+# define TLS_TCB_SIZE		0
+
+/* Alignment requirements for the TCB.  */
+# define TLS_TCB_ALIGN		__alignof__ (struct pthread)
+
+/* This is the size we need before TCB - actually, it includes the TCB.  */
+# define TLS_PRE_TCB_SIZE \
+  (sizeof (struct pthread)						      \
+   + ((sizeof (tcbhead_t) + TLS_TCB_ALIGN - 1) & ~(TLS_TCB_ALIGN - 1)))
+
+/* The thread pointer (in hardware register $29) points to the end of
+   the TCB + 0x7000, as for PowerPC.  The pthread_descr structure is
+   immediately in front of the TCB.  */
+# define TLS_TCB_OFFSET	0x7000
+
+/* Install the dtv pointer.  The pointer passed is to the element with
+   index -1 which contain the length.  */
+# define INSTALL_DTV(tcbp, dtvp) \
+  (((tcbhead_t *) (tcbp))[-1].dtv = (dtvp) + 1)
+
+/* Install new dtv for current thread.  */
+# define INSTALL_NEW_DTV(dtv) \
+  (THREAD_DTV() = (dtv))
+
+/* Return dtv of given thread descriptor.  */
+# define GET_DTV(tcbp) \
+  (((tcbhead_t *) (tcbp))[-1].dtv)
+
+/* Code to initially initialize the thread pointer.  This might need
+   special attention since 'errno' is not yet available and if the
+   operation can cause a failure 'errno' must not be touched.  */
+# define TLS_INIT_TP(tcbp, secondcall) \
+  ({ INTERNAL_SYSCALL_DECL (err);					\
+     long result_var;							\
+     result_var = INTERNAL_SYSCALL (set_thread_area, err, 1,		\
+				    (char *) (tcbp) + TLS_TCB_OFFSET);	\
+     INTERNAL_SYSCALL_ERROR_P (result_var, err)				\
+       ? "unknown error" : NULL; })
+
+/* Return the address of the dtv for the current thread.  */
+# define THREAD_DTV() \
+  (((tcbhead_t *) (READ_THREAD_POINTER () - TLS_TCB_OFFSET))[-1].dtv)
+
+/* Return the thread descriptor for the current thread.  */
+# define THREAD_SELF \
+ ((struct pthread *) (READ_THREAD_POINTER ()			     \
+		      - TLS_TCB_OFFSET - TLS_PRE_TCB_SIZE))
+
+/* Magic for libthread_db to know how to do THREAD_SELF.  */
+# define DB_THREAD_SELF \
+  CONST_THREAD_AREA (32, TLS_TCB_OFFSET + TLS_PRE_TCB_SIZE)
+
+/* Access to data in the thread descriptor is easy.  */
+# define THREAD_GETMEM(descr, member) \
+  descr->member
+# define THREAD_GETMEM_NC(descr, member, idx) \
+  descr->member[idx]
+# define THREAD_SETMEM(descr, member, value) \
+  descr->member = (value)
+# define THREAD_SETMEM_NC(descr, member, idx, value) \
+  descr->member[idx] = (value)
+
+/* l_tls_offset == 0 is perfectly valid on MIPS, so we have to use some
+   different value to mean unset l_tls_offset.  */
+# define NO_TLS_OFFSET		-1
+
+#endif /* __ASSEMBLER__ */
+
+#endif	/* tls.h */
diff --git a/nptl/sysdeps/unix/sysv/linux/mips/bits/semaphore.h b/nptl/sysdeps/unix/sysv/linux/mips/bits/semaphore.h
new file mode 100644
index 0000000000..c4440f9e9e
--- /dev/null
+++ b/nptl/sysdeps/unix/sysv/linux/mips/bits/semaphore.h
@@ -0,0 +1,40 @@
+/* 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
+
+#if _MIPS_SIM == _ABI64
+# define __SIZEOF_SEM_T	32
+#else
+# define __SIZEOF_SEM_T	16
+#endif
+
+/* Value returned if `sem_open' failed.  */
+#define SEM_FAILED      ((sem_t *) 0)
+
+/* Maximum value the semaphore can have.  */
+#define SEM_VALUE_MAX   (2147483647)
+
+
+typedef union
+{
+  char __size[__SIZEOF_SEM_T];
+  long int __align;
+} sem_t;
diff --git a/nptl/sysdeps/unix/sysv/linux/mips/clone.S b/nptl/sysdeps/unix/sysv/linux/mips/clone.S
new file mode 100644
index 0000000000..80c265bf6c
--- /dev/null
+++ b/nptl/sysdeps/unix/sysv/linux/mips/clone.S
@@ -0,0 +1,2 @@
+#define RESET_PID
+#include <sysdeps/unix/sysv/linux/mips/clone.S>
diff --git a/nptl/sysdeps/unix/sysv/linux/mips/createthread.c b/nptl/sysdeps/unix/sysv/linux/mips/createthread.c
new file mode 100644
index 0000000000..5b2234f470
--- /dev/null
+++ b/nptl/sysdeps/unix/sysv/linux/mips/createthread.c
@@ -0,0 +1,24 @@
+/* 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 ((void *) (pd) \
+		   + TLS_TCB_OFFSET + TLS_PRE_TCB_SIZE)
+
+/* Get the real implementation.	 */
+#include <nptl/sysdeps/pthread/createthread.c>
diff --git a/nptl/sysdeps/unix/sysv/linux/mips/fork.c b/nptl/sysdeps/unix/sysv/linux/mips/fork.c
new file mode 100644
index 0000000000..06b7e1c69f
--- /dev/null
+++ b/nptl/sysdeps/unix/sysv/linux/mips/fork.c
@@ -0,0 +1 @@
+#include "../i386/fork.c"
diff --git a/nptl/sysdeps/unix/sysv/linux/mips/lowlevellock.h b/nptl/sysdeps/unix/sysv/linux/mips/lowlevellock.h
new file mode 100644
index 0000000000..7edb28794f
--- /dev/null
+++ b/nptl/sysdeps/unix/sysv/linux/mips/lowlevellock.h
@@ -0,0 +1,216 @@
+/* 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.  */
+
+#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
+
+/* 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);		      \
+    INTERNAL_SYSCALL_ERROR_P (__ret, __err) ? -__ret : __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));	      \
+    INTERNAL_SYSCALL_ERROR_P (__ret, __err) ? -__ret : __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);		      \
+    INTERNAL_SYSCALL_ERROR_P (__ret, __err) ? -__ret : __ret;		      \
+  })
+
+/* 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));		      \
+    INTERNAL_SYSCALL_ERROR_P (__ret, __err);				      \
+  })
+
+
+static inline int __attribute__((always_inline))
+__lll_mutex_trylock(int *futex)
+{
+  return atomic_compare_and_exchange_val_acq (futex, 1, 0) != 0;
+}
+#define lll_mutex_trylock(lock)	__lll_mutex_trylock (&(lock))
+
+
+static inline int __attribute__((always_inline))
+__lll_mutex_cond_trylock(int *futex)
+{
+  return atomic_compare_and_exchange_val_acq (futex, 2, 0) != 0;
+}
+#define lll_mutex_cond_trylock(lock)	__lll_mutex_cond_trylock (&(lock))
+
+
+extern void __lll_lock_wait (int *futex) attribute_hidden;
+
+static inline void __attribute__((always_inline))
+__lll_mutex_lock(int *futex)
+{
+  if (atomic_compare_and_exchange_bool_acq (futex, 1, 0) != 0)
+    __lll_lock_wait (futex);
+}
+#define lll_mutex_lock(futex) __lll_mutex_lock (&(futex))
+
+
+static inline void __attribute__ ((always_inline))
+__lll_mutex_cond_lock (int *futex)
+{
+  if (atomic_compare_and_exchange_bool_acq (futex, 2, 0) != 0)
+    __lll_lock_wait (futex);
+}
+#define lll_mutex_cond_lock(futex) __lll_mutex_cond_lock (&(futex))
+
+
+extern int __lll_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;
+  if (atomic_compare_and_exchange_bool_acq (futex, 1, 0) != 0)
+    result = __lll_timedlock_wait (futex, abstime);
+  return result;
+}
+#define lll_mutex_timedlock(futex, abstime) \
+  __lll_mutex_timedlock (&(futex), abstime)
+
+
+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_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/nptl/sysdeps/unix/sysv/linux/mips/pt-vfork.S b/nptl/sysdeps/unix/sysv/linux/mips/pt-vfork.S
new file mode 100644
index 0000000000..fe2b81bc1f
--- /dev/null
+++ b/nptl/sysdeps/unix/sysv/linux/mips/pt-vfork.S
@@ -0,0 +1,37 @@
+/* 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 <tls.h>
+
+/* Save the PID value.  */
+#define SAVE_PID \
+	READ_THREAD_POINTER(v1);	/* Get the thread pointer.  */	\
+	lw	a2, PID_OFFSET(v1);	/* Load the saved PID.  */	\
+	subu	a2, $0, a2;		/* Negate it.  */		\
+	sw	a2, PID_OFFSET(v1);	/* Store the temporary PID.  */
+
+/* Restore the old PID value in the parent.  */
+#define RESTORE_PID \
+	beqz	v0, 1f;			/* If we are the parent... */	\
+	READ_THREAD_POINTER(v1);	/* Get the thread pointer.  */	\
+	lw	a2, PID_OFFSET(v1);	/* Load the saved PID.  */	\
+	subu	a2, $0, a2;		/* Re-negate it.  */		\
+	sw	a2, PID_OFFSET(v1);	/* Restore the PID.  */		\
+1:
+
+#include <../sysdeps/unix/sysv/linux/mips/vfork.S>
diff --git a/nptl/sysdeps/unix/sysv/linux/mips/pthread_once.c b/nptl/sysdeps/unix/sysv/linux/mips/pthread_once.c
new file mode 100644
index 0000000000..649b752f54
--- /dev/null
+++ b/nptl/sysdeps/unix/sysv/linux/mips/pthread_once.c
@@ -0,0 +1,94 @@
+/* Copyright (C) 2003, 2004, 2005 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+   Contributed by Jakub Jelinek <jakub@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 "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 (once_control, init_routine)
+     pthread_once_t *once_control;
+     void (*init_routine) (void);
+{
+  while (1)
+    {
+      int oldval, val, newval;
+
+      val = *once_control;
+      do
+	{
+	  /* Check if the initialized has already been done.  */
+	  if ((val & 2) != 0)
+	    return 0;
+
+	  oldval = val;
+	  newval = (oldval & 3) | __fork_generation | 1;
+	  val = atomic_compare_and_exchange_val_acq (once_control, newval,
+						     oldval);
+	}
+      while (__builtin_expect (val != oldval, 0));
+
+      /* Check if another thread already runs the initializer.	*/
+      if ((oldval & 1) != 0)
+	{
+	  /* Check whether the initializer execution was interrupted
+	     by a fork.	 */
+	  if (((oldval ^ newval) & -4) == 0)
+	    {
+	      /* Same generation, some other thread was faster. Wait.  */
+	      lll_futex_wait (once_control, newval);
+	      continue;
+	    }
+	}
+
+      /* 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);
+
+
+      /* Add one to *once_control.  */
+      atomic_increment (once_control);
+
+      /* Wake up all other threads.  */
+      lll_futex_wake (once_control, INT_MAX);
+      break;
+    }
+
+  return 0;
+}
+weak_alias (__pthread_once, pthread_once)
+strong_alias (__pthread_once, __pthread_once_internal)
diff --git a/nptl/sysdeps/unix/sysv/linux/mips/sysdep-cancel.h b/nptl/sysdeps/unix/sysv/linux/mips/sysdep-cancel.h
new file mode 100644
index 0000000000..02508e2155
--- /dev/null
+++ b/nptl/sysdeps/unix/sysv/linux/mips/sysdep-cancel.h
@@ -0,0 +1,170 @@
+/* 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 <sysdeps/generic/sysdep.h>
+#include <tls.h>
+#ifndef __ASSEMBLER__
+# include <nptl/pthreadP.h>
+#endif
+
+#if !defined NOT_IN_libc || defined IS_IN_libpthread || defined IS_IN_librt
+
+#ifdef __PIC__
+# undef PSEUDO
+# define PSEUDO(name, syscall_name, args)				      \
+      .align 2;								      \
+  L(pseudo_start):							      \
+      cfi_startproc;							      \
+  99: la t9,__syscall_error;						      \
+      jr t9;								      \
+  .type __##syscall_name##_nocancel, @function;				      \
+  .globl __##syscall_name##_nocancel;					      \
+  __##syscall_name##_nocancel:						      \
+    .set noreorder;							      \
+    .cpload t9;								      \
+    li v0, SYS_ify(syscall_name);					      \
+    syscall;								      \
+    .set reorder;							      \
+    bne a3, zero, SYSCALL_ERROR_LABEL;			       		      \
+    ret;								      \
+  .size __##syscall_name##_nocancel,.-__##syscall_name##_nocancel;	      \
+  ENTRY (name)								      \
+    .set noreorder;							      \
+    .cpload t9;								      \
+    .set reorder;							      \
+    SINGLE_THREAD_P(v1);						      \
+    bne zero, v1, L(pseudo_cancel);					      \
+    .set noreorder;							      \
+    li v0, SYS_ify(syscall_name);					      \
+    syscall;								      \
+    .set reorder;							      \
+    bne a3, zero, SYSCALL_ERROR_LABEL;			       		      \
+    ret;								      \
+  L(pseudo_cancel):							      \
+    SAVESTK_##args;						              \
+    sw ra, 28(sp);							      \
+    cfi_rel_offset (ra, 28);						      \
+    sw gp, 32(sp);							      \
+    cfi_rel_offset (gp, 32);						      \
+    PUSHARGS_##args;			/* save syscall args */	      	      \
+    CENABLE;								      \
+    lw gp, 32(sp);							      \
+    sw v0, 44(sp);			/* save mask */			      \
+    POPARGS_##args;			/* restore syscall args */	      \
+    .set noreorder;							      \
+    li v0, SYS_ify (syscall_name);				      	      \
+    syscall;								      \
+    .set reorder;							      \
+    sw v0, 36(sp);			/* save syscall result */             \
+    sw a3, 40(sp);			/* save syscall error flag */	      \
+    lw a0, 44(sp);			/* pass mask as arg1 */		      \
+    CDISABLE;								      \
+    lw gp, 32(sp);							      \
+    lw v0, 36(sp);			/* restore syscall result */          \
+    lw a3, 40(sp);			/* restore syscall error flag */      \
+    lw ra, 28(sp);			/* restore return address */	      \
+    .set noreorder;							      \
+    bne a3, zero, SYSCALL_ERROR_LABEL;					      \
+     RESTORESTK;						              \
+  L(pseudo_end):							      \
+    .set reorder;
+
+# undef PSEUDO_END
+# define PSEUDO_END(sym) cfi_endproc; .end sym; .size sym,.-sym
+
+#endif
+
+# define PUSHARGS_0	/* nothing to do */
+# define PUSHARGS_1	PUSHARGS_0 sw a0, 0(sp); cfi_rel_offset (a0, 0);
+# define PUSHARGS_2	PUSHARGS_1 sw a1, 4(sp); cfi_rel_offset (a1, 4);
+# define PUSHARGS_3	PUSHARGS_2 sw a2, 8(sp); cfi_rel_offset (a2, 8);
+# define PUSHARGS_4	PUSHARGS_3 sw a3, 12(sp); cfi_rel_offset (a3, 12);
+# define PUSHARGS_5	PUSHARGS_4 /* handled by SAVESTK_## */
+# define PUSHARGS_6	PUSHARGS_5
+# define PUSHARGS_7	PUSHARGS_6
+
+# define POPARGS_0	/* nothing to do */
+# define POPARGS_1	POPARGS_0 lw a0, 0(sp);
+# define POPARGS_2	POPARGS_1 lw a1, 4(sp);
+# define POPARGS_3	POPARGS_2 lw a2, 8(sp);
+# define POPARGS_4	POPARGS_3 lw a3, 12(sp);
+# define POPARGS_5	POPARGS_4 /* args already in new stackframe */
+# define POPARGS_6	POPARGS_5
+# define POPARGS_7	POPARGS_6
+
+
+# define STKSPACE	48
+# define SAVESTK_0 	subu sp, STKSPACE; cfi_adjust_cfa_offset(STKSPACE)
+# define SAVESTK_1      SAVESTK_0
+# define SAVESTK_2      SAVESTK_1
+# define SAVESTK_3      SAVESTK_2
+# define SAVESTK_4      SAVESTK_3
+# define SAVESTK_5      lw t0, 16(sp);		\
+			SAVESTK_0;		\
+			sw t0, 16(sp)
+
+# define SAVESTK_6      lw t0, 16(sp);		\
+			lw t1, 20(sp);		\
+			SAVESTK_0;		\
+			sw t0, 16(sp);		\
+			sw t1, 20(sp)
+
+# define SAVESTK_7      lw t0, 16(sp);		\
+			lw t1, 20(sp);		\
+			lw t2, 24(sp);		\
+			SAVESTK_0;		\
+			sw t0, 16(sp);		\
+			sw t1, 20(sp);		\
+			sw t2, 24(sp)
+
+# define RESTORESTK 	addu sp, STKSPACE; cfi_adjust_cfa_offset(-STKSPACE)
+
+
+/* We use jalr rather than jal.  This means that the assembler will not
+   automatically restore $gp (in case libc has multiple GOTs) so we must
+   do it manually - which we have to do anyway since we don't use .cprestore.
+   It also shuts up the assembler warning about not using .cprestore.  */
+# ifdef IS_IN_libpthread
+#  define CENABLE	la t9, __pthread_enable_asynccancel; jalr t9;
+#  define CDISABLE	la t9, __pthread_disable_asynccancel; jalr t9;
+# elif defined IS_IN_librt
+#  define CENABLE	la t9, __librt_enable_asynccancel; jalr t9;
+#  define CDISABLE	la t9, __librt_disable_asynccancel; jalr t9;
+# else
+#  define CENABLE	la t9, __libc_enable_asynccancel; jalr t9;
+#  define CDISABLE	la t9, __libc_disable_asynccancel; jalr t9;
+# endif
+
+# ifndef __ASSEMBLER__
+#  define SINGLE_THREAD_P						\
+	__builtin_expect (THREAD_GETMEM (THREAD_SELF,			\
+					 header.multiple_threads)	\
+			  == 0, 1)
+# else
+#  define SINGLE_THREAD_P(reg)						\
+	READ_THREAD_POINTER(reg);					\
+	lw reg, MULTIPLE_THREADS_OFFSET(reg)
+#endif
+
+#elif !defined __ASSEMBLER__
+
+# define SINGLE_THREAD_P 1
+# define NO_CANCELLATION 1
+
+#endif
diff --git a/nptl/sysdeps/unix/sysv/linux/mips/vfork.S b/nptl/sysdeps/unix/sysv/linux/mips/vfork.S
new file mode 100644
index 0000000000..874a2e2bf4
--- /dev/null
+++ b/nptl/sysdeps/unix/sysv/linux/mips/vfork.S
@@ -0,0 +1,42 @@
+/* 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 <tls.h>
+
+/* Save the PID value.  */
+#define SAVE_PID \
+	READ_THREAD_POINTER(v1);	/* Get the thread pointer.  */	\
+	lw	a2, PID_OFFSET(v1);	/* Load the saved PID.  */	\
+	subu	a2, $0, a2;		/* Negate it.  */		\
+	bnez	a2, 1f;			/* If it was zero... */		\
+	lui	a2, 0x8000;		/* use 0x80000000 instead.  */	\
+1:	sw	a2, PID_OFFSET(v1);	/* Store the temporary PID.  */
+
+/* Restore the old PID value in the parent.  */
+#define RESTORE_PID \
+	beqz	v0, 1f;			/* If we are the parent... */	\
+	READ_THREAD_POINTER(v1);	/* Get the thread pointer.  */	\
+	lw	a2, PID_OFFSET(v1);	/* Load the saved PID.  */	\
+	subu	a2, $0, a2;		/* Re-negate it.  */		\
+	lui	a0, 0x8000;		/* Load 0x80000000... */	\
+	bne	a2, a0, 2f;		/* ... compare against it... */	\
+	li	a2, 0;			/* ... use 0 instead.  */	\
+2:	sw	a2, PID_OFFSET(v1);	/* Restore the PID.  */		\
+1:
+
+#include <../sysdeps/unix/sysv/linux/mips/vfork.S>
diff --git a/sysdeps/mips/atomicity.h b/sysdeps/mips/atomicity.h
deleted file mode 100644
index 7380e1000c..0000000000
--- a/sysdeps/mips/atomicity.h
+++ /dev/null
@@ -1,113 +0,0 @@
-/* Low-level functions for atomic operations. Mips version.
-   Copyright (C) 2001, 2002, 2003, 2004 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 _MIPS_ATOMICITY_H
-#define _MIPS_ATOMICITY_H    1
-
-#include <inttypes.h>
-#include <sgidefs.h>
-
-static inline int
-__attribute__ ((unused))
-exchange_and_add (volatile uint32_t *mem, int val)
-{
-  int result, tmp;
-
-  __asm__ __volatile__
-    ("/* Inline exchange & add */\n"
-     "1:\n\t"
-     ".set	push\n\t"
-#if _MIPS_SIM == _ABIO32
-     ".set	mips2\n\t"
-#endif
-     "ll	%0,%3\n\t"
-     "addu	%1,%4,%0\n\t"
-     "sc	%1,%2\n\t"
-     ".set	pop\n\t"
-     "beqz	%1,1b\n\t"
-     "/* End exchange & add */"
-     : "=&r"(result), "=&r"(tmp), "=m"(*mem)
-     : "m" (*mem), "r"(val)
-     : "memory");
-
-  return result;
-}
-
-static inline void
-__attribute__ ((unused))
-atomic_add (volatile uint32_t *mem, int val)
-{
-  int result;
-
-  __asm__ __volatile__
-    ("/* Inline atomic add */\n"
-     "1:\n\t"
-     ".set	push\n\t"
-#if _MIPS_SIM == _ABIO32
-     ".set	mips2\n\t"
-#endif
-     "ll	%0,%2\n\t"
-     "addu	%0,%3,%0\n\t"
-     "sc	%0,%1\n\t"
-     ".set	pop\n\t"
-     "beqz	%0,1b\n\t"
-     "/* End atomic add */"
-     : "=&r"(result), "=m"(*mem)
-     : "m" (*mem), "r"(val)
-     : "memory");
-}
-
-static inline int
-__attribute__ ((unused))
-compare_and_swap (volatile long int *p, long int oldval, long int newval)
-{
-  long int ret, temp;
-
-  __asm__ __volatile__
-    ("/* Inline compare & swap */\n"
-     "1:\n\t"
-     ".set	push\n\t"
-#if _MIPS_SIM == _ABIO32
-     ".set	mips2\n\t"
-#endif
-#if _MIPS_SIM == _ABI64
-     "lld	%1,%5\n\t"
-#else
-     "ll	%1,%5\n\t"
-#endif
-     "move	%0,$0\n\t"
-     "bne	%1,%3,2f\n\t"
-     "move	%0,%4\n\t"
-#if _MIPS_SIM == _ABI64
-     "scd	%0,%2\n\t"
-#else
-     "sc	%0,%2\n\t"
-#endif
-     ".set	pop\n\t"
-     "beqz	%0,1b\n"
-     "2:\n\t"
-     "/* End compare & swap */"
-     : "=&r" (ret), "=&r" (temp), "=m" (*p)
-     : "r" (oldval), "r" (newval), "m" (*p)
-     : "memory");
-
-  return ret;
-}
-
-#endif /* atomicity.h */
diff --git a/sysdeps/mips/bits/atomic.h b/sysdeps/mips/bits/atomic.h
new file mode 100644
index 0000000000..167d9a59c8
--- /dev/null
+++ b/sysdeps/mips/bits/atomic.h
@@ -0,0 +1,303 @@
+/* Low-level functions for atomic operations. Mips version.
+   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.  */
+
+#ifndef _MIPS_BITS_ATOMIC_H
+#define _MIPS_BITS_ATOMIC_H 1
+
+#include <inttypes.h>
+#include <sgidefs.h>
+
+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 int64_t atomic64_t;
+typedef uint64_t uatomic64_t;
+typedef int_fast64_t atomic_fast64_t;
+typedef uint_fast64_t uatomic_fast64_t;
+
+typedef intptr_t atomicptr_t;
+typedef uintptr_t uatomicptr_t;
+typedef intmax_t atomic_max_t;
+typedef uintmax_t uatomic_max_t;
+
+#if _MIPS_SIM == _ABIO32
+#define MIPS_PUSH_MIPS2 ".set	mips2\n\t"
+#else
+#define MIPS_PUSH_MIPS2
+#endif
+
+/* See the comments in <sys/asm.h> about the use of the sync instruction.  */
+#ifndef MIPS_SYNC
+# define MIPS_SYNC	sync
+#endif
+
+#define MIPS_SYNC_STR_2(X) #X
+#define MIPS_SYNC_STR_1(X) MIPS_SYNC_STR_2(X)
+#define MIPS_SYNC_STR MIPS_SYNC_STR_1(MIPS_SYNC)
+
+/* Compare and exchange.  For all of the "xxx" routines, we expect a
+   "__prev" and a "__cmp" variable to be provided by the enclosing scope,
+   in which values are returned.  */
+
+#define __arch_compare_and_exchange_xxx_8_int(mem, newval, oldval, rel, acq) \
+  (abort (), __prev = __cmp = 0)
+
+#define __arch_compare_and_exchange_xxx_16_int(mem, newval, oldval, rel, acq) \
+  (abort (), __prev = __cmp = 0)
+
+#define __arch_compare_and_exchange_xxx_32_int(mem, newval, oldval, rel, acq) \
+     __asm__ __volatile__ (						      \
+     ".set	push\n\t"						      \
+     MIPS_PUSH_MIPS2							      \
+     rel	"\n"							      \
+     "1:\t"								      \
+     "ll	%0,%4\n\t"						      \
+     "move	%1,$0\n\t"						      \
+     "bne	%0,%2,2f\n\t"						      \
+     "move	%1,%3\n\t"						      \
+     "sc	%1,%4\n\t"						      \
+     "beqz	%1,1b\n"						      \
+     acq	"\n\t"							      \
+     ".set	pop\n"							      \
+     "2:\n\t"								      \
+	      : "=&r" (__prev), "=&r" (__cmp)				      \
+	      : "r" (oldval), "r" (newval), "m" (*mem)			      \
+	      : "memory")
+
+#if _MIPS_SIM == _ABIO32
+/* We can't do an atomic 64-bit operation in O32.  */
+#define __arch_compare_and_exchange_xxx_64_int(mem, newval, oldval, rel, acq) \
+  (abort (), __prev = __cmp = 0)
+#else
+#define __arch_compare_and_exchange_xxx_64_int(mem, newval, oldval, rel, acq) \
+     __asm__ __volatile__ ("\n"						      \
+     ".set	push\n\t"						      \
+     MIPS_PUSH_MIPS2							      \
+     rel	"\n"							      \
+     "1:\t"								      \
+     "lld	%0,%4\n\t"						      \
+     "move	%1,$0\n\t"						      \
+     "bne	%0,%2,2f\n\t"						      \
+     "move	%1,%3\n\t"						      \
+     "scd	%1,%4\n\t"						      \
+     "beqz	%1,1b\n"						      \
+     acq	"\n\t"							      \
+     ".set	pop\n"							      \
+     "2:\n\t"								      \
+	      : "=&r" (__prev), "=&r" (__cmp)				      \
+	      : "r" (oldval), "r" (newval), "m" (*mem)			      \
+	      : "memory")
+#endif
+
+/* For all "bool" routines, we return FALSE if exchange succesful.  */
+
+#define __arch_compare_and_exchange_bool_8_int(mem, new, old, rel, acq)	\
+({ typeof (*mem) __prev; int __cmp;					\
+   __arch_compare_and_exchange_xxx_8_int(mem, new, old, rel, acq);	\
+   !__cmp; })
+
+#define __arch_compare_and_exchange_bool_16_int(mem, new, old, rel, acq) \
+({ typeof (*mem) __prev; int __cmp;					\
+   __arch_compare_and_exchange_xxx_16_int(mem, new, old, rel, acq);	\
+   !__cmp; })
+
+#define __arch_compare_and_exchange_bool_32_int(mem, new, old, rel, acq) \
+({ typeof (*mem) __prev; int __cmp;					\
+   __arch_compare_and_exchange_xxx_32_int(mem, new, old, rel, acq);	\
+   !__cmp; })
+
+#define __arch_compare_and_exchange_bool_64_int(mem, new, old, rel, acq) \
+({ typeof (*mem) __prev; int __cmp;					\
+   __arch_compare_and_exchange_xxx_64_int(mem, new, old, rel, acq);	\
+   !__cmp; })
+
+/* For all "val" routines, return the old value whether exchange
+   successful or not.  */
+
+#define __arch_compare_and_exchange_val_8_int(mem, new, old, rel, acq)	\
+({ typeof (*mem) __prev; int __cmp;					\
+   __arch_compare_and_exchange_xxx_8_int(mem, new, old, rel, acq);	\
+   (typeof (*mem))__prev; })
+
+#define __arch_compare_and_exchange_val_16_int(mem, new, old, rel, acq) \
+({ typeof (*mem) __prev; int __cmp;					\
+   __arch_compare_and_exchange_xxx_16_int(mem, new, old, rel, acq);	\
+   (typeof (*mem))__prev; })
+
+#define __arch_compare_and_exchange_val_32_int(mem, new, old, rel, acq) \
+({ typeof (*mem) __prev; int __cmp;					\
+   __arch_compare_and_exchange_xxx_32_int(mem, new, old, rel, acq);	\
+   (typeof (*mem))__prev; })
+
+#define __arch_compare_and_exchange_val_64_int(mem, new, old, rel, acq) \
+({ typeof (*mem) __prev; int __cmp;					\
+   __arch_compare_and_exchange_xxx_64_int(mem, new, old, rel, acq);	\
+   (typeof (*mem))__prev; })
+
+/* Compare and exchange with "acquire" semantics, ie barrier after.  */
+
+#define atomic_compare_and_exchange_bool_acq(mem, new, old)	\
+  __atomic_bool_bysize (__arch_compare_and_exchange_bool, int,	\
+		        mem, new, old, "", MIPS_SYNC_STR)
+
+#define atomic_compare_and_exchange_val_acq(mem, new, old)	\
+  __atomic_val_bysize (__arch_compare_and_exchange_val, int,	\
+		       mem, new, old, "", MIPS_SYNC_STR)
+
+/* Compare and exchange with "release" semantics, ie barrier before.  */
+
+#define atomic_compare_and_exchange_bool_rel(mem, new, old)	\
+  __atomic_bool_bysize (__arch_compare_and_exchange_bool, int,	\
+		        mem, new, old, MIPS_SYNC_STR, "")
+
+#define atomic_compare_and_exchange_val_rel(mem, new, old)	\
+  __atomic_val_bysize (__arch_compare_and_exchange_val, int,	\
+		       mem, new, old, MIPS_SYNC_STR, "")
+
+
+
+/* Atomic exchange (without compare).  */
+
+#define __arch_exchange_xxx_8_int(mem, newval, rel, acq) \
+  (abort (), 0)
+
+#define __arch_exchange_xxx_16_int(mem, newval, rel, acq) \
+  (abort (), 0)
+
+#define __arch_exchange_xxx_32_int(mem, newval, rel, acq) \
+({ typeof (*mem) __prev; int __cmp;					      \
+     __asm__ __volatile__ ("\n"						      \
+     ".set	push\n\t"						      \
+     MIPS_PUSH_MIPS2							      \
+     rel	"\n"							      \
+     "1:\t"								      \
+     "ll	%0,%3\n\t"						      \
+     "move	%1,%2\n\t"						      \
+     "sc	%1,%3\n\t"						      \
+     "beqz	%1,1b\n"						      \
+     acq	"\n\t"							      \
+     ".set	pop\n"							      \
+     "2:\n\t"								      \
+	      : "=&r" (__prev), "=&r" (__cmp)				      \
+	      : "r" (newval), "m" (*mem)				      \
+	      : "memory");						      \
+  __prev; })
+
+#if _MIPS_SIM == _ABIO32
+/* We can't do an atomic 64-bit operation in O32.  */
+#define __arch_exchange_xxx_64_int(mem, newval, rel, acq) \
+  (abort (), 0)
+#else
+#define __arch_exchange_xxx_64_int(mem, newval, rel, acq) \
+({ typeof (*mem) __prev; int __cmp;					      \
+     __asm__ __volatile__ ("\n"						      \
+     ".set	push\n\t"						      \
+     MIPS_PUSH_MIPS2							      \
+     rel	"\n"							      \
+     "1:\n"								      \
+     "lld	%0,%3\n\t"						      \
+     "move	%1,%2\n\t"						      \
+     "scd	%1,%3\n\t"						      \
+     "beqz	%1,1b\n"						      \
+     acq	"\n\t"							      \
+     ".set	pop\n"							      \
+     "2:\n\t"								      \
+	      : "=&r" (__prev), "=&r" (__cmp)				      \
+	      : "r" (newval), "m" (*mem)				      \
+	      : "memory");						      \
+  __prev; })
+#endif
+
+#define atomic_exchange_acq(mem, value) \
+  __atomic_val_bysize (__arch_exchange_xxx, int, mem, value, "", MIPS_SYNC_STR)
+
+#define atomic_exchange_rel(mem, value) \
+  __atomic_val_bysize (__arch_exchange_xxx, int, mem, value, MIPS_SYNC_STR, "")
+
+
+/* Atomically add value and return the previous (unincremented) value.  */
+
+#define __arch_exchange_and_add_8_int(mem, newval, rel, acq) \
+  (abort (), (typeof(*mem)) 0)
+
+#define __arch_exchange_and_add_16_int(mem, newval, rel, acq) \
+  (abort (), (typeof(*mem)) 0)
+
+#define __arch_exchange_and_add_32_int(mem, value, rel, acq) \
+({ typeof (*mem) __prev; int __cmp;					      \
+     __asm__ __volatile__ ("\n"						      \
+     ".set	push\n\t"						      \
+     MIPS_PUSH_MIPS2							      \
+     rel	"\n"							      \
+     "1:\t"								      \
+     "ll	%0,%3\n\t"						      \
+     "addu	%1,%0,%2\n\t"						      \
+     "sc	%1,%3\n\t"						      \
+     "beqz	%1,1b\n"						      \
+     acq	"\n\t"							      \
+     ".set	pop\n"							      \
+     "2:\n\t"								      \
+	      : "=&r" (__prev), "=&r" (__cmp)				      \
+	      : "r" (value), "m" (*mem)					      \
+	      : "memory");						      \
+  __prev; })
+
+#if _MIPS_SIM == _ABIO32
+/* We can't do an atomic 64-bit operation in O32.  */
+#define __arch_exchange_and_add_64_int(mem, value, rel, acq) \
+  (abort (), (typeof(*mem)) 0)
+#else
+#define __arch_exchange_and_add_64_int(mem, value, rel, acq) \
+({ typeof (*mem) __prev; int __cmp;					      \
+     __asm__ __volatile__ (						      \
+     ".set	push\n\t"						      \
+     MIPS_PUSH_MIPS2							      \
+     rel	"\n"							      \
+     "1:\t"								      \
+     "lld	%0,%3\n\t"						      \
+     "daddu	%1,%0,%2\n\t"						      \
+     "scd	%1,%3\n\t"						      \
+     "beqz	%1,1b\n"						      \
+     acq	"\n\t"							      \
+     ".set	pop\n"							      \
+     "2:\n\t"								      \
+	      : "=&r" (__prev), "=&r" (__cmp)				      \
+	      : "r" (value), "m" (*mem)					      \
+	      : "memory");						      \
+  __prev; })
+#endif
+
+/* ??? Barrier semantics for atomic_exchange_and_add appear to be 
+   undefined.  Use full barrier for now, as that's safe.  */
+#define atomic_exchange_and_add(mem, value) \
+  __atomic_val_bysize (__arch_exchange_and_add, int, mem, value,	      \
+		       MIPS_SYNC_STR, MIPS_SYNC_STR)
+
+/* TODO: More atomic operations could be implemented efficiently; only the
+   basic requirements are done.  */
+
+#define atomic_full_barrier() \
+  __asm__ __volatile__ (".set push\n\t"					      \
+			MIPS_PUSH_MIPS2					      \
+			MIPS_SYNC_STR "\n\t"				      \
+			".set pop" : : : "memory")
+
+#endif /* bits/atomic.h */
diff --git a/sysdeps/mips/dl-tls.h b/sysdeps/mips/dl-tls.h
new file mode 100644
index 0000000000..6d3ed6f5a4
--- /dev/null
+++ b/sysdeps/mips/dl-tls.h
@@ -0,0 +1,46 @@
+/* Thread-local storage handling in the ELF dynamic linker.  MIPS version.
+   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.  */
+
+
+/* Type used for the representation of TLS information in the GOT.  */
+typedef struct
+{
+  unsigned long int ti_module;
+  unsigned long int ti_offset;
+} tls_index;
+
+/* The thread pointer points 0x7000 past the first static TLS block.  */
+#define TLS_TP_OFFSET		0x7000
+
+/* Dynamic thread vector pointers point 0x8000 past the start of each
+   TLS block.  */
+#define TLS_DTV_OFFSET		0x8000
+
+/* Compute the value for a GOTTPREL reloc.  */
+#define TLS_TPREL_VALUE(sym_map, sym) \
+  ((sym_map)->l_tls_offset + (sym)->st_value - TLS_TP_OFFSET)
+
+/* Compute the value for a DTPREL reloc.  */
+#define TLS_DTPREL_VALUE(sym) \
+  ((sym)->st_value - TLS_DTV_OFFSET)
+
+extern void *__tls_get_addr (tls_index *ti);
+
+# define GET_ADDR_OFFSET	(ti->ti_offset + TLS_DTV_OFFSET)
+# define __TLS_GET_ADDR(__ti)	(__tls_get_addr (__ti) - TLS_DTV_OFFSET)
diff --git a/sysdeps/mips/dl-trampoline.c b/sysdeps/mips/dl-trampoline.c
index c5ea59cdb2..459adf9a8e 100644
--- a/sysdeps/mips/dl-trampoline.c
+++ b/sysdeps/mips/dl-trampoline.c
@@ -153,7 +153,7 @@ __dl_runtime_resolve (ElfW(Word) sym_index,
 
 	    if (version->hash != 0)
 	      {
-		sym_map = _dl_lookup_symbol_x (strtab + sym->st_name, l, 
+		sym_map = _dl_lookup_symbol_x (strtab + sym->st_name, l,
 					       &sym, l->l_scope, version,
 					       ELF_RTYPE_CLASS_PLT, 0, 0);
 		break;
diff --git a/sysdeps/mips/elf/configure b/sysdeps/mips/elf/configure
new file mode 100644
index 0000000000..3d90a1e420
--- /dev/null
+++ b/sysdeps/mips/elf/configure
@@ -0,0 +1,46 @@
+# This file is generated from configure.in by Autoconf.  DO NOT EDIT!
+ # Local configure fragment for sysdeps/mips/elf.
+
+if test "$usetls" != no; then
+# Check for support of thread-local storage handling in assembler and
+# linker.
+echo "$as_me:$LINENO: checking for MIPS TLS support" >&5
+echo $ECHO_N "checking for MIPS TLS support... $ECHO_C" >&6
+if test "${libc_cv_mips_tls+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  cat > conftest.s <<\EOF
+	.section ".tdata", "awT", %progbits
+	.globl foo
+foo:	.long	1
+	.section ".tbss", "awT", %nobits
+	.globl bar
+bar:	.skip	4
+	.text
+
+	lw	$25, %call16(__tls_get_addr)($28)
+	jalr	$25
+	addiu	$4, $28, %tlsgd(x)
+EOF
+if { ac_try='${CC-cc} -c $CFLAGS conftest.s 1>&5'
+  { (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_mips_tls=yes
+else
+  libc_cv_mips_tls=no
+fi
+rm -f conftest*
+fi
+echo "$as_me:$LINENO: result: $libc_cv_mips_tls" >&5
+echo "${ECHO_T}$libc_cv_mips_tls" >&6
+if test $libc_cv_mips_tls = yes; then
+  cat >>confdefs.h <<\_ACEOF
+#define HAVE_TLS_SUPPORT 1
+_ACEOF
+
+fi
+fi
+
diff --git a/sysdeps/mips/elf/configure.in b/sysdeps/mips/elf/configure.in
new file mode 100644
index 0000000000..ecb9108c12
--- /dev/null
+++ b/sysdeps/mips/elf/configure.in
@@ -0,0 +1,35 @@
+GLIBC_PROVIDES dnl See aclocal.m4 in the top level source directory.
+# Local configure fragment for sysdeps/mips/elf.
+
+if test "$usetls" != no; then
+# Check for support of thread-local storage handling in assembler and
+# linker.
+AC_CACHE_CHECK(for MIPS TLS support, libc_cv_mips_tls, [dnl
+cat > conftest.s <<\EOF
+	.section ".tdata", "awT", %progbits
+	.globl foo
+foo:	.long	1
+	.section ".tbss", "awT", %nobits
+	.globl bar
+bar:	.skip	4
+	.text
+
+	lw	$25, %call16(__tls_get_addr)($28)
+	jalr	$25
+	addiu	$4, $28, %tlsgd(x) 
+EOF
+dnl
+if AC_TRY_COMMAND(${CC-cc} -c $CFLAGS conftest.s 1>&AS_MESSAGE_LOG_FD); then
+  libc_cv_mips_tls=yes
+else
+  libc_cv_mips_tls=no
+fi
+rm -f conftest*])
+if test $libc_cv_mips_tls = yes; then
+  AC_DEFINE(HAVE_TLS_SUPPORT)
+fi
+fi
+
+dnl No MIPS GCC supports accessing static and hidden symbols in an
+dnl position independent way.
+dnl AC_DEFINE(PI_STATIC_AND_HIDDEN)
diff --git a/sysdeps/mips/libc-tls.c b/sysdeps/mips/libc-tls.c
new file mode 100644
index 0000000000..157ba3389a
--- /dev/null
+++ b/sysdeps/mips/libc-tls.c
@@ -0,0 +1,37 @@
+/* Thread-local storage handling in the ELF dynamic linker.  MIPS version.
+   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 <sysdeps/generic/libc-tls.c>
+#include <dl-tls.h>
+
+#if USE_TLS
+
+/* On MIPS, linker optimizations are not required, so __tls_get_addr
+   can be called even in statically linked binaries.  In this case module
+   must be always 1 and PT_TLS segment exist in the binary, otherwise it
+   would not link.  */
+
+void *
+__tls_get_addr (tls_index *ti)
+{
+  dtv_t *dtv = THREAD_DTV ();
+  return (char *) dtv[1].pointer.val + GET_ADDR_OFFSET;
+}
+
+#endif
diff --git a/sysdeps/mips/sys/asm.h b/sysdeps/mips/sys/asm.h
index b04c36ba5e..b590802fd9 100644
--- a/sysdeps/mips/sys/asm.h
+++ b/sysdeps/mips/sys/asm.h
@@ -1,4 +1,5 @@
-/* Copyright (C) 1997, 1998, 2002, 2003, 2004 Free Software Foundation, Inc.
+/* Copyright (C) 1997, 1998, 2002, 2003, 2004, 2005
+   Free Software Foundation, Inc.
    This file is part of the GNU C Library.
    Contributed by Ralf Baechle <ralf@gnu.org>.
 
@@ -470,4 +471,20 @@ symbol		=	value
 # define MTC0	dmtc0
 #endif
 
+/* The MIPS archtectures do not have a uniform memory model.  Particular
+   platforms may provide additional guarantees - for instance, the R4000
+   LL and SC instructions implicitly perform a SYNC, and the 4K promises
+   strong ordering.
+
+   However, in the absence of those guarantees, we must assume weak ordering
+   and SYNC explicitly where necessary.
+
+   Some obsolete MIPS processors may not support the SYNC instruction.  This
+   applies to "true" MIPS I processors; most of the processors which compile
+   using MIPS I implement parts of MIPS II.  */
+
+#ifndef MIPS_SYNC
+# define MIPS_SYNC	sync
+#endif
+
 #endif /* sys/asm.h */
diff --git a/sysdeps/mips/tls-macros.h b/sysdeps/mips/tls-macros.h
new file mode 100644
index 0000000000..2d0516b3e4
--- /dev/null
+++ b/sysdeps/mips/tls-macros.h
@@ -0,0 +1,88 @@
+/* Macros to support TLS testing in times of missing compiler support.  */
+
+#if _MIPS_SIM != _ABI64
+
+/* These versions are for o32 and n32.  */
+
+# define TLS_GD(x)					\
+  ({ void *__result;					\
+     extern void *__tls_get_addr (void *);		\
+     asm ("addiu %0, $28, %%tlsgd(" #x ")"		\
+	  : "=r" (__result));				\
+     (int *)__tls_get_addr (__result); })
+#else
+# define TLS_GD(x)					\
+  ({ void *__result;					\
+     extern void *__tls_get_addr (void *);		\
+     asm ("daddiu %0, $28, %%tlsgd(" #x ")"		\
+	  : "=r" (__result));				\
+     (int *)__tls_get_addr (__result); })
+#endif
+
+#if _MIPS_SIM != _ABI64
+# define TLS_LD(x)					\
+  ({ void *__result;					\
+     extern void *__tls_get_addr (void *);		\
+     asm ("addiu %0, $28, %%tlsldm(" #x ")"		\
+	  : "=r" (__result));				\
+     __result = __tls_get_addr (__result);		\
+     asm ("lui $3,%%dtprel_hi(" #x ")\n\t"		\
+	  "addiu $3,$3,%%dtprel_lo(" #x ")\n\t"		\
+	  "addu %0,%0,$3"				\
+	  : "+r" (__result) : : "$3");			\
+     __result; })
+# define TLS_IE(x)					\
+  ({ void *__result;					\
+     asm (".set push\n\t.set mips32r2\n\t"		\
+	  "rdhwr\t%0,$29\n\t.set pop"			\
+	  : "=v" (__result));				\
+     asm ("lw $3,%%gottprel(" #x ")($28)\n\t"		\
+	  "addu %0,%0,$3"				\
+	  : "+r" (__result) : : "$3");			\
+     __result; })
+# define TLS_LE(x)					\
+  ({ void *__result;					\
+     asm (".set push\n\t.set mips32r2\n\t"		\
+	  "rdhwr\t%0,$29\n\t.set pop"			\
+	  : "=v" (__result));				\
+     asm ("lui $3,%%tprel_hi(" #x ")\n\t"		\
+	  "addiu $3,$3,%%tprel_lo(" #x ")\n\t"		\
+	  "addu %0,%0,$3"				\
+	  : "+r" (__result) : : "$3");			\
+     __result; })
+
+#else
+
+/* These versions are for n64.  */
+
+# define TLS_LD(x)					\
+  ({ void *__result;					\
+     extern void *__tls_get_addr (void *);		\
+     asm ("daddiu %0, $28, %%tlsldm(" #x ")"		\
+	  : "=r" (__result));				\
+     __result = __tls_get_addr (__result);		\
+     asm ("lui $3,%%dtprel_hi(" #x ")\n\t"		\
+	  "daddiu $3,$3,%%dtprel_lo(" #x ")\n\t"	\
+	  "daddu %0,%0,$3"				\
+	  : "+r" (__result) : : "$3");			\
+     __result; })
+# define TLS_IE(x)					\
+  ({ void *__result;					\
+     asm (".set push\n\t.set mips32r2\n\t"		\
+	  "rdhwr\t%0,$29\n\t.set pop"			\
+	  : "=v" (__result));				\
+     asm ("ld $3,%%gottprel(" #x ")($28)\n\t"		\
+	  "daddu %0,%0,$3"				\
+	  : "+r" (__result) : : "$3");			\
+     __result; })
+# define TLS_LE(x)					\
+  ({ void *__result;					\
+     asm (".set push\n\t.set mips32r2\n\t"		\
+	  "rdhwr\t%0,$29\n\t.set pop"			\
+	  : "=v" (__result));				\
+     asm ("lui $3,%%tprel_hi(" #x ")\n\t"		\
+	  "daddiu $3,$3,%%tprel_lo(" #x ")\n\t"		\
+	  "daddu %0,%0,$3"				\
+	  : "+r" (__result) : : "$3");			\
+     __result; })
+#endif
diff --git a/sysdeps/unix/sysv/linux/mips/bits/siginfo.h b/sysdeps/unix/sysv/linux/mips/bits/siginfo.h
index 54eba41d6e..787e365139 100644
--- a/sysdeps/unix/sysv/linux/mips/bits/siginfo.h
+++ b/sysdeps/unix/sysv/linux/mips/bits/siginfo.h
@@ -1,5 +1,5 @@
 /* siginfo_t, sigevent and constants.  Linux/MIPS version.
-   Copyright (C) 1997, 1998, 2000, 2001, 2002, 2003, 2004
+   Copyright (C) 1997, 1998, 2000, 2001, 2002, 2003, 2004, 2005
 	Free Software Foundation, Inc.
    This file is part of the GNU C Library.
 
@@ -300,10 +300,11 @@ enum
 # define SIGEV_SIGNAL	SIGEV_SIGNAL
   SIGEV_NONE,			/* Other notification: meaningless.  */
 # define SIGEV_NONE	SIGEV_NONE
-  SIGEV_CALLBACK,		/* Deliver via thread creation.  */
-# define SIGEV_CALLBACK	SIGEV_CALLBACK
-  SIGEV_THREAD			/* Deliver via thread creation.  */
+  SIGEV_THREAD,			/* Deliver via thread creation.  */
 # define SIGEV_THREAD	SIGEV_THREAD
+
+  SIGEV_THREAD_ID = 4		/* Send signal to specific thread.  */
+#define SIGEV_THREAD_ID	SIGEV_THREAD_ID
 };
 
 #endif	/* have _SIGNAL_H.  */
diff --git a/sysdeps/unix/sysv/linux/mips/clone.S b/sysdeps/unix/sysv/linux/mips/clone.S
index 043f5921cb..8b8e0072f5 100644
--- a/sysdeps/unix/sysv/linux/mips/clone.S
+++ b/sysdeps/unix/sysv/linux/mips/clone.S
@@ -1,4 +1,4 @@
-/* Copyright (C) 1996, 1997, 2000, 2003 Free Software Foundation, Inc.
+/* Copyright (C) 1996, 1997, 2000, 2003, 2005 Free Software Foundation, Inc.
    This file is part of the GNU C Library.
    Contributed by Ralf Baechle <ralf@linux-mips.org>, 1996.
 
@@ -24,11 +24,23 @@
 #include <sysdep.h>
 #define _ERRNO_H	1
 #include <bits/errno.h>
+#ifdef RESET_PID
+#include <tls.h>
+#endif
+
+#define CLONE_VM      0x00000100
+#define CLONE_THREAD  0x00010000
 
-/* int clone(int (*fn)(void *arg), void *child_stack, int flags, void *arg) */
+/* int clone(int (*fn)(void *arg), void *child_stack, int flags, void *arg,
+	     void *parent_tidptr, void *tls, void *child_tidptr) */
 
 	.text
-LOCALSZ= 1
+#if _MIPS_SIM == _ABIO32
+# define EXTRA_LOCALS 1
+#else
+# define EXTRA_LOCALS 0
+#endif
+LOCALSZ= 4
 FRAMESZ= (((NARGSAVE+LOCALSZ)*SZREG)+ALSZ)&ALMASK
 GPOFF= FRAMESZ-(1*SZREG)
 NESTED(__clone,4*SZREG,sp)
@@ -56,10 +68,26 @@ NESTED(__clone,4*SZREG,sp)
 	PTR_SUBU	a1,32		/* Reserve argument save space.  */
 	PTR_S		a0,0(a1)	/* Save function pointer.  */
 	PTR_S		a3,PTRSIZE(a1)	/* Save argument pointer.  */
+#ifdef RESET_PID
+	LONG_S		a2,(PTRSIZE*2)(a1)	/* Save clone flags.  */
+#endif
 
+	move		a0,a2
+
+	/* Shuffle in the last three arguments - arguments 5, 6, and 7 to
+	   this function, but arguments 3, 4, and 5 to the syscall.  */
+#if _MIPS_SIM == _ABIO32
+	PTR_L		a2,(FRAMESZ+PTRSIZE+PTRSIZE+16)(sp)
+	PTR_S		a2,16(sp)
+	PTR_L		a2,(FRAMESZ+16)(sp)
+	PTR_L		a3,(FRAMESZ+PTRSIZE+16)(sp)
+#else
+	move		a2,a4
+	move		a3,a5
+	move		a4,a6
+#endif
 
 	/* Do the system call */
-	move		a0,a2
 	li		v0,__NR_clone
 	syscall
 
@@ -94,6 +122,15 @@ L(thread_start):
 	/* cp is already loaded.  */
 	SAVE_GP (GPOFF)
 	/* The stackframe has been created on entry of clone().  */
+
+#ifdef RESET_PID
+	/* Check and see if we need to reset the PID.  */
+	LONG_L		a0,(PTRSIZE*2)(sp)
+	and		a1,a0,CLONE_THREAD
+	beqz		a1,L(restore_pid)
+L(donepid):
+#endif
+
 	/* Restore the arg for user's function.  */
 	PTR_L		t9,0(sp)	/* Function pointer.  */
 	PTR_L		a0,PTRSIZE(sp)	/* Argument pointer.  */
@@ -109,6 +146,21 @@ L(thread_start):
 #else
 	jal		_exit
 #endif
+
+#ifdef RESET_PID
+L(restore_pid):
+	and		a1,a0,CLONE_VM
+	li		v0,-1
+	bnez		a1,L(gotpid)
+	li		v0,__NR_getpid
+	syscall
+L(gotpid):
+	READ_THREAD_POINTER(v1)
+	INT_S		v0,PID_OFFSET(v1)
+	INT_S		v0,TID_OFFSET(v1)
+	b		L(donepid)
+#endif
+
 	END(__thread_start)
 
 weak_alias(__clone, clone)
diff --git a/sysdeps/unix/sysv/linux/mips/mips32/sysdep.h b/sysdeps/unix/sysv/linux/mips/mips32/sysdep.h
index 682ec3d6aa..3da2412259 100644
--- a/sysdeps/unix/sysv/linux/mips/mips32/sysdep.h
+++ b/sysdeps/unix/sysv/linux/mips/mips32/sysdep.h
@@ -1,4 +1,4 @@
-/* Copyright (C) 2000, 2002, 2003, 2004 Free Software Foundation, Inc.
+/* Copyright (C) 2000, 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
@@ -48,12 +48,12 @@
 #undef INLINE_SYSCALL
 #define INLINE_SYSCALL(name, nr, args...)                               \
   ({ INTERNAL_SYSCALL_DECL(err);					\
-     long result_var = INTERNAL_SYSCALL (name, err, nr, args);      	\
-     if ( INTERNAL_SYSCALL_ERROR_P (result_var, err) )  		\
-       {                                                                \
-         __set_errno (INTERNAL_SYSCALL_ERRNO (result_var, err));      	\
-         result_var = -1L;                               		\
-       }                                                                \
+     long result_var = INTERNAL_SYSCALL (name, err, nr, args);		\
+     if ( INTERNAL_SYSCALL_ERROR_P (result_var, err) )			\
+       {								\
+	 __set_errno (INTERNAL_SYSCALL_ERRNO (result_var, err));	\
+	 result_var = -1L;						\
+       }								\
      result_var; })
 
 #undef INTERNAL_SYSCALL_DECL
@@ -66,203 +66,218 @@
 #define INTERNAL_SYSCALL_ERRNO(val, err)     (val)
 
 #undef INTERNAL_SYSCALL
-#define INTERNAL_SYSCALL(name, err, nr, args...) internal_syscall##nr(name, err, args)
+#define INTERNAL_SYSCALL(name, err, nr, args...) \
+	internal_syscall##nr (, "li\t$2, %2\t\t\t# " #name "\n\t",	\
+			      "i" (SYS_ify (name)), err, args)
 
-#define internal_syscall0(name, err, dummy...) 				\
-({ 									\
+#undef INTERNAL_SYSCALL_NCS
+#define INTERNAL_SYSCALL_NCS(number, err, nr, args...) \
+	internal_syscall##nr (= number, , "r" (__v0), err, args)
+
+#define internal_syscall0(ncs_init, cs_init, input, err, dummy...)	\
+({									\
 	long _sys_result;						\
 									\
 	{								\
-	register long __v0 asm("$2"); 					\
-	register long __a3 asm("$7"); 					\
-	__asm__ volatile ( 						\
-	".set\tnoreorder\n\t" 						\
-	"li\t$2, %2\t\t\t# " #name "\n\t"				\
-	"syscall\n\t" 							\
-	".set reorder" 							\
-	: "=r" (__v0), "=r" (__a3) 					\
-	: "i" (SYS_ify(name))						\
-	: __SYSCALL_CLOBBERS); 						\
+	register long __v0 asm("$2") ncs_init;				\
+	register long __a3 asm("$7");					\
+	__asm__ volatile (						\
+	".set\tnoreorder\n\t"						\
+	cs_init								\
+	"syscall\n\t"							\
+	".set reorder"							\
+	: "=r" (__v0), "=r" (__a3)					\
+	: input								\
+	: __SYSCALL_CLOBBERS);						\
 	err = __a3;							\
 	_sys_result = __v0;						\
 	}								\
 	_sys_result;							\
 })
 
-#define internal_syscall1(name, err, arg1) 				\
-({ 									\
+#define internal_syscall1(ncs_init, cs_init, input, err, arg1)		\
+({									\
 	long _sys_result;						\
 									\
 	{								\
-	register long __v0 asm("$2"); 					\
-	register long __a0 asm("$4") = (long) arg1; 			\
-	register long __a3 asm("$7"); 					\
-	__asm__ volatile ( 						\
-	".set\tnoreorder\n\t" 						\
-	"li\t$2, %3\t\t\t# " #name "\n\t"				\
-	"syscall\n\t" 							\
-	".set reorder" 							\
-	: "=r" (__v0), "=r" (__a3) 					\
-	: "r" (__a0), "i" (SYS_ify(name)) 				\
-	: __SYSCALL_CLOBBERS); 						\
+	register long __v0 asm("$2") ncs_init;				\
+	register long __a0 asm("$4") = (long) arg1;			\
+	register long __a3 asm("$7");					\
+	__asm__ volatile (						\
+	".set\tnoreorder\n\t"						\
+	cs_init								\
+	"syscall\n\t"							\
+	".set reorder"							\
+	: "=r" (__v0), "=r" (__a3)					\
+	: input, "r" (__a0)						\
+	: __SYSCALL_CLOBBERS);						\
 	err = __a3;							\
 	_sys_result = __v0;						\
 	}								\
 	_sys_result;							\
 })
 
-#define internal_syscall2(name, err, arg1, arg2) 			\
-({ 									\
+#define internal_syscall2(ncs_init, cs_init, input, err, arg1, arg2)	\
+({									\
 	long _sys_result;						\
 									\
 	{								\
-	register long __v0 asm("$2"); 					\
-	register long __a0 asm("$4") = (long) arg1; 			\
-	register long __a1 asm("$5") = (long) arg2; 			\
-	register long __a3 asm("$7"); 					\
-	__asm__ volatile ( 						\
-	".set\tnoreorder\n\t" 						\
-	"li\t$2, %4\t\t\t# " #name "\n\t" 				\
-	"syscall\n\t" 							\
-	".set\treorder" 						\
-	: "=r" (__v0), "=r" (__a3) 					\
-	: "r" (__a0), "r" (__a1), "i" (SYS_ify(name))			\
-	: __SYSCALL_CLOBBERS); 						\
+	register long __v0 asm("$2") ncs_init;				\
+	register long __a0 asm("$4") = (long) arg1;			\
+	register long __a1 asm("$5") = (long) arg2;			\
+	register long __a3 asm("$7");					\
+	__asm__ volatile (						\
+	".set\tnoreorder\n\t"						\
+	cs_init								\
+	"syscall\n\t"							\
+	".set\treorder"						\
+	: "=r" (__v0), "=r" (__a3)					\
+	: input, "r" (__a0), "r" (__a1)					\
+	: __SYSCALL_CLOBBERS);						\
 	err = __a3;							\
 	_sys_result = __v0;						\
 	}								\
 	_sys_result;							\
 })
 
-#define internal_syscall3(name, err, arg1, arg2, arg3) 			\
-({ 									\
+#define internal_syscall3(ncs_init, cs_init, input, err, arg1, arg2, arg3)\
+({									\
 	long _sys_result;						\
 									\
 	{								\
-	register long __v0 asm("$2"); 					\
-	register long __a0 asm("$4") = (long) arg1; 			\
-	register long __a1 asm("$5") = (long) arg2; 			\
-	register long __a2 asm("$6") = (long) arg3; 			\
-	register long __a3 asm("$7"); 					\
-	__asm__ volatile ( 						\
-	".set\tnoreorder\n\t" 						\
-	"li\t$2, %5\t\t\t# " #name "\n\t" 				\
-	"syscall\n\t" 							\
-	".set\treorder" 						\
-	: "=r" (__v0), "=r" (__a3) 					\
-	: "r" (__a0), "r" (__a1), "r" (__a2), "i" (SYS_ify(name)) 	\
-	: __SYSCALL_CLOBBERS); 						\
+	register long __v0 asm("$2") ncs_init;				\
+	register long __a0 asm("$4") = (long) arg1;			\
+	register long __a1 asm("$5") = (long) arg2;			\
+	register long __a2 asm("$6") = (long) arg3;			\
+	register long __a3 asm("$7");					\
+	__asm__ volatile (						\
+	".set\tnoreorder\n\t"						\
+	cs_init								\
+	"syscall\n\t"							\
+	".set\treorder"						\
+	: "=r" (__v0), "=r" (__a3)					\
+	: input, "r" (__a0), "r" (__a1), "r" (__a2)			\
+	: __SYSCALL_CLOBBERS);						\
 	err = __a3;							\
 	_sys_result = __v0;						\
 	}								\
 	_sys_result;							\
 })
 
-#define internal_syscall4(name, err, arg1, arg2, arg3, arg4) 		\
-({ 									\
+#define internal_syscall4(ncs_init, cs_init, input, err, arg1, arg2, arg3, arg4)\
+({									\
 	long _sys_result;						\
 									\
 	{								\
-	register long __v0 asm("$2"); 					\
-	register long __a0 asm("$4") = (long) arg1; 			\
-	register long __a1 asm("$5") = (long) arg2; 			\
-	register long __a2 asm("$6") = (long) arg3; 			\
-	register long __a3 asm("$7") = (long) arg4; 			\
-	__asm__ volatile ( 						\
-	".set\tnoreorder\n\t" 						\
-	"li\t$2, %5\t\t\t# " #name "\n\t" 				\
-	"syscall\n\t" 							\
-	".set\treorder" 						\
-	: "=r" (__v0), "+r" (__a3) 					\
-	: "r" (__a0), "r" (__a1), "r" (__a2), "i" (SYS_ify(name)) 	\
-	: __SYSCALL_CLOBBERS); 						\
+	register long __v0 asm("$2") ncs_init;				\
+	register long __a0 asm("$4") = (long) arg1;			\
+	register long __a1 asm("$5") = (long) arg2;			\
+	register long __a2 asm("$6") = (long) arg3;			\
+	register long __a3 asm("$7") = (long) arg4;			\
+	__asm__ volatile (						\
+	".set\tnoreorder\n\t"						\
+	cs_init								\
+	"syscall\n\t"							\
+	".set\treorder"						\
+	: "=r" (__v0), "+r" (__a3)					\
+	: input, "r" (__a0), "r" (__a1), "r" (__a2)			\
+	: __SYSCALL_CLOBBERS);						\
 	err = __a3;							\
 	_sys_result = __v0;						\
 	}								\
 	_sys_result;							\
 })
 
-#define internal_syscall5(name, err, arg1, arg2, arg3, arg4, arg5) 	\
-({ 									\
+/* We need to use a frame pointer for the functions in which we
+   adjust $sp around the syscall, or debug information and unwind
+   information will be $sp relative and thus wrong during the syscall.  As
+   of GCC 3.4.3, this is sufficient.  */
+#define FORCE_FRAME_POINTER alloca (4)
+
+#define internal_syscall5(ncs_init, cs_init, input, err, arg1, arg2, arg3, arg4, arg5)\
+({									\
 	long _sys_result;						\
 									\
+	FORCE_FRAME_POINTER;						\
 	{								\
-	register long __v0 asm("$2"); 					\
-	register long __a0 asm("$4") = (long) arg1; 			\
-	register long __a1 asm("$5") = (long) arg2; 			\
-	register long __a2 asm("$6") = (long) arg3; 			\
-	register long __a3 asm("$7") = (long) arg4; 			\
-	__asm__ volatile ( 						\
-	".set\tnoreorder\n\t" 						\
-	"subu\t$29, 32\n\t" 						\
-	"sw\t%6, 16($29)\n\t" 						\
-	"li\t$2, %5\t\t\t# " #name "\n\t" 				\
-	"syscall\n\t" 							\
-	"addiu\t$29, 32\n\t" 						\
-	".set\treorder" 						\
-	: "=r" (__v0), "+r" (__a3) 					\
-	: "r" (__a0), "r" (__a1), "r" (__a2), "i" (SYS_ify(name)), 	\
-	  "r" ((long)arg5) 						\
-	: __SYSCALL_CLOBBERS); 						\
+	register long __v0 asm("$2") ncs_init;				\
+	register long __a0 asm("$4") = (long) arg1;			\
+	register long __a1 asm("$5") = (long) arg2;			\
+	register long __a2 asm("$6") = (long) arg3;			\
+	register long __a3 asm("$7") = (long) arg4;			\
+	__asm__ volatile (						\
+	".set\tnoreorder\n\t"						\
+	"subu\t$29, 32\n\t"						\
+	"sw\t%6, 16($29)\n\t"						\
+	cs_init								\
+	"syscall\n\t"							\
+	"addiu\t$29, 32\n\t"						\
+	".set\treorder"						\
+	: "=r" (__v0), "+r" (__a3)					\
+	: input, "r" (__a0), "r" (__a1), "r" (__a2),			\
+	  "r" ((long)arg5)						\
+	: __SYSCALL_CLOBBERS);						\
 	err = __a3;							\
 	_sys_result = __v0;						\
 	}								\
 	_sys_result;							\
 })
 
-#define internal_syscall6(name, err, arg1, arg2, arg3, arg4, arg5, arg6)\
-({ 									\
+#define internal_syscall6(ncs_init, cs_init, input, err, arg1, arg2, arg3, arg4, arg5, arg6)\
+({									\
 	long _sys_result;						\
 									\
+	FORCE_FRAME_POINTER;						\
 	{								\
-	register long __v0 asm("$2"); 					\
-	register long __a0 asm("$4") = (long) arg1; 			\
-	register long __a1 asm("$5") = (long) arg2; 			\
-	register long __a2 asm("$6") = (long) arg3; 			\
-	register long __a3 asm("$7") = (long) arg4; 			\
-	__asm__ volatile ( 						\
-	".set\tnoreorder\n\t" 						\
-	"subu\t$29, 32\n\t" 						\
-	"sw\t%6, 16($29)\n\t" 						\
-	"sw\t%7, 20($29)\n\t" 						\
-	"li\t$2, %5\t\t\t# " #name "\n\t" 				\
-	"syscall\n\t" 							\
-	"addiu\t$29, 32\n\t" 						\
-	".set\treorder" 						\
-	: "=r" (__v0), "+r" (__a3) 					\
-	: "r" (__a0), "r" (__a1), "r" (__a2), "i" (SYS_ify(name)), 	\
+	register long __v0 asm("$2") ncs_init;				\
+	register long __a0 asm("$4") = (long) arg1;			\
+	register long __a1 asm("$5") = (long) arg2;			\
+	register long __a2 asm("$6") = (long) arg3;			\
+	register long __a3 asm("$7") = (long) arg4;			\
+	__asm__ volatile (						\
+	".set\tnoreorder\n\t"						\
+	"subu\t$29, 32\n\t"						\
+	"sw\t%6, 16($29)\n\t"						\
+	"sw\t%7, 20($29)\n\t"						\
+	cs_init								\
+	"syscall\n\t"							\
+	"addiu\t$29, 32\n\t"						\
+	".set\treorder"						\
+	: "=r" (__v0), "+r" (__a3)					\
+	: input, "r" (__a0), "r" (__a1), "r" (__a2),			\
 	  "r" ((long)arg5), "r" ((long)arg6)				\
-	: __SYSCALL_CLOBBERS); 						\
+	: __SYSCALL_CLOBBERS);						\
 	err = __a3;							\
 	_sys_result = __v0;						\
 	}								\
 	_sys_result;							\
 })
 
-#define internal_syscall7(name, err, arg1, arg2, arg3, arg4, arg5, arg6, arg7)\
-({ 									\
+#define internal_syscall7(ncs_init, cs_init, input, err, arg1, arg2, arg3, arg4, arg5, arg6, arg7)\
+({									\
 	long _sys_result;						\
 									\
+	FORCE_FRAME_POINTER;						\
 	{								\
-	register long __v0 asm("$2"); 					\
-	register long __a0 asm("$4") = (long) arg1; 			\
-	register long __a1 asm("$5") = (long) arg2; 			\
-	register long __a2 asm("$6") = (long) arg3; 			\
-	register long __a3 asm("$7") = (long) arg4; 			\
-	__asm__ volatile ( 						\
-	".set\tnoreorder\n\t" 						\
-	"subu\t$29, 32\n\t" 						\
-	"sw\t%6, 16($29)\n\t" 						\
-	"sw\t%7, 20($29)\n\t" 						\
-	"sw\t%8, 24($29)\n\t" 						\
-	"li\t$2, %5\t\t\t# " #name "\n\t" 				\
-	"syscall\n\t" 							\
-	"addiu\t$29, 32\n\t" 						\
-	".set\treorder" 						\
-	: "=r" (__v0), "+r" (__a3) 					\
-	: "r" (__a0), "r" (__a1), "r" (__a2), "i" (SYS_ify(name)), 	\
+	register long __v0 asm("$2") ncs_init;				\
+	register long __a0 asm("$4") = (long) arg1;			\
+	register long __a1 asm("$5") = (long) arg2;			\
+	register long __a2 asm("$6") = (long) arg3;			\
+	register long __a3 asm("$7") = (long) arg4;			\
+	__asm__ volatile (						\
+	".set\tnoreorder\n\t"						\
+	"subu\t$29, 32\n\t"						\
+	"sw\t%6, 16($29)\n\t"						\
+	"sw\t%7, 20($29)\n\t"						\
+	"sw\t%8, 24($29)\n\t"						\
+	cs_init								\
+	"syscall\n\t"							\
+	"addiu\t$29, 32\n\t"						\
+	".set\treorder"						\
+	: "=r" (__v0), "+r" (__a3)					\
+	: input, "r" (__a0), "r" (__a1), "r" (__a2),			\
 	  "r" ((long)arg5), "r" ((long)arg6), "r" ((long)arg7)		\
-	: __SYSCALL_CLOBBERS); 						\
+	: __SYSCALL_CLOBBERS);						\
 	err = __a3;							\
 	_sys_result = __v0;						\
 	}								\
diff --git a/sysdeps/unix/sysv/linux/mips/vfork.S b/sysdeps/unix/sysv/linux/mips/vfork.S
new file mode 100644
index 0000000000..1383ddc6a7
--- /dev/null
+++ b/sysdeps/unix/sysv/linux/mips/vfork.S
@@ -0,0 +1,98 @@
+/* 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.  */
+
+/* vfork() is just a special case of clone().  */
+
+#include <sys/asm.h>
+#include <sysdep.h>
+#include <asm/unistd.h>
+#include <sgidefs.h>
+
+#ifndef SAVE_PID
+#define SAVE_PID
+#endif
+
+#ifndef RESTORE_PID
+#define RESTORE_PID
+#endif
+
+
+/* int vfork() */
+
+	.text
+LOCALSZ= 1
+FRAMESZ= (((NARGSAVE+LOCALSZ)*SZREG)+ALSZ)&ALMASK
+GPOFF= FRAMESZ-(1*SZREG)
+NESTED(__vfork,FRAMESZ,sp)
+#ifdef __PIC__
+	SETUP_GP
+#endif
+	PTR_SUBU sp, FRAMESZ
+	SETUP_GP64 (a5, __vfork)
+#ifdef __PIC__
+	SAVE_GP (GPOFF)
+#endif
+#ifdef PROF
+# if (_MIPS_SIM != _ABIO32)
+	PTR_S		a5, GPOFF(sp)
+# endif
+	.set		noat
+	move		$1, ra
+# if (_MIPS_SIM == _ABIO32)
+	subu		sp,sp,8
+# endif
+	jal		_mcount
+	.set		at
+# if (_MIPS_SIM != _ABIO32)
+	PTR_L		a5, GPOFF(sp)
+# endif
+#endif
+
+	PTR_ADDU	sp, FRAMESZ
+
+	SAVE_PID
+
+	li		a0, 0x4112	/* CLONE_VM | CLONE_VFORK | SIGCHLD */
+	move		a1, sp
+
+	/* Do the system call */
+	li		v0,__NR_clone
+	syscall
+
+	RESTORE_PID
+
+	bnez		a3,L(error)
+
+	/* Successful return from the parent or child.  */
+	RESTORE_GP64
+	ret
+
+	/* Something bad happened -- no child created.  */
+L(error):
+#ifdef __PIC__
+	PTR_LA		t9, __syscall_error
+	RESTORE_GP64
+	jr		t9
+#else
+	RESTORE_GP64
+	j		__syscall_error
+#endif
+	END(__vfork)
+
+libc_hidden_def(__vfork)
+weak_alias(__vfork, vfork)