Line data Source code
1 : use super::Command;
2 : use crate::common::FromFFI;
3 : use crate::{declare_iterator, to_slice};
4 : use lief_ffi as ffi;
5 : use std::marker::PhantomData;
6 :
7 : /// Class representing the `LC_FUNCTION_VARIANTS` load command.
8 : ///
9 : /// Introduced publicly in `dyld-1284.13` (April 2025), this command supports
10 : /// **function multiversioning**, the ability to associate multiple implementations
11 : /// of the same function, each optimized for a specific platform, architecture,
12 : /// or runtime context.
13 : ///
14 : /// At runtime, the system dispatches the most appropriate variant based on
15 : /// hardware capabilities or execution environment.
16 : ///
17 : /// For example:
18 : ///
19 : /// ```cpp
20 : /// FUNCTION_VARIANT_TABLE(my_function,
21 : /// { (void*)my_function$Rosetta, "rosetta" }, // Rosetta translation
22 : /// { (void*)my_function$Haswell, "haswell" }, // Haswell-optimized
23 : /// { (void*)my_function$Base, "default" } // Default fallback
24 : /// );
25 : /// ```
26 : pub struct FunctionVariants<'a> {
27 : ptr: cxx::UniquePtr<ffi::MachO_FunctionVariants>,
28 : _owner: PhantomData<&'a ffi::MachO_Binary>,
29 : }
30 :
31 : impl FunctionVariants<'_> {
32 : /// Offset in the `__LINKEDIT` segment where the payload is located
33 24 : pub fn data_offset(&self) -> u32 {
34 24 : self.ptr.data_offset()
35 24 : }
36 :
37 : /// Size of the payload
38 24 : pub fn data_size(&self) -> u32 {
39 24 : self.ptr.data_size()
40 24 : }
41 :
42 : /// Raw payload as a slice of bytes
43 0 : pub fn content(&self) -> &[u8] {
44 0 : to_slice!(self.ptr.content());
45 0 : }
46 :
47 : /// Iterator over the different [`RuntimeTable`] entries located in the content
48 : /// of this `__LINKEDIT` command
49 12 : pub fn runtime_table(&self) -> RuntimeTables {
50 12 : RuntimeTables::new(self.ptr.runtime_table())
51 12 : }
52 : }
53 :
54 : impl std::fmt::Debug for FunctionVariants<'_> {
55 24 : fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
56 24 : let base = self as &dyn Command;
57 24 : f.debug_struct("FunctionVariants")
58 24 : .field("base", &base)
59 24 : .field("data_offset", &self.data_offset())
60 24 : .field("data_size", &self.data_size())
61 24 : .finish()
62 24 : }
63 : }
64 :
65 : impl FromFFI<ffi::MachO_FunctionVariants> for FunctionVariants<'_> {
66 12 : fn from_ffi(cmd: cxx::UniquePtr<ffi::MachO_FunctionVariants>) -> Self {
67 12 : Self {
68 12 : ptr: cmd,
69 12 : _owner: PhantomData,
70 12 : }
71 12 : }
72 : }
73 :
74 : impl Command for FunctionVariants<'_> {
75 96 : fn get_base(&self) -> &ffi::MachO_Command {
76 96 : self.ptr.as_ref().unwrap().as_ref()
77 96 : }
78 : }
79 :
80 : /// Represents a runtime table of function variants sharing a common namespace
81 : /// (referred to internally as `FunctionVariantsRuntimeTable` in `dyld`).
82 : ///
83 : /// Each table holds multiple [`RuntimeTableEntry`] instances that map to
84 : /// function implementations optimized for a given [`Kind`].
85 : pub struct RuntimeTable<'a> {
86 : ptr: cxx::UniquePtr<ffi::MachO_FunctionVariants_RuntimeTable>,
87 : _owner: PhantomData<&'a ffi::MachO_FunctionVariants>,
88 : }
89 :
90 : impl FromFFI<ffi::MachO_FunctionVariants_RuntimeTable> for RuntimeTable<'_> {
91 24 : fn from_ffi(cmd: cxx::UniquePtr<ffi::MachO_FunctionVariants_RuntimeTable>) -> Self {
92 24 : Self {
93 24 : ptr: cmd,
94 24 : _owner: PhantomData,
95 24 : }
96 24 : }
97 : }
98 :
99 : impl RuntimeTable<'_> {
100 : /// Kind of this runtime table
101 24 : pub fn kind(&self) -> Kind {
102 24 : Kind::from(self.ptr.kind())
103 24 : }
104 :
105 : /// Original offset in the payload
106 24 : pub fn offset(&self) -> u32 {
107 24 : self.ptr.offset()
108 24 : }
109 :
110 : /// Iterator over the different [`RuntimeTableEntry`] entries
111 24 : pub fn entries(&self) -> RuntimeTableEntries {
112 24 : RuntimeTableEntries::new(self.ptr.entries())
113 24 : }
114 : }
115 :
116 : impl std::fmt::Debug for RuntimeTable<'_> {
117 24 : fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
118 24 : f.debug_struct("RuntimeTable")
119 24 : .field("kind", &self.kind())
120 24 : .field("offset", &self.offset())
121 24 : .finish()
122 24 : }
123 : }
124 :
125 : impl std::fmt::Display for RuntimeTable<'_> {
126 24 : fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
127 24 : write!(f, "{}", self.ptr.to_string())
128 24 : }
129 : }
130 :
131 :
132 : #[allow(non_camel_case_types)]
133 24 : #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
134 : /// Enumeration describing the namespace or category of a function variant.
135 : ///
136 : /// Each [`RuntimeTable`] is associated with one [`Kind`],
137 : /// which indicates the domain or context under which its variant entries
138 : /// should be considered valid or applicable.
139 : ///
140 : /// These categories map to the runtime dispatch logic used by `dyld`
141 : /// when selecting the optimal function variant.
142 : pub enum Kind {
143 : /// Variants that apply on a per-process basis
144 : PER_PROCESS,
145 :
146 : /// Variants that are selected based on system-wide capabilities or configurations.
147 : SYSTEM_WIDE,
148 :
149 : /// Variants optimized for the ARM64 architecture.
150 : ARM64,
151 :
152 : /// Variants optimized for the x86-64 architecture.
153 : X86_64,
154 :
155 : /// Fallback/default kind when the category is not recognized.
156 : UNKNOWN(u32),
157 : }
158 :
159 : impl From<u32> for Kind {
160 24 : fn from(value: u32) -> Self {
161 24 : match value {
162 0 : 0x00000001 => Kind::PER_PROCESS,
163 0 : 0x00000002 => Kind::SYSTEM_WIDE,
164 0 : 0x00000003 => Kind::ARM64,
165 24 : 0x00000004 => Kind::X86_64,
166 0 : _ => Kind::UNKNOWN(value),
167 :
168 : }
169 24 : }
170 : }
171 : impl From<Kind> for u32 {
172 0 : fn from(value: Kind) -> u32 {
173 0 : match value {
174 0 : Kind::PER_PROCESS => 0x00000001,
175 0 : Kind::SYSTEM_WIDE => 0x00000002,
176 0 : Kind::ARM64 => 0x00000003,
177 0 : Kind::X86_64 => 0x00000004,
178 0 : Kind::UNKNOWN(value) => value,
179 :
180 : }
181 0 : }
182 : }
183 :
184 :
185 : /// This class exposes information about a given implementation.
186 : pub struct RuntimeTableEntry<'a> {
187 : ptr: cxx::UniquePtr<ffi::MachO_FunctionVariants_RuntimeTableEntry>,
188 : _owner: PhantomData<&'a ffi::MachO_FunctionVariants_RuntimeTable>,
189 : }
190 :
191 : impl FromFFI<ffi::MachO_FunctionVariants_RuntimeTableEntry> for RuntimeTableEntry<'_> {
192 72 : fn from_ffi(cmd: cxx::UniquePtr<ffi::MachO_FunctionVariants_RuntimeTableEntry>) -> Self {
193 72 : Self {
194 72 : ptr: cmd,
195 72 : _owner: PhantomData,
196 72 : }
197 72 : }
198 : }
199 :
200 : impl RuntimeTableEntry<'_> {
201 : /// The relative address of the implementation or an index if [`RuntimeTableEntry::another_table`] is set.
202 72 : pub fn implementation(&self) -> u32 {
203 72 : self.ptr.implementation()
204 72 : }
205 :
206 : /// Indicates whether [`RuntimeTableEntry::implementation`] refers to an entry in another runtime table,
207 : /// rather than a direct function implementation address.
208 72 : pub fn another_table(&self) -> bool {
209 72 : self.ptr.another_table()
210 72 : }
211 :
212 : /// The `flagBitNums` value as a slice of bytes
213 72 : pub fn flag_bit_nums(&self) -> &[u8] {
214 72 : to_slice!(self.ptr.flag_bit_nums());
215 72 : }
216 :
217 : /// Return the **interpreted** [`RuntimeTableEntry::flag_bit_nums`]
218 72 : pub fn flags(&self) -> Vec<Flag> {
219 72 : let vec = Vec::from(self.ptr.flags().as_slice());
220 72 : vec.iter().map(|e| Flag::from(*e)).collect()
221 72 : }
222 : }
223 :
224 : impl std::fmt::Debug for RuntimeTableEntry<'_> {
225 72 : fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
226 72 : f.debug_struct("RuntimeTableEntry")
227 72 : .field("implementation", &self.implementation())
228 72 : .field("another_table", &self.another_table())
229 72 : .field("flag_bit_nums", &self.flag_bit_nums())
230 72 : .field("flags", &self.flags())
231 72 : .finish()
232 72 : }
233 : }
234 :
235 : impl std::fmt::Display for RuntimeTableEntry<'_> {
236 72 : fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
237 72 : write!(f, "{}", self.ptr.to_string())
238 72 : }
239 : }
240 :
241 :
242 : #[allow(non_camel_case_types)]
243 48 : #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
244 : /// Flags describing the target platform, environment, or architecture
245 : /// for a given function implementation.
246 : ///
247 : /// These are encoded as a `uint32_t`, where high bits determine the namespace
248 : /// ([`Kind`]), and the lower bits encode the specific capability.
249 : pub enum Flag {
250 : ARM64_DEFAULT,
251 : ARM64_FLAGM,
252 : ARM64_FLAGM2,
253 : ARM64_FHM,
254 : ARM64_DOTPROD,
255 : ARM64_SHA3,
256 : ARM64_RDM,
257 : ARM64_LSE,
258 : ARM64_SHA256,
259 : ARM64_SHA512,
260 : ARM64_SHA1,
261 : ARM64_AES,
262 : ARM64_PMULL,
263 : ARM64_SPECRES,
264 : ARM64_SB,
265 : ARM64_FRINTTS,
266 : ARM64_LRCPC,
267 : ARM64_LRCPC2,
268 : ARM64_FCMA,
269 : ARM64_JSCVT,
270 : ARM64_PAUTH,
271 : ARM64_PAUTH2,
272 : ARM64_FPAC,
273 : ARM64_DPB,
274 : ARM64_DPB2,
275 : ARM64_BF16,
276 : ARM64_I8MM,
277 : ARM64_WFXT,
278 : ARM64_RPRES,
279 : ARM64_ECV,
280 : ARM64_AFP,
281 : ARM64_LSE2,
282 : ARM64_CSV2,
283 : ARM64_CVS3,
284 : ARM64_DIT,
285 : ARM64_FP16,
286 : ARM64_SSBS,
287 : ARM64_BTI,
288 : ARM64_SME,
289 : ARM64_SME2,
290 : ARM64_SMEF64F64,
291 : ARM64_SMEI16I64,
292 : ARM64_SMEF32F32,
293 : ARM64_SMEBI32I32,
294 : ARM64_SMEB16F32,
295 : ARM64_SMEF16F32,
296 : ARM64_SMEI8I32,
297 : ARM64_SMEI16I32,
298 : ARM64_ADVSIMD,
299 : ARM64_ADVSIMDHPFP,
300 : ARM64_CRC32,
301 : X86_64_DEFAULT,
302 : X86_64_SSE41,
303 : X86_64_FMA,
304 : X86_64_AVX,
305 : X86_64_AVX2,
306 : X86_64_AVX512F,
307 : X86_64_AVX512BW,
308 : X86_64_BMI1,
309 : X86_64_ROSETTA,
310 : X86_64_HASWELL,
311 : X86_64_IVYBRIDGE,
312 : X86_64_NEHALEM,
313 : SYSTEM_WIDE_DEFAULT,
314 : SYSTEM_WIDE_INTERNAL_INSTALL,
315 : SYSTEM_WIDE_CUSTOMER_INSTALL,
316 : SYSTEM_WIDE_LOCKDOWN,
317 : PER_PROCESS_DEFAULT,
318 : PER_PROCESS_TRANSLATED,
319 : PER_PROCESS_NO_OVERREAD,
320 : UNKNOWN(u32),
321 : }
322 :
323 : impl From<u32> for Flag {
324 48 : fn from(value: u32) -> Self {
325 48 : match value {
326 0 : 0x00300000 => Flag::ARM64_DEFAULT,
327 0 : 0x00300001 => Flag::ARM64_FLAGM,
328 0 : 0x00300002 => Flag::ARM64_FLAGM2,
329 0 : 0x00300003 => Flag::ARM64_FHM,
330 0 : 0x00300004 => Flag::ARM64_DOTPROD,
331 0 : 0x00300005 => Flag::ARM64_SHA3,
332 0 : 0x00300006 => Flag::ARM64_RDM,
333 0 : 0x00300007 => Flag::ARM64_LSE,
334 0 : 0x00300008 => Flag::ARM64_SHA256,
335 0 : 0x00300009 => Flag::ARM64_SHA512,
336 0 : 0x0030000a => Flag::ARM64_SHA1,
337 0 : 0x0030000b => Flag::ARM64_AES,
338 0 : 0x0030000c => Flag::ARM64_PMULL,
339 0 : 0x0030000d => Flag::ARM64_SPECRES,
340 0 : 0x0030000e => Flag::ARM64_SB,
341 0 : 0x0030000f => Flag::ARM64_FRINTTS,
342 0 : 0x00300010 => Flag::ARM64_LRCPC,
343 0 : 0x00300011 => Flag::ARM64_LRCPC2,
344 0 : 0x00300012 => Flag::ARM64_FCMA,
345 0 : 0x00300013 => Flag::ARM64_JSCVT,
346 0 : 0x00300014 => Flag::ARM64_PAUTH,
347 0 : 0x00300015 => Flag::ARM64_PAUTH2,
348 0 : 0x00300016 => Flag::ARM64_FPAC,
349 0 : 0x00300017 => Flag::ARM64_DPB,
350 0 : 0x00300018 => Flag::ARM64_DPB2,
351 0 : 0x00300019 => Flag::ARM64_BF16,
352 0 : 0x0030001a => Flag::ARM64_I8MM,
353 0 : 0x0030001b => Flag::ARM64_WFXT,
354 0 : 0x0030001c => Flag::ARM64_RPRES,
355 0 : 0x0030001d => Flag::ARM64_ECV,
356 0 : 0x0030001e => Flag::ARM64_AFP,
357 0 : 0x0030001f => Flag::ARM64_LSE2,
358 0 : 0x00300020 => Flag::ARM64_CSV2,
359 0 : 0x00300021 => Flag::ARM64_CVS3,
360 0 : 0x00300022 => Flag::ARM64_DIT,
361 0 : 0x00300023 => Flag::ARM64_FP16,
362 0 : 0x00300024 => Flag::ARM64_SSBS,
363 0 : 0x00300025 => Flag::ARM64_BTI,
364 0 : 0x0030002c => Flag::ARM64_SME,
365 0 : 0x0030002d => Flag::ARM64_SME2,
366 0 : 0x0030002e => Flag::ARM64_SMEF64F64,
367 0 : 0x0030002f => Flag::ARM64_SMEI16I64,
368 0 : 0x00300030 => Flag::ARM64_SMEF32F32,
369 0 : 0x00300031 => Flag::ARM64_SMEBI32I32,
370 0 : 0x00300032 => Flag::ARM64_SMEB16F32,
371 0 : 0x00300033 => Flag::ARM64_SMEF16F32,
372 0 : 0x00300034 => Flag::ARM64_SMEI8I32,
373 0 : 0x00300035 => Flag::ARM64_SMEI16I32,
374 0 : 0x00300036 => Flag::ARM64_ADVSIMD,
375 0 : 0x00300037 => Flag::ARM64_ADVSIMDHPFP,
376 0 : 0x00300038 => Flag::ARM64_CRC32,
377 0 : 0x00400000 => Flag::X86_64_DEFAULT,
378 0 : 0x00400001 => Flag::X86_64_SSE41,
379 0 : 0x00400002 => Flag::X86_64_FMA,
380 0 : 0x00400003 => Flag::X86_64_AVX,
381 0 : 0x00400004 => Flag::X86_64_AVX2,
382 0 : 0x00400005 => Flag::X86_64_AVX512F,
383 0 : 0x00400006 => Flag::X86_64_AVX512BW,
384 0 : 0x00400007 => Flag::X86_64_BMI1,
385 24 : 0x00400008 => Flag::X86_64_ROSETTA,
386 24 : 0x00400009 => Flag::X86_64_HASWELL,
387 0 : 0x0040000a => Flag::X86_64_IVYBRIDGE,
388 0 : 0x0040000b => Flag::X86_64_NEHALEM,
389 0 : 0x00200000 => Flag::SYSTEM_WIDE_DEFAULT,
390 0 : 0x00200001 => Flag::SYSTEM_WIDE_INTERNAL_INSTALL,
391 0 : 0x00200002 => Flag::SYSTEM_WIDE_CUSTOMER_INSTALL,
392 0 : 0x00200003 => Flag::SYSTEM_WIDE_LOCKDOWN,
393 0 : 0x00100000 => Flag::PER_PROCESS_DEFAULT,
394 0 : 0x00100001 => Flag::PER_PROCESS_TRANSLATED,
395 0 : 0x00100003 => Flag::PER_PROCESS_NO_OVERREAD,
396 0 : _ => Flag::UNKNOWN(value),
397 :
398 : }
399 48 : }
400 : }
401 : impl From<Flag> for u32 {
402 0 : fn from(value: Flag) -> u32 {
403 0 : match value {
404 0 : Flag::ARM64_DEFAULT => 0x00300000,
405 0 : Flag::ARM64_FLAGM => 0x00300001,
406 0 : Flag::ARM64_FLAGM2 => 0x00300002,
407 0 : Flag::ARM64_FHM => 0x00300003,
408 0 : Flag::ARM64_DOTPROD => 0x00300004,
409 0 : Flag::ARM64_SHA3 => 0x00300005,
410 0 : Flag::ARM64_RDM => 0x00300006,
411 0 : Flag::ARM64_LSE => 0x00300007,
412 0 : Flag::ARM64_SHA256 => 0x00300008,
413 0 : Flag::ARM64_SHA512 => 0x00300009,
414 0 : Flag::ARM64_SHA1 => 0x0030000a,
415 0 : Flag::ARM64_AES => 0x0030000b,
416 0 : Flag::ARM64_PMULL => 0x0030000c,
417 0 : Flag::ARM64_SPECRES => 0x0030000d,
418 0 : Flag::ARM64_SB => 0x0030000e,
419 0 : Flag::ARM64_FRINTTS => 0x0030000f,
420 0 : Flag::ARM64_LRCPC => 0x00300010,
421 0 : Flag::ARM64_LRCPC2 => 0x00300011,
422 0 : Flag::ARM64_FCMA => 0x00300012,
423 0 : Flag::ARM64_JSCVT => 0x00300013,
424 0 : Flag::ARM64_PAUTH => 0x00300014,
425 0 : Flag::ARM64_PAUTH2 => 0x00300015,
426 0 : Flag::ARM64_FPAC => 0x00300016,
427 0 : Flag::ARM64_DPB => 0x00300017,
428 0 : Flag::ARM64_DPB2 => 0x00300018,
429 0 : Flag::ARM64_BF16 => 0x00300019,
430 0 : Flag::ARM64_I8MM => 0x0030001a,
431 0 : Flag::ARM64_WFXT => 0x0030001b,
432 0 : Flag::ARM64_RPRES => 0x0030001c,
433 0 : Flag::ARM64_ECV => 0x0030001d,
434 0 : Flag::ARM64_AFP => 0x0030001e,
435 0 : Flag::ARM64_LSE2 => 0x0030001f,
436 0 : Flag::ARM64_CSV2 => 0x00300020,
437 0 : Flag::ARM64_CVS3 => 0x00300021,
438 0 : Flag::ARM64_DIT => 0x00300022,
439 0 : Flag::ARM64_FP16 => 0x00300023,
440 0 : Flag::ARM64_SSBS => 0x00300024,
441 0 : Flag::ARM64_BTI => 0x00300025,
442 0 : Flag::ARM64_SME => 0x0030002c,
443 0 : Flag::ARM64_SME2 => 0x0030002d,
444 0 : Flag::ARM64_SMEF64F64 => 0x0030002e,
445 0 : Flag::ARM64_SMEI16I64 => 0x0030002f,
446 0 : Flag::ARM64_SMEF32F32 => 0x00300030,
447 0 : Flag::ARM64_SMEBI32I32 => 0x00300031,
448 0 : Flag::ARM64_SMEB16F32 => 0x00300032,
449 0 : Flag::ARM64_SMEF16F32 => 0x00300033,
450 0 : Flag::ARM64_SMEI8I32 => 0x00300034,
451 0 : Flag::ARM64_SMEI16I32 => 0x00300035,
452 0 : Flag::ARM64_ADVSIMD => 0x00300036,
453 0 : Flag::ARM64_ADVSIMDHPFP => 0x00300037,
454 0 : Flag::ARM64_CRC32 => 0x00300038,
455 0 : Flag::X86_64_DEFAULT => 0x00400000,
456 0 : Flag::X86_64_SSE41 => 0x00400001,
457 0 : Flag::X86_64_FMA => 0x00400002,
458 0 : Flag::X86_64_AVX => 0x00400003,
459 0 : Flag::X86_64_AVX2 => 0x00400004,
460 0 : Flag::X86_64_AVX512F => 0x00400005,
461 0 : Flag::X86_64_AVX512BW => 0x00400006,
462 0 : Flag::X86_64_BMI1 => 0x00400007,
463 0 : Flag::X86_64_ROSETTA => 0x00400008,
464 0 : Flag::X86_64_HASWELL => 0x00400009,
465 0 : Flag::X86_64_IVYBRIDGE => 0x0040000a,
466 0 : Flag::X86_64_NEHALEM => 0x0040000b,
467 0 : Flag::SYSTEM_WIDE_DEFAULT => 0x00200000,
468 0 : Flag::SYSTEM_WIDE_INTERNAL_INSTALL => 0x00200001,
469 0 : Flag::SYSTEM_WIDE_CUSTOMER_INSTALL => 0x00200002,
470 0 : Flag::SYSTEM_WIDE_LOCKDOWN => 0x00200003,
471 0 : Flag::PER_PROCESS_DEFAULT => 0x00100000,
472 0 : Flag::PER_PROCESS_TRANSLATED => 0x00100001,
473 0 : Flag::PER_PROCESS_NO_OVERREAD => 0x00100003,
474 0 : Flag::UNKNOWN(value) => value,
475 :
476 : }
477 0 : }
478 : }
479 :
480 24 : declare_iterator!(
481 24 : RuntimeTables,
482 24 : RuntimeTable<'a>,
483 24 : ffi::MachO_FunctionVariants_RuntimeTable,
484 24 : ffi::MachO_FunctionVariants,
485 24 : ffi::MachO_FunctionVariants_it_runtime_table
486 24 : );
487 :
488 72 : declare_iterator!(
489 72 : RuntimeTableEntries,
490 72 : RuntimeTableEntry<'a>,
491 72 : ffi::MachO_FunctionVariants_RuntimeTableEntry,
492 72 : ffi::MachO_FunctionVariants_RuntimeTable,
493 72 : ffi::MachO_FunctionVariants_RuntimeTable_it_entries
494 72 : );
|