https://gcc.gnu.org/bugzilla/show_bug.cgi?id=93704

            Bug ID: 93704
           Summary: SPARC GCC emits R_SPARC_TLS_GD_CALL code not
                    understood by Solaris linker
           Product: gcc
           Version: 9.2.0
            Status: UNCONFIRMED
          Severity: normal
          Priority: P3
         Component: target
          Assignee: unassigned at gcc dot gnu.org
          Reporter: vita.batrla at gmail dot com
  Target Milestone: ---

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.

Reply via email to