summary refs log tree commit diff
diff options
context:
space:
mode:
authorUlrich Drepper <drepper@redhat.com>2003-01-28 10:42:28 +0000
committerUlrich Drepper <drepper@redhat.com>2003-01-28 10:42:28 +0000
commite6ebd2e4db59da6c1726ecfa3516f1f1b3048442 (patch)
treedfa3797737a8e4dc76ed38e52cf7baa27e936003
parent772e3426a7b6f5200cb1029d41308b8b666cdbab (diff)
downloadglibc-e6ebd2e4db59da6c1726ecfa3516f1f1b3048442.tar.gz
glibc-e6ebd2e4db59da6c1726ecfa3516f1f1b3048442.tar.xz
glibc-e6ebd2e4db59da6c1726ecfa3516f1f1b3048442.zip
Update.
2003-01-27  Martin Schwidefsky  <schwidefsky@de.ibm.com>

	* elf/elf.h: Add new s390 relocs.
	* elf/tls-macros.h: Add s390 versions.
	* sysdeps/s390/Versions [GLIBC_2.3] (ld): Export __tls_get_offset.
	* sysdeps/s390/dl-tls.h: New file.
	* sysdeps/s390/libc-tls.c: New file.
	* sysdeps/s390/s390-32/dl-machine.h (elf_machine_type_class): Add TLS
	relocs for class PLT.
	(elf_machine_rela): Handle TLS relocs.
	* sysdeps/s390/s390-64/dl-machine.h: Likewise.
	* sysdeps/s390/s390-32/elf/configure.in: Add TLS check.
	* sysdeps/s390/s390-64/elf/configure.in: Likewise.
	* sysdeps/unix/sysv/linux/s390/s390-32/clone.S: Add support for
	CLONE_CHILD_*TID flags.
	* sysdeps/unix/sysv/linux/s390/s390-64/clone.S: Likewise.
	* sysdeps/unix/sysv/linux/s390/s390-64/mmap.S: Use branch with 32
	bit offset.
	* sysdeps/unix/sysv/linux/s390/s390-64/socket.S: Likewise.
	* sysdeps/unix/sysv/linux/s390/s390-64/syscall.S: Likewise.
	* sysdeps/unix/sysv/linux/s390/s390-32/sysdep.S (__syscall_error):
	Support USE___THREAD. Define RTLD_PRIVATE_ERRNO variant.
	* sysdeps/unix/sysv/linux/s390/s390-64/sysdep.S (__syscall_error):
	Likewise.
	* sysdeps/unix/sysv/linux/s390/s390-32/sysdep.h:
	(SYSCALL_ERROR_LABEL): Move define next to SYSCALL_ERROR_HANDLER.
	(SYSCALL_ERROR_HANDLER): Add USE___THREAD and RTLD_PRIVATE_ERRNO
	variants.
	* sysdeps/unix/sysv/linux/s390/s390-64/sysdep.h:
	(SYSCALL_ERROR_LABEL): Move define next to SYSCALL_ERROR_HANDLER. Use
	direct branch to syscall_error for !PIC and PIC && !_LIBC_REENTRANT.
	(SYSCALL_ERROR_HANDLER): Add USE___THREAD and RTLD_PRIVATE_ERRNO
	variants.
-rw-r--r--ChangeLog34
-rw-r--r--elf/elf.h103
-rw-r--r--elf/tls-macros.h190
-rw-r--r--linuxthreads/ChangeLog14
-rw-r--r--linuxthreads/sysdeps/s390/s390-32/pt-machine.h8
-rw-r--r--linuxthreads/sysdeps/s390/s390-64/pt-machine.h8
-rw-r--r--linuxthreads/sysdeps/s390/tls.h87
-rw-r--r--linuxthreads/sysdeps/unix/sysv/linux/s390/s390-64/sysdep-cancel.h2
-rw-r--r--linuxthreads/sysdeps/unix/sysv/linux/s390/s390-64/vfork.S2
-rw-r--r--nptl/ChangeLog7
-rw-r--r--nptl/sysdeps/pthread/createthread.c145
-rw-r--r--nptl/sysdeps/unix/sysv/linux/i386/createthread.c170
-rw-r--r--sysdeps/s390/Versions6
-rw-r--r--sysdeps/s390/dl-tls.h73
-rw-r--r--sysdeps/s390/libc-tls.c37
-rw-r--r--sysdeps/s390/s390-32/dl-machine.h92
-rw-r--r--sysdeps/s390/s390-32/elf/configure52
-rw-r--r--sysdeps/s390/s390-32/elf/configure.in37
-rw-r--r--sysdeps/s390/s390-64/dl-machine.h92
-rw-r--r--sysdeps/s390/s390-64/elf/configure52
-rw-r--r--sysdeps/s390/s390-64/elf/configure.in37
-rw-r--r--sysdeps/unix/sysv/linux/s390/s390-32/clone.S16
-rw-r--r--sysdeps/unix/sysv/linux/s390/s390-32/sysdep.S139
-rw-r--r--sysdeps/unix/sysv/linux/s390/s390-32/sysdep.h84
-rw-r--r--sysdeps/unix/sysv/linux/s390/s390-64/clone.S26
-rw-r--r--sysdeps/unix/sysv/linux/s390/s390-64/mmap.S2
-rw-r--r--sysdeps/unix/sysv/linux/s390/s390-64/socket.S2
-rw-r--r--sysdeps/unix/sysv/linux/s390/s390-64/syscall.S2
-rw-r--r--sysdeps/unix/sysv/linux/s390/s390-64/sysdep.S113
-rw-r--r--sysdeps/unix/sysv/linux/s390/s390-64/sysdep.h65
30 files changed, 1346 insertions, 351 deletions
diff --git a/ChangeLog b/ChangeLog
index ef422b7f5e..bf8f96fe5e 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,37 @@
+2003-01-27  Martin Schwidefsky  <schwidefsky@de.ibm.com>
+
+	* elf/elf.h: Add new s390 relocs.
+	* elf/tls-macros.h: Add s390 versions.
+	* sysdeps/s390/Versions [GLIBC_2.3] (ld): Export __tls_get_offset.
+	* sysdeps/s390/dl-tls.h: New file.
+	* sysdeps/s390/libc-tls.c: New file.
+	* sysdeps/s390/s390-32/dl-machine.h (elf_machine_type_class): Add TLS
+	relocs for class PLT.
+	(elf_machine_rela): Handle TLS relocs.
+	* sysdeps/s390/s390-64/dl-machine.h: Likewise.
+	* sysdeps/s390/s390-32/elf/configure.in: Add TLS check.
+	* sysdeps/s390/s390-64/elf/configure.in: Likewise.
+	* sysdeps/unix/sysv/linux/s390/s390-32/clone.S: Add support for
+	CLONE_CHILD_*TID flags.
+	* sysdeps/unix/sysv/linux/s390/s390-64/clone.S: Likewise.
+	* sysdeps/unix/sysv/linux/s390/s390-64/mmap.S: Use branch with 32
+	bit offset.
+	* sysdeps/unix/sysv/linux/s390/s390-64/socket.S: Likewise.
+	* sysdeps/unix/sysv/linux/s390/s390-64/syscall.S: Likewise.
+	* sysdeps/unix/sysv/linux/s390/s390-32/sysdep.S (__syscall_error):
+	Support USE___THREAD. Define RTLD_PRIVATE_ERRNO variant.
+	* sysdeps/unix/sysv/linux/s390/s390-64/sysdep.S (__syscall_error):
+	Likewise.
+	* sysdeps/unix/sysv/linux/s390/s390-32/sysdep.h:
+	(SYSCALL_ERROR_LABEL): Move define next to SYSCALL_ERROR_HANDLER.
+	(SYSCALL_ERROR_HANDLER): Add USE___THREAD and RTLD_PRIVATE_ERRNO
+	variants.
+	* sysdeps/unix/sysv/linux/s390/s390-64/sysdep.h:
+	(SYSCALL_ERROR_LABEL): Move define next to SYSCALL_ERROR_HANDLER. Use
+	direct branch to syscall_error for !PIC and PIC && !_LIBC_REENTRANT.
+	(SYSCALL_ERROR_HANDLER): Add USE___THREAD and RTLD_PRIVATE_ERRNO
+	variants.
+
 2003-01-28  Ulrich Drepper  <drepper@redhat.com>
 
 	* sysdeps/unix/common/pause.c (do_pause): New function.  Split
diff --git a/elf/elf.h b/elf/elf.h
index 89ce5684db..30d0669040 100644
--- a/elf/elf.h
+++ b/elf/elf.h
@@ -2228,36 +2228,83 @@ typedef Elf32_Addr Elf32_Conflict;
 
 /* Additional s390 relocs */
 
-#define R_390_NONE	0	       /* No reloc.  */
-#define R_390_8		1	       /* Direct 8 bit.	 */
-#define R_390_12	2	       /* Direct 12 bit.  */
-#define R_390_16	3	       /* Direct 16 bit.  */
-#define R_390_32	4	       /* Direct 32 bit.  */
-#define R_390_PC32	5	       /* PC relative 32 bit.  */
-#define R_390_GOT12	6	       /* 12 bit GOT offset.  */
-#define R_390_GOT32	7	       /* 32 bit GOT offset.  */
-#define R_390_PLT32	8	       /* 32 bit PC relative PLT address.  */
-#define R_390_COPY	9	       /* Copy symbol at runtime.  */
-#define R_390_GLOB_DAT	10	       /* Create GOT entry.  */
-#define R_390_JMP_SLOT	11	       /* Create PLT entry.  */
-#define R_390_RELATIVE	12	       /* Adjust by program base.  */
-#define R_390_GOTOFF	13	       /* 32 bit offset to GOT.	 */
-#define R_390_GOTPC	14	       /* 32 bit PC relative offset to GOT.  */
-#define R_390_GOT16	15	       /* 16 bit GOT offset.  */
-#define R_390_PC16	16	       /* PC relative 16 bit.  */
-#define R_390_PC16DBL	17	       /* PC relative 16 bit shifted by 1.  */
-#define R_390_PLT16DBL	18	       /* 16 bit PC rel. PLT shifted by 1.  */
-#define R_390_PC32DBL	19	       /* PC relative 32 bit shifted by 1.  */
-#define R_390_PLT32DBL	20	       /* 32 bit PC rel. PLT shifted by 1.  */
-#define R_390_GOTPCDBL	21	       /* 32 bit PC rel. GOT shifted by 1.  */
-#define R_390_64	22	       /* Direct 64 bit.  */
-#define R_390_PC64	23	       /* PC relative 64 bit.  */
-#define R_390_GOT64	24	       /* 64 bit GOT offset.  */
-#define R_390_PLT64	25	       /* 64 bit PC relative PLT address.  */
-#define R_390_GOTENT	26	       /* 32 bit PC rel. to GOT entry >> 1. */
+#define R_390_NONE		0	/* No reloc.  */
+#define R_390_8			1	/* Direct 8 bit.  */
+#define R_390_12		2	/* Direct 12 bit.  */
+#define R_390_16		3	/* Direct 16 bit.  */
+#define R_390_32		4	/* Direct 32 bit.  */
+#define R_390_PC32		5	/* PC relative 32 bit.	*/
+#define R_390_GOT12		6	/* 12 bit GOT offset.  */
+#define R_390_GOT32		7	/* 32 bit GOT offset.  */
+#define R_390_PLT32		8	/* 32 bit PC relative PLT address.  */
+#define R_390_COPY		9	/* Copy symbol at runtime.  */
+#define R_390_GLOB_DAT		10	/* Create GOT entry.  */
+#define R_390_JMP_SLOT		11	/* Create PLT entry.  */
+#define R_390_RELATIVE		12	/* Adjust by program base.  */
+#define R_390_GOTOFF32		13	/* 32 bit offset to GOT.	 */
+#define R_390_GOTPC		14	/* 32 bit PC relative offset to GOT.  */
+#define R_390_GOT16		15	/* 16 bit GOT offset.  */
+#define R_390_PC16		16	/* PC relative 16 bit.	*/
+#define R_390_PC16DBL		17	/* PC relative 16 bit shifted by 1.  */
+#define R_390_PLT16DBL		18	/* 16 bit PC rel. PLT shifted by 1.  */
+#define R_390_PC32DBL		19	/* PC relative 32 bit shifted by 1.  */
+#define R_390_PLT32DBL		20	/* 32 bit PC rel. PLT shifted by 1.  */
+#define R_390_GOTPCDBL		21	/* 32 bit PC rel. GOT shifted by 1.  */
+#define R_390_64		22	/* Direct 64 bit.  */
+#define R_390_PC64		23	/* PC relative 64 bit.	*/
+#define R_390_GOT64		24	/* 64 bit GOT offset.  */
+#define R_390_PLT64		25	/* 64 bit PC relative PLT address.  */
+#define R_390_GOTENT		26	/* 32 bit PC rel. to GOT entry >> 1. */
+#define R_390_GOTOFF16		27	/* 16 bit offset to GOT. */
+#define R_390_GOTOFF64		28	/* 64 bit offset to GOT. */
+#define R_390_GOTPLT12		29	/* 12 bit offset to jump slot.	*/
+#define R_390_GOTPLT16		30	/* 16 bit offset to jump slot.	*/
+#define R_390_GOTPLT32		31	/* 32 bit offset to jump slot.	*/
+#define R_390_GOTPLT64		32	/* 64 bit offset to jump slot.	*/
+#define R_390_GOTPLTENT		33	/* 32 bit rel. offset to jump slot.  */
+#define R_390_PLTOFF16		34	/* 16 bit offset from GOT to PLT. */
+#define R_390_PLTOFF32		35	/* 32 bit offset from GOT to PLT. */
+#define R_390_PLTOFF64		36	/* 16 bit offset from GOT to PLT. */
+#define R_390_TLS_LOAD		37	/* Tag for load insn in TLS code.  */
+#define R_390_TLS_GDCALL	38	/* Tag for function call in general
+					   dynamic TLS code. */
+#define R_390_TLS_LDCALL	39	/* Tag for function call in local
+					   dynamic TLS code. */
+#define R_390_TLS_GD32		40	/* Direct 32 bit for general dynamic
+					   thread local data.  */
+#define R_390_TLS_GD64		41	/* Direct 64 bit for general dynamic
+					  thread local data.  */
+#define R_390_TLS_GOTIE12	42	/* 12 bit GOT offset for static TLS
+					   block offset.  */
+#define R_390_TLS_GOTIE32	43	/* 32 bit GOT offset for static TLS
+					   block offset.  */
+#define R_390_TLS_GOTIE64	44	/* 64 bit GOT offset for static TLS
+					   block offset. */
+#define R_390_TLS_LDM32		45	/* Direct 32 bit for local dynamic
+					   thread local data in LE code.  */
+#define R_390_TLS_LDM64		46	/* Direct 64 bit for local dynamic
+					   thread local data in LE code.  */
+#define R_390_TLS_IE32		47	/* 32 bit address of GOT entry for
+					   negated static TLS block offset.  */
+#define R_390_TLS_IE64		48	/* 64 bit address of GOT entry for
+					   negated static TLS block offset.  */
+#define R_390_TLS_IEENT		49	/* 32 bit rel. offset to GOT entry for
+					   negated static TLS block offset.  */
+#define R_390_TLS_LE32		50	/* 32 bit negated offset relative to
+					   static TLS block.  */
+#define R_390_TLS_LE64		51	/* 64 bit negated offset relative to
+					   static TLS block.  */
+#define R_390_TLS_LDO32		52	/* 32 bit offset relative to TLS
+					   block.  */
+#define R_390_TLS_LDO64		53	/* 64 bit offset relative to TLS
+					   block.  */
+#define R_390_TLS_DTPMOD	54	/* ID of module containing symbol.  */
+#define R_390_TLS_DTPOFF	55	/* Offset in TLS block.	 */
+#define R_390_TLS_TPOFF		56	/* Negated offset in static TLS
+					   block.  */
 
 /* Keep this the last entry.  */
