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 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}