Auto merge of #95548 - rcvalle:rust-cfi-2, r=nagisa

Add fine-grained LLVM CFI support to the Rust compiler

This PR improves the LLVM Control Flow Integrity (CFI) support in the Rust compiler by providing forward-edge control flow protection for Rust-compiled code only by aggregating function pointers in groups identified by their return and parameter types.

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) will be provided in later work as part of this project by identifying C char and integer type uses at the time types are encoded (see Type metadata in the design document in the tracking issue https://github.com/rust-lang/rust/issues/89653).

LLVM CFI can be enabled with -Zsanitizer=cfi and requires LTO (i.e., -Clto).

Thank you again, `@eddyb,` `@nagisa,` `@pcc,` and `@tmiasko` for all the help!
This commit is contained in:
bors 2022-07-24 01:22:36 +00:00
commit db8086eb60
25 changed files with 1731 additions and 164 deletions

View File

@ -4519,6 +4519,7 @@ dependencies = [
name = "rustc_symbol_mangling"
version = "0.0.0"
dependencies = [
"bitflags",
"punycode",
"rustc-demangle",
"rustc_data_structures",

View File

@ -784,16 +784,6 @@ impl<'a, 'gcc, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'gcc, 'tcx> {
// TODO(antoyo)
}
fn type_metadata(&mut self, _function: RValue<'gcc>, _typeid: String) {
// Unsupported.
}
fn typeid_metadata(&mut self, _typeid: String) -> RValue<'gcc> {
// Unsupported.
self.context.new_rvalue_from_int(self.int_type, 0)
}
fn store(&mut self, val: RValue<'gcc>, ptr: RValue<'gcc>, align: Align) -> RValue<'gcc> {
self.store_with_flags(val, ptr, align, MemFlags::empty())
}

View File

@ -1,7 +1,7 @@
use std::convert::TryInto;
use gccjit::{RValue, Struct, Type};
use rustc_codegen_ssa::traits::{BaseTypeMethods, DerivedTypeMethods};
use rustc_codegen_ssa::traits::{BaseTypeMethods, DerivedTypeMethods, TypeMembershipMethods};
use rustc_codegen_ssa::common::TypeKind;
use rustc_middle::{bug, ty};
use rustc_middle::ty::layout::TyAndLayout;
@ -290,3 +290,14 @@ pub fn struct_fields<'gcc, 'tcx>(cx: &CodegenCx<'gcc, 'tcx>, layout: TyAndLayout
(result, packed)
}
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)
}
}

View File

@ -18,7 +18,6 @@ rustc_middle = { path = "../rustc_middle" }
rustc-demangle = "0.1.21"
rustc_attr = { path = "../rustc_attr" }
rustc_codegen_ssa = { path = "../rustc_codegen_ssa" }
rustc_symbol_mangling = { path = "../rustc_symbol_mangling" }
rustc_data_structures = { path = "../rustc_data_structures" }
rustc_errors = { path = "../rustc_errors" }
rustc_fs_util = { path = "../rustc_fs_util" }
@ -30,6 +29,7 @@ rustc_metadata = { path = "../rustc_metadata" }
rustc_query_system = { path = "../rustc_query_system" }
rustc_session = { path = "../rustc_session" }
rustc_serialize = { path = "../rustc_serialize" }
rustc_symbol_mangling = { path = "../rustc_symbol_mangling" }
rustc_target = { path = "../rustc_target" }
smallvec = { version = "1.8.1", features = ["union", "may_dangle"] }
rustc_ast = { path = "../rustc_ast" }

View File

@ -626,32 +626,6 @@ impl<'a, 'll, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'll, 'tcx> {
}
}
fn type_metadata(&mut self, function: &'ll Value, typeid: String) {
let typeid_metadata = self.typeid_metadata(typeid);
let v = [self.const_usize(0), typeid_metadata];
unsafe {
llvm::LLVMGlobalSetMetadata(
function,
llvm::MD_type as c_uint,
llvm::LLVMValueAsMetadata(llvm::LLVMMDNodeInContext(
self.cx.llcx,
v.as_ptr(),
v.len() as c_uint,
)),
)
}
}
fn typeid_metadata(&mut self, typeid: String) -> Self::Value {
unsafe {
llvm::LLVMMDStringInContext(
self.cx.llcx,
typeid.as_ptr() as *const c_char,
typeid.as_bytes().len() as c_uint,
)
}
}
fn store(&mut self, val: &'ll Value, ptr: &'ll Value, align: Align) -> &'ll Value {
self.store_with_flags(val, ptr, align, MemFlags::empty())
}

View File

@ -18,7 +18,9 @@ use crate::llvm;
use crate::llvm::AttributePlace::Function;
use crate::type_::Type;
use crate::value::Value;
use rustc_codegen_ssa::traits::TypeMembershipMethods;
use rustc_middle::ty::Ty;
use rustc_symbol_mangling::typeid::typeid_for_fnabi;
use smallvec::SmallVec;
use tracing::debug;
@ -97,6 +99,12 @@ impl<'ll, 'tcx> CodegenCx<'ll, 'tcx> {
fn_abi.llvm_type(self),
);
fn_abi.apply_attrs_llfn(self, llfn);
if self.tcx.sess.is_sanitizer_cfi_enabled() {
let typeid = typeid_for_fnabi(self.tcx, fn_abi);
self.set_type_metadata(llfn, typeid);
}
llfn
}

View File

@ -19,7 +19,7 @@ use rustc_target::abi::{AddressSpace, Align, Integer, Size};
use std::fmt;
use std::ptr;
use libc::c_uint;
use libc::{c_char, c_uint};
impl PartialEq for Type {
fn eq(&self, other: &Self) -> bool {
@ -289,3 +289,31 @@ impl<'ll, 'tcx> LayoutTypeMethods<'tcx> for CodegenCx<'ll, 'tcx> {
ty.llvm_type(self)
}
}
impl<'ll, 'tcx> TypeMembershipMethods<'tcx> for CodegenCx<'ll, 'tcx> {
fn set_type_metadata(&self, function: &'ll Value, typeid: String) {
let typeid_metadata = self.typeid_metadata(typeid);
let v = [self.const_usize(0), typeid_metadata];
unsafe {
llvm::LLVMGlobalSetMetadata(
function,
llvm::MD_type as c_uint,
llvm::LLVMValueAsMetadata(llvm::LLVMMDNodeInContext(
self.llcx,
v.as_ptr(),
v.len() as c_uint,
)),
)
}
}
fn typeid_metadata(&self, typeid: String) -> &'ll Value {
unsafe {
llvm::LLVMMDStringInContext(
self.llcx,
typeid.as_ptr() as *const c_char,
typeid.len() as c_uint,
)
}
}
}

View File

@ -20,7 +20,7 @@ use rustc_middle::ty::print::{with_no_trimmed_paths, with_no_visible_paths};
use rustc_middle::ty::{self, Instance, Ty, TypeVisitable};
use rustc_span::source_map::Span;
use rustc_span::{sym, Symbol};
use rustc_symbol_mangling::typeid_for_fnabi;
use rustc_symbol_mangling::typeid::typeid_for_fnabi;
use rustc_target::abi::call::{ArgAbi, FnAbi, PassMode};
use rustc_target::abi::{self, HasDataLayout, WrappingRange};
use rustc_target::spec::abi::Abi;
@ -918,7 +918,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
// FIXME(rcvalle): Add support for generalized identifiers.
// FIXME(rcvalle): Create distinct unnamed MDNodes for internal identifiers.
let typeid = typeid_for_fnabi(bx.tcx(), fn_abi);
let typeid_metadata = bx.typeid_metadata(typeid);
let typeid_metadata = self.cx.typeid_metadata(typeid);
// Test whether the function pointer is associated with the type identifier.
let cond = bx.type_test(fn_ptr, typeid_metadata);

View File

@ -3,7 +3,6 @@ use rustc_middle::mir;
use rustc_middle::mir::interpret::ErrorHandled;
use rustc_middle::ty::layout::{FnAbiOf, HasTyCtxt, TyAndLayout};
use rustc_middle::ty::{self, Instance, Ty, TypeFoldable, TypeVisitable};
use rustc_symbol_mangling::typeid_for_fnabi;
use rustc_target::abi::call::{FnAbi, PassMode};
use std::iter;
@ -247,13 +246,6 @@ pub fn codegen_mir<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
for (bb, _) in traversal::reverse_postorder(&mir) {
fx.codegen_block(bb);
}
// For backends that support CFI using type membership (i.e., testing whether a given pointer
// is associated with a type identifier).
if cx.tcx().sess.is_sanitizer_cfi_enabled() {
let typeid = typeid_for_fnabi(cx.tcx(), fn_abi);
bx.type_metadata(llfn, typeid);
}
}
/// Produces, for each argument, a `Value` pointing at the

View File

@ -160,8 +160,6 @@ pub trait BuilderMethods<'a, 'tcx>:
fn range_metadata(&mut self, load: Self::Value, range: WrappingRange);
fn nonnull_metadata(&mut self, load: Self::Value);
fn type_metadata(&mut self, function: Self::Function, typeid: String);
fn typeid_metadata(&mut self, typeid: String) -> Self::Value;
fn store(&mut self, val: Self::Value, ptr: Self::Value, align: Align) -> Self::Value;
fn store_with_flags(

View File

@ -40,7 +40,8 @@ pub use self::intrinsic::IntrinsicCallMethods;
pub use self::misc::MiscMethods;
pub use self::statics::{StaticBuilderMethods, StaticMethods};
pub use self::type_::{
ArgAbiMethods, BaseTypeMethods, DerivedTypeMethods, LayoutTypeMethods, TypeMethods,
ArgAbiMethods, BaseTypeMethods, DerivedTypeMethods, LayoutTypeMethods, TypeMembershipMethods,
TypeMethods,
};
pub use self::write::{ModuleBufferMethods, ThinBufferMethods, WriteBackendMethods};

View File

@ -117,6 +117,13 @@ pub trait LayoutTypeMethods<'tcx>: Backend<'tcx> {
) -> Self::Type;
}
// For backends that support CFI using type membership (i.e., testing whether a given pointer is
// associated with a type identifier).
pub trait TypeMembershipMethods<'tcx>: Backend<'tcx> {
fn set_type_metadata(&self, function: Self::Function, typeid: String);
fn typeid_metadata(&self, typeid: String) -> Self::Value;
}
pub trait ArgAbiMethods<'tcx>: HasCodegen<'tcx> {
fn store_fn_arg(
&mut self,
@ -133,6 +140,12 @@ pub trait ArgAbiMethods<'tcx>: HasCodegen<'tcx> {
fn arg_memory_ty(&self, arg_abi: &ArgAbi<'tcx, Ty<'tcx>>) -> Self::Type;
}
pub trait TypeMethods<'tcx>: DerivedTypeMethods<'tcx> + LayoutTypeMethods<'tcx> {}
pub trait TypeMethods<'tcx>:
DerivedTypeMethods<'tcx> + LayoutTypeMethods<'tcx> + TypeMembershipMethods<'tcx>
{
}
impl<'tcx, T> TypeMethods<'tcx> for T where Self: DerivedTypeMethods<'tcx> + LayoutTypeMethods<'tcx> {}
impl<'tcx, T> TypeMethods<'tcx> for T where
Self: DerivedTypeMethods<'tcx> + LayoutTypeMethods<'tcx> + TypeMembershipMethods<'tcx>
{
}

View File

@ -7,6 +7,7 @@ edition = "2021"
doctest = false
[dependencies]
bitflags = "1.2.1"
tracing = "0.1"
punycode = "0.4.0"
rustc-demangle = "0.1.21"

View File

@ -102,9 +102,8 @@ use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrs;
use rustc_middle::mir::mono::{InstantiationMode, MonoItem};
use rustc_middle::ty::query::Providers;
use rustc_middle::ty::subst::SubstsRef;
use rustc_middle::ty::{self, Instance, Ty, TyCtxt};
use rustc_middle::ty::{self, Instance, TyCtxt};
use rustc_session::config::SymbolManglingVersion;
use rustc_target::abi::call::FnAbi;
use tracing::debug;
@ -112,6 +111,7 @@ mod legacy;
mod v0;
pub mod test;
pub mod typeid;
/// This function computes the symbol name for the given `instance` and the
/// given instantiating crate. That is, if you know that instance X is
@ -150,11 +150,6 @@ fn symbol_name_provider<'tcx>(tcx: TyCtxt<'tcx>, instance: Instance<'tcx>) -> ty
ty::SymbolName::new(tcx, &symbol_name)
}
/// This function computes the typeid for the given function ABI.
pub fn typeid_for_fnabi<'tcx>(tcx: TyCtxt<'tcx>, fn_abi: &FnAbi<'tcx, Ty<'tcx>>) -> String {
v0::mangle_typeid_for_fnabi(tcx, fn_abi)
}
pub fn typeid_for_trait_ref<'tcx>(
tcx: TyCtxt<'tcx>,
trait_ref: ty::PolyExistentialTraitRef<'tcx>,

View File

@ -0,0 +1,18 @@
// For more information about type metadata and type metadata identifiers for cross-language LLVM
// CFI support, see Type metadata in the design document in the tracking issue #89653.
use rustc_middle::ty::{FnSig, Ty, TyCtxt};
use rustc_target::abi::call::FnAbi;
mod typeid_itanium_cxx_abi;
use typeid_itanium_cxx_abi::TypeIdOptions;
/// Returns a type metadata identifier for the specified FnAbi.
pub fn typeid_for_fnabi<'tcx>(tcx: TyCtxt<'tcx>, fn_abi: &FnAbi<'tcx, Ty<'tcx>>) -> String {
typeid_itanium_cxx_abi::typeid_for_fnabi(tcx, fn_abi, TypeIdOptions::NO_OPTIONS)
}
/// Returns a type metadata identifier for the specified FnSig.
pub fn typeid_for_fnsig<'tcx>(tcx: TyCtxt<'tcx>, fn_sig: &FnSig<'tcx>) -> String {
typeid_itanium_cxx_abi::typeid_for_fnsig(tcx, fn_sig, TypeIdOptions::NO_OPTIONS)
}

View File

