@@ -3,6 +3,7 @@ use std::ops::Range;
33use rustc_abi:: { Align , ExternAbi , HasDataLayout , Primitive , Scalar , Size , WrappingRange } ;
44use rustc_codegen_ssa:: common;
55use rustc_codegen_ssa:: traits:: * ;
6+ use rustc_data_structures:: fx:: FxHashMap ;
67use rustc_hir:: LangItem ;
78use rustc_hir:: attrs:: Linkage ;
89use rustc_hir:: def:: DefKind ;
@@ -13,8 +14,9 @@ use rustc_middle::mir::interpret::{
1314 read_target_uint,
1415} ;
1516use rustc_middle:: mono:: MonoItem ;
17+ use rustc_middle:: ptrauth:: ptrauth_compute_fn_ptr_type_discriminator_for;
1618use rustc_middle:: ty:: layout:: { HasTypingEnv , LayoutOf } ;
17- use rustc_middle:: ty:: { self , Instance } ;
19+ use rustc_middle:: ty:: { self , Instance , Ty , TyCtxt } ;
1820use rustc_middle:: { bug, span_bug} ;
1921use rustc_span:: Symbol ;
2022use rustc_target:: spec:: Arch ;
@@ -37,11 +39,135 @@ pub(crate) enum IsInitOrFini {
3739 Yes ,
3840 No ,
3941}
42+
43+ /// Recursively walks a type layout and records the offsets of all extern "C"
44+ /// function pointer fields together with their computed type discriminators.
45+ ///
46+ /// Traversal currently supports:
47+ /// - references
48+ /// - direct function pointers
49+ /// - structs
50+ /// - tuples
51+ /// - arrays
52+ ///
53+ /// Offsets are accumulated relative to the containing object.
54+ fn collect_fn_ptr_discriminators < ' tcx > (
55+ tcx : TyCtxt < ' tcx > ,
56+ typing_env : ty:: TypingEnv < ' tcx > ,
57+ ty : Ty < ' tcx > ,
58+ ) -> FxHashMap < Size , u64 > {
59+ let mut map = FxHashMap :: default ( ) ;
60+
61+ collect_fn_ptr_discriminators_inner ( tcx, typing_env, ty, Size :: ZERO , & mut map) ;
62+
63+ map
64+ }
65+
66+ fn collect_fn_ptr_discriminators_inner < ' tcx > (
67+ tcx : TyCtxt < ' tcx > ,
68+ typing_env : ty:: TypingEnv < ' tcx > ,
69+ ty : Ty < ' tcx > ,
70+ base_offset : Size ,
71+ map : & mut FxHashMap < Size , u64 > ,
72+ ) {
73+ // Direct function pointer.
74+ if let Some ( disc) = ptrauth_compute_fn_ptr_type_discriminator_for ( tcx, ty) {
75+ map. insert ( base_offset, disc. into ( ) ) ;
76+
77+ return ;
78+ }
79+
80+ match ty. kind ( ) {
81+ ty:: Ref ( _, pointee, _) => {
82+ collect_fn_ptr_discriminators_inner ( tcx, typing_env, * pointee, base_offset, map) ;
83+ }
84+ ty:: Adt ( def, args) if def. is_struct ( ) => {
85+ let Ok ( layout) = tcx. layout_of ( typing_env. as_query_input ( ty) ) else {
86+ return ;
87+ } ;
88+
89+ let variant = def. non_enum_variant ( ) ;
90+
91+ for ( idx, field_def) in variant. fields . iter_enumerated ( ) {
92+ let field_ty = tcx. normalize_erasing_regions ( typing_env, field_def. ty ( tcx, args) ) ;
93+
94+ let field_offset = layout. fields . offset ( idx. into ( ) ) ;
95+
96+ collect_fn_ptr_discriminators_inner (
97+ tcx,
98+ typing_env,
99+ field_ty,
100+ base_offset + field_offset,
101+ map,
102+ ) ;
103+ }
104+ }
105+ ty:: Tuple ( fields) => {
106+ let Ok ( layout) = tcx. layout_of ( typing_env. as_query_input ( ty) ) else {
107+ return ;
108+ } ;
109+
110+ for ( idx, field_ty) in fields. iter ( ) . enumerate ( ) {
111+ let field_offset = layout. fields . offset ( idx) ;
112+
113+ collect_fn_ptr_discriminators_inner (
114+ tcx,
115+ typing_env,
116+ field_ty,
117+ base_offset + field_offset,
118+ map,
119+ ) ;
120+ }
121+ }
122+ ty:: Array ( elem_ty, len) => {
123+ let count = match len. try_to_target_usize ( tcx) {
124+ Some ( v) => v,
125+ None => return ,
126+ } ;
127+
128+ let Ok ( elem_layout) = tcx. layout_of ( typing_env. as_query_input ( * elem_ty) ) else {
129+ return ;
130+ } ;
131+
132+ let stride = elem_layout. size ;
133+
134+ // Collect discriminator of one element, so we don't have to recompute it for all the
135+ // elements in the array.
136+ let mut elem_map = FxHashMap :: default ( ) ;
137+
138+ collect_fn_ptr_discriminators_inner (
139+ tcx,
140+ typing_env,
141+ * elem_ty,
142+ Size :: ZERO ,
143+ & mut elem_map,
144+ ) ;
145+
146+ // SAFETY: We immediately collect into a Vec and sort by offset.
147+ // The HashMap iteration order is irrelevant and must not affect determinism.
148+ #[ allow( rustc:: potential_query_instability) ]
149+ let mut entries: Vec < ( Size , u64 ) > = elem_map. into_iter ( ) . collect ( ) ;
150+ entries. sort_unstable_by_key ( |( offset, _) | * offset) ;
151+
152+ // Replicate for every array slot.
153+ for i in 0 ..count {
154+ let elem_base = base_offset + stride * i;
155+
156+ for ( inner_offset, discr) in entries. iter ( ) . copied ( ) {
157+ map. insert ( elem_base + inner_offset, discr) ;
158+ }
159+ }
160+ }
161+ _ => { }
162+ }
163+ }
164+
40165pub ( crate ) fn const_alloc_to_llvm < ' ll > (
41166 cx : & CodegenCx < ' ll , ' _ > ,
42167 alloc : & Allocation ,
43168 is_static : IsStatic ,
44169 is_init_fini : IsInitOrFini ,
170+ ptrauth_discriminators : Option < & FxHashMap < Size , u64 > > ,
45171) -> & ' ll Value {
46172 // We expect that callers of const_alloc_to_llvm will instead directly codegen a pointer or
47173 // integer for any &ZST where the ZST is a constant (i.e. not a static). We should never be
@@ -121,14 +247,24 @@ pub(crate) fn const_alloc_to_llvm<'ll>(
121247 as u64 ;
122248
123249 let address_space = cx. tcx . global_alloc ( prov. alloc_id ( ) ) . address_space ( cx) ;
124- let schema = if cx. sess ( ) . pointer_authentication ( ) {
250+ let mut schema = if cx. sess ( ) . pointer_authentication ( ) {
125251 match is_init_fini {
126252 IsInitOrFini :: Yes => cx. sess ( ) . pointer_authentication_init_fini ( ) ,
127253 IsInitOrFini :: No => cx. sess ( ) . pointer_authentication_functions ( ) ,
128254 }
129255 } else {
130256 None
131257 } ;
258+ let discr =
259+ ptrauth_discriminators. as_ref ( ) . and_then ( |m| m. get ( & Size :: from_bytes ( offset as u64 ) ) ) ;
260+
261+ // Init/fini entries must not participate in function pointer type discrimination, they use
262+ // a dedicated constant value (ptrauth_string_discriminator("init_fini") which is: 0xd9d4).
263+ if let ( Some ( schema) , Some ( discr) ) = ( schema. as_mut ( ) , discr)
264+ && is_init_fini == IsInitOrFini :: No
265+ {
266+ schema. constant_discriminator = * discr as u16 ;
267+ }
132268 llvals. push ( cx. scalar_to_backend_with_pac (
133269 InterpScalar :: from_pointer ( Pointer :: new ( prov, Size :: from_bytes ( ptr_offset) ) , & cx. tcx ) ,
134270 Scalar :: Initialized {
@@ -137,6 +273,7 @@ pub(crate) fn const_alloc_to_llvm<'ll>(
137273 } ,
138274 cx. type_ptr_ext ( address_space) ,
139275 schema,
276+ ptrauth_discriminators,
140277 ) ) ;
141278 next_offset = offset + pointer_size_bytes;
142279 }
@@ -160,6 +297,15 @@ fn codegen_static_initializer<'ll, 'tcx>(
160297 cx : & CodegenCx < ' ll , ' tcx > ,
161298 def_id : DefId ,
162299) -> Result < ( & ' ll Value , ConstAllocation < ' tcx > ) , ErrorHandled > {
300+ let ptrauth_discriminators = if cx. sess ( ) . pointer_authentication_fn_ptr_type_discrimination ( ) {
301+ let instance = Instance :: mono ( cx. tcx , def_id) ;
302+ let ty = instance. ty ( cx. tcx , cx. typing_env ( ) ) ;
303+
304+ Some ( collect_fn_ptr_discriminators ( cx. tcx , cx. typing_env ( ) , ty) )
305+ } else {
306+ None
307+ } ;
308+
163309 let alloc = cx. tcx . eval_static_initializer ( def_id) ?;
164310 let attrs = cx. tcx . codegen_fn_attrs ( def_id) ;
165311 // FIXME(jchlanda) Decide if this could be better served by `ctor` crate. See the discussion
@@ -175,7 +321,16 @@ fn codegen_static_initializer<'ll, 'tcx>(
175321 }
176322 } )
177323 . unwrap_or ( IsInitOrFini :: No ) ;
178- Ok ( ( const_alloc_to_llvm ( cx, alloc. inner ( ) , IsStatic :: Yes , is_in_init_fini) , alloc) )
324+ Ok ( (
325+ const_alloc_to_llvm (
326+ cx,
327+ alloc. inner ( ) ,
328+ IsStatic :: Yes ,
329+ is_in_init_fini,
330+ ptrauth_discriminators. as_ref ( ) ,
331+ ) ,
332+ alloc,
333+ ) )
179334}
180335
181336fn set_global_alignment < ' ll > ( cx : & CodegenCx < ' ll , ' _ > , gv : & ' ll Value , mut align : Align ) {
@@ -837,7 +992,13 @@ impl<'ll> StaticCodegenMethods for CodegenCx<'ll, '_> {
837992 fn static_addr_of ( & self , alloc : ConstAllocation < ' _ > , kind : Option < & str > ) -> & ' ll Value {
838993 // FIXME: should we cache `const_alloc_to_llvm` to avoid repeating this for the
839994 // same `ConstAllocation`?
840- let cv = const_alloc_to_llvm ( self , alloc. inner ( ) , IsStatic :: No , IsInitOrFini :: No ) ;
995+ // FIXME(jchlanda): Add support for pointer authentication type discrimination.
996+ // `static_addr_of` only receives a `ConstAllocation`, so it does not have the type
997+ // information needed to compute function pointer type discriminators. We'll likely need
998+ // to either compute the discriminator map at callers that still know the Rust type, or
999+ // extend this API to accept the required type information. See
1000+ // `codegen_static_initializer` for an example of how the discriminator map is computed.
1001+ let cv = const_alloc_to_llvm ( self , alloc. inner ( ) , IsStatic :: No , IsInitOrFini :: No , None ) ;
8411002
8421003 let gv = self . static_addr_of_impl ( cv, alloc. inner ( ) . align , kind) ;
8431004 // static_addr_of_impl returns the bare global variable, which might not be in the default
0 commit comments