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.