MinGW compilers come in multiple flavours, "mingw", "ucrt" and "clang"/"llvm". This asks for the question how many Rust targets there should be. While both MinGW GCC and LLVM toolchains use the same C run-times, they are utilizing different exception handling mechanisms. This makes it impossible to produce a "universal" non-MSVC Windows target that would work with both GCC and Clang. But what about "mingw" vs. "ucrt"? The suggested approach aims to facilitate run-time-neutral <arch>-pc-windows-gnu
target. In more practical terms, after executing rustup target add <arch>-pc-windows-gnu
users will be free to choose between msvcrt and ucrt by simply adjusting their PATH
environment variable(**).
The key component is dllmain.rs
, a trimmed-down MinGW CRT initialization module, that should allow linking std-<release-id>.dll
without reference to a specific run-time, legacy msvcrt.dll or Universal CRT.
While compiling with -C prefer-dynamic
might not be that popular, the fact that the std-<release-id>.dll
can be linked without reference to a specific run-time strongly suggests that the static libstd-<release-id>.rlib
and Rust-generated code in general don't actually have the dependency either. Or rather it's not strong enough(*) to prevent the choice of the run-time to be postponed till the application link time. This is the main proposition.
The rest of the crate is a test-bed PoC around the dllmain.rs
. You're more than likely going to have to run cargo run ...
twice to test. This is because application code gets linked before the library is available. If you know how to arrange the link invocations in specific order, do tell:-) Either way, once confirmed to execute, double-check that poc.dll
doesn't have imports from your CRT. The test registers a pre-main subroutine, an equivalent to __attribute__((constructor))
in C, spawns a rayon thread and prints the value set by the "constructor."
Even though the primary target is MinGW, the dllmain.rs and PoC work even with MSVC.
(*) Rust compiler depends on memcpy/move/set/cmp and strlen being externally available. These are interchangeable between all known run-times.
(**) Caveat lector. By default Rust compiler drivers invokes x86_64-w64-mingw32-gcc
to link the final binary. The catch is that some MinGW clang packages were spotted to provide x86_64-w64-mingw32-gcc
, presumably to smooth the switch. Since it's the C compiler driver that is responsible for linking the exception handling library, it would be a problem if the exception flavours chosen by Rust and first-on-the-PATH C compiler mismatch. In the context one can make a case for a sanity check for a matching C compiler. Most notably MinGW GCC target driver could verify that the library search path in x86_64-w64-mingw32-gcc -print-search-dirs
output has corresponding exception handling library, libgcc_eh.a
.