-- v2: msvcrt: Implement static constructors support. winecrt0: Implement static constructors support.
From: Rémi Bernon rbernon@codeweavers.com
--- dlls/winecrt0/crt_dllmain.c | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+)
diff --git a/dlls/winecrt0/crt_dllmain.c b/dlls/winecrt0/crt_dllmain.c index 181760c884a..56660e55097 100644 --- a/dlls/winecrt0/crt_dllmain.c +++ b/dlls/winecrt0/crt_dllmain.c @@ -25,8 +25,37 @@ #include "windef.h" #include "winbase.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__) +void (*__CTOR_LIST__)(void) __attribute__((weak)) = 0; +void (*__DTOR_LIST__)(void) __attribute__((weak)) = 0; +#endif + +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 +} + BOOL WINAPI DllMainCRTStartup( HINSTANCE inst, DWORD reason, void *reserved ) { + if (reason == DLL_PROCESS_ATTACH) _CRT_INIT(); return DllMain( inst, reason, reserved ); }
From: Rémi Bernon rbernon@codeweavers.com
--- dlls/msvcrt/crt_main.c | 29 +++++++++++++++++++++++++++++ dlls/msvcrt/crt_wmain.c | 29 +++++++++++++++++++++++++++++ 2 files changed, 58 insertions(+)
diff --git a/dlls/msvcrt/crt_main.c b/dlls/msvcrt/crt_main.c index eb785e30bb0..b388489e704 100644 --- a/dlls/msvcrt/crt_main.c +++ b/dlls/msvcrt/crt_main.c @@ -30,6 +30,34 @@ #include "winbase.h" #include "winternl.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__) +void (*__CTOR_LIST__)(void) __attribute__((weak)) = 0; +void (*__DTOR_LIST__)(void) __attribute__((weak)) = 0; +#endif + +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 +} + int __cdecl main(int argc, char **argv, char **env);
static const IMAGE_NT_HEADERS *get_nt_header( void ) @@ -55,6 +83,7 @@ int __cdecl mainCRTStartup(void) #endif _set_app_type(get_nt_header()->OptionalHeader.Subsystem == IMAGE_SUBSYSTEM_WINDOWS_GUI ? _crt_gui_app : _crt_console_app);
+ _CRT_INIT(); ret = main(argc, argv, env);
exit(ret); diff --git a/dlls/msvcrt/crt_wmain.c b/dlls/msvcrt/crt_wmain.c index 79755d1a6e9..38be3ee5942 100644 --- a/dlls/msvcrt/crt_wmain.c +++ b/dlls/msvcrt/crt_wmain.c @@ -30,6 +30,34 @@ #include "winbase.h" #include "winternl.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__) +void (*__CTOR_LIST__)(void) __attribute__((weak)) = 0; +void (*__DTOR_LIST__)(void) __attribute__((weak)) = 0; +#endif + +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 +} + int __cdecl wmain(int argc, WCHAR **argv, WCHAR **env);
static const IMAGE_NT_HEADERS *get_nt_header( void ) @@ -55,6 +83,7 @@ int __cdecl wmainCRTStartup(void) #endif _set_app_type(get_nt_header()->OptionalHeader.Subsystem == IMAGE_SUBSYSTEM_WINDOWS_GUI ? _crt_gui_app : _crt_console_app);
+ _CRT_INIT(); ret = wmain(argc, argv, env);
exit(ret);
Completely unsure how to do that properly but it would be useful, to support C++ code build with Wine CRT.
Is there a reason for not using `_initterm(_e)`?
For C++ DLLs we need to handle destructors as well, otherwise they won't clean up properly on DLL detach and lead to subtle crashes depending on app logic.
On Wed Jun 19 16:43:06 2024 +0000, Jinoh Kang wrote:
For C++ DLLs we need to handle destructors as well, otherwise they won't clean up properly on DLL detach and lead to subtle crashes depending on app logic.
As far as I understand, except for GNU `__attribute__((destructor))`, the cleanup is registered with `atexit` instead, so it doesn't look like it needs destructors support? MinGW runtime doesn't seem to have them (or maybe I just didn't find them).
On Wed Jun 19 16:43:56 2024 +0000, Rémi Bernon wrote:
As far as I understand, except for GNU `__attribute__((destructor))`, the cleanup is registered with `atexit` instead, so it doesn't look like it needs destructors support? MinGW runtime doesn't seem to have them (or maybe I just didn't find them).
Sure but `atexit` is process-global right? IIUC our DLL crt0 doesn't provide its own module-local atexit. Calling unloaded DLL functions at process exit will UAF.
On Wed Jun 19 16:47:22 2024 +0000, Jinoh Kang wrote:
Sure but Wine's currently linked `atexit` is process-global right? IIUC our DLL crt0 doesn't provide its own module-local atexit. Calling unloaded DLL functions at process exit will UAF.
https://learn.microsoft.com/en-us/cpp/c-runtime-library/dllonexit?view=msvc-... looks like a good indicator that Windows does something on DLL detach. I'd say either MinGW is wrong or you didn't find it.
On Thu Jun 20 10:52:21 2024 +0000, Jinoh Kang wrote:
Is there a reason for not using `_initterm(_e)`?
I didn't know it existed, but then it's not very easy to pull in: some DLLs like kernel32, kernelbase, the CRT themselves, etc... don't link with msvcrt.
On Thu Jun 20 12:01:33 2024 +0000, Rémi Bernon wrote:
I didn't know it existed, but then it's not very easy to pull in: some DLLs like kernel32, kernelbase, the CRT themselves, etc... don't link with msvcrt.
Ah, thanks for your reply.
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.