Skip to content

Commit

Permalink
MSVC: Add support for linking against the "spectre-mitigated" CRT (#673)
Browse files Browse the repository at this point in the history
Issue Details:
Since VS 2017, MSVC has shipped a set of "spectre-mitigated" CRT static libs: https://devblogs.microsoft.com/cppblog/spectre-mitigations-in-msvc/

Typically these are used by opening a VS Command Prompt in "spectre mode" (https://docs.microsoft.com/en-us/cpp/build/building-on-the-command-line?view=msvc-170#vcvarsall-syntax) which then sets the `LIB` environment variable to point to the directory with the spectre-mitigated libs. However, since `cc` builds its own `LIB` environment variable, it uses the non-spectre-mitigated libs even when invoked from a VS Command Prompt in "spectre mode". This causes issues when trying to build a spectre-mitigated binary using Rust, as `rustc` uses `cc` for linking.

Fix Details:
When `cc` detects that the `VSCMD_ARG_VCVARS_SPECTRE` environment variable is set to `spectre` (either by being run from a VS Command Prompt in "spectre mode", or users may explicitly set this themselves), it will use the spectre-mitigated lib directory when building its `LIB` environment variable.
  • Loading branch information
dpaoliello authored Apr 30, 2024
1 parent 994dc90 commit 42e98da
Show file tree
Hide file tree
Showing 2 changed files with 36 additions and 5 deletions.
28 changes: 25 additions & 3 deletions dev-tools/cc-test/build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -83,9 +83,6 @@ fn main() {
cc::Build::new().file("src/windows.c").compile("windows");
}

// Test that the `windows_registry` module will set PATH by looking for
// nmake which runs vanilla cl, and then also test it after we remove all
// the relevant env vars from our own process.
if target.contains("msvc") {
let cc_frontend = if compiler.is_like_msvc() {
"MSVC"
Expand All @@ -95,6 +92,9 @@ fn main() {
unimplemented!("Unknown compiler that targets msvc but isn't clang-like or msvc-like")
};

// Test that the `windows_registry` module will set PATH by looking for
// nmake which runs vanilla cl, and then also test it after we remove all
// the relevant env vars from our own process.
let out = out.join("tmp");
fs::create_dir(&out).unwrap();
println!("nmake 1");
Expand Down Expand Up @@ -130,6 +130,28 @@ fn main() {
assert!(status.success());
println!("cargo:rustc-link-lib=msvc");
println!("cargo:rustc-link-search={}", out.display());

// Test that the `windows_registry` module detects if we're in a "spectre
// mode" VS environment.
fn has_spectre(target: &str) -> bool {
cc::windows_registry::find_tool(target, "cl.exe")
.unwrap()
.env()
.iter()
.any(|(k, v)| (k == "LIB") && v.to_str().unwrap().contains(r"\lib\spectre\"))
}

std::env::set_var("VSCMD_ARG_VCVARS_SPECTRE", "spectre");
assert!(
has_spectre(&target),
"LIB should use spectre-mitigated libs when VSCMD_ARG_VCVARS_SPECTRE is set"
);

std::env::remove_var("VSCMD_ARG_VCVARS_SPECTRE");
assert!(
!has_spectre(&target),
"LIB should not use spectre-mitigated libs when VSCMD_ARG_VCVARS_SPECTRE is not set"
);
}

// This tests whether we can build a library but not link it to the main
Expand Down
13 changes: 11 additions & 2 deletions src/windows/find_tools.rs
Original file line number Diff line number Diff line change
Expand Up @@ -575,8 +575,13 @@ mod impl_ {
// architecture, because it contains dlls like mspdb140.dll compiled for
// the host architecture.
let host_dylib_path = host_path.join(host.to_lowercase());
let lib_path = path.join("lib").join(target);
let alt_lib_path = (target == "arm64ec").then(|| path.join("lib").join("arm64ec"));
let lib_fragment = if use_spectre_mitigated_libs() {
r"lib\spectre"
} else {
"lib"
};
let lib_path = path.join(lib_fragment).join(target);
let alt_lib_path = (target == "arm64ec").then(|| path.join(lib_fragment).join("arm64ec"));
let include_path = path.join("include");
Some((
path,
Expand Down Expand Up @@ -625,6 +630,10 @@ mod impl_ {
Some(version)
}

fn use_spectre_mitigated_libs() -> bool {
env::var("VSCMD_ARG_VCVARS_SPECTRE").as_deref() == Ok("spectre")
}

fn atl_paths(target: TargetArch<'_>, path: &Path) -> Option<(PathBuf, PathBuf)> {
let atl_path = path.join("atlmfc");
let sub = lib_subdir(target)?;
Expand Down

0 comments on commit 42e98da

Please sign in to comment.