2e9afc
commit acd98a8ed1460497e788c701eb92616f1df9b446
2e9afc
Author: Andreas Krebbel <krebbel@linux.vnet.ibm.com>
2e9afc
Date:   Fri Nov 29 09:57:04 2013 +0100
2e9afc
2e9afc
    [BZ #16214] S/390: Fix TLS GOT pointer setup.
2e9afc
2e9afc
diff --git a/sysdeps/s390/Versions b/sysdeps/s390/Versions
2e9afc
index e18617c..baf9842 100644
2e9afc
--- a/sysdeps/s390/Versions
2e9afc
+++ b/sysdeps/s390/Versions
2e9afc
@@ -3,4 +3,8 @@ ld {
2e9afc
     # runtime interface to TLS
2e9afc
     __tls_get_offset;
2e9afc
   }
2e9afc
+  GLIBC_PRIVATE {
2e9afc
+    # Exported by ld used by libc.
2e9afc
+    __tls_get_addr_internal;
2e9afc
+  }
2e9afc
 }
2e9afc
diff --git a/sysdeps/s390/dl-tls.h b/sysdeps/s390/dl-tls.h
2e9afc
index 68a5af4..52192a2 100644
2e9afc
--- a/sysdeps/s390/dl-tls.h
2e9afc
+++ b/sysdeps/s390/dl-tls.h
2e9afc
@@ -26,11 +26,26 @@ typedef struct
2e9afc
 
2e9afc
 
2e9afc
 #ifdef SHARED
2e9afc
-/* This is the prototype for the GNU version.  */
2e9afc
-extern void *__tls_get_addr (tls_index *ti) attribute_hidden;
2e9afc
+
2e9afc
 extern unsigned long __tls_get_offset (unsigned long got_offset);
2e9afc
 
2e9afc
 # ifdef IS_IN_rtld
2e9afc
+
2e9afc
+#  include <shlib-compat.h>
2e9afc
+
2e9afc
+extern void *__tls_get_addr (tls_index *ti) attribute_hidden;
2e9afc
+/* Make a temporary alias of __tls_get_addr to remove the hidden
2e9afc
+   attribute.  Then export __tls_get_addr as __tls_get_addr_internal
2e9afc
+   for use from libc.  We do not want to export __tls_get_addr, but we
2e9afc
+   do need to use it from libc when looking up the address of a TLS
2e9afc
+   variable. We don't use __tls_get_offset because it requires r12 to
2e9afc
+   be setup and that might not always be true. Either way it's more
2e9afc
+   optimal to use __tls_get_addr directly (that's what
2e9afc
+   __tls_get_offset does anyways).  */
2e9afc
+strong_alias (__tls_get_addr, __tls_get_addr_internal_tmp);
2e9afc
+versioned_symbol (ld, __tls_get_addr_internal_tmp,
2e9afc
+		  __tls_get_addr_internal, GLIBC_PRIVATE);
2e9afc
+
2e9afc
 /* The special thing about the s390 TLS ABI is that we do not have the
2e9afc
    standard __tls_get_addr function but the __tls_get_offset function
2e9afc
    which differs in two important aspects:
2e9afc
@@ -63,15 +78,21 @@ __tls_get_offset:\n\
2e9afc
 1:	.long	__tls_get_addr - 0b\n\
2e9afc
 ");
2e9afc
 #  endif
2e9afc
-# endif
2e9afc
+# else /* IS_IN_rtld */
2e9afc
+extern void *__tls_get_addr_internal (tls_index *ti);
2e9afc
+# endif /* !IS_IN_rtld */
2e9afc
 
2e9afc
 # define GET_ADDR_OFFSET \
2e9afc
   (ti->ti_offset - (unsigned long) __builtin_thread_pointer ())
2e9afc
 
