Bug 83562

Summary: broken destructors of thread_local objects on i686 mingw targets
Product: gcc Reporter: Liu Hao <lh_mouse>
Component: targetAssignee: Not yet assigned to anyone <unassigned>
Status: RESOLVED FIXED    
Severity: normal CC: 3dw4rd, alexandre.nunes, emsr, ralphengels, thiago
Priority: P3 Keywords: wrong-code
Version: 7.2.0   
Target Milestone: ---   
Host: Target: i?86-mingw
Build: Known to work:
Known to fail: Last reconfirmed: 2018-07-17 00:00:00

Description Liu Hao 2017-12-23 04:29:22 UTC
Test program:

```c++
struct cat {
  cat(){ __builtin_printf("cat(): %p\n", (void *)this); }
  ~cat(){ __builtin_printf("~cat(): %p\n", (void *)this); }
  void meow(){ __builtin_printf("meow(): %p\n", (void *)this); }
};

thread_local cat c;

int main(){
  c.meow();
}
```

Compiling and running this program outputs a garbage pointer inside the dtor:


```plaintext
E:\Desktop>i686-w64-mingw32-g++ test.cpp && a.exe
cat(): 0084177C
meow(): 0084177C
~cat(): 76EC2FED
```

Recompile the original program with `-S -O2` and notice the following assembly code:

```s
C1:
       .ascii "cat(): %p\12\0"
       .text
       .def    ___tls_init.part.0;     .scl    3;      .type   32;     .endef
__tls_init.part.0:
       pushl   %ebx
       subl    $24, %esp
       movl    $___emutls_v.__tls_guard, (%esp)
       call    ___emutls_get_address
       movb    $1, (%eax)
       movl    $___emutls_v.c, (%esp)
       call    ___emutls_get_address
       movl    %eax, %ebx
       movl    %eax, 4(%esp)
       movl    $LC1, (%esp)
       call    _printf
       movl    $0, 8(%esp)
       movl    %ebx, 4(%esp)
       movl    $__ZN3catD1Ev, (%esp)
       call    ___cxa_thread_atexit
       addl    $24, %esp
       popl    %ebx
       ret
```

GCC emits a call to the destructor of `cat` using `__cxa_thread_atexit()`. This causes the crash, because `__cxa_thread_atexit()` requires the callback to use the `__cdecl` calling convention, where its argument is to be pushed onto stack, while the destructor expects the `__thiscall` calling convention, where its argument, the implicit `this` pointer, is to be passed via the ECX register. Hence inside the callback ECX merely gets whatever garbage value that happens to reside in it.

Reference: https://sourceforge.net/p/mingw-w64/bugs/527/
Comment 1 Alexandre Pereira Nunes 2018-04-03 22:27:57 UTC
I can confirm that on gcc 7.3.0 / i686-mingw64.
Comment 2 tino.lange 2018-07-17 13:04:20 UTC
We encountered the same issue today, so this thread_local issue is still present also with gcc 8.1.0

i686-gcc-7.3.0-mingw-5.0.3

i686-gcc-8.1.0-mingw-5.0.3

(Note: With the corresponding 64bit gcc all is fine).


Could this bug please be assigned to someone? (It seems this is somehwat stalled since months unfortunately ...)

Thank you for taking care of this!
Comment 3 Thiago Macieira 2018-12-11 22:04:36 UTC
This can easily be fixed by way of a trampoline that adjusts the parameter.
Comment 4 ralphengels@gmail.com 2020-10-02 10:51:16 UTC
This still is not working causing several boost libraries to fail building when using mingw-w64 compilers (gcc-9.3 and gcc-10.2).

Everything works fine with the 64 bit compiler but the i686 compiler reports broken TLS. While we can get around it by disabling the TLS check for libbost_fiber-mt.dll libboost_stacktrace_windbg_cached-mt.dll is another story (halts the build if we skip the TLS check).