When using GNU as with Solaris ld, the TLS local dynamic tests
(gcc.dg/torture/tls/run-ld.c etc.) FAIL to execute before Solaris 11:
the programs crash with an illegal instruction: e.g. in the
gcc.dg/lto/20090210 test, we have

0x8050ce0 <main+48>: mov    %gs:0x0,%eax
0x8050ce6 <main+54>: call   0x8050ce7 <main+55>
0x8050ce7 <main+55>: cld    
0x8050ce8 <main+56>: (bad)  

The reason is that Solaris ld before Solaris 11 requires the use of the
@tlsldmplt relocation documented in the Solaris 10 Linker and Libraries
Guide:

http://docs.oracle.com/cd/E26505_01/html/E26506/chapter8-20.html#gentextid-23600

Unfortunately, gas doesn't support that relocation.  While adding the
gas side was easy, I completely failed for gld and didn't propose the
incomplete patch to binutils since it would (rightly) have been
rejected.

Old versions of ld mishandle the TLS LD code sequence emitted by gcc
when @tlsldmplt isn't available, causing the crashes observed.  So ld
cannot handle what gas emits and gas cannot emit what ld requires.  The
only solution seems to fall back to the GD model in that case, which is
ugly but certainly better than having perfectly valid programs crash.

The following patch does just that.  It carefully restricts its actions
to Solaris.  I'm falling back to using dis when objdump isn't available
to avoid enabling this fallback unnecessarily.

Bootstraps on i386-pc-solaris2.{9,10,11} (as/ld, gas/ld, gas/gld,
as/gld), amd64-pc-solaris2.{10,11} (as/ld, gas/ld),
x86_64-unknown-linux-gnu, and i686-unknown-linux-gnu either completed
successfully or still running.

Ok for mainline if those pass?

Thanks.
        Rainer


2014-03-04  Rainer Orth  <r...@cebitec.uni-bielefeld.de>

        * configure.ac (TLS_SECTION_ASM_FLAG): Save as tls_section_flag.
        (LIB_TLS_SPEC): Save as ld_tls_libs.
        (HAVE_AS_IX86_TLSLDMPLT): Define as 1/0.
        (HAVE_AS_IX86_TLSLDM): New test.
        * configure, config.in: Regenerate.
        * config/i386/i386.c (legitimize_tls_address): Fall back to
        TLS_MODEL_GLOBAL_DYNAMIC on 32-bit Solaris/x86 if tool chain
        cannot support TLS_MODEL_LOCAL_DYNAMIC.
        * config/i386/i386.md (*tls_local_dynamic_base_32_gnu): Use if
        instead of #ifdef in HAVE_AS_IX86_TLSLDMPLT test.

# HG changeset patch
# Parent 7e1c533059af7a5120250c5c055ae22288bd828a
Disable local dynamic TLS model on Solaris/x86 if as/ld cannot handle it