2e9afc
-# define __TLS_GET_ADDR(__ti) \
2e9afc
-  ({ extern char _GLOBAL_OFFSET_TABLE_[] attribute_hidden;		  \
2e9afc
-     (void *) __tls_get_offset ((char *) (__ti) - _GLOBAL_OFFSET_TABLE_)  \
2e9afc
-     + (unsigned long) __builtin_thread_pointer (); })
2e9afc
+/* Use the privately exported __tls_get_addr_internal instead of
2e9afc
+   __tls_get_offset in order to avoid the __tls_get_offset special
2e9afc
+   linkage requiring the GOT pointer to be set up in r12.  The
2e9afc
+   compiler will take care of setting up r12 only if itself issued the
2e9afc
+   __tls_get_offset call.  */
2e9afc
+# define __TLS_GET_ADDR(__ti)					\
2e9afc
+  ({ (void *) __tls_get_addr_internal ((char *) (__ti))		\
2e9afc
+      + (unsigned long) __builtin_thread_pointer (); })
2e9afc
 
2e9afc
 #endif
2e9afc
 
2e9afc
diff --git a/sysdeps/s390/s390-32/tls-macros.h b/sysdeps/s390/s390-32/tls-macros.h
2e9afc
index 8a0ad58..a592d81 100644
2e9afc
--- a/sysdeps/s390/s390-32/tls-macros.h
2e9afc
+++ b/sysdeps/s390/s390-32/tls-macros.h
2e9afc
@@ -8,12 +8,15 @@
2e9afc
 
2e9afc
 #ifdef PIC
2e9afc
 # define TLS_IE(x) \
2e9afc
-  ({ unsigned long __offset;						      \
2e9afc
+  ({ unsigned long __offset, __got;					      \
2e9afc
      asm ("bras %0,1f\n"						      \
2e9afc
-	  "0:\t.long " #x "@gotntpoff\n"				      \
2e9afc
-	  "1:\tl %0,0(%0)\n\t"						      \
2e9afc
-	  "l %0,0(%0,%%r12):tls_load:" #x				      \
2e9afc
-	  : "=&a" (__offset) : : "cc" );				      \
2e9afc
+	  "0:\t.long _GLOBAL_OFFSET_TABLE_-0b\n\t"			      \
2e9afc
+	  ".long " #x "@gotntpoff\n"					      \
2e9afc
+	  "1:\tl %1,0(%0)\n\t"						      \
2e9afc
+	  "la %1,0(%1,%0)\n\t"						      \
2e9afc
+	  "l %0,4(%0)\n\t"						      \
2e9afc
+	  "l %0,0(%0,%1):tls_load:" #x "\n"				      \
2e9afc
+	  : "=&a" (__offset), "=&a" (__got) : : "cc" );			      \
2e9afc
      (int *) (__builtin_thread_pointer() + __offset); })
2e9afc
 #else
2e9afc
 # define TLS_IE(x) \
2e9afc
diff --git a/sysdeps/s390/s390-64/tls-macros.h b/sysdeps/s390/s390-64/tls-macros.h
2e9afc
index be8aa6c..3c59436 100644
2e9afc
--- a/sysdeps/s390/s390-64/tls-macros.h
2e9afc
+++ b/sysdeps/s390/s390-64/tls-macros.h
2e9afc
@@ -8,12 +8,13 @@
2e9afc
 
2e9afc
 #ifdef PIC
2e9afc
 # define TLS_IE(x) \
2e9afc
-  ({ unsigned long __offset;						      \
2e9afc
-     asm ("bras %0,1f\n"						      \
2e9afc
-	  "0:\t.quad " #x "@gotntpoff\n"				      \
2e9afc
-	  "1:\tlg %0,0(%0)\n\t"						      \
2e9afc
-	  "lg %0,0(%0,%%r12):tls_load:" #x				      \
2e9afc
-	  : "=&a" (__offset) : : "cc" );				      \
2e9afc
+  ({ unsigned long __offset, __got;					      \
2e9afc
+     asm ("bras %0,0f\n\t"						      \
2e9afc
+	  ".quad " #x "@gotntpoff\n"					      \
2e9afc
+	  "0:\tlarl %1,_GLOBAL_OFFSET_TABLE_\n\t"			      \
2e9afc
+	  "lg %0,0(%0)\n\t"						      \
2e9afc
+	  "lg %0,0(%0,%1):tls_load:" #x	"\n"				      \
2e9afc
+	  : "=&a" (__offset), "=&a" (__got) : : "cc" );			      \
2e9afc
      (int *) (__builtin_thread_pointer() + __offset); })
2e9afc
 #else
2e9afc
 # define TLS_IE(x) \