Skip to content

Commit 64184a3

Browse files
committed
Auto merge of #68180 - ajpaverd:cfguard-rust, r=nagisa
Add support for Control Flow Guard on Windows. LLVM now supports Windows Control Flow Guard (CFG): llvm/llvm-project@d157a9b This patch adds support for rustc to emit the required LLVM module flags to enable CFG metadata (cfguard=1) or metadata and checks (cfguard=2). The LLVM module flags are ignored on unsupported targets and operating systems.
2 parents 6c0b779 + c0744e1 commit 64184a3

File tree

8 files changed

+98
-5
lines changed

8 files changed

+98
-5
lines changed

src/librustc_codegen_llvm/context.rs

+11-1
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ use rustc_codegen_ssa::traits::*;
1212
use crate::callee::get_fn;
1313
use rustc::bug;
1414
use rustc::mir::mono::CodegenUnit;
15-
use rustc::session::config::{self, DebugInfo};
15+
use rustc::session::config::{self, CFGuard, DebugInfo};
1616
use rustc::session::Session;
1717
use rustc::ty::layout::{
1818
FnAbiExt, HasParamEnv, LayoutError, LayoutOf, PointeeInfo, Size, TyLayout, VariantIdx,
@@ -227,6 +227,16 @@ pub unsafe fn create_module(
227227
llvm::LLVMRustAddModuleFlag(llmod, avoid_plt, 1);
228228
}
229229

230+
// Set module flags to enable Windows Control Flow Guard (/guard:cf) metadata
231+
// only (`cfguard=1`) or metadata and checks (`cfguard=2`).
232+
match sess.opts.debugging_opts.control_flow_guard {
233+
CFGuard::Disabled => {}
234+
CFGuard::NoChecks => {
235+
llvm::LLVMRustAddModuleFlag(llmod, "cfguard\0".as_ptr() as *const _, 1)
236+
}
237+
CFGuard::Checks => llvm::LLVMRustAddModuleFlag(llmod, "cfguard\0".as_ptr() as *const _, 2),
238+
}
239+
230240
llmod
231241
}
232242

src/librustc_codegen_ssa/back/link.rs

+5-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
use rustc::middle::cstore::{EncodedMetadata, LibSource, NativeLibrary, NativeLibraryKind};
22
use rustc::middle::dependency_format::Linkage;
33
use rustc::session::config::{
4-
self, DebugInfo, OutputFilenames, OutputType, PrintRequest, Sanitizer,
4+
self, CFGuard, DebugInfo, OutputFilenames, OutputType, PrintRequest, Sanitizer,
55
};
66
use rustc::session::search_paths::PathKind;
77
/// For all the linkers we support, and information they might
@@ -1294,6 +1294,10 @@ fn link_args<'a, B: ArchiveBuilder<'a>>(
12941294
cmd.pgo_gen();
12951295
}
12961296

1297+
if sess.opts.debugging_opts.control_flow_guard != CFGuard::Disabled {
1298+
cmd.control_flow_guard();
1299+
}
1300+
12971301
// FIXME (#2397): At some point we want to rpath our guesses as to
12981302
// where extern libraries might live, based on the
12991303
// addl_lib_search_paths

src/librustc_codegen_ssa/back/linker.rs

