Skip to content

Commit c0744e1

Browse files
committed
Add support for Control Flow Guard on Windows.
This patch enables rustc to emit the required LLVM module flags to enable Control Flow Guard metadata (cfguard=1) or metadata and checks (cfguard=2). The LLVM module flags are ignored on unsupported targets and operating systems.
1 parent b181835 commit c0744e1

File tree

8 files changed

+98
-5
lines changed

8 files changed

+98
-5
lines changed

Diff for: 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

Diff for: 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

Diff for: 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) {}

Diff for: 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);

Diff for: 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
}

Diff for: 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

Diff for: 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"

Diff for: 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)