Skip to main content

idakit_sys/
function_flags.rs

1//! The typed [`FuncFlags`] layer over `funcs.hpp`'s `func_t::flags` bits.
2
3use bitflags::bitflags;
4
5bitflags! {
6    /// Function flag bits from `funcs.hpp` (`func_t::flags`).
7    ///
8    /// Accepts any bit pattern (`from_bits_retain`), so it is sound to lift straight from
9    /// `func_flags`'s raw `u64`, which may carry bits this type does not name.
10    #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
11    #[doc(alias(
12        "FUNC_NORET", "FUNC_FAR", "FUNC_LIB", "FUNC_STATICDEF", "FUNC_FRAME", "FUNC_USERFAR",
13        "FUNC_HIDDEN", "FUNC_THUNK", "FUNC_BOTTOMBP", "FUNC_NORET_PENDING", "FUNC_SP_READY",
14        "FUNC_FUZZY_SP", "FUNC_PROLOG_OK", "FUNC_PURGED_OK", "FUNC_TAIL", "FUNC_LUMINA",
15        "FUNC_OUTLINE", "FUNC_REANALYZE", "FUNC_UNWIND", "FUNC_CATCH", "FUNC_RESERVED"
16    ))]
17    pub struct FuncFlags: u64 {
18        /// The function does not return (`FUNC_NORET`).
19        #[doc(alias("FUNC_NORET"))]
20        const NORET = 0x0000_0001;
21        /// A far function (`FUNC_FAR`).
22        #[doc(alias("FUNC_FAR"))]
23        const FAR = 0x0000_0002;
24        /// A library function (`FUNC_LIB`).
25        #[doc(alias("FUNC_LIB"))]
26        const LIB = 0x0000_0004;
27        /// A static function (`FUNC_STATICDEF`).
28        #[doc(alias("FUNC_STATICDEF"))]
29        const STATICDEF = 0x0000_0008;
30        /// The function uses a frame pointer, BP (`FUNC_FRAME`).
31        #[doc(alias("FUNC_FRAME"))]
32        const FRAME = 0x0000_0010;
33        /// The user has specified the function's far-ness (`FUNC_USERFAR`).
34        #[doc(alias("FUNC_USERFAR"))]
35        const USERFAR = 0x0000_0020;
36        /// A hidden function chunk (`FUNC_HIDDEN`).
37        #[doc(alias("FUNC_HIDDEN"))]
38        const HIDDEN = 0x0000_0040;
39        /// A thunk, jump function (`FUNC_THUNK`).
40        #[doc(alias("FUNC_THUNK"))]
41        const THUNK = 0x0000_0080;
42        /// BP points to the bottom of the stack frame (`FUNC_BOTTOMBP`).
43        #[doc(alias("FUNC_BOTTOMBP"))]
44        const BOTTOMBP = 0x0000_0100;
45        /// Non-return analysis is still pending (`FUNC_NORET_PENDING`).
46        #[doc(alias("FUNC_NORET_PENDING"))]
47        const NORET_PENDING = 0x0000_0200;
48        /// SP analysis has been performed (`FUNC_SP_READY`).
49        #[doc(alias("FUNC_SP_READY"))]
50        const SP_READY = 0x0000_0400;
51        /// The function changes SP in an untraceable way (`FUNC_FUZZY_SP`).
52        #[doc(alias("FUNC_FUZZY_SP"))]
53        const FUZZY_SP = 0x0000_0800;
54        /// Prolog analysis has been performed (`FUNC_PROLOG_OK`).
55        #[doc(alias("FUNC_PROLOG_OK"))]
56        const PROLOG_OK = 0x0000_1000;
57        /// The `argsize` field has been validated (`FUNC_PURGED_OK`).
58        #[doc(alias("FUNC_PURGED_OK"))]
59        const PURGED_OK = 0x0000_4000;
60        /// This is a function tail (`FUNC_TAIL`).
61        #[doc(alias("FUNC_TAIL"))]
62        const TAIL = 0x0000_8000;
63        /// Function info is provided by Lumina (`FUNC_LUMINA`).
64        #[doc(alias("FUNC_LUMINA"))]
65        const LUMINA = 0x0001_0000;
66        /// Outlined code, not a real function (`FUNC_OUTLINE`).
67        #[doc(alias("FUNC_OUTLINE"))]
68        const OUTLINE = 0x0002_0000;
69        /// The function frame changed, request to reanalyze (`FUNC_REANALYZE`).
70        #[doc(alias("FUNC_REANALYZE"))]
71        const REANALYZE = 0x0004_0000;
72        /// The function is an exception unwind handler (`FUNC_UNWIND`).
73        #[doc(alias("FUNC_UNWIND"))]
74        const UNWIND = 0x0008_0000;
75        /// The function is an exception catch handler (`FUNC_CATCH`).
76        #[doc(alias("FUNC_CATCH"))]
77        const CATCH = 0x0010_0000;
78        /// Reserved for internal use (`FUNC_RESERVED`).
79        #[doc(alias("FUNC_RESERVED"))]
80        const RESERVED = 0x8000_0000_0000_0000;
81    }
82}
83
84#[cfg(test)]
85mod tests {
86    use assert2::assert;
87    use proptest::prelude::*;
88    use rstest::rstest;
89
90    use super::*;
91
92    #[rstest]
93    #[case::noret(FuncFlags::NORET, 0x0000_0001)]
94    #[case::far(FuncFlags::FAR, 0x0000_0002)]
95    #[case::lib(FuncFlags::LIB, 0x0000_0004)]
96    #[case::staticdef(FuncFlags::STATICDEF, 0x0000_0008)]
97    #[case::frame(FuncFlags::FRAME, 0x0000_0010)]
98    #[case::userfar(FuncFlags::USERFAR, 0x0000_0020)]
99    #[case::hidden(FuncFlags::HIDDEN, 0x0000_0040)]
100    #[case::thunk(FuncFlags::THUNK, 0x0000_0080)]
101    #[case::bottombp(FuncFlags::BOTTOMBP, 0x0000_0100)]
102    #[case::noret_pending(FuncFlags::NORET_PENDING, 0x0000_0200)]
103    #[case::sp_ready(FuncFlags::SP_READY, 0x0000_0400)]
104    #[case::fuzzy_sp(FuncFlags::FUZZY_SP, 0x0000_0800)]
105    #[case::prolog_ok(FuncFlags::PROLOG_OK, 0x0000_1000)]
106    #[case::purged_ok(FuncFlags::PURGED_OK, 0x0000_4000)]
107    #[case::tail(FuncFlags::TAIL, 0x0000_8000)]
108    #[case::lumina(FuncFlags::LUMINA, 0x0001_0000)]
109    #[case::outline(FuncFlags::OUTLINE, 0x0002_0000)]
110    #[case::reanalyze(FuncFlags::REANALYZE, 0x0004_0000)]
111    #[case::unwind(FuncFlags::UNWIND, 0x0008_0000)]
112    #[case::catch(FuncFlags::CATCH, 0x0010_0000)]
113    #[case::reserved(FuncFlags::RESERVED, 0x8000_0000_0000_0000)]
114    fn flags_pin_the_raw_sdk_values(#[case] flag: FuncFlags, #[case] raw: u64) {
115        assert!(flag.bits() == raw);
116    }
117
118    proptest! {
119        #[test]
120        fn from_bits_retain_round_trips_every_bit_pattern(raw: u64) {
121            prop_assert_eq!(FuncFlags::from_bits_retain(raw).bits(), raw);
122        }
123
124        #[test]
125        fn union_and_intersection_are_raw_bitwise_ops(a: u64, b: u64) {
126            let (fa, fb) = (FuncFlags::from_bits_retain(a), FuncFlags::from_bits_retain(b));
127            prop_assert_eq!((fa | fb).bits(), a | b);
128            prop_assert_eq!((fa & fb).bits(), a & b);
129        }
130
131        #[test]
132        fn complement_truncates_to_the_known_flag_mask(a: u64) {
133            let fa = FuncFlags::from_bits_retain(a);
134            prop_assert_eq!((!fa).bits(), !a & FuncFlags::all().bits());
135        }
136    }
137}