-#define R_390_NUM	27
+#define R_390_NUM		57
 
 /* CRIS relocations.  */
 #define R_CRIS_NONE		0
diff --git a/elf/tls-macros.h b/elf/tls-macros.h
index 2de4b1a1af..ea9ec56376 100644
--- a/elf/tls-macros.h
+++ b/elf/tls-macros.h
@@ -367,6 +367,196 @@ register void *__gp __asm__("$29");
 	    "o5", "o7");						      \
      __o0; })
 
+#elif defined __s390x__
+
+# define TLS_LE(x) \
+  ({ unsigned long __offset;						      \
+     asm ("bras %0,1f\n"						      \
+	  "0:\t.quad " #x "@ntpoff\n"					      \
+	  "1:\tlg %0,0(%0)"						      \
+	  : "=a" (__offset) : : "cc" );					      \
+     (int *) (__builtin_thread_pointer() + __offset); })
+
+# ifdef PIC
+#  define TLS_IE(x) \
+  ({ unsigned long __offset;						      \
+     asm ("bras %0,1f\n"						      \
+	  "0:\t.quad " #x "@gotntpoff\n"				      \
+	  "1:\tlg %0,0(%0)\n\t"						      \
+	  "lg %0,0(%0,%%r12):tls_load:" #x				      \
+	  : "=&a" (__offset) : : "cc" );				      \
+     (int *) (__builtin_thread_pointer() + __offset); })
+# else
+#  define TLS_IE(x) \
+  ({ unsigned long  __offset;						      \
+     asm ("bras %0,1f\n"						      \
+	  "0:\t.quad " #x "@indntpoff\n"				      \
+	  "1:\t lg %0,0(%0)\n\t"					      \
+	  "lg %0,0(%0):tls_load:" #x					      \
+	  : "=&a" (__offset) : : "cc" );				      \
+     (int *) (__builtin_thread_pointer() + __offset); })
+# endif
+
+# ifdef PIC
+#  define TLS_LD(x) \
+  ({ unsigned long __offset, __save12;					      \
+     asm ("bras %0,1f\n"						      \
+	  "0:\t.quad " #x "@tlsldm\n\t"					      \
+	  ".quad " #x "@dtpoff\n"					      \
+	  "1:\tlgr %1,%%r12\n\t"					      \
+          "larl %%r12,_GLOBAL_OFFSET_TABLE_\n\t"			      \
+          "lg %%r2,0(%0)\n\t"						      \
+	  "brasl %%r14,__tls_get_offset@plt:tls_ldcall:" #x "\n\t"	      \
+	  "lg %0,8(%0)\n\t"						      \
+	  "algr %0,%%r2\n\t"						      \
+          "lgr %%r12,%1"						      \
+	  : "=&a" (__offset), "=&a" (__save12)				      \
+          : : "cc", "0", "1", "2", "3", "4", "5" );			      \
+     (int *) (__builtin_thread_pointer() + __offset); })
+# else
+#  define TLS_LD(x) \
+  ({ unsigned long __offset;						      \
+     asm ("bras %0,1f\n"						      \
+	  "0:\t.quad " #x "@tlsldm\n\t"					      \
+	  ".quad " #x "@dtpoff\n"					      \
+	  "1:\tlarl %%r12,_GLOBAL_OFFSET_TABLE_\n\t"			      \
+          "lg %%r2,0(%0)\n\t"						      \
+	  "brasl %%r14,__tls_get_offset@plt:tls_ldcall:" #x "\n\t"	      \
+	  "lg %0,8(%0)\n\t"						      \
+	  "algr %0,%%r2"						      \
+	  : "=&a" (__offset) : : "cc", "0", "1", "2", "3", "4", "5", "12" );  \
+     (int *) (__builtin_thread_pointer() + __offset); })
+# endif
+
+# ifdef PIC
+#  define TLS_GD(x) \
+  ({ unsigned long __offset, __save12;					      \
+     asm ("bras %0,1f\n"						      \
+	  "0:\t.quad " #x "@tlsgd\n"					      \
+	  "1:\tlgr %1,%%r12\n\t"					      \
+	  "larl %%r12,_GLOBAL_OFFSET_TABLE_\n\t"			      \
+          "lg %%r2,0(%0)\n\t"						      \
+	  "brasl %%r14,__tls_get_offset@plt:tls_gdcall:" #x "\n\t"	      \
+          "lgr %0,%%r2\n\t"						      \
+          "lgr %%r12,%1"						      \
+	  : "=&a" (__offset), "=&a" (__save12)				      \
+          : : "cc", "0", "1", "2", "3", "4", "5" );			      \
+     (int *) (__builtin_thread_pointer() + __offset); })
+# else
+#  define TLS_GD(x) \
+  ({ unsigned long __offset;						      \
+     asm ("bras %0,1f\n"						      \
+	  "0:\t.quad " #x "@tlsgd\n"					      \
+	  "1:\tlarl %%r12,_GLOBAL_OFFSET_TABLE_\n\t"			      \
+	  "lg %%r2,0(%0)\n\t"						      \
+	  "brasl %%r14,__tls_get_offset@plt:tls_gdcall:" #x "\n\t"	      \
+          "lgr %0,%%r2"							      \
+	  : "=&a" (__offset) : : "cc", "0", "1", "2", "3", "4", "5", "12" );  \
+     (int *) (__builtin_thread_pointer() + __offset); })
+# endif
+
+#elif defined __s390__
+
+# define TLS_LE(x) \
+  ({ unsigned long __offset;						      \
+     asm ("bras %0,1f\n"						      \
+	  "0:\t.long " #x "@ntpoff\n"					      \
+	  "1:\tl %0,0(%0)"						      \
+	  : "=a" (__offset) : : "cc" );					      \
+     (int *) (__builtin_thread_pointer() + __offset); })
+
+# ifdef PIC
+#  define TLS_IE(x) \
+  ({ unsigned long __offset;						      \
+     asm ("bras %0,1f\n"						      \
+	  "0:\t.long " #x "@gotntpoff\n"				      \
+	  "1:\tl %0,0(%0)\n\t"						      \
+	  "l %0,0(%0,%%r12):tls_load:" #x				      \
+	  : "=&a" (__offset) : : "cc" );				      \
+     (int *) (__builtin_thread_pointer() + __offset); })
+# else
+#  define TLS_IE(x) \
+  ({ unsigned long  __offset;						      \
+     asm ("bras %0,1f\n"						      \
+	  "0:\t.long " #x "@indntpoff\n"				      \
+	  "1:\t l %0,0(%0)\n\t"						      \
+	  "l %0,0(%0):tls_load:" #x					      \
+	  : "=&a" (__offset) : : "cc" );				      \
+     (int *) (__builtin_thread_pointer() + __offset); })
+# endif
+
+# ifdef PIC
+#  define TLS_LD(x) \
+  ({ unsigned long __offset, __save12;					      \
+     asm ("bras %0,1f\n"						      \
+	  "0:\t.long _GLOBAL_OFFSET_TABLE_-0b\n\t"			      \
+	  ".long __tls_get_offset@plt-0b\n\t"				      \
+	  ".long " #x "@tlsldm\n\t"					      \
+	  ".long " #x "@dtpoff\n"					      \
+	  "1:\tlr %1,%%r12\n\t"						      \
+          "l %%r12,0(%0)\n\t"						      \
+          "la %%r12,0(%%r12,%0)\n\t"					      \
+	  "l %%r1,4(%0)\n\t"						      \
+	  "l %%r2,8(%0)\n\t"						      \
+	  "bas %%r14,0(%%r1,%0):tls_ldcall:" #x "\n\t"			      \
+	  "l %0,12(%0)\n\t"						      \
+	  "alr %0,%%r2\n\t"						      \
+          "lr %%r12,%1"							      \
+	  : "=&a" (__offset), "=&a" (__save12)				      \
+          : : "cc", "0", "1", "2", "3", "4", "5" );			      \
+     (int *) (__builtin_thread_pointer() + __offset); })
+# else
+#  define TLS_LD(x) \
+  ({ unsigned long __offset;						      \
+     asm ("bras %0,1f\n"						      \
+	  "0:\t.long _GLOBAL_OFFSET_TABLE_\n\t"				      \
+	  ".long __tls_get_offset@plt\n\t"				      \
+	  ".long " #x "@tlsldm\n\t"					      \
+	  ".long " #x "@dtpoff\n"					      \
+	  "1:\tl %%r12,0(%0)\n\t"					      \
+	  "l %%r1,4(%0)\n\t"						      \
+	  "l %%r2,8(%0)\n\t"						      \
+	  "bas %%r14,0(%%r1):tls_ldcall:" #x "\n\t"			      \
+	  "l %0,12(%0)\n\t"						      \
+	  "alr %0,%%r2"							      \
+	  : "=&a" (__offset) : : "cc", "0", "1", "2", "3", "4", "5", "12" );  \
+     (int *) (__builtin_thread_pointer() + __offset); })
+# endif
+
+# ifdef PIC
+#  define TLS_GD(x) \
+  ({ unsigned long __offset, __save12;					      \
+     asm ("bras %0,1f\n"						      \
+	  "0:\t.long _GLOBAL_OFFSET_TABLE_-0b\n\t"			      \
+	  ".long __tls_get_offset@plt-0b\n\t"				      \
+	  ".long " #x "@tlsgd\n"					      \
+	  "1:\tlr %1,%%r12\n\t"						      \
+          "l %%r12,0(%0)\n\t"						      \
+          "la %%r12,0(%%r12,%0)\n\t"					      \
+	  "l %%r1,4(%0)\n\t"						      \
+	  "l %%r2,8(%0)\n\t"						      \
+	  "bas %%r14,0(%%r1,%0):tls_gdcall:" #x "\n\t"			      \
+          "lr %0,%%r2\n\t"						      \
+          "lr %%r12,%1"							      \
+	  : "=&a" (__offset), "=&a" (__save12)				      \
+          : : "cc", "0", "1", "2", "3", "4", "5" );			      \
+     (int *) (__builtin_thread_pointer() + __offset); })
+# else
+#  define TLS_GD(x) \
+  ({ unsigned long __offset;						      \
+     asm ("bras %0,1f\n"						      \
+	  "0:\t.long _GLOBAL_OFFSET_TABLE_\n\t"				      \
+	  ".long __tls_get_offset@plt\n\t"				      \
+	  ".long " #x "@tlsgd\n"					      \
+	  "1:\tl %%r12,0(%0)\n\t"					      \
+	  "l %%r1,4(%0)\n\t"						      \
+	  "l %%r2,8(%0)\n\t"						      \
+	  "bas %%r14,0(%%r1):tls_gdcall:" #x "\n\t"			      \
+          "lr %0,%%r2"							      \
+	  : "=&a" (__offset) : : "cc", "0", "1", "2", "3", "4", "5", "12" );  \
+     (int *) (__builtin_thread_pointer() + __offset); })
+# endif
+
 #else
 # error "No support for this architecture so far."
 #endif
