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
49struct NotSend(*mut u8);
51
52pub 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    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    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    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    fn errno(&self) -> Result<(), Error> {
135        result!(unsafe { sys::cs_errno(self.handle) })
136    }
137
138    pub fn disasm<'s>(&'s self, code: &[u8], address: u64) -> Result<InsnBuffer<'s>, Error> {
142        self.priv_disasm(code, address, 0)
143    }
144
145    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    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        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    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    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    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    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    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    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    #[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; self.mnemonics.insert(insn, mnemonic);
282
283        self.set_mnemonic_inner(insn, mnemonic_ptr)
284    }
285
286    #[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; self.set_mnemonic_inner(insn, mnemonic_ptr)
300    }
301
302    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    #[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    #[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    #[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    #[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    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    pub fn details_enabled(&self) -> bool {
519        self.packed.detail()
520    }
521
522    pub fn skipdata_mode(&self) -> bool {
525        self.packed.skipdata()
526    }
527
528    pub fn arch(&self) -> Arch {
531        self.packed.arch()
532    }
533
534    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    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    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    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    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    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        if (*userdata).pending_panic.borrow().is_some() {
638            return 0;
639        }
640
641        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                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    #[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            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#[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#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)]
721pub enum Syntax {
722    Default,
723    Intel,
725    Att,
727    NoRegName,
729    Masm,
731}
732
733impl Default for Syntax {
734    fn default() -> Self {
735        Self::Default
736    }
737}
738
739#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq, Ord, PartialOrd)]
741pub struct CapstoneVersion {
742    pub major: u16,
744    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,
759        Arm64,
761        Mips,
763        X86,
765        PowerPc,
767        Sparc,
769        SystemZ,
771        XCore,
773        M68K,
775        Tms320C64X,
777        M680X,
779        Evm,
781        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#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)]
795pub enum SupportQuery {
796    Arch(Arch),
798
799    AllArch,
801
802    Diet,
805
806    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        pub struct Mode: libc::c_int {
822            const LittleEndian = 0;
824            const Arm = 0;
826            const Bits16 = 1 << 1;
828            const Bits32 = 1 << 2;
830            const Bits64 = 1 << 3;
832            const Thumb = 1 << 4;
834            const MClass = 1 << 5;
836            const V8 = 1 << 6;
838            const Micro = 1 << 4;
840            const Mips3 = 1 << 5;
842            const Mips32R6 = 1 << 6;
844            const Mips2 = 1 << 7;
846            const V9 = 1 << 4;
848            const Qpx = 1 << 4;
850            const M68K000 = 1 << 1;
852            const M68K010 = 1 << 2;
854            const M68K020 = 1 << 3;
856            const M68K030 = 1 << 4;
858            const M68K040 = 1 << 5;
860            const M68K060 = 1 << 6;
862            const BigEndian = 1 << 31;
864            const Mips32 = Self::Bits32.bits;
866            const Mips64 = Self::Bits64.bits;
868            const M680X6301 = 1 << 1;
870            const M680X6309 = 1 << 2;
872            const M680X6800 = 1 << 3;
874            const M680X6801 = 1 << 4;
876            const M680X6805 = 1 << 5;
878            const M680X6808 = 1 << 6;
880            const M680X6809 = 1 << 7;
882            const M680X6811 = 1 << 8;
884            const M680XCPU12 = 1 << 9;
886            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        Memory = 1,
906        Arch,
908        Handle,
910        Csh,
915        Mode,
917        Option,
919        Detail,
921        MemSetup,
923        Version,
925        Diet,
927        Skipdata,
929        X86Att,
931        X86Intel,
933        X86Masm,
935        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#[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            #[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
1020pub 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
1031pub 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}