Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

rustc always links against non-debug Windows runtime #39016

Open
jdm opened this issue Jan 12, 2017 · 27 comments
Open

rustc always links against non-debug Windows runtime #39016

jdm opened this issue Jan 12, 2017 · 27 comments
Labels
C-feature-request Category: A feature request, i.e: not implemented / a PR. O-windows-msvc Toolchain: MSVC, Operating system: Windows T-libs Relevant to the library team, which will review and decide on the PR/issue.

Comments

@jdm
Copy link
Contributor

jdm commented Jan 12, 2017

When building rust-mozjs with debug symbols enabled, the underlying C++ library links against the debug Windows runtime (msvcrtd.lib) rather than the regular one (msvcrt.lib). Unfortunately, rustc always links against msvcrt.lib unconditionally:

#[link(name = "msvcrt")]
. This makes it impossible to build a debug-enabled version of rust-mozjs on Windows.

@jdm jdm added the O-windows-msvc Toolchain: MSVC, Operating system: Windows label Jan 12, 2017
@jdm
Copy link
Contributor Author

jdm commented Jan 12, 2017

Would there be any objection to doing something like:

#[cfg(all(target_env = "msvc", debug))] // " if " -- appease style checker
#[link(name = "msvcrtd")]
extern {}

#[cfg(all(target_env = "msvc", not(debug_assertions)))] // " if " -- appease style checker
#[link(name = "msvcrt")]
extern {}

cc @retep998

@retep998
Copy link
Member

retep998 commented Jan 12, 2017

We already have a feature for choosing whether to link the static or dynamic CRT. It would be a fairly trivial matter to add another feature to choose whether to link the debug or release CRT (which I did suggest but nobody seemed to notice). I'd rather not tie this sort of thing to whether optimizations are enabled.

@jdm
Copy link
Contributor Author

jdm commented Jan 23, 2017

@retep998 says that the way to fix this is to find the relevant bits in #37545 and duplicate it for crt-debug.

@Mark-Simulacrum Mark-Simulacrum added T-dev-tools Relevant to the dev-tools subteam, which will review and decide on the PR/issue. and removed T-tools labels May 24, 2017
@Mark-Simulacrum Mark-Simulacrum added C-bug Category: This is a bug. C-feature-request Category: A feature request, i.e: not implemented / a PR. and removed C-bug Category: This is a bug. labels Jul 26, 2017
@nox
Copy link
Contributor

nox commented Oct 7, 2017

#37545 landed, what is needed to make some progress on this?

@retep998
Copy link
Member

retep998 commented Oct 7, 2017

@nox As I said before, someone has to find the relevant bits in #37545 and duplicate it for crt-debug.

@asajeffrey
Copy link

This cost me a couple of days debugging why debugmozjs builds of mozjs_sys were failing with link errors under msvc.

@Neurrone
Copy link

Neurrone commented Sep 3, 2018

Would love to be able to configure this as well for c++ code.

@jdm
Copy link
Contributor Author

jdm commented Jul 9, 2019

It would make sense to transfer this issue to https://github.com/rust-lang/libc now that the responsible code is in that repository, but I don't have the permissions to do so.

@gnzlbg
Copy link
Contributor

gnzlbg commented Jul 9, 2019

cc @alexcrichton @retep998 @newpavlov - please review the libc C PR at: rust-lang/libc#1433 - I'm not sure how this will interact with linking libc both via libstd and crates.io, what happens if the features are different in each, what happens if the binary is #[no_std] and no libstd is linked, etc.

@retep998
Copy link
Member

Solving this issue requires changes both in rust and libc.

@gnzlbg
Copy link
Contributor

gnzlbg commented Aug 28, 2019

Since libstd depends on libc, and libc would need to be compiled differently for debug and release on windows, wouldn't we need to ship two libstds one for debug and one for release ?

@retep998
Copy link
Member

Only if there are actual changes to the bindings in libc depending on debug vs release. Simply linking to a different CRT is already handled in std's libc using deferred linking decisions.

@upsuper
Copy link
Contributor

upsuper commented Jan 3, 2020

This looks like a pretty big footgun, and it can cause mysterious runtime error taking people lots of time to figure out. Until this gets fixed, there should probably be some document / compiler message warning about this, so that people know that they need to have their C/C++ program always link against the non-debug crt.

