capstone_git/arch/
x86.rs

1//! Contains x86-specific types
2
3use core::convert::From;
4use core::convert::TryInto;
5use core::{cmp, fmt, slice};
6
7use capstone_sys::cs_x86_encoding;
8pub use capstone_sys::x86_avx_bcast as X86AvxBcast;
9pub use capstone_sys::x86_avx_cc as X86AvxCC;
10pub use capstone_sys::x86_avx_rm as X86AvxRm;
11pub use capstone_sys::x86_insn as X86Insn;
12pub use capstone_sys::x86_insn_group as X86InsnGroup;
13pub use capstone_sys::x86_prefix as X86Prefix;
14pub use capstone_sys::x86_reg as X86Reg;
15pub use capstone_sys::x86_sse_cc as X86SseCC;
16pub use capstone_sys::x86_xop_cc as X86XopCC;
17use capstone_sys::{
18    cs_ac_type, cs_x86, cs_x86_op, cs_x86_op__bindgen_ty_1, x86_op_mem, x86_op_type,
19};
20
21use super::InsnOffsetSpan;
22pub use crate::arch::arch_builder::x86::*;
23use crate::arch::DetailsArchInsn;
24use crate::instruction::{AccessType, RegId, RegIdInt};
25
26/// Contains X86-specific details for an instruction
27pub struct X86InsnDetail<'a>(pub(crate) &'a cs_x86);
28
29// todo(tmfink): expose new types cs_x86__bindgen_ty_1, cs_x86_op::access
30
31impl X86OperandType {
32    fn new(op_type: x86_op_type, value: cs_x86_op__bindgen_ty_1) -> X86OperandType {
33        use self::x86_op_type::*;
34        use self::X86OperandType::*;
35
36        match op_type {
37            X86_OP_REG => Reg(RegId(unsafe { value.reg } as RegIdInt)),
38            X86_OP_IMM => Imm(unsafe { value.imm }),
39            X86_OP_MEM => Mem(X86OpMem(unsafe { value.mem })),
40            X86_OP_INVALID => Invalid,
41        }
42    }
43}
44
45/// X86 operand
46#[derive(Clone, Debug, PartialEq, Eq)]
47pub struct X86Operand {
48    /// Operand size
49    pub size: u8,
50
51    /// How is this operand accessed?
52    ///
53    /// NOTE: this field is always `None` if the "full" feataure is not enabled.
54    pub access: Option<AccessType>,
55
56    /// AVX broadcast
57    pub avx_bcast: X86AvxBcast,
58
59    /// AVX zero opmask
60    pub avx_zero_opmask: bool,
61
62    /// Operand type
63    pub op_type: X86OperandType,
64}
65
66/// X86 operand
67#[derive(Clone, Debug, PartialEq, Eq)]
68pub enum X86OperandType {
69    /// Register
70    Reg(RegId),
71
72    /// Immediate
73    Imm(i64),
74
75    /// Memory
76    Mem(X86OpMem),
77
78    /// Invalid
79    Invalid,
80}
81
82/// X86 memory operand
83#[derive(Debug, Copy, Clone)]
84pub struct X86OpMem(pub(crate) x86_op_mem);
85
86impl X86InsnDetail<'_> {
87    /// Instruction prefix, which can be up to 4 bytes.
88    /// A prefix byte gets value 0 when irrelevant.
89    /// See `X86Prefix` for details.
90    ///
91    /// `prefix[0]` indicates REP/REPNE/LOCK prefix (See `X86_PREFIX_REP`/`REPNE`/`LOCK`)
92    ///
93    /// `prefix[1]` indicates segment override (irrelevant for x86_64):
94    /// See `X86_PREFIX_CS`/`SS`/`DS`/`ES`/`FS`/`GS`.
95    ///
96    /// `prefix[2]` indicates operand-size override (`X86_PREFIX_OPSIZE`)
97    ///
98    /// `prefix[3]` indicates address-size override (`X86_PREFIX_ADDRSIZE`)
99    pub fn prefix(&self) -> &[u8; 4] {
100        &self.0.prefix
101    }
102
103    /// Instruction opcode, which can be from 1 to 4 bytes in size.
104    /// This contains VEX opcode as well.
105    /// A trailing opcode byte gets value 0 when irrelevant.
106    pub fn opcode(&self) -> &[u8; 4] {
107        &self.0.opcode
108    }
109
110    /// Instruction encoding information, e.g. displacement offset, size.
111    pub fn encoding(&self) -> X86Encoding {
112        self.0.encoding.into()
113    }
114
115    /// REX prefix: only a non-zero value is relevant for x86_64
116    pub fn rex(&self) -> u8 {
117        self.0.rex
118    }
119
120    /// Address size
121    pub fn addr_size(&self) -> u8 {
122        self.0.addr_size
123    }
124
125    /// ModR/M byte
126    pub fn modrm(&self) -> u8 {
127        self.0.modrm
128    }
129
130    /// SIB (Scaled Index Byte) value, or 0 when irrelevant
131    pub fn sib(&self) -> u8 {
132        self.0.sib
133    }
134
135    /// Displacement value, valid if encoding.disp_offset != 0
136    pub fn disp(&self) -> i64 {
137        self.0.disp
138    }
139
140    /// Scaled Index Byte (SIB) index, or X86_REG_INVALID when irrelevant
141    pub fn sib_index(&self) -> RegId {
142        RegId(self.0.sib_index as RegIdInt)
143    }
144
145    /// Scaled Index Byte (SIB) scale, or X86_REG_INVALID when irrelevant
146    pub fn sib_scale(&self) -> i8 {
147        self.0.sib_scale
148    }
149
150    /// Scaled Index Byte (SIB) base register, or X86_REG_INVALID when irrelevant
151    pub fn sib_base(&self) -> RegId {
152        RegId(self.0.sib_base as RegIdInt)
153    }
154
155    /// eXtended Operations (XOP) Code Condition
156    pub fn xop_cc(&self) -> X86XopCC {
157        self.0.xop_cc
158    }
159
160    /// Streaming SIMD Extensions (SSE) condition  codes
161    pub fn sse_cc(&self) -> X86SseCC {
162        self.0.sse_cc
163    }
164
165    /// Advanced Vector Extensions (AVX) condition  codes
166    pub fn avx_cc(&self) -> X86AvxCC {
167        self.0.avx_cc
168    }
169
170    /// Advanced Vector Extensions (AVX) sae
171    pub fn avx_sae(&self) -> bool {
172        self.0.avx_sae
173    }
174
175    /// Advanced Vector Extensions (AVX) rm
176    pub fn avx_rm(&self) -> X86AvxRm {
177        self.0.avx_rm
178    }
179}
180
181impl_PartialEq_repr_fields!(X86InsnDetail<'a> [ 'a ];
182    prefix, opcode, rex, addr_size, modrm, sib, disp, sib_index, sib_scale, sib_base, sse_cc,
183    avx_cc, avx_sae, avx_rm, operands
184);
185
186impl X86OpMem {
187    /// Segment
188    pub fn segment(&self) -> RegId {
189        RegId(self.0.segment as RegIdInt)
190    }
191
192    /// Base register
193    pub fn base(&self) -> RegId {
194        RegId(self.0.base as RegIdInt)
195    }
196
197    /// Index register
198    pub fn index(&self) -> RegId {
199        RegId(self.0.index as RegIdInt)
200    }
201
202    /// Scale
203    pub fn scale(&self) -> i32 {
204        self.0.scale as i32
205    }
206
207    /// Display
208    pub fn disp(&self) -> i64 {
209        self.0.disp
210    }
211}
212
213impl_PartialEq_repr_fields!(X86OpMem;
214    segment, base, index, scale, disp
215);
216
217impl cmp::Eq for X86OpMem {}
218
219impl Default for X86Operand {
220    fn default() -> Self {
221        X86Operand {
222            size: 0,
223            access: None,
224            avx_bcast: X86AvxBcast::X86_AVX_BCAST_INVALID,
225            avx_zero_opmask: false,
226            op_type: X86OperandType::Invalid,
227        }
228    }
229}
230
231impl From<&cs_x86_op> for X86Operand {
232    fn from(op: &cs_x86_op) -> X86Operand {
233        let op_type = X86OperandType::new(op.type_, op.__bindgen_anon_1);
234        X86Operand {
235            size: op.size,
236            access: cs_ac_type(op.access as _).try_into().ok(),
237            avx_bcast: op.avx_bcast,
238            avx_zero_opmask: op.avx_zero_opmask,
239            op_type,
240        }
241    }
242}
243
244/// X86 instruction encoding infomation
245#[derive(Clone, Copy, Debug, PartialEq, Eq)]
246pub struct X86Encoding {
247    pub modrm_offset: u8,
248    pub disp: Option<InsnOffsetSpan>,
249    pub imm: Option<InsnOffsetSpan>,
250}
251
252impl From<cs_x86_encoding> for X86Encoding {
253    fn from(encoding: cs_x86_encoding) -> Self {
254        Self {
255            modrm_offset: encoding.modrm_offset,
256            disp: if encoding.disp_offset == 0 {
257                None
258            } else {
259                Some(InsnOffsetSpan {
260                    offset: encoding.disp_offset,
261                    size: encoding.disp_size,
262                })
263            },
264            imm: if encoding.imm_offset == 0 {
265                None
266            } else {
267                Some(InsnOffsetSpan {
268                    offset: encoding.imm_offset,
269                    size: encoding.imm_size,
270                })
271            },
272        }
273    }
274}
275
276def_arch_details_struct!(
277    InsnDetail = X86InsnDetail;
278    Operand = X86Operand;
279    OperandIterator = X86OperandIterator;
280    OperandIteratorLife = X86OperandIterator<'a>;
281    [ pub struct X86OperandIterator<'a>(slice::Iter<'a, cs_x86_op>); ]
282    cs_arch_op = cs_x86_op;
283    cs_arch = cs_x86;
284);
285
286#[cfg(test)]
287mod test {
288    use super::*;
289    use capstone_sys::*;
290
291    #[test]
292    fn test_x86_op_type() {
293        use super::x86_op_type::*;
294        use super::X86OperandType::*;
295
296        fn t(
297            op_type_value: (x86_op_type, cs_x86_op__bindgen_ty_1),
298            expected_op_type: X86OperandType,
299        ) {
300            let (op_type, op_value) = op_type_value;
301            let op_type = X86OperandType::new(op_type, op_value);
302            assert_eq!(expected_op_type, op_type);
303        }
304
305        t(
306            (X86_OP_INVALID, cs_x86_op__bindgen_ty_1 { reg: 0 }),
307            Invalid,
308        );
309        t(
310            (X86_OP_REG, cs_x86_op__bindgen_ty_1 { reg: 0 }),
311            Reg(RegId(0)),
312        );
313    }
314
315    #[test]
316    fn test_x86_op_eq() {
317        let a1 = X86Operand {
318            op_type: X86OperandType::Imm(0),
319            ..Default::default()
320        };
321        let a2 = X86Operand {
322            op_type: X86OperandType::Imm(-100),
323            ..Default::default()
324        };
325
326        assert_eq!(a1, a1.clone());
327        assert_ne!(a1, a2);
328    }
329
330    #[test]
331    fn test_x86_insn_eq() {
332        fn t_eq(a: &cs_x86, b: &cs_x86) {
333            assert_eq!(X86InsnDetail(a), X86InsnDetail(b))
334        }
335        fn t_ne(a: &cs_x86, b: &cs_x86) {
336            assert_ne!(X86InsnDetail(a), X86InsnDetail(b))
337        }
338
339        let a1 = cs_x86 {
340            prefix: [0, 0, 0, 0],
341            opcode: [0, 0, 0, 0],
342            rex: 0,
343            addr_size: 0,
344            modrm: 0,
345            sib: 0,
346            disp: 0,
347            sib_index: x86_reg::X86_REG_INVALID,
348            sib_scale: 0,
349            sib_base: x86_reg::X86_REG_INVALID,
350            sse_cc: x86_sse_cc::X86_SSE_CC_INVALID,
351            avx_cc: x86_avx_cc::X86_AVX_CC_INVALID,
352            avx_sae: false,
353            avx_rm: x86_avx_rm::X86_AVX_RM_INVALID,
354            op_count: 0,
355            __bindgen_anon_1: cs_x86__bindgen_ty_1 { eflags: 0 },
356            encoding: cs_x86_encoding {
357                modrm_offset: 0,
358                disp_offset: 0,
359                disp_size: 0,
360                imm_offset: 0,
361                imm_size: 0,
362            },
363            xop_cc: x86_xop_cc::X86_XOP_CC_INVALID,
364            operands: [cs_x86_op {
365                type_: x86_op_type::X86_OP_INVALID,
366                __bindgen_anon_1: cs_x86_op__bindgen_ty_1 {
367                    reg: x86_reg::X86_REG_INVALID,
368                },
369                size: 0,
370                avx_bcast: x86_avx_bcast::X86_AVX_BCAST_INVALID,
371                avx_zero_opmask: false,
372                access: 0,
373            }; 8],
374        };
375        let mut a2 = a1;
376        a2.operands[1].type_ = x86_op_type::X86_OP_REG;
377        let a1_clone = cs_x86 { ..a1 };
378        let a3 = cs_x86 { rex: 1, ..a1 };
379        let op_count_differ = cs_x86 { op_count: 1, ..a1 };
380        let mut op1_differ = op_count_differ;
381        op1_differ.operands[0].avx_bcast = x86_avx_bcast::X86_AVX_BCAST_2;
382
383        t_eq(&a1, &a1);
384        t_eq(&a1, &a2);
385        t_eq(&a1, &a1_clone);
386        t_ne(&a1, &a3);
387        t_ne(&a1, &op_count_differ);
388        t_ne(&op_count_differ, &op1_differ);
389    }
390
391    #[test]
392    fn test_x86_insn_encoding() {
393        assert_eq!(
394            X86Encoding::from(cs_x86_encoding {
395                modrm_offset: 0,
396                disp_offset: 0,
397                disp_size: 0,
398                imm_offset: 0,
399                imm_size: 0,
400            }),
401            X86Encoding {
402                modrm_offset: 0,
403                disp: None,
404                imm: None
405            }
406        );
407
408        assert_eq!(
409            X86Encoding::from(cs_x86_encoding {
410                modrm_offset: 1,
411                disp_offset: 2,
412                disp_size: 3,
413                imm_offset: 4,
414                imm_size: 5,
415            }),
416            X86Encoding {
417                modrm_offset: 1,
418                disp: Some(InsnOffsetSpan { offset: 2, size: 3 }),
419                imm: Some(InsnOffsetSpan { offset: 4, size: 5 }),
420            }
421        );
422    }
423}