[PATCH v2 0/3] MR9758: crt: Run MinGW constructors and destructors
(stacked over !9265) @jacek does this look like a good idea? -- v2: crt: Run GCC constructors and destructors. winegcc: Use dummy __CTOR/DTOR_LIST__ symbols on MSVC targets. winebuild: Generate dummy __CTOR/DTOR_LIST__ symbols. https://gitlab.winehq.org/wine/wine/-/merge_requests/9758
From: Yuxuan Shui <yshui@codeweavers.com> This creates placeholders in case our runtime library is being linked on MSVC targets, which doesn't create __CTOR_LIST__ and __DTOR_LIST__ symbols. --- tools/winebuild/spec32.c | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/tools/winebuild/spec32.c b/tools/winebuild/spec32.c index 9d99850f887..8ef036916d4 100644 --- a/tools/winebuild/spec32.c +++ b/tools/winebuild/spec32.c @@ -731,6 +731,21 @@ void output_crt_sections(void) output( "%s:\n", asm_name( symbol_name ) ); output( "\t%s 0\n", get_asm_ptr_keyword() ); } + + /* When linking on MinGW targets, the linker script creates a __CTOR_LIST__ and a __DTOR_LIST__ + * symbol for constructor and destructor sections; but MSVC targets don't. To ensure our + * runtime library is usable in either cases, we generate a dummy symbol here, and emit a + * -alternatename flag in winegcc on MSVC targets, creating aliases from __CTOR_LIST__/ + * __DTOR_LIST__ to this dummy symbol, so everything works. + * + * If someone builds on mingw but links on windows, all constructors and destructors will be + * lost. That's user error. */ + output( "\t.section .rdata\n" ); + output( "\t.globl __wine_spec_dummy_ctor_dtor_list__\n" ); + output( "\t.balign %u\n", get_ptr_size() ); + output( "__wine_spec_dummy_ctor_dtor_list__:\n" ); + output( "\t%s -1\n", get_asm_ptr_keyword() ); + output( "\t%s 0\n", get_asm_ptr_keyword() ); } -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/9758
From: Yuxuan Shui <yshui@codeweavers.com> --- tools/winegcc/winegcc.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/tools/winegcc/winegcc.c b/tools/winegcc/winegcc.c index b3cfa15fabc..b7c0aa1e196 100644 --- a/tools/winegcc/winegcc.c +++ b/tools/winegcc/winegcc.c @@ -629,6 +629,11 @@ static struct strarray get_link_args( const char *output_name ) strarray_add( &link_args, strmake( "-Wl,-filealign:%s,-align:%s,-driver", file_align, section_align )); strarray_add( &link_args, "-Wl,-merge:.CRT=.rdata" ); + strarray_add( &link_args, "-Wl,-alternatename:__CTOR_LIST__=__wine_spec_dummy_ctor_dtor_list__" ); + strarray_add( &link_args, "-Wl,-alternatename:__DTOR_LIST__=__wine_spec_dummy_ctor_dtor_list__" ); + strarray_add( &link_args, "-Wl,-alternatename:___CTOR_LIST__=__wine_spec_dummy_ctor_dtor_list__" ); + strarray_add( &link_args, "-Wl,-alternatename:___DTOR_LIST__=__wine_spec_dummy_ctor_dtor_list__" ); + strarray_addall( &link_args, flags ); return link_args; -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/9758
From: Yuxuan Shui <yshui@codeweavers.com> --- dlls/msvcrt/crt_init.h | 15 +++++++++++++++ dlls/winecrt0/crt_dllmain.c | 20 +++++++++++++++++++- 2 files changed, 34 insertions(+), 1 deletion(-) diff --git a/dlls/msvcrt/crt_init.h b/dlls/msvcrt/crt_init.h index 97d9de618cc..7462c82156d 100644 --- a/dlls/msvcrt/crt_init.h +++ b/dlls/msvcrt/crt_init.h @@ -18,6 +18,11 @@ #include <corecrt_startup.h> +/* Reference: mingw-w64-crt: crt/gccmain.c */ +typedef void (*fnptr)(void); +extern fnptr __CTOR_LIST__[]; +extern fnptr __DTOR_LIST__[]; + extern _PIFV __xi_a[]; extern _PIFV __xi_z[]; extern _PVFV __xc_a[]; @@ -39,11 +44,21 @@ static inline int fallback_initterm_e(_PIFV *table, _PIFV *end) static __cdecl void do_global_dtors(void) { + SIZE_T i; + _initterm(__xt_a, __xt_z); + + for (i = 1; __DTOR_LIST__[i]; i++) __DTOR_LIST__[i](); } static void do_global_ctors(void) { + ULONG_PTR nfns = (ULONG_PTR)__CTOR_LIST__[0], i; + + if (nfns == (ULONG_PTR)-1) + for (nfns = 0; __CTOR_LIST__[nfns + 1]; nfns++); + for (i = nfns; i >= 1; i--) __CTOR_LIST__[i](); + if (fallback_initterm_e(__xi_a, __xi_z) != 0) return; _initterm(__xc_a, __xc_z); diff --git a/dlls/winecrt0/crt_dllmain.c b/dlls/winecrt0/crt_dllmain.c index 02dccdec511..fdef5ba11de 100644 --- a/dlls/winecrt0/crt_dllmain.c +++ b/dlls/winecrt0/crt_dllmain.c @@ -26,6 +26,11 @@ #include "windef.h" #include "winbase.h" +/* Reference: mingw-w64-crt: crt/gccmain.c */ +typedef void (*fnptr)(void); +extern fnptr __CTOR_LIST__[]; +extern fnptr __DTOR_LIST__[]; + extern _PIFV __xi_a[]; extern _PIFV __xi_z[]; extern _PVFV __xc_a[]; @@ -53,13 +58,26 @@ BOOL WINAPI DllMainCRTStartup( HINSTANCE inst, DWORD reason, void *reserved ) if (reason == DLL_PROCESS_ATTACH) { + ULONG_PTR nfns = (ULONG_PTR)__CTOR_LIST__[0], i; + + if (nfns == (ULONG_PTR)-1) + for (nfns = 0; __CTOR_LIST__[nfns + 1]; nfns++); + for (i = nfns; i >= 1; i--) __CTOR_LIST__[i](); + if (fallback_initterm_e( __xi_a, __xi_z ) != 0) return FALSE; fallback_initterm( __xc_a, __xc_z ); } ret = DllMain( inst, reason, reserved ); - if (reason == DLL_PROCESS_DETACH) fallback_initterm( __xt_a, __xt_z ); + if (reason == DLL_PROCESS_DETACH) + { + SIZE_T i; + + fallback_initterm( __xt_a, __xt_z ); + + for (i = 1; __DTOR_LIST__[i]; i++) __DTOR_LIST__[i](); + } return ret; } -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/9758
huh, llvm-mingw doesn't define `__CTOR_LIST__`/`__DTOR_LIST__`? -- https://gitlab.winehq.org/wine/wine/-/merge_requests/9758#note_128647
On Tue Feb 3 15:56:21 2026 +0000, Yuxuan Shui wrote:
huh, llvm-mingw doesn't define `__CTOR_LIST__`/`__DTOR_LIST__`? not sure why i am getting undefined symbols on llvm-mingw, it definitely [defines these symbols](https://github.com/llvm/llvm-project/blob/main/lld/COFF/Writer.cpp#L2455)
-- https://gitlab.winehq.org/wine/wine/-/merge_requests/9758#note_128649
On Tue Feb 3 15:56:21 2026 +0000, Yuxuan Shui wrote:
not sure why i am getting undefined symbols on llvm-mingw, it definitely [defines these symbols](https://github.com/llvm/llvm-project/blob/main/lld/COFF/Writer.cpp#L2455) It only does this when the linker is running in mingw mode, see https://github.com/llvm/llvm-project/blob/main/lld/COFF/Writer.cpp#L1308-L13.... If invoked through the mingw style `ld.lld` interface (when invoked through the compiler), it prints error messages with a `ld.lld` prefix (since https://github.com/llvm/llvm-project/commit/1c8bb625b716b647206bcf5f51ac1c35...), while it prints `lld-link` if invoked that way. In `lld-link` mode, it doesn't define such mingw specifics, unless you pass the `-lldmingw` option.
-- https://gitlab.winehq.org/wine/wine/-/merge_requests/9758#note_128719
On Tue Feb 3 21:57:35 2026 +0000, Martin Storsjö wrote:
It only does this when the linker is running in mingw mode, see https://github.com/llvm/llvm-project/blob/main/lld/COFF/Writer.cpp#L1308-L13.... If invoked through the mingw style `ld.lld` interface (when invoked through the compiler), it prints error messages with a `ld.lld` prefix (since https://github.com/llvm/llvm-project/commit/1c8bb625b716b647206bcf5f51ac1c35...), while it prints `lld-link` if invoked that way. In `lld-link` mode, it doesn't define such mingw specifics, unless you pass the `-lldmingw` option. Thanks! That makes sense. So what's the best thing to do here? Should wine be using `ld.lld` when using llvm in MinGW mode? Or maybe I just need to add `-lldmingw`?
-- https://gitlab.winehq.org/wine/wine/-/merge_requests/9758#note_128730
On Wed Feb 4 00:48:08 2026 +0000, Yuxuan Shui wrote:
Thanks! That makes sense. So what's the best thing to do here? Should wine be using `ld.lld` when using llvm in MinGW mode? Or maybe I just need to add `-lldmingw`? I think we should just accept that, when linking in MSVC mode, GCC-style constructors will not work. We could probably just emit fake `__CTOR_LIST__`/`__DTOR_LIST__` symbols in winebuild to make crt happy.
(Another possibility would be to implement those on top of the existing `.CRT` section in a separate object file, which we'd pull only for mingw targets. I'm not sure if it'd be worth complications.) -- https://gitlab.winehq.org/wine/wine/-/merge_requests/9758#note_128763
On Wed Feb 4 12:29:06 2026 +0000, Jacek Caban wrote:
I think we should just accept that, when linking in MSVC mode, GCC-style constructors will not work. We could probably just emit fake `__CTOR_LIST__`/`__DTOR_LIST__` symbols in winebuild to make crt happy. (Another possibility would be to implement those on top of the existing `.CRT` section in a separate object file, which we'd pull only for mingw targets. I'm not sure if it'd be worth complications.) hi. for MSVC mode, this MR uses `-alternatename` to define fallback `__CTOR_LIST__`/`__DTOR_LIST__`, so it's working. this undefined symbols problem comes from using llvm in MinGW mode.
we are using `lld-link` with llvm-mingw, which according to Martin doesn't define some MinGW specific symbols. so sounds like maybe we are supposed to use `lld-link` for llvm MSVC, and `ld.lld` for llvm mingw? -- https://gitlab.winehq.org/wine/wine/-/merge_requests/9758#note_128764
I was wondering if we could implement it on top of .CRT sections to make sharing it among different entry points easier. See https://gitlab.winehq.org/jacek/wine/-/commit/e7f61649b3b725281cb5c6198966fe... for a possible solution. What do you think? I don't see a way to avoid winegcc change, but other than that it seems straightforward. -- https://gitlab.winehq.org/wine/wine/-/merge_requests/9758#note_129402
On Wed Feb 11 21:32:38 2026 +0000, Jacek Caban wrote:
llvm-mingw in mingw mode defines it. it only defines it if the linker is `ld.lld`, but we seem to be [using](https://gitlab.winehq.org/wine/wine/-/jobs/225794#L121) `lld-link`?
-- https://gitlab.winehq.org/wine/wine/-/merge_requests/9758#note_129505
On Thu Feb 12 04:40:44 2026 +0000, Yuxuan Shui wrote:
it only defines it if the linker is `ld.lld`, but we seem to be [using](https://gitlab.winehq.org/wine/wine/-/jobs/225794#L121) `lld-link`? Yes, CI uses lld-link, but it's because is uses Clang's MSVC mode. In the commit I linked, we pull gcc_constr.o only for mingw mode, so we'd never reference `__CTOR_LIST__` in MSVC mode.
(I know it may be a bit confusing; we install llvm-mingw and use clang from that instalation, but we use it in MSVC mode. If you'd like to try it in mingw mode, it requires `--with-mingw=llvm-mingw` configure option). -- https://gitlab.winehq.org/wine/wine/-/merge_requests/9758#note_129515
On Thu Feb 12 11:31:00 2026 +0000, Jacek Caban wrote:
Yes, CI uses lld-link, but it's because is uses Clang's MSVC mode. In the commit I linked, we pull gcc_constr.o only for mingw mode, so we'd never reference `__CTOR_LIST__` in MSVC mode. (I know it may be a bit confusing; we install llvm-mingw and use clang from that instalation, but we use it in MSVC mode. If you'd like to try it in mingw mode, it requires `--with-mingw=llvm-mingw` configure option). makes sense. should i close this MR and let you open one from your commit?
-- https://gitlab.winehq.org/wine/wine/-/merge_requests/9758#note_129541
On Thu Feb 12 20:02:06 2026 +0000, Yuxuan Shui wrote:
makes sense. should i close this MR and let you open one from your commit? I included its modified version in !10121.
-- https://gitlab.winehq.org/wine/wine/-/merge_requests/9758#note_129814
This merge request was closed by Jacek Caban. -- https://gitlab.winehq.org/wine/wine/-/merge_requests/9758
Superseded by !10121. -- https://gitlab.winehq.org/wine/wine/-/merge_requests/9758#note_130126
participants (4)
-
Jacek Caban (@jacek) -
Martin Storsjö (@mstorsjo) -
Yuxuan Shui -
Yuxuan Shui (@yshui)