Skip to content

Commit e11d8d1

Browse files
committed
Add support for generating the EHCont section
In the future Windows will enable Control-flow Enforcement Technology (CET aka Shadow Stacks). To protect the path where the context is updated during exception handling, the binary is required to enumerate valid unwind entrypoints in a dedicated section which is validated when the context is being set during exception handling. The required support for EHCONT has already been merged into LLVM, long ago. This change adds the Rust codegen option to enable it. Reference: * https://reviews.llvm.org/D40223 This also adds a new `ehcont-guard` option to the bootstrap config which enables EHCont Guard when building std.
1 parent 2f8d81f commit e11d8d1

File tree

10 files changed

+76
-2
lines changed

10 files changed

+76
-2
lines changed

compiler/rustc_codegen_llvm/src/context.rs

+9
Original file line numberDiff line numberDiff line change
@@ -350,6 +350,15 @@ pub unsafe fn create_module<'ll>(
350350
1,
351351
);
352352
}
353+
// Set module flag to enable Windows EHCont Guard (/guard:ehcont).
354+
if sess.opts.cg.ehcont_guard {
355+
llvm::LLVMRustAddModuleFlag(
356+
llmod,
357+
llvm::LLVMModFlagBehavior::Warning,
358+
"ehcontguard\0".as_ptr() as *const _,
359+
1,
360+
)
361+
}
353362

354363
// Insert `llvm.ident` metadata.
355364
//

compiler/rustc_codegen_ssa/src/back/link.rs

+5
Original file line numberDiff line numberDiff line change
@@ -2378,6 +2378,11 @@ fn add_order_independent_options(
23782378
cmd.control_flow_guard();
23792379
}
23802380

2381+
// OBJECT-FILES-NO, AUDIT-ORDER
2382+
if sess.opts.cg.ehcont_guard {
2383+
cmd.ehcont_guard();
2384+
}
2385+
23812386
add_rpath_args(cmd, sess, codegen_results, out_filename);
23822387
}
23832388

compiler/rustc_codegen_ssa/src/back/linker.rs

