Skip to main content

rustc_sanitizers/cfi/typeid/itanium_cxx_abi/
mod.rs

1//! Type metadata identifiers (using Itanium C++ ABI mangling for encoding) for LLVM Control Flow
2//! Integrity (CFI) and cross-language LLVM CFI support.
3//!
4//! For more information about LLVM CFI and cross-language LLVM CFI support for the Rust compiler,
5//! see design document in the tracking issue #89653.
6
7use rustc_abi::CanonAbi;
8use rustc_data_structures::fx::FxHashMap;
9use rustc_middle::bug;
10use rustc_middle::ty::{self, Instance, Ty, TyCtxt, TypeFoldable, TypeVisitableExt};
11use rustc_target::callconv::{FnAbi, PassMode};
12use tracing::instrument;
13
14mod encode;
15mod transform;
16use crate::cfi::typeid::TypeIdOptions;
17use crate::cfi::typeid::itanium_cxx_abi::encode::{DictKey, EncodeTyOptions, encode_ty};
18use crate::cfi::typeid::itanium_cxx_abi::transform::{
19    TransformTy, TransformTyOptions, transform_instance,
20};
21
22/// Returns a type metadata identifier for the specified FnAbi using the Itanium C++ ABI with vendor
23/// extended type qualifiers and types for Rust types that are not used at the FFI boundary.
24#[allow(clippy :: suspicious_else_formatting)]
{
    let __tracing_attr_span;
    let __tracing_attr_guard;
    if ::tracing::Level::TRACE <= ::tracing::level_filters::STATIC_MAX_LEVEL
                &&
                ::tracing::Level::TRACE <=
                    ::tracing::level_filters::LevelFilter::current() ||
            { false } {
        __tracing_attr_span =
            {
                use ::tracing::__macro_support::Callsite as _;
                static __CALLSITE: ::tracing::callsite::DefaultCallsite =
                    {
                        static META: ::tracing::Metadata<'static> =
                            {
                                ::tracing_core::metadata::Metadata::new("typeid_for_fnabi",
                                    "rustc_sanitizers::cfi::typeid::itanium_cxx_abi",
                                    ::tracing::Level::TRACE,
                                    ::tracing_core::__macro_support::Option::Some("compiler/rustc_sanitizers/src/cfi/typeid/itanium_cxx_abi/mod.rs"),
                                    ::tracing_core::__macro_support::Option::Some(24u32),
                                    ::tracing_core::__macro_support::Option::Some("rustc_sanitizers::cfi::typeid::itanium_cxx_abi"),
                                    ::tracing_core::field::FieldSet::new(&["fn_abi", "options"],
                                        ::tracing_core::callsite::Identifier(&__CALLSITE)),
                                    ::tracing::metadata::Kind::SPAN)
                            };
                        ::tracing::callsite::DefaultCallsite::new(&META)
                    };
                let mut interest = ::tracing::subscriber::Interest::never();
                if ::tracing::Level::TRACE <=
                                    ::tracing::level_filters::STATIC_MAX_LEVEL &&
                                ::tracing::Level::TRACE <=
                                    ::tracing::level_filters::LevelFilter::current() &&
                            { interest = __CALLSITE.interest(); !interest.is_never() }
                        &&
                        ::tracing::__macro_support::__is_enabled(__CALLSITE.metadata(),
                            interest) {
                    let meta = __CALLSITE.metadata();
                    ::tracing::Span::new(meta,
                        &{
                                #[allow(unused_imports)]
                                use ::tracing::field::{debug, display, Value};
                                let mut iter = meta.fields().iter();
                                meta.fields().value_set(&[(&::tracing::__macro_support::Iterator::next(&mut iter).expect("FieldSet corrupted (this is a bug)"),
                                                    ::tracing::__macro_support::Option::Some(&::tracing::field::debug(&fn_abi)
                                                            as &dyn Value)),
                                                (&::tracing::__macro_support::Iterator::next(&mut iter).expect("FieldSet corrupted (this is a bug)"),
                                                    ::tracing::__macro_support::Option::Some(&::tracing::field::debug(&options)
                                                            as &dyn Value))])
                            })
                } else {
                    let span =
                        ::tracing::__macro_support::__disabled_span(__CALLSITE.metadata());
                    {};
                    span
                }
            };
        __tracing_attr_guard = __tracing_attr_span.enter();
    }

    #[warn(clippy :: suspicious_else_formatting)]
    {

        #[allow(unknown_lints, unreachable_code, clippy ::
        diverging_sub_expression, clippy :: empty_loop, clippy ::
        let_unit_value, clippy :: let_with_type_underscore, clippy ::
        needless_return, clippy :: unreachable)]
        if false {
            let __tracing_attr_fake_return: String = loop {};
            return __tracing_attr_fake_return;
        }
        {
            let mut typeid = String::from("_Z");
            typeid.push_str("TS");
            typeid.push('F');
            let mut dict: FxHashMap<DictKey<'tcx>, usize> =
                FxHashMap::default();
            let mut encode_ty_options =
                EncodeTyOptions::from_bits(options.bits()).unwrap_or_else(||
                        ::rustc_middle::util::bug::bug_fmt(format_args!("typeid_for_fnabi: invalid option(s) `{0:?}`",
                                options.bits())));
            match fn_abi.conv {
                CanonAbi::C => {
                    encode_ty_options.insert(EncodeTyOptions::GENERALIZE_REPR_C);
                }
                _ => {
                    encode_ty_options.remove(EncodeTyOptions::GENERALIZE_REPR_C);
                }
            }
            let transform_ty_options =
                TransformTyOptions::from_bits(options.bits()).unwrap_or_else(||
                        ::rustc_middle::util::bug::bug_fmt(format_args!("typeid_for_fnabi: invalid option(s) `{0:?}`",
                                options.bits())));
            let mut type_folder = TransformTy::new(tcx, transform_ty_options);
            let ty = fn_abi.ret.layout.ty.fold_with(&mut type_folder);
            typeid.push_str(&encode_ty(tcx, ty, &mut dict,
                        encode_ty_options));
            if !fn_abi.c_variadic {
                let mut pushed_arg = false;
                for arg in
                    fn_abi.args.iter().filter(|arg|
                            arg.mode != PassMode::Ignore) {
                    pushed_arg = true;
                    let ty = arg.layout.ty.fold_with(&mut type_folder);
                    typeid.push_str(&encode_ty(tcx, ty, &mut dict,
                                encode_ty_options));
                }
                if !pushed_arg { typeid.push('v'); }
            } else {
                for n in 0..fn_abi.fixed_count as usize {
                    if fn_abi.args[n].mode == PassMode::Ignore { continue; }
                    let ty =
                        fn_abi.args[n].layout.ty.fold_with(&mut type_folder);
                    typeid.push_str(&encode_ty(tcx, ty, &mut dict,
                                encode_ty_options));
                }
                typeid.push('z');
            }
            typeid.push('E');
            if options.contains(EncodeTyOptions::NORMALIZE_INTEGERS) {
                typeid.push_str(".normalized");
            }
            if options.contains(EncodeTyOptions::GENERALIZE_POINTERS) {
                typeid.push_str(".generalized");
            }
            typeid
        }
    }
}#[instrument(level = "trace", skip(tcx))]
25pub fn typeid_for_fnabi<'tcx>(
26    tcx: TyCtxt<'tcx>,
27    fn_abi: &FnAbi<'tcx, Ty<'tcx>>,
28    options: TypeIdOptions,
29) -> String {
30    // A name is mangled by prefixing "_Z" to an encoding of its name, and in the case of functions
31    // its type.
32    let mut typeid = String::from("_Z");
33
34    // Clang uses the Itanium C++ ABI's virtual tables and RTTI typeinfo structure name as type
35    // metadata identifiers for function pointers. The typeinfo name encoding is a two-character
36    // code (i.e., 'TS') prefixed to the type encoding for the function.
37    typeid.push_str("TS");
38
39    // Function types are delimited by an "F..E" pair
40    typeid.push('F');
41
42    // A dictionary of substitution candidates used for compression (see
43    // https://itanium-cxx-abi.github.io/cxx-abi/abi.html#mangling-compression).
44    let mut dict: FxHashMap<DictKey<'tcx>, usize> = FxHashMap::default();
45
46    let mut encode_ty_options = EncodeTyOptions::from_bits(options.bits())
47        .unwrap_or_else(|| bug!("typeid_for_fnabi: invalid option(s) `{:?}`", options.bits()));
48    match fn_abi.conv {
49        CanonAbi::C => {
50            encode_ty_options.insert(EncodeTyOptions::GENERALIZE_REPR_C);
51        }
52        _ => {
53            encode_ty_options.remove(EncodeTyOptions::GENERALIZE_REPR_C);
54        }
55    }
56
57    // Encode the return type
58    let transform_ty_options = TransformTyOptions::from_bits(options.bits())
59        .unwrap_or_else(|| bug!("typeid_for_fnabi: invalid option(s) `{:?}`", options.bits()));
60    let mut type_folder = TransformTy::new(tcx, transform_ty_options);
61    let ty = fn_abi.ret.layout.ty.fold_with(&mut type_folder);
62    typeid.push_str(&encode_ty(tcx, ty, &mut dict, encode_ty_options));
63
64    // Encode the parameter types
65
66    // We erase ZSTs as we go if the argument is skipped. This is an implementation detail of how
67    // MIR is currently treated by rustc, and subject to change in the future. Specifically, MIR
68    // interpretation today will allow skipped arguments to simply not be passed at a call-site.
69    if !fn_abi.c_variadic {
70        let mut pushed_arg = false;
71        for arg in fn_abi.args.iter().filter(|arg| arg.mode != PassMode::Ignore) {
72            pushed_arg = true;
73            let ty = arg.layout.ty.fold_with(&mut type_folder);
74            typeid.push_str(&encode_ty(tcx, ty, &mut dict, encode_ty_options));
75        }
76        if !pushed_arg {
77            // Empty parameter lists, whether declared as () or conventionally as (void), are
78            // encoded with a void parameter specifier "v".
79            typeid.push('v');
80        }
81    } else {
82        for n in 0..fn_abi.fixed_count as usize {
83            if fn_abi.args[n].mode == PassMode::Ignore {
84                continue;
85            }
86            let ty = fn_abi.args[n].layout.ty.fold_with(&mut type_folder);
87            typeid.push_str(&encode_ty(tcx, ty, &mut dict, encode_ty_options));
88        }
89
90        typeid.push('z');
91    }
92
93    // Close the "F..E" pair
94    typeid.push('E');
95
96    // Add encoding suffixes
97    if options.contains(EncodeTyOptions::NORMALIZE_INTEGERS) {
98        typeid.push_str(".normalized");
99    }
100
101    if options.contains(EncodeTyOptions::GENERALIZE_POINTERS) {
102        typeid.push_str(".generalized");
103    }
104
105    typeid
106}
107
108/// Returns a type metadata identifier for the specified Instance using the Itanium C++ ABI with
109/// vendor extended type qualifiers and types for Rust types that are not used at the FFI boundary.
110#[allow(clippy :: suspicious_else_formatting)]
{
    let __tracing_attr_span;
    let __tracing_attr_guard;
    if ::tracing::Level::TRACE <= ::tracing::level_filters::STATIC_MAX_LEVEL
                &&
                ::tracing::Level::TRACE <=
                    ::tracing::level_filters::LevelFilter::current() ||
            { false } {
        __tracing_attr_span =
            {
                use ::tracing::__macro_support::Callsite as _;
                static __CALLSITE: ::tracing::callsite::DefaultCallsite =
                    {
                        static META: ::tracing::Metadata<'static> =
                            {
                                ::tracing_core::metadata::Metadata::new("typeid_for_instance",
                                    "rustc_sanitizers::cfi::typeid::itanium_cxx_abi",
                                    ::tracing::Level::TRACE,
                                    ::tracing_core::__macro_support::Option::Some("compiler/rustc_sanitizers/src/cfi/typeid/itanium_cxx_abi/mod.rs"),
                                    ::tracing_core::__macro_support::Option::Some(110u32),
                                    ::tracing_core::__macro_support::Option::Some("rustc_sanitizers::cfi::typeid::itanium_cxx_abi"),
                                    ::tracing_core::field::FieldSet::new(&["instance",
                                                    "options"],
                                        ::tracing_core::callsite::Identifier(&__CALLSITE)),
                                    ::tracing::metadata::Kind::SPAN)
                            };
                        ::tracing::callsite::DefaultCallsite::new(&META)
                    };
                let mut interest = ::tracing::subscriber::Interest::never();
                if ::tracing::Level::TRACE <=
                                    ::tracing::level_filters::STATIC_MAX_LEVEL &&
                                ::tracing::Level::TRACE <=
                                    ::tracing::level_filters::LevelFilter::current() &&
                            { interest = __CALLSITE.interest(); !interest.is_never() }
                        &&
                        ::tracing::__macro_support::__is_enabled(__CALLSITE.metadata(),
                            interest) {
                    let meta = __CALLSITE.metadata();
                    ::tracing::Span::new(meta,
                        &{
                                #[allow(unused_imports)]
                                use ::tracing::field::{debug, display, Value};
                                let mut iter = meta.fields().iter();
                                meta.fields().value_set(&[(&::tracing::__macro_support::Iterator::next(&mut iter).expect("FieldSet corrupted (this is a bug)"),
                                                    ::tracing::__macro_support::Option::Some(&::tracing::field::debug(&instance)
                                                            as &dyn Value)),
                                                (&::tracing::__macro_support::Iterator::next(&mut iter).expect("FieldSet corrupted (this is a bug)"),
                                                    ::tracing::__macro_support::Option::Some(&::tracing::field::debug(&options)
                                                            as &dyn Value))])
                            })
                } else {
                    let span =
                        ::tracing::__macro_support::__disabled_span(__CALLSITE.metadata());
                    {};
                    span
                }
            };
        __tracing_attr_guard = __tracing_attr_span.enter();
    }

    #[warn(clippy :: suspicious_else_formatting)]
    {

        #[allow(unknown_lints, unreachable_code, clippy ::
        diverging_sub_expression, clippy :: empty_loop, clippy ::
        let_unit_value, clippy :: let_with_type_underscore, clippy ::
        needless_return, clippy :: unreachable)]
        if false {
            let __tracing_attr_fake_return: String = loop {};
            return __tracing_attr_fake_return;
        }
        {
            if !!instance.has_non_region_param() {
                {
                    ::core::panicking::panic_fmt(format_args!("{0:#?} must be fully monomorphic",
                            instance));
                }
            };
            let transform_ty_options =
                TransformTyOptions::from_bits(options.bits()).unwrap_or_else(||
                        ::rustc_middle::util::bug::bug_fmt(format_args!("typeid_for_instance: invalid option(s) `{0:?}`",
                                options.bits())));
            let instance =
                transform_instance(tcx, instance, transform_ty_options);
            let fn_abi =
                tcx.fn_abi_of_instance(ty::TypingEnv::fully_monomorphized().as_query_input((instance,
                                ty::List::empty()))).unwrap_or_else(|error|
                        {
                            ::rustc_middle::util::bug::bug_fmt(format_args!("typeid_for_instance: couldn\'t get fn_abi of instance {0:?}: {1:?}",
                                    instance, error))
                        });
            typeid_for_fnabi(tcx, fn_abi, options)
        }
    }
}#[instrument(level = "trace", skip(tcx))]
111pub fn typeid_for_instance<'tcx>(
112    tcx: TyCtxt<'tcx>,
113    instance: Instance<'tcx>,
114    options: TypeIdOptions,
115) -> String {
116    assert!(!instance.has_non_region_param(), "{instance:#?} must be fully monomorphic");
117    let transform_ty_options = TransformTyOptions::from_bits(options.bits())
118        .unwrap_or_else(|| bug!("typeid_for_instance: invalid option(s) `{:?}`", options.bits()));
119    let instance = transform_instance(tcx, instance, transform_ty_options);
120    let fn_abi = tcx
121        .fn_abi_of_instance(
122            ty::TypingEnv::fully_monomorphized().as_query_input((instance, ty::List::empty())),
123        )
124        .unwrap_or_else(|error| {
125            bug!("typeid_for_instance: couldn't get fn_abi of instance {instance:?}: {error:?}")
126        });
127    typeid_for_fnabi(tcx, fn_abi, options)
128}