@nox
Copy link
Contributor

nox commented Feb 20, 2020

I'm curious how this can be done cleanly, when the base windows target itself adds -lmsvcrt directly into the link args.

bors bot added a commit to ddnet/ddnet that referenced this issue Nov 6, 2022
5599: Add support for Rust code in DDNet r=def- a=heinrich5991

The glue is done using the [cxx crate](https://cxx.rs/) on the Rust side.

As a proof-of-concept, only a small console command (`rust_version`) printing the currently used Rust version was added.

You can generate and open the Rust documentation using `DDNET_TEST_NO_LINK=1 cargo doc --open`.

You can run the Rust tests using `cmake --build <build dir> --target run_rust_tests`, they're automatically included in the `run_tests` target as well.

Rust tests don't work on Windows in debug mode on Windows because Rust cannot currently link with the debug version of the C stdlib on Windows: rust-lang/rust#39016.

---

The stuff in `src/rust-bridge` is generated using
```
cxxbridge src/engine/shared/rust_version.rs --output src/rust-bridge/engine/shared/rust_version.cpp --output src/rust-bridge/engine/shared/rust_version.h
cxxbridge src/engine/console.rs --output src/rust-bridge/cpp/console.cpp --output src/rust-bridge/cpp/console.h
```

Co-authored-by: heinrich5991 <[email protected]>
@ChrisDenton
Copy link
Member

ChrisDenton commented Mar 30, 2023

I really want to find a way forward here as it's a common issue when trying to integrate Rust with existing C/C++ builds.

I do find it slightly odd that linking the C runtime is the responsibility of the libc crate on Windows. That seems unusual compared to other platforms? But ok, I can live with that.

@petrochenkov suggested adding a feature in people's Cargo.toml, which seems a bit odd for this? Like I'm not entirely clear how this will work in practice without special Cargo support, otherwise it puts a lot of the effort of supporting this onto crate authors. A global --cfg (i.e. set in rustflags) might work better. It is super important that everything agrees on which CRT to use. Though I'm still not clear on the benefits of --cfg over a compiler codegen option.

So to sum up I think to do this outside of rustc will require changes to:

  • libc needs additional #[link(name = "...", cfg(...))] options
  • cc needs to be able understand these target cfgs
  • rustc needs to document that the special cfg name(s) are reserved for this use

Is that right?

Btw, there should also be an option to not link a CRT. This is the default for no_std on Windows but is not yet supported for std builds. Having this option is important for more exotic CRTs.

@ChrisDenton ChrisDenton added T-libs Relevant to the library team, which will review and decide on the PR/issue. and removed T-dev-tools Relevant to the dev-tools subteam, which will review and decide on the PR/issue. labels Apr 14, 2023
@danakj
Copy link
Contributor

danakj commented Apr 19, 2023

Hitting this issue in Chromium: https://bugs.chromium.org/p/chromium/issues/detail?id=1434719

dcbaker added a commit to dcbaker/meson that referenced this issue Jun 23, 2023
Rust by default links with the default MSVCRT, (dynamic, release).
MSVCRT's cannot be mixed, so if Meson compiles a C or C++ library and
links it with the debug MSVCRT, then tries to link that with the Rust
library there will be failures. There is no built-in way to fix this for
rustc, so as a workaround we inject the correct arguments early in the
linker line (before any libs at least) to change the runtime. This seems
to work and is recommended as workaround in the upstream rust bug
report: rust-lang/rust#39016.

Given that this bug report has been opened since 2017, it seems unlikely
to be fixed anytime soon, and affects all (currently) released versions
of Rust.
dcbaker added a commit to dcbaker/meson that referenced this issue Jun 26, 2023
Rust by default links with the default MSVCRT, (dynamic, release).
MSVCRT's cannot be mixed, so if Meson compiles a C or C++ library and
links it with the debug MSVCRT, then tries to link that with the Rust
library there will be failures. There is no built-in way to fix this for
rustc, so as a workaround we inject the correct arguments early in the
linker line (before any libs at least) to change the runtime. This seems
to work and is recommended as workaround in the upstream rust bug
report: rust-lang/rust#39016.

Given that this bug report has been opened since 2017, it seems unlikely
to be fixed anytime soon, and affects all (currently) released versions
of Rust.
dcbaker added a commit to dcbaker/meson that referenced this issue Jun 26, 2023
Rust by default links with the default MSVCRT, (dynamic, release).
MSVCRT's cannot be mixed, so if Meson compiles a C or C++ library and
links it with the debug MSVCRT, then tries to link that with the Rust
library there will be failures. There is no built-in way to fix this for
rustc, so as a workaround we inject the correct arguments early in the
linker line (before any libs at least) to change the runtime. This seems
to work and is recommended as workaround in the upstream rust bug
report: rust-lang/rust#39016.

Given that this bug report has been opened since 2017, it seems unlikely
to be fixed anytime soon, and affects all (currently) released versions
of Rust.
dcbaker added a commit to dcbaker/meson that referenced this issue Jun 26, 2023
Rust by default links with the default MSVCRT, (dynamic, release).
MSVCRT's cannot be mixed, so if Meson compiles a C or C++ library and
links it with the debug MSVCRT, then tries to link that with the Rust
library there will be failures. There is no built-in way to fix this for
rustc, so as a workaround we inject the correct arguments early in the
linker line (before any libs at least) to change the runtime. This seems
to work and is recommended as workaround in the upstream rust bug
report: rust-lang/rust#39016.

Given that this bug report has been opened since 2017, it seems unlikely
to be fixed anytime soon, and affects all (currently) released versions
of Rust.
dcbaker added a commit to dcbaker/meson that referenced this issue Jun 26, 2023
Rust by default links with the default MSVCRT, (dynamic, release).
MSVCRT's cannot be mixed, so if Meson compiles a C or C++ library and
links it with the debug MSVCRT, then tries to link that with the Rust
library there will be failures. There is no built-in way to fix this for
rustc, so as a workaround we inject the correct arguments early in the
linker line (before any libs at least) to change the runtime. This seems
to work and is recommended as workaround in the upstream rust bug
report: rust-lang/rust#39016.

Given that this bug report has been opened since 2017, it seems unlikely
to be fixed anytime soon, and affects all (currently) released versions
of Rust.
dcbaker added a commit to dcbaker/meson that referenced this issue Jun 27, 2023
Rust by default links with the default MSVCRT, (dynamic, release).
MSVCRT's cannot be mixed, so if Meson compiles a C or C++ library and
links it with the debug MSVCRT, then tries to link that with the Rust
library there will be failures. There is no built-in way to fix this for
rustc, so as a workaround we inject the correct arguments early in the
linker line (before any libs at least) to change the runtime. This seems
to work and is recommended as workaround in the upstream rust bug
report: rust-lang/rust#39016.

Given that this bug report has been opened since 2017, it seems unlikely
to be fixed anytime soon, and affects all (currently) released versions
of Rust.
dcbaker added a commit to dcbaker/meson that referenced this issue Jun 27, 2023
Rust by default links with the default MSVCRT, (dynamic, release).
MSVCRT's cannot be mixed, so if Meson compiles a C or C++ library and
links it with the debug MSVCRT, then tries to link that with the Rust
library there will be failures. There is no built-in way to fix this for
rustc, so as a workaround we inject the correct arguments early in the
linker line (before any libs at least) to change the runtime. This seems
to work and is recommended as workaround in the upstream rust bug
report: rust-lang/rust#39016.

Given that this bug report has been opened since 2017, it seems unlikely
to be fixed anytime soon, and affects all (currently) released versions
of Rust.
dcbaker added a commit to dcbaker/meson that referenced this issue Jun 27, 2023
Rust by default links with the default MSVCRT, (dynamic, release).
MSVCRT's cannot be mixed, so if Meson compiles a C or C++ library and
links it with the debug MSVCRT, then tries to link that with the Rust
library there will be failures. There is no built-in way to fix this for
rustc, so as a workaround we inject the correct arguments early in the
linker line (before any libs at least) to change the runtime. This seems
to work and is recommended as workaround in the upstream rust bug
report: rust-lang/rust#39016.

Given that this bug report has been opened since 2017, it seems unlikely
to be fixed anytime soon, and affects all (currently) released versions
of Rust.
Dudemanguy pushed a commit to Dudemanguy/meson that referenced this issue Aug 7, 2023
Rust by default links with the default MSVCRT, (dynamic, release).
MSVCRT's cannot be mixed, so if Meson compiles a C or C++ library and
links it with the debug MSVCRT, then tries to link that with the Rust
library there will be failures. There is no built-in way to fix this for
rustc, so as a workaround we inject the correct arguments early in the
linker line (before any libs at least) to change the runtime. This seems
to work and is recommended as workaround in the upstream rust bug
report: rust-lang/rust#39016.

Given that this bug report has been opened since 2017, it seems unlikely
to be fixed anytime soon, and affects all (currently) released versions
of Rust.
@ChrisDenton
Copy link
Member

ChrisDenton commented Oct 3, 2024

Update on this: You can now use a build script (or other build method) to disable the default CRT for a binary. Then you can add back the one you want. However, you need to use a linker argument:

For the dynamic CRT use: /nodefaultlib:msvcrt
For the static CRT use: /nodefaultlib:libcmt

This can be done in a build script, for example:

fn main() {
    // Don't link the default CRT
    println!("cargo::rustc-link-arg-bins=/nodefaultlib:msvcrt")`
    // Link the debug CRT instead
    println!("cargo::rustc-link-arg-bins=/defaultlib:msvcrtd")`
}

Which isn't as nice as having a proper feature but it's at least serviceable.

@sagudev
Copy link
Contributor

sagudev commented Oct 3, 2024

Update on this: You can now use a build script (or other build method) to disable the default CRT for a binary. Then you can add back the one you want. However, you need to use a linker argument:

For the dynamic CRT use: /nodefaultlib:msvcrt For the static CRT use: /nodefaultlib:libcmt

This can be done in a build script, for example:

fn main() {
// Don't link the default CRT
println!("cargo::rustc-link-arg-bins=/nodefaultlib:msvcrt") // Link the debug CRT instead println!("cargo::rustc-link-arg-bins=/defaultlib:msvcrtd")
}

Which isn't as nice as having a proper feature but it's at least serviceable.

What is MSRV for this?

@ChrisDenton
Copy link
Member

1.79

@retep998
Copy link
Member

retep998 commented Oct 6, 2024

Also note that if you do that, you need to make sure every C/C++ library you link against was built using the correct version of the CRT as well. If you end up mixing them then you're going to have a bad time with a lot of linker errors.

@spangaer
Copy link

spangaer commented Dec 5, 2024

Update on this: You can now use a build script (or other build method) to disable the default CRT for a binary. Then you can add back the one you want. However, you need to use a linker argument:

For the dynamic CRT use: /nodefaultlib:msvcrt For the static CRT use: /nodefaultlib:libcmt

This can be done in a build script, for example:

fn main() {
// Don't link the default CRT
println!("cargo::rustc-link-arg-bins=/nodefaultlib:msvcrt") // Link the debug CRT instead println!("cargo::rustc-link-arg-bins=/defaultlib:msvcrtd")
}

Which isn't as nice as having a proper feature but it's at least serviceable.

Thx for this, it helped me a great deal!

It does seem like it does work on Rust 1.79, but does work on 1.83.

I've documented in great detail here: dtolnay/cxx#880 (comment)

Here's my working build.rs construct.

if env::var("TARGET").is_ok_and(|s| s.contains("windows-msvc")) {
    // MSVC compiler suite
    if env::var("CFLAGS").is_ok_and(|s| s.contains("/MDd")) {
        // debug runtime flag is set

        // Don't link the default CRT
        println!("cargo::rustc-link-arg=/nodefaultlib:msvcrt");
        // Link the debug CRT instead
        println!("cargo::rustc-link-arg=/defaultlib:msvcrtd");
    }
}

Note that, if you're not using CFLAGS env var for your build, you should use PROFILE for the second if check

if Ok("debug".to_owned()) == env::var("PROFILE") {

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
C-feature-request Category: A feature request, i.e: not implemented / a PR. O-windows-msvc Toolchain: MSVC, Operating system: Windows T-libs Relevant to the library team, which will review and decide on the PR/issue.
Projects
None yet
Development

Successfully merging a pull request may close this issue.