1use alloc::{self, boxed::Box};
2use core::convert::TryFrom;
3use core::fmt::{self, Debug, Display, Error, Formatter};
4use core::marker::PhantomData;
5use core::mem::MaybeUninit;
6use core::ops::Deref;
7use core::slice;
8use core::str;
9
10use capstone_sys::*;
11
12use crate::arch::ArchDetail;
13use crate::constants::Arch;
14
15use crate::ffi::str_from_cstr_ptr;
16use crate::{RegsAccessBuf, REGS_ACCESS_BUF_LEN};
17
18#[derive(Debug)]
31pub struct Instructions<'a>(&'a mut [cs_insn]);
32
33pub type InsnIdInt = u32;
35
36#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq, PartialOrd, Ord)]
40pub struct InsnId(pub InsnIdInt);
41
42pub type InsnGroupIdInt = u8;
44
45#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq, PartialOrd, Ord)]
49#[repr(transparent)]
50pub struct InsnGroupId(pub InsnGroupIdInt);
51
52pub use capstone_sys::cs_group_type as InsnGroupType;
53
54pub type RegIdInt = u16;
56
57#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq, PartialOrd, Ord)]
61#[repr(transparent)]
62pub struct RegId(pub RegIdInt);
63
64impl RegId {
65 pub const INVALID_REG: Self = Self(0);
67}
68
69#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)]
71pub enum AccessType {
72 ReadOnly,
74 WriteOnly,
76 ReadWrite,
78}
79
80impl AccessType {
81 pub fn is_readable(self) -> bool {
87 self == AccessType::ReadOnly || self == AccessType::ReadWrite
88 }
89
90 pub fn is_writable(self) -> bool {
96 self == AccessType::WriteOnly || self == AccessType::ReadWrite
97 }
98}
99
100impl TryFrom<cs_ac_type> for AccessType {
101 type Error = ();
102
103 fn try_from(access: cs_ac_type) -> Result<Self, Self::Error> {
104 let unknown_flag_mask = !(cs_ac_type::CS_AC_READ | cs_ac_type::CS_AC_WRITE).0;
106 if (access.0 & unknown_flag_mask) != 0 {
107 return Err(());
108 }
109
110 let is_readable = (access & cs_ac_type::CS_AC_READ).0 != 0;
111 let is_writable = (access & cs_ac_type::CS_AC_WRITE).0 != 0;
112 match (is_readable, is_writable) {
113 (true, false) => Ok(AccessType::ReadOnly),
114 (false, true) => Ok(AccessType::WriteOnly),
115 (true, true) => Ok(AccessType::ReadWrite),
116 _ => Err(()),
117 }
118 }
119}
120
121pub type RegAccessType = AccessType;
124
125impl<'a> Instructions<'a> {
126 pub(crate) unsafe fn from_raw_parts(ptr: *mut cs_insn, len: usize) -> Instructions<'a> {
127 Instructions(slice::from_raw_parts_mut(ptr, len))
128 }
129
130 pub(crate) fn new_empty() -> Instructions<'a> {
131 Instructions(&mut [])
132 }
133}
134
135impl<'a> core::ops::Deref for Instructions<'a> {
136 type Target = [Insn<'a>];
137
138 #[inline]
139 fn deref(&self) -> &[Insn<'a>] {
140 unsafe { &*(self.0 as *const [cs_insn] as *const [Insn]) }
142 }
143}
144
145impl<'a> AsRef<[Insn<'a>]> for Instructions<'a> {
146 #[inline]
147 fn as_ref(&self) -> &[Insn<'a>] {
148 self.deref()
149 }
150}
151
152impl Drop for Instructions<'_> {
153 fn drop(&mut self) {
154 if !self.is_empty() {
155 unsafe {
156 cs_free(self.0.as_mut_ptr(), self.len());
157 }
158 }
159 }
160}
161
162#[repr(transparent)]
168pub struct Insn<'a> {
169 pub(crate) insn: cs_insn,
171
172 pub(crate) _marker: PhantomData<&'a InsnDetail<'a>>,
174}
175
176pub(crate) struct RWRegsAccessBuf {
177 pub(crate) read_buf: RegsAccessBuf,
178 pub(crate) write_buf: RegsAccessBuf,
179}
180
181impl RWRegsAccessBuf {
182 pub(crate) fn new() -> Self {
183 Self {
184 read_buf: [MaybeUninit::uninit(); REGS_ACCESS_BUF_LEN],
185 write_buf: [MaybeUninit::uninit(); REGS_ACCESS_BUF_LEN],
186 }
187 }
188}
189
190#[cfg_attr(not(feature = "full"), allow(dead_code))]
192pub(crate) struct PartialInitRegsAccess {
193 pub(crate) regs_buf: Box<RWRegsAccessBuf>,
194 pub(crate) read_len: u16,
195 pub(crate) write_len: u16,
196}
197
198static_assertions::const_assert!(crate::REGS_ACCESS_BUF_LEN <= u16::MAX as usize);
200
201#[cfg_attr(not(feature = "full"), allow(dead_code))]
202impl PartialInitRegsAccess {
203 unsafe fn maybeuninit_slice_to_slice(buf: &[MaybeUninit<RegId>]) -> &[RegId] {
204 &*(buf as *const [MaybeUninit<RegId>] as *const [RegId])
205 }
206
207 pub(crate) fn read(&self) -> &[RegId] {
208 unsafe {
209 Self::maybeuninit_slice_to_slice(&self.regs_buf.read_buf[..self.read_len as usize])
210 }
211 }
212
213 pub(crate) fn write(&self) -> &[RegId] {
214 unsafe {
215 Self::maybeuninit_slice_to_slice(&self.regs_buf.write_buf[..self.write_len as usize])
216 }
217 }
218}
219
220pub struct InsnDetail<'a> {
250 pub(crate) detail: &'a cs_detail,
251 pub(crate) arch: Arch,
252
253 #[cfg_attr(not(feature = "full"), allow(dead_code))]
254 partial_init_regs_access: Option<PartialInitRegsAccess>,
255}
256
257#[allow(clippy::len_without_is_empty)]
258impl Insn<'_> {
259 pub unsafe fn from_raw(insn: *const cs_insn) -> Self {
273 Self {
274 insn: core::ptr::read(insn),
275 _marker: PhantomData,
276 }
277 }
278
279 #[inline]
282 pub fn mnemonic(&self) -> Option<&str> {
283 if cfg!(feature = "full") {
284 unsafe { str_from_cstr_ptr(self.insn.mnemonic.as_ptr()) }
285 } else {
286 None
287 }
288 }
289
290 #[inline]
293 pub fn op_str(&self) -> Option<&str> {
294 if cfg!(feature = "full") {
295 unsafe { str_from_cstr_ptr(self.insn.op_str.as_ptr()) }
296 } else {
297 None
298 }
299 }
300
301 #[inline]
303 pub fn id(&self) -> InsnId {
304 InsnId(self.insn.id)
305 }
306
307 #[inline]
309 pub fn len(&self) -> usize {
310 self.insn.size as usize
311 }
312
313 #[inline]
315 pub fn address(&self) -> u64 {
316 self.insn.address
317 }
318
319 #[inline]
321 pub fn bytes(&self) -> &[u8] {
322 &self.insn.bytes[..self.len()]
323 }
324
325 #[inline]
334 pub(crate) unsafe fn detail(
335 &self,
336 arch: Arch,
337 partial_init_regs_access: Option<PartialInitRegsAccess>,
338 ) -> InsnDetail<'_> {
339 InsnDetail {
340 detail: &*self.insn.detail,
341 arch,
342 partial_init_regs_access,
343 }
344 }
345}
346
347impl From<&Insn<'_>> for OwnedInsn<'_> {
348 fn from(insn: &Insn<'_>) -> Self {
352 let mut new = unsafe { <*const cs_insn>::read(&insn.insn as _) };
353 new.detail = if new.detail.is_null() {
354 new.detail
355 } else {
356 unsafe {
357 let new_detail = Box::new(*new.detail);
358 Box::into_raw(new_detail)
359 }
360 };
361 Self {
362 insn: new,
363 _marker: PhantomData,
364 }
365 }
366}
367
368impl<'a> Deref for OwnedInsn<'a> {
372 type Target = Insn<'a>;
373 fn deref(&self) -> &Self::Target {
374 unsafe { &*(&self.insn as *const cs_insn as *const Insn) }
375 }
376}
377
378pub struct OwnedInsn<'a> {
384 pub(crate) insn: cs_insn,
386
387 pub(crate) _marker: PhantomData<&'a InsnDetail<'a>>,
389}
390
391impl Debug for Insn<'_> {
392 fn fmt(&self, fmt: &mut Formatter) -> Result<(), Error> {
393 fmt.debug_struct("Insn")
394 .field("address", &self.address())
395 .field("len", &self.len())
396 .field("bytes", &self.bytes())
397 .field("mnemonic", &self.mnemonic())
398 .field("op_str", &self.op_str())
399 .finish()
400 }
401}
402
403impl Display for Insn<'_> {
404 fn fmt(&self, fmt: &mut Formatter) -> fmt::Result {
405 write!(fmt, "{:#x}:", self.address())?;
406 if let Some(mnemonic) = self.mnemonic() {
407 write!(fmt, " {mnemonic}")?;
408 if let Some(op_str) = self.op_str() {
409 if !op_str.is_empty() {
410 write!(fmt, " {op_str}")?;
411 }
412 }
413 }
414 Ok(())
415 }
416}
417
418impl Drop for OwnedInsn<'_> {
419 fn drop(&mut self) {
420 if let Some(ptr) = core::ptr::NonNull::new(self.insn.detail) {
421 unsafe { drop(Box::from_raw(ptr.as_ptr())) }
422 }
423 }
424}
425
426impl Debug for OwnedInsn<'_> {
427 fn fmt(&self, fmt: &mut Formatter) -> Result<(), Error> {
428 Debug::fmt(&self.deref(), fmt)
429 }
430}
431
432impl Display for OwnedInsn<'_> {
433 fn fmt(&self, fmt: &mut Formatter) -> fmt::Result {
434 Display::fmt(&self.deref(), fmt)
435 }
436}
437
438impl InsnDetail<'_> {
439 #[cfg(feature = "full")]
440 pub fn regs_read(&self) -> &[RegId] {
442 if let Some(partial) = self.partial_init_regs_access.as_ref() {
443 partial.read()
444 } else {
445 unsafe {
446 &*(&self.detail.regs_read[..self.detail.regs_read_count as usize]
447 as *const [RegIdInt] as *const [RegId])
448 }
449 }
450 }
451
452 #[cfg(feature = "full")]
453 pub fn regs_write(&self) -> &[RegId] {
455 if let Some(partial) = self.partial_init_regs_access.as_ref() {
456 partial.write()
457 } else {
458 unsafe {
459 &*(&self.detail.regs_write[..self.detail.regs_write_count as usize]
460 as *const [RegIdInt] as *const [RegId])
461 }
462 }
463 }
464
465 #[cfg(feature = "full")]
466 pub fn groups(&self) -> &[InsnGroupId] {
468 unsafe {
469 &*(&self.detail.groups[..self.detail.groups_count as usize] as *const [InsnGroupIdInt]
470 as *const [InsnGroupId])
471 }
472 }
473
474 pub fn arch_detail(&self) -> ArchDetail<'_> {
476 macro_rules! def_arch_detail_match {
477 (
478 $( [ $ARCH:ident, $detail:ident, $insn_detail:ident, $arch:ident, $feature:literal ] )*
479 ) => {
480 use self::ArchDetail::*;
481 use crate::Arch::*;
482 $(
483 #[cfg(feature = $feature)]
484 use crate::arch::$arch::$insn_detail;
485 )*
486
487 return match self.arch {
488 $(
489 #[cfg(feature = $feature)]
490 $ARCH => {
491 $detail($insn_detail(unsafe { &self.detail.__bindgen_anon_1.$arch }))
492 }
493 )*,
494 #[allow(unreachable_patterns)]
496 _ => panic!("Cannot convert to arch-specific detail of disabled arch ")
497 }
498 }
499 }
500 def_arch_detail_match!(
501 [ARM, ArmDetail, ArmInsnDetail, arm, "arch_arm"]
502 [ARM64, Arm64Detail, Arm64InsnDetail, arm64, "arch_arm64"]
503 [BPF, BpfDetail, BpfInsnDetail, bpf, "arch_bpf"]
504 [EVM, EvmDetail, EvmInsnDetail, evm, "arch_evm"]
505 [M680X, M680xDetail, M680xInsnDetail, m680x, "arch_m680x"]
506 [M68K, M68kDetail, M68kInsnDetail, m68k, "arch_m68k"]
507 [MIPS, MipsDetail, MipsInsnDetail, mips, "arch_mips"]
508 [MOS65XX, Mos65xxDetail, Mos65xxInsnDetail, mos65xx, "arch_mos65xx"]
509 [PPC, PpcDetail, PpcInsnDetail, ppc, "arch_powerpc"]
510 [RISCV, RiscVDetail, RiscVInsnDetail, riscv, "arch_riscv"]
511 [SH, ShDetail, ShInsnDetail, sh, "arch_sh"]
512 [SPARC, SparcDetail, SparcInsnDetail, sparc, "arch_sparc"]
513 [SYSZ, SysZDetail, SysZInsnDetail, sysz, "arch_sysz"]
514 [TMS320C64X, Tms320c64xDetail, Tms320c64xInsnDetail, tms320c64x, "arch_tms320c64x"]
515 [TRICORE, TriCoreDetail, TriCoreInsnDetail, tricore, "arch_tricore"]
516 [X86, X86Detail, X86InsnDetail, x86, "arch_x86"]
517 [XCORE, XcoreDetail, XcoreInsnDetail, xcore, "arch_xcore"]
518 );
519 }
520}
521
522#[cfg(feature = "full")]
523impl Debug for InsnDetail<'_> {
524 fn fmt(&self, fmt: &mut Formatter) -> fmt::Result {
525 fmt.debug_struct("Detail")
526 .field("regs_read", &self.regs_read())
527 .field("regs_write", &self.regs_write())
528 .field("groups", &self.groups())
529 .finish()
530 }
531}
532
533#[cfg(not(feature = "full"))]
534impl<'a> Debug for InsnDetail<'a> {
535 fn fmt(&self, fmt: &mut Formatter) -> fmt::Result {
536 fmt.debug_struct("Detail").finish()
537 }
538}
539
540impl Display for Instructions<'_> {
541 fn fmt(&self, fmt: &mut Formatter) -> fmt::Result {
542 for instruction in self.iter() {
543 write!(fmt, "{:x}:\t", instruction.address())?;
544 for byte in instruction.bytes() {
545 write!(fmt, " {byte:02x}")?;
546 }
547 let remainder = 16 * 3 - instruction.bytes().len() * 3;
548 for _ in 0..remainder {
549 write!(fmt, " ")?;
550 }
551 if let Some(mnemonic) = instruction.mnemonic() {
552 write!(fmt, " {mnemonic}")?;
553 if let Some(op_str) = instruction.op_str() {
554 write!(fmt, " {op_str}")?;
555 }
556 }
557 writeln!(fmt)?;
558 }
559 Ok(())
560 }
561}
562
563#[cfg(test)]
564mod test {
565 use super::*;
566
567 #[test]
568 fn test_invalid_reg_access() {
569 assert_eq!(RegAccessType::try_from(cs_ac_type(1337)), Err(()));
570 }
571}