#![cfg_attr(not(feature = "std"), no_std)]
#[cfg(all(not(feature = "std"), feature = "alloc"))]
extern crate alloc;
#[macro_use]
mod macros;
pub mod arch;
mod insn;
mod sys;
mod util;
use core::{convert::From, fmt, marker::PhantomData, ptr::NonNull};
#[cfg(feature = "std")]
use std::{
self as alloc, borrow::Cow, cell::RefCell, collections::HashMap as Map, panic::UnwindSafe,
};
#[cfg(all(not(feature = "std"), feature = "alloc"))]
use alloc::{borrow::Cow, boxed::Box, collections::BTreeMap as Map};
pub use arch::{InsnGroup, InsnId, Reg};
pub use insn::{ArchDetails, Details, Insn, InsnBuffer, InsnIter};
pub use arch::arm;
pub use arch::arm64;
pub use arch::evm;
pub use arch::m680x;
pub use arch::m68k;
pub use arch::mips;
pub use arch::mos65xx;
pub use arch::ppc;
pub use arch::sparc;
pub use arch::sysz;
pub use arch::tms320c64x;
pub use arch::x86;
pub use arch::xcore;
#[cfg(feature = "std")]
pub type SkipdataCallback = dyn 'static + UnwindSafe + FnMut(&[u8], usize) -> usize;
#[cfg(all(not(feature = "std"), feature = "alloc"))]
pub type SkipdataCallback = dyn 'static + FnMut(&[u8], usize) -> usize;
#[cfg(all(not(feature = "std"), not(feature = "alloc")))]
pub type SkipdataCallback = fn(&[u8], usize) -> usize;
struct NotSend(*mut u8);
pub struct Capstone {
handle: sys::Handle,
packed: PackedCSInfo,
#[cfg(feature = "alloc")]
mnemonics: Map<InsnId, Cow<'static, str>>,
#[cfg(feature = "alloc")]
skipdata_callback: Option<Box<SkipdataCallback>>,
#[cfg(feature = "alloc")]
skipdata_mnemonic: Option<Cow<'static, str>>,
#[cfg(not(feature = "alloc"))]
skipdata_callback: Option<SkipdataCallback>,
#[cfg(feature = "std")]
pending_panic: RefCell<Option<Box<dyn std::any::Any + Send + 'static>>>,
disable_send: PhantomData<NotSend>,
}
impl Capstone {
pub fn open(arch: Arch, mode: Mode) -> Result<Self, Error> {
let mut handle = sys::Handle(0);
result! {
unsafe { sys::cs_open(arch.into(), mode.into(), &mut handle) },
Capstone {
handle,
packed: PackedCSInfo::new(arch, false, false),
skipdata_callback: None,
#[cfg(feature = "alloc")]
mnemonics: Map::new(),
#[cfg(feature = "alloc")]
skipdata_mnemonic: None,
#[cfg(feature = "std")]
pending_panic: RefCell::new(None),
disable_send: PhantomData,
}
}
}
pub fn details<'i>(&self, insn: &'i Insn) -> insn::Details<'i> {
self.try_details(insn)
.expect("instruction details are not available")
}
pub fn try_details<'i>(&self, insn: &'i Insn) -> Option<insn::Details<'i>> {
if !self.details_enabled() {
return None;
}
unsafe {
insn.detail
.as_ref()
.map(|r| insn::Details::wrap(self.packed.arch(), r))
}
}
fn errno(&self) -> Result<(), Error> {
result!(unsafe { sys::cs_errno(self.handle) })
}
pub fn disasm<'s>(&'s self, code: &[u8], address: u64) -> Result<InsnBuffer<'s>, Error> {
self.priv_disasm(code, address, 0)
}
pub fn disasm_count<'s>(
&'s self,
code: &[u8],
address: u64,
count: usize,
) -> Result<InsnBuffer<'s>, Error> {
if count == 0 {
Ok(InsnBuffer::new(NonNull::dangling().as_ptr(), 0))
} else {
self.priv_disasm(code, address, count)
}
}
fn priv_disasm<'s>(
&'s self,
code: &[u8],
address: u64,
count: usize,
) -> Result<InsnBuffer<'s>, Error> {
let mut insn: *mut Insn = core::ptr::null_mut();
let count = unsafe {
sys::cs_disasm(
self.handle,
code.as_ptr(),
code.len() as libc::size_t,
address,
count as libc::size_t,
&mut insn,
)
} as usize;
#[cfg(feature = "std")]
self.resume_panic();
if count == 0 {
self.errno()?;
return Err(Error::Bindings);
}
Ok(InsnBuffer::new(insn, count))
}
pub fn disasm_iter<'s>(&'s self, code: &[u8], address: u64) -> InsnIter<'s> {
let insn = unsafe { sys::cs_malloc(self.handle) };
assert!(!insn.is_null(), "cs_malloc() returned a null insn");
InsnIter::new(
self,
insn,
code.as_ptr(),
code.len() as libc::size_t,
address,
)
}
pub fn set_syntax(&mut self, syntax: Syntax) -> Result<(), Error> {
match syntax {
Syntax::Default => self.set_option(sys::OptType::Syntax, sys::OPT_VALUE_SYNTAX_DEFAULT),
Syntax::Intel => self.set_option(sys::OptType::Syntax, sys::OPT_VALUE_SYNTAX_INTEL),
Syntax::Att => self.set_option(sys::OptType::Syntax, sys::OPT_VALUE_SYNTAX_ATT),
Syntax::NoRegName => {
self.set_option(sys::OptType::Syntax, sys::OPT_VALUE_SYNTAX_NOREGNAME)
}
Syntax::Masm => self.set_option(sys::OptType::Syntax, sys::OPT_VALUE_SYNTAX_MASM),
}
}
pub fn set_mode(&mut self, mode: Mode) -> Result<(), Error> {
self.set_option(sys::OptType::Mode, mode.bits() as libc::size_t)
}
pub fn set_details_enabled(&mut self, detail: bool) -> Result<(), Error> {
self.set_option(
sys::OptType::Detail,
if detail {
sys::OPT_VALUE_ON
} else {
sys::OPT_VALUE_OFF
},
)?;
self.packed.set_detail(detail);
Ok(())
}
pub fn set_unsigned(&mut self, unsigned: bool) -> Result<(), Error> {
self.set_option(
sys::OptType::Unsigned,
if unsigned {
sys::OPT_VALUE_ON
} else {
sys::OPT_VALUE_OFF
},
)?;
Ok(())
}
pub fn reset_mnemonic<I>(&mut self, insn: I) -> Result<(), Error>
where
I: Into<InsnId>,
{
self.set_mnemonic_inner(insn.into(), core::ptr::null())
}
#[cfg(feature = "alloc")]
pub fn set_mnemonic<I, M>(&mut self, insn: I, mnemonic: M) -> Result<(), Error>
where
I: Into<InsnId>,
M: Into<Cow<'static, str>>,
{
let insn = insn.into();
let mnemonic = util::ensure_c_string(mnemonic.into());
let mnemonic_ptr = mnemonic.as_ptr() as *const libc::c_char;
self.mnemonics.insert(insn, mnemonic);
self.set_mnemonic_inner(insn, mnemonic_ptr)
}
#[cfg(not(feature = "alloc"))]
pub fn set_mnemonic<I, M>(&mut self, insn: I, mnemonic: &'static str) -> Result<(), Error>
where
I: Into<InsnId>,
{
let insn = insn.into();
let mnemonic = util::ensure_c_string(mnemonic);
let mnemonic_ptr = mnemonic.as_ptr() as *const libc::c_char;
self.set_mnemonic_inner(insn, mnemonic_ptr)
}
fn set_mnemonic_inner(
&mut self,
insn: InsnId,
mnemonic: *const libc::c_char,
) -> Result<(), Error> {
let mut opt_mnem = sys::OptMnemonic {
id: insn.to_c(),
mnemonic,
};
self.set_option(
sys::OptType::Mnemonic,
&mut opt_mnem as *mut _ as usize as libc::size_t,
)
}
#[cfg(all(not(feature = "std"), feature = "alloc"))]
pub fn setup_skipdata<M, F>(
&mut self,
mnemonic: Option<M>,
callback: Option<F>,
) -> Result<(), Error>
where
M: Into<Cow<'static, str>>,
F: 'static + FnMut(&[u8], usize) -> usize,
{
self.skipdata_mnemonic = mnemonic.map(|m| util::ensure_c_string(m.into()));
self.skipdata_callback = callback.map(|c| Box::new(c) as _);
let setup = sys::OptSkipdataSetup {
mnemonic: self
.skipdata_mnemonic
.as_ref()
.map(|m| unsafe { NonNull::new_unchecked((&*m).as_ptr() as *mut libc::c_char) }),
callback: self.skipdata_callback.as_ref().map(|_| cs_skipdata_cb as _),
userdata: self as *mut Self as *mut libc::c_void,
};
self.set_option(
sys::OptType::SkipdataSetup,
&setup as *const _ as usize as libc::size_t,
)?;
Ok(())
}
#[cfg(feature = "std")]
pub fn setup_skipdata<M, F>(
&mut self,
mnemonic: Option<M>,
callback: Option<F>,
) -> Result<(), Error>
where
M: Into<Cow<'static, str>>,
F: 'static + UnwindSafe + FnMut(&[u8], usize) -> usize,
{
self.skipdata_mnemonic = mnemonic.map(|m| util::ensure_c_string(m.into()));
self.skipdata_callback = callback.map(|c| Box::new(c) as _);
let setup = sys::OptSkipdataSetup {
mnemonic: self
.skipdata_mnemonic
.as_ref()
.map(|m| unsafe { NonNull::new_unchecked((&*m).as_ptr() as *mut libc::c_char) }),
callback: self.skipdata_callback.as_ref().map(|_| cs_skipdata_cb as _),
userdata: self as *mut Self as *mut libc::c_void,
};
self.set_option(
sys::OptType::SkipdataSetup,
&setup as *const _ as usize as libc::size_t,
)?;
Ok(())
}
#[cfg(not(feature = "alloc"))]
pub fn setup_skipdata<M, F>(
&mut self,
mnemonic: Option<&'static str>,
callback: Option<fn(&[u8], usize) -> usize>,
) -> Result<(), Error> {
self.skipdata_callback = callback;
let setup = sys::OptSkipdataSetup {
mnemonic: mnemonic.map(|m| {
let m = util::ensure_c_string(m);
unsafe { NonNull::new_unchecked(m.as_ptr() as *mut libc::c_char) }
}),
callback: self.skipdata_callback.as_ref().map(|_| cs_skipdata_cb as _),
userdata: self as *mut Self as *mut libc::c_void,
};
self.set_option(
sys::OptType::SkipdataSetup,
&setup as *const _ as usize as libc::size_t,
)?;
Ok(())
}
#[cfg(feature = "std")]
fn resume_panic(&self) {
if self.pending_panic.borrow().is_none() {
return;
}
if let Some(p) = self.pending_panic.borrow_mut().take() {
std::panic::resume_unwind(p);
}
}
pub fn set_skipdata_mode(&mut self, skipdata: bool) -> Result<(), Error> {
self.set_option(
sys::OptType::Skipdata,
if skipdata {
sys::OPT_VALUE_ON
} else {
sys::OPT_VALUE_OFF
},
)?;
self.packed.set_skipdata(skipdata);
Ok(())
}
pub fn details_enabled(&self) -> bool {
self.packed.detail()
}
pub fn skipdata_mode(&self) -> bool {
self.packed.skipdata()
}
pub fn arch(&self) -> Arch {
self.packed.arch()
}
pub fn reg_name<R>(&self, reg: R) -> &str
where
R: Into<Reg>,
{
let reg = reg.into();
let name = unsafe { sys::cs_reg_name(self.handle, reg.to_primitive() as _) };
if name.is_null() {
""
} else {
unsafe { util::cstr(name, 128) }
}
}
pub fn insn_name<I>(&self, insn: I) -> &str
where
I: Into<InsnId>,
{
let insn = insn.into();
let name = unsafe { sys::cs_insn_name(self.handle, insn.to_c() as _) };
if name.is_null() {
""
} else {
unsafe { util::cstr(name, 128) }
}
}
pub fn group_name<G>(&self, group: G) -> &str
where
G: Into<InsnGroup>,
{
let group = group.into();
let name = unsafe { sys::cs_group_name(self.handle, group.to_primitive() as _) };
if name.is_null() {
""
} else {
unsafe { util::cstr(name, 128) }
}
}
pub fn regs_used(&self, insn: &Insn, regs_used_out: &mut RegsUsed) -> Result<(), Error> {
result!(unsafe {
sys::cs_regs_access(
self.handle,
insn,
regs_used_out.read.1.as_mut_ptr(),
&mut regs_used_out.read.0,
regs_used_out.write.1.as_mut_ptr(),
&mut regs_used_out.write.0,
)
})
}
fn set_option(&mut self, type_: sys::OptType, value: libc::size_t) -> Result<(), Error> {
result!(unsafe { sys::cs_option(self.handle, type_, value) })
}
fn close(&mut self) {
result!(unsafe { sys::cs_close(&mut self.handle) })
.expect("error occurred while closing Capstone handle");
}
}
impl Drop for Capstone {
fn drop(&mut self) {
self.close();
}
}
extern "C" fn cs_skipdata_cb(
code: *mut u8,
code_size: *mut libc::size_t,
offset: libc::size_t,
userdata: *mut libc::c_void,
) -> libc::size_t {
if userdata.is_null() {
return 0;
}
let userdata = userdata as *mut Capstone;
#[cfg(feature = "std")]
unsafe {
if (*userdata).pending_panic.borrow().is_some() {
return 0;
}
let cb = std::panic::AssertUnwindSafe(&mut (*userdata).skipdata_callback);
match std::panic::catch_unwind(move || {
if let std::panic::AssertUnwindSafe(Some(ref mut cb)) = cb {
cb(
core::slice::from_raw_parts_mut(code, code_size as usize),
offset as usize,
)
} else {
0
}
}) {
Ok(ret) => ret as libc::size_t,
Err(p) => {
*(*userdata).pending_panic.borrow_mut() = Some(p);
0
}
}
}
#[cfg(not(feature = "std"))]
unsafe {
if let Some(ref mut cb) = (*userdata).skipdata_callback {
cb(
core::slice::from_raw_parts_mut(code, code_size as usize),
offset as usize,
) as libc::size_t
} else {
0
}
}
}
#[derive(Clone, Copy, Default)]
pub struct RegsUsed {
read: RegsBuffer,
write: RegsBuffer,
}
impl RegsUsed {
pub fn read(&self) -> &[Reg] {
&self.read
}
pub fn write(&self) -> &[Reg] {
&self.write
}
}
#[derive(Clone, Copy)]
pub struct RegsBuffer(u8, [Reg; 64]);
impl RegsBuffer {
pub fn new() -> RegsBuffer {
RegsBuffer(0, [Reg::default(); 64])
}
}
impl Default for RegsBuffer {
fn default() -> Self {
Self::new()
}
}
impl core::ops::Deref for RegsBuffer {
type Target = [Reg];
fn deref(&self) -> &Self::Target {
&self.1[..self.0 as usize]
}
}
#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)]
pub enum Syntax {
Default,
Intel,
Att,
NoRegName,
Masm,
}
impl Default for Syntax {
fn default() -> Self {
Self::Default
}
}
#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq, Ord, PartialOrd)]
pub struct CapstoneVersion {
pub major: u16,
pub minor: u16,
}
impl fmt::Display for CapstoneVersion {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}.{}", self.major, self.minor)
}
}
c_enum! {
#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)]
pub enum Arch: u8 {
Arm,
Arm64,
Mips,
X86,
PowerPc,
Sparc,
SystemZ,
XCore,
M68K,
Tms320C64X,
M680X,
Evm,
Mos65xx,
}
}
impl From<Arch> for sys::Arch {
fn from(arch: Arch) -> sys::Arch {
sys::Arch(arch.to_c())
}
}
#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)]
pub enum SupportQuery {
Arch(Arch),
AllArch,
Diet,
X86Reduce,
}
impl From<Arch> for SupportQuery {
fn from(arch: Arch) -> SupportQuery {
SupportQuery::Arch(arch)
}
}
#[allow(non_upper_case_globals)]
mod mode {
bitflags::bitflags! {
pub struct Mode: libc::c_int {
const LittleEndian = 0;
const Arm = 0;
const Bits16 = 1 << 1;
const Bits32 = 1 << 2;
const Bits64 = 1 << 3;
const Thumb = 1 << 4;
const MClass = 1 << 5;
const V8 = 1 << 6;
const Micro = 1 << 4;
const Mips3 = 1 << 5;
const Mips32R6 = 1 << 6;
const Mips2 = 1 << 7;
const V9 = 1 << 4;
const Qpx = 1 << 4;
const M68K000 = 1 << 1;
const M68K010 = 1 << 2;
const M68K020 = 1 << 3;
const M68K030 = 1 << 4;
const M68K040 = 1 << 5;
const M68K060 = 1 << 6;
const BigEndian = 1 << 31;
const Mips32 = Self::Bits32.bits;
const Mips64 = Self::Bits64.bits;
const M680X6301 = 1 << 1;
const M680X6309 = 1 << 2;
const M680X6800 = 1 << 3;
const M680X6801 = 1 << 4;
const M680X6805 = 1 << 5;
const M680X6808 = 1 << 6;
const M680X6809 = 1 << 7;
const M680X6811 = 1 << 8;
const M680XCPU12 = 1 << 9;
const M680XHCS08 = 1 << 10;
}
}
}
#[doc(inline)]
pub use mode::Mode;
impl From<Mode> for sys::Mode {
fn from(mode: Mode) -> sys::Mode {
sys::Mode(mode.bits() as _)
}
}
c_enum! {
#[derive(Clone, Copy, Debug, Hash, PartialEq, Eq)]
pub enum Error: u8 {
Memory = 1,
Arch,
Handle,
Csh,
Mode,
Option,
Detail,
MemSetup,
Version,
Diet,
Skipdata,
X86Att,
X86Intel,
X86Masm,
Bindings,
}
}
impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let msg = match self {
Error::Memory => "out of memory",
Error::Arch => "unsupported architecture",
Error::Handle => "invalid handle",
Error::Csh => "invalid capstone handle",
Error::Mode => "invalid/unsupported mode",
Error::Option => "invalid/unsupported option",
Error::Detail => "detail unavailable",
Error::MemSetup => "dynamic memory management uninitialized",
Error::Version => "unsupported version",
Error::Diet => "accessed irrelevant data in diet engine",
Error::Skipdata => "accessed irrelevant data for data instruction in skipdata mode",
Error::X86Att => "X86 AT&T syntax is unsupported",
Error::X86Intel => "X86 Intel syntex is unsupported",
Error::X86Masm => "X86 MASM syntex is unsupported",
Error::Bindings => "bindings error (please file an issue)",
};
f.write_str(msg)
}
}
#[cfg(feature = "std")]
impl std::error::Error for Error {}
#[derive(Clone, Copy)]
struct PackedCSInfo(u8);
impl PackedCSInfo {
fn new(arch: Arch, detail: bool, skipdata: bool) -> Self {
let mut p = PackedCSInfo(0);
p.set_arch(arch);
p.set_detail(detail);
p.set_skipdata(skipdata);
p
}
fn arch(self) -> Arch {
match Arch::from_primitive(self.0 & 0xF) {
Some(arch) => arch,
#[cfg(test)]
None => unreachable!("bad arch from PackedCSInfo"),
#[cfg(not(test))]
None => unsafe { core::hint::unreachable_unchecked() },
}
}
fn detail(self) -> bool {
((self.0 >> 4) & 1) != 0
}
fn skipdata(self) -> bool {
((self.0 >> 5) & 1) != 0
}
fn set_arch(&mut self, arch: Arch) {
self.0 = (self.0 & !0xF) | arch.to_primitive()
}
fn set_detail(&mut self, detail: bool) {
self.0 = (self.0 & !(1 << 4)) | ((detail as u8) << 4);
}
fn set_skipdata(&mut self, skipdata: bool) {
self.0 = (self.0 & !(1 << 5)) | ((skipdata as u8) << 5);
}
}
pub fn version() -> CapstoneVersion {
let mut major: libc::c_int = 0;
let mut minor: libc::c_int = 0;
unsafe { sys::cs_version(&mut major, &mut minor) };
CapstoneVersion {
major: major as u16,
minor: minor as u16,
}
}
pub fn supports<Query>(query: Query) -> bool
where
Query: Into<SupportQuery>,
{
let query_int = match query.into() {
SupportQuery::Arch(arch) => arch as libc::c_int,
SupportQuery::AllArch => 0xFFFF,
SupportQuery::Diet => 0x10000,
SupportQuery::X86Reduce => 0x10001,
};
unsafe { sys::cs_support(query_int) }
}
#[cfg(test)]
mod test {
use super::*;
const ALL_ARCHS: &[Arch] = &[
Arch::Arm,
Arch::Arm64,
Arch::Mips,
Arch::X86,
Arch::PowerPc,
Arch::Sparc,
Arch::SystemZ,
Arch::XCore,
Arch::M68K,
Arch::Tms320C64X,
Arch::M680X,
Arch::Evm,
Arch::Mos65xx,
];
#[test]
fn open_capstone() {
let mut caps =
Capstone::open(Arch::X86, Mode::LittleEndian).expect("failed to open capstone");
caps.set_details_enabled(true)
.expect("failed to enable capstone instruction details");
caps.set_mnemonic(x86::InsnId::Add, "better-add")
.expect("failed to substitute instruction mnemonic");
println!("capstone size: {} bytes", core::mem::size_of::<Capstone>());
let mut regs_used = RegsUsed::default();
for insn in caps.disasm_iter(
&[
0x8d, 0x4c, 0x32, 0x08, 0x01, 0xd8, 0x81, 0xc6, 0x34, 0x12, 0x00, 0x00, 0x05, 0x23,
0x01, 0x00, 0x00, 0x36, 0x8b, 0x84, 0x91, 0x23, 0x01, 0x00, 0x00, 0x41, 0x8d, 0x84,
0x39, 0x89, 0x67, 0x00, 0x00, 0x8d, 0x87, 0x89, 0x67, 0x00, 0x00, 0xb4, 0xc6, 0xe9,
0xea, 0xbe, 0xad, 0xde, 0xff, 0xa0, 0x23, 0x01, 0x00, 0x00, 0xe8, 0xdf, 0xbe, 0xad,
0xde, 0x74, 0xff,
],
0x0,
) {
let insn = insn.unwrap();
println!("{} {}", insn.mnemonic(), insn.operands());
caps.regs_used(insn, &mut regs_used)
.expect("failed to get registers accessed");
for reg in regs_used.read().iter() {
println!("\t read reg {}", caps.reg_name(*reg));
}
for reg in regs_used.write().iter() {
println!("\twrite reg {}", caps.reg_name(*reg));
}
println!("GROUPS:");
for grp in caps.details(insn).groups() {
println!("\t- {}", caps.group_name(*grp));
}
}
}
#[test]
fn validate_packed_cs_info_states() {
for arch in ALL_ARCHS.iter().copied() {
let packed = PackedCSInfo::new(arch, true, true);
assert_eq!(packed.arch(), arch);
assert_eq!(packed.detail(), true);
assert_eq!(packed.skipdata(), true);
let packed = PackedCSInfo::new(arch, false, true);
assert_eq!(packed.arch(), arch);
assert_eq!(packed.detail(), false);
assert_eq!(packed.skipdata(), true);
let packed = PackedCSInfo::new(arch, true, false);
assert_eq!(packed.arch(), arch);
assert_eq!(packed.detail(), true);
assert_eq!(packed.skipdata(), false);
let packed = PackedCSInfo::new(arch, false, false);
assert_eq!(packed.arch(), arch);
assert_eq!(packed.detail(), false);
assert_eq!(packed.skipdata(), false);
}
}
#[test]
fn test_version() {
pub const EXPECTED_MAJOR_VERSION: u16 = 5;
pub const EXPECTED_MINOR_VERSION: u16 = 0;
let v = version();
assert_eq!(v.major, EXPECTED_MAJOR_VERSION);
assert_eq!(v.minor, EXPECTED_MINOR_VERSION);
}
#[test]
fn test_support() {
assert_eq!(supports(Arch::Arm), cfg!(feature = "arm"));
assert_eq!(supports(Arch::Arm64), cfg!(feature = "aarch64"));
assert_eq!(supports(Arch::Mips), cfg!(feature = "mips"));
assert_eq!(supports(Arch::X86), cfg!(feature = "x86"));
assert_eq!(supports(Arch::PowerPc), cfg!(feature = "powerpc"));
assert_eq!(supports(Arch::Sparc), cfg!(feature = "sparc"));
assert_eq!(supports(Arch::SystemZ), cfg!(feature = "systemz"));
assert_eq!(supports(Arch::XCore), cfg!(feature = "xcore"));
assert_eq!(supports(Arch::M68K), cfg!(feature = "m68k"));
assert_eq!(supports(Arch::Tms320C64X), cfg!(feature = "tms320c64x"));
assert_eq!(supports(Arch::M680X), cfg!(feature = "m680x"));
assert_eq!(supports(Arch::Evm), cfg!(feature = "evm"));
assert_eq!(supports(Arch::Mos65xx), cfg!(feature = "mos65xx"));
assert_eq!(supports(SupportQuery::Diet), cfg!(feature = "diet"));
assert_eq!(
supports(SupportQuery::X86Reduce),
cfg!(feature = "x86-reduce")
);
}
}