@ -0,0 +1,929 @@
// For more information about type metadata and type metadata identifiers for cross-language LLVM
// CFI support, see Type metadata in the design document in the tracking issue #89653.
// FIXME(rcvalle): Identify C char and integer type uses and encode them with their respective
// builtin type encodings as specified by the Itanium C++ ABI for extern function types with the "C"
// calling convention to use this encoding for cross-language LLVM CFI.
use bitflags::bitflags;
use core::fmt::Display;
use rustc_data_structures::base_n;
use rustc_data_structures::fx::FxHashMap;
use rustc_hir as hir;
use rustc_middle::ty::subst::{GenericArg, GenericArgKind, SubstsRef};
use rustc_middle::ty::{
self, Binder, Const, ExistentialPredicate, FloatTy, FnSig, IntTy, List, Region, RegionKind,
Term, Ty, TyCtxt, UintTy,
};
use rustc_span::def_id::DefId;
use rustc_span::symbol::sym;
use rustc_target::abi::call::{Conv, FnAbi};
use rustc_target::spec::abi::Abi;
use std::fmt::Write as _;
/// Type and extended type qualifiers.
#[derive(Eq, Hash, PartialEq)]
enum TyQ {
None,
Const,
Mut,
}
/// Substitution dictionary key.
#[derive(Eq, Hash, PartialEq)]
enum DictKey<'tcx> {
Ty(Ty<'tcx>, TyQ),
Region(Region<'tcx>),
Const(Const<'tcx>),
Predicate(ExistentialPredicate<'tcx>),
}
bitflags! {
/// Options for typeid_for_fnabi and typeid_for_fnsig.
pub struct TypeIdOptions: u32 {
const NO_OPTIONS = 0;
const GENERALIZE_POINTERS = 1;
const GENERALIZE_REPR_C = 2;
}
}
/// Options for encode_ty.
type EncodeTyOptions = TypeIdOptions;
/// Options for transform_ty.
type TransformTyOptions = TypeIdOptions;
/// Converts a number to a disambiguator (see
/// <https://rust-lang.github.io/rfcs/2603-rust-symbol-name-mangling-v0.html>).
fn to_disambiguator(num: u64) -> String {
if let Some(num) = num.checked_sub(1) {
format!("s{}_", base_n::encode(num as u128, 62))
} else {
"s_".to_string()
}
}
/// Converts a number to a sequence number (see
/// <https://itanium-cxx-abi.github.io/cxx-abi/abi.html#mangle.seq-id>).
fn to_seq_id(num: usize) -> String {
if let Some(num) = num.checked_sub(1) {
base_n::encode(num as u128, 36).to_uppercase()
} else {
"".to_string()
}
}
/// Substitutes a component if found in the substitution dictionary (see
/// <https://itanium-cxx-abi.github.io/cxx-abi/abi.html#mangling-compression>).
fn compress<'tcx>(
dict: &mut FxHashMap<DictKey<'tcx>, usize>,
key: DictKey<'tcx>,
comp: &mut String,
) {
match dict.get(&key) {
Some(num) => {
comp.clear();
let _ = write!(comp, "S{}_", to_seq_id(*num));
}
None => {
dict.insert(key, dict.len());
}
}
}
// FIXME(rcvalle): Move to compiler/rustc_middle/src/ty/sty.rs after C types work is done, possibly
// along with other is_c_type methods.
/// Returns whether a `ty::Ty` is `c_void`.
fn is_c_void_ty<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> bool {
match ty.kind() {
ty::Adt(adt_def, ..) => {
let def_id = adt_def.0.did;
let crate_name = tcx.crate_name(def_id.krate);
if tcx.item_name(def_id).as_str() == "c_void"
&& (crate_name == sym::core || crate_name == sym::std || crate_name == sym::libc)
{
true
} else {
false
}
}
_ => false,
}
}
/// Encodes a const using the Itanium C++ ABI as a literal argument (see
/// <https://itanium-cxx-abi.github.io/cxx-abi/abi.html#mangling.literal>).
fn encode_const<'tcx>(
tcx: TyCtxt<'tcx>,
c: Const<'tcx>,
dict: &mut FxHashMap<DictKey<'tcx>, usize>,
options: EncodeTyOptions,
) -> String {
// L<element-type>[n]<element-value>E as literal argument
let mut s = String::from('L');
// Element type
s.push_str(&encode_ty(tcx, c.ty(), dict, options));
// The only allowed types of const parameters are bool, u8, u16, u32, u64, u128, usize i8, i16,
// i32, i64, i128, isize, and char. The bool value false is encoded as 0 and true as 1.
fn push_signed_value<T: Display + PartialOrd>(s: &mut String, value: T, zero: T) {
if value < zero {
s.push('n')
};
let _ = write!(s, "{}", value);
}
fn push_unsigned_value<T: Display>(s: &mut String, value: T) {
let _ = write!(s, "{}", value);
}
if let Some(scalar_int) = c.kind().try_to_scalar_int() {
let signed = c.ty().is_signed();
match scalar_int.size().bits() {
8 if signed => push_signed_value(&mut s, scalar_int.try_to_i8().unwrap(), 0),
16 if signed => push_signed_value(&mut s, scalar_int.try_to_i16().unwrap(), 0),
32 if signed => push_signed_value(&mut s, scalar_int.try_to_i32().unwrap(), 0),
64 if signed => push_signed_value(&mut s, scalar_int.try_to_i64().unwrap(), 0),
128 if signed => push_signed_value(&mut s, scalar_int.try_to_i128().unwrap(), 0),
8 => push_unsigned_value(&mut s, scalar_int.try_to_u8().unwrap()),
16 => push_unsigned_value(&mut s, scalar_int.try_to_u16().unwrap()),
32 => push_unsigned_value(&mut s, scalar_int.try_to_u32().unwrap()),
64 => push_unsigned_value(&mut s, scalar_int.try_to_u64().unwrap()),
128 => push_unsigned_value(&mut s, scalar_int.try_to_u128().unwrap()),
_ => {
bug!("encode_const: unexpected size `{:?}`", scalar_int.size().bits());
}
};
} else {
bug!("encode_const: unexpected type `{:?}`", c.ty());
}
// Close the "L..E" pair
s.push('E');
compress(dict, DictKey::Const(c), &mut s);
s
}
/// Encodes a FnSig using the Itanium C++ ABI with vendor extended type qualifiers and types for
/// Rust types that are not used at the FFI boundary.
fn encode_fnsig<'tcx>(
tcx: TyCtxt<'tcx>,
fn_sig: &FnSig<'tcx>,
dict: &mut FxHashMap<DictKey<'tcx>, usize>,
options: TypeIdOptions,
) -> String {
// Function types are delimited by an "F..E" pair
let mut s = String::from("F");
let mut encode_ty_options = EncodeTyOptions::from_bits(options.bits())
.unwrap_or_else(|| bug!("encode_fnsig: invalid option(s) `{:?}`", options.bits()));
match fn_sig.abi {
Abi::C { .. } => {
encode_ty_options.insert(EncodeTyOptions::GENERALIZE_REPR_C);
}
_ => {
encode_ty_options.remove(EncodeTyOptions::GENERALIZE_REPR_C);
}
}
// Encode the return type
let transform_ty_options = TransformTyOptions::from_bits(options.bits())
.unwrap_or_else(|| bug!("encode_fnsig: invalid option(s) `{:?}`", options.bits()));
let ty = transform_ty(tcx, fn_sig.output(), transform_ty_options);
s.push_str(&encode_ty(tcx, ty, dict, encode_ty_options));
// Encode the parameter types
let tys = fn_sig.inputs();
if !tys.is_empty() {
for ty in tys {
let ty = transform_ty(tcx, *ty, transform_ty_options);
s.push_str(&encode_ty(tcx, ty, dict, encode_ty_options));
}
if fn_sig.c_variadic {
s.push('z');
}
} else {
if fn_sig.c_variadic {
s.push('z');
} else {
// Empty parameter lists, whether declared as () or conventionally as (void), are
// encoded with a void parameter specifier "v".
s.push('v')
}
}
// Close the "F..E" pair
s.push('E');
s
}
/// Encodes a predicate using the Itanium C++ ABI with vendor extended type qualifiers and types for
/// Rust types that are not used at the FFI boundary.
fn encode_predicate<'tcx>(
tcx: TyCtxt<'tcx>,
predicate: Binder<'tcx, ExistentialPredicate<'tcx>>,
dict: &mut FxHashMap<DictKey<'tcx>, usize>,
options: EncodeTyOptions,
) -> String {
// u<length><name>[I<element-type1..element-typeN>E], where <element-type> is <subst>, as vendor
// extended type.
let mut s = String::new();
match predicate.as_ref().skip_binder() {
ty::ExistentialPredicate::Trait(trait_ref) => {
let name = encode_ty_name(tcx, trait_ref.def_id);
let _ = write!(s, "u{}{}", name.len(), &name);
s.push_str(&encode_substs(tcx, trait_ref.substs, dict, options));
}
ty::ExistentialPredicate::Projection(projection) => {
let name = encode_ty_name(tcx, projection.item_def_id);
let _ = write!(s, "u{}{}", name.len(), &name);
s.push_str(&encode_substs(tcx, projection.substs, dict, options));
match projection.term {
Term::Ty(ty) => {
s.push_str(&encode_ty(tcx, ty, dict, options));
}
Term::Const(c) => {
s.push_str(&encode_const(tcx, c, dict, options));
}
}
}
ty::ExistentialPredicate::AutoTrait(def_id) => {
let name = encode_ty_name(tcx, *def_id);
let _ = write!(s, "u{}{}", name.len(), &name);
}
};
compress(dict, DictKey::Predicate(*predicate.as_ref().skip_binder()), &mut s);
s
}
/// Encodes predicates using the Itanium C++ ABI with vendor extended type qualifiers and types for
/// Rust types that are not used at the FFI boundary.
fn encode_predicates<'tcx>(
tcx: TyCtxt<'tcx>,
predicates: &List<Binder<'tcx, ExistentialPredicate<'tcx>>>,
dict: &mut FxHashMap<DictKey<'tcx>, usize>,
options: EncodeTyOptions,
) -> String {
// <predicate1[..predicateN]>E as part of vendor extended type
let mut s = String::new();
let predicates: Vec<Binder<'tcx, ExistentialPredicate<'tcx>>> =
predicates.iter().map(|predicate| predicate).collect();
for predicate in predicates {
s.push_str(&encode_predicate(tcx, predicate, dict, options));
}
s
}
/// Encodes a region using the Itanium C++ ABI as a vendor extended type.
fn encode_region<'tcx>(
_tcx: TyCtxt<'tcx>,
region: Region<'tcx>,
dict: &mut FxHashMap<DictKey<'tcx>, usize>,
_options: EncodeTyOptions,
) -> String {
// u6region[I[<region-disambiguator>][<region-index>]E] as vendor extended type
let mut s = String::new();
match region.kind() {
RegionKind::ReLateBound(debruijn, r) => {
s.push_str("u6regionI");
// Debruijn index, which identifies the binder, as region disambiguator
let num = debruijn.index() as u64;
if num > 0 {
s.push_str(&to_disambiguator(num));
}
// Index within the binder
let _ = write!(s, "{}", r.var.index() as u64);
s.push('E');
compress(dict, DictKey::Region(region), &mut s);
}
RegionKind::ReErased => {
s.push_str("u6region");
compress(dict, DictKey::Region(region), &mut s);
}
RegionKind::ReEarlyBound(..)
| RegionKind::ReFree(..)
| RegionKind::ReStatic
| RegionKind::ReVar(..)
| RegionKind::RePlaceholder(..)
| RegionKind::ReEmpty(..) => {
bug!("encode_region: unexpected `{:?}`", region.kind());
}
}
s
}
/// Encodes substs using the Itanium C++ ABI with vendor extended type qualifiers and types for Rust
/// types that are not used at the FFI boundary.
fn encode_substs<'tcx>(
tcx: TyCtxt<'tcx>,
substs: SubstsRef<'tcx>,
dict: &mut FxHashMap<DictKey<'tcx>, usize>,
options: EncodeTyOptions,
) -> String {
// [I<subst1..substN>E] as part of vendor extended type
let mut s = String::new();
let substs: Vec<GenericArg<'_>> = substs.iter().map(|subst| subst).collect();
if !substs.is_empty() {
s.push('I');
for subst in substs {
match subst.unpack() {
GenericArgKind::Lifetime(region) => {
s.push_str(&encode_region(tcx, region, dict, options));
}
GenericArgKind::Type(ty) => {
s.push_str(&encode_ty(tcx, ty, dict, options));
}
GenericArgKind::Const(c) => {
s.push_str(&encode_const(tcx, c, dict, options));
}
}
}
s.push('E');
}
s
}
/// Encodes a ty:Ty name, including its crate and path disambiguators and names.
fn encode_ty_name<'tcx>(tcx: TyCtxt<'tcx>, def_id: DefId) -> String {
// Encode <name> for use in u<length><name>[I<element-type1..element-typeN>E], where
// <element-type> is <subst>, using v0's <path> without v0's extended form of paths:
//
// N<namespace-tagN>..N<namespace-tag1>
// C<crate-disambiguator><crate-name>
// <path-disambiguator1><path-name1>..<path-disambiguatorN><path-nameN>
//
// With additional tags for DefPathData::Impl and DefPathData::ForeignMod. For instance:
//
// pub type Type1 = impl Send;
// let _: Type1 = <Struct1<i32>>::foo;
// fn foo1(_: Type1) { }
//
// pub type Type2 = impl Send;
// let _: Type2 = <Trait1<i32>>::foo;
// fn foo2(_: Type2) { }
//
// pub type Type3 = impl Send;
// let _: Type3 = <i32 as Trait1<i32>>::foo;
// fn foo3(_: Type3) { }
//
// pub type Type4 = impl Send;
// let _: Type4 = <Struct1<i32> as Trait1<i32>>::foo;
// fn foo3(_: Type4) { }
//
// Are encoded as:
//
// _ZTSFvu29NvNIC1234_5crate8{{impl}}3fooIu3i32EE
// _ZTSFvu27NvNtC1234_5crate6Trait13fooIu3dynIu21NtC1234_5crate6Trait1Iu3i32Eu6regionES_EE
// _ZTSFvu27NvNtC1234_5crate6Trait13fooIu3i32S_EE
// _ZTSFvu27NvNtC1234_5crate6Trait13fooIu22NtC1234_5crate7Struct1Iu3i32ES_EE
//
// The reason for not using v0's extended form of paths is to use a consistent and simpler
// encoding, as the reasoning for using it isn't relevand for type metadata identifiers (i.e.,
// keep symbol names close to how methods are represented in error messages). See
// https://rust-lang.github.io/rfcs/2603-rust-symbol-name-mangling-v0.html#methods.
let mut s = String::new();
// Start and namespace tags
let mut def_path = tcx.def_path(def_id);
def_path.data.reverse();
for disambiguated_data in &def_path.data {
s.push('N');
s.push_str(match disambiguated_data.data {
hir::definitions::DefPathData::Impl => "I", // Not specified in v0's <namespace>
hir::definitions::DefPathData::ForeignMod => "F", // Not specified in v0's <namespace>
hir::definitions::DefPathData::TypeNs(..) => "t",
hir::definitions::DefPathData::ValueNs(..) => "v",
hir::definitions::DefPathData::ClosureExpr => "C",
hir::definitions::DefPathData::Ctor => "c",
hir::definitions::DefPathData::AnonConst => "k",
hir::definitions::DefPathData::ImplTrait => "i",
hir::definitions::DefPathData::CrateRoot
| hir::definitions::DefPathData::Use
| hir::definitions::DefPathData::GlobalAsm
| hir::definitions::DefPathData::MacroNs(..)
| hir::definitions::DefPathData::LifetimeNs(..) => {
bug!("encode_ty_name: unexpected `{:?}`", disambiguated_data.data);
}
});
}
// Crate disambiguator and name
s.push('C');
s.push_str(&to_disambiguator(tcx.stable_crate_id(def_path.krate).to_u64()));
let crate_name = tcx.crate_name(def_path.krate).to_string();
let _ = write!(s, "{}{}", crate_name.len(), &crate_name);
// Disambiguators and names
def_path.data.reverse();
for disambiguated_data in &def_path.data {
let num = disambiguated_data.disambiguator as u64;
if num > 0 {
s.push_str(&to_disambiguator(num));
}
let name = disambiguated_data.data.to_string();
let _ = write!(s, "{}", name.len());
// Prepend a '_' if name starts with a digit or '_'
if let Some(first) = name.as_bytes().get(0) {
if first.is_ascii_digit() || *first == b'_' {
s.push('_');
}
} else {
bug!("encode_ty_name: invalid name `{:?}`", name);
}
s.push_str(&name);
}
s
}
/// Encodes a ty:Ty using the Itanium C++ ABI with vendor extended type qualifiers and types for
/// Rust types that are not used at the FFI boundary.
fn encode_ty<'tcx>(
tcx: TyCtxt<'tcx>,
ty: Ty<'tcx>,
dict: &mut FxHashMap<DictKey<'tcx>, usize>,
options: EncodeTyOptions,
) -> String {
let mut typeid = String::new();
match ty.kind() {
// Primitive types
ty::Bool => {
typeid.push('b');
}
ty::Int(..) | ty::Uint(..) | ty::Float(..) => {
// u<length><type-name> as vendor extended type
let mut s = String::from(match ty.kind() {
ty::Int(IntTy::I8) => "u2i8",
ty::Int(IntTy::I16) => "u3i16",
ty::Int(IntTy::I32) => "u3i32",
ty::Int(IntTy::I64) => "u3i64",
ty::Int(IntTy::I128) => "u4i128",
ty::Int(IntTy::Isize) => "u5isize",
ty::Uint(UintTy::U8) => "u2u8",
ty::Uint(UintTy::U16) => "u3u16",
ty::Uint(UintTy::U32) => "u3u32",
ty::Uint(UintTy::U64) => "u3u64",
ty::Uint(UintTy::U128) => "u4u128",
ty::Uint(UintTy::Usize) => "u5usize",
ty::Float(FloatTy::F32) => "u3f32",
ty::Float(FloatTy::F64) => "u3f64",
_ => "",
});
compress(dict, DictKey::Ty(ty, TyQ::None), &mut s);
typeid.push_str(&s);
}
ty::Char => {
// u4char as vendor extended type
let mut s = String::from("u4char");
compress(dict, DictKey::Ty(ty, TyQ::None), &mut s);
typeid.push_str(&s);
}
ty::Str => {
// u3str as vendor extended type
let mut s = String::from("u3str");
compress(dict, DictKey::Ty(ty, TyQ::None), &mut s);
typeid.push_str(&s);
}
ty::Never => {
// u5never as vendor extended type
let mut s = String::from("u5never");
compress(dict, DictKey::Ty(ty, TyQ::None), &mut s);
typeid.push_str(&s);
}
// Compound types
// () in Rust is equivalent to void return type in C
_ if ty.is_unit() => {
typeid.push('v');
}
// Sequence types
ty::Tuple(tys) => {
// u5tupleI<element-type1..element-typeN>E as vendor extended type
let mut s = String::from("u5tupleI");
for ty in tys.iter() {
s.push_str(&encode_ty(tcx, ty, dict, options));
}
s.push('E');
compress(dict, DictKey::Ty(ty, TyQ::None), &mut s);
typeid.push_str(&s);
}
ty::Array(ty0, len) => {
// A<array-length><element-type>
let mut s = String::from("A");
let _ = write!(s, "{}", &len.kind().try_to_scalar().unwrap().to_u64().unwrap());
s.push_str(&encode_ty(tcx, *ty0, dict, options));
compress(dict, DictKey::Ty(ty, TyQ::None), &mut s);
typeid.push_str(&s);
}
ty::Slice(ty0) => {
// u5sliceI<element-type>E as vendor extended type
let mut s = String::from("u5sliceI");
s.push_str(&encode_ty(tcx, *ty0, dict, options));
s.push('E');
compress(dict, DictKey::Ty(ty, TyQ::None), &mut s);
typeid.push_str(&s);
}
// User-defined types
ty::Adt(adt_def, substs) => {
let mut s = String::new();
let def_id = adt_def.0.did;
if options.contains(EncodeTyOptions::GENERALIZE_REPR_C) && adt_def.repr().c() {
// For for cross-language CFI support, the encoding must be compatible at the FFI
// boundary. For instance:
//
// struct type1 {};
// void foo(struct type1* bar) {}
//
// Is encoded as:
//
// _ZTSFvP5type1E
//
// So, encode any repr(C) user-defined type for extern function types with the "C"
// calling convention (or extern types [i.e., ty::Foreign]) as <length><name>, where
// <name> is <unscoped-name>.
let name = tcx.item_name(def_id).to_string();
let _ = write!(s, "{}{}", name.len(), &name);
compress(dict, DictKey::Ty(ty, TyQ::None), &mut s);
} else {
// u<length><name>[I<element-type1..element-typeN>E], where <element-type> is
// <subst>, as vendor extended type.
let name = encode_ty_name(tcx, def_id);
let _ = write!(s, "u{}{}", name.len(), &name);
s.push_str(&encode_substs(tcx, substs, dict, options));
compress(dict, DictKey::Ty(ty, TyQ::None), &mut s);
}
typeid.push_str(&s);
}
ty::Foreign(def_id) => {
// <length><name>, where <name> is <unscoped-name>
let mut s = String::new();
let name = tcx.item_name(*def_id).to_string();
let _ = write!(s, "{}{}", name.len(), &name);
compress(dict, DictKey::Ty(ty, TyQ::None), &mut s);
typeid.push_str(&s);
}
// Function types
ty::FnDef(def_id, substs)
| ty::Closure(def_id, substs)
| ty::Generator(def_id, substs, ..) => {
// u<length><name>[I<element-type1..element-typeN>E], where <element-type> is <subst>,
// as vendor extended type.
let mut s = String::new();
let name = encode_ty_name(tcx, *def_id);
let _ = write!(s, "u{}{}", name.len(), &name);
s.push_str(&encode_substs(tcx, substs, dict, options));
compress(dict, DictKey::Ty(ty, TyQ::None), &mut s);
typeid.push_str(&s);
}
// Pointer types
ty::Ref(region, ty0, ..) => {
// [U3mut]u3refI<element-type>E as vendor extended type qualifier and type
let mut s = String::new();
s.push_str("u3refI");
s.push_str(&encode_ty(tcx, *ty0, dict, options));
s.push('E');
compress(dict, DictKey::Ty(tcx.mk_imm_ref(*region, *ty0), TyQ::None), &mut s);
if ty.is_mutable_ptr() {
s = format!("{}{}", "U3mut", &s);
compress(dict, DictKey::Ty(ty, TyQ::Mut), &mut s);
}
typeid.push_str(&s);
}
ty::RawPtr(tm) => {
// P[K]<element-type>
let mut s = String::new();
s.push_str(&encode_ty(tcx, tm.ty, dict, options));
if !ty.is_mutable_ptr() {
s = format!("{}{}", "K", &s);
compress(dict, DictKey::Ty(tm.ty, TyQ::Const), &mut s);
};
s = format!("{}{}", "P", &s);
compress(dict, DictKey::Ty(ty, TyQ::None), &mut s);
typeid.push_str(&s);
}
ty::FnPtr(fn_sig) => {
// PF<return-type><parameter-type1..parameter-typeN>E
let mut s = String::from("P");
s.push_str(&encode_fnsig(tcx, &fn_sig.skip_binder(), dict, TypeIdOptions::NO_OPTIONS));
compress(dict, DictKey::Ty(ty, TyQ::None), &mut s);
typeid.push_str(&s);
}
// Trait types
ty::Dynamic(predicates, region) => {
// u3dynI<element-type1[..element-typeN]>E, where <element-type> is <predicate>, as
// vendor extended type.
let mut s = String::from("u3dynI");
s.push_str(&encode_predicates(tcx, predicates, dict, options));
s.push_str(&encode_region(tcx, *region, dict, options));
s.push('E');
compress(dict, DictKey::Ty(ty, TyQ::None), &mut s);
typeid.push_str(&s);
}
// Unexpected types
ty::Bound(..)
| ty::Error(..)
| ty::GeneratorWitness(..)
| ty::Infer(..)
| ty::Opaque(..)
| ty::Param(..)
| ty::Placeholder(..)
| ty::Projection(..) => {
bug!("encode_ty: unexpected `{:?}`", ty.kind());
}
};
typeid
}
// Transforms a ty:Ty for being encoded and used in the substitution dictionary. It transforms all
// c_void types into unit types unconditionally, and generalizes all pointers if
// TransformTyOptions::GENERALIZE_POINTERS option is set.
fn transform_ty<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>, options: TransformTyOptions) -> Ty<'tcx> {
let mut ty = ty;
match ty.kind() {
ty::Bool
| ty::Int(..)
| ty::Uint(..)
| ty::Float(..)
| ty::Char
| ty::Str
| ty::Never
| ty::Foreign(..)
| ty::Dynamic(..) => {}
_ if ty.is_unit() => {}
ty::Tuple(tys) => {
ty = tcx.mk_tup(tys.iter().map(|ty| transform_ty(tcx, ty, options)));
}
ty::Array(ty0, len) => {
let len = len.kind().try_to_scalar().unwrap().to_u64().unwrap();
ty = tcx.mk_array(transform_ty(tcx, *ty0, options), len);
}
ty::Slice(ty0) => {
ty = tcx.mk_slice(transform_ty(tcx, *ty0, options));
}
ty::Adt(adt_def, substs) => {
if is_c_void_ty(tcx, ty) {
ty = tcx.mk_unit();
} else if options.contains(TransformTyOptions::GENERALIZE_REPR_C) && adt_def.repr().c()
{
ty = tcx.mk_adt(*adt_def, ty::List::empty());
} else if adt_def.repr().transparent() && adt_def.is_struct() {
let variant = adt_def.non_enum_variant();
let param_env = tcx.param_env(variant.def_id);
let field = variant.fields.iter().find(|field| {
let ty = tcx.type_of(field.did);
let is_zst =
tcx.layout_of(param_env.and(ty)).map_or(false, |layout| layout.is_zst());
!is_zst
});
if field.is_none() {
// Transform repr(transparent) types without non-ZST field into ()
ty = tcx.mk_unit();
} else {
let ty0 = tcx.type_of(field.unwrap().did);
// Generalize any repr(transparent) user-defined type that is either a pointer
// or reference, and either references itself or any other type that contains or
// references itself, to avoid a reference cycle.
if ty0.is_any_ptr() && ty0.contains(ty) {
ty = transform_ty(
tcx,
ty0,
options | TransformTyOptions::GENERALIZE_POINTERS,
);
} else {
ty = transform_ty(tcx, ty0, options);
}
}
} else {
ty = tcx.mk_adt(*adt_def, transform_substs(tcx, substs, options));
}
}
ty::FnDef(def_id, substs) => {
ty = tcx.mk_fn_def(*def_id, transform_substs(tcx, substs, options));
}
ty::Closure(def_id, substs) => {
ty = tcx.mk_closure(*def_id, transform_substs(tcx, substs, options));
}
ty::Generator(def_id, substs, movability) => {
ty = tcx.mk_generator(*def_id, transform_substs(tcx, substs, options), *movability);
}
ty::Ref(region, ty0, ..) => {
if options.contains(TransformTyOptions::GENERALIZE_POINTERS) {
if ty.is_mutable_ptr() {
ty = tcx.mk_mut_ref(tcx.lifetimes.re_static, tcx.mk_unit());
} else {
ty = tcx.mk_imm_ref(tcx.lifetimes.re_static, tcx.mk_unit());
}
} else {
if ty.is_mutable_ptr() {
ty = tcx.mk_mut_ref(*region, transform_ty(tcx, *ty0, options));
} else {
ty = tcx.mk_imm_ref(*region, transform_ty(tcx, *ty0, options));
}
}
}
ty::RawPtr(tm) => {
if options.contains(TransformTyOptions::GENERALIZE_POINTERS) {
if ty.is_mutable_ptr() {
ty = tcx.mk_mut_ptr(tcx.mk_unit());
} else {
ty = tcx.mk_imm_ptr(tcx.mk_unit());
}
} else {
if ty.is_mutable_ptr() {
ty = tcx.mk_mut_ptr(transform_ty(tcx, tm.ty, options));
} else {
ty = tcx.mk_imm_ptr(transform_ty(tcx, tm.ty, options));
}
}
}
ty::FnPtr(fn_sig) => {
if options.contains(TransformTyOptions::GENERALIZE_POINTERS) {
ty = tcx.mk_imm_ptr(tcx.mk_unit());
} else {
let parameters: Vec<Ty<'tcx>> = fn_sig
.skip_binder()
.inputs()
.iter()
.map(|ty| transform_ty(tcx, *ty, options))
.collect();
let output = transform_ty(tcx, fn_sig.skip_binder().output(), options);
ty = tcx.mk_fn_ptr(ty::Binder::bind_with_vars(
tcx.mk_fn_sig(
parameters.iter(),
&output,
fn_sig.c_variadic(),
fn_sig.unsafety(),
fn_sig.abi(),
),
fn_sig.bound_vars(),
));
}
}
ty::Bound(..)
| ty::Error(..)
| ty::GeneratorWitness(..)
| ty::Infer(..)
| ty::Opaque(..)
| ty::Param(..)
| ty::Placeholder(..)
| ty::Projection(..) => {
bug!("transform_ty: unexpected `{:?}`", ty.kind());
}
}
ty
}
/// Transforms substs for being encoded and used in the substitution dictionary.
fn transform_substs<'tcx>(
tcx: TyCtxt<'tcx>,
substs: SubstsRef<'tcx>,
options: TransformTyOptions,
) -> SubstsRef<'tcx> {
let substs: Vec<GenericArg<'tcx>> = substs
.iter()
.map(|subst| {
if let GenericArgKind::Type(ty) = subst.unpack() {
if is_c_void_ty(tcx, ty) {
tcx.mk_unit().into()
} else {
transform_ty(tcx, ty, options).into()
}
} else {
subst
}
})
.collect();
tcx.mk_substs(substs.iter())
}
/// Returns a type metadata identifier for the specified FnAbi using the Itanium C++ ABI with vendor
/// extended type qualifiers and types for Rust types that are not used at the FFI boundary.
pub fn typeid_for_fnabi<'tcx>(
tcx: TyCtxt<'tcx>,
fn_abi: &FnAbi<'tcx, Ty<'tcx>>,
options: TypeIdOptions,
) -> String {
// A name is mangled by prefixing "_Z" to an encoding of its name, and in the case of functions
// its type.
let mut typeid = String::from("_Z");
// Clang uses the Itanium C++ ABI's virtual tables and RTTI typeinfo structure name as type
// metadata identifiers for function pointers. The typeinfo name encoding is a two-character
// code (i.e., 'TS') prefixed to the type encoding for the function.
typeid.push_str("TS");
// Function types are delimited by an "F..E" pair
typeid.push('F');
// A dictionary of substitution candidates used for compression (see
// https://itanium-cxx-abi.github.io/cxx-abi/abi.html#mangling-compression).
let mut dict: FxHashMap<DictKey<'tcx>, usize> = FxHashMap::default();
let mut encode_ty_options = EncodeTyOptions::from_bits(options.bits())
.unwrap_or_else(|| bug!("typeid_for_fnabi: invalid option(s) `{:?}`", options.bits()));
match fn_abi.conv {
Conv::C => {
encode_ty_options.insert(EncodeTyOptions::GENERALIZE_REPR_C);
}
_ => {
encode_ty_options.remove(EncodeTyOptions::GENERALIZE_REPR_C);
}
}
// Encode the return type
let transform_ty_options = TransformTyOptions::from_bits(options.bits())
.unwrap_or_else(|| bug!("typeid_for_fnabi: invalid option(s) `{:?}`", options.bits()));
let ty = transform_ty(tcx, fn_abi.ret.layout.ty, transform_ty_options);
typeid.push_str(&encode_ty(tcx, ty, &mut dict, encode_ty_options));
// Encode the parameter types
if !fn_abi.c_variadic {
if !fn_abi.args.is_empty() {
for arg in fn_abi.args.iter() {
let ty = transform_ty(tcx, arg.layout.ty, transform_ty_options);
typeid.push_str(&encode_ty(tcx, ty, &mut dict, encode_ty_options));
}
} else {
// Empty parameter lists, whether declared as () or conventionally as (void), are
// encoded with a void parameter specifier "v".
typeid.push('v');
}
} else {
for n in 0..fn_abi.fixed_count {
let ty = transform_ty(tcx, fn_abi.args[n].layout.ty, transform_ty_options);
typeid.push_str(&encode_ty(tcx, ty, &mut dict, encode_ty_options));
}
typeid.push('z');
}
// Close the "F..E" pair
typeid.push('E');
typeid
}
/// Returns a type metadata identifier for the specified FnSig using the Itanium C++ ABI with vendor
/// extended type qualifiers and types for Rust types that are not used at the FFI boundary.
pub fn typeid_for_fnsig<'tcx>(
tcx: TyCtxt<'tcx>,
fn_sig: &FnSig<'tcx>,
options: TypeIdOptions,
) -> String {
// A name is mangled by prefixing "_Z" to an encoding of its name, and in the case of functions
// its type.
let mut typeid = String::from("_Z");
// Clang uses the Itanium C++ ABI's virtual tables and RTTI typeinfo structure name as type
// metadata identifiers for function pointers. The typeinfo name encoding is a two-character
// code (i.e., 'TS') prefixed to the type encoding for the function.
typeid.push_str("TS");
// A dictionary of substitution candidates used for compression (see
// https://itanium-cxx-abi.github.io/cxx-abi/abi.html#mangling-compression).
let mut dict: FxHashMap<DictKey<'tcx>, usize> = FxHashMap::default();
// Encode the function signature
typeid.push_str(&encode_fnsig(tcx, fn_sig, &mut dict, options));
typeid
}

