Merge pull request #1932 from lockbox/rust-conditional-compilation

Rust conditional compilation
This commit is contained in:
2024-03-28 19:29:23 +08:00
committed by GitHub
3 changed files with 364 additions and 328 deletions

View File

@@ -15,30 +15,15 @@ pub type uc_hook = *mut c_void;
pub type uc_context = *mut c_void; pub type uc_context = *mut c_void;
extern "C" { extern "C" {
pub fn uc_version( pub fn uc_version(major: *mut u32, minor: *mut u32) -> u32;
major: *mut u32,
minor: *mut u32,
) -> u32;
pub fn uc_arch_supported(arch: Arch) -> bool; pub fn uc_arch_supported(arch: Arch) -> bool;
pub fn uc_open( pub fn uc_open(arch: Arch, mode: Mode, engine: *mut uc_handle) -> uc_error;
arch: Arch,
mode: Mode,
engine: *mut uc_handle,
) -> uc_error;
pub fn uc_close(engine: uc_handle) -> uc_error; pub fn uc_close(engine: uc_handle) -> uc_error;
pub fn uc_context_free(mem: uc_context) -> uc_error; pub fn uc_context_free(mem: uc_context) -> uc_error;
pub fn uc_errno(engine: uc_handle) -> uc_error; pub fn uc_errno(engine: uc_handle) -> uc_error;
pub fn uc_strerror(error_code: uc_error) -> *const c_char; pub fn uc_strerror(error_code: uc_error) -> *const c_char;
pub fn uc_reg_write( pub fn uc_reg_write(engine: uc_handle, regid: c_int, value: *const c_void) -> uc_error;
engine: uc_handle, pub fn uc_reg_read(engine: uc_handle, regid: c_int, value: *mut c_void) -> uc_error;
regid: c_int,
value: *const c_void,
) -> uc_error;
pub fn uc_reg_read(
engine: uc_handle,
regid: c_int,
value: *mut c_void,
) -> uc_error;
pub fn uc_mem_write( pub fn uc_mem_write(
engine: uc_handle, engine: uc_handle,
address: u64, address: u64,
@@ -51,12 +36,7 @@ extern "C" {
bytes: *mut u8, bytes: *mut u8,
size: libc::size_t, size: libc::size_t,
) -> uc_error; ) -> uc_error;
pub fn uc_mem_map( pub fn uc_mem_map(engine: uc_handle, address: u64, size: libc::size_t, perms: u32) -> uc_error;
engine: uc_handle,
address: u64,
size: libc::size_t,
perms: u32,
) -> uc_error;
pub fn uc_mem_map_ptr( pub fn uc_mem_map_ptr(
engine: uc_handle, engine: uc_handle,
address: u64, address: u64,
@@ -73,11 +53,7 @@ extern "C" {
write_cb: *mut c_void, write_cb: *mut c_void,
user_data_write: *mut c_void, user_data_write: *mut c_void,
) -> uc_error; ) -> uc_error;
pub fn uc_mem_unmap( pub fn uc_mem_unmap(engine: uc_handle, address: u64, size: libc::size_t) -> uc_error;
engine: uc_handle,
address: u64,
size: libc::size_t,
) -> uc_error;
pub fn uc_mem_protect( pub fn uc_mem_protect(
engine: uc_handle, engine: uc_handle,
address: u64, address: u64,
@@ -107,32 +83,12 @@ extern "C" {
end: u64, end: u64,
... ...
) -> uc_error; ) -> uc_error;
pub fn uc_hook_del( pub fn uc_hook_del(engine: uc_handle, hook: uc_hook) -> uc_error;
engine: uc_handle, pub fn uc_query(engine: uc_handle, query_type: Query, result: *mut libc::size_t) -> uc_error;
hook: uc_hook, pub fn uc_context_alloc(engine: uc_handle, context: *mut uc_context) -> uc_error;
) -> uc_error; pub fn uc_context_save(engine: uc_handle, context: uc_context) -> uc_error;
pub fn uc_query( pub fn uc_context_restore(engine: uc_handle, context: uc_context) -> uc_error;
engine: uc_handle, pub fn uc_ctl(engine: uc_handle, control: u32, ...) -> uc_error;
query_type: Query,
result: *mut libc::size_t,
) -> uc_error;
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> { pub struct UcHook<'a, D: 'a, F: 'a> {
@@ -293,10 +249,8 @@ pub unsafe extern "C" fn insn_out_hook_proxy<D, F>(
(user_data.callback)(&mut user_data_uc, port, size, value); (user_data.callback)(&mut user_data_uc, port, size, value);
} }
pub unsafe extern "C" fn insn_sys_hook_proxy<D, F>( pub unsafe extern "C" fn insn_sys_hook_proxy<D, F>(uc: uc_handle, user_data: *mut UcHook<D, F>)
uc: uc_handle, where
user_data: *mut UcHook<D, F>,
) where
F: FnMut(&mut crate::Unicorn<D>), F: FnMut(&mut crate::Unicorn<D>),
{ {
let user_data = &mut *user_data; let user_data = &mut *user_data;

View File

@@ -42,32 +42,72 @@ use libc::c_void;
use ffi::uc_handle; use ffi::uc_handle;
pub use crate::arm::*;
pub use crate::arm64::*;
pub use crate::m68k::*;
pub use crate::mips::*;
pub use crate::ppc::*;
pub use crate::riscv::*;
pub use crate::s390x::*;
pub use crate::sparc::*;
pub use crate::tricore::*;
pub use crate::unicorn_const::*;
pub use crate::x86::*;
#[macro_use] #[macro_use]
pub mod unicorn_const; pub mod unicorn_const;
pub use unicorn_const::*;
pub mod ffi; // lets consumers call ffi if desired pub mod ffi; // lets consumers call ffi if desired
// include arm support if conditionally compiled in
#[cfg(feature = "arch_arm")]
mod arm; mod arm;
#[cfg(feature = "arch_arm")]
pub use crate::arm::*;
// include arm64 support if conditionally compiled in
// NOTE: unicorn-c only separates on top-level arch name,
// not on the bit-length, so we include both
#[cfg(feature = "arch_arm")]
mod arm64; mod arm64;
#[cfg(feature = "arch_arm")]
pub use crate::arm64::*;
// include m68k support if conditionally compiled in
#[cfg(feature = "arch_m68k")]
mod m68k; mod m68k;
#[cfg(feature = "arch_m68k")]
pub use crate::m68k::*;
// include mips support if conditionally compiled in
#[cfg(feature = "arch_mips")]
mod mips; mod mips;
#[cfg(feature = "arch_mips")]
pub use crate::mips::*;
// include ppc support if conditionally compiled in
#[cfg(feature = "arch_ppc")]
mod ppc; mod ppc;
#[cfg(feature = "arch_ppc")]
pub use crate::ppc::*;
// include riscv support if conditionally compiled in
#[cfg(feature = "arch_riscv")]
mod riscv; mod riscv;
#[cfg(feature = "arch_riscv")]
pub use crate::riscv::*;
// include s390x support if conditionally compiled in
#[cfg(feature = "arch_s390x")]
mod s390x; mod s390x;
#[cfg(feature = "arch_s390x")]
pub use crate::s390x::*;
// include sparc support if conditionally compiled in
#[cfg(feature = "arch_sparc")]
mod sparc; mod sparc;
#[cfg(feature = "arch_sparc")]
pub use crate::sparc::*;
// include tricore support if conditionally compiled in
#[cfg(feature = "arch_tricore")]
mod tricore; mod tricore;
#[cfg(feature = "arch_tricore")]
pub use crate::tricore::*;
// include x86 support if conditionally compiled in
#[cfg(feature = "arch_x86")]
mod x86; mod x86;
#[cfg(feature = "arch_x86")]
pub use crate::x86::*;
#[derive(Debug)] #[derive(Debug)]
pub struct Context { pub struct Context {
@@ -103,11 +143,7 @@ impl<'a> MmioCallbackScope<'a> {
!self.regions.is_empty() !self.regions.is_empty()
} }
fn unmap( fn unmap(&mut self, begin: u64, size: usize) {
&mut self,
begin: u64,
size: usize,
) {
let end: u64 = begin + size as u64; let end: u64 = begin + size as u64;
self.regions = self self.regions = self
.regions .regions
@@ -124,7 +160,10 @@ impl<'a> MmioCallbackScope<'a> {
} else { } else {
// The unmapped region is in the middle of this region // The unmapped region is in the middle of this region
let second_b = end + 1; let second_b = end + 1;
vec![(*b, (begin - *b) as usize), (second_b, (e - second_b) as usize)] vec![
(*b, (begin - *b) as usize),
(second_b, (e - second_b) as usize),
]
} }
} else if end > *b { } else if end > *b {
if end >= e { if end >= e {
@@ -175,10 +214,7 @@ pub struct Unicorn<'a, D: 'a> {
impl<'a> Unicorn<'a, ()> { impl<'a> Unicorn<'a, ()> {
/// Create a new instance of the unicorn engine for the specified architecture /// Create a new instance of the unicorn engine for the specified architecture
/// and hardware mode. /// and hardware mode.
pub fn new( pub fn new(arch: Arch, mode: Mode) -> Result<Unicorn<'a, ()>, uc_error> {
arch: Arch,
mode: Mode,
) -> Result<Unicorn<'a, ()>, uc_error> {
Self::new_with_data(arch, mode, ()) Self::new_with_data(arch, mode, ())
} }
@@ -216,11 +252,7 @@ where
{ {
/// Create a new instance of the unicorn engine for the specified architecture /// Create a new instance of the unicorn engine for the specified architecture
/// and hardware mode. /// and hardware mode.
pub fn new_with_data( pub fn new_with_data(arch: Arch, mode: Mode, data: D) -> Result<Unicorn<'a, D>, uc_error> {
arch: Arch,
mode: Mode,
data: D,
) -> Result<Unicorn<'a, D>, uc_error> {
let mut handle = ptr::null_mut(); let mut handle = ptr::null_mut();
unsafe { ffi::uc_open(arch, mode, &mut handle) }.and_then(|| { unsafe { ffi::uc_open(arch, mode, &mut handle) }.and_then(|| {
Ok(Unicorn { Ok(Unicorn {
@@ -238,10 +270,7 @@ where
} }
impl<'a, D> core::fmt::Debug for Unicorn<'a, D> { impl<'a, D> core::fmt::Debug for Unicorn<'a, D> {
fn fmt( fn fmt(&self, formatter: &mut core::fmt::Formatter) -> core::fmt::Result {
&self,
formatter: &mut core::fmt::Formatter,
) -> core::fmt::Result {
write!(formatter, "Unicorn {{ uc: {:p} }}", self.get_handle()) write!(formatter, "Unicorn {{ uc: {:p} }}", self.get_handle())
} }
} }
@@ -286,41 +315,31 @@ impl<'a, D> Unicorn<'a, D> {
pub fn mem_regions(&self) -> Result<Vec<MemRegion>, uc_error> { pub fn mem_regions(&self) -> Result<Vec<MemRegion>, uc_error> {
let mut nb_regions: u32 = 0; let mut nb_regions: u32 = 0;
let p_regions: *const MemRegion = ptr::null_mut(); let p_regions: *const MemRegion = ptr::null_mut();
unsafe { ffi::uc_mem_regions(self.get_handle(), &p_regions, &mut nb_regions) }.and_then(|| { unsafe { ffi::uc_mem_regions(self.get_handle(), &p_regions, &mut nb_regions) }.and_then(
let mut regions = Vec::new(); || {
for i in 0..nb_regions { let mut regions = Vec::new();
regions.push(unsafe { core::mem::transmute_copy(&*p_regions.add(i as usize)) }); for i in 0..nb_regions {
} regions.push(unsafe { core::mem::transmute_copy(&*p_regions.add(i as usize)) });
unsafe { libc::free(p_regions as _) }; }
Ok(regions) unsafe { libc::free(p_regions as _) };
}) Ok(regions)
},
)
} }
/// Read a range of bytes from memory at the specified emulated physical address. /// Read a range of bytes from memory at the specified emulated physical address.
pub fn mem_read( pub fn mem_read(&self, address: u64, buf: &mut [u8]) -> Result<(), uc_error> {
&self,
address: u64,
buf: &mut [u8],
) -> Result<(), uc_error> {
unsafe { ffi::uc_mem_read(self.get_handle(), address, buf.as_mut_ptr(), buf.len()) }.into() unsafe { ffi::uc_mem_read(self.get_handle(), address, buf.as_mut_ptr(), buf.len()) }.into()
} }
/// Return a range of bytes from memory at the specified emulated physical address as vector. /// Return a range of bytes from memory at the specified emulated physical address as vector.
pub fn mem_read_as_vec( pub fn mem_read_as_vec(&self, address: u64, size: usize) -> Result<Vec<u8>, uc_error> {
&self,
address: u64,
size: usize,
) -> Result<Vec<u8>, uc_error> {
let mut buf = vec![0; size]; let mut buf = vec![0; size];
unsafe { ffi::uc_mem_read(self.get_handle(), address, buf.as_mut_ptr(), size) }.and(Ok(buf)) unsafe { ffi::uc_mem_read(self.get_handle(), address, buf.as_mut_ptr(), size) }.and(Ok(buf))
} }
/// Write the data in `bytes` to the emulated physical address `address` /// Write the data in `bytes` to the emulated physical address `address`
pub fn mem_write( pub fn mem_write(&mut self, address: u64, bytes: &[u8]) -> Result<(), uc_error> {
&mut self,
address: u64,
bytes: &[u8],
) -> Result<(), uc_error> {
unsafe { ffi::uc_mem_write(self.get_handle(), address, bytes.as_ptr(), bytes.len()) }.into() unsafe { ffi::uc_mem_write(self.get_handle(), address, bytes.as_ptr(), bytes.len()) }.into()
} }
@@ -437,7 +456,12 @@ impl<'a, D> Unicorn<'a, D> {
where where
F: FnMut(&mut Unicorn<D>, u64, usize) -> u64, F: FnMut(&mut Unicorn<D>, u64, usize) -> u64,
{ {
self.mmio_map(address, size, Some(callback), None::<fn(&mut Unicorn<D>, u64, usize, u64)>) self.mmio_map(
address,
size,
Some(callback),
None::<fn(&mut Unicorn<D>, u64, usize, u64)>,
)
} }
/// Map in a write-only MMIO region backed by a callback. /// Map in a write-only MMIO region backed by a callback.
@@ -453,28 +477,25 @@ impl<'a, D> Unicorn<'a, D> {
where where
F: FnMut(&mut Unicorn<D>, u64, usize, u64), F: FnMut(&mut Unicorn<D>, u64, usize, u64),
{ {
self.mmio_map(address, size, None::<fn(&mut Unicorn<D>, u64, usize) -> u64>, Some(callback)) self.mmio_map(
address,
size,
None::<fn(&mut Unicorn<D>, u64, usize) -> u64>,
Some(callback),
)
} }
/// Unmap a memory region. /// Unmap a memory region.
/// ///
/// `address` must be aligned to 4kb or this will return `Error::ARG`. /// `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`. /// `size` must be a multiple of 4kb or this will return `Error::ARG`.
pub fn mem_unmap( pub fn mem_unmap(&mut self, address: u64, size: libc::size_t) -> Result<(), uc_error> {
&mut self,
address: u64,
size: libc::size_t,
) -> Result<(), uc_error> {
let err = unsafe { ffi::uc_mem_unmap(self.get_handle(), address, size) }; let err = unsafe { ffi::uc_mem_unmap(self.get_handle(), address, size) };
self.mmio_unmap(address, size); self.mmio_unmap(address, size);
err.into() err.into()
} }
fn mmio_unmap( fn mmio_unmap(&mut self, address: u64, size: libc::size_t) {
&mut self,
address: u64,
size: libc::size_t,
) {
for scope in self.inner_mut().mmio_callbacks.iter_mut() { for scope in self.inner_mut().mmio_callbacks.iter_mut() {
scope.unmap(address, size); scope.unmap(address, size);
} }
@@ -497,49 +518,39 @@ impl<'a, D> Unicorn<'a, D> {
} }
/// Write an unsigned value from a register. /// Write an unsigned value from a register.
pub fn reg_write<T: Into<i32>>( pub fn reg_write<T: Into<i32>>(&mut self, regid: T, value: u64) -> Result<(), uc_error> {
&mut self, unsafe { ffi::uc_reg_write(self.get_handle(), regid.into(), &value as *const _ as _) }
regid: T, .into()
value: u64,
) -> Result<(), uc_error> {
unsafe { ffi::uc_reg_write(self.get_handle(), regid.into(), &value as *const _ as _) }.into()
} }
/// Write variable sized values into registers. /// Write variable sized values into registers.
/// ///
/// The user has to make sure that the buffer length matches the register size. /// 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 (x86); Q, V (arm64)).
pub fn reg_write_long<T: Into<i32>>( pub fn reg_write_long<T: Into<i32>>(&self, regid: T, value: &[u8]) -> Result<(), uc_error> {
&self,
regid: T,
value: &[u8],
) -> Result<(), uc_error> {
unsafe { ffi::uc_reg_write(self.get_handle(), regid.into(), value.as_ptr() as _) }.into() unsafe { ffi::uc_reg_write(self.get_handle(), regid.into(), value.as_ptr() as _) }.into()
} }
/// Read an unsigned value from a register. /// Read an unsigned value from a register.
/// ///
/// Not to be used with registers larger than 64 bit. /// Not to be used with registers larger than 64 bit.
pub fn reg_read<T: Into<i32>>( pub fn reg_read<T: Into<i32>>(&self, regid: T) -> Result<u64, uc_error> {
&self,
regid: T,
) -> Result<u64, uc_error> {
let mut value: u64 = 0; let mut value: u64 = 0;
unsafe { ffi::uc_reg_read(self.get_handle(), regid.into(), &mut value as *mut u64 as _) }.and(Ok(value)) unsafe { ffi::uc_reg_read(self.get_handle(), regid.into(), &mut value as *mut u64 as _) }
.and(Ok(value))
} }
/// Read 128, 256 or 512 bit register value into heap allocated byte array. /// 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, ST (x86); Q, V (arm64)).
pub fn reg_read_long<T: Into<i32>>( pub fn reg_read_long<T: Into<i32>>(&self, regid: T) -> Result<Box<[u8]>, uc_error> {
&self,
regid: T,
) -> Result<Box<[u8]>, uc_error> {
let curr_reg_id = regid.into(); let curr_reg_id = regid.into();
let curr_arch = self.get_arch(); let curr_arch = self.get_arch();
let value_size = match curr_arch { let value_size = match curr_arch {
#[cfg(feature = "arch_x86")]
Arch::X86 => Self::value_size_x86(curr_reg_id)?, Arch::X86 => Self::value_size_x86(curr_reg_id)?,
#[cfg(feature = "arch_arm")]
Arch::ARM64 => Self::value_size_arm64(curr_reg_id)?, Arch::ARM64 => Self::value_size_arm64(curr_reg_id)?,
_ => Err(uc_error::ARCH)?, _ => Err(uc_error::ARCH)?,
}; };
@@ -548,6 +559,7 @@ impl<'a, D> Unicorn<'a, D> {
.and_then(|| Ok(value.into_boxed_slice())) .and_then(|| Ok(value.into_boxed_slice()))
} }
#[cfg(feature = "arch_arm")]
fn value_size_arm64(curr_reg_id: i32) -> Result<usize, uc_error> { fn value_size_arm64(curr_reg_id: i32) -> Result<usize, uc_error> {
match curr_reg_id { match curr_reg_id {
r if (RegisterARM64::Q0 as i32..=RegisterARM64::Q31 as i32).contains(&r) r if (RegisterARM64::Q0 as i32..=RegisterARM64::Q31 as i32).contains(&r)
@@ -559,6 +571,7 @@ impl<'a, D> Unicorn<'a, D> {
} }
} }
#[cfg(feature = "arch_x86")]
fn value_size_x86(curr_reg_id: i32) -> Result<usize, uc_error> { fn value_size_x86(curr_reg_id: i32) -> Result<usize, uc_error> {
match curr_reg_id { match curr_reg_id {
r if (RegisterX86::XMM0 as i32..=RegisterX86::XMM31 as i32).contains(&r) => Ok(16), r if (RegisterX86::XMM0 as i32..=RegisterX86::XMM31 as i32).contains(&r) => Ok(16),
@@ -575,12 +588,10 @@ impl<'a, D> Unicorn<'a, D> {
} }
/// Read a signed 32-bit value from a register. /// Read a signed 32-bit value from a register.
pub fn reg_read_i32<T: Into<i32>>( pub fn reg_read_i32<T: Into<i32>>(&self, regid: T) -> Result<i32, uc_error> {
&self,
regid: T,
) -> Result<i32, uc_error> {
let mut value: i32 = 0; let mut value: i32 = 0;
unsafe { ffi::uc_reg_read(self.get_handle(), regid.into(), &mut value as *mut i32 as _) }.and(Ok(value)) unsafe { ffi::uc_reg_read(self.get_handle(), regid.into(), &mut value as *mut i32 as _) }
.and(Ok(value))
} }
/// Add a code hook. /// Add a code hook.
@@ -691,10 +702,7 @@ impl<'a, D> Unicorn<'a, D> {
} }
/// Add an interrupt hook. /// Add an interrupt hook.
pub fn add_intr_hook<F: 'a>( pub fn add_intr_hook<F: 'a>(&mut self, callback: F) -> Result<UcHookId, uc_error>
&mut self,
callback: F,
) -> Result<UcHookId, uc_error>
where where
F: FnMut(&mut Unicorn<D>, u32), F: FnMut(&mut Unicorn<D>, u32),
{ {
@@ -723,10 +731,7 @@ impl<'a, D> Unicorn<'a, D> {
} }
/// Add hook for invalid instructions /// Add hook for invalid instructions
pub fn add_insn_invalid_hook<F: 'a>( pub fn add_insn_invalid_hook<F: 'a>(&mut self, callback: F) -> Result<UcHookId, uc_error>
&mut self,
callback: F,
) -> Result<UcHookId, uc_error>
where where
F: FnMut(&mut Unicorn<D>) -> bool, F: FnMut(&mut Unicorn<D>) -> bool,
{ {
@@ -755,10 +760,8 @@ impl<'a, D> Unicorn<'a, D> {
} }
/// Add hook for x86 IN instruction. /// Add hook for x86 IN instruction.
pub fn add_insn_in_hook<F: 'a>( #[cfg(feature = "arch_x86")]
&mut self, pub fn add_insn_in_hook<F: 'a>(&mut self, callback: F) -> Result<UcHookId, uc_error>
callback: F,
) -> Result<UcHookId, uc_error>
where where
F: FnMut(&mut Unicorn<D>, u32, usize) -> u32, F: FnMut(&mut Unicorn<D>, u32, usize) -> u32,
{ {
@@ -788,10 +791,8 @@ impl<'a, D> Unicorn<'a, D> {
} }
/// Add hook for x86 OUT instruction. /// Add hook for x86 OUT instruction.
pub fn add_insn_out_hook<F: 'a>( #[cfg(feature = "arch_x86")]
&mut self, pub fn add_insn_out_hook<F: 'a>(&mut self, callback: F) -> Result<UcHookId, uc_error>
callback: F,
) -> Result<UcHookId, uc_error>
where where
F: FnMut(&mut Unicorn<D>, u32, usize, u32), F: FnMut(&mut Unicorn<D>, u32, usize, u32),
{ {
@@ -821,6 +822,7 @@ impl<'a, D> Unicorn<'a, D> {
} }
/// Add hook for x86 SYSCALL or SYSENTER. /// Add hook for x86 SYSCALL or SYSENTER.
#[cfg(feature = "arch_x86")]
pub fn add_insn_sys_hook<F>( pub fn add_insn_sys_hook<F>(
&mut self, &mut self,
insn_type: InsnSysX86, insn_type: InsnSysX86,
@@ -892,10 +894,7 @@ impl<'a, D> Unicorn<'a, D> {
/// Remove a hook. /// Remove a hook.
/// ///
/// `hook_id` is the value returned by `add_*_hook` functions. /// `hook_id` is the value returned by `add_*_hook` functions.
pub fn remove_hook( pub fn remove_hook(&mut self, hook_id: UcHookId) -> Result<(), uc_error> {
&mut self,
hook_id: UcHookId,
) -> Result<(), uc_error> {
// drop the hook // drop the hook
let inner = self.inner_mut(); let inner = self.inner_mut();
inner.hooks.retain(|(id, _)| id != &hook_id); inner.hooks.retain(|(id, _)| id != &hook_id);
@@ -908,15 +907,13 @@ impl<'a, D> Unicorn<'a, D> {
/// To be populated via `context_save`. /// To be populated via `context_save`.
pub fn context_alloc(&self) -> Result<Context, uc_error> { pub fn context_alloc(&self) -> Result<Context, uc_error> {
let mut empty_context: ffi::uc_context = ptr::null_mut(); let mut empty_context: ffi::uc_context = ptr::null_mut();
unsafe { ffi::uc_context_alloc(self.get_handle(), &mut empty_context) } unsafe { ffi::uc_context_alloc(self.get_handle(), &mut empty_context) }.and(Ok(Context {
.and(Ok(Context { context: empty_context })) context: empty_context,
}))
} }
/// Save current Unicorn context to previously allocated Context struct. /// Save current Unicorn context to previously allocated Context struct.
pub fn context_save( pub fn context_save(&self, context: &mut Context) -> Result<(), uc_error> {
&self,
context: &mut Context,
) -> Result<(), uc_error> {
unsafe { ffi::uc_context_save(self.get_handle(), context.context) }.into() unsafe { ffi::uc_context_save(self.get_handle(), context.context) }.into()
} }
@@ -930,7 +927,9 @@ impl<'a, D> Unicorn<'a, D> {
unsafe { unsafe {
ffi::uc_context_alloc(self.get_handle(), &mut new_context).and_then(|| { ffi::uc_context_alloc(self.get_handle(), &mut new_context).and_then(|| {
ffi::uc_context_save(self.get_handle(), new_context) ffi::uc_context_save(self.get_handle(), new_context)
.and(Ok(Context { context: new_context })) .and(Ok(Context {
context: new_context,
}))
.map_err(|e| { .map_err(|e| {
ffi::uc_context_free(new_context); ffi::uc_context_free(new_context);
e e
@@ -944,10 +943,7 @@ impl<'a, D> Unicorn<'a, D> {
/// Perform a quick rollback of the CPU context, including registers and some /// Perform a quick rollback of the CPU context, including registers and some
/// internal metadata. Contexts may not be shared across engine instances with /// internal metadata. Contexts may not be shared across engine instances with
/// differing arches or modes. Memory has to be restored manually, if needed. /// differing arches or modes. Memory has to be restored manually, if needed.
pub fn context_restore( pub fn context_restore(&self, context: &Context) -> Result<(), uc_error> {
&self,
context: &Context,
) -> Result<(), uc_error> {
unsafe { ffi::uc_context_restore(self.get_handle(), context.context) }.into() unsafe { ffi::uc_context_restore(self.get_handle(), context.context) }.into()
} }
@@ -978,107 +974,162 @@ impl<'a, D> Unicorn<'a, D> {
/// Query the internal status of the engine. /// Query the internal status of the engine.
/// ///
/// supported: `MODE`, `PAGE_SIZE`, `ARCH` /// supported: `MODE`, `PAGE_SIZE`, `ARCH`
pub fn query( pub fn query(&self, query: Query) -> Result<usize, uc_error> {
&self,
query: Query,
) -> Result<usize, uc_error> {
let mut result: libc::size_t = Default::default(); let mut result: libc::size_t = Default::default();
unsafe { ffi::uc_query(self.get_handle(), query, &mut result) }.and(Ok(result)) unsafe { ffi::uc_query(self.get_handle(), query, &mut result) }.and(Ok(result))
} }
/// Get the `i32` register value for the program counter for the specified architecture.
///
/// If an architecture is not compiled in, this function will return `uc_error::ARCH`.
#[inline]
fn arch_to_pc_register(arch: Arch) -> Result<i32, uc_error> {
match arch {
#[cfg(feature = "arch_x86")]
Arch::X86 => Ok(RegisterX86::RIP as i32),
#[cfg(feature = "arch_arm")]
Arch::ARM => Ok(RegisterARM::PC as i32),
#[cfg(feature = "arch_arm")]
Arch::ARM64 => Ok(RegisterARM64::PC as i32),
#[cfg(feature = "arch_mips")]
Arch::MIPS => Ok(RegisterMIPS::PC as i32),
#[cfg(feature = "arch_sparc")]
Arch::SPARC => Ok(RegisterSPARC::PC as i32),
#[cfg(feature = "arch_m68k")]
Arch::M68K => Ok(RegisterM68K::PC as i32),
#[cfg(feature = "arch_ppc")]
Arch::PPC => Ok(RegisterPPC::PC as i32),
#[cfg(feature = "arch_riscv")]
Arch::RISCV => Ok(RegisterRISCV::PC as i32),
#[cfg(feature = "arch_s390x")]
Arch::S390X => Ok(RegisterS390X::PC as i32),
#[cfg(feature = "arch_tricore")]
Arch::TRICORE => Ok(RegisterTRICORE::PC as i32),
// returns `uc_error::ARCH` for `Arch::MAX`, and any
// other architecture that are not compiled in
_ => Err(uc_error::ARCH),
}
}
/// Gets the current program counter for this `unicorn` instance. /// Gets the current program counter for this `unicorn` instance.
#[inline] #[inline]
pub fn pc_read(&self) -> Result<u64, uc_error> { pub fn pc_read(&self) -> Result<u64, uc_error> {
let arch = self.get_arch(); let arch = self.get_arch();
let reg = match arch {
Arch::X86 => RegisterX86::RIP as i32, self.reg_read(Self::arch_to_pc_register(arch)?)
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,
Arch::PPC => RegisterPPC::PC as i32,
Arch::RISCV => RegisterRISCV::PC as i32,
Arch::S390X => RegisterS390X::PC as i32,
Arch::TRICORE => RegisterTRICORE::PC as i32,
Arch::MAX => panic!("Illegal Arch specified"),
};
self.reg_read(reg)
} }
/// Sets the program counter for this `unicorn` instance. /// Sets the program counter for this `unicorn` instance.
#[inline] #[inline]
pub fn set_pc( pub fn set_pc(&mut self, value: u64) -> Result<(), uc_error> {
&mut self,
value: u64,
) -> Result<(), uc_error> {
let arch = self.get_arch(); let arch = self.get_arch();
let reg = match arch {
Arch::X86 => RegisterX86::RIP as i32, self.reg_write(Self::arch_to_pc_register(arch)?, value)
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,
Arch::PPC => RegisterPPC::PC as i32,
Arch::RISCV => RegisterRISCV::PC as i32,
Arch::S390X => RegisterS390X::PC as i32,
Arch::TRICORE => RegisterTRICORE::PC as i32,
Arch::MAX => panic!("Illegal Arch specified"),
};
self.reg_write(reg, value)
} }
pub fn ctl_get_mode(&self) -> Result<Mode, uc_error> { pub fn ctl_get_mode(&self) -> Result<Mode, uc_error> {
let mut result: i32 = Default::default(); let mut result: i32 = Default::default();
unsafe { ffi::uc_ctl(self.get_handle(), UC_CTL_READ!(ControlType::UC_CTL_UC_MODE), &mut result) } unsafe {
.and_then(|| Ok(Mode::from_bits_truncate(result))) ffi::uc_ctl(
self.get_handle(),
UC_CTL_READ!(ControlType::UC_CTL_UC_MODE),
&mut result,
)
}
.and_then(|| Ok(Mode::from_bits_truncate(result)))
} }
pub fn ctl_get_page_size(&self) -> Result<u32, uc_error> { pub fn ctl_get_page_size(&self) -> Result<u32, uc_error> {
let mut result: u32 = Default::default(); let mut result: u32 = Default::default();
unsafe { ffi::uc_ctl(self.get_handle(), UC_CTL_READ!(ControlType::UC_CTL_UC_PAGE_SIZE), &mut result) } unsafe {
.and_then(|| Ok(result)) ffi::uc_ctl(
self.get_handle(),
UC_CTL_READ!(ControlType::UC_CTL_UC_PAGE_SIZE),
&mut result,
)
}
.and_then(|| Ok(result))
} }
pub fn ctl_set_page_size( pub fn ctl_set_page_size(&self, page_size: u32) -> Result<(), uc_error> {
&self, unsafe {
page_size: u32, ffi::uc_ctl(
) -> Result<(), uc_error> { self.get_handle(),
unsafe { ffi::uc_ctl(self.get_handle(), UC_CTL_WRITE!(ControlType::UC_CTL_UC_PAGE_SIZE), page_size) }.into() UC_CTL_WRITE!(ControlType::UC_CTL_UC_PAGE_SIZE),
page_size,
)
}
.into()
} }
pub fn ctl_get_arch(&self) -> Result<Arch, uc_error> { pub fn ctl_get_arch(&self) -> Result<Arch, uc_error> {
let mut result: i32 = Default::default(); let mut result: i32 = Default::default();
unsafe { ffi::uc_ctl(self.get_handle(), UC_CTL_READ!(ControlType::UC_CTL_UC_ARCH), &mut result) } unsafe {
.and_then(|| Arch::try_from(result as usize)) ffi::uc_ctl(
self.get_handle(),
UC_CTL_READ!(ControlType::UC_CTL_UC_ARCH),
&mut result,
)
}
.and_then(|| Arch::try_from(result as usize))
} }
pub fn ctl_get_timeout(&self) -> Result<u64, uc_error> { pub fn ctl_get_timeout(&self) -> Result<u64, uc_error> {
let mut result: u64 = Default::default(); let mut result: u64 = Default::default();
unsafe { ffi::uc_ctl(self.get_handle(), UC_CTL_READ!(ControlType::UC_CTL_UC_TIMEOUT), &mut result) } unsafe {
.and(Ok(result)) ffi::uc_ctl(
self.get_handle(),
UC_CTL_READ!(ControlType::UC_CTL_UC_TIMEOUT),
&mut result,
)
}
.and(Ok(result))
} }
pub fn ctl_exits_enable(&self) -> Result<(), uc_error> { pub fn ctl_exits_enable(&self) -> Result<(), uc_error> {
unsafe { ffi::uc_ctl(self.get_handle(), UC_CTL_WRITE!(ControlType::UC_CTL_UC_USE_EXITS), 1) }.into() unsafe {
ffi::uc_ctl(
self.get_handle(),
UC_CTL_WRITE!(ControlType::UC_CTL_UC_USE_EXITS),
1,
)
}
.into()
} }
pub fn ctl_exits_disable(&self) -> Result<(), uc_error> { pub fn ctl_exits_disable(&self) -> Result<(), uc_error> {
unsafe { ffi::uc_ctl(self.get_handle(), UC_CTL_WRITE!(ControlType::UC_CTL_UC_USE_EXITS), 0) }.into() unsafe {
ffi::uc_ctl(
self.get_handle(),
UC_CTL_WRITE!(ControlType::UC_CTL_UC_USE_EXITS),
0,
)
}
.into()
} }
pub fn ctl_get_exits_count(&self) -> Result<usize, uc_error> { pub fn ctl_get_exits_count(&self) -> Result<usize, uc_error> {
let mut result: libc::size_t = 0usize; let mut result: libc::size_t = 0usize;
unsafe { ffi::uc_ctl(self.get_handle(), UC_CTL_READ!(ControlType::UC_CTL_UC_EXITS_CNT), &mut result) } unsafe {
.and(Ok(result)) ffi::uc_ctl(
self.get_handle(),
UC_CTL_READ!(ControlType::UC_CTL_UC_EXITS_CNT),
&mut result,
)
}
.and(Ok(result))
} }
pub fn ctl_get_exits(&self) -> Result<Vec<u64>, uc_error> { pub fn ctl_get_exits(&self) -> Result<Vec<u64>, uc_error> {
let exits_count: libc::size_t = self.ctl_get_exits_count()?; let exits_count: libc::size_t = self.ctl_get_exits_count()?;
let mut exits: Vec<u64> = Vec::with_capacity(exits_count); let mut exits: Vec<u64> = Vec::with_capacity(exits_count);
unsafe { unsafe {
ffi::uc_ctl(self.get_handle(), UC_CTL_READ!(ControlType::UC_CTL_UC_EXITS), exits.as_mut_ptr(), exits_count) ffi::uc_ctl(
self.get_handle(),
UC_CTL_READ!(ControlType::UC_CTL_UC_EXITS),
exits.as_mut_ptr(),
exits_count,
)
} }
.and_then(|| unsafe { .and_then(|| unsafe {
exits.set_len(exits_count); exits.set_len(exits_count);
@@ -1086,10 +1137,7 @@ impl<'a, D> Unicorn<'a, D> {
}) })
} }
pub fn ctl_set_exits( pub fn ctl_set_exits(&self, exits: &[u64]) -> Result<(), uc_error> {
&self,
exits: &[u64],
) -> Result<(), uc_error> {
unsafe { unsafe {
ffi::uc_ctl( ffi::uc_ctl(
self.get_handle(), self.get_handle(),
@@ -1103,24 +1151,37 @@ impl<'a, D> Unicorn<'a, D> {
pub fn ctl_get_cpu_model(&self) -> Result<i32, uc_error> { pub fn ctl_get_cpu_model(&self) -> Result<i32, uc_error> {
let mut result: i32 = Default::default(); let mut result: i32 = Default::default();
unsafe { ffi::uc_ctl(self.get_handle(), UC_CTL_READ!(ControlType::UC_CTL_CPU_MODEL), &mut result) } unsafe {
.and(Ok(result)) ffi::uc_ctl(
self.get_handle(),
UC_CTL_READ!(ControlType::UC_CTL_CPU_MODEL),
&mut result,
)
}
.and(Ok(result))
} }
pub fn ctl_set_cpu_model( pub fn ctl_set_cpu_model(&self, cpu_model: i32) -> Result<(), uc_error> {
&self, unsafe {
cpu_model: i32, ffi::uc_ctl(
) -> Result<(), uc_error> { self.get_handle(),
unsafe { ffi::uc_ctl(self.get_handle(), UC_CTL_WRITE!(ControlType::UC_CTL_CPU_MODEL), cpu_model) }.into() UC_CTL_WRITE!(ControlType::UC_CTL_CPU_MODEL),
cpu_model,
)
}
.into()
} }
pub fn ctl_remove_cache( pub fn ctl_remove_cache(&self, address: u64, end: u64) -> Result<(), uc_error> {
&self, unsafe {
address: u64, ffi::uc_ctl(
end: u64, self.get_handle(),
) -> Result<(), uc_error> { UC_CTL_WRITE!(ControlType::UC_CTL_TB_REMOVE_CACHE),
unsafe { ffi::uc_ctl(self.get_handle(), UC_CTL_WRITE!(ControlType::UC_CTL_TB_REMOVE_CACHE), address, end) } address,
.into() end,
)
}
.into()
} }
pub fn ctl_request_cache( pub fn ctl_request_cache(
@@ -1128,29 +1189,56 @@ impl<'a, D> Unicorn<'a, D> {
address: u64, address: u64,
tb: &mut TranslationBlock, tb: &mut TranslationBlock,
) -> Result<(), uc_error> { ) -> Result<(), uc_error> {
unsafe { ffi::uc_ctl(self.get_handle(), UC_CTL_READ_WRITE!(ControlType::UC_CTL_TB_REQUEST_CACHE), address, tb) } unsafe {
.into() ffi::uc_ctl(
self.get_handle(),
UC_CTL_READ_WRITE!(ControlType::UC_CTL_TB_REQUEST_CACHE),
address,
tb,
)
}
.into()
} }
pub fn ctl_flush_tb(&self) -> Result<(), uc_error> { pub fn ctl_flush_tb(&self) -> Result<(), uc_error> {
unsafe { ffi::uc_ctl(self.get_handle(), UC_CTL_WRITE!(ControlType::UC_CTL_TB_FLUSH)) }.into() unsafe {
ffi::uc_ctl(
self.get_handle(),
UC_CTL_WRITE!(ControlType::UC_CTL_TB_FLUSH),
)
}
.into()
} }
pub fn ctl_flush_tlb(&self) -> Result<(), uc_error> { pub fn ctl_flush_tlb(&self) -> Result<(), uc_error> {
unsafe { ffi::uc_ctl(self.get_handle(), UC_CTL_WRITE!(ControlType::UC_CTL_TLB_FLUSH)) }.into() unsafe {
ffi::uc_ctl(
self.get_handle(),
UC_CTL_WRITE!(ControlType::UC_CTL_TLB_FLUSH),
)
}
.into()
} }
pub fn ctl_context_mode( pub fn ctl_context_mode(&self, mode: ContextMode) -> Result<(), uc_error> {
&self, unsafe {
mode: ContextMode, ffi::uc_ctl(
) -> Result<(), uc_error> { self.get_handle(),
unsafe { ffi::uc_ctl(self.get_handle(), UC_CTL_WRITE!(ControlType::UC_CTL_CONTEXT_MODE), mode) }.into() UC_CTL_WRITE!(ControlType::UC_CTL_CONTEXT_MODE),
mode,
)
}
.into()
} }
pub fn ctl_tlb_type( pub fn ctl_tlb_type(&self, t: TlbType) -> Result<(), uc_error> {
&self, unsafe {
t: TlbType, ffi::uc_ctl(
) -> Result<(), uc_error> { self.get_handle(),
unsafe { ffi::uc_ctl(self.get_handle(), UC_CTL_WRITE!(ControlType::UC_CTL_TLB_TYPE), t as i32) }.into() UC_CTL_WRITE!(ControlType::UC_CTL_TLB_TYPE),
t as i32,
)
}
.into()
} }
} }

View File

@@ -14,37 +14,34 @@ pub const MILISECOND_SCALE: u64 = 1_000;
#[derive(PartialEq, Debug, Clone, Copy)] #[derive(PartialEq, Debug, Clone, Copy)]
#[allow(clippy::upper_case_acronyms)] #[allow(clippy::upper_case_acronyms)]
pub enum uc_error { pub enum uc_error {
OK = 0, OK = 0,
NOMEM = 1, NOMEM = 1,
ARCH = 2, ARCH = 2,
HANDLE = 3, HANDLE = 3,
MODE = 4, MODE = 4,
VERSION = 5, VERSION = 5,
READ_UNMAPPED = 6, READ_UNMAPPED = 6,
WRITE_UNMAPPED = 7, WRITE_UNMAPPED = 7,
FETCH_UNMAPPED = 8, FETCH_UNMAPPED = 8,
HOOK = 9, HOOK = 9,
INSN_INVALID = 10, INSN_INVALID = 10,
MAP = 11, MAP = 11,
WRITE_PROT = 12, WRITE_PROT = 12,
READ_PROT = 13, READ_PROT = 13,
FETCH_PROT = 14, FETCH_PROT = 14,
ARG = 15, ARG = 15,
READ_UNALIGNED = 16, READ_UNALIGNED = 16,
WRITE_UNALIGNED = 17, WRITE_UNALIGNED = 17,
FETCH_UNALIGNED = 18, FETCH_UNALIGNED = 18,
HOOK_EXIST = 19, HOOK_EXIST = 19,
RESOURCE = 20, RESOURCE = 20,
EXCEPTION = 21, EXCEPTION = 21,
} }
impl uc_error { impl uc_error {
/// Calls op if the result is Ok, otherwise returns the Err value of self. /// Calls op if the result is Ok, otherwise returns the Err value of self.
/// This function can be used for control flow based on Result values. /// This function can be used for control flow based on Result values.
pub fn and_then<U, F: FnOnce() -> Result<U, uc_error>>( pub fn and_then<U, F: FnOnce() -> Result<U, uc_error>>(self, op: F) -> Result<U, uc_error> {
self,
op: F,
) -> Result<U, uc_error> {
if let Self::OK = self { if let Self::OK = self {
op() op()
} else { } else {
@@ -55,10 +52,7 @@ impl uc_error {
/// Returns res if the result is Ok, otherwise returns the Err value of self. /// Returns res if the result is Ok, otherwise returns the Err value of self.
/// Arguments passed to and are eagerly evaluated; if you are passing the result /// Arguments passed to and are eagerly evaluated; if you are passing the result
/// of a function call, it is recommended to use and_then, which is lazily evaluated. /// of a function call, it is recommended to use and_then, which is lazily evaluated.
pub fn and<U>( pub fn and<U>(self, res: Result<U, uc_error>) -> Result<U, uc_error> {
self,
res: Result<U, uc_error>,
) -> Result<U, uc_error> {
if let Self::OK = self { if let Self::OK = self {
res res
} else { } else {
@@ -80,22 +74,22 @@ impl From<uc_error> for Result<(), uc_error> {
#[repr(C)] #[repr(C)]
#[derive(PartialEq, Debug, Clone, Copy)] #[derive(PartialEq, Debug, Clone, Copy)]
pub enum MemType { pub enum MemType {
READ = 16, READ = 16,
WRITE = 17, WRITE = 17,
FETCH = 18, FETCH = 18,
READ_UNMAPPED = 19, READ_UNMAPPED = 19,
WRITE_UNMAPPED = 20, WRITE_UNMAPPED = 20,
FETCH_UNMAPPED = 21, FETCH_UNMAPPED = 21,
WRITE_PROT = 22, WRITE_PROT = 22,
READ_PROT = 23, READ_PROT = 23,
FETCH_PROT = 24, FETCH_PROT = 24,
READ_AFTER = 25, READ_AFTER = 25,
} }
#[repr(C)] #[repr(C)]
#[derive(PartialEq, Debug, Clone, Copy)] #[derive(PartialEq, Debug, Clone, Copy)]
pub enum TlbType { pub enum TlbType {
CPU = 0, CPU = 0,
VIRTUAL = 1, VIRTUAL = 1,
} }
@@ -142,10 +136,10 @@ bitflags! {
#[derive(PartialEq, Debug, Clone, Copy)] #[derive(PartialEq, Debug, Clone, Copy)]
#[allow(clippy::upper_case_acronyms)] #[allow(clippy::upper_case_acronyms)]
pub enum Query { pub enum Query {
MODE = 1, MODE = 1,
PAGE_SIZE = 2, PAGE_SIZE = 2,
ARCH = 3, ARCH = 3,
TIMEOUT = 4, TIMEOUT = 4,
} }
bitflags! { bitflags! {
@@ -171,17 +165,17 @@ pub struct MemRegion {
#[repr(C)] #[repr(C)]
#[derive(PartialEq, Debug, Clone, Copy)] #[derive(PartialEq, Debug, Clone, Copy)]
pub enum Arch { pub enum Arch {
ARM = 1, ARM = 1,
ARM64 = 2, ARM64 = 2,
MIPS = 3, MIPS = 3,
X86 = 4, X86 = 4,
PPC = 5, PPC = 5,
SPARC = 6, SPARC = 6,
M68K = 7, M68K = 7,
RISCV = 8, RISCV = 8,
S390X = 9, S390X = 9,
TRICORE = 10, TRICORE = 10,
MAX = 11, MAX = 11,
} }
impl TryFrom<usize> for Arch { impl TryFrom<usize> for Arch {
@@ -268,23 +262,23 @@ macro_rules! UC_CTL_READ_WRITE {
#[allow(clippy::upper_case_acronyms)] #[allow(clippy::upper_case_acronyms)]
#[repr(u64)] #[repr(u64)]
pub enum ControlType { pub enum ControlType {
UC_CTL_UC_MODE = 0, UC_CTL_UC_MODE = 0,
UC_CTL_UC_PAGE_SIZE = 1, UC_CTL_UC_PAGE_SIZE = 1,
UC_CTL_UC_ARCH = 2, UC_CTL_UC_ARCH = 2,
UC_CTL_UC_TIMEOUT = 3, UC_CTL_UC_TIMEOUT = 3,
UC_CTL_UC_USE_EXITS = 4, UC_CTL_UC_USE_EXITS = 4,
UC_CTL_UC_EXITS_CNT = 5, UC_CTL_UC_EXITS_CNT = 5,
UC_CTL_UC_EXITS = 6, UC_CTL_UC_EXITS = 6,
UC_CTL_CPU_MODEL = 7, UC_CTL_CPU_MODEL = 7,
UC_CTL_TB_REQUEST_CACHE = 8, UC_CTL_TB_REQUEST_CACHE = 8,
UC_CTL_TB_REMOVE_CACHE = 9, UC_CTL_TB_REMOVE_CACHE = 9,
UC_CTL_TB_FLUSH = 10, UC_CTL_TB_FLUSH = 10,
UC_CTL_TLB_FLUSH = 11, UC_CTL_TLB_FLUSH = 11,
UC_CTL_TLB_TYPE = 12, UC_CTL_TLB_TYPE = 12,
UC_CTL_TCG_BUFFER_SIZE = 13, UC_CTL_TCG_BUFFER_SIZE = 13,
UC_CTL_CONTEXT_MODE = 14, UC_CTL_CONTEXT_MODE = 14,
UC_CTL_IO_READ = 1 << 31, UC_CTL_IO_READ = 1 << 31,
UC_CTL_IO_WRITE = 1 << 30, UC_CTL_IO_WRITE = 1 << 30,
} }
bitflags! { bitflags! {