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

            Bug ID: 83562
           Summary: broken destructors of thread_local objects on i686
                    mingw targets
           Product: gcc
           Version: 7.2.0
            Status: UNCONFIRMED
          Severity: normal
          Priority: P3
         Component: c++
          Assignee: unassigned at gcc dot gnu.org
          Reporter: lh_mouse at 126 dot com
  Target Milestone: ---

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/

Reply via email to