diff --git a/gcc/config/i386/i386.c b/gcc/config/i386/i386.c
--- a/gcc/config/i386/i386.c
+++ b/gcc/config/i386/i386.c
@@ -13396,6 +13396,13 @@ legitimize_tls_address (rtx x, enum tls_
   enum machine_mode tp_mode = Pmode;
   int type;
 
+  /* Fall back to global dynamic model if tool chain cannot support local
+     dynamic.  */
+  if (TARGET_SUN_TLS && !TARGET_64BIT
+      && !HAVE_AS_IX86_TLSLDMPLT && !HAVE_AS_IX86_TLSLDM
+      && model == TLS_MODEL_LOCAL_DYNAMIC)
+    model = TLS_MODEL_GLOBAL_DYNAMIC;
+
   switch (model)
     {
     case TLS_MODEL_GLOBAL_DYNAMIC:
diff --git a/gcc/config/i386/i386.md b/gcc/config/i386/i386.md
--- a/gcc/config/i386/i386.md
+++ b/gcc/config/i386/i386.md
@@ -12962,11 +12962,12 @@
   output_asm_insn
     ("lea{l}\t{%&@tlsldm(%1), %0|%0, %&@tlsldm[%1]}", operands);
   if (TARGET_SUN_TLS)
-#ifdef HAVE_AS_IX86_TLSLDMPLT
-    return "call\t%&@tlsldmplt";
-#else
-    return "call\t%p2@plt";
-#endif
+    {
+      if (HAVE_AS_IX86_TLSLDMPLT)
+	return "call\t%&@tlsldmplt";
+      else
+	return "call\t%p2@plt";
+    }
   return "call\t%P2";
 }
   [(set_attr "type" "multi")
diff --git a/gcc/configure.ac b/gcc/configure.ac
--- a/gcc/configure.ac
+++ b/gcc/configure.ac
@@ -2973,6 +2973,7 @@ foo:	.long	25
 	.section .tdata,"awt",@progbits'
       tls_first_major=0
       tls_first_minor=0
+      tls_section_flag=t
 changequote([,])dnl
       AC_DEFINE(TLS_SECTION_ASM_FLAG, 't',
 [Define to the flag used to mark TLS sections if the default (`T') doesn't work.])
@@ -2982,6 +2983,7 @@ changequote(,)dnl
 	.section ".tdata","awT",@progbits'
       tls_first_major=2
       tls_first_minor=14
+      tls_section_flag=T
       tls_as_opt="--fatal-warnings"
     fi
     conftest_s="$conftest_s
@@ -3012,6 +3014,7 @@ foo:	.long	25
 	movq	$foo@TPOFF, %rax'
 	tls_first_major=2
 	tls_first_minor=14
+	tls_section_flag=T
 	tls_as_opt=--fatal-warnings
 	;;
   ia64-*-*)
@@ -3368,6 +3371,7 @@ case "$target" in
     # (32-bit x86) only lived in libthread, so check for that.  Keep
     # set_have_as_tls if found, disable if not.
     AC_SEARCH_LIBS([$tga_func], [thread],, [set_have_as_tls=no])
+    ld_tls_libs="$LIBS"
     # Clear LIBS if we cannot support TLS.
     if test $set_have_as_tls = no; then
       LIBS=
@@ -3924,9 +3928,48 @@ foo:	nop
 	 && $gcc_cv_ld -o conftest conftest.o -G > /dev/null 2>&1; then
 	   gcc_cv_as_ix86_tlsldmplt=yes
 	 fi
-	 rm -f conftest],
-      [AC_DEFINE(HAVE_AS_IX86_TLSLDMPLT, 1,
-        [Define if your assembler and linker support @tlsldmplt.])])
+	 rm -f conftest])
+    AC_DEFINE_UNQUOTED(HAVE_AS_IX86_TLSLDMPLT,
+      [`if test $gcc_cv_as_ix86_tlsldmplt = yes; then echo 1; else echo 0; fi`],
+      [Define to 1 if your assembler and linker support @tlsldmplt.])
+
+    # Enforce 32-bit output with gas and gld.
+    if test x$gas = xyes; then
+      as_ix86_tls_ldm_opt="--32"
+    fi
+    if echo "$ld_ver" | grep GNU > /dev/null; then
+      if $gcc_cv_ld -V 2>/dev/null | grep elf_i386_sol2 > /dev/null; then
+        ld_ix86_tls_ldm_opt="-melf_i386_sol2"
+      else
+        ld_ix86_tls_ldm_opt="-melf_i386"
+      fi
+    fi
+    conftest_s='
+	.section .text,"ax",@progbits
+        .globl  _start
+        .type   _start, @function
+_start:      
+	leal	value@tlsldm(%ebx), %eax
+	call	___tls_get_addr@plt
+
+        .section .tdata,"aw'$tls_section_flag'",@progbits
+        .type	value, @object
+value:'
+    gcc_GAS_CHECK_FEATURE([R_386_TLS_LDM reloc],
+        gcc_cv_as_ix86_tlsldm,,
+	[$as_ix86_tls_ldm_opt],
+	[$conftest_s],
+	[if test x$gcc_cv_ld != x && test x$gcc_cv_objdump != x \
+	    && $gcc_cv_ld $ld_ix86_tls_ldm_opt -o conftest conftest.o $ld_tls_libs -lc > /dev/null 2>&1; then
+	   if $gcc_cv_objdump -d conftest 2>/dev/null | grep nop > /dev/null \
+	      || dis conftest 2>/dev/null | grep nop > /dev/null; then
+	     gcc_cv_as_ix86_tlsldm=yes
+	   fi
+	 fi
+	 rm -f conftest])
+    AC_DEFINE_UNQUOTED(HAVE_AS_IX86_TLSLDM,
+      [`if test $gcc_cv_as_ix86_tlsldm = yes; then echo 1; else echo 0; fi`],
+      [Define to 1 if your assembler and linker support @tlsldm.])
 
     ;;
 
-- 
-----------------------------------------------------------------------------
Rainer Orth, Center for Biotechnology, Bielefeld University

Reply via email to