+21
Original file line numberDiff line numberDiff line change
@@ -185,6 +185,7 @@ pub trait Linker {
185185
fn optimize(&mut self);
186186
fn pgo_gen(&mut self);
187187
fn control_flow_guard(&mut self);
188+
fn ehcont_guard(&mut self);
188189
fn debuginfo(&mut self, strip: Strip, natvis_debugger_visualizers: &[PathBuf]);
189190
fn no_crt_objects(&mut self);
190191
fn no_default_libraries(&mut self);
@@ -605,6 +606,8 @@ impl<'a> Linker for GccLinker<'a> {
605606

606607
fn control_flow_guard(&mut self) {}
607608

609+
fn ehcont_guard(&mut self) {}
610+
608611
fn debuginfo(&mut self, strip: Strip, _: &[PathBuf]) {
609612
// MacOS linker doesn't support stripping symbols directly anymore.
610613
if self.sess.target.is_like_osx {
@@ -914,6 +917,12 @@ impl<'a> Linker for MsvcLinker<'a> {
914917
self.cmd.arg("/guard:cf");
915918
}
916919

920+
fn ehcont_guard(&mut self) {
921+
if self.sess.target.pointer_width == 64 {
922+
self.cmd.arg("/guard:ehcont");
923+
}
924+
}
925+
917926
fn debuginfo(&mut self, strip: Strip, natvis_debugger_visualizers: &[PathBuf]) {
918927
match strip {
919928
Strip::None => {
@@ -1127,6 +1136,8 @@ impl<'a> Linker for EmLinker<'a> {
11271136

11281137
fn control_flow_guard(&mut self) {}
11291138

1139+
fn ehcont_guard(&mut self) {}
1140+
11301141
fn debuginfo(&mut self, _strip: Strip, _: &[PathBuf]) {
11311142
// Preserve names or generate source maps depending on debug info
11321143
// For more information see https://emscripten.org/docs/tools_reference/emcc.html#emcc-g
@@ -1319,6 +1330,8 @@ impl<'a> Linker for WasmLd<'a> {
13191330

13201331
fn control_flow_guard(&mut self) {}
13211332

1333+
fn ehcont_guard(&mut self) {}
1334+
13221335
fn no_crt_objects(&mut self) {}
13231336

13241337
fn no_default_libraries(&mut self) {}
@@ -1472,6 +1485,8 @@ impl<'a> Linker for L4Bender<'a> {
14721485

14731486
fn control_flow_guard(&mut self) {}
14741487

1488+
fn ehcont_guard(&mut self) {}
1489+
14751490
fn no_crt_objects(&mut self) {}
14761491
}
14771492

@@ -1613,6 +1628,8 @@ impl<'a> Linker for AixLinker<'a> {
16131628

16141629
fn control_flow_guard(&mut self) {}
16151630

1631+
fn ehcont_guard(&mut self) {}
1632+
16161633
fn debuginfo(&mut self, strip: Strip, _: &[PathBuf]) {
16171634
match strip {
16181635
Strip::None => {}
@@ -1835,6 +1852,8 @@ impl<'a> Linker for PtxLinker<'a> {
18351852

18361853
fn control_flow_guard(&mut self) {}
18371854

1855+
fn ehcont_guard(&mut self) {}
1856+
18381857
fn export_symbols(&mut self, _tmpdir: &Path, _crate_type: CrateType, _symbols: &[String]) {}
18391858

18401859
fn subsystem(&mut self, _subsystem: &str) {}
@@ -1931,6 +1950,8 @@ impl<'a> Linker for BpfLinker<'a> {
19311950

19321951
fn control_flow_guard(&mut self) {}
19331952

1953+
fn ehcont_guard(&mut self) {}
1954+
19341955
fn export_symbols(&mut self, tmpdir: &Path, _crate_type: CrateType, symbols: &[String]) {
19351956
let path = tmpdir.join("symbols");
19361957
let res: io::Result<()> = try {

compiler/rustc_session/src/options.rs

+2
Original file line numberDiff line numberDiff line change
@@ -1387,6 +1387,8 @@ options! {
13871387
"allow the linker to link its default libraries (default: no)"),
13881388
dlltool: Option<PathBuf> = (None, parse_opt_pathbuf, [UNTRACKED],
13891389
"import library generation tool (ignored except when targeting windows-gnu)"),
1390+
ehcont_guard: bool = (false, parse_bool, [TRACKED],
1391+
"generate Windows EHCont Guard tables"),
13901392
embed_bitcode: bool = (true, parse_bool, [TRACKED],
13911393
"emit bitcode in rlibs (default: yes)"),
13921394
extra_filename: String = (String::new(), parse_string, [UNTRACKED],

config.example.toml

+4
Original file line numberDiff line numberDiff line change
@@ -686,6 +686,10 @@ change-id = 116881
686686
# This only applies from stage 1 onwards, and only for Windows targets.
687687
#control-flow-guard = false
688688

689+
# Enable Windows EHCont Guard checks in the standard library.
690+
# This only applies from stage 1 onwards, and only for Windows targets.
691+
#ehcont-guard = false
692+
689693
# Enable symbol-mangling-version v0. This can be helpful when profiling rustc,
690694
# as generics will be preserved in symbols (rather than erased into opaque T).
691695
# When no setting is given, the new scheme will be used when compiling the

src/bootstrap/src/core/builder.rs

+11-1
Original file line numberDiff line numberDiff line change
@@ -1964,6 +1964,12 @@ impl<'a> Builder<'a> {
19641964
rustflags.arg("-Ccontrol-flow-guard");
19651965
}
19661966

1967+
// Same for EHCont Guard (this is not combined with the previous if-statement to make
1968+
// merges with upstream easier).
1969+
if cfg!(windows) && mode == Mode::Std && self.config.ehcont_guard && compiler.stage >= 1 {
1970+
rustflags.arg("-Cehcont-guard");
1971+
}
1972+
19671973
// For `cargo doc` invocations, make rustdoc print the Rust version into the docs
19681974
// This replaces spaces with tabs because RUSTDOCFLAGS does not
19691975
// support arguments with regular spaces. Hopefully someday Cargo will
@@ -2172,7 +2178,11 @@ impl<'a> Builder<'a> {
21722178
}
21732179

21742180
// Only execute if it's supposed to run as default
2175-
if desc.default && should_run.is_really_default() { self.ensure(step) } else { None }
2181+
if desc.default && should_run.is_really_default() {
2182+
self.ensure(step)
2183+
} else {
2184+
None
2185+
}
21762186
}
21772187

21782188
/// Checks if any of the "should_run" paths is in the `Builder` paths.

src/bootstrap/src/core/config/config.rs

+3
Original file line numberDiff line numberDiff line change
@@ -248,6 +248,7 @@ pub struct Config {
248248
pub local_rebuild: bool,
249249
pub jemalloc: bool,
250250
pub control_flow_guard: bool,
251+
pub ehcont_guard: bool,
251252

252253
// dist misc
253254
pub dist_sign_folder: Option<PathBuf>,
@@ -1019,6 +1020,7 @@ define_config! {
10191020
test_compare_mode: Option<bool> = "test-compare-mode",
10201021
llvm_libunwind: Option<String> = "llvm-libunwind",
10211022
control_flow_guard: Option<bool> = "control-flow-guard",
1023+
ehcont_guard: Option<bool> = "ehcont-guard",
10221024
new_symbol_mangling: Option<bool> = "new-symbol-mangling",
10231025
profile_generate: Option<String> = "profile-generate",
10241026
profile_use: Option<String> = "profile-use",
@@ -1452,6 +1454,7 @@ impl Config {
14521454
config.rust_thin_lto_import_instr_limit = rust.thin_lto_import_instr_limit;
14531455
set(&mut config.rust_remap_debuginfo, rust.remap_debuginfo);
14541456
set(&mut config.control_flow_guard, rust.control_flow_guard);
1457+
set(&mut config.ehcont_guard, rust.ehcont_guard);
14551458
config.llvm_libunwind_default = rust
14561459
.llvm_libunwind
14571460
.map(|v| v.parse().expect("failed to parse rust.llvm-libunwind"));

src/bootstrap/src/tests/builder.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
use super::*;
2-
use crate::core::config::{Config, DryRun, TargetSelection};
32
use crate::core::build_steps::doc::DocumentationFormat;
3+
use crate::core::config::{Config, DryRun, TargetSelection};
44
use std::thread;
55

66
fn configure(cmd: &str, host: &[&str], target: &[&str]) -> Config {

tests/codegen/ehcontguard_disabled.rs

+10
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
// compile-flags:
2+
3+
#![crate_type = "lib"]
4+
5+
// A basic test function.
6+
pub fn test() {
7+
}
8+
9+
// Ensure the module flag ehcontguard is not present
10+
// CHECK-NOT: !"ehcontguard"

tests/codegen/ehcontguard_enabled.rs

+10
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
// compile-flags: -C ehcont_guard
2+
3+
#![crate_type = "lib"]
4+
5+
// A basic test function.
6+
pub fn test() {
7+
}
8+
9+
// Ensure the module flag ehcontguard=1 is present
10+
// CHECK: !"ehcontguard", i32 1

0 commit comments

Comments
 (0)