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}