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;
118const _: [InsnType; 3] = [arm::ARM_null, mips::MIPS_null, x86::NN_null];
119
120impl Insn {
121    pub(crate) fn from_repr(inner: insn_t) -> Self {
122        Self { inner }
123    }
124
125    pub fn address(&self) -> Address {
126        self.inner.ea
127    }
128
129    pub fn itype(&self) -> InsnType {
130        self.inner.itype as _
131    }
132
133    pub fn mnemonic(&self) -> Option<String> {
134        let s = unsafe { idalib_print_insn_mnem(self.address().into()) };
135
136        if s.is_empty() { None } else { Some(s) }
137    }
138
139    pub fn operand(&self, n: usize) -> Option<Operand> {
140        let op = self.inner.ops.get(n)?;
141
142        if op.type_ != o_void {
143            Some(Operand { inner: *op })
144        } else {
145            None
146        }
147    }
148
149    pub fn operand_count(&self) -> usize {
150        self.inner
151            .ops
152            .iter()
153            .position(|op| op.type_ == o_void)
154            .unwrap_or(self.inner.ops.len())
155    }
156
157    pub fn len(&self) -> usize {
158        self.inner.size as _
159    }
160
161    pub fn is_empty(&self) -> bool {
162        self.len() == 0
163    }
164
165    pub fn is_basic_block_end(&self, call_stops_block: bool) -> bool {
166        unsafe { is_basic_block_end(&self.inner, call_stops_block) }
167    }
168
169    pub fn is_call(&self) -> bool {
170        unsafe { is_call_insn(&self.inner) }
171    }
172
173    pub fn is_indirect_jump(&self) -> bool {
174        unsafe { is_indirect_jump_insn(&self.inner) }
175    }
176
177    pub fn is_ret(&self) -> bool {
178        self.is_ret_with(IsReturnFlags::STRICT)
179    }
180
181    pub fn is_ret_with(&self, iri: IsReturnFlags) -> bool {
182        unsafe { is_ret_insn(&self.inner, iri.bits()) }
183    }
184}
185
186impl Operand {
187    pub fn flags(&self) -> OperandFlags {
188        OperandFlags::from_bits_retain(self.inner.flags)
189    }
190
191    pub fn offb(&self) -> i8 {
192        self.inner.offb as _
193    }
194
195    pub fn offo(&self) -> i8 {
196        self.inner.offo as _
197    }
198
199    pub fn n(&self) -> usize {
200        self.inner.n as _
201    }
202
203    pub fn number(&self) -> usize {
204        self.n()
205    }
206
207    pub fn type_(&self) -> OperandType {
208        OperandType::from_raw(self.inner.type_)
209    }
210
211    pub fn dtype(&self) -> OperandDataType {
212        unsafe { mem::transmute(self.inner.dtype) }
213    }
214
215    pub fn reg(&self) -> Option<Register> {
216        if self.is_processor_specific() || self.type_() == OperandType::Reg {
217            Some(unsafe { self.inner.__bindgen_anon_1.reg })
218        } else {
219            None
220        }
221    }
222
223    pub fn register(&self) -> Option<Register> {
224        self.reg()
225    }
226
227    pub fn phrase(&self) -> Option<Phrase> {
228        if self.is_processor_specific()
229            || matches!(self.type_(), OperandType::Phrase | OperandType::Displ)
230        {
231            Some(unsafe { self.inner.__bindgen_anon_1.phrase })
232        } else {
233            None
234        }
235    }
236
237    pub fn value(&self) -> Option<u64> {
238        if self.is_processor_specific() || self.type_() == OperandType::Imm {
239            Some(unsafe { self.inner.__bindgen_anon_2.value })
240        } else {
241            None
242        }
243    }
244
245    pub fn outer_displacement(&self) -> Option<u64> {
246        if self.flags().contains(OperandFlags::OUTER_DISP) {
247            Some(unsafe { self.inner.__bindgen_anon_2.value })
248        } else {
249            None
250        }
251    }
252
253    pub fn address(&self) -> Option<Address> {
254        self.addr()
255    }
256
257    pub fn addr(&self) -> Option<Address> {
258        if self.is_processor_specific()
259            || matches!(
260                self.type_(),
261                OperandType::Mem | OperandType::Displ | OperandType::Far | OperandType::Near
262            )
263        {
264            Some(unsafe { self.inner.__bindgen_anon_3.addr })
265        } else {
266            None
267        }
268    }
269
270    pub fn processor_specific(&self) -> Option<u64> {
271        if self.is_processor_specific() {
272            Some(unsafe { self.inner.__bindgen_anon_4.specval })
273        } else {
274            None
275        }
276    }
277
278    pub fn processor_specific_low(&self) -> Option<u16> {
279        if self.is_processor_specific() {
280            Some(unsafe { self.inner.__bindgen_anon_4.specval_shorts.low })
281        } else {
282            None
283        }
284    }
285
286    pub fn processor_specific_high(&self) -> Option<u16> {
287        if self.is_processor_specific() {
288            Some(unsafe { self.inner.__bindgen_anon_4.specval_shorts.high })
289        } else {
290            None
291        }
292    }
293
294    pub fn processor_specific_flag1(&self) -> Option<i8> {
295        if self.is_processor_specific() {
296            Some(self.inner.specflag1 as _)
297        } else {
298            None
299        }
300    }
301
302    pub fn processor_specific_flag2(&self) -> Option<i8> {
303        if self.is_processor_specific() {
304            Some(self.inner.specflag2 as _)
305        } else {
306            None
307        }
308    }
309
310    pub fn processor_specific_flag3(&self) -> Option<i8> {
311        if self.is_processor_specific() {
312            Some(self.inner.specflag3 as _)
313        } else {
314            None
315        }
316    }
317
318    pub fn processor_specific_flag4(&self) -> Option<i8> {
319        if self.is_processor_specific() {
320            Some(self.inner.specflag4 as _)
321        } else {
322            None
323        }
324    }
325
326    pub fn is_processor_specific(&self) -> bool {
327        matches!(
328            self.type_(),
329            OperandType::IdpSpec0
330                | OperandType::IdpSpec1
331                | OperandType::IdpSpec2
332                | OperandType::IdpSpec3
333                | OperandType::IdpSpec4
334                | OperandType::IdpSpec5
335                | OperandType::Other(_)
336        )
337    }
338}