@@ -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:: 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,141 @@ pub(crate) enum IsInitOrFini {
3739 Yes ,
3840 No ,
3941}
42+
43+ /// Maps offsets within a static allocation to the function pointer
44+ /// discriminator that should be applied when authenticating the relocation
45+ /// emitted at that offset.
46+ ///
47+ /// Offsets are relative to the beginning of the allocation.
48+ pub ( crate ) struct FnPtrDiscriminatorsAtOffset {
49+ map : FxHashMap < Size , u64 > ,
50+ }
51+
52+ /// Recursively walks a type layout and records the offsets of all extern "C"
53+ /// function pointer fields together with their computed type discriminators.
54+ ///
55+ /// Traversal currently supports:
56+ /// - direct function pointers
57+ /// - transparent wrappers
58+ /// - structs
59+ /// - tuples
60+ /// - arrays
61+ ///
62+ /// Offsets are accumulated relative to the containing object.
63+ fn collect_fn_ptr_discriminators < ' tcx > (
64+ tcx : TyCtxt < ' tcx > ,
65+ typing_env : ty:: TypingEnv < ' tcx > ,
66+ ty : Ty < ' tcx > ,
67+ ) -> FnPtrDiscriminatorsAtOffset {
68+ let mut map = FxHashMap :: default ( ) ;
69+
70+ collect_fn_ptr_discriminators_inner ( tcx, typing_env, ty, Size :: ZERO , & mut map) ;
71+
72+ FnPtrDiscriminatorsAtOffset { map }
73+ }
74+
75+ fn collect_fn_ptr_discriminators_inner < ' tcx > (
76+ tcx : TyCtxt < ' tcx > ,
77+ typing_env : ty:: TypingEnv < ' tcx > ,
78+ ty : Ty < ' tcx > ,
79+ base_offset : Size ,
80+ map : & mut FxHashMap < Size , u64 > ,
81+ ) {
82+ // Direct function pointer.
83+ if let Some ( disc) = compute_fn_ptr_type_discriminator_for ( tcx, ty) {
84+ map. insert ( base_offset, disc. into ( ) ) ;
85+
86+ return ;
87+ }
88+
89+ match ty. kind ( ) {
90+ ty:: Adt ( def, args) if def. is_struct ( ) => {
91+ let Ok ( layout) = tcx. layout_of ( typing_env. as_query_input ( ty) ) else {
92+ return ;
93+ } ;
94+
95+ let variant = def. non_enum_variant ( ) ;
96+
97+ for ( idx, field_def) in variant. fields . iter_enumerated ( ) {
98+ let field_ty = tcx. normalize_erasing_regions ( typing_env, field_def. ty ( tcx, args) ) ;
99+
100+ let field_offset = layout. fields . offset ( idx. into ( ) ) ;
101+
102+ collect_fn_ptr_discriminators_inner (
103+ tcx,
104+ typing_env,
105+ field_ty,
106+ base_offset + field_offset,
107+ map,
108+ ) ;
109+ }
110+ }
111+ ty:: Tuple ( fields) => {
112+ let Ok ( layout) = tcx. layout_of ( typing_env. as_query_input ( ty) ) else {
113+ return ;
114+ } ;
115+
116+ for ( idx, field_ty) in fields. iter ( ) . enumerate ( ) {
117+ let field_offset = layout. fields . offset ( idx) ;
118+
119+ collect_fn_ptr_discriminators_inner (
120+ tcx,
121+ typing_env,
122+ field_ty,
123+ base_offset + field_offset,
124+ map,
125+ ) ;
126+ }
127+ }
128+ ty:: Array ( elem_ty, len) => {
129+ let count = match len. try_to_target_usize ( tcx) {
130+ Some ( v) => v,
131+ None => return ,
132+ } ;
133+
134+ let Ok ( elem_layout) = tcx. layout_of ( typing_env. as_query_input ( * elem_ty) ) else {
135+ return ;
136+ } ;
137+
138+ let stride = elem_layout. size ;
139+
140+ // Collect discriminator of one element, so we don't have to recompute it for all the
141+ // elements in the array.
142+ let mut elem_map = FxHashMap :: default ( ) ;
143+
144+ collect_fn_ptr_discriminators_inner (
145+ tcx,
146+ typing_env,
147+ * elem_ty,
148+ Size :: ZERO ,
149+ & mut elem_map,
150+ ) ;
151+
152+ // SAFETY: We immediately collect into a Vec and sort by offset.
153+ // The HashMap iteration order is irrelevant and must not affect determinism.
154+ #[ allow( rustc:: potential_query_instability) ]
155+ let mut entries: Vec < ( Size , u64 ) > = elem_map. into_iter ( ) . collect ( ) ;
156+ entries. sort_unstable_by_key ( |( offset, _) | * offset) ;
157+
158+ // Replicate for every array slot.
159+ for i in 0 ..count {
160+ let elem_base = base_offset + stride * i;
161+
162+ for ( inner_offset, discr) in entries. iter ( ) . copied ( ) {
163+ map. insert ( elem_base + inner_offset, discr) ;
164+ }
165+ }
166+ }
167+ _ => { }
168+ }
169+ }
170+
40171pub ( crate ) fn const_alloc_to_llvm < ' ll > (
41172 cx : & CodegenCx < ' ll , ' _ > ,
42173 alloc : & Allocation ,
43174 is_static : IsStatic ,
44175 is_init_fini : IsInitOrFini ,
176+ fn_ptr_discriminators : Option < & FnPtrDiscriminatorsAtOffset > ,
45177) -> & ' ll Value {
46178 // We expect that callers of const_alloc_to_llvm will instead directly codegen a pointer or
47179 // integer for any &ZST where the ZST is a constant (i.e. not a static). We should never be
@@ -121,14 +253,24 @@ pub(crate) fn const_alloc_to_llvm<'ll>(
121253 as u64 ;
122254
123255 let address_space = cx. tcx . global_alloc ( prov. alloc_id ( ) ) . address_space ( cx) ;
124- let schema = if cx. sess ( ) . pointer_authentication ( ) {
256+ let mut schema = if cx. sess ( ) . pointer_authentication ( ) {
125257 match is_init_fini {
126258 IsInitOrFini :: Yes => cx. sess ( ) . pointer_authentication_init_fini ( ) ,
127259 IsInitOrFini :: No => cx. sess ( ) . pointer_authentication_functions ( ) ,
128260 }
129261 } else {
130262 None
131263 } ;
264+ let discr = fn_ptr_discriminators
265+ . as_ref ( )
266+ . and_then ( |m| m. map . get ( & Size :: from_bytes ( offset as u64 ) ) ) ;
267+
268+ // Init/fini entries must not participate in function pointer type discrimination.
269+ if let ( Some ( schema) , Some ( discr) ) = ( schema. as_mut ( ) , discr)
270+ && is_init_fini == IsInitOrFini :: No
271+ {
272+ schema. constant_discriminator = * discr as u16 ;
273+ }
132274 llvals. push ( cx. scalar_to_backend_with_pac (
133275 InterpScalar :: from_pointer ( Pointer :: new ( prov, Size :: from_bytes ( ptr_offset) ) , & cx. tcx ) ,
134276 Scalar :: Initialized {
@@ -160,6 +302,15 @@ fn codegen_static_initializer<'ll, 'tcx>(
160302 cx : & CodegenCx < ' ll , ' tcx > ,
161303 def_id : DefId ,
162304) -> Result < ( & ' ll Value , ConstAllocation < ' tcx > ) , ErrorHandled > {
305+ let fn_ptr_discriminators = if cx. sess ( ) . pointer_authentication_fn_ptr_type_discrimination ( ) {
306+ let instance = Instance :: mono ( cx. tcx , def_id) ;
307+ let ty = instance. ty ( cx. tcx , cx. typing_env ( ) ) ;
308+
309+ Some ( collect_fn_ptr_discriminators ( cx. tcx , cx. typing_env ( ) , ty) )
310+ } else {
311+ None
312+ } ;
313+
163314 let alloc = cx. tcx . eval_static_initializer ( def_id) ?;
164315 let attrs = cx. tcx . codegen_fn_attrs ( def_id) ;
165316 // FIXME(jchlanda) Decide if this could be better served by `ctor` crate. See the discussion
@@ -175,7 +326,16 @@ fn codegen_static_initializer<'ll, 'tcx>(
175326 }
176327 } )
177328 . unwrap_or ( IsInitOrFini :: No ) ;
178- Ok ( ( const_alloc_to_llvm ( cx, alloc. inner ( ) , IsStatic :: Yes , is_in_init_fini) , alloc) )
329+ Ok ( (
330+ const_alloc_to_llvm (
331+ cx,
332+ alloc. inner ( ) ,
333+ IsStatic :: Yes ,
334+ is_in_init_fini,
335+ fn_ptr_discriminators. as_ref ( ) ,
336+ ) ,
337+ alloc,
338+ ) )
179339}
180340
181341fn set_global_alignment < ' ll > ( cx : & CodegenCx < ' ll , ' _ > , gv : & ' ll Value , mut align : Align ) {
@@ -837,7 +997,7 @@ impl<'ll> StaticCodegenMethods for CodegenCx<'ll, '_> {
837997 fn static_addr_of ( & self , alloc : ConstAllocation < ' _ > , kind : Option < & str > ) -> & ' ll Value {
838998 // FIXME: should we cache `const_alloc_to_llvm` to avoid repeating this for the
839999 // same `ConstAllocation`?
840- let cv = const_alloc_to_llvm ( self , alloc. inner ( ) , IsStatic :: No , IsInitOrFini :: No ) ;
1000+ let cv = const_alloc_to_llvm ( self , alloc. inner ( ) , IsStatic :: No , IsInitOrFini :: No , None ) ;
8411001
8421002 let gv = self . static_addr_of_impl ( cv, alloc. inner ( ) . align , kind) ;
8431003 // static_addr_of_impl returns the bare global variable, which might not be in the default
0 commit comments