+21
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,7 @@ pub trait Linker {
106106
fn no_relro(&mut self);
107107
fn optimize(&mut self);
108108
fn pgo_gen(&mut self);
109+
fn control_flow_guard(&mut self);
109110
fn debuginfo(&mut self);
110111
fn no_default_libraries(&mut self);
111112
fn build_dylib(&mut self, out_filename: &Path);
@@ -360,6 +361,10 @@ impl<'a> Linker for GccLinker<'a> {
360361
self.cmd.arg("__llvm_profile_runtime");
361362
}
362363

364+
fn control_flow_guard(&mut self) {
365+
self.sess.warn("Windows Control Flow Guard is not supported by this linker.");
366+
}
367+
363368
fn debuginfo(&mut self) {
364369
if let DebugInfo::None = self.sess.opts.debuginfo {
365370
// If we are building without debuginfo enabled and we were called with
@@ -660,6 +665,10 @@ impl<'a> Linker for MsvcLinker<'a> {
660665
// Nothing needed here.
661666
}
662667

668+
fn control_flow_guard(&mut self) {
669+
self.cmd.arg("/guard:cf");
670+
}
671+
663672
fn debuginfo(&mut self) {
664673
// This will cause the Microsoft linker to generate a PDB file
665674
// from the CodeView line tables in the object files.
@@ -862,6 +871,10 @@ impl<'a> Linker for EmLinker<'a> {
862871
// noop, but maybe we need something like the gnu linker?
863872
}
864873

874+
fn control_flow_guard(&mut self) {
875+
self.sess.warn("Windows Control Flow Guard is not supported by this linker.");
876+
}
877+
865878
fn debuginfo(&mut self) {
866879
// Preserve names or generate source maps depending on debug info
867880
self.cmd.arg(match self.sess.opts.debuginfo {
@@ -1058,6 +1071,10 @@ impl<'a> Linker for WasmLd<'a> {
10581071

10591072
fn debuginfo(&mut self) {}
10601073

1074+
fn control_flow_guard(&mut self) {
1075+
self.sess.warn("Windows Control Flow Guard is not supported by this linker.");
1076+
}
1077+
10611078
fn no_default_libraries(&mut self) {}
10621079

10631080
fn build_dylib(&mut self, _out_filename: &Path) {
@@ -1233,6 +1250,10 @@ impl<'a> Linker for PtxLinker<'a> {
12331250

12341251
fn no_default_libraries(&mut self) {}
12351252

1253+
fn control_flow_guard(&mut self) {
1254+
self.sess.warn("Windows Control Flow Guard is not supported by this linker.");
1255+
}
1256+
12361257
fn build_dylib(&mut self, _out_filename: &Path) {}
12371258

12381259
fn export_symbols(&mut self, _tmpdir: &Path, _crate_type: CrateType) {}

src/librustc_session/config.rs

+16-2
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,19 @@ impl FromStr for Sanitizer {
7070
}
7171
}
7272

73+
/// The different settings that the `-Z control_flow_guard` flag can have.
74+
#[derive(Clone, Copy, PartialEq, Hash, Debug)]
75+
pub enum CFGuard {
76+
/// Do not emit Control Flow Guard metadata or checks.
77+
Disabled,
78+
79+
/// Emit Control Flow Guard metadata but no checks.
80+
NoChecks,
81+
82+
/// Emit Control Flow Guard metadata and checks.
83+
Checks,
84+
}
85+
7386
#[derive(Clone, Copy, Debug, PartialEq, Hash)]
7487
pub enum OptLevel {
7588
No, // -O0
@@ -1980,8 +1993,8 @@ impl PpMode {
19801993
/// how the hash should be calculated when adding a new command-line argument.
19811994
crate mod dep_tracking {
19821995
use super::{
1983-
CrateType, DebugInfo, ErrorOutputType, LinkerPluginLto, LtoCli, OptLevel, OutputTypes,
1984-
Passes, Sanitizer, SwitchWithOptPath, SymbolManglingVersion,
1996+
CFGuard, CrateType, DebugInfo, ErrorOutputType, LinkerPluginLto, LtoCli, OptLevel,
1997+
OutputTypes, Passes, Sanitizer, SwitchWithOptPath, SymbolManglingVersion,
19851998
};
19861999
use crate::lint;
19872000
use crate::utils::NativeLibraryKind;
@@ -2053,6 +2066,7 @@ crate mod dep_tracking {
20532066
impl_dep_tracking_hash_via_hash!(NativeLibraryKind);
20542067
impl_dep_tracking_hash_via_hash!(Sanitizer);
20552068
impl_dep_tracking_hash_via_hash!(Option<Sanitizer>);
2069+
impl_dep_tracking_hash_via_hash!(CFGuard);
20562070
impl_dep_tracking_hash_via_hash!(TargetTriple);
20572071
impl_dep_tracking_hash_via_hash!(Edition);
20582072
impl_dep_tracking_hash_via_hash!(LinkerPluginLto);

src/librustc_session/options.rs

+15-1
Original file line numberDiff line numberDiff line change
@@ -263,6 +263,8 @@ macro_rules! options {
263263
pub const parse_sanitizer_list: Option<&str> =
264264
Some("comma separated list of sanitizers");
265265
pub const parse_sanitizer_memory_track_origins: Option<&str> = None;
266+
pub const parse_cfguard: Option<&str> =
267+
Some("either `disabled`, `nochecks`, or `checks`");
266268
pub const parse_linker_flavor: Option<&str> =
267269
Some(::rustc_target::spec::LinkerFlavor::one_of());
268270
pub const parse_optimization_fuel: Option<&str> =
@@ -288,7 +290,7 @@ macro_rules! options {
288290
#[allow(dead_code)]
289291
mod $mod_set {
290292
use super::{$struct_name, Passes, Sanitizer, LtoCli, LinkerPluginLto, SwitchWithOptPath,
291-
SymbolManglingVersion};
293+
SymbolManglingVersion, CFGuard};
292294
use rustc_target::spec::{LinkerFlavor, MergeFunctions, PanicStrategy, RelroLevel};
293295
use std::path::PathBuf;
294296
use std::str::FromStr;
@@ -499,6 +501,16 @@ macro_rules! options {
499501
}
500502
}
501503

504+
fn parse_cfguard(slot: &mut CFGuard, v: Option<&str>) -> bool {
505+
match v {
506+
Some("disabled") => *slot = CFGuard::Disabled,
507+
Some("nochecks") => *slot = CFGuard::NoChecks,
508+
Some("checks") => *slot = CFGuard::Checks,
509+
_ => return false,
510+
}
511+
true
512+
}
513+
502514
fn parse_linker_flavor(slote: &mut Option<LinkerFlavor>, v: Option<&str>) -> bool {
503515
match v.and_then(LinkerFlavor::from_str) {
504516
Some(lf) => *slote = Some(lf),
@@ -950,6 +962,8 @@ options! {DebuggingOptions, DebuggingSetter, basic_debugging_options,
950962
(such as entering an empty infinite loop) by inserting llvm.sideeffect"),
951963
deduplicate_diagnostics: Option<bool> = (None, parse_opt_bool, [UNTRACKED],
952964
"deduplicate identical diagnostics"),
965+
control_flow_guard: CFGuard = (CFGuard::Disabled, parse_cfguard, [UNTRACKED],
966+
"use Windows Control Flow Guard (`disabled`, `nochecks` or `checks`)"),
953967
no_link: bool = (false, parse_bool, [TRACKED],
954968
"compile without linking"),
955969
}

src/test/codegen/cfguard_checks.rs

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

src/test/codegen/cfguard_disabled.rs

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

src/test/codegen/cfguard_nochecks.rs

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

0 commit comments

Comments
 (0)