Skip to main content

idakit_sys/
segment_flags.rs

1//! The typed [`SegmentPermissions`]/[`SegmentFlags`] layers over `segment.hpp`'s
2//! `SEGPERM_*`/`SFL_*` bit masks.
3
4use std::ffi::c_int;
5
6use bitflags::bitflags;
7
8bitflags! {
9    /// Segment permission bits from `segment.hpp` (`SEGPERM_EXEC`/`SEGPERM_WRITE`/`SEGPERM_READ`).
10    ///
11    /// Accepts any bit pattern (`from_bits_retain`), so it is sound to lift straight from
12    /// `get_segm_attr(SEGATTR_PERM)`'s raw byte, which may carry bits this type does not name.
13    #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
14    #[doc(alias("SEGPERM_EXEC", "SEGPERM_WRITE", "SEGPERM_READ"))]
15    pub struct SegmentPermissions: c_int {
16        /// The segment is executable (`SEGPERM_EXEC`).
17        #[doc(alias("SEGPERM_EXEC"))]
18        const EXEC = 1;
19        /// The segment is writable (`SEGPERM_WRITE`).
20        #[doc(alias("SEGPERM_WRITE"))]
21        const WRITE = 2;
22        /// The segment is readable (`SEGPERM_READ`).
23        #[doc(alias("SEGPERM_READ"))]
24        const READ = 4;
25    }
26}
27
28bitflags! {
29    /// Segment flag bits from `segment.hpp` (`SFL_*`, `segment_t::flags`).
30    ///
31    /// Accepts any bit pattern (`from_bits_retain`), so it is sound to lift straight from the raw
32    /// `flags` word, which may carry bits this type does not name.
33    #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
34    #[doc(alias("SFL_COMORG", "SFL_OBOK", "SFL_HIDDEN", "SFL_DEBUG", "SFL_LOADER", "SFL_HIDETYPE", "SFL_HEADER"))]
35    pub struct SegmentFlags: c_int {
36        /// IDP-dependent: the ORG directive is not commented out (`SFL_COMORG`).
37        #[doc(alias("SFL_COMORG"))]
38        const COMORG = 0x01;
39        /// The `orgbase` field is present (`SFL_OBOK`).
40        #[doc(alias("SFL_OBOK"))]
41        const OBOK = 0x02;
42        /// The segment is hidden from the disassembly listing (`SFL_HIDDEN`).
43        #[doc(alias("SFL_HIDDEN"))]
44        const HIDDEN = 0x04;
45        /// The segment was created for the debugger, temporary (`SFL_DEBUG`).
46        #[doc(alias("SFL_DEBUG"))]
47        const DEBUG = 0x08;
48        /// The segment was created by a loader (`SFL_LOADER`).
49        #[doc(alias("SFL_LOADER"))]
50        const LOADER = 0x10;
51        /// The segment's type is hidden in the listing (`SFL_HIDETYPE`).
52        #[doc(alias("SFL_HIDETYPE"))]
53        const HIDETYPE = 0x20;
54        /// A header segment: no offsets are created into it (`SFL_HEADER`).
55        #[doc(alias("SFL_HEADER"))]
56        const HEADER = 0x40;
57    }
58}
59
60#[cfg(test)]
61mod tests {
62    use assert2::assert;
63    use proptest::prelude::*;
64    use rstest::rstest;
65
66    use super::*;
67
68    #[rstest]
69    #[case::exec(SegmentPermissions::EXEC, 1)]
70    #[case::write(SegmentPermissions::WRITE, 2)]
71    #[case::read(SegmentPermissions::READ, 4)]
72    fn flags_pin_the_raw_sdk_values(#[case] flag: SegmentPermissions, #[case] raw: c_int) {
73        assert!(flag.bits() == raw);
74    }
75
76    #[rstest]
77    #[case::comorg(SegmentFlags::COMORG, 0x01)]
78    #[case::obok(SegmentFlags::OBOK, 0x02)]
79    #[case::hidden(SegmentFlags::HIDDEN, 0x04)]
80    #[case::debug(SegmentFlags::DEBUG, 0x08)]
81    #[case::loader(SegmentFlags::LOADER, 0x10)]
82    #[case::hidetype(SegmentFlags::HIDETYPE, 0x20)]
83    #[case::header(SegmentFlags::HEADER, 0x40)]
84    fn seg_flags_pin_the_raw_sdk_values(#[case] flag: SegmentFlags, #[case] raw: c_int) {
85        assert!(flag.bits() == raw);
86    }
87
88    proptest! {
89        #[test]
90        fn seg_perm_from_bits_retain_round_trips_every_bit_pattern(raw: c_int) {
91            prop_assert_eq!(SegmentPermissions::from_bits_retain(raw).bits(), raw);
92        }
93
94        #[test]
95        fn seg_perm_union_and_intersection_are_raw_bitwise_ops(a: c_int, b: c_int) {
96            let (fa, fb) = (SegmentPermissions::from_bits_retain(a), SegmentPermissions::from_bits_retain(b));
97            prop_assert_eq!((fa | fb).bits(), a | b);
98            prop_assert_eq!((fa & fb).bits(), a & b);
99        }
100
101        #[test]
102        fn seg_perm_complement_truncates_to_the_known_flag_mask(a: c_int) {
103            let fa = SegmentPermissions::from_bits_retain(a);
104            prop_assert_eq!((!fa).bits(), !a & SegmentPermissions::all().bits());
105        }
106
107        #[test]
108        fn seg_flags_from_bits_retain_round_trips_every_bit_pattern(raw: c_int) {
109            prop_assert_eq!(SegmentFlags::from_bits_retain(raw).bits(), raw);
110        }
111
112        #[test]
113        fn seg_flags_union_and_intersection_are_raw_bitwise_ops(a: c_int, b: c_int) {
114            let (fa, fb) = (SegmentFlags::from_bits_retain(a), SegmentFlags::from_bits_retain(b));
115            prop_assert_eq!((fa | fb).bits(), a | b);
116            prop_assert_eq!((fa & fb).bits(), a & b);
117        }
118
119        #[test]
120        fn seg_flags_complement_truncates_to_the_known_flag_mask(a: c_int) {
121            let fa = SegmentFlags::from_bits_retain(a);
122            prop_assert_eq!((!fa).bits(), !a & SegmentFlags::all().bits());
123        }
124    }
125}