diff --git a/linuxthreads/ChangeLog b/linuxthreads/ChangeLog
index 2949321c65..80de0debab 100644
--- a/linuxthreads/ChangeLog
+++ b/linuxthreads/ChangeLog
@@ -1,3 +1,17 @@
+2003-01-27  Martin Schwidefsky  <schwidefsky@de.ibm.com>
+
+	* sysdeps/s390/s390-32/pt-machine.h (THREAD_SELF, INIT_THREAD_SELF):
+	Define TLS versions.
+	* sysdeps/s390/s390-64/pt-machine.h (THREAD_SELF, INIT_THREAD_SELF):
+	Likewise.
+	* sysdeps/s390/tls.h [HAVE_TLS_SUPPORT]	(USE_TLS, TLS_INIT_TCB_SIZE,
+	TLS_INIT_TCB_ALIGN, TLS_TCB_SIZE, TLS_TCB_ALIGN, TLS_TCB_AT_TP,
+	INSTALL_DTV, INSTALL_NEW_DTV, GET_DTV, TLS_INIT_TP, THREAD_DTV):
+	Define.
+	* sysdeps/unix/sysv/linux/s390/s390-64/sysdep-cancel.h (PSEUDO): Use
+	branch with 32 bit offset.
+	* sysdeps/unix/sysv/linux/s390/s390-64/vfork.S: Likewise.
+
 2003-01-24  Jakub Jelinek  <jakub@redhat.com>
 
 	* sysdeps/sparc/sparc32/pt-machine.h (__thread_self): Change to %g7,
diff --git a/linuxthreads/sysdeps/s390/s390-32/pt-machine.h b/linuxthreads/sysdeps/s390/s390-32/pt-machine.h
index 18b5919544..ee35320059 100644
--- a/linuxthreads/sysdeps/s390/s390-32/pt-machine.h
+++ b/linuxthreads/sysdeps/s390/s390-32/pt-machine.h
@@ -58,6 +58,13 @@ testandset (int *spinlock)
 #define CURRENT_STACK_FRAME  stack_pointer
 register char * stack_pointer __asm__ ("15");
 
+#ifdef USE_TLS
+/* Return the thread descriptor for the current thread.  */
+# define THREAD_SELF ((pthread_descr) __builtin_thread_pointer ())
+
+/* Initialize the thread-unique value.  */
+#define INIT_THREAD_SELF(descr, nr) __builtin_set_thread_pointer (descr)
+#else
 /* Return the thread descriptor for the current thread.
    S/390 registers uses access register 0 as "thread register".  */
 #define THREAD_SELF  ({                                                       \
@@ -70,6 +77,7 @@ register char * stack_pointer __asm__ ("15");
 #define INIT_THREAD_SELF(descr, nr)  ({                                       \
   __asm__ ("sar %%a0,%0" : : "d" (descr) );                                   \
 })
+#endif
 
 /* Access to data in the thread descriptor is easy.  */
 #define THREAD_GETMEM(descr, member) THREAD_SELF->member
diff --git a/linuxthreads/sysdeps/s390/s390-64/pt-machine.h b/linuxthreads/sysdeps/s390/s390-64/pt-machine.h
index 92ebe1ecc4..c0f30789c2 100644
--- a/linuxthreads/sysdeps/s390/s390-64/pt-machine.h
+++ b/linuxthreads/sysdeps/s390/s390-64/pt-machine.h
@@ -58,6 +58,13 @@ testandset (int *spinlock)
 #define CURRENT_STACK_FRAME  stack_pointer
 register char * stack_pointer __asm__ ("15");
 
+#ifdef USE_TLS
+/* Return the thread descriptor for the current thread.  */
+# define THREAD_SELF ((pthread_descr) __builtin_thread_pointer ())
+
+/* Initialize the thread-unique value.  */
+#define INIT_THREAD_SELF(descr, nr) __builtin_set_thread_pointer (descr)
+#else
 /* Return the thread descriptor for the current thread.
    64 bit S/390 uses access register 0 and 1 as "thread register".  */
 #define THREAD_SELF  ({                                                       \
@@ -76,6 +83,7 @@ register char * stack_pointer __asm__ ("15");
            "   sar  %%a0,0\n"                                                 \
            : : "d" (descr) : "0" );                                           \
 })
+#endif
 
 /* Access to data in the thread descriptor is easy.  */
 #define THREAD_GETMEM(descr, member) THREAD_SELF->member
diff --git a/linuxthreads/sysdeps/s390/tls.h b/linuxthreads/sysdeps/s390/tls.h
index d872bb7553..11f31ba88a 100644
--- a/linuxthreads/sysdeps/s390/tls.h
+++ b/linuxthreads/sysdeps/s390/tls.h
@@ -45,19 +45,96 @@ typedef struct
 # include <tcb-offsets.h>
 #endif /* __ASSEMBLER__ */
 
-#undef USE_TLS
+/* TLS is always supported if the tools support it.  There are no
+   kernel dependencies.  To avoid bothering with the TLS support code
+   at all, use configure --without-tls.
 
-#if USE_TLS
+   We need USE_TLS to be consistently defined, for ldsodefs.h
+   conditionals.  */
 
-#else
+#ifdef HAVE_TLS_SUPPORT
 
