Skip to main content

idalib/
insn.rs

1use std::mem;
2
3use bitflags::bitflags;
4
5use crate::ffi::insn::insn_t;
6use crate::ffi::insn::idalib_print_insn_mnem;
7use crate::ffi::insn::op::*;
8use crate::ffi::util::{is_basic_block_end, is_call_insn, is_indirect_jump_insn, is_ret_insn};
9
10pub use crate::ffi::insn::{arm, mips, x86};
11
12use crate::Address;
13
14pub type Register = u16;
15pub type Phrase = u16;
16
17#[derive(Clone, Copy)]
18#[repr(transparent)]
19pub struct Insn {
20    inner: insn_t,
21}
22
23#[derive(Clone, Copy)]
24#[repr(transparent)]
25pub struct Operand {
26    inner: op_t,
27}
28
29#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
30pub enum OperandType {
31    // Void -- we exclude it during creation
32    Reg,
33    Mem,
34    Phrase,
35    Displ,
36    Imm,
37    Far,
38    Near,
39    IdpSpec0,
40    IdpSpec1,
41    IdpSpec2,
42    IdpSpec3,
43    IdpSpec4,
44    IdpSpec5,
45    Other(u8),
46}
47
48impl OperandType {
49    #[expect(
50        non_upper_case_globals,
51        reason = "the o_* constants come from the SDK and keep their C names"
52    )]
53    fn from_raw(raw: u8) -> Self {
54        match raw {
55            o_reg => Self::Reg,
56            o_mem => Self::Mem,
57            o_phrase => Self::Phrase,
58            o_displ => Self::Displ,
59            o_imm => Self::Imm,
60            o_far => Self::Far,
61            o_near => Self::Near,
62            o_idpspec0 => Self::IdpSpec0,
63            o_idpspec1 => Self::IdpSpec1,
64            o_idpspec2 => Self::IdpSpec2,
65            o_idpspec3 => Self::IdpSpec3,
66            o_idpspec4 => Self::IdpSpec4,
67            o_idpspec5 => Self::IdpSpec5,
68            other => Self::Other(other),
69        }
70    }
71}
72
73#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
74#[repr(u8)]
75pub enum OperandDataType {
76    Byte = dt_byte as _,
77    Word = dt_word as _,
78    DWord = dt_dword as _,
79    Float = dt_float as _,
80    Double = dt_double as _,
81    TByte = dt_tbyte as _,
82    PackReal = dt_packreal as _,
83    QWord = dt_qword as _,
84    Byte16 = dt_byte16 as _,
85    Code = dt_code as _,
86    Void = dt_void as _,
87    FWord = dt_fword as _,
88    Bitfield = dt_bitfild as _,
89    String = dt_string as _,
90    Unicode = dt_unicode as _,
91    LongDouble = dt_ldbl as _,
92    Byte32 = dt_byte32 as _,
93    Byte64 = dt_byte64 as _,
94    Half = dt_half as _,
95}
96
97bitflags! {
98    #[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
99    pub struct OperandFlags: u8 {
100        const NO_BASE_DISP = OF_NO_BASE_DISP as _;
101        const OUTER_DISP = OF_OUTER_DISP as _;
102        const NUMBER = OF_NUMBER as _;
103        const SHOW = OF_SHOW as _;
104    }
105}
106
107bitflags! {
108    #[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
109    pub struct IsReturnFlags: u8 {
110        const EXTENDED = IRI_EXTENDED as _;
111        const RET_LITERALLY = IRI_RET_LITERALLY as _;
112        const SKIP_RETTARGET = IRI_SKIP_RETTARGET as _;
113        const STRICT = IRI_STRICT as _;
114    }
115}
116
117pub type InsnType = u16;
118
119impl Insn {
120    pub(crate) fn from_repr(inner: insn_t) -> Self {
121        Self { inner }
122    }
123
124    pub fn address(&self) -> Address {
125        self.inner.ea
126    }
127
128    pub fn itype(&self) -> InsnType {
129        self.inner.itype as _
130    }
131
132    pub fn mnemonic(&self) -> Option<String> {
133        let s = unsafe { idalib_print_insn_mnem(self.address().into()) };
134
135        if s.is_empty() { None } else { Some(s) }
136    }
137
138    pub fn operand(&self, n: usize) -> Option<Operand> {
139        let op = self.inner.ops.get(n)?;
140
141        if op.type_ != o_void {
142            Some(Operand { inner: *op })
143        } else {
144            None
145        }
146    }
147
148    pub fn operand_count(&self) -> usize {
149        self.inner
150            .ops
151            .iter()
152            .position(|op| op.type_ == o_void)
153            .unwrap_or(self.inner.ops.len())
154    }
155
156    pub fn len(&self) -> usize {
157        self.inner.size as _
158    }
159
160    pub fn is_empty(&self) -> bool {
161        self.len() == 0
162    }
163
164    pub fn is_basic_block_end(&self, call_stops_block: bool) -> bool {
165        unsafe { is_basic_block_end(&self.inner, call_stops_block) }
166    }
167
168    pub fn is_call(&self) -> bool {
169        unsafe { is_call_insn(&self.inner) }
170    }
171
172    pub fn is_indirect_jump(&self) -> bool {
173        unsafe { is_indirect_jump_insn(&self.inner) }
174    }
175
176    pub fn is_ret(&self) -> bool {
177        self.is_ret_with(IsReturnFlags::STRICT)
178    }
179
180    pub fn is_ret_with(&self, iri: IsReturnFlags) -> bool {
181        unsafe { is_ret_insn(&self.inner, iri.bits()) }
182    }
183}
184
185impl Operand {
186    pub fn flags(&self) -> OperandFlags {
187        OperandFlags::from_bits_retain(self.inner.flags)
188    }
189
190    pub fn offb(&self) -> i8 {
191        self.inner.offb
192    }
193
194    pub fn offo(&self) -> i8 {
195        self.inner.offo
196    }
197
198    pub fn n(&self) -> usize {
199        self.inner.n as _
200    }
201
202    pub fn number(&self) -> usize {
203        self.n()
204    }
205
206    pub fn type_(&self) -> OperandType {
207        OperandType::from_raw(self.inner.type_)
208    }
209
210    pub fn dtype(&self) -> OperandDataType {
211        unsafe { mem::transmute(self.inner.dtype) }
212    }
213
214    pub fn reg(&self) -> Option<Register> {
215        if self.is_processor_specific() || self.type_() == OperandType::Reg {
216            Some(unsafe { self.inner.__bindgen_anon_1.reg })
217        } else {
218            None
219        }
220    }
221
222    pub fn register(&self) -> Option<Register> {
223        self.reg()
224    }
225
226    pub fn phrase(&self) -> Option<Phrase> {
227        if self.is_processor_specific()
228            || matches!(self.type_(), OperandType::Phrase | OperandType::Displ)
229        {
230            Some(unsafe { self.inner.__bindgen_anon_1.phrase })
231        } else {
232            None
233        }
234    }
235
236    pub fn value(&self) -> Option<u64> {
237        if self.is_processor_specific() || self.type_() == OperandType::Imm {
238            Some(unsafe { self.inner.__bindgen_anon_2.value })
239        } else {
240            None
241        }
242    }
243
244    pub fn outer_displacement(&self) -> Option<u64> {
245        if self.flags().contains(OperandFlags::OUTER_DISP) {
246            Some(unsafe { self.inner.__bindgen_anon_2.value })
247        } else {
248            None
249        }
250    }
251
252    pub fn address(&self) -> Option<Address> {
253        self.addr()
254    }
255
256    pub fn addr(&self) -> Option<Address> {
257        if self.is_processor_specific()
258            || matches!(
259                self.type_(),
260                OperandType::Mem | OperandType::Displ | OperandType::Far | OperandType::Near
261            )
262        {
263            Some(unsafe { self.inner.__bindgen_anon_3.addr })
264        } else {
265            None
266        }
267    }
268
269    pub fn processor_specific(&self) -> Option<u64> {
270        if self.is_processor_specific() {
271            Some(unsafe { self.inner.__bindgen_anon_4.specval })
272        } else {
273            None
274        }
275    }
276
277    pub fn processor_specific_low(&self) -> Option<u16> {
278        if self.is_processor_specific() {
279            Some(unsafe { self.inner.__bindgen_anon_4.specval_shorts.low })
280        } else {
281            None
282        }
283    }
284
285    pub fn processor_specific_high(&self) -> Option<u16> {
286        if self.is_processor_specific() {
287            Some(unsafe { self.inner.__bindgen_anon_4.specval_shorts.high })
288        } else {
289            None
290        }
291    }
292
293    pub fn processor_specific_flag1(&self) -> Option<i8> {
294        if self.is_processor_specific() {
295            Some(self.inner.specflag1)
296        } else {
297            None
298        }
299    }
300
301    pub fn processor_specific_flag2(&self) -> Option<i8> {
302        if self.is_processor_specific() {
303            Some(self.inner.specflag2)
304        } else {
305            None
306        }
307    }
308
309    pub fn processor_specific_flag3(&self) -> Option<i8> {
310        if self.is_processor_specific() {
311            Some(self.inner.specflag3)
312        } else {
313            None
314        }
315    }
316
317    pub fn processor_specific_flag4(&self) -> Option<i8> {
318        if self.is_processor_specific() {
319            Some(self.inner.specflag4)
320        } else {
321            None
322        }
323    }
324
325    pub fn is_processor_specific(&self) -> bool {
326        matches!(
327            self.type_(),
328            OperandType::IdpSpec0
329                | OperandType::IdpSpec1
330                | OperandType::IdpSpec2
331                | OperandType::IdpSpec3
332                | OperandType::IdpSpec4
333                | OperandType::IdpSpec5
334                | OperandType::Other(_)
335        )
336    }
337}