summaryrefslogtreecommitdiff
path: root/compiler/rustc_codegen_llvm/src/builder.rs
diff options
context:
space:
mode:
Diffstat (limited to 'compiler/rustc_codegen_llvm/src/builder.rs')
-rw-r--r--compiler/rustc_codegen_llvm/src/builder.rs127
1 files changed, 96 insertions, 31 deletions
diff --git a/compiler/rustc_codegen_llvm/src/builder.rs b/compiler/rustc_codegen_llvm/src/builder.rs
index 6819a2af09d..4d0bcd53d15 100644
--- a/compiler/rustc_codegen_llvm/src/builder.rs
+++ b/compiler/rustc_codegen_llvm/src/builder.rs
@@ -15,14 +15,15 @@ use rustc_codegen_ssa::traits::*;
use rustc_codegen_ssa::MemFlags;
use rustc_data_structures::small_c_str::SmallCStr;
use rustc_hir::def_id::DefId;
+use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrs;
use rustc_middle::ty::layout::{
FnAbiError, FnAbiOfHelpers, FnAbiRequest, LayoutError, LayoutOfHelpers, TyAndLayout,
};
use rustc_middle::ty::{self, Ty, TyCtxt};
use rustc_span::Span;
-use rustc_symbol_mangling::typeid::kcfi_typeid_for_fnabi;
+use rustc_symbol_mangling::typeid::{kcfi_typeid_for_fnabi, typeid_for_fnabi, TypeIdOptions};
use rustc_target::abi::{self, call::FnAbi, Align, Size, WrappingRange};
-use rustc_target::spec::{HasTargetSpec, Target};
+use rustc_target::spec::{HasTargetSpec, SanitizerSet, Target};
use std::borrow::Cow;
use std::ffi::CStr;
use std::iter;
@@ -216,6 +217,7 @@ impl<'a, 'll, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'll, 'tcx> {
fn invoke(
&mut self,
llty: &'ll Type,
+ fn_attrs: Option<&CodegenFnAttrs>,
fn_abi: Option<&FnAbi<'tcx, Ty<'tcx>>>,
llfn: &'ll Value,
args: &[&'ll Value],
@@ -230,19 +232,13 @@ impl<'a, 'll, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'll, 'tcx> {
let funclet_bundle = funclet_bundle.as_ref().map(|b| &*b.raw);
let mut bundles = vec![funclet_bundle];
- // Set KCFI operand bundle
- let is_indirect_call = unsafe { llvm::LLVMIsAFunction(llfn).is_none() };
- let kcfi_bundle =
- if self.tcx.sess.is_sanitizer_kcfi_enabled() && let Some(fn_abi) = fn_abi && is_indirect_call {
- let kcfi_typeid = kcfi_typeid_for_fnabi(self.tcx, fn_abi);
- Some(llvm::OperandBundleDef::new("kcfi", &[self.const_u32(kcfi_typeid)]))
- } else {
- None
- };
- if kcfi_bundle.is_some() {
- let kcfi_bundle = kcfi_bundle.as_ref().map(|b| &*b.raw);
- bundles.push(kcfi_bundle);
- }
+ // Emit CFI pointer type membership test
+ self.cfi_type_test(fn_attrs, fn_abi, llfn);
+
+ // Emit KCFI operand bundle
+ let kcfi_bundle = self.kcfi_operand_bundle(fn_attrs, fn_abi, llfn);
+ let kcfi_bundle = kcfi_bundle.as_ref().map(|b| &*b.raw);
+ bundles.push(kcfi_bundle);
bundles.retain(|bundle| bundle.is_some());
let invoke = unsafe {
@@ -989,13 +985,20 @@ impl<'a, 'll, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'll, 'tcx> {
fn cleanup_landing_pad(&mut self, pers_fn: &'ll Value) -> (&'ll Value, &'ll Value) {
let ty = self.type_struct(&[self.type_i8p(), self.type_i32()], false);
- let landing_pad = self.landing_pad(ty, pers_fn, 1 /* FIXME should this be 0? */);
+ let landing_pad = self.landing_pad(ty, pers_fn, 0);
unsafe {
llvm::LLVMSetCleanup(landing_pad, llvm::True);
}
(self.extract_value(landing_pad, 0), self.extract_value(landing_pad, 1))
}
+ fn filter_landing_pad(&mut self, pers_fn: &'ll Value) -> (&'ll Value, &'ll Value) {
+ let ty = self.type_struct(&[self.type_i8p(), self.type_i32()], false);
+ let landing_pad = self.landing_pad(ty, pers_fn, 1);
+ self.add_clause(landing_pad, self.const_array(self.type_i8p(), &[]));
+ (self.extract_value(landing_pad, 0), self.extract_value(landing_pad, 1))
+ }
+
fn resume(&mut self, exn0: &'ll Value, exn1: &'ll Value) {
let ty = self.type_struct(&[self.type_i8p(), self.type_i32()], false);
let mut exn = self.const_poison(ty);
@@ -1183,6 +1186,7 @@ impl<'a, 'll, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'll, 'tcx> {
fn call(
&mut self,
llty: &'ll Type,
+ fn_attrs: Option<&CodegenFnAttrs>,
fn_abi: Option<&FnAbi<'tcx, Ty<'tcx>>>,
llfn: &'ll Value,
args: &[&'ll Value],
@@ -1195,19 +1199,13 @@ impl<'a, 'll, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'll, 'tcx> {
let funclet_bundle = funclet_bundle.as_ref().map(|b| &*b.raw);
let mut bundles = vec![funclet_bundle];
- // Set KCFI operand bundle
- let is_indirect_call = unsafe { llvm::LLVMIsAFunction(llfn).is_none() };
- let kcfi_bundle =
- if let Some(fn_abi) = fn_abi && self.tcx.sess.is_sanitizer_kcfi_enabled() && is_indirect_call {
- let kcfi_typeid = kcfi_typeid_for_fnabi(self.tcx, fn_abi);
- Some(llvm::OperandBundleDef::new("kcfi", &[self.const_u32(kcfi_typeid)]))
- } else {
- None
- };
- if kcfi_bundle.is_some() {
- let kcfi_bundle = kcfi_bundle.as_ref().map(|b| &*b.raw);
- bundles.push(kcfi_bundle);
- }
+ // Emit CFI pointer type membership test
+ self.cfi_type_test(fn_attrs, fn_abi, llfn);
+
+ // Emit KCFI operand bundle
+ let kcfi_bundle = self.kcfi_operand_bundle(fn_attrs, fn_abi, llfn);
+ let kcfi_bundle = kcfi_bundle.as_ref().map(|b| &*b.raw);
+ bundles.push(kcfi_bundle);
bundles.retain(|bundle| bundle.is_some());
let call = unsafe {
@@ -1456,7 +1454,7 @@ impl<'a, 'll, 'tcx> Builder<'a, 'll, 'tcx> {
pub(crate) fn call_intrinsic(&mut self, intrinsic: &str, args: &[&'ll Value]) -> &'ll Value {
let (ty, f) = self.cx.get_intrinsic(intrinsic);
- self.call(ty, None, f, args, None)
+ self.call(ty, None, None, f, args, None)
}
fn call_lifetime_intrinsic(&mut self, intrinsic: &str, ptr: &'ll Value, size: Size) {
@@ -1518,7 +1516,7 @@ impl<'a, 'll, 'tcx> Builder<'a, 'll, 'tcx> {
format!("llvm.{}.sat.i{}.f{}", instr, int_width, float_width)
};
let f = self.declare_cfn(&name, llvm::UnnamedAddr::No, self.type_func(&[src_ty], dest_ty));
- self.call(self.type_func(&[src_ty], dest_ty), None, f, &[val], None)
+ self.call(self.type_func(&[src_ty], dest_ty), None, None, f, &[val], None)
}
pub(crate) fn landing_pad(
@@ -1535,4 +1533,71 @@ impl<'a, 'll, 'tcx> Builder<'a, 'll, 'tcx> {
llvm::LLVMBuildLandingPad(self.llbuilder, ty, None, num_clauses as c_uint, UNNAMED)
}
}
+
+ // Emits CFI pointer type membership tests.
+ fn cfi_type_test(
+ &mut self,
+ fn_attrs: Option<&CodegenFnAttrs>,
+ fn_abi: Option<&FnAbi<'tcx, Ty<'tcx>>>,
+ llfn: &'ll Value,
+ ) {
+ let is_indirect_call = unsafe { llvm::LLVMIsAFunction(llfn).is_none() };
+ if is_indirect_call && fn_abi.is_some() && self.tcx.sess.is_sanitizer_cfi_enabled() {
+ if fn_attrs.is_some() && fn_attrs.unwrap().no_sanitize.contains(SanitizerSet::CFI) {
+ return;
+ }
+
+ let mut options = TypeIdOptions::empty();
+ if self.tcx.sess.is_sanitizer_cfi_generalize_pointers_enabled() {
+ options.insert(TypeIdOptions::GENERALIZE_POINTERS);
+ }
+ if self.tcx.sess.is_sanitizer_cfi_normalize_integers_enabled() {
+ options.insert(TypeIdOptions::NORMALIZE_INTEGERS);
+ }
+
+ let typeid = typeid_for_fnabi(self.tcx, fn_abi.unwrap(), options);
+ let typeid_metadata = self.cx.typeid_metadata(typeid).unwrap();
+
+ // Test whether the function pointer is associated with the type identifier.
+ let cond = self.type_test(llfn, typeid_metadata);
+ let bb_pass = self.append_sibling_block("type_test.pass");
+ let bb_fail = self.append_sibling_block("type_test.fail");
+ self.cond_br(cond, bb_pass, bb_fail);
+
+ self.switch_to_block(bb_fail);
+ self.abort();
+ self.unreachable();
+
+ self.switch_to_block(bb_pass);
+ }
+ }
+
+ // Emits KCFI operand bundles.
+ fn kcfi_operand_bundle(
+ &mut self,
+ fn_attrs: Option<&CodegenFnAttrs>,
+ fn_abi: Option<&FnAbi<'tcx, Ty<'tcx>>>,
+ llfn: &'ll Value,
+ ) -> Option<llvm::OperandBundleDef<'ll>> {
+ let is_indirect_call = unsafe { llvm::LLVMIsAFunction(llfn).is_none() };
+ let kcfi_bundle = if is_indirect_call && self.tcx.sess.is_sanitizer_kcfi_enabled() {
+ if fn_attrs.is_some() && fn_attrs.unwrap().no_sanitize.contains(SanitizerSet::KCFI) {
+ return None;
+ }
+
+ let mut options = TypeIdOptions::empty();
+ if self.tcx.sess.is_sanitizer_cfi_generalize_pointers_enabled() {
+ options.insert(TypeIdOptions::GENERALIZE_POINTERS);
+ }
+ if self.tcx.sess.is_sanitizer_cfi_normalize_integers_enabled() {
+ options.insert(TypeIdOptions::NORMALIZE_INTEGERS);
+ }
+
+ let kcfi_typeid = kcfi_typeid_for_fnabi(self.tcx, fn_abi.unwrap(), options);
+ Some(llvm::OperandBundleDef::new("kcfi", &[self.const_u32(kcfi_typeid)]))
+ } else {
+ None
+ };
+ kcfi_bundle
+ }
}