Change:
There is a very particular set of circumstances that leads to the following error when compiling some C++ files and linking them into a Rust executable targeted at : Summary: I believe the Rust MinGW toolchain needs to be rebuilt or updated to include (which I believe is the dynamically linked ). I used the repository on a with MSYS2 and MingW32 installed. Unfortunately, I could not reproduce the issue on or , but it happens reliably on my VM. (I don't have MSVC installed, but I don't know if that's the reason.) These are the key files: : : : For context, here is the snippet that defines in  from mingw-w64: With nothing in the environment, the build succeeds: It's useful to look at the symbols in the library: With , the build fails: Looking at the symbols in the library confirms that is there (and that is not, as would be expected): One way to work around this issue is to link directly to the MinGW library: Looking at what was linked with , I found that the bundled library was being used. And, indeed, that library is missing : Looking at the symbols in MinGW's , I can now see why linking to it works: There are no definitions of the symbol in .
What is your gcc and g++ version you used? Both in success and failure cases.
If you're using an external MinGW toolchain to build C/C++ code you should also be using that MinGW toolchain to link everything, and not the MinGW that is bundled with Rust.
Both and on my VM are 7.4.0. And is 1.34.1, and is 1.34.0. If I understand the situation correctly, there is no internal-to-Rust MinGW toolchain that can build C/C++ code, because the bundled is only for linking, so I should use the system MinGW toolchain for linking with any crate that involves building C/C++. I've seen this advice mentioned elsewhere (not involving Rust), and it sounds reasonable, but I would appreciate it if you could give me some background reasoning behind it or point me to some sources. Is it because of differences in ABI, symbol names, or something else? Thanks! That said, I guess you might expect the following to make a difference in this issue: But it doesn't. The error is almost exactly the same as above. The only difference is is replaced by .
Is it because of differences in ABI, symbol names, or something else? Yes. Things change between MinGW versions and C/C++ code compiled against headers for a specific MinGW version are only guaranteed to link against libraries from that same MinGW version. Just changing the linker alone isn't enough because rustc still adds the paths to its bundled MinGW libraries in the linker invocation. You'd have to disable the bundled MinGW entirely.
Right. I forgot to set the there. (Edit: Actually, I realize this doesn't affect , but I have done this when invoking the underlying command that appears in the error.) How do I do that?
I cannot reproduce it with in up to date MINGW32 shell in MSYS2. There is similar issue , you can try copying important libraries into rust toolchain directory: Note: you may need to copy more libraries depending on the errors.
Does the symbol appear in your ? If not, can you guess where it might be found? Indeed! Copying and from to does work.
I won't have access to Windows until evening (Europe time) and I have already overridden Rust shipped libs. I haven't made backup because they were broken in other ways.

Discussion:
} } <ins> // Because windows-gnu target is meant to be self-contained for pure Rust code it bundles // own mingw-w64 libraries. These libraries are usually not compatible with mingw-w64 // installed in the system. This breaks many cases where Rust is mixed with other languages // (e.g. *-sys crates). // We prefer system mingw-w64 libraries if they are available to avoid this issue. fn get_crt_libs_path(sess: &Session) -> Option<PathBuf> { fn find_exe_in_path<P>(exe_name: P) -> Option<PathBuf> where P: AsRef<Path>, { for dir in env::split_paths(&env::var_os("PATH")?) { let full_path = dir.join(&exe_name); if full_path.is_file() { return Some(fix_windows_verbatim_for_gcc(&full_path)); } } None } fn probe(sess: &Session) -> Option<PathBuf> { if let (linker, LinkerFlavor::Gcc) = linker_and_flavor(&sess) { let linker_path = if cfg!(windows) && linker.extension().is_none() { linker.with_extension("exe") } else { linker }; if let Some(linker_path) = find_exe_in_path(linker_path) { let mingw_arch = match &sess.target.target.arch { x if x == "x86" => "i686", x => x, }; let mingw_dir = format!("{}-w64-mingw32", mingw_arch); // Here we have path/bin/gcc but we need path/ let mut path = linker_path; path.pop(); path.pop(); // Based on Clang MinGW driver let probe_path = path.join(&mingw_dir).join("lib"); if probe_path.exists() { return Some(probe_path); }; let probe_path = path.join(&mingw_dir).join("sys-root/mingw/lib"); if probe_path.exists() { return Some(probe_path); }; }; }; None } let mut system_library_path = sess.system_library_path.borrow_mut(); match &*system_library_path { Some(Some(compiler_libs_path)) => Some(compiler_libs_path.clone()), Some(None) => None, None => { let path = probe(sess); *system_library_path = Some(path.clone()); path } } } </ins> pub fn get_file_path(sess: &Session, name: &str) -> PathBuf { <ins> // prefer system {,dll}crt2.o libs, see get_crt_libs_path comment for more details if sess.target.target.llvm_target.contains("windows-gnu") { if let Some(compiler_libs_path) = get_crt_libs_path(sess) { let file_path = compiler_libs_path.join(name); if file_path.exists() { return file_path; } } } </ins> let fs = sess.target_filesearch(PathKind::Native); let file_path = fs.get_lib_path().join(name); if file_path.exists() {

// target descriptor let t = &sess.target.target; <ins> // prefer system mingw-w64 libs, see get_crt_libs_path comment for more details if cfg!(windows) && sess.target.target.llvm_target.contains("windows-gnu") { if let Some(compiler_libs_path) = get_crt_libs_path(sess) { cmd.include_path(&compiler_libs_path); } } </ins> cmd.include_path(&fix_windows_verbatim_for_gcc(&lib_path)); for obj in codegen_results.modules.iter().filter_map(|m| m.object.as_ref()) {

/// Mapping from ident span to path span for paths that don't exist as written, but that /// exist under `std`. For example, wrote `str::from_utf8` instead of `std::str::from_utf8`. pub confused_type_with_std_module: Lock<FxHashMap<Span, Span>>, <ins> /// Path for libraries that will take preference over libraries shipped by Rust. /// Used by windows-gnu targets to priortize system mingw-w64 libraries. pub system_library_path: OneThread<RefCell<Option<Option<PathBuf>>>>, </ins> } pub struct PerfStats {

driver_lint_caps, trait_methods_not_found: Lock::new(Default::default()), confused_type_with_std_module: Lock::new(Default::default()), <ins> system_library_path: OneThread::new(RefCell::new(Default::default())), </ins> }; validate_commandline_args_with_session_available(&sess);