use alloc::{self, boxed::Box};
use core::convert::TryFrom;
use core::fmt::{self, Debug, Display, Error, Formatter};
use core::marker::PhantomData;
use core::mem::MaybeUninit;
use core::ops::Deref;
use core::slice;
use core::str;
use capstone_sys::*;
use crate::arch::ArchDetail;
use crate::constants::Arch;
use crate::ffi::str_from_cstr_ptr;
use crate::{RegsAccessBuf, REGS_ACCESS_BUF_LEN};
#[derive(Debug)]
pub struct Instructions<'a>(&'a mut [cs_insn]);
pub type InsnIdInt = u32;
#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq, PartialOrd, Ord)]
pub struct InsnId(pub InsnIdInt);
pub type InsnGroupIdInt = u8;
#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq, PartialOrd, Ord)]
#[repr(transparent)]
pub struct InsnGroupId(pub InsnGroupIdInt);
pub use capstone_sys::cs_group_type as InsnGroupType;
pub type RegIdInt = u16;
#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq, PartialOrd, Ord)]
#[repr(transparent)]
pub struct RegId(pub RegIdInt);
impl RegId {
pub const INVALID_REG: Self = Self(0);
}
#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)]
pub enum AccessType {
ReadOnly,
WriteOnly,
ReadWrite,
}
impl AccessType {
pub fn is_readable(self) -> bool {
self == AccessType::ReadOnly || self == AccessType::ReadWrite
}
pub fn is_writable(self) -> bool {
self == AccessType::WriteOnly || self == AccessType::ReadWrite
}
}
impl TryFrom<cs_ac_type> for AccessType {
type Error = ();
fn try_from(access: cs_ac_type) -> Result<Self, Self::Error> {
let unknown_flag_mask = !(cs_ac_type::CS_AC_READ | cs_ac_type::CS_AC_WRITE).0;
if (access.0 & unknown_flag_mask) != 0 {
return Err(());
}
let is_readable = (access & cs_ac_type::CS_AC_READ).0 != 0;
let is_writable = (access & cs_ac_type::CS_AC_WRITE).0 != 0;
match (is_readable, is_writable) {
(true, false) => Ok(AccessType::ReadOnly),
(false, true) => Ok(AccessType::WriteOnly),
(true, true) => Ok(AccessType::ReadWrite),
_ => Err(()),
}
}
}
pub type RegAccessType = AccessType;
impl<'a> Instructions<'a> {
pub(crate) unsafe fn from_raw_parts(ptr: *mut cs_insn, len: usize) -> Instructions<'a> {
Instructions(slice::from_raw_parts_mut(ptr, len))
}
pub(crate) fn new_empty() -> Instructions<'a> {
Instructions(&mut [])
}
}
impl<'a> core::ops::Deref for Instructions<'a> {
type Target = [Insn<'a>];
#[inline]
fn deref(&self) -> &[Insn<'a>] {
unsafe { &*(self.0 as *const [cs_insn] as *const [Insn]) }
}
}
impl<'a> AsRef<[Insn<'a>]> for Instructions<'a> {
#[inline]
fn as_ref(&self) -> &[Insn<'a>] {
self.deref()
}
}
impl Drop for Instructions<'_> {
fn drop(&mut self) {
if !self.is_empty() {
unsafe {
cs_free(self.0.as_mut_ptr(), self.len());
}
}
}
}
#[repr(transparent)]
pub struct Insn<'a> {
pub(crate) insn: cs_insn,
pub(crate) _marker: PhantomData<&'a InsnDetail<'a>>,
}
pub(crate) struct RWRegsAccessBuf {
pub(crate) read_buf: RegsAccessBuf,
pub(crate) write_buf: RegsAccessBuf,
}
impl RWRegsAccessBuf {
pub(crate) fn new() -> Self {
Self {
read_buf: [MaybeUninit::uninit(); REGS_ACCESS_BUF_LEN],
write_buf: [MaybeUninit::uninit(); REGS_ACCESS_BUF_LEN],
}
}
}
#[cfg_attr(not(feature = "full"), allow(dead_code))]
pub(crate) struct PartialInitRegsAccess {
pub(crate) regs_buf: Box<RWRegsAccessBuf>,
pub(crate) read_len: u16,
pub(crate) write_len: u16,
}
static_assertions::const_assert!(crate::REGS_ACCESS_BUF_LEN <= u16::MAX as usize);
#[cfg_attr(not(feature = "full"), allow(dead_code))]
impl PartialInitRegsAccess {
unsafe fn maybeuninit_slice_to_slice(buf: &[MaybeUninit<RegId>]) -> &[RegId] {
&*(buf as *const [MaybeUninit<RegId>] as *const [RegId])
}
pub(crate) fn read(&self) -> &[RegId] {
unsafe {
Self::maybeuninit_slice_to_slice(&self.regs_buf.read_buf[..self.read_len as usize])
}
}
pub(crate) fn write(&self) -> &[RegId] {
unsafe {
Self::maybeuninit_slice_to_slice(&self.regs_buf.write_buf[..self.write_len as usize])
}
}
}
pub struct InsnDetail<'a> {
pub(crate) detail: &'a cs_detail,
pub(crate) arch: Arch,
#[cfg_attr(not(feature = "full"), allow(dead_code))]
partial_init_regs_access: Option<PartialInitRegsAccess>,
}
#[allow(clippy::len_without_is_empty)]
impl Insn<'_> {
pub unsafe fn from_raw(insn: *const cs_insn) -> Self {
Self {
insn: core::ptr::read(insn),
_marker: PhantomData,
}
}
#[inline]
pub fn mnemonic(&self) -> Option<&str> {
if cfg!(feature = "full") {
unsafe { str_from_cstr_ptr(self.insn.mnemonic.as_ptr()) }
} else {
None
}
}
#[inline]
pub fn op_str(&self) -> Option<&str> {
if cfg!(feature = "full") {
unsafe { str_from_cstr_ptr(self.insn.op_str.as_ptr()) }
} else {
None
}
}
#[inline]
pub fn id(&self) -> InsnId {
InsnId(self.insn.id)
}
#[inline]
pub fn len(&self) -> usize {
self.insn.size as usize
}
#[inline]
pub fn address(&self) -> u64 {
self.insn.address
}
#[inline]
pub fn bytes(&self) -> &[u8] {
&self.insn.bytes[..self.len()]
}
#[inline]
pub(crate) unsafe fn detail(
&self,
arch: Arch,
partial_init_regs_access: Option<PartialInitRegsAccess>,
) -> InsnDetail<'_> {
InsnDetail {
detail: &*self.insn.detail,
arch,
partial_init_regs_access,
}
}
}
impl From<&Insn<'_>> for OwnedInsn<'_> {
fn from(insn: &Insn<'_>) -> Self {
let mut new = unsafe { <*const cs_insn>::read(&insn.insn as _) };
new.detail = if new.detail.is_null() {
new.detail
} else {
unsafe {
let new_detail = Box::new(*new.detail);
Box::into_raw(new_detail)
}
};
Self {
insn: new,
_marker: PhantomData,
}
}
}
impl<'a> Deref for OwnedInsn<'a> {
type Target = Insn<'a>;
fn deref(&self) -> &Self::Target {
unsafe { &*(&self.insn as *const cs_insn as *const Insn) }
}
}
pub struct OwnedInsn<'a> {
pub(crate) insn: cs_insn,
pub(crate) _marker: PhantomData<&'a InsnDetail<'a>>,
}
impl Debug for Insn<'_> {
fn fmt(&self, fmt: &mut Formatter) -> Result<(), Error> {
fmt.debug_struct("Insn")
.field("address", &self.address())
.field("len", &self.len())
.field("bytes", &self.bytes())
.field("mnemonic", &self.mnemonic())
.field("op_str", &self.op_str())
.finish()
}
}
impl Display for Insn<'_> {
fn fmt(&self, fmt: &mut Formatter) -> fmt::Result {
write!(fmt, "{:#x}:", self.address())?;
if let Some(mnemonic) = self.mnemonic() {
write!(fmt, " {mnemonic}")?;
if let Some(op_str) = self.op_str() {
if !op_str.is_empty() {
write!(fmt, " {op_str}")?;
}
}
}
Ok(())
}
}
impl Drop for OwnedInsn<'_> {
fn drop(&mut self) {
if let Some(ptr) = core::ptr::NonNull::new(self.insn.detail) {
unsafe { drop(Box::from_raw(ptr.as_ptr())) }
}
}
}
impl Debug for OwnedInsn<'_> {
fn fmt(&self, fmt: &mut Formatter) -> Result<(), Error> {
Debug::fmt(&self.deref(), fmt)
}
}
impl Display for OwnedInsn<'_> {
fn fmt(&self, fmt: &mut Formatter) -> fmt::Result {
Display::fmt(&self.deref(), fmt)
}
}
impl InsnDetail<'_> {
#[cfg(feature = "full")]
pub fn regs_read(&self) -> &[RegId] {
if let Some(partial) = self.partial_init_regs_access.as_ref() {
partial.read()
} else {
unsafe {
&*(&self.detail.regs_read[..self.detail.regs_read_count as usize]
as *const [RegIdInt] as *const [RegId])
}
}
}
#[cfg(feature = "full")]
pub fn regs_write(&self) -> &[RegId] {
if let Some(partial) = self.partial_init_regs_access.as_ref() {
partial.write()
} else {
unsafe {
&*(&self.detail.regs_write[..self.detail.regs_write_count as usize]
as *const [RegIdInt] as *const [RegId])
}
}
}
#[cfg(feature = "full")]
pub fn groups(&self) -> &[InsnGroupId] {
unsafe {
&*(&self.detail.groups[..self.detail.groups_count as usize] as *const [InsnGroupIdInt]
as *const [InsnGroupId])
}
}
pub fn arch_detail(&self) -> ArchDetail<'_> {
macro_rules! def_arch_detail_match {
(
$( [ $ARCH:ident, $detail:ident, $insn_detail:ident, $arch:ident, $feature:literal ] )*
) => {
use self::ArchDetail::*;
use crate::Arch::*;
$(
#[cfg(feature = $feature)]
use crate::arch::$arch::$insn_detail;
)*
return match self.arch {
$(
#[cfg(feature = $feature)]
$ARCH => {
$detail($insn_detail(unsafe { &self.detail.__bindgen_anon_1.$arch }))
}
)*,
#[allow(unreachable_patterns)]
_ => panic!("Cannot convert to arch-specific detail of disabled arch ")
}
}
}
def_arch_detail_match!(
[ARM, ArmDetail, ArmInsnDetail, arm, "arch_arm"]
[ARM64, Arm64Detail, Arm64InsnDetail, arm64, "arch_arm64"]
[BPF, BpfDetail, BpfInsnDetail, bpf, "arch_bpf"]
[EVM, EvmDetail, EvmInsnDetail, evm, "arch_evm"]
[M680X, M680xDetail, M680xInsnDetail, m680x, "arch_m680x"]
[M68K, M68kDetail, M68kInsnDetail, m68k, "arch_m68k"]
[MIPS, MipsDetail, MipsInsnDetail, mips, "arch_mips"]
[MOS65XX, Mos65xxDetail, Mos65xxInsnDetail, mos65xx, "arch_mos65xx"]
[PPC, PpcDetail, PpcInsnDetail, ppc, "arch_powerpc"]
[RISCV, RiscVDetail, RiscVInsnDetail, riscv, "arch_riscv"]
[SH, ShDetail, ShInsnDetail, sh, "arch_sh"]
[SPARC, SparcDetail, SparcInsnDetail, sparc, "arch_sparc"]
[SYSZ, SysZDetail, SysZInsnDetail, sysz, "arch_sysz"]
[TMS320C64X, Tms320c64xDetail, Tms320c64xInsnDetail, tms320c64x, "arch_tms320c64x"]
[TRICORE, TriCoreDetail, TriCoreInsnDetail, tricore, "arch_tricore"]
[X86, X86Detail, X86InsnDetail, x86, "arch_x86"]
[XCORE, XcoreDetail, XcoreInsnDetail, xcore, "arch_xcore"]
);
}
}
#[cfg(feature = "full")]
impl Debug for InsnDetail<'_> {
fn fmt(&self, fmt: &mut Formatter) -> fmt::Result {
fmt.debug_struct("Detail")
.field("regs_read", &self.regs_read())
.field("regs_write", &self.regs_write())
.field("groups", &self.groups())
.finish()
}
}
#[cfg(not(feature = "full"))]
impl<'a> Debug for InsnDetail<'a> {
fn fmt(&self, fmt: &mut Formatter) -> fmt::Result {
fmt.debug_struct("Detail").finish()
}
}
impl Display for Instructions<'_> {
fn fmt(&self, fmt: &mut Formatter) -> fmt::Result {
for instruction in self.iter() {
write!(fmt, "{:x}:\t", instruction.address())?;
for byte in instruction.bytes() {
write!(fmt, " {byte:02x}")?;
}
let remainder = 16 * 3 - instruction.bytes().len() * 3;
for _ in 0..remainder {
write!(fmt, " ")?;
}
if let Some(mnemonic) = instruction.mnemonic() {
write!(fmt, " {mnemonic}")?;
if let Some(op_str) = instruction.op_str() {
write!(fmt, " {op_str}")?;
}
}
writeln!(fmt)?;
}
Ok(())
}
}
#[cfg(test)]
mod test {
use super::*;
#[test]
fn test_invalid_reg_access() {
assert_eq!(RegAccessType::try_from(cs_ac_type(1337)), Err(()));
}
}