Change:
<!-- Thank you for filing a regression report! A regression is something that changed between versions of Rust but was not supposed to. Please provide a short summary of the regression, along with any information you feel is relevant to replicate it. --I tried to build using a simple then . I expected to see this happen: linking success. Instead, this happened: MSVC link returns LNK2005 and fails to the static library. It most recently worked on: 1.77.2 <!-- Provide the version you are using that has the regression. --: This is caused by both and When creating stdlibs, each MR causes the Rust compiler to add an export of ; one comes from api-ms-win-core-synch-l1-2-0.dll and the other from This error will not be found until one consumes the Rust-generated library. I am filing this report to consolidate the ones in and <!-- Did the compiler crash? If so, please provide a backtrace. --<!-- Include a backtrace in the code block by setting in your environment. E.g. . --<details<summaryBacktrace</summary<p</p</details modify labels: +regression-from-stable-to-stable -regression-untriaged
cc
Using for a rust staticlib seems weird to me but cc
The purpose is to allow reexporting a subset of symbols on MSVC. This can be achieved with partial linking elsewhere, but this is not doable on Windows. I wrote about a specific application here:
Sorry, I'm confused, are you trying to create a dynamic or static library? The article that you linked to talks about static libraries, but the backtrace in the OP shows that you are creating a dynamic library.
Both: I'm using the Rust-produced static library through , plus a module definition file, to create a dynamic library. This is because (apart from size considerations) GNOME's gobject-introspection needs a shared library, and Meson will only accept targets that are generated by itself. Further context is here:
In this particular case, because of the imports present in the stdlib, linking something against the Rust-produced static librsvg is enough to trigger the issue.
I am still struggling to understand why is needed, at least for MSVC. So the DLL is created here: is used so that the DLL includes every single object in the staticlib, even if it's unneeded or indeed causes conflicts. But then is used to add a which presumably defines what actually gets exported from the DLL. What I'm not understanding is what would be different if, for MSVC, it just linked normally without ? I.e. using instead.
On MSVC, static linking doesn't let you reexport dependencies' symbols automatically, they're subject to DCE. . However, PE-COFF static libraries don't support symbol visibility, so the is there to make all Rust symbols private and keep only the librsvg ones. See e.g. and for consequences of not blocking out symbols like .
Sure but the should work regardless of if is used or not because using the export will force the linker to go looking for the symbol(s) listed in the file. E.g. as a test I have the following static lib (using to make symbols easier to find): Which I compile into a static lib using: rustc -O --crate-type=staticlib And I have the following file: Which for simplicity I compile via rustc but I'm using an empty file and also raw linker arguments: rustc -O --crate-type=cdylib -C link-args=" " This compiles and if I see the two functions I wanted: No std symbols are exported.
Independent of whether whole-archive is necessary for this specific use case, we should support linking rust staticlibs with whole-archive, even if because of the fact that not linking with whole-archive can mask linker errors, which will later show up when any change to the cgu partitioning is made, which often causes people to report a regression even though their crate is at fault.
If I repeat the above using even in this simple case I get a bunch of errors, not just : I'm not really sure if rustc can currently guarantee that whole archive will work without doing some of the work of a linker. Which sounds like a new feature and not specific to MSVC.
All duplicate symbol errors for are caused by That it works without is by accident and likely to break in case of cgu partitioning changes.
Sure, and then if I compile the static library with and use whole archive then I get a whole slew of errors, which I'll spoiler for length <details<summarylinker errors</summary</details
There are also some technical details in that kleisauke linked. windows-gnu is unaffected because it uses legacy import libraries format.
To get back to address case we could either: Filter symbols when building a staticlib Try using COMDAT in generated raw-dylibs (might not work in this situation)
Filter in what way?
When linking raw dylibs into a staticlib we could exclude the objects containing . Except for one.

Discussion:
[[package]] name = "ar_archive_writer" <del> version = "0.4.0" </del> <ins> version = "0.4.2" </ins> source = "registry+https://github.com/rust-lang/crates.io-index" <del> checksum = "de11a9d32db3327f981143bdf699ade4d637c6887b13b97e6e91a9154666963c" </del> <ins> checksum = "01667f6f40216b9a0b2945e05fed5f1ad0ab6470e69cb9378001e37b1c0668e4" </ins> dependencies = [ "object 0.36.2", ]

[dependencies] # tidy-alphabetical-start <del> ar_archive_writer = "0.4.0" </del> <ins> ar_archive_writer = "0.4.2" </ins> arrayvec = { version = "0.7", default-features = false } bitflags = "2.4.1" cc = "1.0.90"

&exports, machine, !sess.target.is_like_msvc, <del> /*comdat=*/ false, </del> <ins> // Enable compatibility with MSVC's `/WHOLEARCHIVE` flag. // Without this flag a duplicate symbol error would be emitted // when linking a rust staticlib using `/WHOLEARCHIVE`. // See #129020 true, </ins> ) { sess.dcx() .emit_fatal(ErrorCreatingImportLibrary { lib_name, error: error.to_string() });

<ins> //! This is a regression test for #129020 //! It ensures we can use `/WHOLEARCHIVE` to link a rust staticlib into DLL //! using the MSVC linker //@ only-msvc // Reason: this is testing the MSVC linker use std::path::PathBuf; use run_make_support::{cc, cmd, env_var, extra_c_flags, rustc}; fn main() { // Build the staticlib rustc().crate_type("staticlib").input("static.rs").output("static.lib").run(); // Build an empty object to pass to the linker. cc().input("c.c").output("c.obj").args(["-c"]).run(); // Find the C toolchain's linker. let mut linker = PathBuf::from(env_var("CC")); let linker_flavour = if linker.file_stem().is_some_and(|s| s == "cl") { linker.set_file_name("link.exe"); "msvc" } else if linker.file_stem().is_some_and(|s| s == "clang-cl") { linker.set_file_name("lld-link.exe"); "llvm" } else { panic!("unknown C toolchain"); }; // As a sanity check, make sure this works without /WHOLEARCHIVE. // Otherwise the actual test failure may be caused by something else. cmd(&linker) .args(["c.obj", "./static.lib", "-dll", "-def:dll.def", "-out:dll.dll"]) .args(extra_c_flags()) .run(); // FIXME(@ChrisDenton): this doesn't currently work with llvm's lld-link for other reasons. // May need LLVM patches. if linker_flavour == "msvc" { // Link in the staticlib using `/WHOLEARCHIVE` and produce a DLL. cmd(&linker) .args([ "c.obj", "-WHOLEARCHIVE:./static.lib", "-dll", "-def:dll.def", "-out:dll_whole_archive.dll", ]) .args(extra_c_flags()) .run(); } } </ins>

<ins> #[no_mangle] pub extern "C" fn hello() { println!("Hello world!"); } #[no_mangle] pub extern "C" fn number() -> u32 { 42 } </ins>