-#define NONTLS_INIT_TP \
+/* Signal that TLS support is available.  */
+# define USE_TLS	1
+
+# ifndef __ASSEMBLER__
+/* Get system call information.  */
+#  include <sysdep.h>
+
+
+/* Get the thread descriptor definition.  */
+#  include <linuxthreads/descr.h>
+
+/* This is the size of the initial TCB.  */
+#  define TLS_INIT_TCB_SIZE sizeof (tcbhead_t)
+
+/* Alignment requirements for the initial TCB.  */
+#  define TLS_INIT_TCB_ALIGN __alignof__ (tcbhead_t)
+
+/* This is the size of the TCB.  */
+#  define TLS_TCB_SIZE sizeof (struct _pthread_descr_struct)
+
+/* Alignment requirements for the TCB.  */
+#  define TLS_TCB_ALIGN __alignof__ (struct _pthread_descr_struct)
+
+/* The TCB can have any size and the memory following the address the
+   thread pointer points to is unspecified.  Allocate the TCB there.  */
+#  define TLS_TCB_AT_TP	1
+
+
+/* Install the dtv pointer.  The pointer passed is to the element with
+   index -1 which contain the length.  */
+#  define INSTALL_DTV(descr, dtvp) \
+  ((tcbhead_t *) (descr))->dtv = (dtvp) + 1
+
+/* Install new dtv for current thread.  */
+#  define INSTALL_NEW_DTV(dtv) \
+  (((tcbhead_t *) __builtin_thread_pointer ())->dtv = (dtv))
+
+/* Return dtv of given thread descriptor.  */
+#  define GET_DTV(descr) \
+  (((tcbhead_t *) (descr))->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.
+
+   The value of this macro is null if successful, or an error string.  */
+#  define TLS_INIT_TP(descr, secondcall)				      \
+  ({									      \
+    void *_descr = (descr);						      \
+    tcbhead_t *head = _descr;						      \
+									      \
+    head->tcb = _descr;							      \
+    /* For now the thread descriptor is at the same address.  */	      \
+    head->self = _descr;						      \
+									      \
+    __builtin_set_thread_pointer (_descr);				      \
+    0;									      \
+  })
+
+/* Return the address of the dtv for the current thread.  */
+#  define THREAD_DTV() \
+  (((tcbhead_t *) __builtin_thread_pointer ())->dtv)
+
+# endif /* __ASSEMBLER__ */
+
+#else	/* HAVE_TLS_SUPPORT && (FLOATING_STACKS || !IS_IN_libpthread) */
+
+# ifndef __ASSEMBLER__
+
+/* Get the thread descriptor definition.  */
+#  include <linuxthreads/descr.h>
+
+#  define NONTLS_INIT_TP \
   do { 								\
     static const tcbhead_t nontls_init_tp			\
       = { .multiple_threads = 0 };				\
     INIT_THREAD_SELF (&nontls_init_tp, 0);			\
   } while (0)
 
-#endif /* USE_TLS */
+# endif /* __ASSEMBLER__ */
+
+#endif	/* HAVE_TLS_SUPPORT && (FLOATING_STACKS || !IS_IN_libpthread) */
 
 #endif	/* tls.h */
diff --git a/linuxthreads/sysdeps/unix/sysv/linux/s390/s390-64/sysdep-cancel.h b/linuxthreads/sysdeps/unix/sysv/linux/s390/s390-64/sysdep-cancel.h
index ce4afb1d7b..4e54e550c2 100644
--- a/linuxthreads/sysdeps/unix/sysv/linux/s390/s390-64/sysdep-cancel.h
+++ b/linuxthreads/sysdeps/unix/sysv/linux/s390/s390-64/sysdep-cancel.h
@@ -51,7 +51,7 @@ ENTRY(name)								      \
 L(pseudo_check):							      \
 	lghi	%r4,-4095;						      \
 	clgr	%r2,%r4;						      \
-	jnl	SYSCALL_ERROR_LABEL;					      \
+	jgnl	SYSCALL_ERROR_LABEL;					      \
 L(pseudo_end):
 
 # ifdef IS_IN_libpthread
diff --git a/linuxthreads/sysdeps/unix/sysv/linux/s390/s390-64/vfork.S b/linuxthreads/sysdeps/unix/sysv/linux/s390/s390-64/vfork.S
index e01d4389d3..efc9710d7b 100644
--- a/linuxthreads/sysdeps/unix/sysv/linux/s390/s390-64/vfork.S
+++ b/linuxthreads/sysdeps/unix/sysv/linux/s390/s390-64/vfork.S
@@ -36,7 +36,7 @@ ENTRY (__vfork)
 	/* Check for error.  */
 	lghi	%r4,-4095
 	clgr	%r2,%r4
-	jnl	SYSCALL_ERROR_LABEL
+	jgnl	SYSCALL_ERROR_LABEL
 
 	/* Normal return.  */
 	br	%r14
diff --git a/nptl/ChangeLog b/nptl/ChangeLog
index 9f5080b8aa..1f293f505a 100644
--- a/nptl/ChangeLog
+++ b/nptl/ChangeLog
@@ -1,3 +1,10 @@
+2003-01-28  Ulrich Drepper  <drepper@redhat.com>
+
+	* sysdeps/unix/sysv/linux/i386/createthread.c: Define
+	PREPARE_CREATE and TLS_VALUE with x86-specific bits.  All the rest
+	of the code is moved to ...
+	* sysdeps/pthread/createthread.c: ...here.  New file.
+
 2003-01-27  Ulrich Drepper  <drepper@redhat.com>
 
 	* sysdeps/unix/sysv/linux/i386/i486/lowlevelsem.S
diff --git a/nptl/sysdeps/pthread/createthread.c b/nptl/sysdeps/pthread/createthread.c
new file mode 100644
index 0000000000..b6898368ef
--- /dev/null
+++ b/nptl/sysdeps/pthread/createthread.c
@@ -0,0 +1,145 @@
+/* Copyright (C) 2002, 2003 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+   Contributed by Ulrich Drepper <drepper@redhat.com>, 2002.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, write to the Free
+   Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+   02111-1307 USA.  */
+
+#include <sched.h>
+#include <setjmp.h>
+#include <signal.h>
+#include <stdlib.h>
+#include <atomic.h>
+#include <ldsodefs.h>
+#include <tls.h>
+
+
+#define CLONE_SIGNAL    	(CLONE_SIGHAND | CLONE_THREAD)
+
+/* Unless otherwise specified, the thread "register" is going to be
+   initialized with a pointer to the TCB.  */
+#ifndef TLS_VALUE
+# define TLS_VALUE pd
+#endif
+
+
+static int
+create_thread (struct pthread *pd, STACK_VARIABLES_PARMS)
+{
+#ifdef PREPARE_CREATE
+  PREPARE_CREATE;
+#endif
+
+  assert (pd->header.data.tcb != NULL);
+
+
+  if (__builtin_expect (THREAD_GETMEM (THREAD_SELF, report_events), 0))
+    {
+      /* The parent thread is supposed to report events.  Check whether
+	 the TD_CREATE event is needed, too.  */
+      const int _idx = __td_eventword (TD_CREATE);
+      const uint32_t _mask = __td_eventmask (TD_CREATE);
+
+      if ((_mask & (__nptl_threads_events.event_bits[_idx]
+		    | pd->eventbuf.eventmask.event_bits[_idx])) != 0)
+	{
+	  /* We have to report the new thread.  Make sure the thread
+	     does not run far by forcing it to get a lock.  We lock it
+	     here too so that the new thread cannot continue until we
+	     tell it to.  */
+	  lll_lock (pd->lock);
+
+	  /* Create the thread.  */
+	  if (__clone (start_thread_debug, STACK_VARIABLES_ARGS,
+		       CLONE_VM | CLONE_FS | CLONE_FILES | CLONE_SIGNAL |
+		       CLONE_SETTLS | CLONE_PARENT_SETTID |
+		       CLONE_CHILD_CLEARTID | CLONE_DETACHED | 0,
+		       pd, &pd->tid, TLS_VALUE, &pd->tid) == -1)
+	    /* Failed.  */
+	    return errno;
+
+	  /* We now have for sure more than one thread.  */
+	  pd->header.data.multiple_threads = 1;
+
+	  /* Now fill in the information about the new thread in
+	     the newly created thread's data structure.  We cannot let
+	     the new thread do this since we don't know whether it was
+	     already scheduled when we send the event.  */
+	  pd->eventbuf.eventnum = TD_CREATE;
+	  pd->eventbuf.eventdata = pd;
+
+	  /* Enqueue the descriptor.  */
+	  do
+	    pd->nextevent = __nptl_last_event;
+	  while (atomic_compare_and_exchange_acq (&__nptl_last_event, pd,
+						  pd->nextevent) != 0);
+
+	  /* Now call the function which signals the event.  */
+	  __nptl_create_event ();
+
+	  /* And finally restart the new thread.  */
+	  lll_unlock (pd->lock);
+
+	  return 0;
+	}
+    }
+
+#ifdef NEED_DL_SYSINFO
+  assert (THREAD_GETMEM (THREAD_SELF, header.data.sysinfo)
+	  == pd->header.data.sysinfo);
+#endif
+
+  /* We rely heavily on various flags the CLONE function understands:
+
+     CLONE_VM, CLONE_FS, CLONE_FILES
+	These flags select semantics with shared address space and
+	file descriptors according to what POSIX requires.
+
+     CLONE_SIGNAL
+	This flag selects the POSIX signal semantics.
+
+     CLONE_SETTLS
+	The sixth parameter to CLONE determines the TLS area for the
+	new thread.
+
+     CLONE_PARENT_SETTID
+	The kernels writes the thread ID of the newly created thread
+	into the location pointed to by the fifth parameters to CLONE.
+
+	Note that it would be semantically equivalent to use
+	CLONE_CHILD_SETTID but it is be more expensive in the kernel.
+
+     CLONE_CHILD_CLEARTID
+	The kernels clears the thread ID of a thread that has called
+	sys_exit() - using the same parameter as CLONE_SETTID.
+
+     CLONE_DETACHED
+	No signal is generated if the thread exists and it is
+	automatically reaped.
+
+     The termination signal is chosen to be zero which means no signal
+     is sent.  */
+  if (__clone (start_thread, STACK_VARIABLES_ARGS,
+	       CLONE_VM | CLONE_FS | CLONE_FILES | CLONE_SIGNAL |
+	       CLONE_SETTLS | CLONE_PARENT_SETTID | CLONE_CHILD_CLEARTID |
+	       CLONE_DETACHED | 0, pd, &pd->tid, TLS_VALUE, &pd->tid) == -1)
+    /* Failed.  */
+    return errno;
+
+  /* We now have for sure more than one thread.  */
+  THREAD_SETMEM (THREAD_SELF, header.data.multiple_threads, 1);
+
+  return 0;
+}
diff --git a/nptl/sysdeps/unix/sysv/linux/i386/createthread.c b/nptl/sysdeps/unix/sysv/linux/i386/createthread.c
index def163350f..37e3d94996 100644
--- a/nptl/sysdeps/unix/sysv/linux/i386/createthread.c
+++ b/nptl/sysdeps/unix/sysv/linux/i386/createthread.c
@@ -1,4 +1,4 @@
-/* Copyright (C) 2002 Free Software Foundation, Inc.
+/* Copyright (C) 2002, 2003 Free Software Foundation, Inc.
    This file is part of the GNU C Library.
    Contributed by Ulrich Drepper <drepper@redhat.com>, 2002.
 
@@ -17,141 +17,33 @@
    Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
    02111-1307 USA.  */
 
-#include <sched.h>
-#include <setjmp.h>
-#include <signal.h>
-#include <stdlib.h>
-#include <atomic.h>
-#include <ldsodefs.h>
-#include <tls.h>
-
-
-#define CLONE_SIGNAL    	(CLONE_SIGHAND | CLONE_THREAD)
-
-
-static int
-create_thread (struct pthread *pd, STACK_VARIABLES_PARMS)
-{
-  union user_desc_init desc;
-
-  /* Describe the thread-local storage segment.  */
-
-  /* The 'entry_number' field.  The first three bits of the segment
-     register value select the GDT, ignore them.  We get the index
-     from the value of the %gs register in the current thread.  */
-  desc.vals[0] = TLS_GET_GS () >> 3;
-  /* The 'base_addr' field.  Pointer to the TCB.  */
-  desc.vals[1] = (unsigned long int) pd;
-  /* The 'limit' field.  We use 4GB which is 0xfffff pages.  */
-  desc.vals[2] = 0xfffff;
-  /* Collapsed value of the bitfield:
-       .seg_32bit = 1
-       .contents = 0
-       .read_exec_only = 0
-       .limit_in_pages = 1
-       .seg_not_present = 0
-       .useable = 1 */
-  desc.vals[3] = 0x51;
-
-
-  assert (pd->header.data.tcb != NULL);
-
-
-  if (__builtin_expect (THREAD_GETMEM (THREAD_SELF, report_events), 0))
-    {
-      /* The parent thread is supposed to report events.  Check whether
-	 the TD_CREATE event is needed, too.  */
-      const int _idx = __td_eventword (TD_CREATE);
-      const uint32_t _mask = __td_eventmask (TD_CREATE);
-
-      if ((_mask & (__nptl_threads_events.event_bits[_idx]
-		    | pd->eventbuf.eventmask.event_bits[_idx])) != 0)
-	{
-	  /* We have to report the new thread.  Make sure the thread
-	     does not run far by forcing it to get a lock.  We lock it
-	     here too so that the new thread cannot continue until we
-	     tell it to.  */
-	  lll_lock (pd->lock);
-
-	  /* Create the thread.  */
-	  if (__clone (start_thread_debug, STACK_VARIABLES_ARGS,
-		       CLONE_VM | CLONE_FS | CLONE_FILES | CLONE_SIGNAL |
-		       CLONE_SETTLS | CLONE_PARENT_SETTID |
-		       CLONE_CHILD_CLEARTID | CLONE_DETACHED | 0,
-		       pd, &pd->tid, &desc.desc, &pd->tid) == -1)
-	    /* Failed.  */
-	    return errno;
-
-	  /* We now have for sure more than one thread.  */
-	  pd->header.data.multiple_threads = 1;
-
-	  /* Now fill in the information about the new thread in
-	     the newly created thread's data structure.  We cannot let
-	     the new thread do this since we don't know whether it was
-	     already scheduled when we send the event.  */
-	  pd->eventbuf.eventnum = TD_CREATE;
-	  pd->eventbuf.eventdata = pd;
-
-	  /* Enqueue the descriptor.  */
-	  do
-	    pd->nextevent = __nptl_last_event;
-	  while (atomic_compare_and_exchange_acq (&__nptl_last_event, pd,
-						  pd->nextevent) != 0);
-
-	  /* Now call the function which signals the event.  */
-	  __nptl_create_event ();
-
-	  /* And finally restart the new thread.  */
-	  lll_unlock (pd->lock);
-
-	  return 0;
-	}
-    }
-
-#ifdef NEED_DL_SYSINFO
-  assert (THREAD_GETMEM (THREAD_SELF, header.data.sysinfo)
-	  == pd->header.data.sysinfo);
-#endif
-
-  /* We rely heavily on various flags the CLONE function understands:
-
-     CLONE_VM, CLONE_FS, CLONE_FILES
-	These flags select semantics with shared address space and
-	file descriptors according to what POSIX requires.
-
-     CLONE_SIGNAL
-	This flag selects the POSIX signal semantics.
-
-     CLONE_SETTLS
-	The sixth parameter to CLONE determines the TLS area for the
-	new thread.
-
-     CLONE_PARENT_SETTID
-	The kernels writes the thread ID of the newly created thread
-	into the location pointed to by the fifth parameters to CLONE.
-
-	Note that it would be semantically equivalent to use
-	CLONE_CHILD_SETTID but it is be more expensive in the kernel.
-
-     CLONE_CHILD_CLEARTID
-	The kernels clears the thread ID of a thread that has called
-	sys_exit() - using the same parameter as CLONE_SETTID.
-
-     CLONE_DETACHED
-	No signal is generated if the thread exists and it is
-	automatically reaped.
-
-     The termination signal is chosen to be zero which means no signal
-     is sent.  */
-  if (__clone (start_thread, STACK_VARIABLES_ARGS,
-	       CLONE_VM | CLONE_FS | CLONE_FILES | CLONE_SIGNAL |
-	       CLONE_SETTLS | CLONE_PARENT_SETTID | CLONE_CHILD_CLEARTID |
-	       CLONE_DETACHED | 0, pd, &pd->tid, &desc.desc, &pd->tid) == -1)
-    /* Failed.  */
-    return errno;
-
-  /* We now have for sure more than one thread.  */
-  THREAD_SETMEM (THREAD_SELF, header.data.multiple_threads, 1);
-
-  return 0;
-}
+/* The "thread register" gets initialized from a segment descriptor.
+   Initialize such a descriptor first.  */
+#define PREPARE_CREATE \
+  union user_desc_init desc;						      \
+									      \
+  /* Describe the thread-local storage segment.  */			      \
+									      \
+  /* The 'entry_number' field.  The first three bits of the segment	      \
+     register value select the GDT, ignore them.  We get the index	      \
+     from the value of the %gs register in the current thread.  */	      \
+  desc.vals[0] = TLS_GET_GS () >> 3;					      \
+  /* The 'base_addr' field.  Pointer to the TCB.  */			      \
+  desc.vals[1] = (unsigned long int) pd;				      \
+  /* The 'limit' field.  We use 4GB which is 0xfffff pages.  */		      \
+  desc.vals[2] = 0xfffff;						      \
+  /* Collapsed value of the bitfield:					      \
+       .seg_32bit = 1							      \
+       .contents = 0							      \
+       .read_exec_only = 0						      \
+       .limit_in_pages = 1						      \
+       .seg_not_present = 0						      \
+       .useable = 1 */							      \
+  desc.vals[3] = 0x51
+
+/* Value passed to 'clone' for initialization of the thread register.  */
+#define TLS_VALUE &desc.desc
+
+
+/* Get the real implementation.  */
+#include <nptl/sysdeps/pthread/createthread.c>
diff --git a/sysdeps/s390/Versions b/sysdeps/s390/Versions
new file mode 100644
index 0000000000..7136af2760
--- /dev/null
+++ b/sysdeps/s390/Versions
@@ -0,0 +1,6 @@
+ld {
+  GLIBC_2.3 {
+    # runtime interface to TLS
+    __tls_get_offset;
+  }
+}
\ No newline at end of file
diff --git a/sysdeps/s390/dl-tls.h b/sysdeps/s390/dl-tls.h
new file mode 100644
index 0000000000..fbd85c1753
--- /dev/null
+++ b/sysdeps/s390/dl-tls.h
@@ -0,0 +1,73 @@
+/* Thread-local storage handling in the ELF dynamic linker.  s390 version.
+   Copyright (C) 2003 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;
+
+
+#ifdef SHARED
+/* This is the prototype for the GNU version.  */
+extern void *__tls_get_addr (tls_index *ti) attribute_hidden;
+extern unsigned long __tls_get_offset (unsigned long got_offset);
+
+/* The special thing about the s390 TLS ABI is that we do not have the
+   standard __tls_get_addr function but the __tls_get_offset function
+   which differs in two important aspects:
+   1) __tls_get_offset gets a got offset instead of a pointer to the
+      tls_index structure
+   2) __tls_get_offset returns the offset of the requested variable to
+      the thread descriptor instead of a pointer to the variable.
+ */
+#if defined __s390x__
+asm("\n\
+	.text\n\
+	.globl __tls_get_offset\n\
+	.type __tls_get_offset, @function\n\
+	.align 4\n\
+__tls_get_offset:\n\
+	la	%r2,0(%r2,%r12)\n\
+	jg	__tls_get_addr\n\
+");
+#elif defined __s390__
+asm("\n\
+	.text\n\
+	.globl __tls_get_offset\n\
+	.type __tls_get_offset, @function\n\
+	.align 4\n\
+__tls_get_offset:\n\
+	basr	%r3,0\n\
+0:	la	%r2,0(%r2,%r12)\n\
+	l	%r4,1f-0b(%r3)\n\
+	b	0(%r4,%r3)\n\
+1:	.long	__tls_get_addr - 0b\n\
+");
+#endif
+
+#define GET_ADDR_OFFSET \
+  (ti->ti_offset - (unsigned long) __builtin_thread_pointer ())
+
+#define __TLS_GET_ADDR(__ti) \
+  (__tls_get_addr(__ti) + (unsigned long) __builtin_thread_pointer ())
+
+#endif
diff --git a/sysdeps/s390/libc-tls.c b/sysdeps/s390/libc-tls.c
new file mode 100644
index 0000000000..bc433f8f6f
--- /dev/null
+++ b/sysdeps/s390/libc-tls.c
@@ -0,0 +1,37 @@
+/* Thread-local storage handling in the ELF dynamic linker.  IA-64 version.
+   Copyright (C) 2003 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>
+
+#if USE_TLS
+
+/* On s390, the literal pool entry that refers to __tls_get_offset
+   is not removed, even if all branches that use the literal pool
+   entry gets removed by TLS optimizations. To get binaries
+   statically linked __tls_get_offset is defined here but
+   aborts if it is used.  */
+
+void *
+__tls_get_offset (size_t m, size_t offset)
+{
+  abort ();
+}
+
+#endif
+
diff --git a/sysdeps/s390/s390-32/dl-machine.h b/sysdeps/s390/s390-32/dl-machine.h
index 557c04e4ea..6905814c41 100644
--- a/sysdeps/s390/s390-32/dl-machine.h
+++ b/sysdeps/s390/s390-32/dl-machine.h
@@ -1,5 +1,5 @@
 /* Machine-dependent ELF dynamic relocation inline functions.  S390 Version.
-   Copyright (C) 2000, 2001, 2002 Free Software Foundation, Inc.
+   Copyright (C) 2000, 2001, 2002, 2003 Free Software Foundation, Inc.
    Contributed by Carl Pederson & Martin Schwidefsky.
    This file is part of the GNU C Library.
 
@@ -312,13 +312,22 @@ _dl_start_user:\n\
 #define RTLD_START_SPECIAL_INIT /* nothing */
 #endif
 
-/* ELF_RTYPE_CLASS_PLT iff TYPE describes relocation of a PLT entry, so
-   PLT entries should not be allowed to define the value.
+/* ELF_RTYPE_CLASS_PLT iff TYPE describes relocation of a PLT entry or
+   TLS variable, so undefined references should not be allowed to
+   define the value.
    ELF_RTYPE_CLASS_NOCOPY iff TYPE should not be allowed to resolve to one
    of the main executable's symbols, as for a COPY reloc.  */
-#define elf_machine_type_class(type) \
+#ifdef USE_TLS
+# define elf_machine_type_class(type) \
+  ((((type) == R_390_JMP_SLOT || (type) == R_390_TLS_DTPMOD		      \
+     || (type) == R_390_TLS_DTPOFF || (type) == R_390_TLS_TPOFF)	      \
+    * ELF_RTYPE_CLASS_PLT)						      \
+   | (((type) == R_390_COPY) * ELF_RTYPE_CLASS_COPY))
+#else
+# define elf_machine_type_class(type) \
   ((((type) == R_390_JMP_SLOT) * ELF_RTYPE_CLASS_PLT)	\
    | (((type) == R_390_COPY) * ELF_RTYPE_CLASS_COPY))
+#endif
 
 /* A reloc type used for ld.so cmdline arg lookups to reject PLT entries.  */
 #define ELF_MACHINE_JMP_SLOT    R_390_JMP_SLOT
@@ -372,25 +381,90 @@ elf_machine_rela (struct link_map *map, const Elf32_Rela *reloc,
 {
   const unsigned int r_type = ELF32_R_TYPE (reloc->r_info);
 
+#if !defined RTLD_BOOTSTRAP || !defined HAVE_Z_COMBRELOC
   if (__builtin_expect (r_type == R_390_RELATIVE, 0))
-    *reloc_addr = map->l_addr + reloc->r_addend;
-#ifndef RTLD_BOOTSTRAP
-  else if (__builtin_expect (r_type == R_390_NONE, 0))
-    return;
+    {
+# if !defined RTLD_BOOTSTRAP && !defined HAVE_Z_COMBRELOC
+      /* This is defined in rtld.c, but nowhere in the static libc.a;
+	 make the reference weak so static programs can still link.
+	 This declaration cannot be done when compiling rtld.c
+	 (i.e. #ifdef RTLD_BOOTSTRAP) because rtld.c contains the
+	 common defn for _dl_rtld_map, which is incompatible with a
+	 weak decl in the same file.  */
+#  ifndef SHARED
+      weak_extern (GL(dl_rtld_map));
+#  endif
+      if (map != &GL(dl_rtld_map)) /* Already done in rtld itself.  */
+# endif
+	*reloc_addr = map->l_addr + reloc->r_addend;
+    }
+  else
 #endif
+  if (__builtin_expect (r_type == R_390_NONE, 0))
+    return;
   else
     {
       const Elf32_Sym *const refsym = sym;
+#if defined USE_TLS && !defined RTLD_BOOTSTRAP
+      struct link_map *sym_map = RESOLVE_MAP (&sym, version, r_type);
+      Elf32_Addr value = sym == NULL ? 0 : sym_map->l_addr + sym->st_value;
+#else
       Elf32_Addr value = RESOLVE (&sym, version, r_type);
+
+# ifndef RTLD_BOOTSTRAP
       if (sym)
+# endif
 	value += sym->st_value;
+#endif /* use TLS and !RTLD_BOOTSTRAP */
 
       switch (r_type)
 	{
 	case R_390_GLOB_DAT:
 	case R_390_JMP_SLOT:
-	  *reloc_addr = value;
+	  *reloc_addr = value + reloc->r_addend;
+	  break;
+
+#if defined USE_TLS && (!defined RTLD_BOOTSTRAP || USE___THREAD)
+	case R_390_TLS_DTPMOD:
+# ifdef RTLD_BOOTSTRAP
+	  /* During startup the dynamic linker is always the module
+	     with index 1.
+	     XXX If this relocation is necessary move before RESOLVE
+	     call.  */
+	  *reloc_addr = 1;
+# else
+	  /* Get the information from the link map returned by the
+	     resolv function.  */
+	  if (sym_map != NULL)
+	    *reloc_addr = sym_map->l_tls_modid;
+# endif
+	  break;
+	case R_390_TLS_DTPOFF:
+# ifndef RTLD_BOOTSTRAP
+	  /* During relocation all TLS symbols are defined and used.
+	     Therefore the offset is already correct.  */
+	  if (sym != NULL)
+	    *reloc_addr = sym->st_value + reloc->r_addend;
+# endif
 	  break;
+	case R_390_TLS_TPOFF:
+	  /* The offset is negative, forward from the thread pointer.  */
+# ifdef RTLD_BOOTSTRAP
+	  *reloc_addr = sym->st_value + reloc->r_addend - map->l_tls_offset;
+# else
+	  /* We know the offset of the object the symbol is contained in.
+	     It is a negative value which will be added to the
+	     thread pointer.  */
+	  if (sym != NULL)
+	    {
+	      CHECK_STATIC_TLS (map, sym_map);
+	      *reloc_addr = (sym->st_value + reloc->r_addend
+			     - sym_map->l_tls_offset);
+	    }
+#endif
+	  break;
+#endif  /* use TLS */
+
 #ifndef RTLD_BOOTSTRAP
 	case R_390_COPY:
 	  if (sym == NULL)
diff --git a/sysdeps/s390/s390-32/elf/configure b/sysdeps/s390/s390-32/elf/configure
new file mode 100644
index 0000000000..64eff1f05f
--- /dev/null
+++ b/sysdeps/s390/s390-32/elf/configure
@@ -0,0 +1,52 @@
+# This file is generated from configure.in by Autoconf.  DO NOT EDIT!
+ # Local configure fragment for sysdeps/s390/elf.
+
+if test "$usetls" != no; then
+# Check for support of thread-local storage handling in assembler and
+# linker.
+echo "$as_me:$LINENO: checking for s390 TLS support" >&5
+echo $ECHO_N "checking for s390 TLS support... $ECHO_C" >&6
+if test "${libc_cv_390_tls+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  cat > conftest.s <<\EOF
+	.section ".tdata", "awT", @progbits
+foo:	.long	25
+	.text
+	.long	foo@TLSGD
+	.long	foo@TLSLDM
+	.long	foo@DTPOFF
+	.long	foo@NTPOFF
+	.long	foo@GOTNTPOFF
+	.long	foo@INDNTPOFF
+	l	%r1,foo@GOTNTPOFF(%r12)
+	l	%r1,0(%r1):tls_load:foo
+	bas	%r14,0(%r1,%r13):tls_gdcall:foo
+	bas	%r14,0(%r1,%r13):tls_ldcall:foo
+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_390_tls=yes
+else
+  libc_cv_390_tls=no
+fi
+rm -f conftest*
+fi
+echo "$as_me:$LINENO: result: $libc_cv_390_tls" >&5
+echo "${ECHO_T}$libc_cv_390_tls" >&6
+if test $libc_cv_390_tls = yes; then
+  cat >>confdefs.h <<\_ACEOF
+#define HAVE_TLS_SUPPORT 1
+_ACEOF
+
+fi
+fi
+
+cat >>confdefs.h <<\_ACEOF
+#define PI_STATIC_AND_HIDDEN 1
+_ACEOF
+
diff --git a/sysdeps/s390/s390-32/elf/configure.in b/sysdeps/s390/s390-32/elf/configure.in
new file mode 100644
index 0000000000..fcf9329995
--- /dev/null
+++ b/sysdeps/s390/s390-32/elf/configure.in
@@ -0,0 +1,37 @@
+GLIBC_PROVIDES dnl See aclocal.m4 in the top level source directory.
+# Local configure fragment for sysdeps/s390/elf.
+
+if test "$usetls" != no; then
+# Check for support of thread-local storage handling in assembler and
+# linker.
+AC_CACHE_CHECK(for s390 TLS support, libc_cv_390_tls, [dnl
+cat > conftest.S <<\EOF
+	.section ".tdata", "awT", @progbits
+foo:	.long	25
+	.text
+	.long	foo@TLSGD
+	.long	foo@TLSLDM
+	.long	foo@DTPOFF
+	.long	foo@NTPOFF
+	.long	foo@GOTNTPOFF
+	.long	foo@INDNTPOFF
+	l	%r1,foo@GOTNTPOFF(%r12)
+	l	%r1,0(%r1):tls_load:foo
+	bas	%r14,0(%r1,%r13):tls_gdcall:foo
+	bas	%r14,0(%r1,%r13):tls_ldcall:foo
+EOF
+dnl
+if AC_TRY_COMMAND(${CC-cc} -S $CFLAGS conftest.S 1>&AS_MESSAGE_LOG_FD); then
+  libc_cv_390_tls=yes
+else
+  libc_cv_390_tls=no
+fi
+rm -f conftest*])
+if test $libc_cv_390_tls = yes; then
+  AC_DEFINE(HAVE_TLS_SUPPORT)
+fi
+fi
+
+dnl It is always possible to access static and hidden symbols in an
+dnl position independent way.
+AC_DEFINE(PI_STATIC_AND_HIDDEN)
diff --git a/sysdeps/s390/s390-64/dl-machine.h b/sysdeps/s390/s390-64/dl-machine.h
index a4033eeed1..e7ae3f77f8 100644
--- a/sysdeps/s390/s390-64/dl-machine.h
+++ b/sysdeps/s390/s390-64/dl-machine.h
@@ -1,6 +1,6 @@
 /* Machine-dependent ELF dynamic relocation inline functions.
    64 bit S/390 Version.
-   Copyright (C) 2001, 2002 Free Software Foundation, Inc.
+   Copyright (C) 2001, 2002, 2003 Free Software Foundation, Inc.
    Contributed by Martin Schwidefsky (schwidefsky@de.ibm.com).
    This file is part of the GNU C Library.
 
@@ -284,13 +284,22 @@ _dl_start_user:\n\
 #define RTLD_START_SPECIAL_INIT /* nothing */
 #endif
 
-/* ELF_RTYPE_CLASS_PLT iff TYPE describes relocation of a PLT entry, so
-   PLT entries should not be allowed to define the value.
+/* ELF_RTYPE_CLASS_PLT iff TYPE describes relocation of a PLT entry or
+   TLS variable, so undefined references should not be allowed to
+   define the value.
    ELF_RTYPE_CLASS_NOCOPY iff TYPE should not be allowed to resolve to one
    of the main executable's symbols, as for a COPY reloc.  */
-#define elf_machine_type_class(type) \
+#ifdef USE_TLS
+# define elf_machine_type_class(type) \
+  ((((type) == R_390_JMP_SLOT || (type) == R_390_TLS_DTPMOD		      \
+     || (type) == R_390_TLS_DTPOFF || (type) == R_390_TLS_TPOFF)	      \
+    * ELF_RTYPE_CLASS_PLT)						      \
+   | (((type) == R_390_COPY) * ELF_RTYPE_CLASS_COPY))
+#else
+# define elf_machine_type_class(type) \
   ((((type) == R_390_JMP_SLOT) * ELF_RTYPE_CLASS_PLT)	\
    | (((type) == R_390_COPY) * ELF_RTYPE_CLASS_COPY))
+#endif
 
 /* A reloc type used for ld.so cmdline arg lookups to reject PLT entries.  */
 #define ELF_MACHINE_JMP_SLOT	R_390_JMP_SLOT
@@ -340,18 +349,41 @@ elf_machine_rela (struct link_map *map, const Elf64_Rela *reloc,
 {
   const unsigned int r_type = ELF64_R_TYPE (reloc->r_info);
 
+#if !defined RTLD_BOOTSTRAP || !defined HAVE_Z_COMBRELOC
   if (__builtin_expect (r_type == R_390_RELATIVE, 0))
-    *reloc_addr = map->l_addr + reloc->r_addend;
-#ifndef RTLD_BOOTSTRAP
-  else if (__builtin_expect (r_type == R_390_NONE, 0))
-    return;
+    {
+# if !defined RTLD_BOOTSTRAP && !defined HAVE_Z_COMBRELOC
+      /* This is defined in rtld.c, but nowhere in the static libc.a;
+	 make the reference weak so static programs can still link.
+	 This declaration cannot be done when compiling rtld.c
+	 (i.e. #ifdef RTLD_BOOTSTRAP) because rtld.c contains the
+	 common defn for _dl_rtld_map, which is incompatible with a
+	 weak decl in the same file.  */
+#  ifndef SHARED
+      weak_extern (GL(dl_rtld_map));
+#  endif
+      if (map != &GL(dl_rtld_map)) /* Already done in rtld itself.  */
+# endif
+	*reloc_addr = map->l_addr + reloc->r_addend;
+    }
+  else
 #endif
+  if (__builtin_expect (r_type == R_390_NONE, 0))
+    return;
   else
     {
       const Elf64_Sym *const refsym = sym;
+#if defined USE_TLS && !defined RTLD_BOOTSTRAP
+      struct link_map *sym_map = RESOLVE_MAP (&sym, version, r_type);
+      Elf64_Addr value = sym == NULL ? 0 : sym_map->l_addr + sym->st_value;
+#else
       Elf64_Addr value = RESOLVE (&sym, version, r_type);
+
+# ifndef RTLD_BOOTSTRAP
       if (sym)
+# endif
 	value += sym->st_value;
+#endif /* use TLS and !RTLD_BOOTSTRAP */
 
       switch (r_type)
 	{
@@ -359,6 +391,48 @@ elf_machine_rela (struct link_map *map, const Elf64_Rela *reloc,
 	case R_390_JMP_SLOT:
 	  *reloc_addr = value + reloc->r_addend;
 	  break;
+
+#if defined USE_TLS && (!defined RTLD_BOOTSTRAP || USE___THREAD)
+	case R_390_TLS_DTPMOD:
+# ifdef RTLD_BOOTSTRAP
+	  /* During startup the dynamic linker is always the module
+	     with index 1.
+	     XXX If this relocation is necessary move before RESOLVE
+	     call.  */
+	  *reloc_addr = 1;
+# else
+	  /* Get the information from the link map returned by the
+	     resolv function.  */
+	  if (sym_map != NULL)
+	    *reloc_addr = sym_map->l_tls_modid;
+# endif
+	  break;
+	case R_390_TLS_DTPOFF:
+# ifndef RTLD_BOOTSTRAP
+	  /* During relocation all TLS symbols are defined and used.
+	     Therefore the offset is already correct.  */
+	  if (sym != NULL)
+	    *reloc_addr = sym->st_value + reloc->r_addend;
+# endif
+	  break;
+	case R_390_TLS_TPOFF:
+	  /* The offset is negative, forward from the thread pointer.  */
+# ifdef RTLD_BOOTSTRAP
+	  *reloc_addr = sym->st_value + reloc->r_addend - map->l_tls_offset;
+# else
+	  /* We know the offset of the object the symbol is contained in.
+	     It is a negative value which will be added to the
+	     thread pointer.  */
+	  if (sym != NULL)
+	    {
+	      CHECK_STATIC_TLS (map, sym_map);
+	      *reloc_addr = (sym->st_value + reloc->r_addend
+			     - sym_map->l_tls_offset);
+	    }
+#endif
+	  break;
+#endif  /* use TLS */
+
 #ifndef RTLD_BOOTSTRAP
 	case R_390_COPY:
 	  if (sym == NULL)
@@ -439,7 +513,7 @@ elf_machine_lazy_rel (struct link_map *map,
 		      Elf64_Addr l_addr, const Elf64_Rela *reloc)
 {
   Elf64_Addr *const reloc_addr = (void *) (l_addr + reloc->r_offset);
-  const unsigned int r_type = ELF32_R_TYPE (reloc->r_info);
+  const unsigned int r_type = ELF64_R_TYPE (reloc->r_info);
   /* Check for unexpected PLT reloc type.  */
   if (__builtin_expect (r_type == R_390_JMP_SLOT, 1))
     {
diff --git a/sysdeps/s390/s390-64/elf/configure b/sysdeps/s390/s390-64/elf/configure
new file mode 100644
index 0000000000..42fc6e6a5f
--- /dev/null
+++ b/sysdeps/s390/s390-64/elf/configure
@@ -0,0 +1,52 @@
+# This file is generated from configure.in by Autoconf.  DO NOT EDIT!
+ # Local configure fragment for sysdeps/s390/elf.
+
+if test "$usetls" != no; then
+# Check for support of thread-local storage handling in assembler and
+# linker.
+echo "$as_me:$LINENO: checking for s390 TLS support" >&5
+echo $ECHO_N "checking for s390 TLS support... $ECHO_C" >&6
+if test "${libc_cv_390_tls+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  cat > conftest.s <<\EOF
+	.section ".tdata", "awT", @progbits
+foo:	.long	25
+	.text
+	.quad	foo@TLSGD
+	.quad	foo@TLSLDM
+	.quad	foo@DTPOFF
+	.quad	foo@NTPOFF
+	.quad	foo@GOTNTPOFF
+	.quad	foo@INDNTPOFF
+	lg	%r1,foo@GOTNTPOFF(%r12)
+	lg	%r1,0(%r1):tls_load:foo
+	brasl	%r14,__tls_get_offset@plt:tls_gdcall:foo
+	brasl	%r14,__tls_get_offset@plt:tls_ldcall:foo
+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_390_tls=yes
+else
+  libc_cv_390_tls=no
+fi
+rm -f conftest*
+fi
+echo "$as_me:$LINENO: result: $libc_cv_390_tls" >&5
+echo "${ECHO_T}$libc_cv_390_tls" >&6
+if test $libc_cv_390_tls = yes; then
+  cat >>confdefs.h <<\_ACEOF
+#define HAVE_TLS_SUPPORT 1
+_ACEOF
+
+fi
+fi
+
+cat >>confdefs.h <<\_ACEOF
+#define PI_STATIC_AND_HIDDEN 1
+_ACEOF
+
diff --git a/sysdeps/s390/s390-64/elf/configure.in b/sysdeps/s390/s390-64/elf/configure.in
new file mode 100644
index 0000000000..ac953fcff3
--- /dev/null
+++ b/sysdeps/s390/s390-64/elf/configure.in
@@ -0,0 +1,37 @@
+GLIBC_PROVIDES dnl See aclocal.m4 in the top level source directory.
+# Local configure fragment for sysdeps/s390/elf.
+
+if test "$usetls" != no; then
+# Check for support of thread-local storage handling in assembler and
+# linker.
+AC_CACHE_CHECK(for s390 TLS support, libc_cv_390_tls, [dnl
+cat > conftest.S <<\EOF
+	.section ".tdata", "awT", @progbits
+foo:	.long	25
+	.text
+	.quad	foo@TLSGD
+	.quad	foo@TLSLDM
+	.quad	foo@DTPOFF
+	.quad	foo@NTPOFF
+	.quad	foo@GOTNTPOFF
+	.quad	foo@INDNTPOFF
+	lg	%r1,foo@GOTNTPOFF(%r12)
+	lg	%r1,0(%r1):tls_load:foo
+	brasl	%r14,__tls_get_offset@plt:tls_gdcall:foo
+	brasl	%r14,__tls_get_offset@plt:tls_ldcall:foo
+EOF
+dnl
+if AC_TRY_COMMAND(${CC-cc} -S $CFLAGS conftest.S 1>&AS_MESSAGE_LOG_FD); then
+  libc_cv_390_tls=yes
+else
+  libc_cv_390_tls=no
+fi
+rm -f conftest*])
+if test $libc_cv_390_tls = yes; then
+  AC_DEFINE(HAVE_TLS_SUPPORT)
+fi
+fi
+
+dnl It is always possible to access static and hidden symbols in an
+dnl position independent way.
+AC_DEFINE(PI_STATIC_AND_HIDDEN)
diff --git a/sysdeps/unix/sysv/linux/s390/s390-32/clone.S b/sysdeps/unix/sysv/linux/s390/s390-32/clone.S
index c93570ed72..23fb4647ee 100644
--- a/sysdeps/unix/sysv/linux/s390/s390-32/clone.S
+++ b/sysdeps/unix/sysv/linux/s390/s390-32/clone.S
@@ -24,20 +24,24 @@
 #define _ERRNO_H	1
 #include <bits/errno.h>
 
-/*int __clone(int (*fn)(void *arg), void *child_stack, int flags, void *arg);*/
-/* sys_clone(void *child_stack, unsigned long flags) */
+/* int __clone(int (*fn)(void *arg), void *child_stack, int flags, void *arg,
+               void *tls, pid_t *parent_tid, pid_t *child_tid);  */
+/* sys_clone  (void *child_stack, unsigned long flags,
+               pid_t *parent_tid, pid_t *child_tid, void *tls);  */
 
 	.text
 ENTRY(__clone)
 	/* Sanity check arguments & move registers */
+	lr	%r0,%r5			/* move *arg out of the way */
 	ltr     %r1,%r2			/* no NULL function pointers */
 	lhi     %r2,-EINVAL
 	jz      SYSCALL_ERROR_LABEL
 	ltr     %r3,%r3			/* no NULL stack pointers */
 	jz      SYSCALL_ERROR_LABEL
-	/* move child_stack and flags, then call SVC */
+	/* set up registers, then call SVC */
 	lr      %r2,%r3
 	lr      %r3,%r4
+	lm	%r4,%r5,96(%r15)
 	svc     SYS_ify(clone)
 	ltr     %r2,%r2			/* check return code */
 	jm      SYSCALL_ERROR_LABEL
@@ -45,10 +49,10 @@ ENTRY(__clone)
 	br      %r14
 
 thread_start:
-	/* fn is in gpr 1, arg in gpr 5 */
-	lr      %r2,%r5         /* set first parameter to void *arg */
-	sr      %r11,%r11	/* terminate the stack frame */
+	/* fn is in gpr 1, arg in gpr 0 */
+	lr      %r2,%r0         /* set first parameter to void *arg */
 	ahi     %r15,-96        /* make room on the stack for the save area */
+	xc	0(4,%r15),0(%r15)
 	basr    %r14,%r1        /* jump to fn */
 #ifdef PIC
 	basr    %r12,0
diff --git a/sysdeps/unix/sysv/linux/s390/s390-32/sysdep.S b/sysdeps/unix/sysv/linux/s390/s390-32/sysdep.S
index 98b00722d8..94d772f67e 100644
--- a/sysdeps/unix/sysv/linux/s390/s390-32/sysdep.S
+++ b/sysdeps/unix/sysv/linux/s390/s390-32/sysdep.S
@@ -1,4 +1,4 @@
-/* Copyright (C) 2000, 2001, 2002 Free Software Foundation, Inc.
+/* Copyright (C) 2000, 2001, 2002, 2003 Free Software Foundation, Inc.
    Contributed by Martin Schwidefsky (schwidefsky@de.ibm.com).
    This file is part of the GNU C Library.
 
@@ -31,59 +31,94 @@
 	.text
 ENTRY(__syscall_error)
 #ifndef PIC
-#ifndef _LIBC_REENTRANT
-	lcr     %r2,%r2
-	basr    %r1,0
-.L0:    l       %r1,.L1-.L0(%r1)
-	st      %r2,0(0,%r1)
-	lhi     %r2,-1
-	br      %r14
-.L1:    .long  errno
-#else
-	stm     %r11,%r15,44(%r15)
-	lr      %r0,%r15
-	ahi     %r15,-96
-	st      %r0,0(%r15)
-	lcr     %r11,%r2
-	basr    %r13,0
-.L0:	l       %r1,.L1-.L0(%r13)
-	basr    %r14,%r1
-	st      %r11,0(%r2)
-	lhi     %r2,-1
-	l       %r15,0(%r15)
-	lm      %r11,%r15,44(%r15)
-	br      %r14
-.L1:	.long  __errno_location
+# if USE___THREAD
+#  ifndef NOT_IN_libc
+#   define SYSCALL_ERROR_ERRNO __libc_errno
+#  else
+#   define SYSCALL_ERROR_ERRNO errno
+#  endif
+	basr	%r1,0
+0:	l	%r1,1f-0b(%r1)
+	ear	%r3,%a0
+	lcr	%r2,%r2
+	st	%r2,0(%r1,%r3)
+	lhi	%r2,-1
+	br	%r14
+1:	.long	SYSCALL_ERROR_ERRNO@ntpoff
+# elif !defined _LIBC_REENTRANT
+	basr	%r1,0
+0:	l	%r1,1f-0b(%r1)
+	lcr	%r2,%r2
+	st	%r2,0(%r1)
+	lhi	%r2,-1
+	br	%r14
+1:	.long  errno
+# else
+	stm	%r13,%r15,52(%r15)
+	lr	%r0,%r15
+	ahi	%r15,-96
+	lcr	%r13,%r2
+	st	%r0,0(%r15)
+	basr	%r1,0
+0:	l	%r1,1f-0b(%r1)
+	basr	%r14,%r1
+	st	%r13,0(%r2)
+	lm	%r13,%r15,148(%r15)
+	lhi	%r2,-1
+	br	%r14
+1:	.long  __errno_location
 #endif
 #else
-#ifndef _LIBC_REENTRANT
-	basr    %r1,0
-.L0:    al      %r1,.L1-.L0(%r1)
-	l       %r1,errno@GOT12(%r1)
-	lcr     %r2,%r2
-	st      %r2,0(0,%r1)
-	lhi     %r2,-1
-	br      %r14
-.L1:    .long   _GLOBAL_OFFSET_TABLE_-0b
-#else
-	stm     %r11,%r15,44(%r15)
-	lr      %r0,%r15
-	ahi     %r15,-96
-	st      %r0,0(%r15)
-	lcr     %r11,%r2
-	basr    %r13,0
-.L0:	l       %r12,.L1-.L0(%r13)
-	ar      %r12,%r13
-	l       %r14,.L2-.L0(%r13)
-	bas     %r14,0(%r14,%r13)
-	st      %r11,0(0,%r2)
-	lhi     %r2,-1
-	l       %r15,0(%r15)
-	lm      %r11,%r15,44(%r15)
-	br      %r14
-.L1:	.long _GLOBAL_OFFSET_TABLE_ - .L0
-.L2:    .long __errno_location@PLT - .L0
-#endif
+# if RTLD_PRIVATE_ERRNO
+	basr	%r1,0
+0:	al	%r1,1f-0b(%r1)
+	lcr	%r2,%r2
+	st	%r2,0(%r1)
+	lhi	%r2,-1
+	br	%r14
+1:	.long	errno - 0b
+# elif USE___THREAD
+#  ifndef NOT_IN_libc
+#   define SYSCALL_ERROR_ERRNO __libc_errno
+#  else
+#   define SYSCALL_ERROR_ERRNO errno
+#  endif
+	basr	%r1,0
+0:	al	%r1,1f-0b(%r1)
+	ear	%r3,%a0
+	l	%r1,SYSCALL_ERROR_ERRNO@gotntpoff(%r1)
+	lcr	%r2,%r2
+	st	%r2,0(%r1,%r3)
+	lhi	%r2,-1
+	br	%r14
+1:	.long	_GLOBAL_OFFSET_TABLE_-0b
+# elif !defined _LIBC_REENTRANT
+	basr	%r1,0
+0:	al	%r1,1f-0b(%r1)
+	l	%r1,errno@GOT(%r1)
+	lcr	%r2,%r2
+	st	%r2,0(0,%r1)
+	lhi	%r2,-1
+	br	%r14
+1:	.long	_GLOBAL_OFFSET_TABLE_-0b
+# else
+	stm	%r11,%r15,44(%r15)
+	lr	%r0,%r15
+	ahi	%r15,-96
+	lcr	%r11,%r2
+	st	%r0,0(%r15)
+	basr	%r13,0
+0:	l	%r12,1f-0b(%r13)
+	l	%r1,2f-0b(%r13)
+	la	%r12,0(%r12,%r13)
+	bas	%r14,0(%r1,%r13)
+	st	%r11,0(%r2)
+	lm	%r11,%r15,140(%r15)
+	lhi	%r2,-1
+	br	%r14
+1:	.long _GLOBAL_OFFSET_TABLE_-0b
+2:	.long __errno_location@PLT-0b
+# endif
 #endif
 
 END (__syscall_error)
diff --git a/sysdeps/unix/sysv/linux/s390/s390-32/sysdep.h b/sysdeps/unix/sysv/linux/s390/s390-32/sysdep.h
index e89e6a1425..f7bfb8dac1 100644
--- a/sysdeps/unix/sysv/linux/s390/s390-32/sysdep.h
+++ b/sysdeps/unix/sysv/linux/s390/s390-32/sysdep.h
@@ -45,8 +45,6 @@
    number.  Linus said he will make sure the no syscall returns a value
    in -1 .. -4095 as a valid result so we can savely test with -4095.  */
 
-#define SYSCALL_ERROR_LABEL 0f
-
 #undef PSEUDO
 #define	PSEUDO(name, syscall_name, args)				      \
   .text;                                                                      \
@@ -54,42 +52,70 @@
     DO_CALL (syscall_name, args);                                             \
     lhi  %r4,-4095 ;                                                          \
     clr  %r2,%r4 ;		                                              \
-    jnl  SYSCALL_ERROR_LABEL ;                                                \
-  L(pseudo_end):
+    jnl  SYSCALL_ERROR_LABEL
 
 #undef PSEUDO_END
 #define PSEUDO_END(name)						      \
   SYSCALL_ERROR_HANDLER;						      \
   END (name)
 
-#ifndef _LIBC_REENTRANT
 #ifndef PIC
-#define SYSCALL_ERROR_HANDLER                                                 \
-0:  lcr     %r2,%r2 ;                                                         \
-    basr    %r1,0 ;                                                           \
-1:  l       %r1,2f-1b(%r1)                                                    \
-    st      %r2,0(%r1)                                                        \
-    lhi     %r2,-1                                                            \
-    br      %r14                                                              \
-2:  .long   errno
+# define SYSCALL_ERROR_LABEL 0f
+# define SYSCALL_ERROR_HANDLER \
+0:  basr  %r1,0;							      \
+1:  l     %r1,2f-1b(%r1);						      \
+    br    %r1;								      \
+2:  .long syscall_error
 #else
-#define SYSCALL_ERROR_HANDLER						      \
-0:  basr    %r1,0 ;                                                           \
-1:  al      %r1,2f-1b(%r1) ;                                                  \
-    l       %r1,errno@GOT12(%r1) ;                                            \
-    lcr     %r2,%r2 ;							      \
-    st      %r2,0(%r1) ;						      \
-    lhi     %r2,-1 ;                                                          \
-    br      %r14 ;                                                            \
-2:  .long   _GLOBAL_OFFSET_TABLE_-1b
+# if RTLD_PRIVATE_ERRNO
+#  define SYSCALL_ERROR_LABEL 0f
+#  define SYSCALL_ERROR_HANDLER \
+0:  basr  %r1,0;							      \
+1:  al    %r1,2f-1b(%r1);						      \
+    lcr   %r2,%r2;							      \
+    st    %r2,0(%r1);							      \
+    lhi   %r2,-1;							      \
+    br    %r14;								      \
+2:  .long errno-1b
+# elif defined _LIBC_REENTRANT
+#  if USE___THREAD
+#   ifndef NOT_IN_libc
+#    define SYSCALL_ERROR_ERRNO __libc_errno
+#   else
+#    define SYSCALL_ERROR_ERRNO errno
+#   endif
+#   define SYSCALL_ERROR_LABEL 0f
+#   define SYSCALL_ERROR_HANDLER \
+0:  lcr   %r0,%r2;							      \
+    basr  %r1,0;							      \
+1:  al    %r1,2f-1b(%r1);						      \
+    l     %r1,SYSCALL_ERROR_ERRNO@gotntpoff(%r1)			      \
+    ear   %r2,%a0							      \
+    st    %r0,0(%r1,%r2);						      \
+    lhi   %r2,-1;							      \
+    br    %r14;								      \
+2:  .long _GLOBAL_OFFSET_TABLE_-1b
+#  else
+#   define SYSCALL_ERROR_LABEL 0f
+#   define SYSCALL_ERROR_HANDLER \
+0:  basr  %r1,0;							      \
+1:  al    %r1,2f-1b(%r1);						      \
+    br    %r1;								      \
+2:  .long syscall_error@plt-1b
+#  endif
+# else
+#  define SYSCALL_ERROR_LABEL 0f
+#  define SYSCALL_ERROR_HANDLER \
+0:  basr  %r1,0;							      \
+1:  al    %r1,2f-1b(%r1);						      \
+    l     %r1,errno@GOT(%r1);						      \
+    lcr   %r2,%r2;							      \
+    st    %r2,0(%r1);							      \
+    lhi   %r2,-1;							      \
+    br    %r14;								      \
+2:  .long _GLOBAL_OFFSET_TABLE_-1b
+# endif /* _LIBC_REENTRANT */
 #endif /* PIC */
-#else
-#define SYSCALL_ERROR_HANDLER                                                 \
-0:  basr    %r1,0 ;                                                           \
-1:  al      %r1,2f-1b(%r1) ;                                                  \
-    br      %r1 ;                                                             \
-2:  .long   __syscall_error@PLT-1b
-#endif /* _LIBC_REENTRANT */
 
 /* Linux takes system call arguments in registers:
 
diff --git a/sysdeps/unix/sysv/linux/s390/s390-64/clone.S b/sysdeps/unix/sysv/linux/s390/s390-64/clone.S
index 992cb2f10f..117411824f 100644
--- a/sysdeps/unix/sysv/linux/s390/s390-64/clone.S
+++ b/sysdeps/unix/sysv/linux/s390/s390-64/clone.S
@@ -25,32 +25,40 @@
 #define _ERRNO_H	1
 #include <bits/errno.h>
 
+/* int __clone(int (*fn)(void *arg), void *child_stack, int flags, void *arg,
+               void *tls, pid_t *parent_tid, pid_t *child_tid);  */
+/* sys_clone  (void *child_stack, unsigned long flags,
+               pid_t *parent_tid, pid_t *child_tid, void *tls);  */
+
 	.text
 ENTRY(__clone)
 	/* Sanity check arguments & move registers */
+	lgr	%r0,%r5			/* move *arg out of the way */
 	ltgr	%r1,%r2			/* no NULL function pointers */
 	lghi	%r2,-EINVAL
-	jz	SYSCALL_ERROR_LABEL
+	jgz	SYSCALL_ERROR_LABEL
 	ltgr	%r3,%r3			/* no NULL stack pointers */
-	jz	SYSCALL_ERROR_LABEL
-	/* move child_stack and flags, then call SVC */
+	jgz	SYSCALL_ERROR_LABEL
+	/* set up registers, then call SVC */
 	lgr	%r2,%r3
 	lgr	%r3,%r4
+	lmg	%r4,%r5,160(%r15)
 	svc	SYS_ify(clone)
 	ltgr	%r2,%r2			/* check return code */
-	jm	SYSCALL_ERROR_LABEL
+	jgm	SYSCALL_ERROR_LABEL
 	jz	thread_start
 	br	%r14
 
 thread_start:
-	/* fn is in gpr 1, arg in gpr 5 */
-	lgr	%r2,%r5		/* set first parameter to void *arg */
-	sgr	%r11,%r11	/* terminate the stack frame */
+	/* fn is in gpr 1, arg in gpr 0 */
+	lgr	%r2,%r0		/* set first parameter to void *arg */
 	aghi	%r15,-160	/* make room on the stack for the save area */
+	xc	0(8,%r15),0(%r15)
 	basr	%r14,%r1	/* jump to fn */
 #ifdef PIC
-	larl	%r12,_GLOBAL_OFFSET_TABLE_
-#endif
 	jg	_exit@PLT	/* branch to _exit -> thread termination */
+#else
+	jg	_exit		/* branch to _exit -> thread termination */
+#endif
 PSEUDO_END (__clone)
 weak_alias (__clone, clone)
diff --git a/sysdeps/unix/sysv/linux/s390/s390-64/mmap.S b/sysdeps/unix/sysv/linux/s390/s390-64/mmap.S
index 23205901d4..8c94fd4c28 100644
--- a/sysdeps/unix/sysv/linux/s390/s390-64/mmap.S
+++ b/sysdeps/unix/sysv/linux/s390/s390-64/mmap.S
@@ -55,7 +55,7 @@ ENTRY(__mmap)
         /* Check gpr 2 for error.  */ 
         lghi    %r0,-4096
         clgr    %r2,%r0
-        jnl     SYSCALL_ERROR_LABEL
+        jgnl    SYSCALL_ERROR_LABEL
 
         /* Successful; return the syscall's value.  */
         br      %r14
diff --git a/sysdeps/unix/sysv/linux/s390/s390-64/socket.S b/sysdeps/unix/sysv/linux/s390/s390-64/socket.S
index c9bd02baee..3a2454eba4 100644
--- a/sysdeps/unix/sysv/linux/s390/s390-64/socket.S
+++ b/sysdeps/unix/sysv/linux/s390/s390-64/socket.S
@@ -93,7 +93,7 @@ ENTRY(__socket)
 	/* gpr2 is < 0 if there was an error.  */
         lghi    %r0,-125 
         clgr    %r2,%r0
-        jnl     SYSCALL_ERROR_LABEL
+        jgnl    SYSCALL_ERROR_LABEL
  
 	/* Successful; return the syscall's value.  */
 	br      %r14
diff --git a/sysdeps/unix/sysv/linux/s390/s390-64/syscall.S b/sysdeps/unix/sysv/linux/s390/s390-64/syscall.S
index 047c667542..718d559c3b 100644
--- a/sysdeps/unix/sysv/linux/s390/s390-64/syscall.S
+++ b/sysdeps/unix/sysv/linux/s390/s390-64/syscall.S
@@ -45,7 +45,7 @@ ENTRY (syscall)
 
 	lghi   %r0,-4095
 	clgr   %r2,%r0		   /* Check R2 for error.  */
-	jnl    SYSCALL_ERROR_LABEL
+	jgnl   SYSCALL_ERROR_LABEL
 	br     %r14		   /* Return to caller.	 */
 .L1:	.word  0x0A00		   /* Opcode for SVC 0.	 */
 PSEUDO_END (syscall)
diff --git a/sysdeps/unix/sysv/linux/s390/s390-64/sysdep.S b/sysdeps/unix/sysv/linux/s390/s390-64/sysdep.S
index a29b0b983a..84c747aef0 100644
--- a/sysdeps/unix/sysv/linux/s390/s390-64/sysdep.S
+++ b/sysdeps/unix/sysv/linux/s390/s390-64/sysdep.S
@@ -1,4 +1,4 @@
-/* Copyright (C) 2001, 2002 Free Software Foundation, Inc.
+/* Copyright (C) 2001, 2002, 2003 Free Software Foundation, Inc.
    Contributed by Martin Schwidefsky (schwidefsky@de.ibm.com).
    This file is part of the GNU C Library.
 
@@ -29,47 +29,84 @@
 #undef CALL_MCOUNT
 #define CALL_MCOUNT
 
-        .text
+.text
 ENTRY(__syscall_error)
 #ifndef PIC
-#ifndef _LIBC_REENTRANT
-	lcr     %r2,%r2
-	larl    %r1,errno
-	st      %r2,0(%r1)
-	lghi    %r2,-1
-	br      %r14
-#else
-        stmg    %r13,%r15,104(%r15)
-        lgr     %r0,%r15
-        aghi    %r15,-160
-        lcr     %r13,%r2
-        stg     %r0,0(%r15)
-	brasl   %r14,__errno_location
-	st      %r13,0(%r2)
-	lmg     %r13,%r15,264(%r15)
-	lghi    %r2,-1
-	br      %r14
+# if USE___THREAD
+#  ifndef NOT_IN_libc
+#   define SYSCALL_ERROR_ERRNO __libc_errno
+#  else
+#   define SYSCALL_ERROR_ERRNO errno
+#  endif
+	basr	%r1,0
+0:	lg	%r1,1f-0b(%r1)
+	ear	%r3,%a0
+	sllg	%r3,%r3,32
+	ear	%r3,%a1
+	lcr	%r2,%r2
+	st	%r2,0(%r1,%r3)
+	lghi	%r2,-1
+	br	%r14
+1:	.quad	SYSCALL_ERROR_ERRNO@ntpoff
+# elif !defined _LIBC_REENTRANT
+	larl	%r1,errno
+	lcr	%r2,%r2
+	st	%r2,0(%r1)
+	lghi	%r2,-1
+	br	%r14
+# else
+	stmg	%r13,%r15,104(%r15)
+	lgr	%r0,%r15
+	aghi	%r15,-160
+	lcr	%r13,%r2
+	stg	%r0,0(%r15)
+	brasl	%r14,__errno_location
+	st	%r13,0(%r2)
+	lmg	%r13,%r15,264(%r15)
+	lghi	%r2,-1
+	br	%r14
 #endif
 #else
-#ifndef _LIBC_REENTRANT
-	larl    %r1,_GLOBAL_OFFSET_TABLE_
-	lg      %r1,errno@GOT(%r1)
-	lcr     %r2,%r2
-	st      %r2,0(%r1)
-	lghi    %r2,-1
-	br      %r14
-#else
-        stmg    %r13,%r15,104(%r15)
-        lgr     %r0,%r15
-        aghi    %r15,-160
-        lcr     %r13,%r2
-        stg     %r0,0(%r15)
-	brasl   %r14,__errno_location@PLT
-	st      %r13,0(%r2)
-	lmg     %r13,%r15,264(%r15)
-	lghi    %r2,-1
-	br      %r14
-#endif
+# if RTLD_PRIVATE_ERRNO
+	larl	%r1,errno
+	lcr	%r2,%r2
+	st	%r2,0(%r1)
+	lghi	%r2,-1
+	br	%r14
+# elif USE___THREAD
+#  ifndef NOT_IN_libc
+#   define SYSCALL_ERROR_ERRNO __libc_errno
+#  else
+#   define SYSCALL_ERROR_ERRNO errno
+#  endif
+	larl	%r1,_GLOBAL_OFFSET_TABLE_
+	lg	%r1,SYSCALL_ERROR_ERRNO@gotntpoff(%r1)
+	ear	%r3,%a0
+	sllg	%r3,%r3,32
+	ear	%r3,%a1
+	lcr	%r2,%r2
+	st	%r2,0(%r1,%r3)
+	lghi	%r2,-1
+	br	%r14
+# elif !defined _LIBC_REENTRANT
+	larl	%r1,_GLOBAL_OFFSET_TABLE_
+	lg	%r1,errno@GOT(%r1)
+	lcr	%r2,%r2
+	st	%r2,0(%r1)
+	lghi	%r2,-1
+	br	%r14	
+# else
+	stmg	%r13,%r15,104(%r15)
+	lgr	%r0,%r15
+	aghi	%r15,-160
+	lcr	%r13,%r2
+	stg	%r0,0(%r15)
+	brasl	%r14,__errno_location@PLT
+	st	%r13,0(%r2)
+	lmg	%r13,%r15,264(%r15)
+	lghi	%r2,-1
+	br	%r14
+# endif
 #endif
 
 END (__syscall_error)
diff --git a/sysdeps/unix/sysv/linux/s390/s390-64/sysdep.h b/sysdeps/unix/sysv/linux/s390/s390-64/sysdep.h
index dbe77df979..976fd26f50 100644
--- a/sysdeps/unix/sysv/linux/s390/s390-64/sysdep.h
+++ b/sysdeps/unix/sysv/linux/s390/s390-64/sysdep.h
@@ -46,8 +46,6 @@
    number.  Linus said he will make sure the no syscall returns a value
    in -1 .. -4095 as a valid result so we can savely test with -4095.  */
 
-#define SYSCALL_ERROR_LABEL 0f
-
 #undef PSEUDO
 #define	PSEUDO(name, syscall_name, args)				      \
   .text;								      \
@@ -55,35 +53,58 @@
     DO_CALL (syscall_name, args);					      \
     lghi %r4,-4095 ;							      \
     clgr %r2,%r4 ;							      \
-    jnl SYSCALL_ERROR_LABEL ;						      \
-  L(pseudo_end):
+    jgnl SYSCALL_ERROR_LABEL
 
 #undef PSEUDO_END
 #define PSEUDO_END(name)						      \
   SYSCALL_ERROR_HANDLER;						      \
   END (name)
 
-#ifndef _LIBC_REENTRANT
 #ifndef PIC
-#define SYSCALL_ERROR_HANDLER						      \
-0:  lcr	    %r2,%r2 ;							      \
-    larl    %r1,errno ;							      \
-    st	    %r2,0(%r1) ;						      \
-    lghi    %r2,-1 ;							      \
-    br	    %r14
+# define SYSCALL_ERROR_LABEL syscall_error
+# define SYSCALL_ERROR_HANDLER
 #else
-#define SYSCALL_ERROR_HANDLER						      \
-0:  larl    %r1,_GLOBAL_OFFSET_TABLE_ ;					      \
-    lg	    %r1,errno@GOT(%r1) ;					      \
-    lcr	    %r2,%r2 ;							      \
-    st	    %r2,0(%r1) ;						      \
-    lghi    %r2,-1 ;							      \
-    br	    %r14
+# if RTLD_PRIVATE_ERRNO
+#  define SYSCALL_ERROR_LABEL 0f
+#  define SYSCALL_ERROR_HANDLER \
+0:  larl  %r1,errno;							      \
+    lcr   %r2,%r2;							      \
+    st    %r2,0(%r1);							      \
+    lghi  %r2,-1;							      \
+    br    %r14
+# elif defined _LIBC_REENTRANT
+#  if USE___THREAD
+#   ifndef NOT_IN_libc
+#    define SYSCALL_ERROR_ERRNO __libc_errno
+#   else
+#    define SYSCALL_ERROR_ERRNO errno
+#   endif
+#   define SYSCALL_ERROR_LABEL 0f
+#   define SYSCALL_ERROR_HANDLER \
+0:  lcr   %r0,%r2;							      \
+    larl  %r1,SYSCALL_ERROR_ERRNO@indntpoff;				      \
+    lg    %r1,0(%r1);							      \
+    ear   %r2,%a0;							      \
+    sllg  %r2,%r2,32;							      \
+    ear   %r2,%a1;							      \
+    st    %r0,0(%r1,%r2);						      \
+    lghi   %r2,-1;							      \
+    br    %r14
+#  else
+#   define SYSCALL_ERROR_LABEL syscall_error@plt
+#   define SYSCALL_ERROR_HANDLER
+#  endif
+# else
+#  define SYSCALL_ERROR_LABEL 0f
+#  define SYSCALL_ERROR_HANDLER \
+0:  larl  %r1,_GLOBAL_OFFSET_TABLE_;					      \
+    lg    %r1,errno@GOT(%r1);						      \
+    lcr   %r2,%r2;							      \
+    st    %r2,0(%r1);							      \
+    lghi  %r2,-1;							      \
+    br    %r14
+# endif /* _LIBC_REENTRANT */
 #endif /* PIC */
-#else
-#define SYSCALL_ERROR_HANDLER						      \
-0:  jg	    __syscall_error@PLT
-#endif /* _LIBC_REENTRANT */
 
 /* Linux takes system call arguments in registers: