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#[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#[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}