View File

@ -12,7 +12,6 @@ use rustc_middle::ty::{
self, EarlyBinder, FloatTy, Instance, IntTy, Ty, TyCtxt, TypeVisitable, UintTy,
};
use rustc_span::symbol::kw;
use rustc_target::abi::call::FnAbi;
use rustc_target::abi::Integer;
use rustc_target::spec::abi::Abi;
@ -59,41 +58,6 @@ pub(super) fn mangle<'tcx>(
std::mem::take(&mut cx.out)
}
pub(super) fn mangle_typeid_for_fnabi<'tcx>(
_tcx: TyCtxt<'tcx>,
fn_abi: &FnAbi<'tcx, Ty<'tcx>>,
) -> String {
// LLVM uses type metadata to allow IR modules to aggregate pointers by their types.[1] This
// type metadata is used by LLVM Control Flow Integrity to test whether a given pointer is
// associated with a type identifier (i.e., test type membership).
//
// Clang uses the Itanium C++ ABI's[2] virtual tables and RTTI typeinfo structure name[3] as
// type metadata identifiers for function pointers. The typeinfo name encoding is a
// two-character code (i.e., “TS”) prefixed to the type encoding for the function.
//
// For cross-language LLVM CFI support, a compatible encoding must be used by either
//
// a. Using a superset of types that encompasses types used by Clang (i.e., Itanium C++ ABI's
// type encodings[4]), or at least types used at the FFI boundary.
// b. Reducing the types to the least common denominator between types used by Clang (or at
// least types used at the FFI boundary) and Rust compilers (if even possible).
// c. Creating a new ABI for cross-language CFI and using it for Clang and Rust compilers (and
// possibly other compilers).
//
// Option (b) may weaken the protection for Rust-compiled only code, so it should be provided
// as an alternative to a Rust-specific encoding for when mixing Rust and C and C++ -compiled
// code. Option (c) would require changes to Clang to use the new ABI.
//
// [1] https://llvm.org/docs/TypeMetadata.html
// [2] https://itanium-cxx-abi.github.io/cxx-abi/abi.html
// [3] https://itanium-cxx-abi.github.io/cxx-abi/abi.html#mangling-special-vtables
// [4] https://itanium-cxx-abi.github.io/cxx-abi/abi.html#mangling-type
//
// FIXME(rcvalle): See comment above.
let arg_count = fn_abi.args.len() + fn_abi.ret.is_indirect() as usize;
format!("typeid{}", arg_count)
}
pub(super) fn mangle_typeid_for_trait_ref<'tcx>(
tcx: TyCtxt<'tcx>,
trait_ref: ty::PolyExistentialTraitRef<'tcx>,

