diff --git a/compiler/rustc_codegen_llvm/src/context.rs b/compiler/rustc_codegen_llvm/src/context.rs index 242c6aed906b4..e6c5085cc0efd 100644 --- a/compiler/rustc_codegen_llvm/src/context.rs +++ b/compiler/rustc_codegen_llvm/src/context.rs @@ -351,6 +351,16 @@ pub unsafe fn create_module<'ll>( ); } + // Set module flag to enable Windows EHCont Guard (/guard:ehcont). + if sess.opts.unstable_opts.ehcont_guard { + llvm::LLVMRustAddModuleFlag( + llmod, + llvm::LLVMModFlagBehavior::Warning, + "ehcontguard\0".as_ptr() as *const _, + 1, + ) + } + // Insert `llvm.ident` metadata. // // On the wasm targets it will get hooked up to the "producer" sections diff --git a/compiler/rustc_codegen_ssa/src/back/link.rs b/compiler/rustc_codegen_ssa/src/back/link.rs index 33e8f352cd8ab..b35ffac91b7e2 100644 --- a/compiler/rustc_codegen_ssa/src/back/link.rs +++ b/compiler/rustc_codegen_ssa/src/back/link.rs @@ -2378,6 +2378,11 @@ fn add_order_independent_options( cmd.control_flow_guard(); } + // OBJECT-FILES-NO, AUDIT-ORDER + if sess.opts.unstable_opts.ehcont_guard { + cmd.ehcont_guard(); + } + add_rpath_args(cmd, sess, codegen_results, out_filename); } diff --git a/compiler/rustc_codegen_ssa/src/back/linker.rs b/compiler/rustc_codegen_ssa/src/back/linker.rs index 09434513e31e9..73c4aa9712d0c 100644 --- a/compiler/rustc_codegen_ssa/src/back/linker.rs +++ b/compiler/rustc_codegen_ssa/src/back/linker.rs @@ -185,6 +185,7 @@ pub trait Linker { fn optimize(&mut self); fn pgo_gen(&mut self); fn control_flow_guard(&mut self); + fn ehcont_guard(&mut self); fn debuginfo(&mut self, strip: Strip, natvis_debugger_visualizers: &[PathBuf]); fn no_crt_objects(&mut self); fn no_default_libraries(&mut self); @@ -605,6 +606,8 @@ impl<'a> Linker for GccLinker<'a> { fn control_flow_guard(&mut self) {} + fn ehcont_guard(&mut self) {} + fn debuginfo(&mut self, strip: Strip, _: &[PathBuf]) { // MacOS linker doesn't support stripping symbols directly anymore. if self.sess.target.is_like_osx { @@ -914,6 +917,12 @@ impl<'a> Linker for MsvcLinker<'a> { self.cmd.arg("/guard:cf"); } + fn ehcont_guard(&mut self) { + if self.sess.target.pointer_width == 64 { + self.cmd.arg("/guard:ehcont"); + } + } + fn debuginfo(&mut self, strip: Strip, natvis_debugger_visualizers: &[PathBuf]) { match strip { Strip::None => { @@ -1127,6 +1136,8 @@ impl<'a> Linker for EmLinker<'a> { fn control_flow_guard(&mut self) {} + fn ehcont_guard(&mut self) {} + fn debuginfo(&mut self, _strip: Strip, _: &[PathBuf]) { // Preserve names or generate source maps depending on debug info // For more information see https://emscripten.org/docs/tools_reference/emcc.html#emcc-g @@ -1319,6 +1330,8 @@ impl<'a> Linker for WasmLd<'a> { fn control_flow_guard(&mut self) {} + fn ehcont_guard(&mut self) {} + fn no_crt_objects(&mut self) {} fn no_default_libraries(&mut self) {} @@ -1472,6 +1485,8 @@ impl<'a> Linker for L4Bender<'a> { fn control_flow_guard(&mut self) {} + fn ehcont_guard(&mut self) {} + fn no_crt_objects(&mut self) {} } @@ -1613,6 +1628,8 @@ impl<'a> Linker for AixLinker<'a> { fn control_flow_guard(&mut self) {} + fn ehcont_guard(&mut self) {} + fn debuginfo(&mut self, strip: Strip, _: &[PathBuf]) { match strip { Strip::None => {} @@ -1835,6 +1852,8 @@ impl<'a> Linker for PtxLinker<'a> { fn control_flow_guard(&mut self) {} + fn ehcont_guard(&mut self) {} + fn export_symbols(&mut self, _tmpdir: &Path, _crate_type: CrateType, _symbols: &[String]) {} fn subsystem(&mut self, _subsystem: &str) {} @@ -1931,6 +1950,8 @@ impl<'a> Linker for BpfLinker<'a> { fn control_flow_guard(&mut self) {} + fn ehcont_guard(&mut self) {} + fn export_symbols(&mut self, tmpdir: &Path, _crate_type: CrateType, symbols: &[String]) { let path = tmpdir.join("symbols"); let res: io::Result<()> = try { diff --git a/compiler/rustc_session/src/options.rs b/compiler/rustc_session/src/options.rs index b824eb51ef7be..4e669c81bf327 100644 --- a/compiler/rustc_session/src/options.rs +++ b/compiler/rustc_session/src/options.rs @@ -1582,6 +1582,8 @@ options! { "version of DWARF debug information to emit (default: 2 or 4, depending on platform)"), dylib_lto: bool = (false, parse_bool, [UNTRACKED], "enables LTO for dylib crate type"), + ehcont_guard: bool = (false, parse_bool, [TRACKED], + "generate Windows EHCont Guard tables"), emit_stack_sizes: bool = (false, parse_bool, [UNTRACKED], "emit a section containing stack size metadata (default: no)"), emit_thin_lto: bool = (true, parse_bool, [TRACKED], diff --git a/config.example.toml b/config.example.toml index 170856bd97dbd..5f9ae039b252a 100644 --- a/config.example.toml +++ b/config.example.toml @@ -686,6 +686,10 @@ change-id = 116881 # This only applies from stage 1 onwards, and only for Windows targets. #control-flow-guard = false +# Enable Windows EHCont Guard checks in the standard library. +# This only applies from stage 1 onwards, and only for Windows targets. +#ehcont-guard = false + # Enable symbol-mangling-version v0. This can be helpful when profiling rustc, # as generics will be preserved in symbols (rather than erased into opaque T). # When no setting is given, the new scheme will be used when compiling the diff --git a/src/bootstrap/src/core/builder.rs b/src/bootstrap/src/core/builder.rs index c755324df1a57..507306fd274c8 100644 --- a/src/bootstrap/src/core/builder.rs +++ b/src/bootstrap/src/core/builder.rs @@ -1964,6 +1964,16 @@ impl<'a> Builder<'a> { rustflags.arg("-Ccontrol-flow-guard"); } + // If EHCont Guard is enabled, pass the `-Zehcont-guard` flag to rustc when compiling the + // standard library, since this might be linked into the final outputs produced by rustc. + // Since this mitigation is only available on Windows, only enable it for the standard + // library in case the compiler is run on a non-Windows platform. + // This is not needed for stage 0 artifacts because these will only be used for building + // the stage 1 compiler. + if cfg!(windows) && mode == Mode::Std && self.config.ehcont_guard && compiler.stage >= 1 { + rustflags.arg("-Zehcont-guard"); + } + // For `cargo doc` invocations, make rustdoc print the Rust version into the docs // This replaces spaces with tabs because RUSTDOCFLAGS does not // support arguments with regular spaces. Hopefully someday Cargo will diff --git a/src/bootstrap/src/core/config/config.rs b/src/bootstrap/src/core/config/config.rs index fa8b0b20cec45..9ef90798590ce 100644 --- a/src/bootstrap/src/core/config/config.rs +++ b/src/bootstrap/src/core/config/config.rs @@ -248,6 +248,7 @@ pub struct Config { pub local_rebuild: bool, pub jemalloc: bool, pub control_flow_guard: bool, + pub ehcont_guard: bool, // dist misc pub dist_sign_folder: Option, @@ -1019,6 +1020,7 @@ define_config! { test_compare_mode: Option = "test-compare-mode", llvm_libunwind: Option = "llvm-libunwind", control_flow_guard: Option = "control-flow-guard", + ehcont_guard: Option = "ehcont-guard", new_symbol_mangling: Option = "new-symbol-mangling", profile_generate: Option = "profile-generate", profile_use: Option = "profile-use", @@ -1452,6 +1454,7 @@ impl Config { config.rust_thin_lto_import_instr_limit = rust.thin_lto_import_instr_limit; set(&mut config.rust_remap_debuginfo, rust.remap_debuginfo); set(&mut config.control_flow_guard, rust.control_flow_guard); + set(&mut config.ehcont_guard, rust.ehcont_guard); config.llvm_libunwind_default = rust .llvm_libunwind .map(|v| v.parse().expect("failed to parse rust.llvm-libunwind")); diff --git a/src/bootstrap/src/tests/builder.rs b/src/bootstrap/src/tests/builder.rs index 96139f7b099ce..744015e8e8204 100644 --- a/src/bootstrap/src/tests/builder.rs +++ b/src/bootstrap/src/tests/builder.rs @@ -1,6 +1,6 @@ use super::*; -use crate::core::config::{Config, DryRun, TargetSelection}; use crate::core::build_steps::doc::DocumentationFormat; +use crate::core::config::{Config, DryRun, TargetSelection}; use std::thread; fn configure(cmd: &str, host: &[&str], target: &[&str]) -> Config { diff --git a/tests/codegen/ehcontguard_disabled.rs b/tests/codegen/ehcontguard_disabled.rs new file mode 100644 index 0000000000000..7773384e5ead4 --- /dev/null +++ b/tests/codegen/ehcontguard_disabled.rs @@ -0,0 +1,10 @@ +// compile-flags: + +#![crate_type = "lib"] + +// A basic test function. +pub fn test() { +} + +// Ensure the module flag ehcontguard is not present +// CHECK-NOT: !"ehcontguard" diff --git a/tests/codegen/ehcontguard_enabled.rs b/tests/codegen/ehcontguard_enabled.rs new file mode 100644 index 0000000000000..03aaa342b9678 --- /dev/null +++ b/tests/codegen/ehcontguard_enabled.rs @@ -0,0 +1,10 @@ +// compile-flags: -Z ehcont-guard + +#![crate_type = "lib"] + +// A basic test function. +pub fn test() { +} + +// Ensure the module flag ehcontguard=1 is present +// CHECK: !"ehcontguard", i32 1