Skip to content

Commit d26f945

Browse files
committed
rs: minor fix-ups regs_access; add tests
1 parent 14e855c commit d26f945

File tree

2 files changed

+111
-7
lines changed

2 files changed

+111
-7
lines changed

capstone-rs/src/capstone.rs

+17-7
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ use crate::instruction::{Insn, InsnDetail, InsnGroupId, InsnId, Instructions, Re
1414

1515
use {crate::ffi::str_from_cstr_ptr, alloc::string::ToString, libc::c_uint};
1616

17-
/// This is taken from the [python bindings](https://github.com/capstone-engine/capstone/blob/5fb8a423d4455cade99b12912142fd3a0c10d957/bindings/python/capstone/__init__.py#L929)
17+
/// taken from the [python bindings](https://github.com/capstone-engine/capstone/blob/5fb8a423d4455cade99b12912142fd3a0c10d957/bindings/python/capstone/__init__.py#L929)
1818
const MAX_NUM_REGISTERS: usize = 64;
1919

2020
/// An instance of the capstone disassembler
@@ -103,11 +103,20 @@ impl Iterator for EmptyExtraModeIter {
103103
}
104104
}
105105

106+
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
106107
pub struct RegAccess {
107108
pub read: Vec<RegId>,
108109
pub write: Vec<RegId>,
109110
}
110111

112+
impl RegAccess {
113+
/// Sort read and write fields
114+
pub fn sort(&mut self) {
115+
self.read.sort_unstable();
116+
self.write.sort_unstable();
117+
}
118+
}
119+
111120
impl Capstone {
112121
/// Create a new instance of the decompiler using the builder pattern interface.
113122
/// This is the recommended interface to `Capstone`.
@@ -373,19 +382,20 @@ impl Capstone {
373382
}
374383
}
375384

376-
/// Get the registers are which are read to and written to
377-
pub fn regs_access_buf(&self, insn: &Insn) -> CsResult<RegAccess> {
385+
// todo(tmfink): integrate into regs_read()/regs_write() methods to avoid the need to call
386+
/// Get the registers which are read and written
387+
pub fn regs_access(&self, insn: &Insn) -> CsResult<RegAccess> {
378388
let mut read = Vec::new();
379389
let mut write = Vec::new();
380390

381-
self.regs_access(insn, &mut read, &mut write)?;
391+
self.regs_access_buf(insn, &mut read, &mut write)?;
382392

383393
Ok(RegAccess { read, write })
384394
}
385395

386-
/// Get the registers are which are read to and written to\
387-
/// the registers are pushed to the back of the provided buffers
388-
pub fn regs_access(
396+
/// Get the registers are which are read and written.
397+
/// The registers are pushed to the back of the provided buffers
398+
pub(crate) fn regs_access_buf(
389399
&self,
390400
insn: &Insn,
391401
read: &mut Vec<RegId>,

capstone-rs/src/test.rs

+94
Original file line numberDiff line numberDiff line change
@@ -3510,3 +3510,97 @@ fn test_arch_bpf_detail() {
35103510
],
35113511
);
35123512
}
3513+
3514+
#[cfg(feature = "full")]
3515+
fn assert_regs_access_matches(
3516+
cs: &mut Capstone,
3517+
bytes: &[u8],
3518+
expected_regs_access: CsResult<&[RegAccess]>,
3519+
) {
3520+
let expected_regs_access = expected_regs_access.clone().map(|accesses: &[RegAccess]| {
3521+
accesses
3522+
.iter()
3523+
.map(|regs| {
3524+
let mut regs = regs.clone();
3525+
regs.sort();
3526+
regs
3527+
})
3528+
.collect::<Vec<RegAccess>>()
3529+
});
3530+
let insns = cs.disasm_all(bytes, 0x1000).unwrap();
3531+
let reg_access: CsResult<Vec<RegAccess>> = insns
3532+
.iter()
3533+
.map(|insn| {
3534+
cs.regs_access(insn).map(|mut regs| {
3535+
regs.sort();
3536+
regs
3537+
})
3538+
})
3539+
.collect();
3540+
let reg_access = reg_access.as_ref().map(|access| access.as_slice());
3541+
assert_eq!(reg_access, expected_regs_access.as_deref());
3542+
}
3543+
3544+
fn as_reg_access<T: TryInto<RegIdInt> + Copy + Debug>(read: &[T], write: &[T]) -> RegAccess {
3545+
let as_reg_access = |input: &[T]| -> Vec<RegId> {
3546+
input
3547+
.iter()
3548+
.copied()
3549+
.map(|reg| {
3550+
RegId(
3551+
reg.try_into()
3552+
.unwrap_or_else(|_| panic!("Failed to create RegInt")),
3553+
)
3554+
})
3555+
.collect()
3556+
};
3557+
RegAccess {
3558+
read: as_reg_access(read),
3559+
write: as_reg_access(write),
3560+
}
3561+
}
3562+
3563+
#[cfg(feature = "full")]
3564+
fn test_regs_access(mut cs: Capstone, code: &[u8], expected_regs_access: CsResult<&[RegAccess]>) {
3565+
// should always fail when not in debug mode
3566+
assert_regs_access_matches(&mut cs, code, CsResult::Err(Error::DetailOff));
3567+
3568+
// now detail is enabled, check for expected outcome
3569+
cs.set_detail(true).expect("failed to set detail");
3570+
assert_regs_access_matches(&mut cs, code, expected_regs_access);
3571+
}
3572+
3573+
#[cfg(feature = "full")]
3574+
#[test]
3575+
fn test_regs_access_arm() {
3576+
use crate::arch::arm::ArmReg::*;
3577+
3578+
test_regs_access(
3579+
Capstone::new()
3580+
.arm()
3581+
.mode(arm::ArchMode::Thumb)
3582+
.build()
3583+
.unwrap(),
3584+
b"\xf0\xbd",
3585+
CsResult::Ok(&[as_reg_access(
3586+
&[ARM_REG_SP],
3587+
&[
3588+
ARM_REG_SP, ARM_REG_R4, ARM_REG_R5, ARM_REG_R6, ARM_REG_R7, ARM_REG_PC,
3589+
],
3590+
)]),
3591+
);
3592+
}
3593+
3594+
#[cfg(feature = "full")]
3595+
#[test]
3596+
fn test_regs_tms320c64x() {
3597+
test_regs_access(
3598+
Capstone::new()
3599+
.tms320c64x()
3600+
.mode(tms320c64x::ArchMode::Default)
3601+
.build()
3602+
.unwrap(),
3603+
b"\x01\xac\x88\x40",
3604+
CsResult::Err(Error::UnsupportedArch),
3605+
);
3606+
}

0 commit comments

Comments
 (0)