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.
This commit is contained in:
Andrew Paverd 2020-01-13 13:25:39 +00:00
parent b181835a6b
commit c0744e1e0c
8 changed files with 98 additions and 5 deletions

View File

@ -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,
@ -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
}

View File

@ -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
@ -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

View File

@ -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);
@ -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
@ -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.
@ -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 {
@ -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) {
@ -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) {}

View File

@ -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
@ -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;
@ -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);

View File

@ -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> =
@ -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;
@ -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),
@ -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"),
}

View File

@ -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

View File

@ -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"

View File

@ -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