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_data_structures::fx::FxHashMap;
8use rustc_middle::bug;
9use rustc_middle::ty::{self, Instance, Ty, TyCtxt, TypeFoldable, TypeVisitableExt};
10use rustc_target::callconv::{Conv, FnAbi, PassMode};
11use tracing::instrument;
12
13mod encode;
14mod transform;
15use crate::cfi::typeid::TypeIdOptions;
16use crate::cfi::typeid::itanium_cxx_abi::encode::{DictKey, EncodeTyOptions, encode_ty};
17use crate::cfi::typeid::itanium_cxx_abi::transform::{
18    TransformTy, TransformTyOptions, transform_instance,
19};
20
21/// Returns a type metadata identifier for the specified FnAbi using the Itanium C++ ABI with vendor
22/// extended type qualifiers and types for Rust types that are not used at the FFI boundary.
23#[instrument(level = "trace", skip(tcx))]
24pub fn typeid_for_fnabi<'tcx>(
25    tcx: TyCtxt<'tcx>,
26    fn_abi: &FnAbi<'tcx, Ty<'tcx>>,
27    options: TypeIdOptions,
28) -> String {
29    // A name is mangled by prefixing "_Z" to an encoding of its name, and in the case of functions
30    // its type.
31    let mut typeid = String::from("_Z");
32
33    // Clang uses the Itanium C++ ABI's virtual tables and RTTI typeinfo structure name as type
34    // metadata identifiers for function pointers. The typeinfo name encoding is a two-character
35    // code (i.e., 'TS') prefixed to the type encoding for the function.
36    typeid.push_str("TS");
37
38    // Function types are delimited by an "F..E" pair
39    typeid.push('F');
40
41    // A dictionary of substitution candidates used for compression (see
42    // https://itanium-cxx-abi.github.io/cxx-abi/abi.html#mangling-compression).
43    let mut dict: FxHashMap<DictKey<'tcx>, usize> = FxHashMap::default();
44
45    let mut encode_ty_options = EncodeTyOptions::from_bits(options.bits())
46        .unwrap_or_else(|| bug!("typeid_for_fnabi: invalid option(s) `{:?}`", options.bits()));
47    match fn_abi.conv {
48        Conv::C => {
49            encode_ty_options.insert(EncodeTyOptions::GENERALIZE_REPR_C);
50        }
51        _ => {
52            encode_ty_options.remove(EncodeTyOptions::GENERALIZE_REPR_C);
53        }
54    }
55
56    // Encode the return type
57    let transform_ty_options = TransformTyOptions::from_bits(options.bits())
58        .unwrap_or_else(|| bug!("typeid_for_fnabi: invalid option(s) `{:?}`", options.bits()));
59    let mut type_folder = TransformTy::new(tcx, transform_ty_options);
60    let ty = fn_abi.ret.layout.ty.fold_with(&mut type_folder);
61    typeid.push_str(&encode_ty(tcx, ty, &mut dict, encode_ty_options));
62
63    // Encode the parameter types
64
65    // We erase ZSTs as we go if the argument is skipped. This is an implementation detail of how
66    // MIR is currently treated by rustc, and subject to change in the future. Specifically, MIR
67    // interpretation today will allow skipped arguments to simply not be passed at a call-site.
68    if !fn_abi.c_variadic {
69        let mut pushed_arg = false;
70        for arg in fn_abi.args.iter().filter(|arg| arg.mode != PassMode::Ignore) {
71            pushed_arg = true;
72            let ty = arg.layout.ty.fold_with(&mut type_folder);
73            typeid.push_str(&encode_ty(tcx, ty, &mut dict, encode_ty_options));
74        }
75        if !pushed_arg {
76            // Empty parameter lists, whether declared as () or conventionally as (void), are
77            // encoded with a void parameter specifier "v".
78            typeid.push('v');
79        }
80    } else {
81        for n in 0..fn_abi.fixed_count as usize {
82            if fn_abi.args[n].mode == PassMode::Ignore {
83                continue;
84            }
85            let ty = fn_abi.args[n].layout.ty.fold_with(&mut type_folder);
86            typeid.push_str(&encode_ty(tcx, ty, &mut dict, encode_ty_options));
87        }
88
89        typeid.push('z');
90    }
91
92    // Close the "F..E" pair
93    typeid.push('E');
94
95    // Add encoding suffixes
96    if options.contains(EncodeTyOptions::NORMALIZE_INTEGERS) {
97        typeid.push_str(".normalized");
98    }
99
100    if options.contains(EncodeTyOptions::GENERALIZE_POINTERS) {
101        typeid.push_str(".generalized");
102    }
103
104    typeid
105}
106
107/// Returns a type metadata identifier for the specified Instance using the Itanium C++ ABI with
108/// vendor extended type qualifiers and types for Rust types that are not used at the FFI boundary.
109#[instrument(level = "trace", skip(tcx))]
110pub fn typeid_for_instance<'tcx>(
111    tcx: TyCtxt<'tcx>,
112    instance: Instance<'tcx>,
113    options: TypeIdOptions,
114) -> String {
115    assert!(!instance.has_non_region_param(), "{instance:#?} must be fully monomorphic");
116    let transform_ty_options = TransformTyOptions::from_bits(options.bits())
117        .unwrap_or_else(|| bug!("typeid_for_instance: invalid option(s) `{:?}`", options.bits()));
118    let instance = transform_instance(tcx, instance, transform_ty_options);
119    let fn_abi = tcx
120        .fn_abi_of_instance(
121            ty::TypingEnv::fully_monomorphized().as_query_input((instance, ty::List::empty())),
122        )
123        .unwrap_or_else(|error| {
124            bug!("typeid_for_instance: couldn't get fn_abi of instance {instance:?}: {error:?}")
125        });
126    typeid_for_fnabi(tcx, fn_abi, options)
127}