Skip to content

Commit

Permalink
Add support for Control Flow Guard on Windows.
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
ajpaverd committed Jan 28, 2020
1 parent b181835 commit c0744e1
Show file tree
Hide file tree
Showing 8 changed files with 98 additions and 5 deletions.
12 changes: 11 additions & 1 deletion src/librustc_codegen_llvm/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ use rustc_codegen_ssa::traits::*;
use crate::callee::get_fn;
use rustc::bug;
use rustc::mir::mono::CodegenUnit;
use rustc::session::config::{self, DebugInfo};
use rustc::session::config::{self, CFGuard, DebugInfo};
use rustc::session::Session;
use rustc::ty::layout::{
FnAbiExt, HasParamEnv, LayoutError, LayoutOf, PointeeInfo, Size, TyLayout, VariantIdx,
Expand Down Expand Up @@ -227,6 +227,16 @@ pub unsafe fn create_module(
llvm::LLVMRustAddModuleFlag(llmod, avoid_plt, 1);
}

// Set module flags to enable Windows Control Flow Guard (/guard:cf) metadata
// only (`cfguard=1`) or metadata and checks (`cfguard=2`).
match sess.opts.debugging_opts.control_flow_guard {
CFGuard::Disabled => {}
CFGuard::NoChecks => {
llvm::LLVMRustAddModuleFlag(llmod, "cfguard\0".as_ptr() as *const _, 1)
}
CFGuard::Checks => llvm::LLVMRustAddModuleFlag(llmod, "cfguard\0".as_ptr() as *const _, 2),
}

llmod
}

Expand Down
6 changes: 5 additions & 1 deletion src/librustc_codegen_ssa/back/link.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use rustc::middle::cstore::{EncodedMetadata, LibSource, NativeLibrary, NativeLibraryKind};
use rustc::middle::dependency_format::Linkage;
use rustc::session::config::{
self, DebugInfo, OutputFilenames, OutputType, PrintRequest, Sanitizer,
self, CFGuard, DebugInfo, OutputFilenames, OutputType, PrintRequest, Sanitizer,
};
use rustc::session::search_paths::PathKind;
/// For all the linkers we support, and information they might
Expand Down Expand Up @@ -1294,6 +1294,10 @@ fn link_args<'a, B: ArchiveBuilder<'a>>(
cmd.pgo_gen();
}

if sess.opts.debugging_opts.control_flow_guard != CFGuard::Disabled {
cmd.control_flow_guard();
}

