Add cross-language LLVM CFI support to the Rust compiler
This commit adds cross-language LLVM Control Flow Integrity (CFI) support to the Rust compiler by adding the `-Zsanitizer-cfi-normalize-integers` option to be used with Clang `-fsanitize-cfi-icall-normalize-integers` for normalizing integer types (see https://reviews.llvm.org/D139395). It provides forward-edge control flow protection for C or C++ and Rust -compiled code "mixed binaries" (i.e., for when C or C++ and Rust -compiled code share the same virtual address space). For more information about LLVM CFI and cross-language LLVM CFI support for the Rust compiler, see design document in the tracking issue #89653. Cross-language LLVM CFI can be enabled with -Zsanitizer=cfi and -Zsanitizer-cfi-normalize-integers, and requires proper (i.e., non-rustc) LTO (i.e., -Clinker-plugin-lto).
This commit is contained in:
parent
f40e4da5d9
commit
0d183f8e74
@ -501,7 +501,7 @@ fn codegen_inline_asm(&mut self, template: &[InlineAsmTemplatePiece], rust_opera
|
|||||||
if options.contains(InlineAsmOptions::NORETURN) {
|
if options.contains(InlineAsmOptions::NORETURN) {
|
||||||
let builtin_unreachable = self.context.get_builtin_function("__builtin_unreachable");
|
let builtin_unreachable = self.context.get_builtin_function("__builtin_unreachable");
|
||||||
let builtin_unreachable: RValue<'gcc> = unsafe { std::mem::transmute(builtin_unreachable) };
|
let builtin_unreachable: RValue<'gcc> = unsafe { std::mem::transmute(builtin_unreachable) };
|
||||||
self.call(self.type_void(), None, builtin_unreachable, &[], None);
|
self.call(self.type_void(), None, None, builtin_unreachable, &[], None);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Write results to outputs.
|
// Write results to outputs.
|
||||||
|
@ -35,6 +35,7 @@
|
|||||||
};
|
};
|
||||||
use rustc_data_structures::fx::FxHashSet;
|
use rustc_data_structures::fx::FxHashSet;
|
||||||
use rustc_middle::bug;
|
use rustc_middle::bug;
|
||||||
|
use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrs;
|
||||||
use rustc_middle::ty::{ParamEnv, Ty, TyCtxt};
|
use rustc_middle::ty::{ParamEnv, Ty, TyCtxt};
|
||||||
use rustc_middle::ty::layout::{FnAbiError, FnAbiOfHelpers, FnAbiRequest, HasParamEnv, HasTyCtxt, LayoutError, LayoutOfHelpers, TyAndLayout};
|
use rustc_middle::ty::layout::{FnAbiError, FnAbiOfHelpers, FnAbiRequest, HasParamEnv, HasTyCtxt, LayoutError, LayoutOfHelpers, TyAndLayout};
|
||||||
use rustc_span::Span;
|
use rustc_span::Span;
|
||||||
@ -455,12 +456,12 @@ fn switch(&mut self, value: RValue<'gcc>, default_block: Block<'gcc>, cases: imp
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature="master")]
|
#[cfg(feature="master")]
|
||||||
fn invoke(&mut self, typ: Type<'gcc>, _fn_abi: Option<&FnAbi<'tcx, Ty<'tcx>>>, func: RValue<'gcc>, args: &[RValue<'gcc>], then: Block<'gcc>, catch: Block<'gcc>, _funclet: Option<&Funclet>) -> RValue<'gcc> {
|
fn invoke(&mut self, typ: Type<'gcc>, fn_attrs: Option<&CodegenFnAttrs>, _fn_abi: Option<&FnAbi<'tcx, Ty<'tcx>>>, func: RValue<'gcc>, args: &[RValue<'gcc>], then: Block<'gcc>, catch: Block<'gcc>, _funclet: Option<&Funclet>) -> RValue<'gcc> {
|
||||||
let try_block = self.current_func().new_block("try");
|
let try_block = self.current_func().new_block("try");
|
||||||
|
|
||||||
let current_block = self.block.clone();
|
let current_block = self.block.clone();
|
||||||
self.block = try_block;
|
self.block = try_block;
|
||||||
let call = self.call(typ, None, func, args, None); // TODO(antoyo): use funclet here?
|
let call = self.call(typ, fn_attrs, None, func, args, None); // TODO(antoyo): use funclet here?
|
||||||
self.block = current_block;
|
self.block = current_block;
|
||||||
|
|
||||||
let return_value = self.current_func()
|
let return_value = self.current_func()
|
||||||
@ -483,8 +484,8 @@ fn invoke(&mut self, typ: Type<'gcc>, _fn_abi: Option<&FnAbi<'tcx, Ty<'tcx>>>, f
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(not(feature="master"))]
|
#[cfg(not(feature="master"))]
|
||||||
fn invoke(&mut self, typ: Type<'gcc>, fn_abi: Option<&FnAbi<'tcx, Ty<'tcx>>>, func: RValue<'gcc>, args: &[RValue<'gcc>], then: Block<'gcc>, catch: Block<'gcc>, _funclet: Option<&Funclet>) -> RValue<'gcc> {
|
fn invoke(&mut self, typ: Type<'gcc>, fn_attrs: &CodegenFnAttrs, fn_abi: Option<&FnAbi<'tcx, Ty<'tcx>>>, func: RValue<'gcc>, args: &[RValue<'gcc>], then: Block<'gcc>, catch: Block<'gcc>, _funclet: Option<&Funclet>) -> RValue<'gcc> {
|
||||||
let call_site = self.call(typ, None, func, args, None);
|
let call_site = self.call(typ, fn_attrs, None, func, args, None);
|
||||||
let condition = self.context.new_rvalue_from_int(self.bool_type, 1);
|
let condition = self.context.new_rvalue_from_int(self.bool_type, 1);
|
||||||
self.llbb().end_with_conditional(None, condition, then, catch);
|
self.llbb().end_with_conditional(None, condition, then, catch);
|
||||||
if let Some(_fn_abi) = fn_abi {
|
if let Some(_fn_abi) = fn_abi {
|
||||||
@ -1351,6 +1352,7 @@ fn lifetime_end(&mut self, _ptr: RValue<'gcc>, _size: Size) {
|
|||||||
fn call(
|
fn call(
|
||||||
&mut self,
|
&mut self,
|
||||||
_typ: Type<'gcc>,
|
_typ: Type<'gcc>,
|
||||||
|
_fn_attrs: Option<&CodegenFnAttrs>,
|
||||||
fn_abi: Option<&FnAbi<'tcx, Ty<'tcx>>>,
|
fn_abi: Option<&FnAbi<'tcx, Ty<'tcx>>>,
|
||||||
func: RValue<'gcc>,
|
func: RValue<'gcc>,
|
||||||
args: &[RValue<'gcc>],
|
args: &[RValue<'gcc>],
|
||||||
|
@ -113,7 +113,7 @@ fn codegen_intrinsic_call(&mut self, instance: Instance<'tcx>, fn_abi: &FnAbi<'t
|
|||||||
_ if simple.is_some() => {
|
_ if simple.is_some() => {
|
||||||
// FIXME(antoyo): remove this cast when the API supports function.
|
// FIXME(antoyo): remove this cast when the API supports function.
|
||||||
let func = unsafe { std::mem::transmute(simple.expect("simple")) };
|
let func = unsafe { std::mem::transmute(simple.expect("simple")) };
|
||||||
self.call(self.type_void(), None, func, &args.iter().map(|arg| arg.immediate()).collect::<Vec<_>>(), None)
|
self.call(self.type_void(), None, None, func, &args.iter().map(|arg| arg.immediate()).collect::<Vec<_>>(), None)
|
||||||
},
|
},
|
||||||
sym::likely => {
|
sym::likely => {
|
||||||
self.expect(args[0].immediate(), true)
|
self.expect(args[0].immediate(), true)
|
||||||
@ -326,7 +326,7 @@ fn codegen_intrinsic_call(&mut self, instance: Instance<'tcx>, fn_abi: &FnAbi<'t
|
|||||||
let masked = self.and(addr, mask);
|
let masked = self.and(addr, mask);
|
||||||
self.bitcast(masked, void_ptr_type)
|
self.bitcast(masked, void_ptr_type)
|
||||||
},
|
},
|
||||||
|
|
||||||
_ if name_str.starts_with("simd_") => {
|
_ if name_str.starts_with("simd_") => {
|
||||||
match generic_simd_intrinsic(self, name, callee_ty, args, ret_ty, llret_ty, span) {
|
match generic_simd_intrinsic(self, name, callee_ty, args, ret_ty, llret_ty, span) {
|
||||||
Ok(llval) => llval,
|
Ok(llval) => llval,
|
||||||
@ -354,7 +354,7 @@ fn codegen_intrinsic_call(&mut self, instance: Instance<'tcx>, fn_abi: &FnAbi<'t
|
|||||||
fn abort(&mut self) {
|
fn abort(&mut self) {
|
||||||
let func = self.context.get_builtin_function("abort");
|
let func = self.context.get_builtin_function("abort");
|
||||||
let func: RValue<'gcc> = unsafe { std::mem::transmute(func) };
|
let func: RValue<'gcc> = unsafe { std::mem::transmute(func) };
|
||||||
self.call(self.type_void(), None, func, &[], None);
|
self.call(self.type_void(), None, None, func, &[], None);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn assume(&mut self, value: Self::Value) {
|
fn assume(&mut self, value: Self::Value) {
|
||||||
@ -1135,7 +1135,7 @@ fn saturating_sub(&mut self, lhs: RValue<'gcc>, rhs: RValue<'gcc>, signed: bool,
|
|||||||
|
|
||||||
fn try_intrinsic<'a, 'b, 'gcc, 'tcx>(bx: &'b mut Builder<'a, 'gcc, 'tcx>, try_func: RValue<'gcc>, data: RValue<'gcc>, _catch_func: RValue<'gcc>, dest: RValue<'gcc>) {
|
fn try_intrinsic<'a, 'b, 'gcc, 'tcx>(bx: &'b mut Builder<'a, 'gcc, 'tcx>, try_func: RValue<'gcc>, data: RValue<'gcc>, _catch_func: RValue<'gcc>, dest: RValue<'gcc>) {
|
||||||
if bx.sess().panic_strategy() == PanicStrategy::Abort {
|
if bx.sess().panic_strategy() == PanicStrategy::Abort {
|
||||||
bx.call(bx.type_void(), None, try_func, &[data], None);
|
bx.call(bx.type_void(), None, None, try_func, &[data], None);
|
||||||
// Return 0 unconditionally from the intrinsic call;
|
// Return 0 unconditionally from the intrinsic call;
|
||||||
// we can never unwind.
|
// we can never unwind.
|
||||||
let ret_align = bx.tcx.data_layout.i32_align.abi;
|
let ret_align = bx.tcx.data_layout.i32_align.abi;
|
||||||
@ -1204,21 +1204,21 @@ fn codegen_gnu_try<'gcc>(bx: &mut Builder<'_, 'gcc, '_>, try_func: RValue<'gcc>,
|
|||||||
let zero = bx.cx.context.new_rvalue_zero(bx.int_type);
|
let zero = bx.cx.context.new_rvalue_zero(bx.int_type);
|
||||||
let ptr = bx.cx.context.new_call(None, eh_pointer_builtin, &[zero]);
|
let ptr = bx.cx.context.new_call(None, eh_pointer_builtin, &[zero]);
|
||||||
let catch_ty = bx.type_func(&[bx.type_i8p(), bx.type_i8p()], bx.type_void());
|
let catch_ty = bx.type_func(&[bx.type_i8p(), bx.type_i8p()], bx.type_void());
|
||||||
bx.call(catch_ty, None, catch_func, &[data, ptr], None);
|
bx.call(catch_ty, None, None, catch_func, &[data, ptr], None);
|
||||||
bx.ret(bx.const_i32(1));
|
bx.ret(bx.const_i32(1));
|
||||||
|
|
||||||
// NOTE: the blocks must be filled before adding the try/catch, otherwise gcc will not
|
// NOTE: the blocks must be filled before adding the try/catch, otherwise gcc will not
|
||||||
// generate a try/catch.
|
// generate a try/catch.
|
||||||
// FIXME(antoyo): add a check in the libgccjit API to prevent this.
|
// FIXME(antoyo): add a check in the libgccjit API to prevent this.
|
||||||
bx.switch_to_block(current_block);
|
bx.switch_to_block(current_block);
|
||||||
bx.invoke(try_func_ty, None, try_func, &[data], then, catch, None);
|
bx.invoke(try_func_ty, None, None, try_func, &[data], then, catch, None);
|
||||||
});
|
});
|
||||||
|
|
||||||
let func = unsafe { std::mem::transmute(func) };
|
let func = unsafe { std::mem::transmute(func) };
|
||||||
|
|
||||||
// Note that no invoke is used here because by definition this function
|
// Note that no invoke is used here because by definition this function
|
||||||
// can't panic (that's what it's catching).
|
// can't panic (that's what it's catching).
|
||||||
let ret = bx.call(llty, None, func, &[try_func, data, catch_func], None);
|
let ret = bx.call(llty, None, None, func, &[try_func, data, catch_func], None);
|
||||||
let i32_align = bx.tcx().data_layout.i32_align.abi;
|
let i32_align = bx.tcx().data_layout.i32_align.abi;
|
||||||
bx.store(ret, dest, i32_align);
|
bx.store(ret, dest, i32_align);
|
||||||
}
|
}
|
||||||
|
12
src/type_.rs
12
src/type_.rs
@ -280,16 +280,4 @@ pub fn struct_fields<'gcc, 'tcx>(cx: &CodegenCx<'gcc, 'tcx>, layout: TyAndLayout
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl<'gcc, 'tcx> TypeMembershipMethods<'tcx> for CodegenCx<'gcc, 'tcx> {
|
impl<'gcc, 'tcx> TypeMembershipMethods<'tcx> for CodegenCx<'gcc, 'tcx> {
|
||||||
fn set_type_metadata(&self, _function: RValue<'gcc>, _typeid: String) {
|
|
||||||
// Unsupported.
|
|
||||||
}
|
|
||||||
|
|
||||||
fn typeid_metadata(&self, _typeid: String) -> RValue<'gcc> {
|
|
||||||
// Unsupported.
|
|
||||||
self.context.new_rvalue_from_int(self.int_type, 0)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn set_kcfi_type_metadata(&self, _function: RValue<'gcc>, _kcfi_typeid: u32) {
|
|
||||||
// Unsupported.
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user