Bug 83562 - broken destructors of thread_local objects on i686 mingw targets
Summary: broken destructors of thread_local objects on i686 mingw targets
Status: RESOLVED FIXED
Alias: None
Product: gcc
Classification: Unclassified
Component: target (show other bugs)
Version: 7.2.0
: P3 normal
Target Milestone: ---
Assignee: Not yet assigned to anyone
URL:
Keywords: wrong-code
Depends on:
Blocks:
 
Reported: 2017-12-23 04:29 UTC by LIU Hao
Modified: 2024-10-31 04:27 UTC (History)
5 users (show)

See Also:
Host:
Target: i?86-mingw
Build:
Known to work:
Known to fail:
Last reconfirmed: 2018-07-17 00:00:00


Attachments

Note You need to log in before you can comment on or make changes to this bug.
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).
Comment 6 LIU Hao 2024-10-31 04:27:39 UTC
The mingw-w64 bug which you've linked has been fixed for UCRT: https://github.com/mingw-w64/mingw-w64/blob/0d42217123d3aec0341b79f6d959c76e09648a1e/mingw-w64-crt/crt/tls_atexit.c#L119