-- v3: winecrt0: Implement static constructors support.
From: Rémi Bernon rbernon@codeweavers.com
--- dlls/kernel32/kernel_main.c | 5 +++ dlls/kernelbase/main.c | 5 +++ dlls/ntdll/misc.c | 5 +++ dlls/win32u/main.c | 5 +++ dlls/winecrt0/crt_dllmain.c | 77 ++++++++++++++++++++++++++++++++++++- dlls/wow64/system.c | 5 +++ dlls/wow64cpu/cpu.c | 5 +++ dlls/wow64win/syscall.c | 5 +++ 8 files changed, 111 insertions(+), 1 deletion(-)
diff --git a/dlls/kernel32/kernel_main.c b/dlls/kernel32/kernel_main.c index ae16195a550..b7bfe4981b9 100644 --- a/dlls/kernel32/kernel_main.c +++ b/dlls/kernel32/kernel_main.c @@ -214,3 +214,8 @@ BOOL WINAPI GetSystemRegistryQuota(PDWORD pdwQuotaAllowed, PDWORD pdwQuotaUsed)
return TRUE; } + +BOOL WINAPI DllMainCRTStartup( HINSTANCE inst, DWORD reason, void *reserved ) +{ + return DllMain( inst, reason, reserved ); +} diff --git a/dlls/kernelbase/main.c b/dlls/kernelbase/main.c index 60173ba6513..b687e84acfe 100644 --- a/dlls/kernelbase/main.c +++ b/dlls/kernelbase/main.c @@ -570,3 +570,8 @@ HRESULT WINAPI GetAcceptLanguagesW(WCHAR *langbuf, DWORD *buflen) *buflen = 0; return E_NOT_SUFFICIENT_BUFFER; } + +BOOL WINAPI DllMainCRTStartup( HINSTANCE inst, DWORD reason, void *reserved ) +{ + return DllMain( inst, reason, reserved ); +} diff --git a/dlls/ntdll/misc.c b/dlls/ntdll/misc.c index ab01c77531c..e5f0c886b66 100644 --- a/dlls/ntdll/misc.c +++ b/dlls/ntdll/misc.c @@ -501,3 +501,8 @@ ULONG WINAPIV EtwTraceMessage( TRACEHANDLE handle, ULONG flags, LPGUID guid, /*U va_end( valist ); return ret; } + +BOOL WINAPI DllMainCRTStartup( HINSTANCE inst, DWORD reason, void *reserved ) +{ + return DllMain( inst, reason, reserved ); +} diff --git a/dlls/win32u/main.c b/dlls/win32u/main.c index 8e32fc63556..ca2ab58299d 100644 --- a/dlls/win32u/main.c +++ b/dlls/win32u/main.c @@ -2211,3 +2211,8 @@ BOOL WINAPI DllMain( HINSTANCE inst, DWORD reason, void *reserved ) } return TRUE; } + +BOOL WINAPI DllMainCRTStartup( HINSTANCE inst, DWORD reason, void *reserved ) +{ + return DllMain( inst, reason, reserved ); +} diff --git a/dlls/winecrt0/crt_dllmain.c b/dlls/winecrt0/crt_dllmain.c index 181760c884a..6c24908f037 100644 --- a/dlls/winecrt0/crt_dllmain.c +++ b/dlls/winecrt0/crt_dllmain.c @@ -25,9 +25,84 @@ #include "windef.h" #include "winbase.h"
+#include "corecrt_startup.h" + +#if defined(_MSC_VER) +#define _CRTALLOC(x) __declspec(allocate(x)) +#elif defined(__GNUC__) +#define _CRTALLOC(x) __attribute__((section(x))) +#else +#define _CRTALLOC(x) static /* unsupported */ +#endif + +_CRTALLOC(".CRT$XIA") void (*__xi_a)(void) = 0; +_CRTALLOC(".CRT$XIZ") void (*__xi_z)(void) = 0; +_CRTALLOC(".CRT$XCA") void (*__xc_a)(void) = 0; +_CRTALLOC(".CRT$XCZ") void (*__xc_z)(void) = 0; + +#if defined(__GNUC__) +extern void (*__CTOR_LIST__[])(void) __attribute__((weak)); +extern void (*__DTOR_LIST__[])(void) __attribute__((weak)); +#endif + +static CRITICAL_SECTION onexit_cs; +static CRITICAL_SECTION_DEBUG onexit_cs_debug = +{ + 0, 0, &onexit_cs, + { &onexit_cs_debug.ProcessLocksList, &onexit_cs_debug.ProcessLocksList }, + 0, 0, { (DWORD_PTR)(__FILE__ ": onexit_cs") } +}; +static CRITICAL_SECTION onexit_cs = { &onexit_cs_debug, -1, 0, 0, 0, 0 }; +static _onexit_t *onexit_begin, *onexit_end; + +static void _CRT_INIT(void) +{ + void (**ctor)(void); + if ((ctor = &__xi_a)) while (++ctor != &__xi_z && *ctor) (*ctor)(); + if ((ctor = &__xc_a)) while (++ctor != &__xc_z && *ctor) (*ctor)(); +#if defined(__GNUC__) + if ((ctor = __CTOR_LIST__) && *ctor == (void *)-1) while (++ctor != __DTOR_LIST__ && *ctor) (*ctor)(); +#endif +} + +static _onexit_t dll_onexit( _onexit_t func, _onexit_t **start, _onexit_t **end ) +{ + int len = (*end - *start); + _onexit_t *tmp; + + if (!func || ++len <= 0) return NULL; + if (!(tmp = HeapReAlloc( GetProcessHeap(), 0, *start, len * sizeof(*tmp) ))) return NULL; + *start = tmp; + *end = tmp + len; + tmp[len - 1] = func; + + return func; +} + +int __cdecl atexit( void (__cdecl *func)(void) ) +{ + int ret; + EnterCriticalSection( &onexit_cs ); + ret = dll_onexit( (_onexit_t)func, &onexit_begin, &onexit_end ) == (_onexit_t)func; + LeaveCriticalSection( &onexit_cs ); + return ret; +} + BOOL WINAPI DllMainCRTStartup( HINSTANCE inst, DWORD reason, void *reserved ) { - return DllMain( inst, reason, reserved ); + BOOL ret; + + if (reason == DLL_PROCESS_ATTACH) _CRT_INIT(); + + ret = DllMain( inst, reason, reserved ); + + if (reason == DLL_PROCESS_DETACH) + { + while (onexit_end-- > onexit_begin) (**onexit_end)(); + HeapFree( GetProcessHeap(), 0, onexit_begin ); + } + + return ret; }
#endif diff --git a/dlls/wow64/system.c b/dlls/wow64/system.c index 6e78c462984..355d10850d2 100644 --- a/dlls/wow64/system.c +++ b/dlls/wow64/system.c @@ -850,3 +850,8 @@ NTSTATUS WINAPI wow64_NtWow64GetNativeSystemInformation( UINT *args ) return STATUS_INVALID_INFO_CLASS; } } + +BOOL WINAPI DllMainCRTStartup( HINSTANCE inst, DWORD reason, void *reserved ) +{ + return DllMain( inst, reason, reserved ); +} diff --git a/dlls/wow64cpu/cpu.c b/dlls/wow64cpu/cpu.c index c574315ec7d..abfa45e0667 100644 --- a/dlls/wow64cpu/cpu.c +++ b/dlls/wow64cpu/cpu.c @@ -436,3 +436,8 @@ NTSTATUS WINAPI BTCpuTurboThunkControl( ULONG enable ) /* we don't have turbo thunks yet */ return STATUS_SUCCESS; } + +BOOL WINAPI DllMainCRTStartup( HINSTANCE inst, DWORD reason, void *reserved ) +{ + return DllMain( inst, reason, reserved ); +} diff --git a/dlls/wow64win/syscall.c b/dlls/wow64win/syscall.c index 55b9dec9722..2b2c0fe54dc 100644 --- a/dlls/wow64win/syscall.c +++ b/dlls/wow64win/syscall.c @@ -58,3 +58,8 @@ BOOL WINAPI DllMain( HINSTANCE inst, DWORD reason, void *reserved ) NtCurrentTeb()->Peb->KernelCallbackTable = user_callbacks; return TRUE; } + +BOOL WINAPI DllMainCRTStartup( HINSTANCE inst, DWORD reason, void *reserved ) +{ + return DllMain( inst, reason, reserved ); +}
On Mon Jun 24 09:09:07 2024 +0000, Jacek Caban wrote:
MinGW runtime doesn't seem to have them (or maybe I just didn't find them).
See `__do_global_dtors`. It's registered using `atexit`, which is a static function that, in case of DLLs, executes on `PROCESS_DETACH`. Also, we currently allow exchanging static libraries between MSVC and MinGW targets (so you may build Wine in one mode and use it to build with `winegcc` for the other target). This may get tricky as we extend static libraries, but I think it would be nice to keep if we can. For that, using `__GNUC__` like you do can't work.
Thanks for the pointers! IIUC the static library compatibility issue is only a problem for the msvcrt changes, because winecrt0 objects will never be included in static libraries, right?
I actually didn't really need the C++ constructor support for executables, that was just for some tests I made to check the implementation, so the msvcrt change isn't really needed. I've updated the MR to only include the winecrt0 part.
Updated with just the winecrt0 part, also added atexit support, which requires to pull some allocation function and thus caused some linking problems for various modules which can't import it. I added explicit DllMainCRTStartup declarations for those modules to avoid pulling in the winecrt0 object for them.
IIUC the static library compatibility issue is only a problem for the msvcrt changes, because winecrt0 objects will never be included in static libraries, right?
I'm not sure what you mean. An example of the problem is if user has Wine built in MSVC mode (something like `--with-mingw=clang`) and then uses it to build with something like `winegcc -b x86_64-w64-mingw32` (backed by GCC-based mingw or llvm-mingw, doesn't matter). With your patch, constructors will not work. I guess something like weak symbols would do the trick (although it needs more testing, weak symbols are known to be buggy in GNU LD).
I added explicit DllMainCRTStartup declarations for those modules to avoid pulling in the winecrt0 object for them.
Maybe we could just provide a fallback no-op `_CRT_INIT` or something along those lines.
Jinoh Kang (@iamahuman) commented about dlls/winecrt0/crt_dllmain.c:
- LeaveCriticalSection( &onexit_cs );
- return ret;
+}
BOOL WINAPI DllMainCRTStartup( HINSTANCE inst, DWORD reason, void *reserved ) {
- return DllMain( inst, reason, reserved );
- BOOL ret;
- if (reason == DLL_PROCESS_ATTACH) _CRT_INIT();
- ret = DllMain( inst, reason, reserved );
- if (reason == DLL_PROCESS_DETACH)
- {
while (onexit_end-- > onexit_begin) (**onexit_end)();
NULL pointer arithmetic is UB. Also, you should only do equality or inequality check with NULL.
```suggestion:-0+0 while (onexit_end != onexit_begin) (**--onexit_end)(); ```
On Wed Jun 26 11:42:45 2024 +0000, Jacek Caban wrote:
IIUC the static library compatibility issue is only a problem for the
msvcrt changes, because winecrt0 objects will never be included in static libraries, right? I'm not sure what you mean. An example of the problem is if user has Wine built in MSVC mode (something like `--with-mingw=clang`) and then uses it to build with something like `winegcc -b x86_64-w64-mingw32` (backed by GCC-based mingw or llvm-mingw, doesn't matter). With your patch, constructors will not work. I guess something like weak symbols would do the trick (although it needs more testing, weak symbols are known to be buggy in GNU LD).
Then I think we should either make `__CTOR_LIST__` selectany on MSVC mode (`_MSC_VER`), or if that doesn't work, make it so that mixed mingw/msvc build explicitly fails when a constructor is detected (by inducing a symbol conflict).