From 42e98da2954e508c57f497989d5b981146a0fc25 Mon Sep 17 00:00:00 2001 From: Daniel Paoliello Date: Mon, 29 Apr 2024 20:03:08 -0700 Subject: [PATCH] MSVC: Add support for linking against the "spectre-mitigated" CRT (#673) 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. --- dev-tools/cc-test/build.rs | 28 +++++++++++++++++++++++++--- src/windows/find_tools.rs | 13 +++++++++++-- 2 files changed, 36 insertions(+), 5 deletions(-) diff --git a/dev-tools/cc-test/build.rs b/dev-tools/cc-test/build.rs index f26cbbb6..6fa3c1cf 100644 --- a/dev-tools/cc-test/build.rs +++ b/dev-tools/cc-test/build.rs @@ -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" @@ -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"); @@ -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 diff --git a/src/windows/find_tools.rs b/src/windows/find_tools.rs index 79ce3cb0..b0061948 100644 --- a/src/windows/find_tools.rs +++ b/src/windows/find_tools.rs @@ -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, @@ -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)?;