From f0bdeb5a74a614d43163e395a8015c7cb0e9b907 Mon Sep 17 00:00:00 2001 From: Amaan Qureshi Date: Sat, 12 Apr 2025 22:36:24 -0400 Subject: [PATCH] feat(rust): improve ARM CP register ergonomics (#2160) --- bindings/rust/src/hook.rs | 4 +- bindings/rust/src/lib.rs | 63 ++++++++++++---- bindings/rust/src/tests/arm.rs | 35 +-------- bindings/rust/src/tests/arm64.rs | 5 +- bindings/rust/src/tests/ppc.rs | 1 - bindings/rust/sys/build.rs | 2 +- bindings/rust/sys/src/lib.rs | 125 +++++++++++++++++++++++++++++++ 7 files changed, 184 insertions(+), 51 deletions(-) diff --git a/bindings/rust/src/hook.rs b/bindings/rust/src/hook.rs index 2dd22464..71ba1e93 100644 --- a/bindings/rust/src/hook.rs +++ b/bindings/rust/src/hook.rs @@ -215,11 +215,11 @@ where pub unsafe extern "C" fn insn_sys_hook_proxy_arm64( uc: *mut uc_engine, reg: sys::RegisterARM64, - cp_reg: *const sys::RegisterARM64_CP, + cp_reg: *const sys::RegisterARM64CP, user_data: *mut UcHook, ) -> bool where - F: FnMut(&mut crate::Unicorn, sys::RegisterARM64, &sys::RegisterARM64_CP) -> bool, + F: FnMut(&mut crate::Unicorn, sys::RegisterARM64, &sys::RegisterARM64CP) -> bool, { let user_data = unsafe { &mut *user_data }; let mut user_data_uc = Unicorn { diff --git a/bindings/rust/src/lib.rs b/bindings/rust/src/lib.rs index 0f5217ff..775e9172 100644 --- a/bindings/rust/src/lib.rs +++ b/bindings/rust/src/lib.rs @@ -573,7 +573,7 @@ impl<'a, D> Unicorn<'a, D> { } /// Read ARM Coprocessor register - pub fn reg_read_arm_coproc(&self, reg: &mut RegisterARM_CP) -> Result<(), uc_error> { + pub fn reg_read_arm_coproc(&self, reg: &mut RegisterARMCP) -> Result<(), uc_error> { let curr_arch = self.get_arch(); match curr_arch { #[cfg(feature = "arch_arm")] @@ -585,14 +585,33 @@ impl<'a, D> Unicorn<'a, D> { uc_reg_read( self.get_handle(), RegisterARM::CP_REG.into(), - core::ptr::from_mut::(reg).cast(), + core::ptr::from_mut(reg).cast(), + ) + } + .into() + } + + /// Write ARM Coprocessor register + pub fn reg_write_arm_coproc(&mut self, reg: &RegisterARMCP) -> Result<(), uc_error> { + let curr_arch = self.get_arch(); + match curr_arch { + #[cfg(feature = "arch_arm")] + Arch::ARM => {} + _ => return Err(uc_error::ARCH), + } + + unsafe { + uc_reg_write( + self.get_handle(), + RegisterARM::CP_REG.into(), + core::ptr::from_ref(reg).cast(), ) } .into() } /// Read ARM64 Coprocessor register - pub fn reg_read_arm64_coproc(&self) -> Result { + pub fn reg_read_arm64_coproc(&self, reg: &mut RegisterARM64CP) -> Result<(), uc_error> { let curr_arch = self.get_arch(); match curr_arch { #[cfg(feature = "arch_aarch64")] @@ -600,17 +619,33 @@ impl<'a, D> Unicorn<'a, D> { _ => return Err(uc_error::ARCH), } - let regid = RegisterARM64::CP_REG; - let mut reg = RegisterARM64_CP { - crn: 0, - crm: 0, - op0: 0, - op1: 0, - op2: 0, - val: 0, - }; + unsafe { + uc_reg_read( + self.get_handle(), + RegisterARM64::CP_REG.into(), + core::ptr::from_mut(reg).cast(), + ) + } + .and(Ok(())) + } - unsafe { uc_reg_read(self.get_handle(), regid as i32, (&raw mut reg).cast()) }.and(Ok(reg)) + /// Write ARM64 Coprocessor register + pub fn reg_write_arm64_coproc(&mut self, reg: &RegisterARM64CP) -> Result<(), uc_error> { + let curr_arch = self.get_arch(); + match curr_arch { + #[cfg(feature = "arch_aarch64")] + Arch::ARM64 => {} + _ => return Err(uc_error::ARCH), + } + + unsafe { + uc_reg_write( + self.get_handle(), + RegisterARM64::CP_REG.into(), + core::ptr::from_ref(reg).cast(), + ) + } + .and(Ok(())) } #[cfg(feature = "arch_arm")] @@ -925,7 +960,7 @@ impl<'a, D> Unicorn<'a, D> { callback: F, ) -> Result where - F: FnMut(&mut Unicorn, RegisterARM64, &RegisterARM64_CP) -> bool + 'a, + F: FnMut(&mut Unicorn, RegisterARM64, &RegisterARM64CP) -> bool + 'a, { let mut hook_id = 0; let mut user_data = Box::new(hook::UcHook { diff --git a/bindings/rust/src/tests/arm.rs b/bindings/rust/src/tests/arm.rs index 68aa6596..32dcbd3d 100644 --- a/bindings/rust/src/tests/arm.rs +++ b/bindings/rust/src/tests/arm.rs @@ -1,5 +1,5 @@ use super::*; -use crate::{ArmCpuModel, RegisterARM, RegisterARM_CP, TcgOpCode, TcgOpFlag, uc_error}; +use crate::{ArmCpuModel, RegisterARM, RegisterARMCP, TcgOpCode, TcgOpFlag, uc_error}; #[test] fn test_arm_nop() { @@ -600,16 +600,7 @@ fn test_arm_mem_access_abort() { #[test] fn test_arm_read_sctlr() { let uc = Unicorn::new(Arch::ARM, Mode::ARM).unwrap(); - let mut reg = RegisterARM_CP { - cp: 15, - is64: 0, - sec: 0, - crn: 1, - crm: 0, - opc1: 0, - opc2: 0, - val: 0, - }; + let mut reg = RegisterARMCP::new().cp(15).crn(1); uc.reg_read_arm_coproc(&mut reg).unwrap(); assert_eq!((reg.val >> 31) & 1, 0); } @@ -620,16 +611,7 @@ fn test_arm_be_cpsr_sctlr() { uc.ctl_set_cpu_model(ArmCpuModel::Model_1176 as i32) .unwrap(); - let mut reg = RegisterARM_CP { - cp: 15, - is64: 0, - sec: 0, - crn: 1, - crm: 0, - opc1: 0, - opc2: 0, - val: 0, - }; + let mut reg = RegisterARMCP::new().cp(15).crn(1); uc.reg_read_arm_coproc(&mut reg).unwrap(); let cpsr = uc.reg_read(RegisterARM::CPSR).unwrap(); @@ -640,16 +622,7 @@ fn test_arm_be_cpsr_sctlr() { uc.ctl_set_cpu_model(ArmCpuModel::CORTEX_A15 as i32) .unwrap(); - let mut reg = RegisterARM_CP { - cp: 15, - is64: 0, - sec: 0, - crn: 1, - crm: 0, - opc1: 0, - opc2: 0, - val: 0, - }; + let mut reg = RegisterARMCP::new().cp(15).crn(1); uc.reg_read_arm_coproc(&mut reg).unwrap(); let cpsr = uc.reg_read(RegisterARM::CPSR).unwrap(); diff --git a/bindings/rust/src/tests/arm64.rs b/bindings/rust/src/tests/arm64.rs index 8c881c60..7992f6f6 100644 --- a/bindings/rust/src/tests/arm64.rs +++ b/bindings/rust/src/tests/arm64.rs @@ -1,4 +1,4 @@ -use unicorn_engine_sys::{Arm64CpuModel, Arm64Insn, RegisterARM64}; +use unicorn_engine_sys::{Arm64CpuModel, Arm64Insn, RegisterARM64, RegisterARM64CP}; use super::*; @@ -142,7 +142,8 @@ fn test_arm64_v8_pac() { fn test_arm64_read_sctlr() { let uc = Unicorn::new(Arch::ARM64, Mode::ARM | Mode::LITTLE_ENDIAN).unwrap(); - let reg = uc.reg_read_arm64_coproc().unwrap(); + let mut reg = RegisterARM64CP::new().crn(1).op0(0b11); + uc.reg_read_arm64_coproc(&mut reg).unwrap(); assert_eq!(reg.val >> 58, 0); } diff --git a/bindings/rust/src/tests/ppc.rs b/bindings/rust/src/tests/ppc.rs index f7b27437..44ff88e1 100644 --- a/bindings/rust/src/tests/ppc.rs +++ b/bindings/rust/src/tests/ppc.rs @@ -25,7 +25,6 @@ fn test_ppc32_add() { // https://www.ibm.com/docs/en/aix/7.2?topic=set-fadd-fa-floating-add-instruction #[test] -// #[ignore = "Crashes on Windows & some Linux distros"] fn test_ppc32_fadd() { let code = [ 0xfc, 0xc4, 0x28, 0x2a, // fadd 6, 4, 5 diff --git a/bindings/rust/sys/build.rs b/bindings/rust/sys/build.rs index f6e53e56..45ffbddd 100644 --- a/bindings/rust/sys/build.rs +++ b/bindings/rust/sys/build.rs @@ -227,7 +227,7 @@ impl ParseCallbacks for Renamer { return original_item_name .strip_prefix("uc_") .and_then(|suffix| suffix.strip_suffix("_reg")) - .map(|suffix| format!("Register{}", suffix.to_uppercase())); + .map(|suffix| format!("Register{}", suffix.replace('_', "").to_uppercase())); } if original_item_name.ends_with("_insn") { diff --git a/bindings/rust/sys/src/lib.rs b/bindings/rust/sys/src/lib.rs index 94425f7d..90d3801d 100644 --- a/bindings/rust/sys/src/lib.rs +++ b/bindings/rust/sys/src/lib.rs @@ -113,6 +113,131 @@ impl ControlType { pub const IO_WRITE: Self = Self(1 << 30); } +impl Default for RegisterARMCP { + fn default() -> Self { + Self::new() + } +} + +impl RegisterARMCP { + #[must_use] + pub const fn new() -> Self { + Self { + cp: 0, + is64: 0, + sec: 0, + crn: 0, + crm: 0, + opc1: 0, + opc2: 0, + val: 0, + } + } + + #[must_use] + pub const fn cp(mut self, cp: u32) -> Self { + self.cp = cp; + self + } + + #[must_use] + pub const fn is64(mut self, is64: u32) -> Self { + self.is64 = is64; + self + } + + #[must_use] + pub const fn sec(mut self, sec: u32) -> Self { + self.sec = sec; + self + } + + #[must_use] + pub const fn crn(mut self, crn: u32) -> Self { + self.crn = crn; + self + } + + #[must_use] + pub const fn crm(mut self, crm: u32) -> Self { + self.crm = crm; + self + } + + #[must_use] + pub const fn opc1(mut self, opc1: u32) -> Self { + self.opc1 = opc1; + self + } + + #[must_use] + pub const fn opc2(mut self, opc2: u32) -> Self { + self.opc2 = opc2; + self + } + + #[must_use] + pub const fn val(mut self, val: u64) -> Self { + self.val = val; + self + } +} + +impl Default for RegisterARM64CP { + fn default() -> Self { + Self::new() + } +} + +impl RegisterARM64CP { + #[must_use] + pub const fn new() -> Self { + Self { + crn: 0, + crm: 0, + op0: 0, + op1: 0, + op2: 0, + val: 0, + } + } + #[must_use] + pub const fn crn(mut self, crn: u32) -> Self { + self.crn = crn; + self + } + + #[must_use] + pub const fn crm(mut self, crm: u32) -> Self { + self.crm = crm; + self + } + + #[must_use] + pub const fn op0(mut self, op0: u32) -> Self { + self.op0 = op0; + self + } + + #[must_use] + pub const fn op1(mut self, op1: u32) -> Self { + self.op1 = op1; + self + } + + #[must_use] + pub const fn op2(mut self, op2: u32) -> Self { + self.op2 = op2; + self + } + + #[must_use] + pub const fn val(mut self, val: u64) -> Self { + self.val = val; + self + } +} + impl From for i32 { fn from(value: M68kCpuModel) -> Self { value as Self