View File

@ -193,7 +193,8 @@ Shadow byte legend (one shadow byte represents 8 application bytes):
The LLVM Control Flow Integrity (CFI) support in the Rust compiler initially
provides forward-edge control flow protection for Rust-compiled code only by
aggregating function pointers in groups identified by their number of arguments.
aggregating function pointers in groups identified by their return and parameter
types.
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
@ -245,7 +246,7 @@ fn do_twice(f: fn(i32) -> i32, arg: i32) -> i32 {
fn main() {
let answer = do_twice(add_one, 5);
println!("The answer is: {answer}");
println!("The answer is: {}", answer);
println!("With CFI enabled, you should not see the next answer");
let f: fn(i32) -> i32 = unsafe {
@ -255,18 +256,18 @@ fn main() {
};
let next_answer = do_twice(f, 5);
println!("The next answer is: {next_answer}");
println!("The next answer is: {}", next_answer);
}
```
Fig. 1.Modified example from the [Advanced Functions and
Closures][rust-book-ch19-05] chapter of the [The Rust Programming
Language][rust-book] book.
[//]: # (FIXME: Replace with output from cargo using nightly when #89652 is merged)
```shell
$ rustc rust_cfi.rs -o rust_cfi
$ ./rust_cfi
$ cargo run --release
Compiling rust-cfi-1 v0.1.0 (/home/rcvalle/rust-cfi-1)
Finished release [optimized] target(s) in 0.76s
Running `target/release/rust-cfi-1`
The answer is: 12
With CFI enabled, you should not see the next answer
The next answer is: 14
@ -274,11 +275,11 @@ $
```
Fig. 2.Build and execution of the modified example with LLVM CFI disabled.
[//]: # (FIXME: Replace with output from cargo using nightly when #89652 is merged)
```shell
$ rustc -Clto -Zsanitizer=cfi rust_cfi.rs -o rust_cfi
$ ./rust_cfi
$ RUSTFLAGS="-Zsanitizer=cfi -Cembed-bitcode=yes -Clto" cargo run --release
Compiling rust-cfi-1 v0.1.0 (/home/rcvalle/rust-cfi-1)
Finished release [optimized] target(s) in 3.39s
Running `target/release/rust-cfi-1`
The answer is: 12
With CFI enabled, you should not see the next answer
Illegal instruction
@ -308,25 +309,25 @@ fn do_twice(f: fn(i32) -> i32, arg: i32) -> i32 {
fn main() {
let answer = do_twice(add_one, 5);
println!("The answer is: {answer}");
println!("The answer is: {}", answer);
println!("With CFI enabled, you should not see the next answer");
let f: fn(i32) -> i32 =
unsafe { mem::transmute::<*const u8, fn(i32) -> i32>(add_two as *const u8) };
let next_answer = do_twice(f, 5);
println!("The next answer is: {next_answer}");
println!("The next answer is: {}", next_answer);
}
```
Fig. 4.Another modified example from the [Advanced Functions and
Closures][rust-book-ch19-05] chapter of the [The Rust Programming
Language][rust-book] book.
[//]: # (FIXME: Replace with output from cargo using nightly when #89652 is merged)
```shell
$ rustc rust_cfi.rs -o rust_cfi
$ ./rust_cfi
$ cargo run --release
Compiling rust-cfi-2 v0.1.0 (/home/rcvalle/rust-cfi-2)
Finished release [optimized] target(s) in 0.76s
Running `target/release/rust-cfi-2`
The answer is: 12
With CFI enabled, you should not see the next answer
The next answer is: 14
@ -334,11 +335,11 @@ $
```
Fig. 5.Build and execution of the modified example with LLVM CFI disabled.
[//]: # (FIXME: Replace with output from cargo using nightly when #89652 is merged)
```shell
$ rustc -Clto -Zsanitizer=cfi rust_cfi.rs -o rust_cfi
$ ./rust_cfi
$ RUSTFLAGS="-Zsanitizer=cfi -Cembed-bitcode=yes -Clto" cargo run --release
Compiling rust-cfi-2 v0.1.0 (/home/rcvalle/rust-cfi-2)
Finished release [optimized] target(s) in 3.38s
Running `target/release/rust-cfi-2`
The answer is: 12
With CFI enabled, you should not see the next answer
Illegal instruction
@ -348,14 +349,69 @@ Fig. 6.Build and execution of the modified example with LLVM CFI enabled.
When LLVM CFI is enabled, if there are any attempts to change/hijack control
flow using an indirect branch/call to a function with different number of
arguments than intended/passed in the call/branch site, the execution is also
terminated (see Fig. 6).
parameters than arguments intended/passed in the call/branch site, the
execution is also terminated (see Fig. 6).
Forward-edge control flow protection not only by aggregating function pointers
in groups identified by their number of arguments, but also their argument
types, will also be provided in later work by defining and using compatible type
identifiers (see Type metadata in the design document in the tracking
issue [#89653](https://github.com/rust-lang/rust/issues/89653)).
```rust
use std::mem;
fn add_one(x: i32) -> i32 {
x + 1
}
fn add_two(x: i64) -> i64 {
x + 2
}
fn do_twice(f: fn(i32) -> i32, arg: i32) -> i32 {
f(arg) + f(arg)
}
fn main() {
let answer = do_twice(add_one, 5);
println!("The answer is: {}", answer);
println!("With CFI enabled, you should not see the next answer");
let f: fn(i32) -> i32 =
unsafe { mem::transmute::<*const u8, fn(i32) -> i32>(add_two as *const u8) };
let next_answer = do_twice(f, 5);
println!("The next answer is: {}", next_answer);
}
```
Fig. 7.Another modified example from the [Advanced Functions and
Closures][rust-book-ch19-05] chapter of the [The Rust Programming
Language][rust-book] book.
```shell
cargo run --release
Compiling rust-cfi-3 v0.1.0 (/home/rcvalle/rust-cfi-3)
Finished release [optimized] target(s) in 0.74s
Running `target/release/rust-cfi-3`
The answer is: 12
With CFI enabled, you should not see the next answer
The next answer is: 14
$
```
Fig. 8.Build and execution of the modified example with LLVM CFI disabled.
```shell
$ RUSTFLAGS="-Zsanitizer=cfi -Cembed-bitcode=yes -Clto" cargo run --release
Compiling rust-cfi-3 v0.1.0 (/home/rcvalle/rust-cfi-3)
Finished release [optimized] target(s) in 3.40s
Running `target/release/rust-cfi-3`
The answer is: 12
With CFI enabled, you should not see the next answer
Illegal instruction
$
```
Fig. 9.Build and execution of the modified example with LLVM CFI enabled.
When LLVM CFI is enabled, if there are any attempts to change/hijack control
flow using an indirect branch/call to a function with different return and
parameter types than the return type expected and arguments intended/passed in
the call/branch site, the execution is also terminated (see Fig. 9).
[rust-book-ch19-05]: https://doc.rust-lang.org/book/ch19-05-advanced-functions-and-closures.html
[rust-book]: https://doc.rust-lang.org/book/title-page.html

View File

@ -1,10 +1,7 @@
// Verifies that "CFI Canonical Jump Tables" module flag is added.
//
// ignore-windows
// needs-sanitizer-cfi
// only-aarch64
// only-x86_64
// compile-flags: -Clto -Zsanitizer=cfi
// compile-flags: -Clto -Ctarget-feature=-crt-static -Zsanitizer=cfi
#![crate_type="lib"]

View File

@ -1,10 +1,7 @@
// Verifies that pointer type membership tests for indirect calls are emitted.
//
// ignore-windows
// needs-sanitizer-cfi
// only-aarch64
// only-x86_64
// compile-flags: -Clto -Cno-prepopulate-passes -Zsanitizer=cfi
// compile-flags: -Clto -Cno-prepopulate-passes -Ctarget-feature=-crt-static -Zsanitizer=cfi
#![crate_type="lib"]

View File

@ -0,0 +1,575 @@
// Verifies that type metadata identifiers for functions are emitted correctly.
//
// needs-sanitizer-cfi
// compile-flags: -Clto -Cno-prepopulate-passes -Ctarget-feature=-crt-static -Zsanitizer=cfi
#![crate_type="lib"]
#![allow(dead_code)]
#![allow(incomplete_features)]
#![allow(unused_must_use)]
#![feature(adt_const_params, extern_types, inline_const, type_alias_impl_trait)]
extern crate core;
use core::ffi::c_void;
use std::marker::PhantomData;
// User-defined type (structure)
pub struct Struct1<T> {
member1: T,
}
// User-defined type (enum)
pub enum Enum1<T> {
Variant1(T),
}
// User-defined type (union)
pub union Union1<T> {
member1: std::mem::ManuallyDrop<T>,
}
// Extern type
extern {
pub type type1;
}
// Trait
pub trait Trait1<T> {
fn foo(&self) { }
}
// Trait implementation
impl<T> Trait1<T> for i32 {
fn foo(&self) { }
}
// Trait implementation
impl<T> Trait1<T> for Struct1<T> {
fn foo(&self) { }
}
// impl Trait type aliases for helping with defining other types (see below)
pub type Type1 = impl Send;
pub type Type2 = impl Send;
pub type Type3 = impl Send;
pub type Type4 = impl Send;
pub type Type5 = impl Send;
pub type Type6 = impl Send;
pub type Type7 = impl Send;
pub type Type8 = impl Send;
pub type Type9 = impl Send;
pub type Type10 = impl Send;
pub type Type11 = impl Send;
pub fn fn1<'a>() {
// Closure
let closure1 = || { };
let _: Type1 = closure1;
// Constructor
pub struct Foo(i32);
let _: Type2 = Foo;
// Type in extern path
extern {
fn foo();
}
let _: Type3 = foo;
// Type in closure path
|| {
pub struct Foo;
let _: Type4 = Foo;
};
// Type in const path
const {
pub struct Foo;
fn foo() -> Type5 { Foo }
};
// Type in impl path
impl<T> Struct1<T> {
fn foo(&self) { }
}
let _: Type6 = <Struct1<i32>>::foo;
// Trait method
let _: Type7 = <dyn Trait1<i32>>::foo;
// Trait method
let _: Type8 = <i32 as Trait1<i32>>::foo;
// Trait method
let _: Type9 = <Struct1<i32> as Trait1<i32>>::foo;
// Const generics
pub struct Qux<T, const N: usize>([T; N]);
let _: Type10 = Qux([0; 32]);
// Lifetimes/regions
pub struct Quux<'a>(&'a i32);
pub struct Quuux<'a, 'b>(&'a i32, &'b Quux<'b>);
let _: Type11 = Quuux;
}
// repr(transparent) user-defined type
struct Foo(i32);
#[repr(transparent)]
pub struct Type12 {
member1: (),
member2: PhantomData<i32>,
member3: Foo,
}
// Self-referencing repr(transparent) user-defined type
#[repr(transparent)]
pub struct Type13<'a> {
member1: (),
member2: PhantomData<i32>,
member3: &'a Type13<'a>,
}
pub fn foo0(_: ()) { }
// CHECK: define{{.*}}foo0{{.*}}!type ![[TYPE0:[0-9]+]]
pub fn foo1(_: c_void, _: ()) { }
// CHECK: define{{.*}}foo1{{.*}}!type ![[TYPE1:[0-9]+]]
pub fn foo2(_: (), _: c_void, _: c_void) { }
// CHECK: define{{.*}}foo2{{.*}}!type ![[TYPE2:[0-9]+]]
pub fn foo3(_: *mut c_void) { }
// CHECK: define{{.*}}foo3{{.*}}!type ![[TYPE3:[0-9]+]]
pub fn foo4(_: *mut c_void, _: *mut ()) { }
// CHECK: define{{.*}}foo4{{.*}}!type ![[TYPE4:[0-9]+]]
pub fn foo5(_: *mut (), _: *mut c_void, _: *mut c_void) { }
// CHECK: define{{.*}}foo5{{.*}}!type ![[TYPE5:[0-9]+]]
pub fn foo6(_: *const c_void) { }
// CHECK: define{{.*}}foo6{{.*}}!type ![[TYPE6:[0-9]+]]
pub fn foo7(_: *const c_void, _: *const ()) { }
// CHECK: define{{.*}}foo7{{.*}}!type ![[TYPE7:[0-9]+]]
pub fn foo8(_: *const (), _: *const c_void, _: *const c_void) { }
// CHECK: define{{.*}}foo8{{.*}}!type ![[TYPE8:[0-9]+]]
pub fn foo9(_: bool) { }
// CHECK: define{{.*}}foo9{{.*}}!type ![[TYPE9:[0-9]+]]
pub fn foo10(_: bool, _: bool) { }
// CHECK: define{{.*}}foo10{{.*}}!type ![[TYPE10:[0-9]+]]
pub fn foo11(_: bool, _: bool, _: bool) { }
// CHECK: define{{.*}}foo11{{.*}}!type ![[TYPE11:[0-9]+]]
pub fn foo12(_: i8) { }
// CHECK: define{{.*}}foo12{{.*}}!type ![[TYPE12:[0-9]+]]
pub fn foo13(_: i8, _: i8) { }
// CHECK: define{{.*}}foo13{{.*}}!type ![[TYPE13:[0-9]+]]
pub fn foo14(_: i8, _: i8, _: i8) { }
// CHECK: define{{.*}}foo14{{.*}}!type ![[TYPE14:[0-9]+]]
pub fn foo15(_: i16) { }
// CHECK: define{{.*}}foo15{{.*}}!type ![[TYPE15:[0-9]+]]
pub fn foo16(_: i16, _: i16) { }
// CHECK: define{{.*}}foo16{{.*}}!type ![[TYPE16:[0-9]+]]
pub fn foo17(_: i16, _: i16, _: i16) { }
// CHECK: define{{.*}}foo17{{.*}}!type ![[TYPE17:[0-9]+]]
pub fn foo18(_: i32) { }
// CHECK: define{{.*}}foo18{{.*}}!type ![[TYPE18:[0-9]+]]
pub fn foo19(_: i32, _: i32) { }
// CHECK: define{{.*}}foo19{{.*}}!type ![[TYPE19:[0-9]+]]
pub fn foo20(_: i32, _: i32, _: i32) { }
// CHECK: define{{.*}}foo20{{.*}}!type ![[TYPE20:[0-9]+]]
pub fn foo21(_: i64) { }
// CHECK: define{{.*}}foo21{{.*}}!type ![[TYPE21:[0-9]+]]
pub fn foo22(_: i64, _: i64) { }
// CHECK: define{{.*}}foo22{{.*}}!type ![[TYPE22:[0-9]+]]
pub fn foo23(_: i64, _: i64, _: i64) { }
// CHECK: define{{.*}}foo23{{.*}}!type ![[TYPE23:[0-9]+]]
pub fn foo24(_: i128) { }
// CHECK: define{{.*}}foo24{{.*}}!type ![[TYPE24:[0-9]+]]
pub fn foo25(_: i128, _: i128) { }
// CHECK: define{{.*}}foo25{{.*}}!type ![[TYPE25:[0-9]+]]
pub fn foo26(_: i128, _: i128, _: i128) { }
// CHECK: define{{.*}}foo26{{.*}}!type ![[TYPE26:[0-9]+]]
pub fn foo27(_: isize) { }
// CHECK: define{{.*}}foo27{{.*}}!type ![[TYPE27:[0-9]+]]
pub fn foo28(_: isize, _: isize) { }
// CHECK: define{{.*}}foo28{{.*}}!type ![[TYPE28:[0-9]+]]
pub fn foo29(_: isize, _: isize, _: isize) { }
// CHECK: define{{.*}}foo29{{.*}}!type ![[TYPE29:[0-9]+]]
pub fn foo30(_: u8) { }
// CHECK: define{{.*}}foo30{{.*}}!type ![[TYPE30:[0-9]+]]
pub fn foo31(_: u8, _: u8) { }
// CHECK: define{{.*}}foo31{{.*}}!type ![[TYPE31:[0-9]+]]
pub fn foo32(_: u8, _: u8, _: u8) { }
// CHECK: define{{.*}}foo32{{.*}}!type ![[TYPE32:[0-9]+]]
pub fn foo33(_: u16) { }
// CHECK: define{{.*}}foo33{{.*}}!type ![[TYPE33:[0-9]+]]
pub fn foo34(_: u16, _: u16) { }
// CHECK: define{{.*}}foo34{{.*}}!type ![[TYPE34:[0-9]+]]
pub fn foo35(_: u16, _: u16, _: u16) { }
// CHECK: define{{.*}}foo35{{.*}}!type ![[TYPE35:[0-9]+]]
pub fn foo36(_: u32) { }
// CHECK: define{{.*}}foo36{{.*}}!type ![[TYPE36:[0-9]+]]
pub fn foo37(_: u32, _: u32) { }
// CHECK: define{{.*}}foo37{{.*}}!type ![[TYPE37:[0-9]+]]
pub fn foo38(_: u32, _: u32, _: u32) { }
// CHECK: define{{.*}}foo38{{.*}}!type ![[TYPE38:[0-9]+]]
pub fn foo39(_: u64) { }
// CHECK: define{{.*}}foo39{{.*}}!type ![[TYPE39:[0-9]+]]
pub fn foo40(_: u64, _: u64) { }
// CHECK: define{{.*}}foo40{{.*}}!type ![[TYPE40:[0-9]+]]
pub fn foo41(_: u64, _: u64, _: u64) { }
// CHECK: define{{.*}}foo41{{.*}}!type ![[TYPE41:[0-9]+]]
pub fn foo42(_: u128) { }
// CHECK: define{{.*}}foo42{{.*}}!type ![[TYPE42:[0-9]+]]
pub fn foo43(_: u128, _: u128) { }
// CHECK: define{{.*}}foo43{{.*}}!type ![[TYPE43:[0-9]+]]
pub fn foo44(_: u128, _: u128, _: u128) { }
// CHECK: define{{.*}}foo44{{.*}}!type ![[TYPE44:[0-9]+]]
pub fn foo45(_: usize) { }
// CHECK: define{{.*}}foo45{{.*}}!type ![[TYPE45:[0-9]+]]
pub fn foo46(_: usize, _: usize) { }
// CHECK: define{{.*}}foo46{{.*}}!type ![[TYPE46:[0-9]+]]
pub fn foo47(_: usize, _: usize, _: usize) { }
// CHECK: define{{.*}}foo47{{.*}}!type ![[TYPE47:[0-9]+]]
pub fn foo48(_: f32) { }
// CHECK: define{{.*}}foo48{{.*}}!type ![[TYPE48:[0-9]+]]
pub fn foo49(_: f32, _: f32) { }
// CHECK: define{{.*}}foo49{{.*}}!type ![[TYPE49:[0-9]+]]
pub fn foo50(_: f32, _: f32, _: f32) { }
// CHECK: define{{.*}}foo50{{.*}}!type ![[TYPE50:[0-9]+]]
pub fn foo51(_: f64) { }
// CHECK: define{{.*}}foo51{{.*}}!type ![[TYPE51:[0-9]+]]
pub fn foo52(_: f64, _: f64) { }
// CHECK: define{{.*}}foo52{{.*}}!type ![[TYPE52:[0-9]+]]
pub fn foo53(_: f64, _: f64, _: f64) { }
// CHECK: define{{.*}}foo53{{.*}}!type ![[TYPE53:[0-9]+]]
pub fn foo54(_: char) { }
// CHECK: define{{.*}}foo54{{.*}}!type ![[TYPE54:[0-9]+]]
pub fn foo55(_: char, _: char) { }
// CHECK: define{{.*}}foo55{{.*}}!type ![[TYPE55:[0-9]+]]
pub fn foo56(_: char, _: char, _: char) { }
// CHECK: define{{.*}}foo56{{.*}}!type ![[TYPE56:[0-9]+]]
pub fn foo57(_: &str) { }
// CHECK: define{{.*}}foo57{{.*}}!type ![[TYPE57:[0-9]+]]
pub fn foo58(_: &str, _: &str) { }
// CHECK: define{{.*}}foo58{{.*}}!type ![[TYPE58:[0-9]+]]
pub fn foo59(_: &str, _: &str, _: &str) { }
// CHECK: define{{.*}}foo59{{.*}}!type ![[TYPE59:[0-9]+]]
pub fn foo60(_: (i32, i32)) { }
// CHECK: define{{.*}}foo60{{.*}}!type ![[TYPE60:[0-9]+]]
pub fn foo61(_: (i32, i32), _: (i32, i32)) { }
// CHECK: define{{.*}}foo61{{.*}}!type ![[TYPE61:[0-9]+]]
pub fn foo62(_: (i32, i32), _: (i32, i32), _: (i32, i32)) { }
// CHECK: define{{.*}}foo62{{.*}}!type ![[TYPE62:[0-9]+]]
pub fn foo63(_: [i32; 32]) { }
// CHECK: define{{.*}}foo63{{.*}}!type ![[TYPE63:[0-9]+]]
pub fn foo64(_: [i32; 32], _: [i32; 32]) { }
// CHECK: define{{.*}}foo64{{.*}}!type ![[TYPE64:[0-9]+]]
pub fn foo65(_: [i32; 32], _: [i32; 32], _: [i32; 32]) { }
// CHECK: define{{.*}}foo65{{.*}}!type ![[TYPE65:[0-9]+]]
pub fn foo66(_: &[i32]) { }
// CHECK: define{{.*}}foo66{{.*}}!type ![[TYPE66:[0-9]+]]
pub fn foo67(_: &[i32], _: &[i32]) { }
// CHECK: define{{.*}}foo67{{.*}}!type ![[TYPE67:[0-9]+]]
pub fn foo68(_: &[i32], _: &[i32], _: &[i32]) { }
// CHECK: define{{.*}}foo68{{.*}}!type ![[TYPE68:[0-9]+]]
pub fn foo69(_: &Struct1::<i32>) { }
// CHECK: define{{.*}}foo69{{.*}}!type ![[TYPE69:[0-9]+]]
pub fn foo70(_: &Struct1::<i32>, _: &Struct1::<i32>) { }
// CHECK: define{{.*}}foo70{{.*}}!type ![[TYPE70:[0-9]+]]
pub fn foo71(_: &Struct1::<i32>, _: &Struct1::<i32>, _: &Struct1::<i32>) { }
// CHECK: define{{.*}}foo71{{.*}}!type ![[TYPE71:[0-9]+]]
pub fn foo72(_: &Enum1::<i32>) { }
// CHECK: define{{.*}}foo72{{.*}}!type ![[TYPE72:[0-9]+]]
pub fn foo73(_: &Enum1::<i32>, _: &Enum1::<i32>) { }
// CHECK: define{{.*}}foo73{{.*}}!type ![[TYPE73:[0-9]+]]
pub fn foo74(_: &Enum1::<i32>, _: &Enum1::<i32>, _: &Enum1::<i32>) { }
// CHECK: define{{.*}}foo74{{.*}}!type ![[TYPE74:[0-9]+]]
pub fn foo75(_: &Union1::<i32>) { }
// CHECK: define{{.*}}foo75{{.*}}!type ![[TYPE75:[0-9]+]]
pub fn foo76(_: &Union1::<i32>, _: &Union1::<i32>) { }
// CHECK: define{{.*}}foo76{{.*}}!type ![[TYPE76:[0-9]+]]
pub fn foo77(_: &Union1::<i32>, _: &Union1::<i32>, _: &Union1::<i32>) { }
// CHECK: define{{.*}}foo77{{.*}}!type ![[TYPE77:[0-9]+]]
pub fn foo78(_: *mut type1) { }
// CHECK: define{{.*}}foo78{{.*}}!type ![[TYPE78:[0-9]+]]
pub fn foo79(_: *mut type1, _: *mut type1) { }
// CHECK: define{{.*}}foo79{{.*}}!type ![[TYPE79:[0-9]+]]
pub fn foo80(_: *mut type1, _: *mut type1, _: *mut type1) { }
// CHECK: define{{.*}}foo80{{.*}}!type ![[TYPE80:[0-9]+]]
pub fn foo81(_: &mut i32) { }
// CHECK: define{{.*}}foo81{{.*}}!type ![[TYPE81:[0-9]+]]
pub fn foo82(_: &mut i32, _: &i32) { }
// CHECK: define{{.*}}foo82{{.*}}!type ![[TYPE82:[0-9]+]]
pub fn foo83(_: &mut i32, _: &i32, _: &i32) { }
// CHECK: define{{.*}}foo83{{.*}}!type ![[TYPE83:[0-9]+]]
pub fn foo84(_: &i32) { }
// CHECK: define{{.*}}foo84{{.*}}!type ![[TYPE84:[0-9]+]]
pub fn foo85(_: &i32, _: &mut i32) { }
// CHECK: define{{.*}}foo85{{.*}}!type ![[TYPE85:[0-9]+]]
pub fn foo86(_: &i32, _: &mut i32, _: &mut i32) { }
// CHECK: define{{.*}}foo86{{.*}}!type ![[TYPE86:[0-9]+]]
pub fn foo87(_: *mut i32) { }
// CHECK: define{{.*}}foo87{{.*}}!type ![[TYPE87:[0-9]+]]
pub fn foo88(_: *mut i32, _: *const i32) { }
// CHECK: define{{.*}}foo88{{.*}}!type ![[TYPE88:[0-9]+]]
pub fn foo89(_: *mut i32, _: *const i32, _: *const i32) { }
// CHECK: define{{.*}}foo89{{.*}}!type ![[TYPE89:[0-9]+]]
pub fn foo90(_: *const i32) { }
// CHECK: define{{.*}}foo90{{.*}}!type ![[TYPE90:[0-9]+]]
pub fn foo91(_: *const i32, _: *mut i32) { }
// CHECK: define{{.*}}foo91{{.*}}!type ![[TYPE91:[0-9]+]]
pub fn foo92(_: *const i32, _: *mut i32, _: *mut i32) { }
// CHECK: define{{.*}}foo92{{.*}}!type ![[TYPE92:[0-9]+]]
pub fn foo93(_: fn(i32) -> i32) { }
// CHECK: define{{.*}}foo93{{.*}}!type ![[TYPE93:[0-9]+]]
pub fn foo94(_: fn(i32) -> i32, _: fn(i32) -> i32) { }
// CHECK: define{{.*}}foo94{{.*}}!type ![[TYPE94:[0-9]+]]
pub fn foo95(_: fn(i32) -> i32, _: fn(i32) -> i32, _: fn(i32) -> i32) { }
// CHECK: define{{.*}}foo95{{.*}}!type ![[TYPE95:[0-9]+]]
pub fn foo96(_: &dyn Fn(i32) -> i32) { }
// CHECK: define{{.*}}foo96{{.*}}!type ![[TYPE96:[0-9]+]]
pub fn foo97(_: &dyn Fn(i32) -> i32, _: &dyn Fn(i32) -> i32) { }
// CHECK: define{{.*}}foo97{{.*}}!type ![[TYPE97:[0-9]+]]
pub fn foo98(_: &dyn Fn(i32) -> i32, _: &dyn Fn(i32) -> i32, _: &dyn Fn(i32) -> i32) { }
// CHECK: define{{.*}}foo98{{.*}}!type ![[TYPE98:[0-9]+]]
pub fn foo99(_: &dyn FnMut(i32) -> i32) { }
// CHECK: define{{.*}}foo99{{.*}}!type ![[TYPE99:[0-9]+]]
pub fn foo100(_: &dyn FnMut(i32) -> i32, _: &dyn FnMut(i32) -> i32) { }
// CHECK: define{{.*}}foo100{{.*}}!type ![[TYPE100:[0-9]+]]
pub fn foo101(_: &dyn FnMut(i32) -> i32, _: &dyn FnMut(i32) -> i32, _: &dyn FnMut(i32) -> i32) { }
// CHECK: define{{.*}}foo101{{.*}}!type ![[TYPE101:[0-9]+]]
pub fn foo102(_: &dyn FnOnce(i32) -> i32) { }
// CHECK: define{{.*}}foo102{{.*}}!type ![[TYPE102:[0-9]+]]
pub fn foo103(_: &dyn FnOnce(i32) -> i32, _: &dyn FnOnce(i32) -> i32) { }
// CHECK: define{{.*}}foo103{{.*}}!type ![[TYPE103:[0-9]+]]
pub fn foo104(_: &dyn FnOnce(i32) -> i32, _: &dyn FnOnce(i32) -> i32, _: &dyn FnOnce(i32) -> i32) {}
// CHECK: define{{.*}}foo104{{.*}}!type ![[TYPE104:[0-9]+]]
pub fn foo105(_: &dyn Send) { }
// CHECK: define{{.*}}foo105{{.*}}!type ![[TYPE105:[0-9]+]]
pub fn foo106(_: &dyn Send, _: &dyn Send) { }
// CHECK: define{{.*}}foo106{{.*}}!type ![[TYPE106:[0-9]+]]
pub fn foo107(_: &dyn Send, _: &dyn Send, _: &dyn Send) { }
// CHECK: define{{.*}}foo107{{.*}}!type ![[TYPE107:[0-9]+]]
pub fn foo108(_: Type1) { }
// CHECK: define{{.*}}foo108{{.*}}!type ![[TYPE108:[0-9]+]]
pub fn foo109(_: Type1, _: Type1) { }
// CHECK: define{{.*}}foo109{{.*}}!type ![[TYPE109:[0-9]+]]
pub fn foo110(_: Type1, _: Type1, _: Type1) { }
// CHECK: define{{.*}}foo110{{.*}}!type ![[TYPE110:[0-9]+]]
pub fn foo111(_: Type2) { }
// CHECK: define{{.*}}foo111{{.*}}!type ![[TYPE111:[0-9]+]]
pub fn foo112(_: Type2, _: Type2) { }
// CHECK: define{{.*}}foo112{{.*}}!type ![[TYPE112:[0-9]+]]
pub fn foo113(_: Type2, _: Type2, _: Type2) { }
// CHECK: define{{.*}}foo113{{.*}}!type ![[TYPE113:[0-9]+]]
pub fn foo114(_: Type3) { }
// CHECK: define{{.*}}foo114{{.*}}!type ![[TYPE114:[0-9]+]]
pub fn foo115(_: Type3, _: Type3) { }
// CHECK: define{{.*}}foo115{{.*}}!type ![[TYPE115:[0-9]+]]
pub fn foo116(_: Type3, _: Type3, _: Type3) { }
// CHECK: define{{.*}}foo116{{.*}}!type ![[TYPE116:[0-9]+]]
pub fn foo117(_: Type4) { }
// CHECK: define{{.*}}foo117{{.*}}!type ![[TYPE117:[0-9]+]]
pub fn foo118(_: Type4, _: Type4) { }
// CHECK: define{{.*}}foo118{{.*}}!type ![[TYPE118:[0-9]+]]
pub fn foo119(_: Type4, _: Type4, _: Type4) { }
// CHECK: define{{.*}}foo119{{.*}}!type ![[TYPE119:[0-9]+]]
pub fn foo120(_: Type5) { }
// CHECK: define{{.*}}foo120{{.*}}!type ![[TYPE120:[0-9]+]]
pub fn foo121(_: Type5, _: Type5) { }
// CHECK: define{{.*}}foo121{{.*}}!type ![[TYPE121:[0-9]+]]
pub fn foo122(_: Type5, _: Type5, _: Type5) { }
// CHECK: define{{.*}}foo122{{.*}}!type ![[TYPE122:[0-9]+]]
pub fn foo123(_: Type6) { }
// CHECK: define{{.*}}foo123{{.*}}!type ![[TYPE123:[0-9]+]]
pub fn foo124(_: Type6, _: Type6) { }
// CHECK: define{{.*}}foo124{{.*}}!type ![[TYPE124:[0-9]+]]
pub fn foo125(_: Type6, _: Type6, _: Type6) { }
// CHECK: define{{.*}}foo125{{.*}}!type ![[TYPE125:[0-9]+]]
pub fn foo126(_: Type7) { }
// CHECK: define{{.*}}foo126{{.*}}!type ![[TYPE126:[0-9]+]]
pub fn foo127(_: Type7, _: Type7) { }
// CHECK: define{{.*}}foo127{{.*}}!type ![[TYPE127:[0-9]+]]
pub fn foo128(_: Type7, _: Type7, _: Type7) { }
// CHECK: define{{.*}}foo128{{.*}}!type ![[TYPE128:[0-9]+]]
pub fn foo129(_: Type8) { }
// CHECK: define{{.*}}foo129{{.*}}!type ![[TYPE129:[0-9]+]]
pub fn foo130(_: Type8, _: Type8) { }
// CHECK: define{{.*}}foo130{{.*}}!type ![[TYPE130:[0-9]+]]
pub fn foo131(_: Type8, _: Type8, _: Type8) { }
// CHECK: define{{.*}}foo131{{.*}}!type ![[TYPE131:[0-9]+]]
pub fn foo132(_: Type9) { }
// CHECK: define{{.*}}foo132{{.*}}!type ![[TYPE132:[0-9]+]]
pub fn foo133(_: Type9, _: Type9) { }
// CHECK: define{{.*}}foo133{{.*}}!type ![[TYPE133:[0-9]+]]
pub fn foo134(_: Type9, _: Type9, _: Type9) { }
// CHECK: define{{.*}}foo134{{.*}}!type ![[TYPE134:[0-9]+]]
pub fn foo135(_: Type10) { }
// CHECK: define{{.*}}foo135{{.*}}!type ![[TYPE135:[0-9]+]]
pub fn foo136(_: Type10, _: Type10) { }
// CHECK: define{{.*}}foo136{{.*}}!type ![[TYPE136:[0-9]+]]
pub fn foo137(_: Type10, _: Type10, _: Type10) { }
// CHECK: define{{.*}}foo137{{.*}}!type ![[TYPE137:[0-9]+]]
pub fn foo138(_: Type11) { }
// CHECK: define{{.*}}foo138{{.*}}!type ![[TYPE138:[0-9]+]]
pub fn foo139(_: Type11, _: Type11) { }
// CHECK: define{{.*}}foo139{{.*}}!type ![[TYPE139:[0-9]+]]
pub fn foo140(_: Type11, _: Type11, _: Type11) { }
// CHECK: define{{.*}}foo140{{.*}}!type ![[TYPE140:[0-9]+]]
pub fn foo141(_: Type12) { }
// CHECK: define{{.*}}foo141{{.*}}!type ![[TYPE141:[0-9]+]]
pub fn foo142(_: Type12, _: Type12) { }
// CHECK: define{{.*}}foo142{{.*}}!type ![[TYPE142:[0-9]+]]
pub fn foo143(_: Type12, _: Type12, _: Type12) { }
// CHECK: define{{.*}}foo143{{.*}}!type ![[TYPE143:[0-9]+]]
pub fn foo144(_: Type13) { }
// CHECK: define{{.*}}foo144{{.*}}!type ![[TYPE144:[0-9]+]]
pub fn foo145(_: Type13, _: Type13) { }
// CHECK: define{{.*}}foo145{{.*}}!type ![[TYPE145:[0-9]+]]
pub fn foo146(_: Type13, _: Type13, _: Type13) { }
// CHECK: define{{.*}}foo146{{.*}}!type ![[TYPE146:[0-9]+]]
// CHECK: ![[TYPE0]] = !{i64 0, !"_ZTSFvvE"}
// CHECK: ![[TYPE1]] = !{i64 0, !"_ZTSFvvvE"}
// CHECK: ![[TYPE2]] = !{i64 0, !"_ZTSFvvvvE"}
// CHECK: ![[TYPE3]] = !{i64 0, !"_ZTSFvPvE"}
// CHECK: ![[TYPE4]] = !{i64 0, !"_ZTSFvPvS_E"}
// CHECK: ![[TYPE5]] = !{i64 0, !"_ZTSFvPvS_S_E"}
// CHECK: ![[TYPE6]] = !{i64 0, !"_ZTSFvPKvE"}
// CHECK: ![[TYPE7]] = !{i64 0, !"_ZTSFvPKvS0_E"}
// CHECK: ![[TYPE8]] = !{i64 0, !"_ZTSFvPKvS0_S0_E"}
// CHECK: ![[TYPE9]] = !{i64 0, !"_ZTSFvbE"}
// CHECK: ![[TYPE10]] = !{i64 0, !"_ZTSFvbbE"}
// CHECK: ![[TYPE11]] = !{i64 0, !"_ZTSFvbbbE"}
// CHECK: ![[TYPE12]] = !{i64 0, !"_ZTSFvu2i8E"}
// CHECK: ![[TYPE13]] = !{i64 0, !"_ZTSFvu2i8S_E"}
// CHECK: ![[TYPE14]] = !{i64 0, !"_ZTSFvu2i8S_S_E"}
// CHECK: ![[TYPE15]] = !{i64 0, !"_ZTSFvu3i16E"}
// CHECK: ![[TYPE16]] = !{i64 0, !"_ZTSFvu3i16S_E"}
// CHECK: ![[TYPE17]] = !{i64 0, !"_ZTSFvu3i16S_S_E"}
// CHECK: ![[TYPE18]] = !{i64 0, !"_ZTSFvu3i32E"}
// CHECK: ![[TYPE19]] = !{i64 0, !"_ZTSFvu3i32S_E"}
// CHECK: ![[TYPE20]] = !{i64 0, !"_ZTSFvu3i32S_S_E"}
// CHECK: ![[TYPE21]] = !{i64 0, !"_ZTSFvu3i64E"}
// CHECK: ![[TYPE22]] = !{i64 0, !"_ZTSFvu3i64S_E"}
// CHECK: ![[TYPE23]] = !{i64 0, !"_ZTSFvu3i64S_S_E"}
// CHECK: ![[TYPE24]] = !{i64 0, !"_ZTSFvu4i128E"}
// CHECK: ![[TYPE25]] = !{i64 0, !"_ZTSFvu4i128S_E"}
// CHECK: ![[TYPE26]] = !{i64 0, !"_ZTSFvu4i128S_S_E"}
// CHECK: ![[TYPE27]] = !{i64 0, !"_ZTSFvu5isizeE"}
// CHECK: ![[TYPE28]] = !{i64 0, !"_ZTSFvu5isizeS_E"}
// CHECK: ![[TYPE29]] = !{i64 0, !"_ZTSFvu5isizeS_S_E"}
// CHECK: ![[TYPE30]] = !{i64 0, !"_ZTSFvu2u8E"}
// CHECK: ![[TYPE31]] = !{i64 0, !"_ZTSFvu2u8S_E"}
// CHECK: ![[TYPE32]] = !{i64 0, !"_ZTSFvu2u8S_S_E"}
// CHECK: ![[TYPE33]] = !{i64 0, !"_ZTSFvu3u16E"}
// CHECK: ![[TYPE34]] = !{i64 0, !"_ZTSFvu3u16S_E"}
// CHECK: ![[TYPE35]] = !{i64 0, !"_ZTSFvu3u16S_S_E"}
// CHECK: ![[TYPE36]] = !{i64 0, !"_ZTSFvu3u32E"}
// CHECK: ![[TYPE37]] = !{i64 0, !"_ZTSFvu3u32S_E"}
// CHECK: ![[TYPE38]] = !{i64 0, !"_ZTSFvu3u32S_S_E"}
// CHECK: ![[TYPE39]] = !{i64 0, !"_ZTSFvu3u64E"}
// CHECK: ![[TYPE40]] = !{i64 0, !"_ZTSFvu3u64S_E"}
// CHECK: ![[TYPE41]] = !{i64 0, !"_ZTSFvu3u64S_S_E"}
// CHECK: ![[TYPE42]] = !{i64 0, !"_ZTSFvu4u128E"}
// CHECK: ![[TYPE43]] = !{i64 0, !"_ZTSFvu4u128S_E"}
// CHECK: ![[TYPE44]] = !{i64 0, !"_ZTSFvu4u128S_S_E"}
// CHECK: ![[TYPE45]] = !{i64 0, !"_ZTSFvu5usizeE"}
// CHECK: ![[TYPE46]] = !{i64 0, !"_ZTSFvu5usizeS_E"}
// CHECK: ![[TYPE47]] = !{i64 0, !"_ZTSFvu5usizeS_S_E"}
// CHECK: ![[TYPE48]] = !{i64 0, !"_ZTSFvu3f32E"}
// CHECK: ![[TYPE49]] = !{i64 0, !"_ZTSFvu3f32S_E"}
// CHECK: ![[TYPE50]] = !{i64 0, !"_ZTSFvu3f32S_S_E"}
// CHECK: ![[TYPE51]] = !{i64 0, !"_ZTSFvu3f64E"}
// CHECK: ![[TYPE52]] = !{i64 0, !"_ZTSFvu3f64S_E"}
// CHECK: ![[TYPE53]] = !{i64 0, !"_ZTSFvu3f64S_S_E"}
// CHECK: ![[TYPE54]] = !{i64 0, !"_ZTSFvu4charE"}
// CHECK: ![[TYPE55]] = !{i64 0, !"_ZTSFvu4charS_E"}
// CHECK: ![[TYPE56]] = !{i64 0, !"_ZTSFvu4charS_S_E"}
// CHECK: ![[TYPE57]] = !{i64 0, !"_ZTSFvu3refIu3strEE"}
// CHECK: ![[TYPE58]] = !{i64 0, !"_ZTSFvu3refIu3strES0_E"}
// CHECK: ![[TYPE59]] = !{i64 0, !"_ZTSFvu3refIu3strES0_S0_E"}
// CHECK: ![[TYPE60]] = !{i64 0, !"_ZTSFvu5tupleIu3i32S_EE"}
// CHECK: ![[TYPE61]] = !{i64 0, !"_ZTSFvu5tupleIu3i32S_ES0_E"}
// CHECK: ![[TYPE62]] = !{i64 0, !"_ZTSFvu5tupleIu3i32S_ES0_S0_E"}
// CHECK: ![[TYPE63]] = !{i64 0, !"_ZTSFvA32u3i32E"}
// CHECK: ![[TYPE64]] = !{i64 0, !"_ZTSFvA32u3i32S0_E"}
// CHECK: ![[TYPE65]] = !{i64 0, !"_ZTSFvA32u3i32S0_S0_E"}
// CHECK: ![[TYPE66]] = !{i64 0, !"_ZTSFvu3refIu5sliceIu3i32EEE"}
// CHECK: ![[TYPE67]] = !{i64 0, !"_ZTSFvu3refIu5sliceIu3i32EES1_E"}
// CHECK: ![[TYPE68]] = !{i64 0, !"_ZTSFvu3refIu5sliceIu3i32EES1_S1_E"}
// CHECK: ![[TYPE69]] = !{i64 0, !"_ZTSFvu3refIu{{[0-9]+}}NtC{{[[:print:]]+}}_51sanitizer_cfi_emit_type_metadata_id_itanium_cxx_abi7Struct1Iu3i32EEE"}
// CHECK: ![[TYPE70]] = !{i64 0, !"_ZTSFvu3refIu{{[0-9]+}}NtC{{[[:print:]]+}}_51sanitizer_cfi_emit_type_metadata_id_itanium_cxx_abi7Struct1Iu3i32EES1_E"}
// CHECK: ![[TYPE71]] = !{i64 0, !"_ZTSFvu3refIu{{[0-9]+}}NtC{{[[:print:]]+}}_51sanitizer_cfi_emit_type_metadata_id_itanium_cxx_abi7Struct1Iu3i32EES1_S1_E"}
// CHECK: ![[TYPE72]] = !{i64 0, !"_ZTSFvu3refIu{{[0-9]+}}NtC{{[[:print:]]+}}_51sanitizer_cfi_emit_type_metadata_id_itanium_cxx_abi5Enum1Iu3i32EEE"}
// CHECK: ![[TYPE73]] = !{i64 0, !"_ZTSFvu3refIu{{[0-9]+}}NtC{{[[:print:]]+}}_51sanitizer_cfi_emit_type_metadata_id_itanium_cxx_abi5Enum1Iu3i32EES1_E"}
// CHECK: ![[TYPE74]] = !{i64 0, !"_ZTSFvu3refIu{{[0-9]+}}NtC{{[[:print:]]+}}_51sanitizer_cfi_emit_type_metadata_id_itanium_cxx_abi5Enum1Iu3i32EES1_S1_E"}
// CHECK: ![[TYPE75]] = !{i64 0, !"_ZTSFvu3refIu{{[0-9]+}}NtC{{[[:print:]]+}}_51sanitizer_cfi_emit_type_metadata_id_itanium_cxx_abi6Union1Iu3i32EEE"}
// CHECK: ![[TYPE76]] = !{i64 0, !"_ZTSFvu3refIu{{[0-9]+}}NtC{{[[:print:]]+}}_51sanitizer_cfi_emit_type_metadata_id_itanium_cxx_abi6Union1Iu3i32EES1_E"}
// CHECK: ![[TYPE77]] = !{i64 0, !"_ZTSFvu3refIu{{[0-9]+}}NtC{{[[:print:]]+}}_51sanitizer_cfi_emit_type_metadata_id_itanium_cxx_abi6Union1Iu3i32EES1_S1_E"}
// CHECK: ![[TYPE78]] = !{i64 0, !"_ZTSFvP5type1E"}
// CHECK: ![[TYPE79]] = !{i64 0, !"_ZTSFvP5type1S0_E"}
// CHECK: ![[TYPE80]] = !{i64 0, !"_ZTSFvP5type1S0_S0_E"}
// CHECK: ![[TYPE81]] = !{i64 0, !"_ZTSFvU3mutu3refIu3i32EE"}
// CHECK: ![[TYPE82]] = !{i64 0, !"_ZTSFvU3mutu3refIu3i32ES0_E"}
// CHECK: ![[TYPE83]] = !{i64 0, !"_ZTSFvU3mutu3refIu3i32ES0_S0_E"}
// CHECK: ![[TYPE84]] = !{i64 0, !"_ZTSFvu3refIu3i32EE"}
// CHECK: ![[TYPE85]] = !{i64 0, !"_ZTSFvu3refIu3i32EU3mutS0_E"}
// CHECK: ![[TYPE86]] = !{i64 0, !"_ZTSFvu3refIu3i32EU3mutS0_S1_E"}
// CHECK: ![[TYPE87]] = !{i64 0, !"_ZTSFvPu3i32E"}
// CHECK: ![[TYPE88]] = !{i64 0, !"_ZTSFvPu3i32PKS_E"}
// CHECK: ![[TYPE89]] = !{i64 0, !"_ZTSFvPu3i32PKS_S2_E"}
// CHECK: ![[TYPE90]] = !{i64 0, !"_ZTSFvPKu3i32E"}
// CHECK: ![[TYPE91]] = !{i64 0, !"_ZTSFvPKu3i32PS_E"}
// CHECK: ![[TYPE92]] = !{i64 0, !"_ZTSFvPKu3i32PS_S2_E"}
// CHECK: ![[TYPE93]] = !{i64 0, !"_ZTSFvPFu3i32S_EE"}
// CHECK: ![[TYPE94]] = !{i64 0, !"_ZTSFvPFu3i32S_ES0_E"}
// CHECK: ![[TYPE95]] = !{i64 0, !"_ZTSFvPFu3i32S_ES0_S0_E"}
// CHECK: ![[TYPE96]] = !{i64 0, !"_ZTSFvu3refIu3dynIu{{[0-9]+}}NtNtNtC{{[[:print:]]+}}_4core3ops8function2FnIu5tupleIu3i32EEu{{[0-9]+}}NtNtNtNtC{{[[:print:]]+}}_4core3ops8function6FnOnce6OutputIS0_ES_u6regionEEE"}
// CHECK: ![[TYPE97]] = !{i64 0, !"_ZTSFvu3refIu3dynIu{{[0-9]+}}NtNtNtC{{[[:print:]]+}}_4core3ops8function2FnIu5tupleIu3i32EEu{{[0-9]+}}NtNtNtNtC{{[[:print:]]+}}_4core3ops8function6FnOnce6OutputIS0_ES_u6regionEES5_E"}
// CHECK: ![[TYPE98]] = !{i64 0, !"_ZTSFvu3refIu3dynIu{{[0-9]+}}NtNtNtC{{[[:print:]]+}}_4core3ops8function2FnIu5tupleIu3i32EEu{{[0-9]+}}NtNtNtNtC{{[[:print:]]+}}_4core3ops8function6FnOnce6OutputIS0_ES_u6regionEES5_S5_E"}
// CHECK: ![[TYPE99]] = !{i64 0, !"_ZTSFvu3refIu3dynIu{{[0-9]+}}NtNtNtC{{[[:print:]]+}}_4core3ops8function5FnMutIu5tupleIu3i32EEu{{[0-9]+}}NtNtNtNtC{{[[:print:]]+}}_4core3ops8function6FnOnce6OutputIS0_ES_u6regionEEE"}
// CHECK: ![[TYPE100]] = !{i64 0, !"_ZTSFvu3refIu3dynIu{{[0-9]+}}NtNtNtC{{[[:print:]]+}}_4core3ops8function5FnMutIu5tupleIu3i32EEu{{[0-9]+}}NtNtNtNtC{{[[:print:]]+}}_4core3ops8function6FnOnce6OutputIS0_ES_u6regionEES5_E"}
// CHECK: ![[TYPE101]] = !{i64 0, !"_ZTSFvu3refIu3dynIu{{[0-9]+}}NtNtNtC{{[[:print:]]+}}_4core3ops8function5FnMutIu5tupleIu3i32EEu{{[0-9]+}}NtNtNtNtC{{[[:print:]]+}}_4core3ops8function6FnOnce6OutputIS0_ES_u6regionEES5_S5_E"}
// CHECK: ![[TYPE102]] = !{i64 0, !"_ZTSFvu3refIu3dynIu{{[0-9]+}}NtNtNtC{{[[:print:]]+}}_4core3ops8function6FnOnceIu5tupleIu3i32EEu{{[0-9]+}}NtNtNtNtC{{[[:print:]]+}}_4core3ops8function6FnOnce6OutputIS0_ES_u6regionEEE"}
// CHECK: ![[TYPE103]] = !{i64 0, !"_ZTSFvu3refIu3dynIu{{[0-9]+}}NtNtNtC{{[[:print:]]+}}_4core3ops8function6FnOnceIu5tupleIu3i32EEu{{[0-9]+}}NtNtNtNtC{{[[:print:]]+}}_4core3ops8function6FnOnce6OutputIS0_ES_u6regionEES5_E"}
// CHECK: ![[TYPE104]] = !{i64 0, !"_ZTSFvu3refIu3dynIu{{[0-9]+}}NtNtNtC{{[[:print:]]+}}_4core3ops8function6FnOnceIu5tupleIu3i32EEu{{[0-9]+}}NtNtNtNtC{{[[:print:]]+}}_4core3ops8function6FnOnce6OutputIS0_ES_u6regionEES5_S5_E"}
// CHECK: ![[TYPE105]] = !{i64 0, !"_ZTSFvu3refIu3dynIu{{[0-9]+}}NtNtC{{[[:print:]]+}}_4core6marker4Sendu6regionEEE"}
// CHECK: ![[TYPE106]] = !{i64 0, !"_ZTSFvu3refIu3dynIu{{[0-9]+}}NtNtC{{[[:print:]]+}}_4core6marker4Sendu6regionEES2_E"}
// CHECK: ![[TYPE107]] = !{i64 0, !"_ZTSFvu3refIu3dynIu{{[0-9]+}}NtNtC{{[[:print:]]+}}_4core6marker4Sendu6regionEES2_S2_E"}
// CHECK: ![[TYPE108]] = !{i64 0, !"_ZTSFvu{{[0-9]+}}NCNvC{{[[:print:]]+}}_51sanitizer_cfi_emit_type_metadata_id_itanium_cxx_abi3fn111{{[{}][{}]}}closure{{[}][}]}}Iu2i8PFvvEvEE"}
// CHECK: ![[TYPE109]] = !{i64 0, !"_ZTSFvu{{[0-9]+}}NCNvC{{[[:print:]]+}}_51sanitizer_cfi_emit_type_metadata_id_itanium_cxx_abi3fn111{{[{}][{}]}}closure{{[}][}]}}Iu2i8PFvvEvES1_E"}
// CHECK: ![[TYPE110]] = !{i64 0, !"_ZTSFvu{{[0-9]+}}NCNvC{{[[:print:]]+}}_51sanitizer_cfi_emit_type_metadata_id_itanium_cxx_abi3fn111{{[{}][{}]}}closure{{[}][}]}}Iu2i8PFvvEvES1_S1_E"}
// CHECK: ![[TYPE111]] = !{i64 0, !"_ZTSFvu{{[0-9]+}}NcNtNvC{{[[:print:]]+}}_51sanitizer_cfi_emit_type_metadata_id_itanium_cxx_abi3fn13Foo15{{[{}][{}]}}constructor{{[}][}]}}E"}
// CHECK: ![[TYPE112]] = !{i64 0, !"_ZTSFvu{{[0-9]+}}NcNtNvC{{[[:print:]]+}}_51sanitizer_cfi_emit_type_metadata_id_itanium_cxx_abi3fn13Foo15{{[{}][{}]}}constructor{{[}][}]}}S_E"}
// CHECK: ![[TYPE113]] = !{i64 0, !"_ZTSFvu{{[0-9]+}}NcNtNvC{{[[:print:]]+}}_51sanitizer_cfi_emit_type_metadata_id_itanium_cxx_abi3fn13Foo15{{[{}][{}]}}constructor{{[}][}]}}S_S_E"}
// CHECK: ![[TYPE114]] = !{i64 0, !"_ZTSFvu{{[0-9]+}}NvNFNvC{{[[:print:]]+}}_51sanitizer_cfi_emit_type_metadata_id_itanium_cxx_abi3fn110{{[{}][{}]}}extern{{[}][}]}}3fooE"}
// CHECK: ![[TYPE115]] = !{i64 0, !"_ZTSFvu{{[0-9]+}}NvNFNvC{{[[:print:]]+}}_51sanitizer_cfi_emit_type_metadata_id_itanium_cxx_abi3fn110{{[{}][{}]}}extern{{[}][}]}}3fooS_E"}
// CHECK: ![[TYPE116]] = !{i64 0, !"_ZTSFvu{{[0-9]+}}NvNFNvC{{[[:print:]]+}}_51sanitizer_cfi_emit_type_metadata_id_itanium_cxx_abi3fn110{{[{}][{}]}}extern{{[}][}]}}3fooS_S_E"}
// CHECK: ![[TYPE117]] = !{i64 0, !"_ZTSFvu{{[0-9]+}}NtNCNvC{{[[:print:]]+}}_51sanitizer_cfi_emit_type_metadata_id_itanium_cxx_abi3fn1s0_11{{[{}][{}]}}closure{{[}][}]}}3FooE"}
// CHECK: ![[TYPE118]] = !{i64 0, !"_ZTSFvu{{[0-9]+}}NtNCNvC{{[[:print:]]+}}_51sanitizer_cfi_emit_type_metadata_id_itanium_cxx_abi3fn1s0_11{{[{}][{}]}}closure{{[}][}]}}3FooS_E"}
// CHECK: ![[TYPE119]] = !{i64 0, !"_ZTSFvu{{[0-9]+}}NtNCNvC{{[[:print:]]+}}_51sanitizer_cfi_emit_type_metadata_id_itanium_cxx_abi3fn1s0_11{{[{}][{}]}}closure{{[}][}]}}3FooS_S_E"}
// CHECK: ![[TYPE120]] = !{i64 0, !"_ZTSFvu{{[0-9]+}}NtNkNvC{{[[:print:]]+}}_51sanitizer_cfi_emit_type_metadata_id_itanium_cxx_abi3fn112{{[{}][{}]}}constant{{[}][}]}}3FooE"}
// CHECK: ![[TYPE121]] = !{i64 0, !"_ZTSFvu{{[0-9]+}}NtNkNvC{{[[:print:]]+}}_51sanitizer_cfi_emit_type_metadata_id_itanium_cxx_abi3fn112{{[{}][{}]}}constant{{[}][}]}}3FooS_E"}
// CHECK: ![[TYPE122]] = !{i64 0, !"_ZTSFvu{{[0-9]+}}NtNkNvC{{[[:print:]]+}}_51sanitizer_cfi_emit_type_metadata_id_itanium_cxx_abi3fn112{{[{}][{}]}}constant{{[}][}]}}3FooS_S_E"}
// CHECK: ![[TYPE123]] = !{i64 0, !"_ZTSFvu{{[0-9]+}}NvNINvC{{[[:print:]]+}}_51sanitizer_cfi_emit_type_metadata_id_itanium_cxx_abi3fn18{{[{}][{}]}}impl{{[}][}]}}3fooIu3i32EE"}
// CHECK: ![[TYPE124]] = !{i64 0, !"_ZTSFvu{{[0-9]+}}NvNINvC{{[[:print:]]+}}_51sanitizer_cfi_emit_type_metadata_id_itanium_cxx_abi3fn18{{[{}][{}]}}impl{{[}][}]}}3fooIu3i32ES0_E"}
// CHECK: ![[TYPE125]] = !{i64 0, !"_ZTSFvu{{[0-9]+}}NvNINvC{{[[:print:]]+}}_51sanitizer_cfi_emit_type_metadata_id_itanium_cxx_abi3fn18{{[{}][{}]}}impl{{[}][}]}}3fooIu3i32ES0_S0_E"}
// CHECK: ![[TYPE126]] = !{i64 0, !"_ZTSFvu{{[0-9]+}}NvNtC{{[[:print:]]+}}_51sanitizer_cfi_emit_type_metadata_id_itanium_cxx_abi6Trait13fooIu3dynIu{{[0-9]+}}NtC{{[[:print:]]+}}_51sanitizer_cfi_emit_type_metadata_id_itanium_cxx_abi6Trait1Iu3i32Eu6regionES_EE"}
// CHECK: ![[TYPE127]] = !{i64 0, !"_ZTSFvu{{[0-9]+}}NvNtC{{[[:print:]]+}}_51sanitizer_cfi_emit_type_metadata_id_itanium_cxx_abi6Trait13fooIu3dynIu{{[0-9]+}}NtC{{[[:print:]]+}}_51sanitizer_cfi_emit_type_metadata_id_itanium_cxx_abi6Trait1Iu3i32Eu6regionES_ES3_E"}
// CHECK: ![[TYPE128]] = !{i64 0, !"_ZTSFvu{{[0-9]+}}NvNtC{{[[:print:]]+}}_51sanitizer_cfi_emit_type_metadata_id_itanium_cxx_abi6Trait13fooIu3dynIu{{[0-9]+}}NtC{{[[:print:]]+}}_51sanitizer_cfi_emit_type_metadata_id_itanium_cxx_abi6Trait1Iu3i32Eu6regionES_ES3_S3_E"}
// CHECK: ![[TYPE129]] = !{i64 0, !"_ZTSFvu{{[0-9]+}}NvNtC{{[[:print:]]+}}_51sanitizer_cfi_emit_type_metadata_id_itanium_cxx_abi6Trait13fooIu3i32S_EE"}
// CHECK: ![[TYPE130]] = !{i64 0, !"_ZTSFvu{{[0-9]+}}NvNtC{{[[:print:]]+}}_51sanitizer_cfi_emit_type_metadata_id_itanium_cxx_abi6Trait13fooIu3i32S_ES0_E"}
// CHECK: ![[TYPE131]] = !{i64 0, !"_ZTSFvu{{[0-9]+}}NvNtC{{[[:print:]]+}}_51sanitizer_cfi_emit_type_metadata_id_itanium_cxx_abi6Trait13fooIu3i32S_ES0_S0_E"}
// CHECK: ![[TYPE132]] = !{i64 0, !"_ZTSFvu{{[0-9]+}}NvNtC{{[[:print:]]+}}_51sanitizer_cfi_emit_type_metadata_id_itanium_cxx_abi6Trait13fooIu{{[0-9]+}}NtC{{[[:print:]]+}}_51sanitizer_cfi_emit_type_metadata_id_itanium_cxx_abi7Struct1Iu3i32ES_EE"}
// CHECK: ![[TYPE133]] = !{i64 0, !"_ZTSFvu{{[0-9]+}}NvNtC{{[[:print:]]+}}_51sanitizer_cfi_emit_type_metadata_id_itanium_cxx_abi6Trait13fooIu{{[0-9]+}}NtC{{[[:print:]]+}}_51sanitizer_cfi_emit_type_metadata_id_itanium_cxx_abi7Struct1Iu3i32ES_ES1_E"}
// CHECK: ![[TYPE134]] = !{i64 0, !"_ZTSFvu{{[0-9]+}}NvNtC{{[[:print:]]+}}_51sanitizer_cfi_emit_type_metadata_id_itanium_cxx_abi6Trait13fooIu{{[0-9]+}}NtC{{[[:print:]]+}}_51sanitizer_cfi_emit_type_metadata_id_itanium_cxx_abi7Struct1Iu3i32ES_ES1_S1_E"}
// CHECK: ![[TYPE135]] = !{i64 0, !"_ZTSFvu{{[0-9]+}}NtNvC{{[[:print:]]+}}_51sanitizer_cfi_emit_type_metadata_id_itanium_cxx_abi3fn13QuxIu3i32Lu5usize32EEE"}
// CHECK: ![[TYPE136]] = !{i64 0, !"_ZTSFvu{{[0-9]+}}NtNvC{{[[:print:]]+}}_51sanitizer_cfi_emit_type_metadata_id_itanium_cxx_abi3fn13QuxIu3i32Lu5usize32EES2_E"}
// CHECK: ![[TYPE137]] = !{i64 0, !"_ZTSFvu{{[0-9]+}}NtNvC{{[[:print:]]+}}_51sanitizer_cfi_emit_type_metadata_id_itanium_cxx_abi3fn13QuxIu3i32Lu5usize32EES2_S2_E"}
// CHECK: ![[TYPE138]] = !{i64 0, !"_ZTSFvu{{[0-9]+}}NcNtNvC{{[[:print:]]+}}_51sanitizer_cfi_emit_type_metadata_id_itanium_cxx_abi3fn15Quuux15{{[{}][{}]}}constructor{{[}][}]}}Iu6regionS_EE"}
// CHECK: ![[TYPE139]] = !{i64 0, !"_ZTSFvu{{[0-9]+}}NcNtNvC{{[[:print:]]+}}_51sanitizer_cfi_emit_type_metadata_id_itanium_cxx_abi3fn15Quuux15{{[{}][{}]}}constructor{{[}][}]}}Iu6regionS_ES0_E"}
// CHECK: ![[TYPE140]] = !{i64 0, !"_ZTSFvu{{[0-9]+}}NcNtNvC{{[[:print:]]+}}_51sanitizer_cfi_emit_type_metadata_id_itanium_cxx_abi3fn15Quuux15{{[{}][{}]}}constructor{{[}][}]}}Iu6regionS_ES0_S0_E"}
// CHECK: ![[TYPE141]] = !{i64 0, !"_ZTSFvu{{[0-9]+}}NtC{{[[:print:]]+}}_51sanitizer_cfi_emit_type_metadata_id_itanium_cxx_abi3FooE"}
// CHECK: ![[TYPE142]] = !{i64 0, !"_ZTSFvu{{[0-9]+}}NtC{{[[:print:]]+}}_51sanitizer_cfi_emit_type_metadata_id_itanium_cxx_abi3FooS_E"}
// CHECK: ![[TYPE143]] = !{i64 0, !"_ZTSFvu{{[0-9]+}}NtC{{[[:print:]]+}}_51sanitizer_cfi_emit_type_metadata_id_itanium_cxx_abi3FooS_S_E"}
// CHECK: ![[TYPE144]] = !{i64 0, !"_ZTSFvu3refIu3refIvEEE"}
// CHECK: ![[TYPE145]] = !{i64 0, !"_ZTSFvu3refIu3refIvEES0_E"}
// CHECK: ![[TYPE146]] = !{i64 0, !"_ZTSFvu3refIu3refIvEES0_S0_E"}

View File

@ -0,0 +1,31 @@
// Verifies that type metadata for functions are emitted.
//
// needs-sanitizer-cfi
// compile-flags: -Clto -Cno-prepopulate-passes -Ctarget-feature=-crt-static -Zsanitizer=cfi
#![crate_type="lib"]
pub fn foo(f: fn(i32) -> i32, arg: i32) -> i32 {
// CHECK-LABEL: define{{.*}}foo
// CHECK-SAME: {{.*}}!type ![[TYPE1:[0-9]+]]
// CHECK: %1 = call i1 @llvm.type.test(i8* %0, metadata !"_ZTSFu3i32S_E")
f(arg)
}
pub fn bar(f: fn(i32, i32) -> i32, arg1: i32, arg2: i32) -> i32 {
// CHECK-LABEL: define{{.*}}bar
// CHECK-SAME: {{.*}}!type ![[TYPE2:[0-9]+]]
// CHECK: %1 = call i1 @llvm.type.test(i8* %0, metadata !"_ZTSFu3i32S_S_E")
f(arg1, arg2)
}
pub fn baz(f: fn(i32, i32, i32) -> i32, arg1: i32, arg2: i32, arg3: i32) -> i32 {
// CHECK-LABEL: define{{.*}}baz
// CHECK-SAME: {{.*}}!type ![[TYPE3:[0-9]+]]
// CHECK: %1 = call i1 @llvm.type.test(i8* %0, metadata !"_ZTSFu3i32S_S_S_E")
f(arg1, arg2, arg3)
}
// CHECK: ![[TYPE1]] = !{i64 0, !"_ZTSFu3i32PFS_S_ES_E"}
// CHECK: ![[TYPE2]] = !{i64 0, !"_ZTSFu3i32PFS_S_S_ES_S_E"}
// CHECK: ![[TYPE3]] = !{i64 0, !"_ZTSFu3i32PFS_S_S_S_ES_S_S_E"}

View File

@ -1,31 +0,0 @@
// Verifies that type metadata for functions are emitted.
//
// ignore-windows
// needs-sanitizer-cfi
// only-aarch64
// only-x86_64
// compile-flags: -Clto -Cno-prepopulate-passes -Zsanitizer=cfi
#![crate_type="lib"]
pub fn foo(f: fn(i32) -> i32, arg: i32) -> i32 {
// CHECK-LABEL: define{{.*}}foo{{.*}}!type !{{[0-9]+}}
// CHECK: %1 = call i1 @llvm.type.test(i8* %0, metadata !"typeid1")
f(arg)
}
pub fn bar(f: fn(i32, i32) -> i32, arg1: i32, arg2: i32) -> i32 {
// CHECK-LABEL: define{{.*}}bar{{.*}}!type !{{[0-9]+}}
// CHECK: %1 = call i1 @llvm.type.test(i8* %0, metadata !"typeid2")
f(arg1, arg2)
}
pub fn baz(f: fn(i32, i32, i32) -> i32, arg1: i32, arg2: i32, arg3: i32) -> i32 {
// CHECK-LABEL: define{{.*}}baz{{.*}}!type !{{[0-9]+}}
// CHECK: %1 = call i1 @llvm.type.test(i8* %0, metadata !"typeid3")
f(arg1, arg2, arg3)
}
// CHECK: !{{[0-9]+}} = !{i64 0, !"typeid2"}
// CHECK: !{{[0-9]+}} = !{i64 0, !"typeid3"}
// CHECK: !{{[0-9]+}} = !{i64 0, !"typeid4"}

View File

@ -871,6 +871,7 @@ pub fn make_test_description<R: Read>(
let rustc_has_sanitizer_support = env::var_os("RUSTC_SANITIZER_SUPPORT").is_some();
let has_asm_support = util::has_asm_support(&config.target);
let has_asan = util::ASAN_SUPPORTED_TARGETS.contains(&&*config.target);
let has_cfi = util::CFI_SUPPORTED_TARGETS.contains(&&*config.target);
let has_lsan = util::LSAN_SUPPORTED_TARGETS.contains(&&*config.target);
let has_msan = util::MSAN_SUPPORTED_TARGETS.contains(&&*config.target);
let has_tsan = util::TSAN_SUPPORTED_TARGETS.contains(&&*config.target);
@ -909,6 +910,7 @@ pub fn make_test_description<R: Read>(
ignore |= !rustc_has_sanitizer_support
&& config.parse_name_directive(ln, "needs-sanitizer-support");
ignore |= !has_asan && config.parse_name_directive(ln, "needs-sanitizer-address");
ignore |= !has_cfi && config.parse_name_directive(ln, "needs-sanitizer-cfi");
ignore |= !has_lsan && config.parse_name_directive(ln, "needs-sanitizer-leak");
ignore |= !has_msan && config.parse_name_directive(ln, "needs-sanitizer-memory");
ignore |= !has_tsan && config.parse_name_directive(ln, "needs-sanitizer-thread");

View File

@ -96,6 +96,23 @@ pub const ASAN_SUPPORTED_TARGETS: &[&str] = &[
"x86_64-unknown-linux-gnu",
];
// FIXME(rcvalle): More targets are likely supported.
pub const CFI_SUPPORTED_TARGETS: &[&str] = &[
"aarch64-apple-darwin",
"aarch64-fuchsia",
"aarch64-linux-android",
"aarch64-unknown-freebsd",
"aarch64-unknown-linux-gnu",
"x86_64-apple-darwin",
"x86_64-fuchsia",
"x86_64-pc-solaris",
"x86_64-unknown-freebsd",
"x86_64-unknown-illumos",
"x86_64-unknown-linux-gnu",
"x86_64-unknown-linux-musl",
"x86_64-unknown-netbsd",
];
pub const LSAN_SUPPORTED_TARGETS: &[&str] = &[
// FIXME: currently broken, see #88132
// "aarch64-apple-darwin",