From 6d394d0e319f7169684fbe6054eed82d3c866f60 Mon Sep 17 00:00:00 2001 From: Daniel Paoliello Date: Tue, 12 Apr 2022 12:51:04 -0700 Subject: [PATCH] MSVC: Add support for linking against the "spectre-mitigated" CRT 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. --- cc-test/build.rs | 26 +++++++++++++++++++++++--- src/windows_registry.rs | 12 +++++++++++- 2 files changed, 34 insertions(+), 4 deletions(-) diff --git a/cc-test/build.rs b/cc-test/build.rs index 695c75473..88478c12f 100644 --- a/cc-test/build.rs +++ b/cc-test/build.rs @@ -57,10 +57,10 @@ 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") { + // 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"); @@ -91,6 +91,26 @@ 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_registry.rs b/src/windows_registry.rs index 549082b30..6608e4e33 100644 --- a/src/windows_registry.rs +++ b/src/windows_registry.rs @@ -488,11 +488,21 @@ mod impl_ { .join("bin") .join(&format!("Host{}", host)) .join(&host.to_lowercase()); - let lib_path = path.join("lib").join(&target); + let lib_path = path + .join(if use_spectre_mitigated_libs() { + r"lib\spectre" + } else { + "lib" + }) + .join(&target); let include_path = path.join("include"); Some((bin_path, host_dylib_path, lib_path, include_path)) } + fn use_spectre_mitigated_libs() -> bool { + env::var("VSCMD_ARG_VCVARS_SPECTRE").as_deref() == Ok("spectre") + } + fn atl_paths(target: &str, path: &Path) -> Option<(PathBuf, PathBuf)> { let atl_path = path.join("atlmfc"); let sub = lib_subdir(target)?;