Bug 93704

Summary: TLS sequences not understood by SPARC/Solaris linker
Product: gcc Reporter: Vita Batrla <vita.batrla>
Component: targetAssignee: Eric Botcazou <ebotcazou>
Status: RESOLVED FIXED    
Severity: normal CC: ebotcazou, ro
Priority: P3    
Version: 9.2.0   
Target Milestone: 8.4   
Host: Target: sparc-solaris2.*
Build: Known to work:
Known to fail: Last reconfirmed: 2020-02-12 00:00:00

Description Vita Batrla 2020-02-12 12:18:36 UTC
In the ELF Handling for Thread-Local Storage document [1] in section 4.1.3
SPARC General Dynamic TLS Model on page 20-21, following code sequence is
defined for GD relocations:

General Dynamic Model Code Sequence     Initial Relocation      Symbol
0x00 sethi %hi(@dtlndx(x)),     %o0     R_SPARC_TLS_GD_HI22     x
0x04 add   %o0,%lo(@dtlndx(x)), %o0     R_SPARC_TLS_GD_LO10     x
0x08 add   %l7,%o0,             %o0     R_SPARC_TLS_GD_ADD      x
0x0c call  __tls_get_addr               R_SPARC_TLS_GD_CALL     x

With comment:

> The code sequence must appear in the code as is. It is not possible to move
> the second add instruction in the delay slot of the call instruction since
> the linker would not recognize the instruction sequence. (2)

and note (2):

> This is at least what Sun’s documentation says and apparently how Sun’s
> linker works. Given the relocations which show exactly what the instructions
> do this seems not really necessary.

However, GCC 7/9 emits this sequence in .o file:

  2c:   11 00 00 00     sethi  %hi(0), %o0
                        2c: R_SPARC_TLS_GD_HI22 __gcov_indirect_call_callee
  30:   90 02 20 00     add  %o0, 0, %o0        ! 0 <main>
                        30: R_SPARC_TLS_GD_LO10 __gcov_indirect_call_callee
  34:   40 00 00 00     call  34 <main+0x34>
                        34: R_SPARC_TLS_GD_CALL __gcov_indirect_call_callee
  38:   90 05 c0 08     add  %l7, %o0, %o0
                        38: R_SPARC_TLS_GD_ADD  __gcov_indirect_call_callee

It is apparent that the compiler generated the GD TLS sequence in different
order than the TLS document suggests. It placed the second add instruction in
the branch delay slot of call instruction, which is against the document
statement:

"It is not possible to move the second add instruction in the delay slot of the call instruction"

This relaxed order generated by GCC is no doubt more efficient, however, it is not understood by Solaris linker, which converts the call instruction during GD->IE transition to add instruction: 'add %g7, %o0, %o0' (opcode: 90 01 c0 08). The problem is that the original instruction in branch delay slot was supposed to execute before instruction on branch target (it calculates input argument for call instruction target), but after it is converted to add instruction, branch is gone and so is its delay slot and instructions execute in program order. This corrupts value in %o0 register as both instructions write to it but execute in different than originally supposed order and corrupted %o0 causes SIGSEGV (if lucky) in subsequent attempt to use it in memory de-reference to get value of a TLS variable.
Comment 1 Vita Batrla 2020-02-12 12:30:37 UTC
[1] https://www.uclibc.org/docs/tls.pdf
Comment 2 Andrew Pinski 2020-02-12 12:47:51 UTC
How did you configure gcc?
Comment 3 Eric Botcazou 2020-02-12 12:54:02 UTC
Yep, a thinko pertaining to TARGET_GNU_TLS.
Comment 4 Eric Botcazou 2020-02-12 12:55:45 UTC
Fixing.
Comment 5 Vita Batrla 2020-02-12 13:00:38 UTC
To answer previous question,
here are configure options for GCC in Solaris:

https://github.com/oracle/solaris-userland/blob/60efc343ffab1adac2d1c9cac7629af26d40de50/components/gcc9/Makefile#L91

