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