Using a thread_local variable inside a DLL will cause a segfault when the process is exiting. I've attached a minimal example (hopefully). Basically, create one file like this:
#include <iostream>
#include <string>
thread_local std::string myThreadString;
extern "C"
{
__attribute__((visibility("default"))) __declspec(dllexport)
void hello()
{
if(!myThreadString.empty())
myThreadString += " ";
myThreadString += "hello";
std::cout << myThreadString << std::endl;
}
}
Then a second file which calls that one:
extern "C"
{
__declspec(dllimport) void hello();
}
int main(int argc, char** argv)
{
hello();
hello();
hello();
}
Then build them (I was using WSL, but this same bug repros with the version directly through Windows, or when cross-compiling from within a Docker container/Linux host):
/usr/bin/x86_64-w64-mingw32-g++ \
-fPIC -O0 -g -std=gnu++1z -static-libgcc -static-libstdc++ -fvisibility=hidden \
-shared -o libbadmin.dll -Wl,--out-implib,libbadmin.dll.a \
badmin_impl.cpp
/usr/bin/x86_64-w64-mingw32-g++ \
-O0 -g -std=gnu++1z -static-libgcc -static-libstdc++ \
-o badmin-caller.exe \
badmin_caller.cpp libbadmin.dll.a
When running under GDB, here's the stack trace:
Thread 1 received signal SIGSEGV, Segmentation fault.
0x0000000050a894b7 in __cxxabiv1::__vmi_class_type_info::~__vmi_class_type_info() ()
from c:\projects\setmatch\mingw-bug-minimal\libbadmin.dll
(gdb) bt
#0 0x0000000050a894b7 in __cxxabiv1::__vmi_class_type_info::~__vmi_class_type_info() ()
from c:\projects\setmatch\mingw-bug-minimal\libbadmin.dll
#1 0x0000000050a711e2 in _CRT_INIT (hDllHandle=hDllHandle@entry=0x50a70000, dwReason=dwReason@entry=0,
lpreserved=lpreserved@entry=0x1) at ./mingw-w64-crt/crt/crtdll.c:144
#2 0x0000000050a712f4 in __DllMainCRTStartup (hDllHandle=0x50a70000, dwReason=0, lpreserved=0x1)
at ./mingw-w64-crt/crt/crtdll.c:211
#3 0x00007ffa0ebfb583 in ntdll!RtlActivateActivationContextUnsafeFast () from C:\WINDOWS\SYSTEM32\ntdll.dll
#4 0x00007ffa0ec07f85 in ntdll!LdrShutdownProcess () from C:\WINDOWS\SYSTEM32\ntdll.dll
#5 0x00007ffa0ec07e48 in ntdll!RtlExitUserProcess () from C:\WINDOWS\SYSTEM32\ntdll.dll
#6 0x00007ffa0d4fb19a in KERNEL32!FatalExit () from C:\WINDOWS\System32\kernel32.dll
#7 0x00007ffa0c4c9ce5 in msvcrt!_exit () from C:\WINDOWS\System32\msvcrt.dll
#8 0x00007ffa0c4ca345 in msvcrt!_initterm_e () from C:\WINDOWS\System32\msvcrt.dll
#9 0x00000000004014cd in __tmainCRTStartup () at ./mingw-w64-crt/crt/crtexe.c:337
#10 0x000000000040151b in mainCRTStartup () at ./mingw-w64-crt/crt/crtexe.c:212
This repros with versions 7.3.x and 8.1.x
The issue is in DLL unloading, therefore the problem presists if you link to the DLL using for example Visual Studio instead of creating the caller in MinGW.
This is a blocking bug for me.
Possible cause: DllMain should never use the process heap when the program is shutting down. From: https://docs.microsoft.com/en-us/windows/desktop/Dlls/dllmain
If fdwReason is DLL_PROCESS_DETACH, lpvReserved is NULL if FreeLibrary has been called or the DLL load failed and non-NULL if the process is terminating.
I think if fdwReason is DLL_PROCESS_DETACH and lpvReserved is non-null, it's too late to do any destructors. I'm not 100% sure that's the root cause, just a guess.
It repros on several different versions, but I've verified it on this specific version: https://launchpad.net/~mati865/+archive/ubuntu/mingw-w64. Checked both on WSL and inside Docker. The versions included are 6.0.0-3/7.3.0-29
Windows version: Windows 10 Pro 1803 17134.523
Last edit: Bobby Fraser 2019-02-06