CONFIGURE_OPTIONS +=	--infodir=$(CONFIGURE_INFODIR)
CONFIGURE_OPTIONS +=	--libexecdir=$(CONFIGURE_PREFIX)/lib
CONFIGURE_OPTIONS +=	--enable-languages="c,c++,fortran,go,objc"
CONFIGURE_OPTIONS +=	--enable-shared
CONFIGURE_OPTIONS +=	--enable-initfini-array
CONFIGURE_OPTIONS +=	--disable-rpath
CONFIGURE_OPTIONS +=	--with-system-zlib
CONFIGURE_OPTIONS +=	--with-build-config=no
CONFIGURE_OPTIONS +=	--with-gmp-include=$(shell pkg-config --variable=includedir libgmp)
CONFIGURE_OPTIONS +=	--with-mpfr-include=$(shell pkg-config --variable=includedir libmpfr)
CONFIGURE_OPTIONS +=	--without-gnu-ld --with-ld=$(USRBINDIR)/ld
CONFIGURE_OPTIONS +=	--with-gnu-as --with-as=$(GNUBIN)/as

See last two lines,
GCC is built with GNU as, but Solaris ld.
Comment 6 GCC Commits 2020-02-14 18:26:28 UTC
The master branch has been updated by Eric Botcazou <ebotcazou@gcc.gnu.org>:

https://gcc.gnu.org/g:81fc552558f54c6f691e7816da9ab4ad8483f7ef

commit r10-6639-g81fc552558f54c6f691e7816da9ab4ad8483f7ef
Author: Eric Botcazou <ebotcazou@gcc.gnu.org>
Date:   Fri Feb 14 19:21:02 2020 +0100

    Fix problematic TLS sequences for the Solaris linker
    
    This is an old thinko pertaining to the interaction between TLS
    sequences and delay slot filling: the compiler knows that it cannot
    put instructions with TLS relocations into delay slots with the
    original Sun TLS model, but it tests TARGET_SUN_TLS in this context,
    which depends only on the assembler.  So if the compiler is configured
    with the GNU assembler and the Solaris linker, then TARGET_GNU_TLS is
    set instead and the limitation is not enforced.
    
    	PR target/93704
    	* config/sparc/sparc.c (eligible_for_call_delay): Test HAVE_GNU_LD
    	in conjunction with TARGET_GNU_TLS in early return.
Comment 7 GCC Commits 2020-02-14 18:27:57 UTC
The releases/gcc-9 branch has been updated by Eric Botcazou <ebotcazou@gcc.gnu.org>:

https://gcc.gnu.org/g:c1379a1c645684599b8902edcdf4a2b5f2648542

commit r9-8239-gc1379a1c645684599b8902edcdf4a2b5f2648542
Author: Eric Botcazou <ebotcazou@gcc.gnu.org>
Date:   Fri Feb 14 19:21:02 2020 +0100

    Fix problematic TLS sequences for the Solaris linker
    
    This is an old thinko pertaining to the interaction between TLS
    sequences and delay slot filling: the compiler knows that it cannot
    put instructions with TLS relocations into delay slots with the
    original Sun TLS model, but it tests TARGET_SUN_TLS in this context,
    which depends only on the assembler.  So if the compiler is configured
    with the GNU assembler and the Solaris linker, then TARGET_GNU_TLS is
    set instead and the limitation is not enforced.
    
    	PR target/93704
    	* config/sparc/sparc.c (eligible_for_call_delay): Test HAVE_GNU_LD
    	in conjunction with TARGET_GNU_TLS in early return.
Comment 8 GCC Commits 2020-02-14 18:29:53 UTC
The releases/gcc-8 branch has been updated by Eric Botcazou <ebotcazou@gcc.gnu.org>:

https://gcc.gnu.org/g:c35cfc834eda41cd8b08ee989b028552ad9cd6a8

commit r8-10023-gc35cfc834eda41cd8b08ee989b028552ad9cd6a8
Author: Eric Botcazou <ebotcazou@gcc.gnu.org>
Date:   Fri Feb 14 19:21:02 2020 +0100

    Fix problematic TLS sequences for the Solaris linker
    
    This is an old thinko pertaining to the interaction between TLS
    sequences and delay slot filling: the compiler knows that it cannot
    put instructions with TLS relocations into delay slots with the
    original Sun TLS model, but it tests TARGET_SUN_TLS in this context,
    which depends only on the assembler.  So if the compiler is configured
    with the GNU assembler and the Solaris linker, then TARGET_GNU_TLS is
    set instead and the limitation is not enforced.
    
    	PR target/93704
    	* config/sparc/sparc.c (eligible_for_call_delay): Test HAVE_GNU_LD
    	in conjunction with TARGET_GNU_TLS in early return.
Comment 9 Eric Botcazou 2020-02-14 18:31:26 UTC
Thanks for reporting the problem.