capstone_git/
instruction.rs

1use alloc::{self, boxed::Box};
2use core::convert::TryFrom;
3use core::fmt::{self, Debug, Display, Error, Formatter};
4use core::marker::PhantomData;
5use core::mem::MaybeUninit;
6use core::ops::Deref;
7use core::slice;
8use core::str;
9
10use capstone_sys::*;
11
12use crate::arch::ArchDetail;
13use crate::constants::Arch;
14
15use crate::ffi::str_from_cstr_ptr;
16use crate::{RegsAccessBuf, REGS_ACCESS_BUF_LEN};
17
18/// Represents a slice of [`Insn`] returned by [`Capstone`](crate::Capstone) `disasm*()` methods.
19///
20/// To access inner [`&[Insn]`](Insn), use [`.as_ref()`](AsRef::as_ref).
21/// ```
22/// # use capstone::Instructions;
23/// # use capstone::prelude::*;
24/// # let cs = Capstone::new().x86().mode(arch::x86::ArchMode::Mode32).build().unwrap();
25/// let insns: Instructions = cs.disasm_all(b"\x55\x48\x8b\x05", 0x1000).unwrap();
26/// for insn in insns.as_ref() {
27///     println!("{}", insn);
28/// }
29/// ```
30#[derive(Debug)]
31pub struct Instructions<'a>(&'a mut [cs_insn]);
32
33/// Integer type used in `InsnId`
34pub type InsnIdInt = u32;
35
36/// Represents an instruction id, which may be architecture-specific.
37///
38/// To translate to a human-readable name, see [`Capstone::insn_name()`](crate::Capstone::insn_name).
39#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq, PartialOrd, Ord)]
40pub struct InsnId(pub InsnIdInt);
41
42/// Integer type used in `InsnGroupId`
43pub type InsnGroupIdInt = u8;
44
45/// Represents the group an instruction belongs to, which may be architecture-specific.
46///
47/// To translate to a human-readable name, see [`Capstone::group_name()`](crate::Capstone::group_name).
48#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq, PartialOrd, Ord)]
49#[repr(transparent)]
50pub struct InsnGroupId(pub InsnGroupIdInt);
51
52pub use capstone_sys::cs_group_type as InsnGroupType;
53
54/// Integer type used in `RegId`
55pub type RegIdInt = u16;
56
57/// Represents an register id, which is architecture-specific.
58///
59/// To translate to a human-readable name, see [`Capstone::reg_name()`](crate::Capstone::reg_name).
60#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq, PartialOrd, Ord)]
61#[repr(transparent)]
62pub struct RegId(pub RegIdInt);
63
64impl RegId {
65    /// Invalid Register
66    pub const INVALID_REG: Self = Self(0);
67}
68
69/// Represents how the operand is accessed.
70#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)]
71pub enum AccessType {
72    /// Operand read from memory or register.
73    ReadOnly,
74    /// Operand write from memory or register.
75    WriteOnly,
76    /// Operand read and write from memory or register.
77    ReadWrite,
78}
79
80impl AccessType {
81    /// Returns whether the instruction reads from the operand.
82    ///
83    /// Note that an instruction may read and write to the register
84    /// simultaneously. In this case, the operand is also considered as
85    /// readable.
86    pub fn is_readable(self) -> bool {
87        self == AccessType::ReadOnly || self == AccessType::ReadWrite
88    }
89
90    /// Returns whether the instruction writes from the operand.
91    ///
92    /// Note that an instruction may read and write to the register
93    /// simultaneously. In this case, the operand is also considered as
94    /// writable.
95    pub fn is_writable(self) -> bool {
96        self == AccessType::WriteOnly || self == AccessType::ReadWrite
97    }
98}
99
100impl TryFrom<cs_ac_type> for AccessType {
101    type Error = ();
102
103    fn try_from(access: cs_ac_type) -> Result<Self, Self::Error> {
104        // Check for flags other than CS_AC_READ or CS_AC_WRITE.
105        let unknown_flag_mask = !(cs_ac_type::CS_AC_READ | cs_ac_type::CS_AC_WRITE).0;
106        if (access.0 & unknown_flag_mask) != 0 {
107            return Err(());
108        }
109
110        let is_readable = (access & cs_ac_type::CS_AC_READ).0 != 0;
111        let is_writable = (access & cs_ac_type::CS_AC_WRITE).0 != 0;
112        match (is_readable, is_writable) {
113            (true, false) => Ok(AccessType::ReadOnly),
114            (false, true) => Ok(AccessType::WriteOnly),
115            (true, true) => Ok(AccessType::ReadWrite),
116            _ => Err(()),
117        }
118    }
119}
120
121/// Previously the enum was called RegAccessType, see issue #135
122/// Maintain compatibility with legacy code
123pub type RegAccessType = AccessType;
124
125impl<'a> Instructions<'a> {
126    pub(crate) unsafe fn from_raw_parts(ptr: *mut cs_insn, len: usize) -> Instructions<'a> {
127        Instructions(slice::from_raw_parts_mut(ptr, len))
128    }
129
130    pub(crate) fn new_empty() -> Instructions<'a> {
131        Instructions(&mut [])
132    }
133}
134
135impl<'a> core::ops::Deref for Instructions<'a> {
136    type Target = [Insn<'a>];
137
138    #[inline]
139    fn deref(&self) -> &[Insn<'a>] {
140        // SAFETY: `cs_insn` has the same memory layout as `Insn`
141        unsafe { &*(self.0 as *const [cs_insn] as *const [Insn]) }
142    }
143}
144
145impl<'a> AsRef<[Insn<'a>]> for Instructions<'a> {
146    #[inline]
147    fn as_ref(&self) -> &[Insn<'a>] {
148        self.deref()
149    }
150}
151
152impl Drop for Instructions<'_> {
153    fn drop(&mut self) {
154        if !self.is_empty() {
155            unsafe {
156                cs_free(self.0.as_mut_ptr(), self.len());
157            }
158        }
159    }
160}
161
162/// A single disassembled CPU instruction.
163///
164/// # Detail
165///
166/// To learn how to get more instruction details, see [`InsnDetail`].
167#[repr(transparent)]
168pub struct Insn<'a> {
169    /// Inner `cs_insn`
170    pub(crate) insn: cs_insn,
171
172    /// Adds lifetime
173    pub(crate) _marker: PhantomData<&'a InsnDetail<'a>>,
174}
175
176pub(crate) struct RWRegsAccessBuf {
177    pub(crate) read_buf: RegsAccessBuf,
178    pub(crate) write_buf: RegsAccessBuf,
179}
180
181impl RWRegsAccessBuf {
182    pub(crate) fn new() -> Self {
183        Self {
184            read_buf: [MaybeUninit::uninit(); REGS_ACCESS_BUF_LEN],
185            write_buf: [MaybeUninit::uninit(); REGS_ACCESS_BUF_LEN],
186        }
187    }
188}
189
190/// Contains partially initialized buffer of registers
191#[cfg_attr(not(feature = "full"), allow(dead_code))]
192pub(crate) struct PartialInitRegsAccess {
193    pub(crate) regs_buf: Box<RWRegsAccessBuf>,
194    pub(crate) read_len: u16,
195    pub(crate) write_len: u16,
196}
197
198// make sure len fields can be stored as u16
199static_assertions::const_assert!(crate::REGS_ACCESS_BUF_LEN <= u16::MAX as usize);
200
201#[cfg_attr(not(feature = "full"), allow(dead_code))]
202impl PartialInitRegsAccess {
203    unsafe fn maybeuninit_slice_to_slice(buf: &[MaybeUninit<RegId>]) -> &[RegId] {
204        &*(buf as *const [MaybeUninit<RegId>] as *const [RegId])
205    }
206
207    pub(crate) fn read(&self) -> &[RegId] {
208        unsafe {
209            Self::maybeuninit_slice_to_slice(&self.regs_buf.read_buf[..self.read_len as usize])
210        }
211    }
212
213    pub(crate) fn write(&self) -> &[RegId] {
214        unsafe {
215            Self::maybeuninit_slice_to_slice(&self.regs_buf.write_buf[..self.write_len as usize])
216        }
217    }
218}
219
220/// Contains architecture-independent details about an [`Insn`].
221///
222/// To get more detail about the instruction, enable extra details for the
223/// [`Capstone`](crate::Capstone) instance with
224/// [`Capstone::set_detail(True)`](crate::Capstone::set_detail) and use
225/// [`Capstone::insn_detail()`](crate::Capstone::insn_detail).
226///
227/// ```
228/// # use capstone::Instructions;
229/// # use capstone::prelude::*;
230/// let cs = Capstone::new()
231///     .x86()
232///     .mode(arch::x86::ArchMode::Mode32)
233///     .detail(true) // needed to enable detail
234///     .build()
235///     .unwrap();
236/// let insns = cs.disasm_all(b"\x90", 0x1000).unwrap();
237/// for insn in insns.as_ref() {
238///     println!("{}", insn);
239///     let insn_detail: InsnDetail = cs.insn_detail(insn).unwrap();
240///     println!("    {:?}", insn_detail.groups());
241/// }
242/// ```
243///
244/// # Arch-specific detail
245///
246/// To get additional architecture-specific information, use the
247/// [`.arch_detail()`](Self::arch_detail) method to get an `ArchDetail` enum.
248///
249pub struct InsnDetail<'a> {
250    pub(crate) detail: &'a cs_detail,
251    pub(crate) arch: Arch,
252
253    #[cfg_attr(not(feature = "full"), allow(dead_code))]
254    partial_init_regs_access: Option<PartialInitRegsAccess>,
255}
256
257#[allow(clippy::len_without_is_empty)]
258impl Insn<'_> {
259    /// Create an `Insn` from a raw pointer to a [`capstone_sys::cs_insn`].
260    ///
261    /// This function serves to allow integration with libraries which generate `capstone_sys::cs_insn`'s internally.
262    ///
263    /// # Safety
264    ///
265    /// Note that this function is unsafe, and assumes that you know what you are doing. In
266    /// particular, it generates a lifetime for the `Insn` from nothing, and that lifetime is in
267    /// no-way actually tied to the cs_insn itself. It is the responsibility of the caller to
268    /// ensure that the resulting `Insn` lives only as long as the `cs_insn`. This function
269    /// assumes that the pointer passed is non-null and a valid `cs_insn` pointer.
270    ///
271    /// The caller is fully responsible for the backing allocations lifetime, including freeing.
272    pub unsafe fn from_raw(insn: *const cs_insn) -> Self {
273        Self {
274            insn: core::ptr::read(insn),
275            _marker: PhantomData,
276        }
277    }
278
279    /// The mnemonic for the instruction.
280    /// Unavailable in Diet mode.
281    #[inline]
282    pub fn mnemonic(&self) -> Option<&str> {
283        if cfg!(feature = "full") {
284            unsafe { str_from_cstr_ptr(self.insn.mnemonic.as_ptr()) }
285        } else {
286            None
287        }
288    }
289
290    /// The operand string associated with the instruction.
291    /// Unavailable in Diet mode.
292    #[inline]
293    pub fn op_str(&self) -> Option<&str> {
294        if cfg!(feature = "full") {
295            unsafe { str_from_cstr_ptr(self.insn.op_str.as_ptr()) }
296        } else {
297            None
298        }
299    }
300
301    /// Access instruction id
302    #[inline]
303    pub fn id(&self) -> InsnId {
304        InsnId(self.insn.id)
305    }
306
307    /// Size of instruction (in bytes)
308    #[inline]
309    pub fn len(&self) -> usize {
310        self.insn.size as usize
311    }
312
313    /// Instruction address
314    #[inline]
315    pub fn address(&self) -> u64 {
316        self.insn.address
317    }
318
319    /// Byte-level representation of the instruction
320    #[inline]
321    pub fn bytes(&self) -> &[u8] {
322        &self.insn.bytes[..self.len()]
323    }
324
325    /// Returns the `Detail` object, if there is one. It is up to the caller to determine
326    /// the pre-conditions are satisfied.
327    ///
328    /// Be careful this is still in early stages and largely untested with various `cs_option` and
329    /// architecture matrices
330    ///
331    /// # Safety
332    /// The [`cs_insn::detail`] pointer must be valid and non-null.
333    #[inline]
334    pub(crate) unsafe fn detail(
335        &self,
336        arch: Arch,
337        partial_init_regs_access: Option<PartialInitRegsAccess>,
338    ) -> InsnDetail<'_> {
339        InsnDetail {
340            detail: &*self.insn.detail,
341            arch,
342            partial_init_regs_access,
343        }
344    }
345}
346
347impl From<&Insn<'_>> for OwnedInsn<'_> {
348    // SAFETY: assumes that `cs_detail` struct transitively only contains owned
349    // types and no pointers, including the union over the architecture-specific
350    // types.
351    fn from(insn: &Insn<'_>) -> Self {
352        let mut new = unsafe { <*const cs_insn>::read(&insn.insn as _) };
353        new.detail = if new.detail.is_null() {
354            new.detail
355        } else {
356            unsafe {
357                let new_detail = Box::new(*new.detail);
358                Box::into_raw(new_detail)
359            }
360        };
361        Self {
362            insn: new,
363            _marker: PhantomData,
364        }
365    }
366}
367
368/// SAFETY:
369/// 1. [`OwnedInsn`] and [`Insn`] must be `#repr(transparent)` of [`cs_insn`]
370/// 2. all [`Insn`] methods must be safe to perform for an [`OwnedInsn`]
371impl<'a> Deref for OwnedInsn<'a> {
372    type Target = Insn<'a>;
373    fn deref(&self) -> &Self::Target {
374        unsafe { &*(&self.insn as *const cs_insn as *const Insn) }
375    }
376}
377
378/// A single disassembled CPU instruction that lives on the Rust heap.
379///
380/// # Detail
381///
382/// To learn how to get more instruction details, see [`InsnDetail`].
383pub struct OwnedInsn<'a> {
384    /// Inner cs_insn
385    pub(crate) insn: cs_insn,
386
387    /// Adds lifetime
388    pub(crate) _marker: PhantomData<&'a InsnDetail<'a>>,
389}
390
391impl Debug for Insn<'_> {
392    fn fmt(&self, fmt: &mut Formatter) -> Result<(), Error> {
393        fmt.debug_struct("Insn")
394            .field("address", &self.address())
395            .field("len", &self.len())
396            .field("bytes", &self.bytes())
397            .field("mnemonic", &self.mnemonic())
398            .field("op_str", &self.op_str())
399            .finish()
400    }
401}
402
403impl Display for Insn<'_> {
404    fn fmt(&self, fmt: &mut Formatter) -> fmt::Result {
405        write!(fmt, "{:#x}:", self.address())?;
406        if let Some(mnemonic) = self.mnemonic() {
407            write!(fmt, " {mnemonic}")?;
408            if let Some(op_str) = self.op_str() {
409                if !op_str.is_empty() {
410                    write!(fmt, " {op_str}")?;
411                }
412            }
413        }
414        Ok(())
415    }
416}
417
418impl Drop for OwnedInsn<'_> {
419    fn drop(&mut self) {
420        if let Some(ptr) = core::ptr::NonNull::new(self.insn.detail) {
421            unsafe { drop(Box::from_raw(ptr.as_ptr())) }
422        }
423    }
424}
425
426impl Debug for OwnedInsn<'_> {
427    fn fmt(&self, fmt: &mut Formatter) -> Result<(), Error> {
428        Debug::fmt(&self.deref(), fmt)
429    }
430}
431
432impl Display for OwnedInsn<'_> {
433    fn fmt(&self, fmt: &mut Formatter) -> fmt::Result {
434        Display::fmt(&self.deref(), fmt)
435    }
436}
437
438impl InsnDetail<'_> {
439    #[cfg(feature = "full")]
440    /// Returns the read registers
441    pub fn regs_read(&self) -> &[RegId] {
442        if let Some(partial) = self.partial_init_regs_access.as_ref() {
443            partial.read()
444        } else {
445            unsafe {
446                &*(&self.detail.regs_read[..self.detail.regs_read_count as usize]
447                    as *const [RegIdInt] as *const [RegId])
448            }
449        }
450    }
451
452    #[cfg(feature = "full")]
453    /// Returns the written to registers
454    pub fn regs_write(&self) -> &[RegId] {
455        if let Some(partial) = self.partial_init_regs_access.as_ref() {
456            partial.write()
457        } else {
458            unsafe {
459                &*(&self.detail.regs_write[..self.detail.regs_write_count as usize]
460                    as *const [RegIdInt] as *const [RegId])
461            }
462        }
463    }
464
465    #[cfg(feature = "full")]
466    /// Returns the groups to which this instruction belongs
467    pub fn groups(&self) -> &[InsnGroupId] {
468        unsafe {
469            &*(&self.detail.groups[..self.detail.groups_count as usize] as *const [InsnGroupIdInt]
470                as *const [InsnGroupId])
471        }
472    }
473
474    /// Architecture-specific detail
475    pub fn arch_detail(&self) -> ArchDetail<'_> {
476        macro_rules! def_arch_detail_match {
477            (
478                $( [ $ARCH:ident, $detail:ident, $insn_detail:ident, $arch:ident, $feature:literal ] )*
479            ) => {
480                use self::ArchDetail::*;
481                use crate::Arch::*;
482                $(
483                    #[cfg(feature = $feature)]
484                    use crate::arch::$arch::$insn_detail;
485                )*
486
487                return match self.arch {
488                    $(
489                        #[cfg(feature = $feature)]
490                        $ARCH => {
491                            $detail($insn_detail(unsafe { &self.detail.__bindgen_anon_1.$arch }))
492                        }
493                    )*,
494                    // handle disabled archs if not all archs are enabled
495                    #[allow(unreachable_patterns)]
496                    _ => panic!("Cannot convert to arch-specific detail of disabled arch ")
497                }
498            }
499        }
500        def_arch_detail_match!(
501            [ARM, ArmDetail, ArmInsnDetail, arm, "arch_arm"]
502            [ARM64, Arm64Detail, Arm64InsnDetail, arm64, "arch_arm64"]
503            [BPF, BpfDetail, BpfInsnDetail, bpf, "arch_bpf"]
504            [EVM, EvmDetail, EvmInsnDetail, evm, "arch_evm"]
505            [M680X, M680xDetail, M680xInsnDetail, m680x, "arch_m680x"]
506            [M68K, M68kDetail, M68kInsnDetail, m68k, "arch_m68k"]
507            [MIPS, MipsDetail, MipsInsnDetail, mips, "arch_mips"]
508            [MOS65XX, Mos65xxDetail, Mos65xxInsnDetail, mos65xx, "arch_mos65xx"]
509            [PPC, PpcDetail, PpcInsnDetail, ppc, "arch_powerpc"]
510            [RISCV, RiscVDetail, RiscVInsnDetail, riscv, "arch_riscv"]
511            [SH, ShDetail, ShInsnDetail, sh, "arch_sh"]
512            [SPARC, SparcDetail, SparcInsnDetail, sparc, "arch_sparc"]
513            [SYSZ, SysZDetail, SysZInsnDetail, sysz, "arch_sysz"]
514            [TMS320C64X, Tms320c64xDetail, Tms320c64xInsnDetail, tms320c64x, "arch_tms320c64x"]
515            [TRICORE, TriCoreDetail, TriCoreInsnDetail, tricore, "arch_tricore"]
516            [X86, X86Detail, X86InsnDetail, x86, "arch_x86"]
517            [XCORE, XcoreDetail, XcoreInsnDetail, xcore, "arch_xcore"]
518        );
519    }
520}
521
522#[cfg(feature = "full")]
523impl Debug for InsnDetail<'_> {
524    fn fmt(&self, fmt: &mut Formatter) -> fmt::Result {
525        fmt.debug_struct("Detail")
526            .field("regs_read", &self.regs_read())
527            .field("regs_write", &self.regs_write())
528            .field("groups", &self.groups())
529            .finish()
530    }
531}
532
533#[cfg(not(feature = "full"))]
534impl<'a> Debug for InsnDetail<'a> {
535    fn fmt(&self, fmt: &mut Formatter) -> fmt::Result {
536        fmt.debug_struct("Detail").finish()
537    }
538}
539
540impl Display for Instructions<'_> {
541    fn fmt(&self, fmt: &mut Formatter) -> fmt::Result {
542        for instruction in self.iter() {
543            write!(fmt, "{:x}:\t", instruction.address())?;
544            for byte in instruction.bytes() {
545                write!(fmt, " {byte:02x}")?;
546            }
547            let remainder = 16 * 3 - instruction.bytes().len() * 3;
548            for _ in 0..remainder {
549                write!(fmt, " ")?;
550            }
551            if let Some(mnemonic) = instruction.mnemonic() {
552                write!(fmt, " {mnemonic}")?;
553                if let Some(op_str) = instruction.op_str() {
554                    write!(fmt, " {op_str}")?;
555                }
556            }
557            writeln!(fmt)?;
558        }
559        Ok(())
560    }
561}
562
563#[cfg(test)]
564mod test {
565    use super::*;
566
567    #[test]
568    fn test_invalid_reg_access() {
569        assert_eq!(RegAccessType::try_from(cs_ac_type(1337)), Err(()));
570    }
571}