// FIXME (#2397): At some point we want to rpath our guesses as to
// where extern libraries might live, based on the
// addl_lib_search_paths
Expand Down
21 changes: 21 additions & 0 deletions src/librustc_codegen_ssa/back/linker.rs
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,7 @@ pub trait Linker {
fn no_relro(&mut self);
fn optimize(&mut self);
fn pgo_gen(&mut self);
fn control_flow_guard(&mut self);
fn debuginfo(&mut self);
fn no_default_libraries(&mut self);
fn build_dylib(&mut self, out_filename: &Path);
Expand Down Expand Up @@ -360,6 +361,10 @@ impl<'a> Linker for GccLinker<'a> {
self.cmd.arg("__llvm_profile_runtime");
}

fn control_flow_guard(&mut self) {
self.sess.warn("Windows Control Flow Guard is not supported by this linker.");
}

fn debuginfo(&mut self) {
if let DebugInfo::None = self.sess.opts.debuginfo {
// If we are building without debuginfo enabled and we were called with
Expand Down Expand Up @@ -660,6 +665,10 @@ impl<'a> Linker for MsvcLinker<'a> {
// Nothing needed here.
}

fn control_flow_guard(&mut self) {
self.cmd.arg("/guard:cf");
}

fn debuginfo(&mut self) {
// This will cause the Microsoft linker to generate a PDB file
// from the CodeView line tables in the object files.
Expand Down Expand Up @@ -862,6 +871,10 @@ impl<'a> Linker for EmLinker<'a> {
// noop, but maybe we need something like the gnu linker?
}

fn control_flow_guard(&mut self) {
self.sess.warn("Windows Control Flow Guard is not supported by this linker.");
}

fn debuginfo(&mut self) {
// Preserve names or generate source maps depending on debug info
self.cmd.arg(match self.sess.opts.debuginfo {
Expand Down Expand Up @@ -1058,6 +1071,10 @@ impl<'a> Linker for WasmLd<'a> {

fn debuginfo(&mut self) {}

fn control_flow_guard(&mut self) {
self.sess.warn("Windows Control Flow Guard is not supported by this linker.");
}

fn no_default_libraries(&mut self) {}

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

fn no_default_libraries(&mut self) {}

fn control_flow_guard(&mut self) {
self.sess.warn("Windows Control Flow Guard is not supported by this linker.");
}

fn build_dylib(&mut self, _out_filename: &Path) {}

fn export_symbols(&mut self, _tmpdir: &Path, _crate_type: CrateType) {}
Expand Down
18 changes: 16 additions & 2 deletions src/librustc_session/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,19 @@ impl FromStr for Sanitizer {
}
}

/// The different settings that the `-Z control_flow_guard` flag can have.
#[derive(Clone, Copy, PartialEq, Hash, Debug)]
pub enum CFGuard {
/// Do not emit Control Flow Guard metadata or checks.
Disabled,

/// Emit Control Flow Guard metadata but no checks.
NoChecks,

/// Emit Control Flow Guard metadata and checks.
Checks,
}

#[derive(Clone, Copy, Debug, PartialEq, Hash)]
pub enum OptLevel {
No, // -O0
Expand Down Expand Up @@ -1980,8 +1993,8 @@ impl PpMode {
/// how the hash should be calculated when adding a new command-line argument.
crate mod dep_tracking {
use super::{
CrateType, DebugInfo, ErrorOutputType, LinkerPluginLto, LtoCli, OptLevel, OutputTypes,
Passes, Sanitizer, SwitchWithOptPath, SymbolManglingVersion,
CFGuard, CrateType, DebugInfo, ErrorOutputType, LinkerPluginLto, LtoCli, OptLevel,
OutputTypes, Passes, Sanitizer, SwitchWithOptPath, SymbolManglingVersion,
};
use crate::lint;
use crate::utils::NativeLibraryKind;
Expand Down Expand Up @@ -2053,6 +2066,7 @@ crate mod dep_tracking {
impl_dep_tracking_hash_via_hash!(NativeLibraryKind);
impl_dep_tracking_hash_via_hash!(Sanitizer);
impl_dep_tracking_hash_via_hash!(Option<Sanitizer>);
impl_dep_tracking_hash_via_hash!(CFGuard);
impl_dep_tracking_hash_via_hash!(TargetTriple);
impl_dep_tracking_hash_via_hash!(Edition);
impl_dep_tracking_hash_via_hash!(LinkerPluginLto);
Expand Down
16 changes: 15 additions & 1 deletion src/librustc_session/options.rs
Original file line number Diff line number Diff line change
Expand Up @@ -263,6 +263,8 @@ macro_rules! options {
pub const parse_sanitizer_list: Option<&str> =
Some("comma separated list of sanitizers");
pub const parse_sanitizer_memory_track_origins: Option<&str> = None;
pub const parse_cfguard: Option<&str> =
Some("either `disabled`, `nochecks`, or `checks`");
pub const parse_linker_flavor: Option<&str> =
Some(::rustc_target::spec::LinkerFlavor::one_of());
pub const parse_optimization_fuel: Option<&str> =
Expand All @@ -288,7 +290,7 @@ macro_rules! options {
#[allow(dead_code)]
mod $mod_set {
use super::{$struct_name, Passes, Sanitizer, LtoCli, LinkerPluginLto, SwitchWithOptPath,
SymbolManglingVersion};
SymbolManglingVersion, CFGuard};
use rustc_target::spec::{LinkerFlavor, MergeFunctions, PanicStrategy, RelroLevel};
use std::path::PathBuf;
use std::str::FromStr;
Expand Down Expand Up @@ -499,6 +501,16 @@ macro_rules! options {
}
}

fn parse_cfguard(slot: &mut CFGuard, v: Option<&str>) -> bool {
match v {
Some("disabled") => *slot = CFGuard::Disabled,
Some("nochecks") => *slot = CFGuard::NoChecks,
Some("checks") => *slot = CFGuard::Checks,
_ => return false,
}
true
}

fn parse_linker_flavor(slote: &mut Option<LinkerFlavor>, v: Option<&str>) -> bool {
match v.and_then(LinkerFlavor::from_str) {
Some(lf) => *slote = Some(lf),
Expand Down Expand Up @@ -950,6 +962,8 @@ options! {DebuggingOptions, DebuggingSetter, basic_debugging_options,
(such as entering an empty infinite loop) by inserting llvm.sideeffect"),
deduplicate_diagnostics: Option<bool> = (None, parse_opt_bool, [UNTRACKED],
"deduplicate identical diagnostics"),
control_flow_guard: CFGuard = (CFGuard::Disabled, parse_cfguard, [UNTRACKED],
"use Windows Control Flow Guard (`disabled`, `nochecks` or `checks`)"),
no_link: bool = (false, parse_bool, [TRACKED],
"compile without linking"),
}
10 changes: 10 additions & 0 deletions src/test/codegen/cfguard_checks.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
// compile-flags: -Z control_flow_guard=checks

#![crate_type = "lib"]

// A basic test function.
pub fn test() {
}

// Ensure the module flag cfguard=2 is present
// CHECK: !"cfguard", i32 2
10 changes: 10 additions & 0 deletions src/test/codegen/cfguard_disabled.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
// compile-flags: -Z control_flow_guard=disabled

#![crate_type = "lib"]

// A basic test function.
pub fn test() {
}

// Ensure the module flag cfguard is not present
// CHECK-NOT: !"cfguard"
10 changes: 10 additions & 0 deletions src/test/codegen/cfguard_nochecks.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
// compile-flags: -Z control_flow_guard=nochecks

#![crate_type = "lib"]

// A basic test function.
pub fn test() {
}

// Ensure the module flag cfguard=1 is present
// CHECK: !"cfguard", i32 1

0 comments on commit c0744e1

Please sign in to comment.