Merge branch 'dev' into uc-py-next
This commit is contained in:
@@ -56,8 +56,8 @@ def test_uc_ctl_tb_cache():
|
||||
|
||||
# Now we clear cache for all TBs.
|
||||
for i in range(8):
|
||||
uc.ctl_remove_cache(addr + i * 512, addr + 0x1000)
|
||||
|
||||
uc.ctl_remove_cache(addr + i * 512, addr + i * 512 + 1)
|
||||
|
||||
evicted = time_emulation(uc, addr, addr + len(code))
|
||||
|
||||
print(f">>> Run time: First time {standard}, Cached: {cached}, Cached evicted: {evicted}")
|
||||
@@ -65,7 +65,7 @@ def test_uc_ctl_tb_cache():
|
||||
def trace_new_edge(uc, cur, prev, data):
|
||||
print(f">>> Getting a new edge from {hex(prev.pc + prev.size - 1)} to {hex(cur.pc)}")
|
||||
|
||||
def trace_tcg_sub(uc, address, arg1, arg2, data):
|
||||
def trace_tcg_sub(uc, address, arg1, arg2, size, data):
|
||||
print(f">>> Get a tcg sub opcode at {hex(address)} with args: {arg1} and {arg2}")
|
||||
|
||||
def test_uc_ctl_exits():
|
||||
|
||||
@@ -182,7 +182,7 @@ __set_lib_prototypes(uclib)
|
||||
|
||||
# native hook callback signatures
|
||||
HOOK_INTR_CFUNC = ctypes.CFUNCTYPE(None, uc_engine, ctypes.c_uint32, ctypes.c_void_p)
|
||||
HOOK_CODE_CFUNC = ctypes.CFUNCTYPE(None, uc_engine, ctypes.c_uint64, ctypes.c_size_t, ctypes.c_void_p)
|
||||
HOOK_CODE_CFUNC = ctypes.CFUNCTYPE(None, uc_engine, ctypes.c_uint64, ctypes.c_uint32, ctypes.c_void_p)
|
||||
HOOK_MEM_INVALID_CFUNC = ctypes.CFUNCTYPE(ctypes.c_bool, uc_engine, ctypes.c_int, ctypes.c_uint64, ctypes.c_int, ctypes.c_int64, ctypes.c_void_p)
|
||||
HOOK_MEM_ACCESS_CFUNC = ctypes.CFUNCTYPE(None, uc_engine, ctypes.c_int, ctypes.c_uint64, ctypes.c_int, ctypes.c_int64, ctypes.c_void_p)
|
||||
HOOK_INSN_INVALID_CFUNC = ctypes.CFUNCTYPE(ctypes.c_bool, uc_engine, ctypes.c_void_p)
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
|
||||
use crate::{Unicorn, UnicornInner};
|
||||
|
||||
use super::unicorn_const::{uc_error, Arch, HookType, MemRegion, MemType, Mode, Query};
|
||||
use super::unicorn_const::{uc_error, Arch, HookType, MemRegion, MemType, Mode, Query, TlbEntry};
|
||||
use alloc::rc::Weak;
|
||||
use core::{cell::UnsafeCell, ffi::c_void};
|
||||
use libc::{c_char, c_int};
|
||||
@@ -86,6 +86,7 @@ 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_ctl(engine: uc_handle, control: u32, ...) -> uc_error;
|
||||
}
|
||||
|
||||
pub struct UcHook<'a, D: 'a, F: 'a> {
|
||||
@@ -251,3 +252,25 @@ where
|
||||
debug_assert_eq!(uc, user_data_uc.get_handle());
|
||||
(user_data.callback)(&mut user_data_uc);
|
||||
}
|
||||
|
||||
pub extern "C" fn tlb_lookup_hook_proxy<D, F>(uc: uc_handle, vaddr: u64, mem_type: MemType, result: *mut TlbEntry, user_data: *mut UcHook<D, F>) -> bool
|
||||
where
|
||||
F: FnMut(&mut crate::Unicorn<D>, u64, MemType) -> Option<TlbEntry>,
|
||||
{
|
||||
let user_data = unsafe { &mut *user_data };
|
||||
let mut user_data_uc = Unicorn {
|
||||
inner: user_data.uc.upgrade().unwrap(),
|
||||
};
|
||||
debug_assert_eq!(uc, user_data_uc.get_handle());
|
||||
let r = (user_data.callback)(&mut user_data_uc, vaddr, mem_type);
|
||||
match r {
|
||||
Some(ref e) => {
|
||||
unsafe {
|
||||
let ref_result: &mut TlbEntry = &mut *result;
|
||||
*ref_result = *e;
|
||||
}
|
||||
},
|
||||
None => {},
|
||||
};
|
||||
return r.is_some();
|
||||
}
|
||||
|
||||
@@ -30,7 +30,9 @@
|
||||
|
||||
#[macro_use]
|
||||
extern crate alloc;
|
||||
extern crate std;
|
||||
|
||||
#[macro_use]
|
||||
pub mod unicorn_const;
|
||||
|
||||
mod arm;
|
||||
@@ -883,6 +885,33 @@ impl<'a, D> Unicorn<'a, D> {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn add_tlb_hook<F>(&mut self, begin: u64, end: u64, callback: F) -> Result<ffi::uc_hook, uc_error>
|
||||
where
|
||||
F: FnMut(&mut crate::Unicorn<D>, u64, MemType) -> Option<TlbEntry> + 'a,
|
||||
{
|
||||
let mut hook_ptr = core::ptr::null_mut();
|
||||
let mut user_data = Box::new(ffi::UcHook {
|
||||
callback,
|
||||
uc: Rc::downgrade(&self.inner),
|
||||
});
|
||||
let err = unsafe {
|
||||
ffi::uc_hook_add(self.get_handle(),
|
||||
&mut hook_ptr,
|
||||
HookType::TLB,
|
||||
ffi::tlb_lookup_hook_proxy::<D, F> as _,
|
||||
user_data.as_mut() as *mut _ as _,
|
||||
begin,
|
||||
end,
|
||||
)
|
||||
};
|
||||
if err == uc_error::OK {
|
||||
self.inner_mut().hooks.push((hook_ptr, user_data));
|
||||
Ok(hook_ptr)
|
||||
} else {
|
||||
Err(err)
|
||||
}
|
||||
}
|
||||
|
||||
/// Remove a hook.
|
||||
///
|
||||
/// `hook` is the value returned by `add_*_hook` functions.
|
||||
@@ -1051,4 +1080,166 @@ impl<'a, D> Unicorn<'a, D> {
|
||||
};
|
||||
self.reg_write(reg, value)
|
||||
}
|
||||
|
||||
pub fn ctl_get_mode(&self) -> Result<Mode, uc_error> {
|
||||
let mut result: i32 = Default::default();
|
||||
let err = unsafe { ffi::uc_ctl(self.get_handle(), UC_CTL_READ!(ControlType::UC_CTL_UC_MODE), &mut result) };
|
||||
if err == uc_error::OK {
|
||||
Ok(Mode::from_bits_truncate(result))
|
||||
} else {
|
||||
Err(err)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn ctl_get_page_size(&self) -> Result<u32, uc_error> {
|
||||
let mut result: u32 = Default::default();
|
||||
let err = unsafe { ffi::uc_ctl(self.get_handle(), UC_CTL_READ!(ControlType::UC_CTL_UC_PAGE_SIZE), &mut result) };
|
||||
if err == uc_error::OK {
|
||||
Ok(result)
|
||||
} else {
|
||||
Err(err)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn ctl_set_page_size(&self, page_size: u32) -> Result<(), uc_error> {
|
||||
let err = unsafe { ffi::uc_ctl(self.get_handle(), UC_CTL_WRITE!(ControlType::UC_CTL_UC_PAGE_SIZE), page_size) };
|
||||
if err == uc_error::OK {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(err)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn ctl_get_arch(&self) -> Result<Arch, uc_error> {
|
||||
let mut result: i32 = Default::default();
|
||||
let err = unsafe { ffi::uc_ctl(self.get_handle(), UC_CTL_READ!(ControlType::UC_CTL_UC_ARCH), &mut result) };
|
||||
if err == uc_error::OK {
|
||||
Arch::try_from(result as usize)
|
||||
} else {
|
||||
Err(err)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn ctl_get_timeout(&self) -> Result<u64, uc_error> {
|
||||
let mut result: u64 = Default::default();
|
||||
let err = unsafe { ffi::uc_ctl(self.get_handle(), UC_CTL_READ!(ControlType::UC_CTL_UC_TIMEOUT), &mut result) };
|
||||
if err == uc_error::OK {
|
||||
Ok(result)
|
||||
} else {
|
||||
Err(err)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn ctl_exits_enable(&self) -> Result<(), uc_error> {
|
||||
let err = unsafe { ffi::uc_ctl(self.get_handle(), UC_CTL_WRITE!(ControlType::UC_CTL_UC_USE_EXITS), 1) };
|
||||
if err == uc_error::OK {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(err)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn ctl_exits_disable(&self) -> Result<(), uc_error> {
|
||||
let err = unsafe { ffi::uc_ctl(self.get_handle(), UC_CTL_WRITE!(ControlType::UC_CTL_UC_USE_EXITS), 0) };
|
||||
if err == uc_error::OK {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(err)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn ctl_get_exits_count(&self) -> Result<usize, uc_error> {
|
||||
let mut result: libc::size_t = Default::default();
|
||||
let err = unsafe { ffi::uc_ctl(self.get_handle(), UC_CTL_READ!(ControlType::UC_CTL_UC_EXITS_CNT), &mut result) };
|
||||
if err == uc_error::OK {
|
||||
Ok(result)
|
||||
} else {
|
||||
Err(err)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn ctl_get_exits(&self) -> Result<Vec<u64>, uc_error> {
|
||||
let exits_count: libc::size_t = self.ctl_get_exits_count()?;
|
||||
let mut exits: Vec<u64> = Vec::with_capacity(exits_count);
|
||||
let err = unsafe { ffi::uc_ctl(self.get_handle(), UC_CTL_READ!(ControlType::UC_CTL_UC_EXITS), exits.as_mut_ptr(), exits_count) };
|
||||
if err == uc_error::OK {
|
||||
unsafe { exits.set_len(exits_count); }
|
||||
Ok(exits)
|
||||
} else {
|
||||
Err(err)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn ctl_set_exits(&self, exits: &[u64]) -> Result<(), uc_error> {
|
||||
let err = unsafe { ffi::uc_ctl(self.get_handle(), UC_CTL_WRITE!(ControlType::UC_CTL_UC_EXITS), exits.as_ptr(), exits.len() as libc::size_t) };
|
||||
if err == uc_error::OK {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(err)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn ctl_get_cpu_model(&self) -> Result<i32, uc_error> {
|
||||
let mut result: i32 = Default::default();
|
||||
let err = unsafe { ffi::uc_ctl(self.get_handle(), UC_CTL_READ!(ControlType::UC_CTL_CPU_MODEL), &mut result) };
|
||||
if err == uc_error::OK {
|
||||
Ok(result)
|
||||
} else {
|
||||
Err(err)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn ctl_set_cpu_model(&self, cpu_model: i32) -> Result<(), uc_error> {
|
||||
let err = unsafe { ffi::uc_ctl(self.get_handle(), UC_CTL_WRITE!(ControlType::UC_CTL_CPU_MODEL), cpu_model) };
|
||||
if err == uc_error::OK {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(err)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn ctl_remove_cache(&self, address: u64, end: u64) -> Result<(), uc_error> {
|
||||
let err = unsafe { ffi::uc_ctl(self.get_handle(), UC_CTL_WRITE!(ControlType::UC_CTL_TB_REMOVE_CACHE), address, end) };
|
||||
if err == uc_error::OK {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(err)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn ctl_request_cache(&self, address: u64, tb: &mut TranslationBlock) -> Result<(), uc_error> {
|
||||
let err = unsafe { ffi::uc_ctl(self.get_handle(), UC_CTL_READ_WRITE!(ControlType::UC_CTL_TB_REQUEST_CACHE), address, tb) };
|
||||
if err == uc_error::OK {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(err)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn ctl_flush_tb(&self) -> Result<(), uc_error> {
|
||||
let err = unsafe { ffi::uc_ctl(self.get_handle(), UC_CTL_WRITE!(ControlType::UC_CTL_TB_FLUSH)) };
|
||||
if err == uc_error::OK {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(err)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn ctl_flush_tlb(&self) -> Result<(), uc_error> {
|
||||
let err = unsafe { ffi::uc_ctl(self.get_handle(), UC_CTL_WRITE!(ControlType::UC_CTL_TLB_FLUSH)) };
|
||||
if err == uc_error::OK {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(err)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn ctl_tlb_type(&self, t: TlbType) -> Result<(), uc_error> {
|
||||
let err = unsafe { ffi::uc_ctl(self.get_handle(), UC_CTL_WRITE!(ControlType::UC_CTL_TLB_TYPE), t as i32) };
|
||||
if err == uc_error::OK {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -53,6 +53,13 @@ pub enum MemType {
|
||||
READ_AFTER = 25,
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(PartialEq, Debug, Clone, Copy)]
|
||||
pub enum TlbType {
|
||||
CPU = 0,
|
||||
VIRTUAL = 1,
|
||||
}
|
||||
|
||||
bitflags! {
|
||||
#[repr(C)]
|
||||
pub struct HookType: i32 {
|
||||
@@ -86,6 +93,8 @@ bitflags! {
|
||||
const MEM_INVALID = Self::MEM_READ_INVALID.bits | Self::MEM_WRITE_INVALID.bits | Self::MEM_FETCH_INVALID.bits;
|
||||
|
||||
const MEM_ALL = Self::MEM_VALID.bits | Self::MEM_INVALID.bits;
|
||||
|
||||
const TLB = (1 << 17);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -187,3 +196,55 @@ bitflags! {
|
||||
const RISCV64 = Self::MIPS64.bits;
|
||||
}
|
||||
}
|
||||
|
||||
// Represent a TranslationBlock.
|
||||
#[repr(C)]
|
||||
pub struct TranslationBlock {
|
||||
pub pc: u64,
|
||||
pub icount: u16,
|
||||
pub size: u16
|
||||
}
|
||||
|
||||
macro_rules! UC_CTL_READ {
|
||||
($expr:expr) => {
|
||||
$expr as u32 | ControlType::UC_CTL_IO_READ as u32
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! UC_CTL_WRITE {
|
||||
($expr:expr) => {
|
||||
$expr as u32 | ControlType::UC_CTL_IO_WRITE as u32
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! UC_CTL_READ_WRITE {
|
||||
($expr:expr) => {
|
||||
$expr as u32 | ControlType::UC_CTL_IO_WRITE as u32 | ControlType::UC_CTL_IO_READ as u32
|
||||
};
|
||||
}
|
||||
|
||||
#[allow(clippy::upper_case_acronyms)]
|
||||
pub enum ControlType {
|
||||
UC_CTL_UC_MODE = 0,
|
||||
UC_CTL_UC_PAGE_SIZE = 1,
|
||||
UC_CTL_UC_ARCH = 2,
|
||||
UC_CTL_UC_TIMEOUT = 3,
|
||||
UC_CTL_UC_USE_EXITS = 4,
|
||||
UC_CTL_UC_EXITS_CNT = 5,
|
||||
UC_CTL_UC_EXITS = 6,
|
||||
UC_CTL_CPU_MODEL = 7,
|
||||
UC_CTL_TB_REQUEST_CACHE = 8,
|
||||
UC_CTL_TB_REMOVE_CACHE = 9,
|
||||
UC_CTL_TB_FLUSH = 10,
|
||||
UC_CTL_TLB_FLUSH = 11,
|
||||
UC_CTL_TLB_TYPE = 12,
|
||||
UC_CTL_IO_READ = 1<<31,
|
||||
UC_CTL_IO_WRITE = 1<<30,
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub struct TlbEntry {
|
||||
pub paddr: u64,
|
||||
pub perms: Permission,
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user