From: Kevin Puetz PuetzKevinA@JohnDeere.com
Run C++ global/static destructors during DLL_PROCESS_DETACH while other other dllimport functions they may want to call are still viable. While windows imposes many restrictions on what may be done in such destructors (given that the run inside the loader lock), there are lots of legal and useful kernel32 functions like DestroyCriticalSection, DeleteAtom, TlsFree, etc that are both useful and legal. Currently this does not work for builtin modules because all the Win32 structures are discarded well before NtUnmapViewOfSection finally does the dlllose.
Even for a winelib .dll.so module, it would be preferable for destructors to execute during process_detach (before wine tears down the MODREF and detaches dependant dlls), rather than be left until after the last NtUnmapViewOfSection (when we finally reach dlclose)
Therefore, winegcc now always uses the DllMainCRTStartup entry point unless you specify your own --entry=func. Previously it did this only for PE modules using msvcrt. Making this default consistent matches cl.exe, which also always defaults to _DllMainCRTStartup unless overridden by /entry:foo https://docs.microsoft.com/en-us/cpp/build/reference/entry-entry-point-symbo...
The ELF version of winecrt0.a now provides a DllMainCRTStartup which, per the Itanium ABI that is in practice what is used by gcc and clang, performs this this destruction by calling __cxa_finalize(&__dso_handle).. This libc function is required to be idempotent, so it's OK that dlclose still calls it again later (there will just be no further work to do).
Multiple calls to __cxa_finalize shall not result in calling termination function entries multiple times; the implementation may either remove entries or mark them finished.
https://itanium-cxx-abi.github.io/cxx-abi/abi.html#dso-dtor-runtime-api
This has two main effects; it moves ELF destructors earlier (before imports are unmapped), and it moves them inside the Nt loader lock. Being earlier was the intended goal, and moving them inside the lock seems fine. Any Win32 API calls in destructors are just being subjected to the same lock hierarchy rules as usual on windows (MSVC also runs destructors from DllMainCrtStartup)
https://docs.microsoft.com/en-us/cpp/build/run-time-library-behavior?view=ms...
And any purely-ELF destructors that happen to also run earlier should never call functions exported from wine (and thus don't care about ntdll's locks). --- dlls/winecrt0/crt_dllmain.c | 19 +++++++++++++++---- dlls/winecrt0/exe_entry.c | 16 +++++++++++++++- dlls/winecrt0/exe_wentry.c | 16 +++++++++++++++- tools/winegcc/winegcc.c | 4 +++- 4 files changed, 48 insertions(+), 7 deletions(-)
diff --git a/dlls/winecrt0/crt_dllmain.c b/dlls/winecrt0/crt_dllmain.c index 181760c884a..539f5f6115c 100644 --- a/dlls/winecrt0/crt_dllmain.c +++ b/dlls/winecrt0/crt_dllmain.c @@ -18,8 +18,6 @@ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA */
-#ifdef __WINE_PE_BUILD - #include <stdarg.h> #include <stdio.h> #include "windef.h" @@ -27,7 +25,20 @@
BOOL WINAPI DllMainCRTStartup( HINSTANCE inst, DWORD reason, void *reserved ) { - return DllMain( inst, reason, reserved ); -} + BOOL result = DllMain( inst, reason, reserved );
+#ifndef __WINE_PE_BUILD + if(reason == DLL_PROCESS_DETACH) { +#if __has_attribute(sysv_abi) /* winecrt0 uses -mabi=ms, but this is a sysv function */ + extern void __cxa_finalize (void *) __attribute__((weak,sysv_abi)); +#else + extern void __cxa_finalize (void *) __attribute__((weak)); #endif + extern void *__dso_handle __attribute((visibility("hidden"),weak)); + if(__cxa_finalize && &__dso_handle) { + __cxa_finalize(&__dso_handle); + } + } +#endif + return result; +} diff --git a/dlls/winecrt0/exe_entry.c b/dlls/winecrt0/exe_entry.c index d4d1d7d6757..b412ab7e14f 100644 --- a/dlls/winecrt0/exe_entry.c +++ b/dlls/winecrt0/exe_entry.c @@ -97,5 +97,19 @@ DWORD WINAPI DECLSPEC_HIDDEN __wine_spec_exe_entry( PEB *peb ) int argc; char **argv = build_argv( GetCommandLineA(), &argc );
- ExitProcess( main( argc, argv )); + int ret = main( argc, argv ); + +#ifndef __WINE_PE_BUILD +#if __has_attribute(sysv_abi) /* winecrt0 uses -mabi=ms, but this is a sysv function */ + extern void __cxa_finalize (void *) __attribute__((weak,sysv_abi)); +#else + extern void __cxa_finalize (void *) __attribute__((weak)); +#endif + extern void *__dso_handle __attribute((visibility("hidden"),weak)); + if(__cxa_finalize && &__dso_handle) { + __cxa_finalize(&__dso_handle); + } +#endif + + ExitProcess( ret ); } diff --git a/dlls/winecrt0/exe_wentry.c b/dlls/winecrt0/exe_wentry.c index a4c1a0897fb..17081dd90ca 100644 --- a/dlls/winecrt0/exe_wentry.c +++ b/dlls/winecrt0/exe_wentry.c @@ -97,5 +97,19 @@ DWORD WINAPI DECLSPEC_HIDDEN __wine_spec_exe_wentry( PEB *peb ) int argc; WCHAR **argv = build_argv( GetCommandLineW(), &argc );
- ExitProcess( wmain( argc, argv )); + int ret = wmain( argc, argv ); + +#ifndef __WINE_PE_BUILD +#if __has_attribute(sysv_abi) /* winecrt0 uses -mabi=ms, but this is a sysv function */ + extern void __cxa_finalize (void *) __attribute__((weak,sysv_abi)); +#else + extern void __cxa_finalize (void *) __attribute__((weak)); +#endif + extern void *__dso_handle __attribute((visibility("hidden"),weak)); + if(__cxa_finalize && &__dso_handle) { + __cxa_finalize(&__dso_handle); + } +#endif + + ExitProcess( ret ); } diff --git a/tools/winegcc/winegcc.c b/tools/winegcc/winegcc.c index ab26adb07e8..639c2af5587 100644 --- a/tools/winegcc/winegcc.c +++ b/tools/winegcc/winegcc.c @@ -1266,6 +1266,8 @@ static void build(struct options* opts) entry_point = (is_pe && opts->target.cpu == CPU_i386) ? "DriverEntry@8" : "DriverEntry"; else if (opts->use_msvcrt && !opts->shared && !opts->win16_app) entry_point = opts->unicode_app ? "wmainCRTStartup" : "mainCRTStartup"; + else if (opts->shared && !opts->win16_app) + entry_point = opts->target.cpu == CPU_i386 ? "DllMainCRTStartup@12" : "DllMainCRTStartup"; } else entry_point = opts->entry_point;
@@ -1303,7 +1305,7 @@ static void build(struct options* opts) for ( j = 0; j < lib_dirs.count; j++ ) strarray_add(&link_args, strmake("-L%s", lib_dirs.str[j]));
- if (is_pe && opts->use_msvcrt && !entry_point && (opts->shared || opts->win16_app)) + if (is_pe && opts->use_msvcrt && !entry_point && opts->win16_app) entry_point = opts->target.cpu == CPU_i386 ? "DllMainCRTStartup@12" : "DllMainCRTStartup";
if (is_pe && entry_point)