From f8f0d4471faf80f32942ebf56354482fa9ed6159 Mon Sep 17 00:00:00 2001 From: Dominik Maier Date: Mon, 8 Nov 2021 19:34:53 +0100 Subject: [PATCH 01/12] Unicorn rust bindings improvements --- bindings/rust/Cargo.toml | 2 +- bindings/rust/build.rs | 5 +- bindings/rust/src/arm.rs | 6 + bindings/rust/src/arm64.rs | 10 +- bindings/rust/src/ffi.rs | 185 ++++++------- bindings/rust/src/lib.rs | 415 +++++++++++++++-------------- bindings/rust/src/m68k.rs | 7 +- bindings/rust/src/mips.rs | 6 + bindings/rust/src/ppc.rs | 6 + bindings/rust/src/riscv.rs | 6 + bindings/rust/src/sparc.rs | 14 +- bindings/rust/src/unicorn_const.rs | 2 + bindings/rust/src/x86.rs | 12 +- bindings/rust/tests/unicorn.rs | 97 ++++--- include/uc_priv.h | 1 + uc.c | 21 ++ 16 files changed, 412 insertions(+), 383 deletions(-) diff --git a/bindings/rust/Cargo.toml b/bindings/rust/Cargo.toml index 9c020538a7..23482b933e 100644 --- a/bindings/rust/Cargo.toml +++ b/bindings/rust/Cargo.toml @@ -10,7 +10,7 @@ include = [ "/Cargo.toml", "/README.md", "/src/*", - "build.rs" + "build.rs", ] license = "GPL-2.0" readme = "README.md" diff --git a/bindings/rust/build.rs b/bindings/rust/build.rs index 9900870d31..195e9e0d0c 100644 --- a/bindings/rust/build.rs +++ b/bindings/rust/build.rs @@ -1,11 +1,11 @@ use bytes::Buf; use flate2::read::GzDecoder; use reqwest::header::USER_AGENT; -use std::path::PathBuf; +use std::path::{Path, PathBuf}; use std::{env, process::Command}; use tar::Archive; -fn find_unicorn(unicorn_dir: &PathBuf) -> Option { +fn find_unicorn(unicorn_dir: &Path) -> Option { for entry in std::fs::read_dir(unicorn_dir).ok()? { let entry = entry.unwrap(); let path = entry.path(); @@ -49,6 +49,7 @@ fn download_unicorn() -> Option { } } +#[allow(clippy::branches_sharing_code)] fn main() { let profile = env::var("PROFILE").unwrap(); diff --git a/bindings/rust/src/arm.rs b/bindings/rust/src/arm.rs index 6d72cefea0..ac56b7f85f 100644 --- a/bindings/rust/src/arm.rs +++ b/bindings/rust/src/arm.rs @@ -164,3 +164,9 @@ impl RegisterARM { pub const FP: RegisterARM = RegisterARM::R11; pub const IP: RegisterARM = RegisterARM::R12; } + +impl From for i32 { + fn from(r: RegisterARM) -> Self { + r as i32 + } +} diff --git a/bindings/rust/src/arm64.rs b/bindings/rust/src/arm64.rs index 299b1bbf14..eb0a264734 100644 --- a/bindings/rust/src/arm64.rs +++ b/bindings/rust/src/arm64.rs @@ -1,9 +1,7 @@ -#![allow(non_camel_case_types)] -// For Unicorn Engine. AUTO-GENERATED FILE, DO NOT EDIT - // ARM64 registers #[repr(C)] #[derive(PartialEq, Debug, Clone, Copy)] +#[allow(non_camel_case_types)] pub enum RegisterARM64 { INVALID = 0, X29 = 1, @@ -319,3 +317,9 @@ impl RegisterARM64 { pub const FP: RegisterARM64 = RegisterARM64::X29; pub const LR: RegisterARM64 = RegisterARM64::X30; } + +impl From for i32 { + fn from(r: RegisterARM64) -> Self { + r as i32 + } +} diff --git a/bindings/rust/src/ffi.rs b/bindings/rust/src/ffi.rs index 3252a3bda8..a309c51972 100644 --- a/bindings/rust/src/ffi.rs +++ b/bindings/rust/src/ffi.rs @@ -1,10 +1,11 @@ #![allow(non_camel_case_types)] #![allow(dead_code)] -use super::unicorn_const::*; +use crate::Unicorn; + +use super::unicorn_const::{uc_error, Arch, HookType, MemRegion, MemType, Mode, Query}; use libc::{c_char, c_int}; -use std::ffi::c_void; -use std::pin::Pin; +use std::{ffi::c_void, marker::PhantomData}; pub type uc_handle = *mut c_void; pub type uc_hook = *mut c_void; @@ -75,156 +76,120 @@ extern "C" { pub fn uc_context_alloc(engine: uc_handle, context: *mut uc_context) -> uc_error; pub fn uc_context_save(engine: uc_handle, context: uc_context) -> uc_error; pub fn uc_context_restore(engine: uc_handle, context: uc_context) -> uc_error; + pub fn uc_set_data_ptr(engine: uc_handle, ptr: *mut c_void) -> uc_error; + pub fn uc_get_data_ptr(engine: uc_handle) -> *mut c_void; } -pub struct CodeHook { - pub unicorn: *mut crate::UnicornInner, - pub callback: Box, +pub struct UcHook<'a, D: 'a, F: 'a> { + pub callback: F, + pub phantom: PhantomData<&'a D>, } -pub struct BlockHook { - pub unicorn: *mut crate::UnicornInner, - pub callback: Box, -} +pub trait IsUcHook<'a> {} -pub struct MemHook { - pub unicorn: *mut crate::UnicornInner, - pub callback: Box, -} - -pub struct InterruptHook { - pub unicorn: *mut crate::UnicornInner, - pub callback: Box, -} - -pub struct InstructionInHook { - pub unicorn: *mut crate::UnicornInner, - pub callback: Box, -} - -pub struct InstructionOutHook { - pub unicorn: *mut crate::UnicornInner, - pub callback: Box, -} +impl<'a, D, F> IsUcHook<'a> for UcHook<'a, D, F> {} -pub struct InstructionSysHook { - pub unicorn: *mut crate::UnicornInner, - pub callback: Box, +fn read_uc_from_uc_handle<'a, D>(uc: uc_handle) -> &'a mut crate::Unicorn<'a, D> +where + D: 'a, +{ + unsafe { + (uc_get_data_ptr(uc) as *mut Unicorn<'a, D>) + .as_mut() + .unwrap() + } } -pub extern "C" fn code_hook_proxy( +pub extern "C" fn code_hook_proxy( uc: uc_handle, address: u64, size: u32, - user_data: *mut CodeHook, -) { - let unicorn = unsafe { &mut *(*user_data).unicorn }; - let callback = &mut unsafe { &mut *(*user_data).callback }; + user_data: *mut UcHook, +) where + F: FnMut(&mut crate::Unicorn, u64, u32), +{ + let unicorn = read_uc_from_uc_handle(uc); + let callback = unsafe { &mut (*user_data).callback }; assert_eq!(uc, unicorn.uc); - callback( - crate::UnicornHandle { - inner: unsafe { Pin::new_unchecked(unicorn) }, - }, - address, - size, - ); + callback(unicorn, address, size); } -pub extern "C" fn block_hook_proxy( +pub extern "C" fn block_hook_proxy( uc: uc_handle, address: u64, size: u32, - user_data: *mut BlockHook, -) { - let unicorn = unsafe { &mut *(*user_data).unicorn }; - let callback = &mut unsafe { &mut *(*user_data).callback }; + user_data: *mut UcHook, +) where + F: FnMut(&mut crate::Unicorn, u64, u32), +{ + let unicorn = read_uc_from_uc_handle(uc); + let callback = unsafe { &mut (*user_data).callback }; assert_eq!(uc, unicorn.uc); - callback( - crate::UnicornHandle { - inner: unsafe { Pin::new_unchecked(unicorn) }, - }, - address, - size, - ); + callback(unicorn, address, size); } -pub extern "C" fn mem_hook_proxy( +pub extern "C" fn mem_hook_proxy( uc: uc_handle, mem_type: MemType, address: u64, size: u32, value: i64, - user_data: *mut MemHook, -) { - let unicorn = unsafe { &mut *(*user_data).unicorn }; - let callback = &mut unsafe { &mut *(*user_data).callback }; + user_data: *mut UcHook, +) -> bool +where + F: FnMut(&mut crate::Unicorn, MemType, u64, usize, i64) -> bool, +{ + let unicorn = read_uc_from_uc_handle(uc); + let callback = unsafe { &mut (*user_data).callback }; assert_eq!(uc, unicorn.uc); - callback( - crate::UnicornHandle { - inner: unsafe { Pin::new_unchecked(unicorn) }, - }, - mem_type, - address, - size as usize, - value, - ); + callback(unicorn, mem_type, address, size as usize, value) } -pub extern "C" fn intr_hook_proxy(uc: uc_handle, value: u32, user_data: *mut InterruptHook) { - let unicorn = unsafe { &mut *(*user_data).unicorn }; - let callback = &mut unsafe { &mut *(*user_data).callback }; +pub extern "C" fn intr_hook_proxy(uc: uc_handle, value: u32, user_data: *mut UcHook) +where + F: FnMut(&mut crate::Unicorn, u32), +{ + let unicorn = read_uc_from_uc_handle(uc); + let callback = unsafe { &mut (*user_data).callback }; assert_eq!(uc, unicorn.uc); - callback( - crate::UnicornHandle { - inner: unsafe { Pin::new_unchecked(unicorn) }, - }, - value, - ); + callback(unicorn, value); } -pub extern "C" fn insn_in_hook_proxy( +pub extern "C" fn insn_in_hook_proxy( uc: uc_handle, port: u32, size: usize, - user_data: *mut InstructionInHook, -) { - let unicorn = unsafe { &mut *(*user_data).unicorn }; - let callback = &mut unsafe { &mut *(*user_data).callback }; + user_data: *mut UcHook, +) where + F: FnMut(&mut crate::Unicorn, u32, usize), +{ + let unicorn = read_uc_from_uc_handle(uc); + let callback = unsafe { &mut (*user_data).callback }; assert_eq!(uc, unicorn.uc); - callback( - crate::UnicornHandle { - inner: unsafe { Pin::new_unchecked(unicorn) }, - }, - port, - size, - ); + callback(unicorn, port, size); } -pub extern "C" fn insn_out_hook_proxy( +pub extern "C" fn insn_out_hook_proxy( uc: uc_handle, port: u32, size: usize, value: u32, - user_data: *mut InstructionOutHook, -) { - let unicorn = unsafe { &mut *(*user_data).unicorn }; - let callback = &mut unsafe { &mut *(*user_data).callback }; + user_data: *mut UcHook, +) where + F: FnMut(&mut crate::Unicorn, u32, usize, u32), +{ + let unicorn = read_uc_from_uc_handle(uc); + let callback = unsafe { &mut (*user_data).callback }; assert_eq!(uc, unicorn.uc); - callback( - crate::UnicornHandle { - inner: unsafe { Pin::new_unchecked(unicorn) }, - }, - port, - size, - value, - ); + callback(unicorn, port, size, value); } -pub extern "C" fn insn_sys_hook_proxy(uc: uc_handle, user_data: *mut InstructionSysHook) { - let unicorn = unsafe { &mut *(*user_data).unicorn }; - let callback = &mut unsafe { &mut *(*user_data).callback }; +pub extern "C" fn insn_sys_hook_proxy(uc: uc_handle, user_data: *mut UcHook) +where + F: FnMut(&mut crate::Unicorn), +{ + let unicorn = read_uc_from_uc_handle(uc); assert_eq!(uc, unicorn.uc); - callback(crate::UnicornHandle { - inner: unsafe { Pin::new_unchecked(unicorn) }, - }); + let callback = unsafe { &mut (*user_data).callback }; + callback(unicorn); } diff --git a/bindings/rust/src/lib.rs b/bindings/rust/src/lib.rs index 99e1531c65..6d007edc58 100644 --- a/bindings/rust/src/lib.rs +++ b/bindings/rust/src/lib.rs @@ -9,7 +9,7 @@ //! use unicorn_engine::RegisterARM; //! use unicorn_engine::unicorn_const::{Arch, Mode, Permission, SECOND_SCALE}; //! -//! fn main() { +//! fn emulate() { //! let arm_code32: Vec = vec![0x17, 0x00, 0x40, 0xe2]; // sub r0, #23 //! //! let mut unicorn = unicorn_engine::Unicorn::new(Arch::ARM, Mode::LITTLE_ENDIAN).expect("failed to initialize Unicorn instance"); @@ -35,17 +35,15 @@ mod arm64; mod m68k; mod mips; mod ppc; -mod riscv; mod sparc; mod x86; -pub use crate::{arm::*, arm64::*, m68k::*, mips::*, ppc::*, riscv::*, sparc::*, x86::*}; +use std::{marker::PhantomData, ptr}; + +pub use crate::{arm::*, arm64::*, m68k::*, mips::*, ppc::*, sparc::*, x86::*}; use ffi::uc_handle; -use std::collections::HashMap; -use std::ffi::c_void; -use std::marker::PhantomPinned; -use std::pin::Pin; -use unicorn_const::*; +use libc::c_void; +use unicorn_const::{uc_error, Arch, HookType, MemRegion, MemType, Mode, Permission, Query}; #[derive(Debug)] pub struct Context { @@ -53,106 +51,127 @@ pub struct Context { } impl Context { + #[must_use] pub fn new() -> Self { Context { context: 0 } } + #[must_use] pub fn is_initialized(&self) -> bool { self.context != 0 } } +impl Default for Context { + fn default() -> Self { + Self::new() + } +} + impl Drop for Context { fn drop(&mut self) { - unsafe { ffi::uc_context_free(self.context) }; + if self.is_initialized() { + unsafe { + ffi::uc_context_free(self.context); + } + } + self.context = 0; } } -#[derive(Debug)] /// A Unicorn emulator instance. -pub struct Unicorn { - inner: Pin>, +pub struct Unicorn<'a, D: 'a> { + pub uc: uc_handle, + pub arch: Arch, + /// to keep ownership over the hook for this uc instance's lifetime + pub hooks: Vec<(ffi::uc_hook, Box + 'a>)>, + pub data: D, } -#[derive(Debug)] -/// Handle used to safely access exposed functions and data of a Unicorn instance. -pub struct UnicornHandle<'a> { - inner: Pin<&'a mut UnicornInner>, +impl Unicorn<()> { + /// Create a new instance of the unicorn engine for the specified architecture + /// and hardware mode. + pub fn new(arch: Arch, mode: Mode) -> Result, uc_error> { + Self::new_with_data(arch, mode, ()) + } } -/// Internal Management struct -pub struct UnicornInner { - pub uc: uc_handle, - pub arch: Arch, - pub code_hooks: HashMap<*mut libc::c_void, Box>, - pub block_hooks: HashMap<*mut libc::c_void, Box>, - pub mem_hooks: HashMap<*mut libc::c_void, Box>, - pub intr_hooks: HashMap<*mut libc::c_void, Box>, - pub insn_in_hooks: HashMap<*mut libc::c_void, Box>, - pub insn_out_hooks: HashMap<*mut libc::c_void, Box>, - pub insn_sys_hooks: HashMap<*mut libc::c_void, Box>, - _pin: PhantomPinned, -} +impl<'a, D> Unicorn<'a, D> +where + D: 'a, +{ + /// Create a new instance of the unicorn engine for the specified architecture + /// and hardware mode. + pub fn new(arch: Arch, mode: Mode) -> Result, uc_error> { + Self::new_with_data(arch, mode, ()) + } -impl Unicorn { /// Create a new instance of the unicorn engine for the specified architecture /// and hardware mode. - pub fn new(arch: Arch, mode: Mode) -> Result { + pub fn new_with_data(arch: Arch, mode: Mode, data: D) -> Result, uc_error> { let mut handle = std::ptr::null_mut(); let err = unsafe { ffi::uc_open(arch, mode, &mut handle) }; if err == uc_error::OK { Ok(Unicorn { - inner: Box::pin(UnicornInner { - uc: handle, - arch: arch, - code_hooks: HashMap::new(), - block_hooks: HashMap::new(), - mem_hooks: HashMap::new(), - intr_hooks: HashMap::new(), - insn_in_hooks: HashMap::new(), - insn_out_hooks: HashMap::new(), - insn_sys_hooks: HashMap::new(), - _pin: std::marker::PhantomPinned, - }), + uc: handle, + arch, + data, + hooks: vec![], }) } else { Err(err) } } - - pub fn borrow<'a>(&'a mut self) -> UnicornHandle<'a> { - UnicornHandle { - inner: self.inner.as_mut(), - } - } } -impl Drop for Unicorn { +/// Drop UC +/// TODO: !!! Right now, this leaks the unicorn instance on purpose. +/// UC 1 for some platforms, for example aarch64, seems to crash on cleanup. +/// After updating to Unicorn 2, we should call `uc_close` again! +impl<'a, D> Drop for Unicorn<'a, D> { fn drop(&mut self) { - unsafe { ffi::uc_close(self.inner.uc) }; + if !self.uc.is_null() { + // TODO: !!! + // This is a deliberate leak, get rid of it after updating to UC2! + // unsafe { ffi::uc_close(self.uc) }; + } + self.uc = ptr::null_mut(); } } -impl std::fmt::Debug for UnicornInner { +impl<'a, D> std::fmt::Debug for Unicorn<'a, D> { fn fmt(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result { write!(formatter, "Unicorn {{ uc: {:p} }}", self.uc) } } -impl<'a> UnicornHandle<'a> { +impl<'a, D> Unicorn<'a, D> { + /// Return whatever data was passed during initialization. + /// + /// For an example, have a look at `utils::init_emu_with_heap` where + /// a struct is passed which is used for a custom allocator. + pub fn get_data(&self) -> &D { + &self.data + } + + /// Return a mutable reference to whatever data was passed during initialization. + pub fn get_data_mut(&mut self) -> &mut D { + &mut self.data + } + /// Return the architecture of the current emulator. pub fn get_arch(&self) -> Arch { - self.inner.arch + self.arch } /// Returns a vector with the memory regions that are mapped in the emulator. pub fn mem_regions(&self) -> Result, uc_error> { let mut nb_regions: u32 = 0; - let mut p_regions: *const MemRegion = std::ptr::null_mut(); - let err = unsafe { ffi::uc_mem_regions(self.inner.uc, &mut p_regions, &mut nb_regions) }; + let p_regions: *const MemRegion = std::ptr::null_mut(); + let err = unsafe { ffi::uc_mem_regions(self.uc, &p_regions, &mut nb_regions) }; if err == uc_error::OK { let mut regions = Vec::new(); for i in 0..nb_regions { - regions.push(unsafe { std::mem::transmute_copy(&*p_regions.offset(i as isize)) }); + regions.push(unsafe { std::mem::transmute_copy(&*p_regions.add(i as usize)) }); } unsafe { libc::free(p_regions as _) }; Ok(regions) @@ -163,7 +182,7 @@ impl<'a> UnicornHandle<'a> { /// Read a range of bytes from memory at the specified address. pub fn mem_read(&self, address: u64, buf: &mut [u8]) -> Result<(), uc_error> { - let err = unsafe { ffi::uc_mem_read(self.inner.uc, address, buf.as_mut_ptr(), buf.len()) }; + let err = unsafe { ffi::uc_mem_read(self.uc, address, buf.as_mut_ptr(), buf.len()) }; if err == uc_error::OK { Ok(()) } else { @@ -174,7 +193,7 @@ impl<'a> UnicornHandle<'a> { /// Return a range of bytes from memory at the specified address as vector. pub fn mem_read_as_vec(&self, address: u64, size: usize) -> Result, uc_error> { let mut buf = vec![0; size]; - let err = unsafe { ffi::uc_mem_read(self.inner.uc, address, buf.as_mut_ptr(), size) }; + let err = unsafe { ffi::uc_mem_read(self.uc, address, buf.as_mut_ptr(), size) }; if err == uc_error::OK { Ok(buf) } else { @@ -183,7 +202,7 @@ impl<'a> UnicornHandle<'a> { } pub fn mem_write(&mut self, address: u64, bytes: &[u8]) -> Result<(), uc_error> { - let err = unsafe { ffi::uc_mem_write(self.inner.uc, address, bytes.as_ptr(), bytes.len()) }; + let err = unsafe { ffi::uc_mem_write(self.uc, address, bytes.as_ptr(), bytes.len()) }; if err == uc_error::OK { Ok(()) } else { @@ -193,6 +212,8 @@ impl<'a> UnicornHandle<'a> { /// Map an existing memory region in the emulator at the specified address. /// + /// # Safety + /// /// This function is marked unsafe because it is the responsibility of the caller to /// ensure that `size` matches the size of the passed buffer, an invalid `size` value will /// likely cause a crash in unicorn. @@ -202,14 +223,14 @@ impl<'a> UnicornHandle<'a> { /// `size` must be a multiple of 4kb or this will return `Error::ARG`. /// /// `ptr` is a pointer to the provided memory region that will be used by the emulator. - pub fn mem_map_ptr( + pub unsafe fn mem_map_ptr( &mut self, address: u64, size: usize, perms: Permission, ptr: *mut c_void, ) -> Result<(), uc_error> { - let err = unsafe { ffi::uc_mem_map_ptr(self.inner.uc, address, size, perms.bits(), ptr) }; + let err = ffi::uc_mem_map_ptr(self.uc, address, size, perms.bits(), ptr); if err == uc_error::OK { Ok(()) } else { @@ -227,7 +248,7 @@ impl<'a> UnicornHandle<'a> { size: libc::size_t, perms: Permission, ) -> Result<(), uc_error> { - let err = unsafe { ffi::uc_mem_map(self.inner.uc, address, size, perms.bits()) }; + let err = unsafe { ffi::uc_mem_map(self.uc, address, size, perms.bits()) }; if err == uc_error::OK { Ok(()) } else { @@ -240,7 +261,7 @@ impl<'a> UnicornHandle<'a> { /// `address` must be aligned to 4kb or this will return `Error::ARG`. /// `size` must be a multiple of 4kb or this will return `Error::ARG`. pub fn mem_unmap(&mut self, address: u64, size: libc::size_t) -> Result<(), uc_error> { - let err = unsafe { ffi::uc_mem_unmap(self.inner.uc, address, size) }; + let err = unsafe { ffi::uc_mem_unmap(self.uc, address, size) }; if err == uc_error::OK { Ok(()) } else { @@ -258,7 +279,7 @@ impl<'a> UnicornHandle<'a> { size: libc::size_t, perms: Permission, ) -> Result<(), uc_error> { - let err = unsafe { ffi::uc_mem_protect(self.inner.uc, address, size, perms.bits()) }; + let err = unsafe { ffi::uc_mem_protect(self.uc, address, size, perms.bits()) }; if err == uc_error::OK { Ok(()) } else { @@ -268,8 +289,7 @@ impl<'a> UnicornHandle<'a> { /// Write an unsigned value from a register. pub fn reg_write>(&mut self, regid: T, value: u64) -> Result<(), uc_error> { - let err = - unsafe { ffi::uc_reg_write(self.inner.uc, regid.into(), &value as *const _ as _) }; + let err = unsafe { ffi::uc_reg_write(self.uc, regid.into(), &value as *const _ as _) }; if err == uc_error::OK { Ok(()) } else { @@ -281,8 +301,8 @@ impl<'a> UnicornHandle<'a> { /// /// The user has to make sure that the buffer length matches the register size. /// This adds support for registers >64 bit (GDTR/IDTR, XMM, YMM, ZMM (x86); Q, V (arm64)). - pub fn reg_write_long>(&self, regid: T, value: Box<[u8]>) -> Result<(), uc_error> { - let err = unsafe { ffi::uc_reg_write(self.inner.uc, regid.into(), value.as_ptr() as _) }; + pub fn reg_write_long>(&self, regid: T, value: &[u8]) -> Result<(), uc_error> { + let err = unsafe { ffi::uc_reg_write(self.uc, regid.into(), value.as_ptr() as _) }; if err == uc_error::OK { Ok(()) } else { @@ -295,8 +315,7 @@ impl<'a> UnicornHandle<'a> { /// Not to be used with registers larger than 64 bit. pub fn reg_read>(&self, regid: T) -> Result { let mut value: u64 = 0; - let err = - unsafe { ffi::uc_reg_read(self.inner.uc, regid.into(), &mut value as *mut u64 as _) }; + let err = unsafe { ffi::uc_reg_read(self.uc, regid.into(), &mut value as *mut u64 as _) }; if err == uc_error::OK { Ok(value) } else { @@ -306,7 +325,7 @@ impl<'a> UnicornHandle<'a> { /// Read 128, 256 or 512 bit register value into heap allocated byte array. /// - /// This adds safe support for registers >64 bit (GDTR/IDTR, XMM, YMM, ZMM, ST (x86); Q, V (arm64)). + /// This adds safe support for registers >64 bit (GDTR/IDTR, XMM, YMM, ZMM (x86); Q, V (arm64)). pub fn reg_read_long>(&self, regid: T) -> Result, uc_error> { let err: uc_error; let boxed: Box<[u8]>; @@ -329,8 +348,6 @@ impl<'a> UnicornHandle<'a> { value = vec![0; 64]; } else if curr_reg_id == x86::RegisterX86::GDTR as i32 || curr_reg_id == x86::RegisterX86::IDTR as i32 - || (curr_reg_id >= x86::RegisterX86::ST0 as i32 - && curr_reg_id <= x86::RegisterX86::ST7 as i32) { value = vec![0; 10]; // 64 bit base address in IA-32e mode } else { @@ -350,7 +367,7 @@ impl<'a> UnicornHandle<'a> { return Err(uc_error::ARCH); } - err = unsafe { ffi::uc_reg_read(self.inner.uc, curr_reg_id, value.as_mut_ptr() as _) }; + err = unsafe { ffi::uc_reg_read(self.uc, curr_reg_id, value.as_mut_ptr() as _) }; if err == uc_error::OK { boxed = value.into_boxed_slice(); @@ -363,8 +380,7 @@ impl<'a> UnicornHandle<'a> { /// Read a signed 32-bit value from a register. pub fn reg_read_i32>(&self, regid: T) -> Result { let mut value: i32 = 0; - let err = - unsafe { ffi::uc_reg_read(self.inner.uc, regid.into(), &mut value as *mut i32 as _) }; + let err = unsafe { ffi::uc_reg_read(self.uc, regid.into(), &mut value as *mut i32 as _) }; if err == uc_error::OK { Ok(value) } else { @@ -373,36 +389,34 @@ impl<'a> UnicornHandle<'a> { } /// Add a code hook. - pub fn add_code_hook( + pub fn add_code_hook( &mut self, begin: u64, end: u64, callback: F, ) -> Result where - F: FnMut(UnicornHandle, u64, u32), + F: FnMut(&mut crate::Unicorn, u64, u32) + 'a, { let mut hook_ptr = std::ptr::null_mut(); - let mut user_data = Box::new(ffi::CodeHook { - unicorn: unsafe { self.inner.as_mut().get_unchecked_mut() } as _, - callback: Box::new(callback), + let mut user_data = Box::new(ffi::UcHook { + callback, + phantom: PhantomData::<&D>, }); let err = unsafe { ffi::uc_hook_add( - self.inner.uc, + self.uc, &mut hook_ptr, HookType::CODE, - ffi::code_hook_proxy as _, + ffi::code_hook_proxy:: as _, user_data.as_mut() as *mut _ as _, begin, end, ) }; if err == uc_error::OK { - unsafe { self.inner.as_mut().get_unchecked_mut() } - .code_hooks - .insert(hook_ptr, user_data); + self.hooks.push((hook_ptr, user_data)); Ok(hook_ptr) } else { Err(err) @@ -410,31 +424,30 @@ impl<'a> UnicornHandle<'a> { } /// Add a block hook. - pub fn add_block_hook(&mut self, callback: F) -> Result + pub fn add_block_hook(&mut self, callback: F) -> Result where - F: FnMut(UnicornHandle, u64, u32), + F: FnMut(&mut Unicorn, u64, u32), { let mut hook_ptr = std::ptr::null_mut(); - let mut user_data = Box::new(ffi::BlockHook { - unicorn: unsafe { self.inner.as_mut().get_unchecked_mut() } as _, - callback: Box::new(callback), + let mut user_data = Box::new(ffi::UcHook { + callback, + phantom: PhantomData::<&D>, }); let err = unsafe { ffi::uc_hook_add( - self.inner.uc, + self.uc, &mut hook_ptr, HookType::BLOCK, - ffi::block_hook_proxy as _, + ffi::block_hook_proxy:: as _, user_data.as_mut() as *mut _ as _, 1, 0, ) }; if err == uc_error::OK { - unsafe { self.inner.as_mut().get_unchecked_mut() } - .block_hooks - .insert(hook_ptr, user_data); + self.hooks.push((hook_ptr, user_data)); + Ok(hook_ptr) } else { Err(err) @@ -442,7 +455,7 @@ impl<'a> UnicornHandle<'a> { } /// Add a memory hook. - pub fn add_mem_hook( + pub fn add_mem_hook( &mut self, hook_type: HookType, begin: u64, @@ -450,33 +463,32 @@ impl<'a> UnicornHandle<'a> { callback: F, ) -> Result where - F: FnMut(UnicornHandle, MemType, u64, usize, i64), + F: FnMut(&mut Unicorn, MemType, u64, usize, i64) -> bool, { if !(HookType::MEM_ALL | HookType::MEM_READ_AFTER).contains(hook_type) { return Err(uc_error::ARG); } let mut hook_ptr = std::ptr::null_mut(); - let mut user_data = Box::new(ffi::MemHook { - unicorn: unsafe { self.inner.as_mut().get_unchecked_mut() } as _, - callback: Box::new(callback), + let mut user_data = Box::new(ffi::UcHook { + callback, + phantom: PhantomData::<&D>, }); let err = unsafe { ffi::uc_hook_add( - self.inner.uc, + self.uc, &mut hook_ptr, hook_type, - ffi::mem_hook_proxy as _, + ffi::mem_hook_proxy:: as _, user_data.as_mut() as *mut _ as _, begin, end, ) }; if err == uc_error::OK { - unsafe { self.inner.as_mut().get_unchecked_mut() } - .mem_hooks - .insert(hook_ptr, user_data); + self.hooks.push((hook_ptr, user_data)); + Ok(hook_ptr) } else { Err(err) @@ -484,31 +496,30 @@ impl<'a> UnicornHandle<'a> { } /// Add an interrupt hook. - pub fn add_intr_hook(&mut self, callback: F) -> Result + pub fn add_intr_hook(&mut self, callback: F) -> Result where - F: FnMut(UnicornHandle, u32), + F: FnMut(&mut Unicorn, u32), { let mut hook_ptr = std::ptr::null_mut(); - let mut user_data = Box::new(ffi::InterruptHook { - unicorn: unsafe { self.inner.as_mut().get_unchecked_mut() } as _, - callback: Box::new(callback), + let mut user_data = Box::new(ffi::UcHook { + callback, + phantom: PhantomData::<&D>, }); let err = unsafe { ffi::uc_hook_add( - self.inner.uc, + self.uc, &mut hook_ptr, HookType::INTR, - ffi::intr_hook_proxy as _, + ffi::intr_hook_proxy:: as _, user_data.as_mut() as *mut _ as _, 0, 0, ) }; if err == uc_error::OK { - unsafe { self.inner.as_mut().get_unchecked_mut() } - .intr_hooks - .insert(hook_ptr, user_data); + self.hooks.push((hook_ptr, user_data)); + Ok(hook_ptr) } else { Err(err) @@ -516,22 +527,22 @@ impl<'a> UnicornHandle<'a> { } /// Add hook for x86 IN instruction. - pub fn add_insn_in_hook(&mut self, callback: F) -> Result + pub fn add_insn_in_hook(&mut self, callback: F) -> Result where - F: FnMut(UnicornHandle, u32, usize), + F: FnMut(&mut Unicorn, u32, usize) + 'a, { let mut hook_ptr = std::ptr::null_mut(); - let mut user_data = Box::new(ffi::InstructionInHook { - unicorn: unsafe { self.inner.as_mut().get_unchecked_mut() } as _, - callback: Box::new(callback), + let mut user_data = Box::new(ffi::UcHook { + callback, + phantom: PhantomData::<&D>, }); let err = unsafe { ffi::uc_hook_add( - self.inner.uc, + self.uc, &mut hook_ptr, HookType::INSN, - ffi::insn_in_hook_proxy as _, + ffi::insn_in_hook_proxy:: as _, user_data.as_mut() as *mut _ as _, 0, 0, @@ -539,9 +550,8 @@ impl<'a> UnicornHandle<'a> { ) }; if err == uc_error::OK { - unsafe { self.inner.as_mut().get_unchecked_mut() } - .insn_in_hooks - .insert(hook_ptr, user_data); + self.hooks.push((hook_ptr, user_data)); + Ok(hook_ptr) } else { Err(err) @@ -549,22 +559,22 @@ impl<'a> UnicornHandle<'a> { } /// Add hook for x86 OUT instruction. - pub fn add_insn_out_hook(&mut self, callback: F) -> Result + pub fn add_insn_out_hook(&mut self, callback: F) -> Result where - F: FnMut(UnicornHandle, u32, usize, u32), + F: FnMut(&mut Unicorn, u32, usize, u32) + 'a, { let mut hook_ptr = std::ptr::null_mut(); - let mut user_data = Box::new(ffi::InstructionOutHook { - unicorn: unsafe { self.inner.as_mut().get_unchecked_mut() } as _, - callback: Box::new(callback), + let mut user_data = Box::new(ffi::UcHook { + callback, + phantom: PhantomData::<&D>, }); let err = unsafe { ffi::uc_hook_add( - self.inner.uc, + self.uc, &mut hook_ptr, HookType::INSN, - ffi::insn_out_hook_proxy as _, + ffi::insn_out_hook_proxy:: as _, user_data.as_mut() as *mut _ as _, 0, 0, @@ -572,9 +582,8 @@ impl<'a> UnicornHandle<'a> { ) }; if err == uc_error::OK { - unsafe { self.inner.as_mut().get_unchecked_mut() } - .insn_out_hooks - .insert(hook_ptr, user_data); + self.hooks.push((hook_ptr, user_data)); + Ok(hook_ptr) } else { Err(err) @@ -582,7 +591,7 @@ impl<'a> UnicornHandle<'a> { } /// Add hook for x86 SYSCALL or SYSENTER. - pub fn add_insn_sys_hook( + pub fn add_insn_sys_hook( &mut self, insn_type: x86::InsnSysX86, begin: u64, @@ -590,20 +599,20 @@ impl<'a> UnicornHandle<'a> { callback: F, ) -> Result where - F: FnMut(UnicornHandle), + F: FnMut(&mut Unicorn) + 'a, { let mut hook_ptr = std::ptr::null_mut(); - let mut user_data = Box::new(ffi::InstructionSysHook { - unicorn: unsafe { self.inner.as_mut().get_unchecked_mut() } as _, - callback: Box::new(callback), + let mut user_data = Box::new(ffi::UcHook { + callback, + phantom: PhantomData::<&D>, }); let err = unsafe { ffi::uc_hook_add( - self.inner.uc, + self.uc, &mut hook_ptr, HookType::INSN, - ffi::insn_sys_hook_proxy as _, + ffi::insn_sys_hook_proxy:: as _, user_data.as_mut() as *mut _ as _, begin, end, @@ -611,9 +620,8 @@ impl<'a> UnicornHandle<'a> { ) }; if err == uc_error::OK { - unsafe { self.inner.as_mut().get_unchecked_mut() } - .insn_sys_hooks - .insert(hook_ptr, user_data); + self.hooks.push((hook_ptr, user_data)); + Ok(hook_ptr) } else { Err(err) @@ -624,50 +632,13 @@ impl<'a> UnicornHandle<'a> { /// /// `hook` is the value returned by `add_*_hook` functions. pub fn remove_hook(&mut self, hook: ffi::uc_hook) -> Result<(), uc_error> { - let handle = unsafe { self.inner.as_mut().get_unchecked_mut() }; let err: uc_error; - let mut in_one_hashmap = false; - - if handle.code_hooks.contains_key(&hook) { - in_one_hashmap = true; - handle.code_hooks.remove(&hook); - } - - if handle.mem_hooks.contains_key(&hook) { - in_one_hashmap = true; - handle.mem_hooks.remove(&hook); - } - - if handle.block_hooks.contains_key(&hook) { - in_one_hashmap = true; - handle.block_hooks.remove(&hook); - } - - if handle.intr_hooks.contains_key(&hook) { - in_one_hashmap = true; - handle.intr_hooks.remove(&hook); - } - - if handle.insn_in_hooks.contains_key(&hook) { - in_one_hashmap = true; - handle.insn_in_hooks.remove(&hook); - } - if handle.insn_out_hooks.contains_key(&hook) { - in_one_hashmap = true; - handle.insn_out_hooks.remove(&hook); - } - - if handle.insn_sys_hooks.contains_key(&hook) { - in_one_hashmap = true; - handle.insn_sys_hooks.remove(&hook); - } + // drop the hook + self.hooks + .retain(|(hook_ptr, _hook_impl)| hook_ptr != &hook); - if in_one_hashmap { - err = unsafe { ffi::uc_hook_del(handle.uc, hook) }; - } else { - err = uc_error::HOOK; - } + err = unsafe { ffi::uc_hook_del(self.uc, hook) }; if err == uc_error::OK { Ok(()) @@ -678,10 +649,10 @@ impl<'a> UnicornHandle<'a> { /// Allocate and return an empty Unicorn context. /// - /// To be populated via context_save. + /// To be populated via `context_save`. pub fn context_alloc(&self) -> Result { let mut empty_context: ffi::uc_context = Default::default(); - let err = unsafe { ffi::uc_context_alloc(self.inner.uc, &mut empty_context) }; + let err = unsafe { ffi::uc_context_alloc(self.uc, &mut empty_context) }; if err == uc_error::OK { Ok(Context { context: empty_context, @@ -693,7 +664,7 @@ impl<'a> UnicornHandle<'a> { /// Save current Unicorn context to previously allocated Context struct. pub fn context_save(&self, context: &mut Context) -> Result<(), uc_error> { - let err = unsafe { ffi::uc_context_save(self.inner.uc, context.context) }; + let err = unsafe { ffi::uc_context_save(self.uc, context.context) }; if err == uc_error::OK { Ok(()) } else { @@ -703,16 +674,16 @@ impl<'a> UnicornHandle<'a> { /// Allocate and return a Context struct initialized with the current CPU context. /// - /// This can be used for fast rollbacks with context_restore. - /// In case of many non-concurrent context saves, use context_alloc and *_save + /// This can be used for fast rollbacks with `context_restore`. + /// In case of many non-concurrent context saves, use `context_alloc` and *_save /// individually to avoid unnecessary allocations. pub fn context_init(&self) -> Result { let mut new_context: ffi::uc_context = Default::default(); - let err = unsafe { ffi::uc_context_alloc(self.inner.uc, &mut new_context) }; + let err = unsafe { ffi::uc_context_alloc(self.uc, &mut new_context) }; if err != uc_error::OK { return Err(err); } - let err = unsafe { ffi::uc_context_save(self.inner.uc, new_context) }; + let err = unsafe { ffi::uc_context_save(self.uc, new_context) }; if err == uc_error::OK { Ok(Context { context: new_context, @@ -729,7 +700,7 @@ impl<'a> UnicornHandle<'a> { /// internal metadata. Contexts may not be shared across engine instances with /// differing arches or modes. Memory has to be restored manually, if needed. pub fn context_restore(&self, context: &Context) -> Result<(), uc_error> { - let err = unsafe { ffi::uc_context_restore(self.inner.uc, context.context) }; + let err = unsafe { ffi::uc_context_restore(self.uc, context.context) }; if err == uc_error::OK { Ok(()) } else { @@ -750,11 +721,15 @@ impl<'a> UnicornHandle<'a> { timeout: u64, count: usize, ) -> Result<(), uc_error> { - let err = unsafe { ffi::uc_emu_start(self.inner.uc, begin, until, timeout, count as _) }; - if err == uc_error::OK { - Ok(()) - } else { - Err(err) + unsafe { + ffi::uc_set_data_ptr(self.uc, self as *mut _ as *mut _); + let err = ffi::uc_emu_start(self.uc, begin, until, timeout, count as _); + ffi::uc_set_data_ptr(self.uc, ptr::null_mut()); + if err == uc_error::OK { + Ok(()) + } else { + Err(err) + } } } @@ -763,7 +738,7 @@ impl<'a> UnicornHandle<'a> { /// This is usually called from callback function in hooks. /// NOTE: For now, this will stop the execution only after the current block. pub fn emu_stop(&mut self) -> Result<(), uc_error> { - let err = unsafe { ffi::uc_emu_stop(self.inner.uc) }; + let err = unsafe { ffi::uc_emu_stop(self.uc) }; if err == uc_error::OK { Ok(()) } else { @@ -773,14 +748,46 @@ impl<'a> UnicornHandle<'a> { /// Query the internal status of the engine. /// - /// supported: MODE, PAGE_SIZE, ARCH + /// supported: `MODE`, `PAGE_SIZE`, `ARCH` pub fn query(&self, query: Query) -> Result { let mut result: libc::size_t = Default::default(); - let err = unsafe { ffi::uc_query(self.inner.uc, query, &mut result) }; + let err = unsafe { ffi::uc_query(self.uc, query, &mut result) }; if err == uc_error::OK { Ok(result) } else { Err(err) } } + + /// Gets the current program counter for this `unicorn` instance. + #[inline] + pub fn pc_read(&self) -> Result { + let arch = self.get_arch(); + let reg = match arch { + Arch::X86 => RegisterX86::RIP as i32, + Arch::ARM => RegisterARM::PC as i32, + Arch::ARM64 => RegisterARM64::PC as i32, + Arch::MIPS => RegisterMIPS::PC as i32, + Arch::SPARC => RegisterSPARC::PC as i32, + Arch::M68K => RegisterM68K::PC as i32, + _ => panic!("Arch pc not yet know to the unicorn rust bindings"), + }; + self.reg_read(reg) + } + + /// Sets the program counter for this `unicorn` instance. + #[inline] + pub fn set_pc(&mut self, value: u64) -> Result<(), uc_error> { + let arch = self.get_arch(); + let reg = match arch { + Arch::X86 => RegisterX86::RIP as i32, + Arch::ARM => RegisterARM::PC as i32, + Arch::ARM64 => RegisterARM64::PC as i32, + Arch::MIPS => RegisterMIPS::PC as i32, + Arch::SPARC => RegisterSPARC::PC as i32, + Arch::M68K => RegisterM68K::PC as i32, + _ => panic!("Arch not yet known to the unicorn rust bindings"), + }; + self.reg_write(reg, value) + } } diff --git a/bindings/rust/src/m68k.rs b/bindings/rust/src/m68k.rs index 54c6292052..097cd4ec83 100644 --- a/bindings/rust/src/m68k.rs +++ b/bindings/rust/src/m68k.rs @@ -21,5 +21,10 @@ pub enum RegisterM68K { D7, SR, PC, - ENDING, +} + +impl From for i32 { + fn from(r: RegisterM68K) -> Self { + r as i32 + } } diff --git a/bindings/rust/src/mips.rs b/bindings/rust/src/mips.rs index 5f462af03e..77002f8253 100644 --- a/bindings/rust/src/mips.rs +++ b/bindings/rust/src/mips.rs @@ -245,3 +245,9 @@ impl RegisterMIPS { pub const LO2: RegisterMIPS = RegisterMIPS::AC2; pub const LO3: RegisterMIPS = RegisterMIPS::AC3; } + +impl From for i32 { + fn from(r: RegisterMIPS) -> Self { + r as i32 + } +} diff --git a/bindings/rust/src/ppc.rs b/bindings/rust/src/ppc.rs index a28827d3b2..ba63356547 100644 --- a/bindings/rust/src/ppc.rs +++ b/bindings/rust/src/ppc.rs @@ -40,3 +40,9 @@ pub enum RegisterPPC { GPR30 = 32, GPR31 = 33, } + +impl From for i32 { + fn from(r: RegisterPPC) -> Self { + r as i32 + } +} diff --git a/bindings/rust/src/riscv.rs b/bindings/rust/src/riscv.rs index ca91c3505e..1d7e0db1f1 100644 --- a/bindings/rust/src/riscv.rs +++ b/bindings/rust/src/riscv.rs @@ -211,3 +211,9 @@ impl RegisterRISCV { pub const FT10: RegisterRISCV = RegisterRISCV::F30; pub const FT11: RegisterRISCV = RegisterRISCV::F31; } + +impl From for i32 { + fn from(r: RegisterRISCV) -> Self { + r as i32 + } +} diff --git a/bindings/rust/src/sparc.rs b/bindings/rust/src/sparc.rs index 6c6892e62f..f7295750ae 100644 --- a/bindings/rust/src/sparc.rs +++ b/bindings/rust/src/sparc.rs @@ -1,8 +1,7 @@ -// For Unicorn Engine. AUTO-GENERATED FILE, DO NOT EDIT - // SPARC registers #[repr(C)] #[derive(PartialEq, Debug, Clone, Copy)] +#[allow(clippy::upper_case_acronyms)] pub enum RegisterSPARC { INVALID = 0, F0 = 1, @@ -93,13 +92,10 @@ pub enum RegisterSPARC { Y = 86, XCC = 87, PC = 88, - ENDING = 89, } -impl RegisterSPARC { - // alias registers - // (assoc) O6 = 84, - // (assoc) I6 = 67, - pub const O6: RegisterSPARC = RegisterSPARC::SP; - pub const I6: RegisterSPARC = RegisterSPARC::FP; +impl From for i32 { + fn from(r: RegisterSPARC) -> Self { + r as i32 + } } diff --git a/bindings/rust/src/unicorn_const.rs b/bindings/rust/src/unicorn_const.rs index 9cb14da739..01975bdf32 100644 --- a/bindings/rust/src/unicorn_const.rs +++ b/bindings/rust/src/unicorn_const.rs @@ -11,6 +11,7 @@ pub const MILISECOND_SCALE: u64 = 1_000; #[repr(C)] #[derive(PartialEq, Debug, Clone, Copy)] +#[allow(clippy::upper_case_acronyms)] pub enum uc_error { OK = 0, NOMEM = 1, @@ -89,6 +90,7 @@ bitflags! { #[repr(C)] #[derive(PartialEq, Debug, Clone, Copy)] +#[allow(clippy::upper_case_acronyms)] pub enum Query { MODE = 1, PAGE_SIZE = 2, diff --git a/bindings/rust/src/x86.rs b/bindings/rust/src/x86.rs index 7ed652308c..944ec250e1 100644 --- a/bindings/rust/src/x86.rs +++ b/bindings/rust/src/x86.rs @@ -1,9 +1,7 @@ -#![allow(non_camel_case_types)] -// For Unicorn Engine. AUTO-GENERATED FILE, DO NOT EDIT - // X86 registers #[repr(C)] #[derive(PartialEq, Debug, Clone, Copy)] +#[allow(clippy::upper_case_acronyms, non_camel_case_types)] pub enum RegisterX86 { INVALID = 0, AH = 1, @@ -242,8 +240,15 @@ pub enum RegisterX86 { ENDING = 234, } +impl From for i32 { + fn from(r: RegisterX86) -> Self { + r as i32 + } +} + #[repr(C)] #[derive(PartialEq, Debug, Clone, Copy)] +#[allow(clippy::upper_case_acronyms)] pub enum InsnX86 { IN = 218, OUT = 500, @@ -254,6 +259,7 @@ pub enum InsnX86 { #[repr(C)] #[derive(PartialEq, Debug, Clone, Copy)] +#[allow(clippy::upper_case_acronyms)] pub enum InsnSysX86 { SYSCALL = InsnX86::SYSCALL as isize, SYSENTER = InsnX86::SYSENTER as isize, diff --git a/bindings/rust/tests/unicorn.rs b/bindings/rust/tests/unicorn.rs index aaf208b36d..80dc68c3db 100644 --- a/bindings/rust/tests/unicorn.rs +++ b/bindings/rust/tests/unicorn.rs @@ -32,6 +32,7 @@ pub static X86_REGISTERS: [RegisterX86; 125] = [ RegisterX86::EDX, RegisterX86::EFLAGS, RegisterX86::EIP, + RegisterX86::EIZ, RegisterX86::ES, RegisterX86::ESI, RegisterX86::ESP, @@ -46,6 +47,7 @@ pub static X86_REGISTERS: [RegisterX86; 125] = [ RegisterX86::RDI, RegisterX86::RDX, RegisterX86::RIP, + RegisterX86::RIZ, RegisterX86::RSI, RegisterX86::RSP, RegisterX86::SI, @@ -58,7 +60,17 @@ pub static X86_REGISTERS: [RegisterX86; 125] = [ RegisterX86::CR2, RegisterX86::CR3, RegisterX86::CR4, + RegisterX86::CR5, + RegisterX86::CR6, + RegisterX86::CR7, RegisterX86::CR8, + RegisterX86::CR9, + RegisterX86::CR10, + RegisterX86::CR11, + RegisterX86::CR12, + RegisterX86::CR13, + RegisterX86::CR14, + RegisterX86::CR15, RegisterX86::DR0, RegisterX86::DR1, RegisterX86::DR2, @@ -67,6 +79,14 @@ pub static X86_REGISTERS: [RegisterX86; 125] = [ RegisterX86::DR5, RegisterX86::DR6, RegisterX86::DR7, + RegisterX86::DR8, + RegisterX86::DR9, + RegisterX86::DR10, + RegisterX86::DR11, + RegisterX86::DR12, + RegisterX86::DR13, + RegisterX86::DR14, + RegisterX86::DR15, RegisterX86::FP0, RegisterX86::FP1, RegisterX86::FP2, @@ -133,15 +153,12 @@ pub static X86_REGISTERS: [RegisterX86; 125] = [ RegisterX86::R15W, ]; -type Unicorn<'a> = unicorn_engine::UnicornHandle<'a>; - #[test] fn emulate_x86() { let x86_code32: Vec = vec![0x41, 0x4a]; // INC ecx; DEC edx - let mut unicorn = unicorn_engine::Unicorn::new(Arch::X86, Mode::MODE_32) + let mut emu = unicorn_emulator::Unicorn::new(Arch::X86, Mode::MODE_32, 0) .expect("failed to initialize unicorn instance"); - let mut emu = unicorn.borrow(); assert_eq!(emu.reg_write(RegisterX86::EAX as i32, 123), Ok(())); assert_eq!(emu.reg_read(RegisterX86::EAX as i32), Ok(123)); @@ -183,16 +200,15 @@ fn x86_code_callback() { let codes_cell = Rc::new(RefCell::new(codes)); let callback_codes = codes_cell.clone(); - let callback = move |_: Unicorn<'_>, address: u64, size: u32| { + let callback = move |_: &mut Unicorn<'_, ()>, address: u64, size: u32| { let mut codes = callback_codes.borrow_mut(); codes.push(CodeExpectation(address, size)); }; let x86_code32: Vec = vec![0x41, 0x4a]; // INC ecx; DEC edx - let mut unicorn = unicorn_engine::Unicorn::new(Arch::X86, Mode::MODE_32) + let mut emu = unicorn_emulator::Unicorn::new(Arch::X86, Mode::MODE_32, ()) .expect("failed to initialize unicorn instance"); - let mut emu = unicorn.borrow(); assert_eq!(emu.mem_map(0x1000, 0x4000, Permission::ALL), Ok(())); assert_eq!(emu.mem_write(0x1000, &x86_code32), Ok(())); @@ -215,15 +231,14 @@ fn x86_intr_callback() { let intr_cell = Rc::new(RefCell::new(IntrExpectation(0))); let callback_intr = intr_cell.clone(); - let callback = move |_: Unicorn<'_>, intno: u32| { + let callback = move |_: &mut Unicorn<'_, ()>, intno: u32| { *callback_intr.borrow_mut() = IntrExpectation(intno); }; let x86_code32: Vec = vec![0xcd, 0x80]; // INT 0x80; - let mut unicorn = unicorn_engine::Unicorn::new(Arch::X86, Mode::MODE_32) + let mut emu = unicorn_emulator::Unicorn::new(Arch::X86, Mode::MODE_32, ()) .expect("failed to initialize unicorn instance"); - let mut emu = unicorn.borrow(); assert_eq!(emu.mem_map(0x1000, 0x4000, Permission::ALL), Ok(())); assert_eq!(emu.mem_write(0x1000, &x86_code32), Ok(())); @@ -251,22 +266,16 @@ fn x86_mem_callback() { let expects = vec![ MemExpectation(MemType::WRITE, 0x2000, 4, 0xdeadbeef), MemExpectation(MemType::READ_UNMAPPED, 0x10000, 4, 0), - MemExpectation(MemType::READ, 0x10000, 4, 0), ]; let mems: Vec = Vec::new(); let mems_cell = Rc::new(RefCell::new(mems)); let callback_mems = mems_cell.clone(); let callback = - move |uc: Unicorn<'_>, mem_type: MemType, address: u64, size: usize, value: i64| { + move |_: &mut Unicorn<'_, ()>, mem_type: MemType, address: u64, size: usize, value: i64| { let mut mems = callback_mems.borrow_mut(); - let mut uc = uc; - mems.push(MemExpectation(mem_type, address, size, value)); - - if mem_type == MemType::READ_UNMAPPED { - uc.mem_map(address, 0x1000, Permission::ALL).unwrap(); - } + true }; // mov eax, 0xdeadbeef; @@ -276,9 +285,8 @@ fn x86_mem_callback() { 0xB8, 0xEF, 0xBE, 0xAD, 0xDE, 0xA3, 0x00, 0x20, 0x00, 0x00, 0xA1, 0x00, 0x00, 0x01, 0x00, ]; - let mut unicorn = unicorn_engine::Unicorn::new(Arch::X86, Mode::MODE_32) + let mut emu = unicorn_emulator::Unicorn::new(Arch::X86, Mode::MODE_32, ()) .expect("failed to initialize unicorn instance"); - let mut emu = unicorn.borrow(); assert_eq!(emu.mem_map(0x1000, 0x4000, Permission::ALL), Ok(())); assert_eq!(emu.mem_write(0x1000, &x86_code32), Ok(())); @@ -293,7 +301,7 @@ fn x86_mem_callback() { 10 * SECOND_SCALE, 0x1000 ), - Ok(()) + Err(uc_error::READ_UNMAPPED) ); assert_eq!(expects, *mems_cell.borrow()); @@ -308,15 +316,14 @@ fn x86_insn_in_callback() { let insn_cell = Rc::new(RefCell::new(InsnInExpectation(0, 0))); let callback_insn = insn_cell.clone(); - let callback = move |_: Unicorn<'_>, port: u32, size: usize| { + let callback = move |_: &mut Unicorn<'_, ()>, port: u32, size: usize| { *callback_insn.borrow_mut() = InsnInExpectation(port, size); }; let x86_code32: Vec = vec![0xe5, 0x10]; // IN eax, 0x10; - let mut unicorn = unicorn_engine::Unicorn::new(Arch::X86, Mode::MODE_32) + let mut emu = unicorn_emulator::Unicorn::new(Arch::X86, Mode::MODE_32, ()) .expect("failed to initialize unicorn instance"); - let mut emu = unicorn.borrow(); assert_eq!(emu.mem_map(0x1000, 0x4000, Permission::ALL), Ok(())); assert_eq!(emu.mem_write(0x1000, &x86_code32), Ok(())); @@ -345,15 +352,14 @@ fn x86_insn_out_callback() { let insn_cell = Rc::new(RefCell::new(InsnOutExpectation(0, 0, 0))); let callback_insn = insn_cell.clone(); - let callback = move |_: Unicorn<'_>, port: u32, size: usize, value: u32| { + let callback = move |_: &mut Unicorn<'_, ()>, port: u32, size: usize, value: u32| { *callback_insn.borrow_mut() = InsnOutExpectation(port, size, value); }; let x86_code32: Vec = vec![0xb0, 0x32, 0xe6, 0x46]; // MOV al, 0x32; OUT 0x46, al; - let mut unicorn = unicorn_engine::Unicorn::new(Arch::X86, Mode::MODE_32) + let mut emu = unicorn_emulator::Unicorn::new(Arch::X86, Mode::MODE_32, ()) .expect("failed to initialize unicorn instance"); - let mut emu = unicorn.borrow(); assert_eq!(emu.mem_map(0x1000, 0x4000, Permission::ALL), Ok(())); assert_eq!(emu.mem_write(0x1000, &x86_code32), Ok(())); @@ -382,7 +388,7 @@ fn x86_insn_sys_callback() { let insn_cell = Rc::new(RefCell::new(InsnSysExpectation(0))); let callback_insn = insn_cell.clone(); - let callback = move |uc: Unicorn<'_>| { + let callback = move |uc: &mut Unicorn<'_, ()>| { println!("!!!!"); let rax = uc.reg_read(RegisterX86::RAX as i32).unwrap(); *callback_insn.borrow_mut() = InsnSysExpectation(rax); @@ -393,9 +399,8 @@ fn x86_insn_sys_callback() { 0x48, 0xB8, 0xEF, 0xBE, 0xAD, 0xDE, 0x00, 0x00, 0x00, 0x00, 0x0F, 0x05, ]; - let mut unicorn = unicorn_engine::Unicorn::new(Arch::X86, Mode::MODE_64) + let mut emu = unicorn_emulator::Unicorn::new(Arch::X86, Mode::MODE_64, ()) .expect("failed to initialize unicorn instance"); - let mut emu = unicorn.borrow(); assert_eq!(emu.mem_map(0x1000, 0x4000, Permission::ALL), Ok(())); assert_eq!(emu.mem_write(0x1000, &x86_code), Ok(())); @@ -420,9 +425,8 @@ fn x86_insn_sys_callback() { fn emulate_arm() { let arm_code32: Vec = vec![0x83, 0xb0]; // sub sp, #0xc - let mut unicorn = unicorn_engine::Unicorn::new(Arch::ARM, Mode::THUMB) + let mut emu = unicorn_emulator::Unicorn::new(Arch::ARM, Mode::THUMB, ()) .expect("failed to initialize unicorn instance"); - let mut emu = unicorn.borrow(); assert_eq!(emu.reg_write(RegisterARM::R1 as i32, 123), Ok(())); assert_eq!(emu.reg_read(RegisterARM::R1 as i32), Ok(123)); @@ -461,9 +465,8 @@ fn emulate_arm() { fn emulate_mips() { let mips_code32 = vec![0x56, 0x34, 0x21, 0x34]; // ori $at, $at, 0x3456; - let mut unicorn = unicorn_engine::Unicorn::new(Arch::MIPS, Mode::MODE_32) + let mut emu = unicorn_emulator::Unicorn::new(Arch::MIPS, Mode::MODE_32, ()) .expect("failed to initialize unicorn instance"); - let mut emu = unicorn.borrow(); assert_eq!(emu.mem_map(0x1000, 0x4000, Permission::ALL), Ok(())); assert_eq!(emu.mem_write(0x1000, &mips_code32), Ok(())); assert_eq!( @@ -487,9 +490,8 @@ fn emulate_mips() { fn emulate_ppc() { let ppc_code32 = vec![0x7F, 0x46, 0x1A, 0x14]; // add 26, 6, 3 - let mut unicorn = unicorn_engine::Unicorn::new(Arch::PPC, Mode::PPC32 | Mode::BIG_ENDIAN) + let mut emu = unicorn_emulator::Unicorn::new(Arch::PPC, Mode::PPC32, ()) .expect("failed to initialize unicorn instance"); - let mut emu = unicorn.borrow(); assert_eq!(emu.mem_map(0x1000, 0x4000, Permission::ALL), Ok(())); assert_eq!(emu.mem_write(0x1000, &ppc_code32), Ok(())); assert_eq!( @@ -512,9 +514,8 @@ fn emulate_ppc() { #[test] fn mem_unmapping() { - let mut unicorn = unicorn_engine::Unicorn::new(Arch::X86, Mode::MODE_32) + let mut emu = unicorn_emulator::Unicorn::new(Arch::X86, Mode::MODE_32, ()) .expect("failed to initialize unicorn instance"); - let mut emu = unicorn.borrow(); assert_eq!(emu.mem_map(0x1000, 0x4000, Permission::ALL), Ok(())); assert_eq!(emu.mem_unmap(0x1000, 0x4000), Ok(())); } @@ -525,9 +526,8 @@ fn mem_map_ptr() { let mut mem: [u8; 4000] = [0; 4000]; let x86_code32: Vec = vec![0x41, 0x4a]; // INC ecx; DEC edx - let mut unicorn = unicorn_engine::Unicorn::new(Arch::X86, Mode::MODE_32) + let mut emu = unicorn_emulator::Unicorn::new(Arch::X86, Mode::MODE_32, ()) .expect("failed to initialize unicorn instance"); - let mut emu = unicorn.borrow(); // Attempt to write to memory before mapping it. assert_eq!( @@ -536,7 +536,7 @@ fn mem_map_ptr() { ); assert_eq!( - emu.mem_map_ptr(0x1000, 0x4000, Permission::ALL, mem.as_mut_ptr() as _), + unsafe { emu.mem_map_ptr(0x1000, 0x4000, Permission::ALL, mem.as_mut_ptr() as _) }, Ok(()) ); assert_eq!(emu.mem_write(0x1000, &x86_code32), Ok(())); @@ -572,7 +572,7 @@ fn mem_map_ptr() { ); assert_eq!( - emu.mem_map_ptr(0x1000, 0x4000, Permission::ALL, mem.as_mut_ptr() as _), + unsafe { emu.mem_map_ptr(0x1000, 0x4000, Permission::ALL, mem.as_mut_ptr() as _) }, Ok(()) ); assert_eq!(emu.mem_write(0x1000, &x86_code32), Ok(())); @@ -600,13 +600,12 @@ fn mem_map_ptr() { #[test] fn x86_context_save_and_restore() { - for mode in vec![Mode::MODE_32, Mode::MODE_64] { + for mode in &[Mode::MODE_32, Mode::MODE_64] { let x86_code: Vec = vec![ 0x48, 0xB8, 0xEF, 0xBE, 0xAD, 0xDE, 0x00, 0x00, 0x00, 0x00, 0x0F, 0x05, ]; - let mut unicorn = unicorn_engine::Unicorn::new(Arch::X86, mode) + let mut emu = unicorn_emulator::Unicorn::new(Arch::X86, *mode, ()) .expect("failed to initialize unicorn instance"); - let mut emu = unicorn.borrow(); assert_eq!(emu.mem_map(0x1000, 0x4000, Permission::ALL), Ok(())); assert_eq!(emu.mem_write(0x1000, &x86_code), Ok(())); let _ = emu.emu_start( @@ -621,9 +620,8 @@ fn x86_context_save_and_restore() { let context = context.unwrap(); /* and create a new emulator, into which we will "restore" that context */ - let mut unicorn2 = unicorn_engine::Unicorn::new(Arch::X86, mode) + let emu2 = unicorn_emulator::Unicorn::new(Arch::X86, *mode, ()) .expect("failed to initialize unicorn instance"); - let emu2 = unicorn2.borrow(); assert_eq!(emu2.context_restore(&context), Ok(())); for register in X86_REGISTERS.iter() { println!("Testing register {:?}", register); @@ -644,16 +642,15 @@ fn x86_block_callback() { let blocks_cell = Rc::new(RefCell::new(blocks)); let callback_blocks = blocks_cell.clone(); - let callback = move |_: Unicorn<'_>, address: u64, size: u32| { + let callback = move |_: &mut Unicorn<'_, ()>, address: u64, size: u32| { let mut blocks = callback_blocks.borrow_mut(); blocks.push(BlockExpectation(address, size)); }; let x86_code32: Vec = vec![0x41, 0x4a]; // INC ecx; DEC edx - let mut unicorn = unicorn_engine::Unicorn::new(Arch::X86, Mode::MODE_32) + let mut emu = unicorn_emulator::Unicorn::new(Arch::X86, Mode::MODE_32, ()) .expect("failed to initialize unicorn instance"); - let mut emu = unicorn.borrow(); assert_eq!(emu.mem_map(0x1000, 0x4000, Permission::ALL), Ok(())); assert_eq!(emu.mem_write(0x1000, &x86_code32), Ok(())); diff --git a/include/uc_priv.h b/include/uc_priv.h index 9d88c233e5..debd85f8a8 100644 --- a/include/uc_priv.h +++ b/include/uc_priv.h @@ -342,6 +342,7 @@ struct uc_struct { bool no_exit_request; // Disable check_exit_request temporarily. A // workaround to treat the IT block as a whole block. bool init_done; // Whether the initialization is done. + void *data_ptr; // optional data pointer for bindings }; // Metadata stub for the variable-size cpu context used with uc_context_*() diff --git a/uc.c b/uc.c index ecb954ccbd..e40901c242 100644 --- a/uc.c +++ b/uc.c @@ -686,6 +686,27 @@ static void clear_deleted_hooks(uc_engine *uc) list_clear(&uc->hooks_to_del); } +// set a data ptr (for use in bindings) +UNICORN_EXPORT +uc_err uc_emu_set_data_ptr(uc_engine *uc, void *data) +{ + if (uc == NULL) { + return UC_ERR_ARG; + } + uc->data_ptr = data; +} + +// get a data ptr (for use in bindings) +UNICORN_EXPORT +void *uc_emu_get_data_ptr(uc_engine *uc, void *data) +{ + if (uc == NULL) { + return NULL; + } + return uc->data_ptr; +} + + UNICORN_EXPORT uc_err uc_emu_start(uc_engine *uc, uint64_t begin, uint64_t until, uint64_t timeout, size_t count) From a8f9655189e327f96b54f90842cab1b19abcde14 Mon Sep 17 00:00:00 2001 From: Dominik Maier Date: Mon, 8 Nov 2021 19:37:38 +0100 Subject: [PATCH 02/12] lifetime --- bindings/rust/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bindings/rust/src/lib.rs b/bindings/rust/src/lib.rs index 6d007edc58..b3652404c4 100644 --- a/bindings/rust/src/lib.rs +++ b/bindings/rust/src/lib.rs @@ -87,7 +87,7 @@ pub struct Unicorn<'a, D: 'a> { pub data: D, } -impl Unicorn<()> { +impl<'a> Unicorn<'a, ()> { /// Create a new instance of the unicorn engine for the specified architecture /// and hardware mode. pub fn new(arch: Arch, mode: Mode) -> Result, uc_error> { From 0dfc0cba1fbc4ba3d444b0fe68ab62b9eb45ee61 Mon Sep 17 00:00:00 2001 From: Dominik Maier Date: Mon, 8 Nov 2021 19:39:15 +0100 Subject: [PATCH 03/12] removed duplicate --- bindings/rust/src/lib.rs | 6 ------ 1 file changed, 6 deletions(-) diff --git a/bindings/rust/src/lib.rs b/bindings/rust/src/lib.rs index b3652404c4..dfe1599250 100644 --- a/bindings/rust/src/lib.rs +++ b/bindings/rust/src/lib.rs @@ -99,12 +99,6 @@ impl<'a, D> Unicorn<'a, D> where D: 'a, { - /// Create a new instance of the unicorn engine for the specified architecture - /// and hardware mode. - pub fn new(arch: Arch, mode: Mode) -> Result, uc_error> { - Self::new_with_data(arch, mode, ()) - } - /// Create a new instance of the unicorn engine for the specified architecture /// and hardware mode. pub fn new_with_data(arch: Arch, mode: Mode, data: D) -> Result, uc_error> { From d7ead41a51aa3d7d928465a5bc1b72f3f6639eb4 Mon Sep 17 00:00:00 2001 From: Dominik Maier Date: Tue, 9 Nov 2021 14:49:18 +0100 Subject: [PATCH 04/12] fixed testcases --- bindings/rust/src/lib.rs | 7 +------ bindings/rust/tests/unicorn.rs | 30 +++++++++++++++--------------- 2 files changed, 16 insertions(+), 21 deletions(-) diff --git a/bindings/rust/src/lib.rs b/bindings/rust/src/lib.rs index dfe1599250..5ffbc2de5a 100644 --- a/bindings/rust/src/lib.rs +++ b/bindings/rust/src/lib.rs @@ -118,15 +118,10 @@ where } /// Drop UC -/// TODO: !!! Right now, this leaks the unicorn instance on purpose. -/// UC 1 for some platforms, for example aarch64, seems to crash on cleanup. -/// After updating to Unicorn 2, we should call `uc_close` again! impl<'a, D> Drop for Unicorn<'a, D> { fn drop(&mut self) { if !self.uc.is_null() { - // TODO: !!! - // This is a deliberate leak, get rid of it after updating to UC2! - // unsafe { ffi::uc_close(self.uc) }; + unsafe { ffi::uc_close(self.uc) }; } self.uc = ptr::null_mut(); } diff --git a/bindings/rust/tests/unicorn.rs b/bindings/rust/tests/unicorn.rs index 80dc68c3db..039569bcbf 100644 --- a/bindings/rust/tests/unicorn.rs +++ b/bindings/rust/tests/unicorn.rs @@ -157,7 +157,7 @@ pub static X86_REGISTERS: [RegisterX86; 125] = [ fn emulate_x86() { let x86_code32: Vec = vec![0x41, 0x4a]; // INC ecx; DEC edx - let mut emu = unicorn_emulator::Unicorn::new(Arch::X86, Mode::MODE_32, 0) + let mut emu = unicorn_engine::Unicorn::new(Arch::X86, Mode::MODE_32) .expect("failed to initialize unicorn instance"); assert_eq!(emu.reg_write(RegisterX86::EAX as i32, 123), Ok(())); assert_eq!(emu.reg_read(RegisterX86::EAX as i32), Ok(123)); @@ -207,7 +207,7 @@ fn x86_code_callback() { let x86_code32: Vec = vec![0x41, 0x4a]; // INC ecx; DEC edx - let mut emu = unicorn_emulator::Unicorn::new(Arch::X86, Mode::MODE_32, ()) + let mut emu = unicorn_engine::Unicorn::new(Arch::X86, Mode::MODE_32) .expect("failed to initialize unicorn instance"); assert_eq!(emu.mem_map(0x1000, 0x4000, Permission::ALL), Ok(())); assert_eq!(emu.mem_write(0x1000, &x86_code32), Ok(())); @@ -237,7 +237,7 @@ fn x86_intr_callback() { let x86_code32: Vec = vec![0xcd, 0x80]; // INT 0x80; - let mut emu = unicorn_emulator::Unicorn::new(Arch::X86, Mode::MODE_32, ()) + let mut emu = unicorn_engine::Unicorn::new(Arch::X86, Mode::MODE_32) .expect("failed to initialize unicorn instance"); assert_eq!(emu.mem_map(0x1000, 0x4000, Permission::ALL), Ok(())); assert_eq!(emu.mem_write(0x1000, &x86_code32), Ok(())); @@ -285,7 +285,7 @@ fn x86_mem_callback() { 0xB8, 0xEF, 0xBE, 0xAD, 0xDE, 0xA3, 0x00, 0x20, 0x00, 0x00, 0xA1, 0x00, 0x00, 0x01, 0x00, ]; - let mut emu = unicorn_emulator::Unicorn::new(Arch::X86, Mode::MODE_32, ()) + let mut emu = unicorn_engine::Unicorn::new(Arch::X86, Mode::MODE_32) .expect("failed to initialize unicorn instance"); assert_eq!(emu.mem_map(0x1000, 0x4000, Permission::ALL), Ok(())); assert_eq!(emu.mem_write(0x1000, &x86_code32), Ok(())); @@ -322,7 +322,7 @@ fn x86_insn_in_callback() { let x86_code32: Vec = vec![0xe5, 0x10]; // IN eax, 0x10; - let mut emu = unicorn_emulator::Unicorn::new(Arch::X86, Mode::MODE_32, ()) + let mut emu = unicorn_engine::Unicorn::new(Arch::X86, Mode::MODE_32) .expect("failed to initialize unicorn instance"); assert_eq!(emu.mem_map(0x1000, 0x4000, Permission::ALL), Ok(())); assert_eq!(emu.mem_write(0x1000, &x86_code32), Ok(())); @@ -358,7 +358,7 @@ fn x86_insn_out_callback() { let x86_code32: Vec = vec![0xb0, 0x32, 0xe6, 0x46]; // MOV al, 0x32; OUT 0x46, al; - let mut emu = unicorn_emulator::Unicorn::new(Arch::X86, Mode::MODE_32, ()) + let mut emu = unicorn_engine::Unicorn::new(Arch::X86, Mode::MODE_32) .expect("failed to initialize unicorn instance"); assert_eq!(emu.mem_map(0x1000, 0x4000, Permission::ALL), Ok(())); assert_eq!(emu.mem_write(0x1000, &x86_code32), Ok(())); @@ -399,7 +399,7 @@ fn x86_insn_sys_callback() { 0x48, 0xB8, 0xEF, 0xBE, 0xAD, 0xDE, 0x00, 0x00, 0x00, 0x00, 0x0F, 0x05, ]; - let mut emu = unicorn_emulator::Unicorn::new(Arch::X86, Mode::MODE_64, ()) + let mut emu = unicorn_engine::Unicorn::new(Arch::X86, Mode::MODE_64) .expect("failed to initialize unicorn instance"); assert_eq!(emu.mem_map(0x1000, 0x4000, Permission::ALL), Ok(())); assert_eq!(emu.mem_write(0x1000, &x86_code), Ok(())); @@ -425,7 +425,7 @@ fn x86_insn_sys_callback() { fn emulate_arm() { let arm_code32: Vec = vec![0x83, 0xb0]; // sub sp, #0xc - let mut emu = unicorn_emulator::Unicorn::new(Arch::ARM, Mode::THUMB, ()) + let mut emu = unicorn_engine::Unicorn::new(Arch::ARM, Mode::THUMB) .expect("failed to initialize unicorn instance"); assert_eq!(emu.reg_write(RegisterARM::R1 as i32, 123), Ok(())); assert_eq!(emu.reg_read(RegisterARM::R1 as i32), Ok(123)); @@ -465,7 +465,7 @@ fn emulate_arm() { fn emulate_mips() { let mips_code32 = vec![0x56, 0x34, 0x21, 0x34]; // ori $at, $at, 0x3456; - let mut emu = unicorn_emulator::Unicorn::new(Arch::MIPS, Mode::MODE_32, ()) + let mut emu = unicorn_engine::Unicorn::new(Arch::MIPS, Mode::MODE_32) .expect("failed to initialize unicorn instance"); assert_eq!(emu.mem_map(0x1000, 0x4000, Permission::ALL), Ok(())); assert_eq!(emu.mem_write(0x1000, &mips_code32), Ok(())); @@ -490,7 +490,7 @@ fn emulate_mips() { fn emulate_ppc() { let ppc_code32 = vec![0x7F, 0x46, 0x1A, 0x14]; // add 26, 6, 3 - let mut emu = unicorn_emulator::Unicorn::new(Arch::PPC, Mode::PPC32, ()) + let mut emu = unicorn_engine::Unicorn::new(Arch::PPC, Mode::PPC32) .expect("failed to initialize unicorn instance"); assert_eq!(emu.mem_map(0x1000, 0x4000, Permission::ALL), Ok(())); assert_eq!(emu.mem_write(0x1000, &ppc_code32), Ok(())); @@ -514,7 +514,7 @@ fn emulate_ppc() { #[test] fn mem_unmapping() { - let mut emu = unicorn_emulator::Unicorn::new(Arch::X86, Mode::MODE_32, ()) + let mut emu = unicorn_engine::Unicorn::new(Arch::X86, Mode::MODE_32) .expect("failed to initialize unicorn instance"); assert_eq!(emu.mem_map(0x1000, 0x4000, Permission::ALL), Ok(())); assert_eq!(emu.mem_unmap(0x1000, 0x4000), Ok(())); @@ -526,7 +526,7 @@ fn mem_map_ptr() { let mut mem: [u8; 4000] = [0; 4000]; let x86_code32: Vec = vec![0x41, 0x4a]; // INC ecx; DEC edx - let mut emu = unicorn_emulator::Unicorn::new(Arch::X86, Mode::MODE_32, ()) + let mut emu = unicorn_engine::Unicorn::new(Arch::X86, Mode::MODE_32) .expect("failed to initialize unicorn instance"); // Attempt to write to memory before mapping it. @@ -604,7 +604,7 @@ fn x86_context_save_and_restore() { let x86_code: Vec = vec![ 0x48, 0xB8, 0xEF, 0xBE, 0xAD, 0xDE, 0x00, 0x00, 0x00, 0x00, 0x0F, 0x05, ]; - let mut emu = unicorn_emulator::Unicorn::new(Arch::X86, *mode, ()) + let mut emu = unicorn_engine::Unicorn::new(Arch::X86, *mode) .expect("failed to initialize unicorn instance"); assert_eq!(emu.mem_map(0x1000, 0x4000, Permission::ALL), Ok(())); assert_eq!(emu.mem_write(0x1000, &x86_code), Ok(())); @@ -620,7 +620,7 @@ fn x86_context_save_and_restore() { let context = context.unwrap(); /* and create a new emulator, into which we will "restore" that context */ - let emu2 = unicorn_emulator::Unicorn::new(Arch::X86, *mode, ()) + let emu2 = unicorn_engine::Unicorn::new(Arch::X86, *mode) .expect("failed to initialize unicorn instance"); assert_eq!(emu2.context_restore(&context), Ok(())); for register in X86_REGISTERS.iter() { @@ -649,7 +649,7 @@ fn x86_block_callback() { let x86_code32: Vec = vec![0x41, 0x4a]; // INC ecx; DEC edx - let mut emu = unicorn_emulator::Unicorn::new(Arch::X86, Mode::MODE_32, ()) + let mut emu = unicorn_engine::Unicorn::new(Arch::X86, Mode::MODE_32) .expect("failed to initialize unicorn instance"); assert_eq!(emu.mem_map(0x1000, 0x4000, Permission::ALL), Ok(())); assert_eq!(emu.mem_write(0x1000, &x86_code32), Ok(())); From a3e139847dacc93cf922ba2bcb297ef58be7673e Mon Sep 17 00:00:00 2001 From: Dominik Maier Date: Tue, 9 Nov 2021 16:04:59 +0100 Subject: [PATCH 05/12] reapply missing changes --- bindings/rust/README.md | 12 ++++++------ bindings/rust/src/lib.rs | 4 +++- 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/bindings/rust/README.md b/bindings/rust/README.md index dba1d53c3d..d855cea3a8 100644 --- a/bindings/rust/README.md +++ b/bindings/rust/README.md @@ -5,23 +5,23 @@ Rust bindings for the [Unicorn](http://www.unicorn-engine.org/) emulator with ut Checkout Unicorn2 source code at [dev branch](https://github.com/unicorn-engine/unicorn/tree/dev). ```rust -use unicorn_engine::RegisterARM; +use unicorn_engine::{Unicorn, RegisterARM}; use unicorn_engine::unicorn_const::{Arch, Mode, Permission, SECOND_SCALE}; fn main() { let arm_code32: Vec = vec![0x17, 0x00, 0x40, 0xe2]; // sub r0, #23 - let mut unicorn = unicorn-engine::Unicorn::new(Arch::ARM, Mode::LITTLE_ENDIAN).expect("failed to initialize Unicorn instance"); + let mut unicorn = Unicorn::new(Arch::ARM, Mode::LITTLE_ENDIAN).expect("failed to initialize Unicorn instance"); let mut emu = unicorn.borrow(); emu.mem_map(0x1000, 0x4000, Permission::ALL).expect("failed to map code page"); emu.mem_write(0x1000, &arm_code32).expect("failed to write instructions"); - emu.reg_write(RegisterARM::R0 as i32, 123).expect("failed write R0"); - emu.reg_write(RegisterARM::R5 as i32, 1337).expect("failed write R5"); + emu.reg_write(RegisterARM::R0, 123).expect("failed write R0"); + emu.reg_write(RegisterARM::R5, 1337).expect("failed write R5"); let _ = emu.emu_start(0x1000, (0x1000 + arm_code32.len()) as u64, 10 * SECOND_SCALE, 1000); - assert_eq!(emu.reg_read(RegisterARM::R0 as i32), Ok(100)); - assert_eq!(emu.reg_read(RegisterARM::R5 as i32), Ok(1337)); + assert_eq!(emu.reg_read(RegisterARM::R0, Ok(100)); + assert_eq!(emu.reg_read(RegisterARM::R5, Ok(1337)); } ``` Further sample code can be found in ```tests/unicorn.rs```. diff --git a/bindings/rust/src/lib.rs b/bindings/rust/src/lib.rs index 5ffbc2de5a..4663b42c71 100644 --- a/bindings/rust/src/lib.rs +++ b/bindings/rust/src/lib.rs @@ -289,7 +289,7 @@ impl<'a, D> Unicorn<'a, D> { /// Write variable sized values into registers. /// /// The user has to make sure that the buffer length matches the register size. - /// This adds support for registers >64 bit (GDTR/IDTR, XMM, YMM, ZMM (x86); Q, V (arm64)). + /// This adds support for registers >64 bit (GDTR/IDTR, XMM, YMM, ZMM, ST (x86); Q, V (arm64)). pub fn reg_write_long>(&self, regid: T, value: &[u8]) -> Result<(), uc_error> { let err = unsafe { ffi::uc_reg_write(self.uc, regid.into(), value.as_ptr() as _) }; if err == uc_error::OK { @@ -337,6 +337,8 @@ impl<'a, D> Unicorn<'a, D> { value = vec![0; 64]; } else if curr_reg_id == x86::RegisterX86::GDTR as i32 || curr_reg_id == x86::RegisterX86::IDTR as i32 + || (curr_reg_id >= x86::RegisterX86::ST0 as i32 + && curr_reg_id <= x86::RegisterX86::ST7 as i32) { value = vec![0; 10]; // 64 bit base address in IA-32e mode } else { From 3b0ce89a7aca2f8d768735f026810ff47a5e7827 Mon Sep 17 00:00:00 2001 From: Dominik Maier Date: Tue, 9 Nov 2021 16:09:02 +0100 Subject: [PATCH 06/12] missing pcs --- bindings/rust/src/lib.rs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/bindings/rust/src/lib.rs b/bindings/rust/src/lib.rs index 4663b42c71..9cfeb10edf 100644 --- a/bindings/rust/src/lib.rs +++ b/bindings/rust/src/lib.rs @@ -761,6 +761,8 @@ impl<'a, D> Unicorn<'a, D> { Arch::MIPS => RegisterMIPS::PC as i32, Arch::SPARC => RegisterSPARC::PC as i32, Arch::M68K => RegisterM68K::PC as i32, + Arch::PPC => RegisterPPC::PC as i32, + Arch::RISCV => RegisterRISCV::PC as i32, _ => panic!("Arch pc not yet know to the unicorn rust bindings"), }; self.reg_read(reg) @@ -777,6 +779,8 @@ impl<'a, D> Unicorn<'a, D> { Arch::MIPS => RegisterMIPS::PC as i32, Arch::SPARC => RegisterSPARC::PC as i32, Arch::M68K => RegisterM68K::PC as i32, + Arch::PPC => RegisterPPC::PC as i32, + Arch::RISCV => RegisterRISCV::PC as i32, _ => panic!("Arch not yet known to the unicorn rust bindings"), }; self.reg_write(reg, value) From 492779d7d45b0dcd6b8cb27d25690fa533f7bc4a Mon Sep 17 00:00:00 2001 From: Dominik Maier Date: Tue, 9 Nov 2021 16:19:27 +0100 Subject: [PATCH 07/12] fixed context --- bindings/rust/src/ffi.rs | 2 +- bindings/rust/src/lib.rs | 21 ++++++--------------- 2 files changed, 7 insertions(+), 16 deletions(-) diff --git a/bindings/rust/src/ffi.rs b/bindings/rust/src/ffi.rs index a309c51972..9c2b0bd14d 100644 --- a/bindings/rust/src/ffi.rs +++ b/bindings/rust/src/ffi.rs @@ -9,7 +9,7 @@ use std::{ffi::c_void, marker::PhantomData}; pub type uc_handle = *mut c_void; pub type uc_hook = *mut c_void; -pub type uc_context = libc::size_t; +pub type uc_context = *mut c_void; extern "C" { pub fn uc_version(major: *mut u32, minor: *mut u32) -> u32; diff --git a/bindings/rust/src/lib.rs b/bindings/rust/src/lib.rs index 9cfeb10edf..0a1df9987b 100644 --- a/bindings/rust/src/lib.rs +++ b/bindings/rust/src/lib.rs @@ -35,11 +35,12 @@ mod arm64; mod m68k; mod mips; mod ppc; +mod riscv; mod sparc; mod x86; use std::{marker::PhantomData, ptr}; -pub use crate::{arm::*, arm64::*, m68k::*, mips::*, ppc::*, sparc::*, x86::*}; +pub use crate::{arm::*, arm64::*, m68k::*, mips::*, ppc::*, riscv::*, sparc::*, x86::*}; use ffi::uc_handle; use libc::c_void; @@ -51,19 +52,9 @@ pub struct Context { } impl Context { - #[must_use] - pub fn new() -> Self { - Context { context: 0 } - } #[must_use] pub fn is_initialized(&self) -> bool { - self.context != 0 - } -} - -impl Default for Context { - fn default() -> Self { - Self::new() + !self.context.is_null() } } @@ -74,7 +65,7 @@ impl Drop for Context { ffi::uc_context_free(self.context); } } - self.context = 0; + self.context = ptr::null_mut(); } } @@ -642,7 +633,7 @@ impl<'a, D> Unicorn<'a, D> { /// /// To be populated via `context_save`. pub fn context_alloc(&self) -> Result { - let mut empty_context: ffi::uc_context = Default::default(); + let mut empty_context: ffi::uc_context = ptr::null_mut(); let err = unsafe { ffi::uc_context_alloc(self.uc, &mut empty_context) }; if err == uc_error::OK { Ok(Context { @@ -669,7 +660,7 @@ impl<'a, D> Unicorn<'a, D> { /// In case of many non-concurrent context saves, use `context_alloc` and *_save /// individually to avoid unnecessary allocations. pub fn context_init(&self) -> Result { - let mut new_context: ffi::uc_context = Default::default(); + let mut new_context: ffi::uc_context = ptr::null_mut(); let err = unsafe { ffi::uc_context_alloc(self.uc, &mut new_context) }; if err != uc_error::OK { return Err(err); From 177b21c2af05ac5897a450eea451fa625a7685cf Mon Sep 17 00:00:00 2001 From: Dominik Maier Date: Wed, 10 Nov 2021 04:11:14 +0100 Subject: [PATCH 08/12] removed need for extra api; no_std --- bindings/rust/Cargo.toml | 2 +- bindings/rust/build.rs | 174 +++++++++++++++------------ bindings/rust/src/ffi.rs | 66 ++++------- bindings/rust/src/lib.rs | 211 ++++++++++++++++++++------------- bindings/rust/tests/unicorn.rs | 124 +++++++++---------- uc.c | 21 ---- 6 files changed, 308 insertions(+), 290 deletions(-) diff --git a/bindings/rust/Cargo.toml b/bindings/rust/Cargo.toml index 23482b933e..7aa9ca73b4 100644 --- a/bindings/rust/Cargo.toml +++ b/bindings/rust/Cargo.toml @@ -3,7 +3,7 @@ name = "unicorn-engine" version = "2.0.0-rc3" authors = ["Ziqiao Kong", "Lukas Seidel"] documentation = "https://github.com/unicorn-engine/unicorn/wiki" -edition = "2018" +edition = "2021" include = [ "/.gitmodules", "/COPYING", diff --git a/bindings/rust/build.rs b/bindings/rust/build.rs index 195e9e0d0c..8efb188744 100644 --- a/bindings/rust/build.rs +++ b/bindings/rust/build.rs @@ -18,7 +18,12 @@ fn find_unicorn(unicorn_dir: &Path) -> Option { None } -fn download_unicorn() -> Option { +fn out_dir() -> PathBuf { + let out_dir = env::var("OUT_DIR").unwrap(); + Path::new(&out_dir).to_path_buf() +} + +fn download_unicorn() -> PathBuf { // https://docs.github.com/en/rest/reference/repos#download-a-repository-archive-tar let pkg_version; if let Ok(unicorn_version) = env::var("UNICORN_VERSION") { @@ -26,7 +31,7 @@ fn download_unicorn() -> Option { } else { pkg_version = env::var("CARGO_PKG_VERSION").unwrap(); } - let out_dir = PathBuf::from(env::var("OUT_DIR").unwrap()); + let out_dir = out_dir(); let client = reqwest::blocking::Client::new(); let resp = client .get(format!( @@ -43,85 +48,107 @@ fn download_unicorn() -> Option { let mut archive = Archive::new(tar); archive.unpack(&out_dir).unwrap(); - match find_unicorn(&out_dir) { - Some(dir) => Some(String::from(out_dir.join(dir).to_str()?)), - None => None, - } + find_unicorn(&out_dir).unwrap() } -#[allow(clippy::branches_sharing_code)] +#[allow(clippy::too_many_lines)] fn main() { let profile = env::var("PROFILE").unwrap(); - let unicorn_dir = download_unicorn().unwrap(); - - println!("cargo:rerun-if-changed={}", &unicorn_dir); - - // We don't use TARGET since we can't cross-build. - if env::consts::OS == "windows" { - // Windows - let mut cmd = Command::new("cmake"); - cmd.current_dir(&unicorn_dir) - .arg("-B") - .arg("rust_build") - .arg("-DUNICORN_BUILD_SHARED=off") - .arg("-G") - .arg("Visual Studio 16 2019"); - - if profile == "debug" { - cmd.arg("-DCMAKE_BUILD_TYPE=Debug"); - } else { - cmd.arg("-DCMAKE_BUILD_TYPE=Release"); - } - - cmd.output() - .expect("Fail to create build directory on Windows."); - - let mut platform = "x64"; - let mut conf = "Release"; - if std::mem::size_of::() == 4 { - platform = "Win32"; - } - if profile == "debug" { - conf = "Debug"; - } - - Command::new("msbuild") - .current_dir(format!("{}/rust_build", &unicorn_dir)) - .arg("unicorn.sln") - .arg("-m") - .arg("-p:Platform=".to_owned() + platform) - .arg("-p:Configuration=".to_owned() + conf) - .output() - .expect("Fail to build unicorn on Win32."); + if let Some(unicorn_dir) = find_unicorn(&out_dir()) { + let rust_build_path = unicorn_dir.join("rust_build"); + println!( + "cargo:rustc-link-search={}", + rust_build_path.to_str().unwrap() + ); println!( - "cargo:rustc-link-search={}/rust_build/{}", - unicorn_dir, conf + "cargo:rustc-link-search={}", + rust_build_path.join("Debug").to_str().unwrap() + ); + println!( + "cargo:rustc-link-search={}", + rust_build_path.join("Release").to_str().unwrap() ); } else { - // Most Unix-like systems - let mut cmd = Command::new("cmake"); - cmd.current_dir(&unicorn_dir) - .arg("-B") - .arg("rust_build") - .arg("-DUNICORN_BUILD_SHARED=off"); - - if profile == "debug" { - cmd.arg("-DCMAKE_BUILD_TYPE=Debug"); + let unicorn_dir = if env::var("CI").is_ok() { + Path::new("..").join("..") } else { - cmd.arg("-DCMAKE_BUILD_TYPE=Release"); - } + println!("cargo:warning=Unicorn not found. Downloading..."); + download_unicorn() + }; - cmd.output() - .expect("Fail to create build directory on *nix."); + let rust_build_path = unicorn_dir.join("rust_build"); - Command::new("make") - .current_dir(format!("{}/rust_build", &unicorn_dir)) - .arg("-j6") - .output() - .expect("Fail to build unicorn on *nix."); + let mut cmd = Command::new("cmake"); - println!("cargo:rustc-link-search={}/rust_build", unicorn_dir); + // We don't use TARGET since we can't cross-build. + if env::consts::OS == "windows" { + // Windows + cmd.current_dir(&unicorn_dir) + .arg("-B") + .arg("rust_build") + .arg("-DUNICORN_BUILD_SHARED=off") + .arg("-G") + .arg("Visual Studio 16 2019"); + + if profile == "debug" { + cmd.arg("-DCMAKE_BUILD_TYPE=Debug"); + } else { + cmd.arg("-DCMAKE_BUILD_TYPE=Release"); + } + + cmd.output() + .expect("Fail to create build directory on Windows."); + + let mut platform = "x64"; + let mut conf = "Release"; + if std::mem::size_of::() == 4 { + platform = "Win32"; + } + if profile == "debug" { + conf = "Debug"; + } + + Command::new("msbuild") + .current_dir(&rust_build_path) + .arg("unicorn.sln") + .arg("-m") + .arg("-p:Platform=".to_owned() + platform) + .arg("-p:Configuration=".to_owned() + conf) + .output() + .expect("Fail to build unicorn on Win32."); + println!( + "cargo:rustc-link-search={}", + rust_build_path.join(conf).to_str().unwrap() + ); + } else { + // Most Unix-like systems + let mut cmd = Command::new("cmake"); + cmd.current_dir(&unicorn_dir) + .arg("-B") + .arg("rust_build") + .arg("-DUNICORN_BUILD_SHARED=off"); + + if profile == "debug" { + cmd.arg("-DCMAKE_BUILD_TYPE=Debug"); + } else { + cmd.arg("-DCMAKE_BUILD_TYPE=Release"); + } + + cmd.output() + .expect("Fail to create build directory on *nix."); + + Command::new("make") + .current_dir(&rust_build_path) + .arg("-j6") + .output() + .expect("Fail to build unicorn on *nix."); + + println!( + "cargo:rustc-link-search={}", + rust_build_path.to_str().unwrap() + ); + } } // This is a workaround for Unicorn static link since libunicorn.a is also linked again lib*-softmmu.a. @@ -130,7 +157,7 @@ fn main() { // // Lazymio(@wtdcode): Why do I stick to static link? See: https://github.com/rust-lang/cargo/issues/5077 println!("cargo:rustc-link-lib=unicorn"); - for arch in [ + for arch in &[ "x86_64", "arm", "armeb", @@ -147,10 +174,11 @@ fn main() { "m68k", "ppc", "ppc64", - ] - .iter() - { + ] { println!("cargo:rustc-link-lib={}-softmmu", arch); } println!("cargo:rustc-link-lib=unicorn-common"); + + println!("cargo:rerun-if-changed=build.rs"); + println!("cargo:rerun-if-changed=src"); } diff --git a/bindings/rust/src/ffi.rs b/bindings/rust/src/ffi.rs index 9c2b0bd14d..300e12206b 100644 --- a/bindings/rust/src/ffi.rs +++ b/bindings/rust/src/ffi.rs @@ -4,8 +4,8 @@ use crate::Unicorn; use super::unicorn_const::{uc_error, Arch, HookType, MemRegion, MemType, Mode, Query}; +use core::ffi::c_void; use libc::{c_char, c_int}; -use std::{ffi::c_void, marker::PhantomData}; pub type uc_handle = *mut c_void; pub type uc_hook = *mut c_void; @@ -76,30 +76,17 @@ extern "C" { pub fn uc_context_alloc(engine: uc_handle, context: *mut uc_context) -> uc_error; pub fn uc_context_save(engine: uc_handle, context: uc_context) -> uc_error; pub fn uc_context_restore(engine: uc_handle, context: uc_context) -> uc_error; - pub fn uc_set_data_ptr(engine: uc_handle, ptr: *mut c_void) -> uc_error; - pub fn uc_get_data_ptr(engine: uc_handle) -> *mut c_void; } pub struct UcHook<'a, D: 'a, F: 'a> { pub callback: F, - pub phantom: PhantomData<&'a D>, + pub uc: Unicorn<'a, D>, } pub trait IsUcHook<'a> {} impl<'a, D, F> IsUcHook<'a> for UcHook<'a, D, F> {} -fn read_uc_from_uc_handle<'a, D>(uc: uc_handle) -> &'a mut crate::Unicorn<'a, D> -where - D: 'a, -{ - unsafe { - (uc_get_data_ptr(uc) as *mut Unicorn<'a, D>) - .as_mut() - .unwrap() - } -} - pub extern "C" fn code_hook_proxy( uc: uc_handle, address: u64, @@ -108,10 +95,9 @@ pub extern "C" fn code_hook_proxy( ) where F: FnMut(&mut crate::Unicorn, u64, u32), { - let unicorn = read_uc_from_uc_handle(uc); - let callback = unsafe { &mut (*user_data).callback }; - assert_eq!(uc, unicorn.uc); - callback(unicorn, address, size); + let user_data = unsafe { &mut *user_data }; + debug_assert_eq!(uc, user_data.uc.inner().uc); + (user_data.callback)(&mut user_data.uc, address, size); } pub extern "C" fn block_hook_proxy( @@ -122,10 +108,9 @@ pub extern "C" fn block_hook_proxy( ) where F: FnMut(&mut crate::Unicorn, u64, u32), { - let unicorn = read_uc_from_uc_handle(uc); - let callback = unsafe { &mut (*user_data).callback }; - assert_eq!(uc, unicorn.uc); - callback(unicorn, address, size); + let user_data = unsafe { &mut *user_data }; + debug_assert_eq!(uc, user_data.uc.inner().uc); + (user_data.callback)(&mut user_data.uc, address, size); } pub extern "C" fn mem_hook_proxy( @@ -139,20 +124,18 @@ pub extern "C" fn mem_hook_proxy( where F: FnMut(&mut crate::Unicorn, MemType, u64, usize, i64) -> bool, { - let unicorn = read_uc_from_uc_handle(uc); - let callback = unsafe { &mut (*user_data).callback }; - assert_eq!(uc, unicorn.uc); - callback(unicorn, mem_type, address, size as usize, value) + let user_data = unsafe { &mut *user_data }; + debug_assert_eq!(uc, user_data.uc.inner().uc); + (user_data.callback)(&mut user_data.uc, mem_type, address, size as usize, value) } pub extern "C" fn intr_hook_proxy(uc: uc_handle, value: u32, user_data: *mut UcHook) where F: FnMut(&mut crate::Unicorn, u32), { - let unicorn = read_uc_from_uc_handle(uc); - let callback = unsafe { &mut (*user_data).callback }; - assert_eq!(uc, unicorn.uc); - callback(unicorn, value); + let user_data = unsafe { &mut *user_data }; + debug_assert_eq!(uc, user_data.uc.inner().uc); + (user_data.callback)(&mut user_data.uc, value); } pub extern "C" fn insn_in_hook_proxy( @@ -163,10 +146,9 @@ pub extern "C" fn insn_in_hook_proxy( ) where F: FnMut(&mut crate::Unicorn, u32, usize), { - let unicorn = read_uc_from_uc_handle(uc); - let callback = unsafe { &mut (*user_data).callback }; - assert_eq!(uc, unicorn.uc); - callback(unicorn, port, size); + let user_data = unsafe { &mut *user_data }; + debug_assert_eq!(uc, user_data.uc.inner().uc); + (user_data.callback)(&mut user_data.uc, port, size); } pub extern "C" fn insn_out_hook_proxy( @@ -178,18 +160,16 @@ pub extern "C" fn insn_out_hook_proxy( ) where F: FnMut(&mut crate::Unicorn, u32, usize, u32), { - let unicorn = read_uc_from_uc_handle(uc); - let callback = unsafe { &mut (*user_data).callback }; - assert_eq!(uc, unicorn.uc); - callback(unicorn, port, size, value); + let user_data = unsafe { &mut *user_data }; + debug_assert_eq!(uc, user_data.uc.inner().uc); + (user_data.callback)(&mut user_data.uc, port, size, value); } pub extern "C" fn insn_sys_hook_proxy(uc: uc_handle, user_data: *mut UcHook) where F: FnMut(&mut crate::Unicorn), { - let unicorn = read_uc_from_uc_handle(uc); - assert_eq!(uc, unicorn.uc); - let callback = unsafe { &mut (*user_data).callback }; - callback(unicorn); + let user_data = unsafe { &mut *user_data }; + debug_assert_eq!(uc, user_data.uc.inner().uc); + (user_data.callback)(&mut user_data.uc); } diff --git a/bindings/rust/src/lib.rs b/bindings/rust/src/lib.rs index 0a1df9987b..7b5c35c64c 100644 --- a/bindings/rust/src/lib.rs +++ b/bindings/rust/src/lib.rs @@ -27,21 +27,29 @@ //! ``` //! -mod ffi; +#![no_std] + +#[macro_use] +extern crate alloc; + pub mod unicorn_const; mod arm; mod arm64; +mod ffi; mod m68k; mod mips; mod ppc; mod riscv; mod sparc; mod x86; -use std::{marker::PhantomData, ptr}; -pub use crate::{arm::*, arm64::*, m68k::*, mips::*, ppc::*, riscv::*, sparc::*, x86::*}; +pub use crate::{ + arm::*, arm64::*, m68k::*, mips::*, ppc::*, riscv::*, sparc::*, unicorn_const::*, x86::*, +}; +use alloc::{boxed::Box, rc::Rc, vec::Vec}; +use core::{cell::UnsafeCell, ptr}; use ffi::uc_handle; use libc::c_void; use unicorn_const::{uc_error, Arch, HookType, MemRegion, MemType, Mode, Permission, Query}; @@ -69,8 +77,7 @@ impl Drop for Context { } } -/// A Unicorn emulator instance. -pub struct Unicorn<'a, D: 'a> { +pub struct UnicornInner<'a, D> { pub uc: uc_handle, pub arch: Arch, /// to keep ownership over the hook for this uc instance's lifetime @@ -78,6 +85,21 @@ pub struct Unicorn<'a, D: 'a> { pub data: D, } +/// Drop UC +impl<'a, D> Drop for UnicornInner<'a, D> { + fn drop(&mut self) { + if !self.uc.is_null() { + unsafe { ffi::uc_close(self.uc) }; + } + self.uc = ptr::null_mut(); + } +} + +/// A Unicorn emulator instance. +pub struct Unicorn<'a, D: 'a> { + inner: Rc>>, +} + impl<'a> Unicorn<'a, ()> { /// Create a new instance of the unicorn engine for the specified architecture /// and hardware mode. @@ -93,14 +115,16 @@ where /// Create a new instance of the unicorn engine for the specified architecture /// and hardware mode. pub fn new_with_data(arch: Arch, mode: Mode, data: D) -> Result, uc_error> { - let mut handle = std::ptr::null_mut(); + let mut handle = core::ptr::null_mut(); let err = unsafe { ffi::uc_open(arch, mode, &mut handle) }; if err == uc_error::OK { Ok(Unicorn { - uc: handle, - arch, - data, - hooks: vec![], + inner: Rc::new(UnsafeCell::from(UnicornInner { + uc: handle, + arch, + data, + hooks: vec![], + })), }) } else { Err(err) @@ -108,50 +132,51 @@ where } } -/// Drop UC -impl<'a, D> Drop for Unicorn<'a, D> { - fn drop(&mut self) { - if !self.uc.is_null() { - unsafe { ffi::uc_close(self.uc) }; - } - self.uc = ptr::null_mut(); +impl<'a, D> core::fmt::Debug for Unicorn<'a, D> { + fn fmt(&self, formatter: &mut core::fmt::Formatter) -> core::fmt::Result { + write!(formatter, "Unicorn {{ uc: {:p} }}", self.inner().uc) } } -impl<'a, D> std::fmt::Debug for Unicorn<'a, D> { - fn fmt(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result { - write!(formatter, "Unicorn {{ uc: {:p} }}", self.uc) +impl<'a, D> Unicorn<'a, D> { + fn inner(&self) -> &UnicornInner<'a, D> { + unsafe { self.inner.get().as_ref().unwrap() } + } + + fn inner_mut(&mut self) -> &mut UnicornInner<'a, D> { + unsafe { self.inner.get().as_mut().unwrap() } } -} -impl<'a, D> Unicorn<'a, D> { /// Return whatever data was passed during initialization. /// /// For an example, have a look at `utils::init_emu_with_heap` where /// a struct is passed which is used for a custom allocator. + #[must_use] pub fn get_data(&self) -> &D { - &self.data + &self.inner().data } /// Return a mutable reference to whatever data was passed during initialization. + #[must_use] pub fn get_data_mut(&mut self) -> &mut D { - &mut self.data + &mut self.inner_mut().data } /// Return the architecture of the current emulator. + #[must_use] pub fn get_arch(&self) -> Arch { - self.arch + self.inner().arch } /// Returns a vector with the memory regions that are mapped in the emulator. pub fn mem_regions(&self) -> Result, uc_error> { let mut nb_regions: u32 = 0; - let p_regions: *const MemRegion = std::ptr::null_mut(); - let err = unsafe { ffi::uc_mem_regions(self.uc, &p_regions, &mut nb_regions) }; + let p_regions: *const MemRegion = core::ptr::null_mut(); + let err = unsafe { ffi::uc_mem_regions(self.inner().uc, &p_regions, &mut nb_regions) }; if err == uc_error::OK { let mut regions = Vec::new(); for i in 0..nb_regions { - regions.push(unsafe { std::mem::transmute_copy(&*p_regions.add(i as usize)) }); + regions.push(unsafe { core::mem::transmute_copy(&*p_regions.add(i as usize)) }); } unsafe { libc::free(p_regions as _) }; Ok(regions) @@ -162,7 +187,8 @@ impl<'a, D> Unicorn<'a, D> { /// Read a range of bytes from memory at the specified address. pub fn mem_read(&self, address: u64, buf: &mut [u8]) -> Result<(), uc_error> { - let err = unsafe { ffi::uc_mem_read(self.uc, address, buf.as_mut_ptr(), buf.len()) }; + let err = + unsafe { ffi::uc_mem_read(self.inner().uc, address, buf.as_mut_ptr(), buf.len()) }; if err == uc_error::OK { Ok(()) } else { @@ -173,7 +199,7 @@ impl<'a, D> Unicorn<'a, D> { /// Return a range of bytes from memory at the specified address as vector. pub fn mem_read_as_vec(&self, address: u64, size: usize) -> Result, uc_error> { let mut buf = vec![0; size]; - let err = unsafe { ffi::uc_mem_read(self.uc, address, buf.as_mut_ptr(), size) }; + let err = unsafe { ffi::uc_mem_read(self.inner().uc, address, buf.as_mut_ptr(), size) }; if err == uc_error::OK { Ok(buf) } else { @@ -182,7 +208,8 @@ impl<'a, D> Unicorn<'a, D> { } pub fn mem_write(&mut self, address: u64, bytes: &[u8]) -> Result<(), uc_error> { - let err = unsafe { ffi::uc_mem_write(self.uc, address, bytes.as_ptr(), bytes.len()) }; + let err = + unsafe { ffi::uc_mem_write(self.inner().uc, address, bytes.as_ptr(), bytes.len()) }; if err == uc_error::OK { Ok(()) } else { @@ -210,7 +237,7 @@ impl<'a, D> Unicorn<'a, D> { perms: Permission, ptr: *mut c_void, ) -> Result<(), uc_error> { - let err = ffi::uc_mem_map_ptr(self.uc, address, size, perms.bits(), ptr); + let err = ffi::uc_mem_map_ptr(self.inner().uc, address, size, perms.bits(), ptr); if err == uc_error::OK { Ok(()) } else { @@ -228,7 +255,7 @@ impl<'a, D> Unicorn<'a, D> { size: libc::size_t, perms: Permission, ) -> Result<(), uc_error> { - let err = unsafe { ffi::uc_mem_map(self.uc, address, size, perms.bits()) }; + let err = unsafe { ffi::uc_mem_map(self.inner().uc, address, size, perms.bits()) }; if err == uc_error::OK { Ok(()) } else { @@ -241,7 +268,7 @@ impl<'a, D> Unicorn<'a, D> { /// `address` must be aligned to 4kb or this will return `Error::ARG`. /// `size` must be a multiple of 4kb or this will return `Error::ARG`. pub fn mem_unmap(&mut self, address: u64, size: libc::size_t) -> Result<(), uc_error> { - let err = unsafe { ffi::uc_mem_unmap(self.uc, address, size) }; + let err = unsafe { ffi::uc_mem_unmap(self.inner().uc, address, size) }; if err == uc_error::OK { Ok(()) } else { @@ -259,7 +286,7 @@ impl<'a, D> Unicorn<'a, D> { size: libc::size_t, perms: Permission, ) -> Result<(), uc_error> { - let err = unsafe { ffi::uc_mem_protect(self.uc, address, size, perms.bits()) }; + let err = unsafe { ffi::uc_mem_protect(self.inner().uc, address, size, perms.bits()) }; if err == uc_error::OK { Ok(()) } else { @@ -269,7 +296,8 @@ impl<'a, D> Unicorn<'a, D> { /// Write an unsigned value from a register. pub fn reg_write>(&mut self, regid: T, value: u64) -> Result<(), uc_error> { - let err = unsafe { ffi::uc_reg_write(self.uc, regid.into(), &value as *const _ as _) }; + let err = + unsafe { ffi::uc_reg_write(self.inner().uc, regid.into(), &value as *const _ as _) }; if err == uc_error::OK { Ok(()) } else { @@ -282,7 +310,7 @@ impl<'a, D> Unicorn<'a, D> { /// The user has to make sure that the buffer length matches the register size. /// This adds support for registers >64 bit (GDTR/IDTR, XMM, YMM, ZMM, ST (x86); Q, V (arm64)). pub fn reg_write_long>(&self, regid: T, value: &[u8]) -> Result<(), uc_error> { - let err = unsafe { ffi::uc_reg_write(self.uc, regid.into(), value.as_ptr() as _) }; + let err = unsafe { ffi::uc_reg_write(self.inner().uc, regid.into(), value.as_ptr() as _) }; if err == uc_error::OK { Ok(()) } else { @@ -295,7 +323,8 @@ impl<'a, D> Unicorn<'a, D> { /// Not to be used with registers larger than 64 bit. pub fn reg_read>(&self, regid: T) -> Result { let mut value: u64 = 0; - let err = unsafe { ffi::uc_reg_read(self.uc, regid.into(), &mut value as *mut u64 as _) }; + let err = + unsafe { ffi::uc_reg_read(self.inner().uc, regid.into(), &mut value as *mut u64 as _) }; if err == uc_error::OK { Ok(value) } else { @@ -349,7 +378,7 @@ impl<'a, D> Unicorn<'a, D> { return Err(uc_error::ARCH); } - err = unsafe { ffi::uc_reg_read(self.uc, curr_reg_id, value.as_mut_ptr() as _) }; + err = unsafe { ffi::uc_reg_read(self.inner().uc, curr_reg_id, value.as_mut_ptr() as _) }; if err == uc_error::OK { boxed = value.into_boxed_slice(); @@ -362,7 +391,8 @@ impl<'a, D> Unicorn<'a, D> { /// Read a signed 32-bit value from a register. pub fn reg_read_i32>(&self, regid: T) -> Result { let mut value: i32 = 0; - let err = unsafe { ffi::uc_reg_read(self.uc, regid.into(), &mut value as *mut i32 as _) }; + let err = + unsafe { ffi::uc_reg_read(self.inner().uc, regid.into(), &mut value as *mut i32 as _) }; if err == uc_error::OK { Ok(value) } else { @@ -380,15 +410,17 @@ impl<'a, D> Unicorn<'a, D> { where F: FnMut(&mut crate::Unicorn, u64, u32) + 'a, { - let mut hook_ptr = std::ptr::null_mut(); + let mut hook_ptr = core::ptr::null_mut(); let mut user_data = Box::new(ffi::UcHook { callback, - phantom: PhantomData::<&D>, + uc: Unicorn { + inner: self.inner.clone(), + }, }); let err = unsafe { ffi::uc_hook_add( - self.uc, + self.inner().uc, &mut hook_ptr, HookType::CODE, ffi::code_hook_proxy:: as _, @@ -398,7 +430,7 @@ impl<'a, D> Unicorn<'a, D> { ) }; if err == uc_error::OK { - self.hooks.push((hook_ptr, user_data)); + self.inner_mut().hooks.push((hook_ptr, user_data)); Ok(hook_ptr) } else { Err(err) @@ -410,15 +442,17 @@ impl<'a, D> Unicorn<'a, D> { where F: FnMut(&mut Unicorn, u64, u32), { - let mut hook_ptr = std::ptr::null_mut(); + let mut hook_ptr = core::ptr::null_mut(); let mut user_data = Box::new(ffi::UcHook { callback, - phantom: PhantomData::<&D>, + uc: Unicorn { + inner: self.inner.clone(), + }, }); let err = unsafe { ffi::uc_hook_add( - self.uc, + self.inner().uc, &mut hook_ptr, HookType::BLOCK, ffi::block_hook_proxy:: as _, @@ -428,7 +462,7 @@ impl<'a, D> Unicorn<'a, D> { ) }; if err == uc_error::OK { - self.hooks.push((hook_ptr, user_data)); + self.inner_mut().hooks.push((hook_ptr, user_data)); Ok(hook_ptr) } else { @@ -451,15 +485,17 @@ impl<'a, D> Unicorn<'a, D> { return Err(uc_error::ARG); } - let mut hook_ptr = std::ptr::null_mut(); + let mut hook_ptr = core::ptr::null_mut(); let mut user_data = Box::new(ffi::UcHook { callback, - phantom: PhantomData::<&D>, + uc: Unicorn { + inner: self.inner.clone(), + }, }); let err = unsafe { ffi::uc_hook_add( - self.uc, + self.inner().uc, &mut hook_ptr, hook_type, ffi::mem_hook_proxy:: as _, @@ -469,7 +505,7 @@ impl<'a, D> Unicorn<'a, D> { ) }; if err == uc_error::OK { - self.hooks.push((hook_ptr, user_data)); + self.inner_mut().hooks.push((hook_ptr, user_data)); Ok(hook_ptr) } else { @@ -482,15 +518,17 @@ impl<'a, D> Unicorn<'a, D> { where F: FnMut(&mut Unicorn, u32), { - let mut hook_ptr = std::ptr::null_mut(); + let mut hook_ptr = core::ptr::null_mut(); let mut user_data = Box::new(ffi::UcHook { callback, - phantom: PhantomData::<&D>, + uc: Unicorn { + inner: self.inner.clone(), + }, }); let err = unsafe { ffi::uc_hook_add( - self.uc, + self.inner().uc, &mut hook_ptr, HookType::INTR, ffi::intr_hook_proxy:: as _, @@ -500,7 +538,7 @@ impl<'a, D> Unicorn<'a, D> { ) }; if err == uc_error::OK { - self.hooks.push((hook_ptr, user_data)); + self.inner_mut().hooks.push((hook_ptr, user_data)); Ok(hook_ptr) } else { @@ -511,17 +549,19 @@ impl<'a, D> Unicorn<'a, D> { /// Add hook for x86 IN instruction. pub fn add_insn_in_hook(&mut self, callback: F) -> Result where - F: FnMut(&mut Unicorn, u32, usize) + 'a, + F: FnMut(&mut Unicorn, u32, usize), { - let mut hook_ptr = std::ptr::null_mut(); + let mut hook_ptr = core::ptr::null_mut(); let mut user_data = Box::new(ffi::UcHook { callback, - phantom: PhantomData::<&D>, + uc: Unicorn { + inner: self.inner.clone(), + }, }); let err = unsafe { ffi::uc_hook_add( - self.uc, + self.inner().uc, &mut hook_ptr, HookType::INSN, ffi::insn_in_hook_proxy:: as _, @@ -532,7 +572,7 @@ impl<'a, D> Unicorn<'a, D> { ) }; if err == uc_error::OK { - self.hooks.push((hook_ptr, user_data)); + self.inner_mut().hooks.push((hook_ptr, user_data)); Ok(hook_ptr) } else { @@ -543,17 +583,19 @@ impl<'a, D> Unicorn<'a, D> { /// Add hook for x86 OUT instruction. pub fn add_insn_out_hook(&mut self, callback: F) -> Result where - F: FnMut(&mut Unicorn, u32, usize, u32) + 'a, + F: FnMut(&mut Unicorn, u32, usize, u32), { - let mut hook_ptr = std::ptr::null_mut(); + let mut hook_ptr = core::ptr::null_mut(); let mut user_data = Box::new(ffi::UcHook { callback, - phantom: PhantomData::<&D>, + uc: Unicorn { + inner: self.inner.clone(), + }, }); let err = unsafe { ffi::uc_hook_add( - self.uc, + self.inner().uc, &mut hook_ptr, HookType::INSN, ffi::insn_out_hook_proxy:: as _, @@ -564,7 +606,7 @@ impl<'a, D> Unicorn<'a, D> { ) }; if err == uc_error::OK { - self.hooks.push((hook_ptr, user_data)); + self.inner_mut().hooks.push((hook_ptr, user_data)); Ok(hook_ptr) } else { @@ -583,15 +625,17 @@ impl<'a, D> Unicorn<'a, D> { where F: FnMut(&mut Unicorn) + 'a, { - let mut hook_ptr = std::ptr::null_mut(); + let mut hook_ptr = core::ptr::null_mut(); let mut user_data = Box::new(ffi::UcHook { callback, - phantom: PhantomData::<&D>, + uc: Unicorn { + inner: self.inner.clone(), + }, }); let err = unsafe { ffi::uc_hook_add( - self.uc, + self.inner().uc, &mut hook_ptr, HookType::INSN, ffi::insn_sys_hook_proxy:: as _, @@ -602,7 +646,7 @@ impl<'a, D> Unicorn<'a, D> { ) }; if err == uc_error::OK { - self.hooks.push((hook_ptr, user_data)); + self.inner_mut().hooks.push((hook_ptr, user_data)); Ok(hook_ptr) } else { @@ -617,10 +661,11 @@ impl<'a, D> Unicorn<'a, D> { let err: uc_error; // drop the hook - self.hooks + self.inner_mut() + .hooks .retain(|(hook_ptr, _hook_impl)| hook_ptr != &hook); - err = unsafe { ffi::uc_hook_del(self.uc, hook) }; + err = unsafe { ffi::uc_hook_del(self.inner().uc, hook) }; if err == uc_error::OK { Ok(()) @@ -634,7 +679,7 @@ impl<'a, D> Unicorn<'a, D> { /// To be populated via `context_save`. pub fn context_alloc(&self) -> Result { let mut empty_context: ffi::uc_context = ptr::null_mut(); - let err = unsafe { ffi::uc_context_alloc(self.uc, &mut empty_context) }; + let err = unsafe { ffi::uc_context_alloc(self.inner().uc, &mut empty_context) }; if err == uc_error::OK { Ok(Context { context: empty_context, @@ -646,7 +691,7 @@ impl<'a, D> Unicorn<'a, D> { /// Save current Unicorn context to previously allocated Context struct. pub fn context_save(&self, context: &mut Context) -> Result<(), uc_error> { - let err = unsafe { ffi::uc_context_save(self.uc, context.context) }; + let err = unsafe { ffi::uc_context_save(self.inner().uc, context.context) }; if err == uc_error::OK { Ok(()) } else { @@ -661,11 +706,11 @@ impl<'a, D> Unicorn<'a, D> { /// individually to avoid unnecessary allocations. pub fn context_init(&self) -> Result { let mut new_context: ffi::uc_context = ptr::null_mut(); - let err = unsafe { ffi::uc_context_alloc(self.uc, &mut new_context) }; + let err = unsafe { ffi::uc_context_alloc(self.inner().uc, &mut new_context) }; if err != uc_error::OK { return Err(err); } - let err = unsafe { ffi::uc_context_save(self.uc, new_context) }; + let err = unsafe { ffi::uc_context_save(self.inner().uc, new_context) }; if err == uc_error::OK { Ok(Context { context: new_context, @@ -682,7 +727,7 @@ impl<'a, D> Unicorn<'a, D> { /// internal metadata. Contexts may not be shared across engine instances with /// differing arches or modes. Memory has to be restored manually, if needed. pub fn context_restore(&self, context: &Context) -> Result<(), uc_error> { - let err = unsafe { ffi::uc_context_restore(self.uc, context.context) }; + let err = unsafe { ffi::uc_context_restore(self.inner().uc, context.context) }; if err == uc_error::OK { Ok(()) } else { @@ -704,9 +749,7 @@ impl<'a, D> Unicorn<'a, D> { count: usize, ) -> Result<(), uc_error> { unsafe { - ffi::uc_set_data_ptr(self.uc, self as *mut _ as *mut _); - let err = ffi::uc_emu_start(self.uc, begin, until, timeout, count as _); - ffi::uc_set_data_ptr(self.uc, ptr::null_mut()); + let err = ffi::uc_emu_start(self.inner().uc, begin, until, timeout, count as _); if err == uc_error::OK { Ok(()) } else { @@ -720,7 +763,7 @@ impl<'a, D> Unicorn<'a, D> { /// This is usually called from callback function in hooks. /// NOTE: For now, this will stop the execution only after the current block. pub fn emu_stop(&mut self) -> Result<(), uc_error> { - let err = unsafe { ffi::uc_emu_stop(self.uc) }; + let err = unsafe { ffi::uc_emu_stop(self.inner().uc) }; if err == uc_error::OK { Ok(()) } else { @@ -733,7 +776,7 @@ impl<'a, D> Unicorn<'a, D> { /// supported: `MODE`, `PAGE_SIZE`, `ARCH` pub fn query(&self, query: Query) -> Result { let mut result: libc::size_t = Default::default(); - let err = unsafe { ffi::uc_query(self.uc, query, &mut result) }; + let err = unsafe { ffi::uc_query(self.inner().uc, query, &mut result) }; if err == uc_error::OK { Ok(result) } else { @@ -754,7 +797,7 @@ impl<'a, D> Unicorn<'a, D> { Arch::M68K => RegisterM68K::PC as i32, Arch::PPC => RegisterPPC::PC as i32, Arch::RISCV => RegisterRISCV::PC as i32, - _ => panic!("Arch pc not yet know to the unicorn rust bindings"), + Arch::MAX => panic!("Illegal Arch specified"), }; self.reg_read(reg) } @@ -772,7 +815,7 @@ impl<'a, D> Unicorn<'a, D> { Arch::M68K => RegisterM68K::PC as i32, Arch::PPC => RegisterPPC::PC as i32, Arch::RISCV => RegisterRISCV::PC as i32, - _ => panic!("Arch not yet known to the unicorn rust bindings"), + Arch::MAX => panic!("Illegal Arch specified"), }; self.reg_write(reg, value) } diff --git a/bindings/rust/tests/unicorn.rs b/bindings/rust/tests/unicorn.rs index 039569bcbf..98ca78e1fb 100644 --- a/bindings/rust/tests/unicorn.rs +++ b/bindings/rust/tests/unicorn.rs @@ -1,9 +1,11 @@ -use std::cell::RefCell; -use std::rc::Rc; +extern crate alloc; + +use alloc::rc::Rc; +use core::cell::RefCell; use unicorn_engine::unicorn_const::{ uc_error, Arch, HookType, MemType, Mode, Permission, SECOND_SCALE, }; -use unicorn_engine::{InsnSysX86, RegisterARM, RegisterMIPS, RegisterPPC, RegisterX86}; +use unicorn_engine::{InsnSysX86, RegisterARM, RegisterMIPS, RegisterPPC, RegisterX86, Unicorn}; pub static X86_REGISTERS: [RegisterX86; 125] = [ RegisterX86::AH, @@ -32,7 +34,6 @@ pub static X86_REGISTERS: [RegisterX86; 125] = [ RegisterX86::EDX, RegisterX86::EFLAGS, RegisterX86::EIP, - RegisterX86::EIZ, RegisterX86::ES, RegisterX86::ESI, RegisterX86::ESP, @@ -47,7 +48,6 @@ pub static X86_REGISTERS: [RegisterX86; 125] = [ RegisterX86::RDI, RegisterX86::RDX, RegisterX86::RIP, - RegisterX86::RIZ, RegisterX86::RSI, RegisterX86::RSP, RegisterX86::SI, @@ -60,17 +60,7 @@ pub static X86_REGISTERS: [RegisterX86; 125] = [ RegisterX86::CR2, RegisterX86::CR3, RegisterX86::CR4, - RegisterX86::CR5, - RegisterX86::CR6, - RegisterX86::CR7, RegisterX86::CR8, - RegisterX86::CR9, - RegisterX86::CR10, - RegisterX86::CR11, - RegisterX86::CR12, - RegisterX86::CR13, - RegisterX86::CR14, - RegisterX86::CR15, RegisterX86::DR0, RegisterX86::DR1, RegisterX86::DR2, @@ -79,14 +69,6 @@ pub static X86_REGISTERS: [RegisterX86; 125] = [ RegisterX86::DR5, RegisterX86::DR6, RegisterX86::DR7, - RegisterX86::DR8, - RegisterX86::DR9, - RegisterX86::DR10, - RegisterX86::DR11, - RegisterX86::DR12, - RegisterX86::DR13, - RegisterX86::DR14, - RegisterX86::DR15, RegisterX86::FP0, RegisterX86::FP1, RegisterX86::FP2, @@ -159,8 +141,8 @@ fn emulate_x86() { let mut emu = unicorn_engine::Unicorn::new(Arch::X86, Mode::MODE_32) .expect("failed to initialize unicorn instance"); - assert_eq!(emu.reg_write(RegisterX86::EAX as i32, 123), Ok(())); - assert_eq!(emu.reg_read(RegisterX86::EAX as i32), Ok(123)); + assert_eq!(emu.reg_write(RegisterX86::EAX, 123), Ok(())); + assert_eq!(emu.reg_read(RegisterX86::EAX), Ok(123)); // Attempt to write to memory before mapping it. assert_eq!( @@ -175,8 +157,8 @@ fn emulate_x86() { Ok(x86_code32.clone()) ); - assert_eq!(emu.reg_write(RegisterX86::ECX as i32, 10), Ok(())); - assert_eq!(emu.reg_write(RegisterX86::EDX as i32, 50), Ok(())); + assert_eq!(emu.reg_write(RegisterX86::ECX, 10), Ok(())); + assert_eq!(emu.reg_write(RegisterX86::EDX, 50), Ok(())); assert_eq!( emu.emu_start( @@ -187,8 +169,8 @@ fn emulate_x86() { ), Ok(()) ); - assert_eq!(emu.reg_read(RegisterX86::ECX as i32), Ok(11)); - assert_eq!(emu.reg_read(RegisterX86::EDX as i32), Ok(49)); + assert_eq!(emu.reg_read(RegisterX86::ECX), Ok(11)); + assert_eq!(emu.reg_read(RegisterX86::EDX), Ok(49)); } #[test] @@ -266,17 +248,26 @@ fn x86_mem_callback() { let expects = vec![ MemExpectation(MemType::WRITE, 0x2000, 4, 0xdeadbeef), MemExpectation(MemType::READ_UNMAPPED, 0x10000, 4, 0), + MemExpectation(MemType::READ, 0x10000, 4, 0), ]; let mems: Vec = Vec::new(); let mems_cell = Rc::new(RefCell::new(mems)); let callback_mems = mems_cell.clone(); - let callback = - move |_: &mut Unicorn<'_, ()>, mem_type: MemType, address: u64, size: usize, value: i64| { - let mut mems = callback_mems.borrow_mut(); - mems.push(MemExpectation(mem_type, address, size, value)); - true - }; + let callback = move |uc: &mut Unicorn<'_, ()>, + mem_type: MemType, + address: u64, + size: usize, + value: i64| { + let mut mems = callback_mems.borrow_mut(); + + mems.push(MemExpectation(mem_type, address, size, value)); + + if mem_type == MemType::READ_UNMAPPED { + uc.mem_map(address, 0x1000, Permission::ALL).unwrap(); + } + true + }; // mov eax, 0xdeadbeef; // mov [0x2000], eax; @@ -291,9 +282,9 @@ fn x86_mem_callback() { assert_eq!(emu.mem_write(0x1000, &x86_code32), Ok(())); let hook = emu - .add_mem_hook(HookType::MEM_ALL, 0, std::u64::MAX, callback) + .add_mem_hook(HookType::MEM_ALL, 0, u64::MAX, callback) .expect("failed to add memory hook"); - assert_eq!(emu.reg_write(RegisterX86::EAX as i32, 0x123), Ok(())); + assert_eq!(emu.reg_write(RegisterX86::EAX, 0x123), Ok(())); assert_eq!( emu.emu_start( 0x1000, @@ -301,7 +292,7 @@ fn x86_mem_callback() { 10 * SECOND_SCALE, 0x1000 ), - Err(uc_error::READ_UNMAPPED) + Ok(()) ); assert_eq!(expects, *mems_cell.borrow()); @@ -316,7 +307,7 @@ fn x86_insn_in_callback() { let insn_cell = Rc::new(RefCell::new(InsnInExpectation(0, 0))); let callback_insn = insn_cell.clone(); - let callback = move |_: &mut Unicorn<'_, ()>, port: u32, size: usize| { + let callback = move |_: &mut Unicorn<()>, port: u32, size: usize| { *callback_insn.borrow_mut() = InsnInExpectation(port, size); }; @@ -390,7 +381,7 @@ fn x86_insn_sys_callback() { let callback_insn = insn_cell.clone(); let callback = move |uc: &mut Unicorn<'_, ()>| { println!("!!!!"); - let rax = uc.reg_read(RegisterX86::RAX as i32).unwrap(); + let rax = uc.reg_read(RegisterX86::RAX).unwrap(); *callback_insn.borrow_mut() = InsnSysExpectation(rax); }; @@ -427,8 +418,8 @@ fn emulate_arm() { let mut emu = unicorn_engine::Unicorn::new(Arch::ARM, Mode::THUMB) .expect("failed to initialize unicorn instance"); - assert_eq!(emu.reg_write(RegisterARM::R1 as i32, 123), Ok(())); - assert_eq!(emu.reg_read(RegisterARM::R1 as i32), Ok(123)); + assert_eq!(emu.reg_write(RegisterARM::R1, 123), Ok(())); + assert_eq!(emu.reg_read(RegisterARM::R1), Ok(123)); // Attempt to write to memory before mapping it. assert_eq!( @@ -443,8 +434,8 @@ fn emulate_arm() { Ok(arm_code32.clone()) ); - assert_eq!(emu.reg_write(RegisterARM::SP as i32, 12), Ok(())); - assert_eq!(emu.reg_write(RegisterARM::R0 as i32, 10), Ok(())); + assert_eq!(emu.reg_write(RegisterARM::SP, 12), Ok(())); + assert_eq!(emu.reg_write(RegisterARM::R0, 10), Ok(())); // ARM checks the least significant bit of the address to know // if the code is in Thumb mode. @@ -457,8 +448,8 @@ fn emulate_arm() { ), Ok(()) ); - assert_eq!(emu.reg_read(RegisterARM::SP as i32), Ok(0)); - assert_eq!(emu.reg_read(RegisterARM::R0 as i32), Ok(10)); + assert_eq!(emu.reg_read(RegisterARM::SP), Ok(0)); + assert_eq!(emu.reg_read(RegisterARM::R0), Ok(10)); } #[test] @@ -473,7 +464,7 @@ fn emulate_mips() { emu.mem_read_as_vec(0x1000, mips_code32.len()), Ok(mips_code32.clone()) ); - assert_eq!(emu.reg_write(RegisterMIPS::AT as i32, 0), Ok(())); + assert_eq!(emu.reg_write(RegisterMIPS::AT, 0), Ok(())); assert_eq!( emu.emu_start( 0x1000, @@ -483,14 +474,14 @@ fn emulate_mips() { ), Ok(()) ); - assert_eq!(emu.reg_read(RegisterMIPS::AT as i32), Ok(0x3456)); + assert_eq!(emu.reg_read(RegisterMIPS::AT), Ok(0x3456)); } #[test] fn emulate_ppc() { let ppc_code32 = vec![0x7F, 0x46, 0x1A, 0x14]; // add 26, 6, 3 - let mut emu = unicorn_engine::Unicorn::new(Arch::PPC, Mode::PPC32) + let mut emu = unicorn_engine::Unicorn::new(Arch::PPC, Mode::PPC32 | Mode::BIG_ENDIAN) .expect("failed to initialize unicorn instance"); assert_eq!(emu.mem_map(0x1000, 0x4000, Permission::ALL), Ok(())); assert_eq!(emu.mem_write(0x1000, &ppc_code32), Ok(())); @@ -498,8 +489,8 @@ fn emulate_ppc() { emu.mem_read_as_vec(0x1000, ppc_code32.len()), Ok(ppc_code32.clone()) ); - assert_eq!(emu.reg_write(RegisterPPC::GPR3 as i32, 42), Ok(())); - assert_eq!(emu.reg_write(RegisterPPC::GPR6 as i32, 1337), Ok(())); + assert_eq!(emu.reg_write(RegisterPPC::GPR3, 42), Ok(())); + assert_eq!(emu.reg_write(RegisterPPC::GPR6, 1337), Ok(())); assert_eq!( emu.emu_start( 0x1000, @@ -509,7 +500,7 @@ fn emulate_ppc() { ), Ok(()) ); - assert_eq!(emu.reg_read(RegisterPPC::GPR26 as i32), Ok(1379)); + assert_eq!(emu.reg_read(RegisterPPC::GPR26), Ok(1379)); } #[test] @@ -545,8 +536,8 @@ fn mem_map_ptr() { Ok(x86_code32.clone()) ); - assert_eq!(emu.reg_write(RegisterX86::ECX as i32, 10), Ok(())); - assert_eq!(emu.reg_write(RegisterX86::EDX as i32, 50), Ok(())); + assert_eq!(emu.reg_write(RegisterX86::ECX, 10), Ok(())); + assert_eq!(emu.reg_write(RegisterX86::EDX, 50), Ok(())); assert_eq!( emu.emu_start( @@ -557,8 +548,8 @@ fn mem_map_ptr() { ), Ok(()) ); - assert_eq!(emu.reg_read(RegisterX86::ECX as i32), Ok(11)); - assert_eq!(emu.reg_read(RegisterX86::EDX as i32), Ok(49)); + assert_eq!(emu.reg_read(RegisterX86::ECX), Ok(11)); + assert_eq!(emu.reg_read(RegisterX86::EDX), Ok(49)); assert_eq!(emu.mem_unmap(0x1000, 0x4000), Ok(())); // Use a Vec for the emulator memory. @@ -581,8 +572,8 @@ fn mem_map_ptr() { Ok(x86_code32.clone()) ); - assert_eq!(emu.reg_write(RegisterX86::ECX as i32, 10), Ok(())); - assert_eq!(emu.reg_write(RegisterX86::EDX as i32, 50), Ok(())); + assert_eq!(emu.reg_write(RegisterX86::ECX, 10), Ok(())); + assert_eq!(emu.reg_write(RegisterX86::EDX, 50), Ok(())); assert_eq!( emu.emu_start( @@ -593,18 +584,18 @@ fn mem_map_ptr() { ), Ok(()) ); - assert_eq!(emu.reg_read(RegisterX86::ECX as i32), Ok(11)); - assert_eq!(emu.reg_read(RegisterX86::EDX as i32), Ok(49)); + assert_eq!(emu.reg_read(RegisterX86::ECX), Ok(11)); + assert_eq!(emu.reg_read(RegisterX86::EDX), Ok(49)); assert_eq!(emu.mem_unmap(0x1000, 0x4000), Ok(())); } #[test] fn x86_context_save_and_restore() { - for mode in &[Mode::MODE_32, Mode::MODE_64] { - let x86_code: Vec = vec![ + for mode in [Mode::MODE_32, Mode::MODE_64] { + let x86_code = [ 0x48, 0xB8, 0xEF, 0xBE, 0xAD, 0xDE, 0x00, 0x00, 0x00, 0x00, 0x0F, 0x05, ]; - let mut emu = unicorn_engine::Unicorn::new(Arch::X86, *mode) + let mut emu = unicorn_engine::Unicorn::new(Arch::X86, mode) .expect("failed to initialize unicorn instance"); assert_eq!(emu.mem_map(0x1000, 0x4000, Permission::ALL), Ok(())); assert_eq!(emu.mem_write(0x1000, &x86_code), Ok(())); @@ -620,15 +611,12 @@ fn x86_context_save_and_restore() { let context = context.unwrap(); /* and create a new emulator, into which we will "restore" that context */ - let emu2 = unicorn_engine::Unicorn::new(Arch::X86, *mode) + let emu2 = unicorn_engine::Unicorn::new(Arch::X86, mode) .expect("failed to initialize unicorn instance"); assert_eq!(emu2.context_restore(&context), Ok(())); for register in X86_REGISTERS.iter() { println!("Testing register {:?}", register); - assert_eq!( - emu2.reg_read(*register as i32), - emu.reg_read(*register as i32) - ); + assert_eq!(emu2.reg_read(*register), emu.reg_read(*register)); } } } diff --git a/uc.c b/uc.c index e40901c242..ecb954ccbd 100644 --- a/uc.c +++ b/uc.c @@ -686,27 +686,6 @@ static void clear_deleted_hooks(uc_engine *uc) list_clear(&uc->hooks_to_del); } -// set a data ptr (for use in bindings) -UNICORN_EXPORT -uc_err uc_emu_set_data_ptr(uc_engine *uc, void *data) -{ - if (uc == NULL) { - return UC_ERR_ARG; - } - uc->data_ptr = data; -} - -// get a data ptr (for use in bindings) -UNICORN_EXPORT -void *uc_emu_get_data_ptr(uc_engine *uc, void *data) -{ - if (uc == NULL) { - return NULL; - } - return uc->data_ptr; -} - - UNICORN_EXPORT uc_err uc_emu_start(uc_engine *uc, uint64_t begin, uint64_t until, uint64_t timeout, size_t count) From a231440cf848d1aa3fe2782d3bf393d02987c781 Mon Sep 17 00:00:00 2001 From: Dominik Maier Date: Wed, 10 Nov 2021 04:14:56 +0100 Subject: [PATCH 09/12] reverted build.rs --- bindings/rust/build.rs | 174 ++++++++++++++++----------------------- bindings/rust/src/ffi.rs | 6 +- bindings/rust/src/lib.rs | 19 ++--- 3 files changed, 85 insertions(+), 114 deletions(-) diff --git a/bindings/rust/build.rs b/bindings/rust/build.rs index 8efb188744..195e9e0d0c 100644 --- a/bindings/rust/build.rs +++ b/bindings/rust/build.rs @@ -18,12 +18,7 @@ fn find_unicorn(unicorn_dir: &Path) -> Option { None } -fn out_dir() -> PathBuf { - let out_dir = env::var("OUT_DIR").unwrap(); - Path::new(&out_dir).to_path_buf() -} - -fn download_unicorn() -> PathBuf { +fn download_unicorn() -> Option { // https://docs.github.com/en/rest/reference/repos#download-a-repository-archive-tar let pkg_version; if let Ok(unicorn_version) = env::var("UNICORN_VERSION") { @@ -31,7 +26,7 @@ fn download_unicorn() -> PathBuf { } else { pkg_version = env::var("CARGO_PKG_VERSION").unwrap(); } - let out_dir = out_dir(); + let out_dir = PathBuf::from(env::var("OUT_DIR").unwrap()); let client = reqwest::blocking::Client::new(); let resp = client .get(format!( @@ -48,107 +43,85 @@ fn download_unicorn() -> PathBuf { let mut archive = Archive::new(tar); archive.unpack(&out_dir).unwrap(); - find_unicorn(&out_dir).unwrap() + match find_unicorn(&out_dir) { + Some(dir) => Some(String::from(out_dir.join(dir).to_str()?)), + None => None, + } } -#[allow(clippy::too_many_lines)] +#[allow(clippy::branches_sharing_code)] fn main() { let profile = env::var("PROFILE").unwrap(); - if let Some(unicorn_dir) = find_unicorn(&out_dir()) { - let rust_build_path = unicorn_dir.join("rust_build"); - println!( - "cargo:rustc-link-search={}", - rust_build_path.to_str().unwrap() - ); - println!( - "cargo:rustc-link-search={}", - rust_build_path.join("Debug").to_str().unwrap() - ); - println!( - "cargo:rustc-link-search={}", - rust_build_path.join("Release").to_str().unwrap() - ); - } else { - let unicorn_dir = if env::var("CI").is_ok() { - Path::new("..").join("..") + let unicorn_dir = download_unicorn().unwrap(); + + println!("cargo:rerun-if-changed={}", &unicorn_dir); + + // We don't use TARGET since we can't cross-build. + if env::consts::OS == "windows" { + // Windows + let mut cmd = Command::new("cmake"); + cmd.current_dir(&unicorn_dir) + .arg("-B") + .arg("rust_build") + .arg("-DUNICORN_BUILD_SHARED=off") + .arg("-G") + .arg("Visual Studio 16 2019"); + + if profile == "debug" { + cmd.arg("-DCMAKE_BUILD_TYPE=Debug"); } else { - println!("cargo:warning=Unicorn not found. Downloading..."); - download_unicorn() - }; + cmd.arg("-DCMAKE_BUILD_TYPE=Release"); + } + + cmd.output() + .expect("Fail to create build directory on Windows."); - let rust_build_path = unicorn_dir.join("rust_build"); + let mut platform = "x64"; + let mut conf = "Release"; + if std::mem::size_of::() == 4 { + platform = "Win32"; + } + if profile == "debug" { + conf = "Debug"; + } + Command::new("msbuild") + .current_dir(format!("{}/rust_build", &unicorn_dir)) + .arg("unicorn.sln") + .arg("-m") + .arg("-p:Platform=".to_owned() + platform) + .arg("-p:Configuration=".to_owned() + conf) + .output() + .expect("Fail to build unicorn on Win32."); + println!( + "cargo:rustc-link-search={}/rust_build/{}", + unicorn_dir, conf + ); + } else { + // Most Unix-like systems let mut cmd = Command::new("cmake"); + cmd.current_dir(&unicorn_dir) + .arg("-B") + .arg("rust_build") + .arg("-DUNICORN_BUILD_SHARED=off"); - // We don't use TARGET since we can't cross-build. - if env::consts::OS == "windows" { - // Windows - cmd.current_dir(&unicorn_dir) - .arg("-B") - .arg("rust_build") - .arg("-DUNICORN_BUILD_SHARED=off") - .arg("-G") - .arg("Visual Studio 16 2019"); - - if profile == "debug" { - cmd.arg("-DCMAKE_BUILD_TYPE=Debug"); - } else { - cmd.arg("-DCMAKE_BUILD_TYPE=Release"); - } - - cmd.output() - .expect("Fail to create build directory on Windows."); - - let mut platform = "x64"; - let mut conf = "Release"; - if std::mem::size_of::() == 4 { - platform = "Win32"; - } - if profile == "debug" { - conf = "Debug"; - } - - Command::new("msbuild") - .current_dir(&rust_build_path) - .arg("unicorn.sln") - .arg("-m") - .arg("-p:Platform=".to_owned() + platform) - .arg("-p:Configuration=".to_owned() + conf) - .output() - .expect("Fail to build unicorn on Win32."); - println!( - "cargo:rustc-link-search={}", - rust_build_path.join(conf).to_str().unwrap() - ); + if profile == "debug" { + cmd.arg("-DCMAKE_BUILD_TYPE=Debug"); } else { - // Most Unix-like systems - let mut cmd = Command::new("cmake"); - cmd.current_dir(&unicorn_dir) - .arg("-B") - .arg("rust_build") - .arg("-DUNICORN_BUILD_SHARED=off"); - - if profile == "debug" { - cmd.arg("-DCMAKE_BUILD_TYPE=Debug"); - } else { - cmd.arg("-DCMAKE_BUILD_TYPE=Release"); - } - - cmd.output() - .expect("Fail to create build directory on *nix."); - - Command::new("make") - .current_dir(&rust_build_path) - .arg("-j6") - .output() - .expect("Fail to build unicorn on *nix."); - - println!( - "cargo:rustc-link-search={}", - rust_build_path.to_str().unwrap() - ); + cmd.arg("-DCMAKE_BUILD_TYPE=Release"); } + + cmd.output() + .expect("Fail to create build directory on *nix."); + + Command::new("make") + .current_dir(format!("{}/rust_build", &unicorn_dir)) + .arg("-j6") + .output() + .expect("Fail to build unicorn on *nix."); + + println!("cargo:rustc-link-search={}/rust_build", unicorn_dir); } // This is a workaround for Unicorn static link since libunicorn.a is also linked again lib*-softmmu.a. @@ -157,7 +130,7 @@ fn main() { // // Lazymio(@wtdcode): Why do I stick to static link? See: https://github.com/rust-lang/cargo/issues/5077 println!("cargo:rustc-link-lib=unicorn"); - for arch in &[ + for arch in [ "x86_64", "arm", "armeb", @@ -174,11 +147,10 @@ fn main() { "m68k", "ppc", "ppc64", - ] { + ] + .iter() + { println!("cargo:rustc-link-lib={}-softmmu", arch); } println!("cargo:rustc-link-lib=unicorn-common"); - - println!("cargo:rerun-if-changed=build.rs"); - println!("cargo:rerun-if-changed=src"); } diff --git a/bindings/rust/src/ffi.rs b/bindings/rust/src/ffi.rs index 300e12206b..2cf7faf93e 100644 --- a/bindings/rust/src/ffi.rs +++ b/bindings/rust/src/ffi.rs @@ -80,12 +80,12 @@ extern "C" { pub struct UcHook<'a, D: 'a, F: 'a> { pub callback: F, - pub uc: Unicorn<'a, D>, + pub uc: Unicorn, } -pub trait IsUcHook<'a> {} +pub trait IsUcHook {} -impl<'a, D, F> IsUcHook<'a> for UcHook<'a, D, F> {} +impl<'a, D, F> IsUcHook for UcHook<'a, D, F> {} pub extern "C" fn code_hook_proxy( uc: uc_handle, diff --git a/bindings/rust/src/lib.rs b/bindings/rust/src/lib.rs index 7b5c35c64c..f96b795f82 100644 --- a/bindings/rust/src/lib.rs +++ b/bindings/rust/src/lib.rs @@ -77,16 +77,15 @@ impl Drop for Context { } } -pub struct UnicornInner<'a, D> { +pub struct UnicornInner { pub uc: uc_handle, pub arch: Arch, /// to keep ownership over the hook for this uc instance's lifetime - pub hooks: Vec<(ffi::uc_hook, Box + 'a>)>, - pub data: D, + pub hooks: Vec<(ffi::uc_hook, Box)>, } /// Drop UC -impl<'a, D> Drop for UnicornInner<'a, D> { +impl Drop for UnicornInner { fn drop(&mut self) { if !self.uc.is_null() { unsafe { ffi::uc_close(self.uc) }; @@ -96,25 +95,25 @@ impl<'a, D> Drop for UnicornInner<'a, D> { } /// A Unicorn emulator instance. -pub struct Unicorn<'a, D: 'a> { - inner: Rc>>, +pub struct Unicorn { + inner: Rc>>, } -impl<'a> Unicorn<'a, ()> { +impl<'a> Unicorn<'a> { /// Create a new instance of the unicorn engine for the specified architecture /// and hardware mode. - pub fn new(arch: Arch, mode: Mode) -> Result, uc_error> { + pub fn new(arch: Arch, mode: Mode) -> Result, uc_error> { Self::new_with_data(arch, mode, ()) } } -impl<'a, D> Unicorn<'a, D> +impl<'a, D> Unicorn<'a> where D: 'a, { /// Create a new instance of the unicorn engine for the specified architecture /// and hardware mode. - pub fn new_with_data(arch: Arch, mode: Mode, data: D) -> Result, uc_error> { + pub fn new_with_data(arch: Arch, mode: Mode, data: D) -> Result, uc_error> { let mut handle = core::ptr::null_mut(); let err = unsafe { ffi::uc_open(arch, mode, &mut handle) }; if err == uc_error::OK { From 7873e6074087f6b66cc4fc78054e8f8653333a92 Mon Sep 17 00:00:00 2001 From: Dominik Maier Date: Wed, 10 Nov 2021 04:21:30 +0100 Subject: [PATCH 10/12] reverted unfinished commit --- bindings/rust/src/ffi.rs | 6 +++--- bindings/rust/src/lib.rs | 19 ++++++++++--------- 2 files changed, 13 insertions(+), 12 deletions(-) diff --git a/bindings/rust/src/ffi.rs b/bindings/rust/src/ffi.rs index 2cf7faf93e..300e12206b 100644 --- a/bindings/rust/src/ffi.rs +++ b/bindings/rust/src/ffi.rs @@ -80,12 +80,12 @@ extern "C" { pub struct UcHook<'a, D: 'a, F: 'a> { pub callback: F, - pub uc: Unicorn, + pub uc: Unicorn<'a, D>, } -pub trait IsUcHook {} +pub trait IsUcHook<'a> {} -impl<'a, D, F> IsUcHook for UcHook<'a, D, F> {} +impl<'a, D, F> IsUcHook<'a> for UcHook<'a, D, F> {} pub extern "C" fn code_hook_proxy( uc: uc_handle, diff --git a/bindings/rust/src/lib.rs b/bindings/rust/src/lib.rs index f96b795f82..7b5c35c64c 100644 --- a/bindings/rust/src/lib.rs +++ b/bindings/rust/src/lib.rs @@ -77,15 +77,16 @@ impl Drop for Context { } } -pub struct UnicornInner { +pub struct UnicornInner<'a, D> { pub uc: uc_handle, pub arch: Arch, /// to keep ownership over the hook for this uc instance's lifetime - pub hooks: Vec<(ffi::uc_hook, Box)>, + pub hooks: Vec<(ffi::uc_hook, Box + 'a>)>, + pub data: D, } /// Drop UC -impl Drop for UnicornInner { +impl<'a, D> Drop for UnicornInner<'a, D> { fn drop(&mut self) { if !self.uc.is_null() { unsafe { ffi::uc_close(self.uc) }; @@ -95,25 +96,25 @@ impl Drop for UnicornInner { } /// A Unicorn emulator instance. -pub struct Unicorn { - inner: Rc>>, +pub struct Unicorn<'a, D: 'a> { + inner: Rc>>, } -impl<'a> Unicorn<'a> { +impl<'a> Unicorn<'a, ()> { /// Create a new instance of the unicorn engine for the specified architecture /// and hardware mode. - pub fn new(arch: Arch, mode: Mode) -> Result, uc_error> { + pub fn new(arch: Arch, mode: Mode) -> Result, uc_error> { Self::new_with_data(arch, mode, ()) } } -impl<'a, D> Unicorn<'a> +impl<'a, D> Unicorn<'a, D> where D: 'a, { /// Create a new instance of the unicorn engine for the specified architecture /// and hardware mode. - pub fn new_with_data(arch: Arch, mode: Mode, data: D) -> Result, uc_error> { + pub fn new_with_data(arch: Arch, mode: Mode, data: D) -> Result, uc_error> { let mut handle = core::ptr::null_mut(); let err = unsafe { ffi::uc_open(arch, mode, &mut handle) }; if err == uc_error::OK { From 7adcced19b4ad8069250d418ac98cd909714ab79 Mon Sep 17 00:00:00 2001 From: Dominik Maier Date: Wed, 10 Nov 2021 04:26:56 +0100 Subject: [PATCH 11/12] cleanup --- bindings/rust/src/lib.rs | 4 ++-- bindings/rust/src/sparc.rs | 9 +++++++++ include/uc_priv.h | 1 - 3 files changed, 11 insertions(+), 3 deletions(-) diff --git a/bindings/rust/src/lib.rs b/bindings/rust/src/lib.rs index 7b5c35c64c..197d3c9d72 100644 --- a/bindings/rust/src/lib.rs +++ b/bindings/rust/src/lib.rs @@ -308,7 +308,7 @@ impl<'a, D> Unicorn<'a, D> { /// Write variable sized values into registers. /// /// The user has to make sure that the buffer length matches the register size. - /// This adds support for registers >64 bit (GDTR/IDTR, XMM, YMM, ZMM, ST (x86); Q, V (arm64)). + /// This adds support for registers >64 bit (GDTR/IDTR, XMM, YMM, ZMM (x86); Q, V (arm64)). pub fn reg_write_long>(&self, regid: T, value: &[u8]) -> Result<(), uc_error> { let err = unsafe { ffi::uc_reg_write(self.inner().uc, regid.into(), value.as_ptr() as _) }; if err == uc_error::OK { @@ -334,7 +334,7 @@ impl<'a, D> Unicorn<'a, D> { /// Read 128, 256 or 512 bit register value into heap allocated byte array. /// - /// This adds safe support for registers >64 bit (GDTR/IDTR, XMM, YMM, ZMM (x86); Q, V (arm64)). + /// This adds safe support for registers >64 bit (GDTR/IDTR, XMM, YMM, ZMM, ST (x86); Q, V (arm64)). pub fn reg_read_long>(&self, regid: T) -> Result, uc_error> { let err: uc_error; let boxed: Box<[u8]>; diff --git a/bindings/rust/src/sparc.rs b/bindings/rust/src/sparc.rs index f7295750ae..e65b35892a 100644 --- a/bindings/rust/src/sparc.rs +++ b/bindings/rust/src/sparc.rs @@ -92,6 +92,15 @@ pub enum RegisterSPARC { Y = 86, XCC = 87, PC = 88, + ENDING = 89, +} + +impl RegisterSPARC { + // alias registers + // (assoc) O6 = 84, + // (assoc) I6 = 67, + pub const O6: RegisterSPARC = RegisterSPARC::SP; + pub const I6: RegisterSPARC = RegisterSPARC::FP; } impl From for i32 { diff --git a/include/uc_priv.h b/include/uc_priv.h index debd85f8a8..9d88c233e5 100644 --- a/include/uc_priv.h +++ b/include/uc_priv.h @@ -342,7 +342,6 @@ struct uc_struct { bool no_exit_request; // Disable check_exit_request temporarily. A // workaround to treat the IT block as a whole block. bool init_done; // Whether the initialization is done. - void *data_ptr; // optional data pointer for bindings }; // Metadata stub for the variable-size cpu context used with uc_context_*() From 67cd9b1c2f4d18392f209401ce0382e5f0d05089 Mon Sep 17 00:00:00 2001 From: Dominik Maier Date: Wed, 10 Nov 2021 04:49:27 +0100 Subject: [PATCH 12/12] fixed doc tests --- bindings/rust/src/lib.rs | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/bindings/rust/src/lib.rs b/bindings/rust/src/lib.rs index 197d3c9d72..b2a8225256 100644 --- a/bindings/rust/src/lib.rs +++ b/bindings/rust/src/lib.rs @@ -10,19 +10,18 @@ //! use unicorn_engine::unicorn_const::{Arch, Mode, Permission, SECOND_SCALE}; //! //! fn emulate() { -//! let arm_code32: Vec = vec![0x17, 0x00, 0x40, 0xe2]; // sub r0, #23 +//! let arm_code32 = [0x17, 0x00, 0x40, 0xe2]; // sub r0, #23 //! -//! let mut unicorn = unicorn_engine::Unicorn::new(Arch::ARM, Mode::LITTLE_ENDIAN).expect("failed to initialize Unicorn instance"); -//! let mut emu = unicorn.borrow(); +//! let mut emu = unicorn_engine::Unicorn::new(Arch::ARM, Mode::LITTLE_ENDIAN).expect("failed to initialize Unicorn instance"); //! emu.mem_map(0x1000, 0x4000, Permission::ALL).expect("failed to map code page"); //! emu.mem_write(0x1000, &arm_code32).expect("failed to write instructions"); //! -//! emu.reg_write(RegisterARM::R0 as i32, 123).expect("failed write R0"); -//! emu.reg_write(RegisterARM::R5 as i32, 1337).expect("failed write R5"); +//! emu.reg_write(RegisterARM::R0, 123).expect("failed write R0"); +//! emu.reg_write(RegisterARM::R5, 1337).expect("failed write R5"); //! -//! let _ = emu.emu_start(0x1000, (0x1000 + arm_code32.len()) as u64, 10 * SECOND_SCALE, 1000); -//! assert_eq!(emu.reg_read(RegisterARM::R0 as i32), Ok(100)); -//! assert_eq!(emu.reg_read(RegisterARM::R5 as i32), Ok(1337)); +//! emu.emu_start(0x1000, (0x1000 + arm_code32.len()) as u64, 10 * SECOND_SCALE, 1000).unwrap(); +//! assert_eq!(emu.reg_read(RegisterARM::R0), Ok(100)); +//! assert_eq!(emu.reg_read(RegisterARM::R5), Ok(1337)); //! } //! ``` //!