Skip to content

Commit

Permalink
Auto merge of #68180 - ajpaverd:cfguard-rust, r=nagisa
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
bors committed Feb 1, 2020
2 parents 6c0b779 + c0744e1 commit 64184a3
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
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
@@ -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
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
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
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
@@ -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
@@ -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
@@ -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 64184a3

Please sign in to comment.