ep_capstone/
lib.rs

1#![cfg_attr(not(feature = "std"), no_std)]
2
3#[cfg(all(not(feature = "std"), feature = "alloc"))]
4extern crate alloc;
5
6#[macro_use]
7mod macros;
8pub mod arch;
9mod insn;
10mod sys;
11mod util;
12
13use core::{convert::From, fmt, marker::PhantomData, ptr::NonNull};
14
15#[cfg(feature = "std")]
16use std::{
17    self as alloc, borrow::Cow, cell::RefCell, collections::HashMap as Map, panic::UnwindSafe,
18};
19
20#[cfg(all(not(feature = "std"), feature = "alloc"))]
21use alloc::{borrow::Cow, boxed::Box, collections::BTreeMap as Map};
22
23pub use arch::{InsnGroup, InsnId, Reg};
24pub use insn::{ArchDetails, Details, Insn, InsnBuffer, InsnIter};
25
26pub use arch::arm;
27pub use arch::arm64;
28pub use arch::evm;
29pub use arch::m680x;
30pub use arch::m68k;
31pub use arch::mips;
32pub use arch::mos65xx;
33pub use arch::ppc;
34pub use arch::sparc;
35pub use arch::sysz;
36pub use arch::tms320c64x;
37pub use arch::x86;
38pub use arch::xcore;
39
40#[cfg(feature = "std")]
41pub type SkipdataCallback = dyn 'static + UnwindSafe + FnMut(&[u8], usize) -> usize;
42
43#[cfg(all(not(feature = "std"), feature = "alloc"))]
44pub type SkipdataCallback = dyn 'static + FnMut(&[u8], usize) -> usize;
45
46#[cfg(all(not(feature = "std"), not(feature = "alloc")))]
47pub type SkipdataCallback = fn(&[u8], usize) -> usize;
48
49/// This is just used to make the Capstone instance !Send and !Sync
50struct NotSend(*mut u8);
51
52/// A capstone instance that can be used for disassembly.
53pub struct Capstone {
54    handle: sys::Handle,
55    packed: PackedCSInfo,
56
57    #[cfg(feature = "alloc")]
58    mnemonics: Map<InsnId, Cow<'static, str>>,
59
60    #[cfg(feature = "alloc")]
61    skipdata_callback: Option<Box<SkipdataCallback>>,
62
63    #[cfg(feature = "alloc")]
64    skipdata_mnemonic: Option<Cow<'static, str>>,
65
66    #[cfg(not(feature = "alloc"))]
67    skipdata_callback: Option<SkipdataCallback>,
68
69    #[cfg(feature = "std")]
70    pending_panic: RefCell<Option<Box<dyn std::any::Any + Send + 'static>>>,
71
72    disable_send: PhantomData<NotSend>,
73}
74
75impl Capstone {
76    /// Initializes capstone with the given arch and mode.
77    pub fn open(arch: Arch, mode: Mode) -> Result<Self, Error> {
78        let mut handle = sys::Handle(0);
79
80        result! {
81            unsafe { sys::cs_open(arch.into(), mode.into(), &mut handle) },
82            Capstone {
83                handle,
84                packed: PackedCSInfo::new(arch, false, false),
85                skipdata_callback: None,
86
87                #[cfg(feature = "alloc")]
88                mnemonics: Map::new(),
89
90                #[cfg(feature = "alloc")]
91                skipdata_mnemonic: None,
92
93                #[cfg(feature = "std")]
94                pending_panic: RefCell::new(None),
95
96                disable_send: PhantomData,
97            }
98        }
99    }
100
101    /// Retrieves some general details about an instruction. This value is
102    /// only available if the engine was not compiled in DIET mode and details
103    /// mode is turned on for this instance of Capstone. If details about an
104    /// instruction are not available, this will **panic**.
105    ///
106    /// # Panics
107    ///
108    /// If instruction details were not turned on for this Capstone instance
109    /// or if Capstone was compiled in DIET mode.
110    pub fn details<'i>(&self, insn: &'i Insn) -> insn::Details<'i> {
111        self.try_details(insn)
112            .expect("instruction details are not available")
113    }
114
115    /// Retrieves some general details about an instruction. This value is
116    /// only available if the engine was not compiled in DIET mode and details
117    /// mode is turned on for this instance of Capstone. If details about an
118    /// instruction are not available, this will return [`Option::None`].
119    pub fn try_details<'i>(&self, insn: &'i Insn) -> Option<insn::Details<'i>> {
120        if !self.details_enabled() {
121            return None;
122        }
123
124        unsafe {
125            insn.detail
126                .as_ref()
127                .map(|r| insn::Details::wrap(self.packed.arch(), r))
128        }
129    }
130
131    /// Reports the last error that occurred in the API after a function
132    /// has failed. Like glibc's errno, this might not retain its old value
133    /// once it has been accessed.
134    fn errno(&self) -> Result<(), Error> {
135        result!(unsafe { sys::cs_errno(self.handle) })
136    }
137
138    /// Disassembles all of the instructions in a buffer with the given
139    /// starting address. This will dynamically allocate memory to
140    /// contain the disassembled instructions.
141    pub fn disasm<'s>(&'s self, code: &[u8], address: u64) -> Result<InsnBuffer<'s>, Error> {
142        self.priv_disasm(code, address, 0)
143    }
144
145    /// Disassembles at most `count` instructions from the buffer using
146    /// the given starting address. This will dynamically allocate memory
147    /// to contain the disassembled instructions.
148    pub fn disasm_count<'s>(
149        &'s self,
150        code: &[u8],
151        address: u64,
152        count: usize,
153    ) -> Result<InsnBuffer<'s>, Error> {
154        if count == 0 {
155            Ok(InsnBuffer::new(NonNull::dangling().as_ptr(), 0))
156        } else {
157            self.priv_disasm(code, address, count)
158        }
159    }
160
161    /// Disassembles a binary given a buffer, a starting address, and the number
162    /// of instructions to disassemble. If `count` is `0`, this will disassbmle
163    /// all of the instructiosn in the buffer. This API will dynamically allocate
164    /// memory to contain the disassembled instructions.
165    fn priv_disasm<'s>(
166        &'s self,
167        code: &[u8],
168        address: u64,
169        count: usize,
170    ) -> Result<InsnBuffer<'s>, Error> {
171        let mut insn: *mut Insn = core::ptr::null_mut();
172
173        // the real count
174        let count = unsafe {
175            sys::cs_disasm(
176                self.handle,
177                code.as_ptr(),
178                code.len() as libc::size_t,
179                address,
180                count as libc::size_t,
181                &mut insn,
182            )
183        } as usize;
184
185        #[cfg(feature = "std")]
186        self.resume_panic();
187
188        if count == 0 {
189            self.errno()?;
190            return Err(Error::Bindings);
191        }
192
193        Ok(InsnBuffer::new(insn, count))
194    }
195
196    /// Returns an iterator that will lazily disassemble the instructions
197    /// in the given binary.
198    pub fn disasm_iter<'s>(&'s self, code: &[u8], address: u64) -> InsnIter<'s> {
199        let insn = unsafe { sys::cs_malloc(self.handle) };
200        assert!(!insn.is_null(), "cs_malloc() returned a null insn");
201
202        InsnIter::new(
203            self,
204            insn,
205            code.as_ptr(),
206            code.len() as libc::size_t,
207            address,
208        )
209    }
210
211    /// Sets the assembly syntax for the disassembling engine at runtime.
212    ///
213    /// If the syntax is supported then [`Result::Ok`] is returned
214    /// with no value. If the syntax is not supported then [`Result::Err`]
215    /// is returned.
216    pub fn set_syntax(&mut self, syntax: Syntax) -> Result<(), Error> {
217        match syntax {
218            Syntax::Default => self.set_option(sys::OptType::Syntax, sys::OPT_VALUE_SYNTAX_DEFAULT),
219            Syntax::Intel => self.set_option(sys::OptType::Syntax, sys::OPT_VALUE_SYNTAX_INTEL),
220            Syntax::Att => self.set_option(sys::OptType::Syntax, sys::OPT_VALUE_SYNTAX_ATT),
221            Syntax::NoRegName => {
222                self.set_option(sys::OptType::Syntax, sys::OPT_VALUE_SYNTAX_NOREGNAME)
223            }
224            Syntax::Masm => self.set_option(sys::OptType::Syntax, sys::OPT_VALUE_SYNTAX_MASM),
225        }
226    }
227
228    /// Change the engine's mode at runtime after it has been initialized.
229    pub fn set_mode(&mut self, mode: Mode) -> Result<(), Error> {
230        self.set_option(sys::OptType::Mode, mode.bits() as libc::size_t)
231    }
232
233    /// Setting `detail` to true will make the disassembling engine break
234    /// down instruction structure into details.
235    pub fn set_details_enabled(&mut self, detail: bool) -> Result<(), Error> {
236        self.set_option(
237            sys::OptType::Detail,
238            if detail {
239                sys::OPT_VALUE_ON
240            } else {
241                sys::OPT_VALUE_OFF
242            },
243        )?;
244
245        self.packed.set_detail(detail);
246        Ok(())
247    }
248
249    /// Setting `unsigned` to true will make the disassembling engine print
250    /// immediate operands in unsigned form.
251    pub fn set_unsigned(&mut self, unsigned: bool) -> Result<(), Error> {
252        self.set_option(
253            sys::OptType::Unsigned,
254            if unsigned {
255                sys::OPT_VALUE_ON
256            } else {
257                sys::OPT_VALUE_OFF
258            },
259        )?;
260        Ok(())
261    }
262
263    /// Removes a custom mnemonic that was previously set by [`Capstone::set_mnemonic`].
264    pub fn reset_mnemonic<I>(&mut self, insn: I) -> Result<(), Error>
265    where
266        I: Into<InsnId>,
267    {
268        self.set_mnemonic_inner(insn.into(), core::ptr::null())
269    }
270
271    /// Customize the mnemonic for an instruction with an alternative name.
272    #[cfg(feature = "alloc")]
273    pub fn set_mnemonic<I, M>(&mut self, insn: I, mnemonic: M) -> Result<(), Error>
274    where
275        I: Into<InsnId>,
276        M: Into<Cow<'static, str>>,
277    {
278        let insn = insn.into();
279        let mnemonic = util::ensure_c_string(mnemonic.into());
280        let mnemonic_ptr = mnemonic.as_ptr() as *const libc::c_char; // this is a stable pointer to string data.
281        self.mnemonics.insert(insn, mnemonic);
282
283        self.set_mnemonic_inner(insn, mnemonic_ptr)
284    }
285
286    /// Customize the mnemonic for an instruction with an alternative name.
287    /// `mnemonic` must be a valid C string (must end with the null terminator `\0`).
288    ///
289    /// # Panics
290    /// If `mnemonic` is not a valid C string.
291    #[cfg(not(feature = "alloc"))]
292    pub fn set_mnemonic<I, M>(&mut self, insn: I, mnemonic: &'static str) -> Result<(), Error>
293    where
294        I: Into<InsnId>,
295    {
296        let insn = insn.into();
297        let mnemonic = util::ensure_c_string(mnemonic);
298        let mnemonic_ptr = mnemonic.as_ptr() as *const libc::c_char; // this is a stable pointer to string data.
299        self.set_mnemonic_inner(insn, mnemonic_ptr)
300    }
301
302    /// Customize the mnemonic for an instruction with an alternative name.
303    fn set_mnemonic_inner(
304        &mut self,
305        insn: InsnId,
306        mnemonic: *const libc::c_char,
307    ) -> Result<(), Error> {
308        let mut opt_mnem = sys::OptMnemonic {
309            id: insn.to_c(),
310            mnemonic,
311        };
312
313        self.set_option(
314            sys::OptType::Mnemonic,
315            &mut opt_mnem as *mut _ as usize as libc::size_t,
316        )
317    }
318
319    /// Sets a custom setup for SKIPDATA mode.
320    ///
321    /// Setting mnemonic allows for customizing the mnemonic of the instruction
322    /// used to represent data. By default this will be `.byte`.
323    ///
324    /// The user defined callback (if there is one) will be called whenever
325    /// Capstone hits data. If the returned value from the callback is positive (greater than `0`), Capstone
326    /// will skip exactly that number of bytes and continue. Otherwise, if the callback retruns `0`,
327    /// Capstone stops disassembling and returns immediately from [`Capstone::disasm`] or causes
328    /// the [`Iterator`] from [`Capstone::disasm_iter`] to return [`None`].
329    ///
330    /// # Note
331    ///
332    /// If the callback is `None`, Capstone will skip a number of bytes depending on the
333    /// architecture:
334    ///
335    /// * Arm:     2 bytes (Thumb mode) or 4 bytes.
336    /// * Arm64:   4 bytes.
337    /// * Mips:    4 bytes.
338    /// * M680x:   1 byte.
339    /// * PowerPC: 4 bytes.
340    /// * Sparc:   4 bytes.
341    /// * SystemZ: 2 bytes.
342    /// * X86:     1 bytes.
343    /// * XCore:   2 bytes.
344    /// * EVM:     1 bytes.
345    /// * MOS65XX: 1 bytes.
346    #[cfg(all(not(feature = "std"), feature = "alloc"))]
347    pub fn setup_skipdata<M, F>(
348        &mut self,
349        mnemonic: Option<M>,
350        callback: Option<F>,
351    ) -> Result<(), Error>
352    where
353        M: Into<Cow<'static, str>>,
354        F: 'static + FnMut(&[u8], usize) -> usize,
355    {
356        self.skipdata_mnemonic = mnemonic.map(|m| util::ensure_c_string(m.into()));
357        self.skipdata_callback = callback.map(|c| Box::new(c) as _);
358
359        let setup = sys::OptSkipdataSetup {
360            mnemonic: self
361                .skipdata_mnemonic
362                .as_ref()
363                .map(|m| unsafe { NonNull::new_unchecked((&*m).as_ptr() as *mut libc::c_char) }),
364            callback: self.skipdata_callback.as_ref().map(|_| cs_skipdata_cb as _),
365            userdata: self as *mut Self as *mut libc::c_void,
366        };
367
368        self.set_option(
369            sys::OptType::SkipdataSetup,
370            &setup as *const _ as usize as libc::size_t,
371        )?;
372        Ok(())
373    }
374
375    /// Sets a custom setup for SKIPDATA mode.
376    ///
377    /// Setting mnemonic allows for customizing the mnemonic of the instruction
378    /// used to represent data. By default this will be `.byte`.
379    ///
380    /// The user defined callback (if there is one) will be called whenever
381    /// Capstone hits data. If the returned value from the callback is positive (greater than `0`), Capstone
382    /// will skip exactly that number of bytes and continue. Otherwise, if the callback retruns `0`,
383    /// Capstone stops disassembling and returns immediately from [`Capstone::disasm`] or causes
384    /// the [`Iterator`] from [`Capstone::disasm_iter`] to return [`None`].
385    ///
386    /// # Note
387    ///
388    /// If the callback is `None`, Capstone will skip a number of bytes depending on the
389    /// architecture:
390    ///
391    /// * Arm:     2 bytes (Thumb mode) or 4 bytes.
392    /// * Arm64:   4 bytes.
393    /// * Mips:    4 bytes.
394    /// * M680x:   1 byte.
395    /// * PowerPC: 4 bytes.
396    /// * Sparc:   4 bytes.
397    /// * SystemZ: 2 bytes.
398    /// * X86:     1 bytes.
399    /// * XCore:   2 bytes.
400    /// * EVM:     1 bytes.
401    /// * MOS65XX: 1 bytes.
402    #[cfg(feature = "std")]
403    pub fn setup_skipdata<M, F>(
404        &mut self,
405        mnemonic: Option<M>,
406        callback: Option<F>,
407    ) -> Result<(), Error>
408    where
409        M: Into<Cow<'static, str>>,
410        F: 'static + UnwindSafe + FnMut(&[u8], usize) -> usize,
411    {
412        self.skipdata_mnemonic = mnemonic.map(|m| util::ensure_c_string(m.into()));
413        self.skipdata_callback = callback.map(|c| Box::new(c) as _);
414
415        let setup = sys::OptSkipdataSetup {
416            mnemonic: self
417                .skipdata_mnemonic
418                .as_ref()
419                .map(|m| unsafe { NonNull::new_unchecked((&*m).as_ptr() as *mut libc::c_char) }),
420            callback: self.skipdata_callback.as_ref().map(|_| cs_skipdata_cb as _),
421            userdata: self as *mut Self as *mut libc::c_void,
422        };
423
424        self.set_option(
425            sys::OptType::SkipdataSetup,
426            &setup as *const _ as usize as libc::size_t,
427        )?;
428        Ok(())
429    }
430
431    /// Sets a custom setup for SKIPDATA mode.
432    ///
433    /// Setting mnemonic allows for customizing the mnemonic of the instruction
434    /// used to represent data. By default this will be `.byte`.
435    ///
436    /// The user defined callback (if there is one) will be called whenever
437    /// Capstone hits data. If the returned value from the callback is positive (greater than `0`), Capstone
438    /// will skip exactly that number of bytes and continue. Otherwise, if the callback retruns `0`,
439    /// Capstone stops disassembling and returns immediately from [`Capstone::disasm`] or causes
440    /// the [`Iterator`] from [`Capstone::disasm_iter`] to return [`None`].
441    ///
442    /// # Note
443    ///
444    /// `mnemonic` must be a valid C string (it must end with the null terminator `\0`.
445    ///
446    ///
447    /// If the callback is `None`, Capstone will skip a number of bytes depending on the
448    /// architecture:
449    ///
450    /// * Arm:     2 bytes (Thumb mode) or 4 bytes.
451    /// * Arm64:   4 bytes.
452    /// * Mips:    4 bytes.
453    /// * M680x:   1 byte.
454    /// * PowerPC: 4 bytes.
455    /// * Sparc:   4 bytes.
456    /// * SystemZ: 2 bytes.
457    /// * X86:     1 bytes.
458    /// * XCore:   2 bytes.
459    /// * EVM:     1 bytes.
460    /// * MOS65XX: 1 bytes.
461    ///
462    /// # Panics
463    /// If `mnemonic` is not a valid C string.
464    #[cfg(not(feature = "alloc"))]
465    pub fn setup_skipdata<M, F>(
466        &mut self,
467        mnemonic: Option<&'static str>,
468        callback: Option<fn(&[u8], usize) -> usize>,
469    ) -> Result<(), Error> {
470        self.skipdata_callback = callback;
471
472        let setup = sys::OptSkipdataSetup {
473            mnemonic: mnemonic.map(|m| {
474                let m = util::ensure_c_string(m);
475                unsafe { NonNull::new_unchecked(m.as_ptr() as *mut libc::c_char) }
476            }),
477            callback: self.skipdata_callback.as_ref().map(|_| cs_skipdata_cb as _),
478            userdata: self as *mut Self as *mut libc::c_void,
479        };
480
481        self.set_option(
482            sys::OptType::SkipdataSetup,
483            &setup as *const _ as usize as libc::size_t,
484        )?;
485        Ok(())
486    }
487
488    /// If there is a panic waiting in [`Capstone::pending_panic`], this will
489    /// resume it.
490    #[cfg(feature = "std")]
491    fn resume_panic(&self) {
492        if self.pending_panic.borrow().is_none() {
493            return;
494        }
495
496        if let Some(p) = self.pending_panic.borrow_mut().take() {
497            std::panic::resume_unwind(p);
498        }
499    }
500
501    /// Place the disassembling engine in SKIPDATA mode.
502    /// Use [`Capstone::setup_skipdata`] to configure this mode.
503    pub fn set_skipdata_mode(&mut self, skipdata: bool) -> Result<(), Error> {
504        self.set_option(
505            sys::OptType::Skipdata,
506            if skipdata {
507                sys::OPT_VALUE_ON
508            } else {
509                sys::OPT_VALUE_OFF
510            },
511        )?;
512
513        self.packed.set_skipdata(skipdata);
514        Ok(())
515    }
516
517    /// Returns true if this Capstone instance has instruction details enabled.
518    pub fn details_enabled(&self) -> bool {
519        self.packed.detail()
520    }
521
522    /// Returns true if the disassembling engine is currently in SKIPDATA
523    /// mode.
524    pub fn skipdata_mode(&self) -> bool {
525        self.packed.skipdata()
526    }
527
528    /// Returns the current arch that this instance of the Capstone
529    /// disassembly engine is set to disassemble.
530    pub fn arch(&self) -> Arch {
531        self.packed.arch()
532    }
533
534    /// Returns the user friendly name of a register. This will return an empty string
535    /// if the register is not valid for the current architecture.
536    pub fn reg_name<R>(&self, reg: R) -> &str
537    where
538        R: Into<Reg>,
539    {
540        let reg = reg.into();
541        let name = unsafe { sys::cs_reg_name(self.handle, reg.to_primitive() as _) };
542
543        if name.is_null() {
544            ""
545        } else {
546            unsafe { util::cstr(name, 128) }
547        }
548    }
549
550    /// Returns the user friendly name of an instruction. This will return an empty string
551    /// if the instruction is not valid for the current architecture.
552    pub fn insn_name<I>(&self, insn: I) -> &str
553    where
554        I: Into<InsnId>,
555    {
556        let insn = insn.into();
557        let name = unsafe { sys::cs_insn_name(self.handle, insn.to_c() as _) };
558
559        if name.is_null() {
560            ""
561        } else {
562            unsafe { util::cstr(name, 128) }
563        }
564    }
565
566    /// Returns the user friendly name of an instruction group. This will return an empty string
567    /// if the instruction group is not valid for the current architecture.
568    pub fn group_name<G>(&self, group: G) -> &str
569    where
570        G: Into<InsnGroup>,
571    {
572        let group = group.into();
573        let name = unsafe { sys::cs_group_name(self.handle, group.to_primitive() as _) };
574
575        if name.is_null() {
576            ""
577        } else {
578            unsafe { util::cstr(name, 128) }
579        }
580    }
581
582    /// Retrieves all of the registers read from and written to either
583    /// implicitly or explicitly by an instruction and places them into
584    /// the given buffer.
585    pub fn regs_used(&self, insn: &Insn, regs_used_out: &mut RegsUsed) -> Result<(), Error> {
586        result!(unsafe {
587            sys::cs_regs_access(
588                self.handle,
589                insn,
590                regs_used_out.read.1.as_mut_ptr(),
591                &mut regs_used_out.read.0,
592                regs_used_out.write.1.as_mut_ptr(),
593                &mut regs_used_out.write.0,
594            )
595        })
596    }
597
598    /// Set an option for the disassembling engine at runtime.
599    fn set_option(&mut self, type_: sys::OptType, value: libc::size_t) -> Result<(), Error> {
600        result!(unsafe { sys::cs_option(self.handle, type_, value) })
601    }
602
603    /// Closes the capstone handle.
604    ///
605    /// # Panics
606    ///
607    /// Panics if an error occurs while closing the CS handle.
608    /// The only possible error really is an invalid handle, in which case
609    /// something has gone very wrong in the bindings.
610    fn close(&mut self) {
611        result!(unsafe { sys::cs_close(&mut self.handle) })
612            .expect("error occurred while closing Capstone handle");
613    }
614}
615
616impl Drop for Capstone {
617    fn drop(&mut self) {
618        self.close();
619    }
620}
621
622extern "C" fn cs_skipdata_cb(
623    code: *mut u8,
624    code_size: *mut libc::size_t,
625    offset: libc::size_t,
626    userdata: *mut libc::c_void,
627) -> libc::size_t {
628    if userdata.is_null() {
629        return 0;
630    }
631    let userdata = userdata as *mut Capstone;
632
633    #[cfg(feature = "std")]
634    unsafe {
635        // Don't allow any callbacks to be used again if there is a panic
636        // that has not yet been handled.
637        if (*userdata).pending_panic.borrow().is_some() {
638            return 0;
639        }
640
641        // SAFETY: If a panic occurs we never use this closure again.
642        //         Although I might be misunderstanding unwind safety here (- Marc)
643        let cb = std::panic::AssertUnwindSafe(&mut (*userdata).skipdata_callback);
644
645        match std::panic::catch_unwind(move || {
646            if let std::panic::AssertUnwindSafe(Some(ref mut cb)) = cb {
647                cb(
648                    core::slice::from_raw_parts_mut(code, code_size as usize),
649                    offset as usize,
650                )
651            } else {
652                // This should technically be unreachable.
653                0
654            }
655        }) {
656            Ok(ret) => ret as libc::size_t,
657            Err(p) => {
658                *(*userdata).pending_panic.borrow_mut() = Some(p);
659                0
660            }
661        }
662    }
663
664    // The no_std and no_std+alloc version of these can just share the same code.
665    #[cfg(not(feature = "std"))]
666    unsafe {
667        if let Some(ref mut cb) = (*userdata).skipdata_callback {
668            cb(
669                core::slice::from_raw_parts_mut(code, code_size as usize),
670                offset as usize,
671            ) as libc::size_t
672        } else {
673            // This should be unreachable.
674            0
675        }
676    }
677}
678
679#[derive(Clone, Copy, Default)]
680pub struct RegsUsed {
681    read: RegsBuffer,
682    write: RegsBuffer,
683}
684
685impl RegsUsed {
686    pub fn read(&self) -> &[Reg] {
687        &self.read
688    }
689
690    pub fn write(&self) -> &[Reg] {
691        &self.write
692    }
693}
694
695/// A list of registers that are either read from or written to by an instruction.
696#[derive(Clone, Copy)]
697pub struct RegsBuffer(u8, [Reg; 64]);
698
699impl RegsBuffer {
700    pub fn new() -> RegsBuffer {
701        RegsBuffer(0, [Reg::default(); 64])
702    }
703}
704
705impl Default for RegsBuffer {
706    fn default() -> Self {
707        Self::new()
708    }
709}
710
711impl core::ops::Deref for RegsBuffer {
712    type Target = [Reg];
713
714    fn deref(&self) -> &Self::Target {
715        &self.1[..self.0 as usize]
716    }
717}
718
719/// Disassembling engine assembly syntax.
720#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)]
721pub enum Syntax {
722    Default,
723    /// Intel assembly syntax.
724    Intel,
725    /// AT&T assembly syntax.
726    Att,
727    /// Print register names as numbers.
728    NoRegName,
729    /// Intel MASM assembly syntax.
730    Masm,
731}
732
733impl Default for Syntax {
734    fn default() -> Self {
735        Self::Default
736    }
737}
738
739/// The API version of capstone.
740#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq, Ord, PartialOrd)]
741pub struct CapstoneVersion {
742    /// The major version of capstone.
743    pub major: u16,
744    /// The minor version of capstone.
745    pub minor: u16,
746}
747
748impl fmt::Display for CapstoneVersion {
749    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
750        write!(f, "{}.{}", self.major, self.minor)
751    }
752}
753
754c_enum! {
755    #[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)]
756    pub enum Arch: u8 {
757        /// ARM architecture (including Thumb, Thumb-2)
758        Arm,
759        /// ARM-64, also called AArch64
760        Arm64,
761        /// Mips architecture
762        Mips,
763        /// X86 architecture (including x86 & x86-64)
764        X86,
765        /// PowerPC architecture
766        PowerPc,
767        /// Sparc architecture
768        Sparc,
769        /// SystemZ architecture
770        SystemZ,
771        /// XCore architecture
772        XCore,
773        /// 68K architecture
774        M68K,
775        /// TMS320C64x architecture
776        Tms320C64X,
777        /// 680X architecture
778        M680X,
779        /// Ethereum architecture
780        Evm,
781        /// MOS65XX architecture (including MOS6502)
782        Mos65xx,
783    }
784}
785
786impl From<Arch> for sys::Arch {
787    fn from(arch: Arch) -> sys::Arch {
788        sys::Arch(arch.to_c())
789    }
790}
791
792/// Support query that can be used along with `supports` to check
793/// the current Capstone build's capabilities.
794#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)]
795pub enum SupportQuery {
796    /// Support query for a specific architecture.
797    Arch(Arch),
798
799    /// Support query for all architectures known to capstone.
800    AllArch,
801
802    /// Support query for verifying that the current capstone
803    /// engine is in diet mode.
804    Diet,
805
806    /// Support query for verifying that the current capstone
807    /// engine is currently in X86 reduce mode.
808    X86Reduce,
809}
810
811impl From<Arch> for SupportQuery {
812    fn from(arch: Arch) -> SupportQuery {
813        SupportQuery::Arch(arch)
814    }
815}
816
817#[allow(non_upper_case_globals)]
818mod mode {
819    bitflags::bitflags! {
820        /// Mode flags for configuring `Capstone`.
821        pub struct Mode: libc::c_int {
822            /// little-endian mode (default mode)
823            const LittleEndian = 0;
824            /// 32-bit ARM
825            const Arm = 0;
826            /// 16-bit mode (X86)
827            const Bits16 = 1 << 1;
828            /// 32-bit mode (X86)
829            const Bits32 = 1 << 2;
830            /// 64-bit mode (X86, PPC)
831            const Bits64 = 1 << 3;
832            /// ARM's Thumb mode, including Thumb-2
833            const Thumb = 1 << 4;
834            /// ARM's Cortex-M series
835            const MClass = 1 << 5;
836            /// ARMv8 A32 encodings for ARM
837            const V8 = 1 << 6;
838            /// MicroMips mode (MIPS)
839            const Micro = 1 << 4;
840            /// MIPS III ISA
841            const Mips3 = 1 << 5;
842            /// MIPS32R6 ISA
843            const Mips32R6 = 1 << 6;
844            /// Mips II ISA
845            const Mips2 = 1 << 7;
846            /// SparcV9 mode (Sparc)
847            const V9 = 1 << 4;
848            /// Quad Processing eXtensions mode (PPC)
849            const Qpx = 1 << 4;
850            /// M68K 68000 mode
851            const M68K000 = 1 << 1;
852            /// M68K 68010 mode
853            const M68K010 = 1 << 2;
854            /// M68K 68020 mode
855            const M68K020 = 1 << 3;
856            /// M68K 68030 mode
857            const M68K030 = 1 << 4;
858            /// M68K 68040 mode
859            const M68K040 = 1 << 5;
860            /// M68K 68060 mode
861            const M68K060 = 1 << 6;
862            /// big-endian mode
863            const BigEndian = 1 << 31;
864            /// MIPS32 ISA (Mips)
865            const Mips32 = Self::Bits32.bits;
866            /// MIPS64 ISA (Mips)
867            const Mips64 = Self::Bits64.bits;
868            /// M680X Hitachi 6301,6303 mode
869            const M680X6301 = 1 << 1;
870            /// M680X Hitachi 6309 mode
871            const M680X6309 = 1 << 2;
872            /// M680X Motorola 6800,6802 mode
873            const M680X6800 = 1 << 3;
874            /// M680X Motorola 6801,6803 mode
875            const M680X6801 = 1 << 4;
876            /// M680X Motorola/Freescale 6805 mode
877            const M680X6805 = 1 << 5;
878            /// M680X Motorola/Freescale/NXP 68HC08 mode
879            const M680X6808 = 1 << 6;
880            /// M680X Motorola 6809 mode
881            const M680X6809 = 1 << 7;
882            /// M680X Motorola/Freescale/NXP 68HC11 mode
883            const M680X6811 = 1 << 8;
884            /// M680X Motorola/Freescale/NXP CPU12 used on M68HC12/HCS12
885            const M680XCPU12 = 1 << 9;
886            /// M680X Freescale/NXP HCS08 mode
887            const M680XHCS08 = 1 << 10;
888        }
889    }
890}
891
892#[doc(inline)]
893pub use mode::Mode;
894
895impl From<Mode> for sys::Mode {
896    fn from(mode: Mode) -> sys::Mode {
897        sys::Mode(mode.bits() as _)
898    }
899}
900
901c_enum! {
902    #[derive(Clone, Copy, Debug, Hash, PartialEq, Eq)]
903    pub enum Error: u8 {
904        /// Out of memory error.
905        Memory = 1,
906        /// Unsupported architecture.
907        Arch,
908        /// Invalid handle.
909        Handle,
910        /// Invalid Capstone handle argument.
911        ///
912        /// **NOTE**: This should not come up using the safe bindings. If
913        /// it does please file an issue.
914        Csh,
915        /// Invalid/unsupported mode.
916        Mode,
917        /// Invalid/unsupported option.
918        Option,
919        /// Information is unavailable because detail option is OFF.
920        Detail,
921        /// Dynamic memory management uninitialized.
922        MemSetup,
923        /// Unsupported version (bindings).
924        Version,
925        /// Accessed irrelevant data in "diet" engine.
926        Diet,
927        /// Accessed irrelevant data for "data" instruction in SKIPDATA mode.
928        Skipdata,
929        /// X86 AT&T syntax is unsupported (opted out at compile time).
930        X86Att,
931        /// X86 Intel syntex is unsupported (opted out at compile time).
932        X86Intel,
933        /// X86 MASM syntex is unsupported (opted out at compile time).
934        X86Masm,
935        /// An error occurred in the bindings. Truly terrible.
936        Bindings,
937    }
938}
939
940impl fmt::Display for Error {
941    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
942        let msg = match self {
943            Error::Memory => "out of memory",
944            Error::Arch => "unsupported architecture",
945            Error::Handle => "invalid handle",
946            Error::Csh => "invalid capstone handle",
947            Error::Mode => "invalid/unsupported mode",
948            Error::Option => "invalid/unsupported option",
949            Error::Detail => "detail unavailable",
950            Error::MemSetup => "dynamic memory management uninitialized",
951            Error::Version => "unsupported version",
952            Error::Diet => "accessed irrelevant data in diet engine",
953            Error::Skipdata => "accessed irrelevant data for data instruction in skipdata mode",
954            Error::X86Att => "X86 AT&T syntax is unsupported",
955            Error::X86Intel => "X86 Intel syntex is unsupported",
956            Error::X86Masm => "X86 MASM syntex is unsupported",
957            Error::Bindings => "bindings error (please file an issue)",
958        };
959
960        f.write_str(msg)
961    }
962}
963
964#[cfg(feature = "std")]
965impl std::error::Error for Error {}
966
967/// Packed information about a current instance of capstone.
968///
969/// The bits are packed in this format:
970/// value       start   end     range
971/// arch        0       3       0-15
972/// detail      4       4       1
973/// skipdata    5       5       1
974#[derive(Clone, Copy)]
975struct PackedCSInfo(u8);
976
977impl PackedCSInfo {
978    fn new(arch: Arch, detail: bool, skipdata: bool) -> Self {
979        let mut p = PackedCSInfo(0);
980        p.set_arch(arch);
981        p.set_detail(detail);
982        p.set_skipdata(skipdata);
983        p
984    }
985
986    fn arch(self) -> Arch {
987        match Arch::from_primitive(self.0 & 0xF) {
988            Some(arch) => arch,
989
990            #[cfg(test)]
991            None => unreachable!("bad arch from PackedCSInfo"),
992
993            // SAFETY: we never allow an invalid Arch to be set on PackedCSInfo.
994            #[cfg(not(test))]
995            None => unsafe { core::hint::unreachable_unchecked() },
996        }
997    }
998
999    fn detail(self) -> bool {
1000        ((self.0 >> 4) & 1) != 0
1001    }
1002
1003    fn skipdata(self) -> bool {
1004        ((self.0 >> 5) & 1) != 0
1005    }
1006
1007    fn set_arch(&mut self, arch: Arch) {
1008        self.0 = (self.0 & !0xF) | arch.to_primitive()
1009    }
1010
1011    fn set_detail(&mut self, detail: bool) {
1012        self.0 = (self.0 & !(1 << 4)) | ((detail as u8) << 4);
1013    }
1014
1015    fn set_skipdata(&mut self, skipdata: bool) {
1016        self.0 = (self.0 & !(1 << 5)) | ((skipdata as u8) << 5);
1017    }
1018}
1019
1020/// Returns the current version of the capstone API.
1021pub fn version() -> CapstoneVersion {
1022    let mut major: libc::c_int = 0;
1023    let mut minor: libc::c_int = 0;
1024    unsafe { sys::cs_version(&mut major, &mut minor) };
1025    CapstoneVersion {
1026        major: major as u16,
1027        minor: minor as u16,
1028    }
1029}
1030
1031/// Queries Capstone's capabilities. Use this to check if the current build of
1032/// Capstone supports a certain architecture or if the feature set is reduced.
1033pub fn supports<Query>(query: Query) -> bool
1034where
1035    Query: Into<SupportQuery>,
1036{
1037    let query_int = match query.into() {
1038        SupportQuery::Arch(arch) => arch as libc::c_int,
1039        SupportQuery::AllArch => 0xFFFF,
1040        SupportQuery::Diet => 0x10000,
1041        SupportQuery::X86Reduce => 0x10001,
1042    };
1043    unsafe { sys::cs_support(query_int) }
1044}
1045
1046#[cfg(test)]
1047mod test {
1048    use super::*;
1049
1050    const ALL_ARCHS: &[Arch] = &[
1051        Arch::Arm,
1052        Arch::Arm64,
1053        Arch::Mips,
1054        Arch::X86,
1055        Arch::PowerPc,
1056        Arch::Sparc,
1057        Arch::SystemZ,
1058        Arch::XCore,
1059        Arch::M68K,
1060        Arch::Tms320C64X,
1061        Arch::M680X,
1062        Arch::Evm,
1063        Arch::Mos65xx,
1064    ];
1065
1066    #[test]
1067    fn open_capstone() {
1068        let mut caps =
1069            Capstone::open(Arch::X86, Mode::LittleEndian).expect("failed to open capstone");
1070        caps.set_details_enabled(true)
1071            .expect("failed to enable capstone instruction details");
1072        caps.set_mnemonic(x86::InsnId::Add, "better-add")
1073            .expect("failed to substitute instruction mnemonic");
1074
1075        println!("capstone size: {} bytes", core::mem::size_of::<Capstone>());
1076        let mut regs_used = RegsUsed::default();
1077
1078        for insn in caps.disasm_iter(
1079            &[
1080                0x8d, 0x4c, 0x32, 0x08, 0x01, 0xd8, 0x81, 0xc6, 0x34, 0x12, 0x00, 0x00, 0x05, 0x23,
1081                0x01, 0x00, 0x00, 0x36, 0x8b, 0x84, 0x91, 0x23, 0x01, 0x00, 0x00, 0x41, 0x8d, 0x84,
1082                0x39, 0x89, 0x67, 0x00, 0x00, 0x8d, 0x87, 0x89, 0x67, 0x00, 0x00, 0xb4, 0xc6, 0xe9,
1083                0xea, 0xbe, 0xad, 0xde, 0xff, 0xa0, 0x23, 0x01, 0x00, 0x00, 0xe8, 0xdf, 0xbe, 0xad,
1084                0xde, 0x74, 0xff,
1085            ],
1086            0x0,
1087        ) {
1088            let insn = insn.unwrap();
1089            println!("{} {}", insn.mnemonic(), insn.operands());
1090            caps.regs_used(insn, &mut regs_used)
1091                .expect("failed to get registers accessed");
1092
1093            for reg in regs_used.read().iter() {
1094                println!("\t read reg {}", caps.reg_name(*reg));
1095            }
1096
1097            for reg in regs_used.write().iter() {
1098                println!("\twrite reg {}", caps.reg_name(*reg));
1099            }
1100
1101            println!("GROUPS:");
1102            for grp in caps.details(insn).groups() {
1103                println!("\t- {}", caps.group_name(*grp));
1104            }
1105        }
1106    }
1107
1108    #[test]
1109    fn validate_packed_cs_info_states() {
1110        for arch in ALL_ARCHS.iter().copied() {
1111            let packed = PackedCSInfo::new(arch, true, true);
1112            assert_eq!(packed.arch(), arch);
1113            assert_eq!(packed.detail(), true);
1114            assert_eq!(packed.skipdata(), true);
1115
1116            let packed = PackedCSInfo::new(arch, false, true);
1117            assert_eq!(packed.arch(), arch);
1118            assert_eq!(packed.detail(), false);
1119            assert_eq!(packed.skipdata(), true);
1120
1121            let packed = PackedCSInfo::new(arch, true, false);
1122            assert_eq!(packed.arch(), arch);
1123            assert_eq!(packed.detail(), true);
1124            assert_eq!(packed.skipdata(), false);
1125
1126            let packed = PackedCSInfo::new(arch, false, false);
1127            assert_eq!(packed.arch(), arch);
1128            assert_eq!(packed.detail(), false);
1129            assert_eq!(packed.skipdata(), false);
1130        }
1131    }
1132
1133    #[test]
1134    fn test_version() {
1135        pub const EXPECTED_MAJOR_VERSION: u16 = 5;
1136        pub const EXPECTED_MINOR_VERSION: u16 = 0;
1137
1138        let v = version();
1139        assert_eq!(v.major, EXPECTED_MAJOR_VERSION);
1140        assert_eq!(v.minor, EXPECTED_MINOR_VERSION);
1141    }
1142
1143    #[test]
1144    fn test_support() {
1145        assert_eq!(supports(Arch::Arm), cfg!(feature = "arm"));
1146        assert_eq!(supports(Arch::Arm64), cfg!(feature = "aarch64"));
1147        assert_eq!(supports(Arch::Mips), cfg!(feature = "mips"));
1148        assert_eq!(supports(Arch::X86), cfg!(feature = "x86"));
1149        assert_eq!(supports(Arch::PowerPc), cfg!(feature = "powerpc"));
1150        assert_eq!(supports(Arch::Sparc), cfg!(feature = "sparc"));
1151        assert_eq!(supports(Arch::SystemZ), cfg!(feature = "systemz"));
1152        assert_eq!(supports(Arch::XCore), cfg!(feature = "xcore"));
1153        assert_eq!(supports(Arch::M68K), cfg!(feature = "m68k"));
1154        assert_eq!(supports(Arch::Tms320C64X), cfg!(feature = "tms320c64x"));
1155        assert_eq!(supports(Arch::M680X), cfg!(feature = "m680x"));
1156        assert_eq!(supports(Arch::Evm), cfg!(feature = "evm"));
1157        assert_eq!(supports(Arch::Mos65xx), cfg!(feature = "mos65xx"));
1158
1159        assert_eq!(supports(SupportQuery::Diet), cfg!(feature = "diet"));
1160        assert_eq!(
1161            supports(SupportQuery::X86Reduce),
1162            cfg!(feature = "x86-reduce")
1163        );
1164    }
1165}