crt: tls: Move _register_thread_local_exe_atexit_callback into own source unit and do not call it for DLL builds Add a new internal function __mingw_register_thread_local_exe_atexit_callback which will call the CRT _register_thread_local_exe_atexit_callback() function with the mingw-w64 supplied callback function. Supplied callback function for now just calls the __mingw_cxa_atexit_callback_ptr (which is pointer to the tls_atexit_callback) previously directly passed as argument to _register_thread_local_exe_atexit_callback(). Stores pointer of the __mingw_register_thread_local_exe_atexit_callback into the tentative __mingw_register_thread_local_exe_atexit_callback_ptr variable and call it from crtexe.c on demand when is linked into final exectuable. And reference the __mingw_register_thread_local_exe_atexit_callback symbol from tls_atexit.c where is the __mingw_cxa_atexit_callback_ptr defined and expected to be __mingw_register_thread_local_exe_atexit_callback called from CRT EXE startup code. With these tenative definitions, the _register_thread_local_exe_atexit_callback() would be included in final binary image only if the callback is really required and only for EXE builds. Before this change the _register_thread_local_exe_atexit_callback() with tls_atexit_callback argument was called from PE TLS DLL_PROCESS_ATTACH callback. After this change, _register_thread_local_exe_atexit_callback() (with new mingw-w64 callback which calls tls_atexit_callback via __mingw_cxa_atexit_callback_ptr) is called from new function __mingw_register_thread_local_exe_atexit_callback() which is called from CRT EXE startup code (crtexe.c). Also extend documentation for tls_atexit_callback and tls_callback. Signed-off-by: LIU Hao <lh_mouse@126.com>
diff --git a/mingw-w64-crt/Makefile.am b/mingw-w64-crt/Makefile.am index 07c050d..b196372 100644 --- a/mingw-w64-crt/Makefile.am +++ b/mingw-w64-crt/Makefile.am
@@ -128,6 +128,7 @@ crt/usermatherr.c \ crt/xtxtmode.c \ crt/crtdll_atexit_register.c \ + crt/tls_exe_atexit_callback.c \ crt/tlsdyn.c crt/tlsdtor.c \ crt/tlsthrd_register.c crt/tls_atexit_register.c \ crt/tlsthrd.c crt/tlsmthread.c crt/tlsmcrt.c \
diff --git a/mingw-w64-crt/crt/crtdll.c b/mingw-w64-crt/crt/crtdll.c index af7a477..15ee52a 100644 --- a/mingw-w64-crt/crt/crtdll.c +++ b/mingw-w64-crt/crt/crtdll.c
@@ -216,4 +216,8 @@ return retcode ; } +/* not available for DLL builds, must be non-tentative to not be overridden */ +void (__cdecl *const __mingw_register_thread_local_exe_atexit_callback_ptr)(void) = NULL; +extern const uintptr_t __mingw_register_thread_local_exe_atexit_callback_provider __attribute__((alias("__mingw_register_thread_local_exe_atexit_callback_ptr"))); + const char __mingw_module_is_dll = 1;
diff --git a/mingw-w64-crt/crt/crtexe.c b/mingw-w64-crt/crt/crtexe.c index e7dbcff..ca9b006 100644 --- a/mingw-w64-crt/crt/crtexe.c +++ b/mingw-w64-crt/crt/crtexe.c
@@ -51,6 +51,8 @@ /* TLS initialization hook. */ const PIMAGE_TLS_CALLBACK __dyn_tls_init_callback __attribute__((common)); /* tentative */ +void (__cdecl *const __mingw_register_thread_local_exe_atexit_callback_ptr)(void) __attribute__((common)); /* tentative */ + extern int __mingw_app_type; static int argc; @@ -258,6 +260,9 @@ if (__dyn_tls_init_callback != NULL) __dyn_tls_init_callback (NULL, DLL_THREAD_ATTACH, NULL); + if (__mingw_register_thread_local_exe_atexit_callback_ptr != NULL) + __mingw_register_thread_local_exe_atexit_callback_ptr (); + #ifdef _UNICODE __winitenv = envp; #else
diff --git a/mingw-w64-crt/crt/tls_atexit.c b/mingw-w64-crt/crt/tls_atexit.c index 44dd5c8..bb0390f 100644 --- a/mingw-w64-crt/crt/tls_atexit.c +++ b/mingw-w64-crt/crt/tls_atexit.c
@@ -93,10 +93,38 @@ TlsSetValue(tls_dtors_slot, NULL); } -#if defined(__i386__) -/* We need to make sure that we align the stack to 16 bytes for the sake of SSE */ -__attribute__((force_align_arg_pointer)) -#endif +/* + * For EXE builds the tls_atexit_callback() function is called by the exit() + * function before it calls atexit callbacks. For UCRT builds this is achieved + * by UCRT directly, for non-UCRT builds this is achieved by the mingw-w64 + * exit() wrapper function. The tls_atexit_callback() function is called + * indirectly via the __mingw_cxa_atexit_callback_ptr from the mingw-w64 + * callback registered by the CRT _register_thread_local_exe_atexit_callback. + * If the process does not call CRT exit function and is terminated by different + * way (e.g. _exit(), _Exit() or ExitProcess()) then the tls_atexit_callback() + * function is not called at all. + * + * For DLL builds the tls_atexit_callback() function is called explicitly from + * the tls_callback() function which is below. This function is called also when + * process is terminated by different way (e.g. _exit(), _Exit() or ExitProcess()) + * not just by exit() function. + * + * Reason for these differences is the fact that for DLL builds the tls_callback() + * function is called before invocation of atexit callbacks, but for EXE builds + * it is called after all atexit callbacks. For DLL builds, all atexit callbacks + * are executed from the DLL entry point when DLL_PROCESS_DETACH event happens + * and for EXE builds, they are executed from the CRT exit() function which is + * called before execution of native PE TLS callbacks. + * + * For EXE builds the tls_callback() function is called PE TLS callback and + * hence after all atexit callbacks. For DLL builds the tls_callback() function + * is called from DLL entry point before all atexit callbacks. Note that for + * DLL builds also PE TLS callbacks are executed before all atexit callbacks. + * + * mingw cxa dtors (executed by tls_atexit_callback) has to be run before atexit + * callbacks because it is required for execution of global C++ destructors. + * So this is why the different behavior for DLL and EXE builds is needed. + */ static void WINAPI tls_atexit_callback(HANDLE __UNUSED_PARAM(hDllHandle), DWORD dwReason, LPVOID __UNUSED_PARAM(lpReserved)) { if (dwReason == DLL_PROCESS_DETACH) { run_thread_dtor_list(); @@ -107,7 +135,23 @@ } } } +const _tls_callback_type __mingw_cxa_atexit_callback_ptr = tls_atexit_callback; +/* Force inclusion of code which registers __mingw_cxa_atexit_callback_ptr for EXE builds */ +extern const uintptr_t __mingw_register_thread_local_exe_atexit_callback_provider; +static __attribute__((used)) const void *const _include_mingw_cxa_atexit_callback_caller = &__mingw_register_thread_local_exe_atexit_callback_provider; + + +/* + * For EXE builds the tls_callback is called PE TLS callback. If the CRT exit + * function was called then the PE TLS callback for DLL_PROCESS_DETACH event + * is called after execution of exit function and so after execution of all + * registered atexit callbacks. + * + * For DLL builds the tls_callback is called from DLL entry point. And for + * the DLL_PROCESS_DETACH event, the tls_callback is called before execution + * of atexit callbacks. + */ static WINBOOL WINAPI tls_callback(HANDLE hDllHandle, DWORD dwReason, LPVOID __UNUSED_PARAM(lpReserved)) { switch (dwReason) { case DLL_PROCESS_ATTACH: @@ -117,18 +161,6 @@ return FALSE; InitializeCriticalSection(&lock); __dso_handle = hDllHandle; - /* - * We can only call _register_thread_local_exe_atexit_callback once - * in a process; if we call it a second time the process terminates. - * When DLLs are unloaded, this callback is invoked before we run the - * _onexit tables, but for exes, we need to ask this to be called before - * all other registered atexit functions. - * Since we are registered as a normal TLS callback, we will be called - * another time later as well, but that doesn't matter, it's safe to - * invoke this with DLL_PROCESS_DETACH twice. - */ - if (!__mingw_module_is_dll) - _register_thread_local_exe_atexit_callback(tls_atexit_callback); } inited = 1; break; @@ -150,9 +182,11 @@ * standard says, but differs from what MSVC does with a dynamically * linked CRT (which still runs TLS destructors for the main thread). * - * For DLLs, run dtors when detached. For EXEs, run dtors via the - * thread local atexit callback, to make sure they don't run when - * exiting the process with _exit or ExitProcess. + * For DLLs, run dtors when detached explicitly by tls_atexit_callback(). + * For EXEs, run dtors via the thread local atexit callback, to make sure + * they don't run when exiting the process with _exit or ExitProcess. + * mingw-w64 thread local atexit callback calls the tls_atexit_callback() + * and is registered for EXEs by _register_thread_local_exe_atexit_callback. */ if (__mingw_module_is_dll) tls_atexit_callback(NULL, DLL_PROCESS_DETACH, NULL);
diff --git a/mingw-w64-crt/crt/tls_exe_atexit_callback.c b/mingw-w64-crt/crt/tls_exe_atexit_callback.c new file mode 100644 index 0000000..fa9d270 --- /dev/null +++ b/mingw-w64-crt/crt/tls_exe_atexit_callback.c
@@ -0,0 +1,27 @@ +/** + * This file has no copyright assigned and is placed in the Public Domain. + * This file is part of the mingw-w64 runtime package. + * No warranty is given; refer to the file DISCLAIMER.PD within this package. + */ + +#include <process.h> + +const _tls_callback_type __mingw_cxa_atexit_callback_ptr __attribute__((common)); /* tentative */ + +#if defined(__i386__) +/* We need to make sure that we align the stack to 16 bytes for the sake of SSE */ +__attribute__((force_align_arg_pointer)) +#endif +static void __stdcall tls_exe_atexit_callback(void *handle, unsigned long reason, void *reserved) +{ + if (__mingw_cxa_atexit_callback_ptr) + __mingw_cxa_atexit_callback_ptr(handle, reason, reserved); +} + +static void __cdecl __mingw_register_thread_local_exe_atexit_callback(void) +{ + _register_thread_local_exe_atexit_callback(tls_exe_atexit_callback); +} + +void (__cdecl *const __mingw_register_thread_local_exe_atexit_callback_ptr)(void) = __mingw_register_thread_local_exe_atexit_callback; /* override tentative */ +extern const uintptr_t __mingw_register_thread_local_exe_atexit_callback_provider __attribute__((alias("__mingw_register_thread_local_exe_atexit_callback_ptr"))); /* nontentative alias */