pub(crate) mod cpuid_table;
pub(crate) mod enums;
pub(crate) mod factory;
pub(crate) mod info_table;
pub(crate) mod rflags_table;
#[cfg(test)]
mod tests;
use crate::iced_constants::IcedConstants;
pub use crate::info::factory::*;
use crate::*;
use alloc::vec::Vec;
use core::fmt;
#[derive(Default, Copy, Clone, Eq, PartialEq, Hash)]
pub struct UsedRegister {
register: Register,
access: OpAccess,
}
impl UsedRegister {
#[must_use]
#[inline]
pub const fn new(register: Register, access: OpAccess) -> Self {
Self { register, access }
}
#[must_use]
#[inline]
pub const fn register(&self) -> Register {
self.register
}
#[must_use]
#[inline]
pub const fn access(&self) -> OpAccess {
self.access
}
}
impl fmt::Debug for UsedRegister {
#[allow(clippy::missing_inline_in_public_items)]
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{:?}:{:?}", self.register(), self.access())?;
Ok(())
}
}
#[derive(Default, Copy, Clone, Eq, PartialEq, Hash)]
pub struct UsedMemory {
displacement: u64,
segment: Register,
base: Register,
index: Register,
scale: u8,
memory_size: MemorySize,
access: OpAccess,
address_size: CodeSize,
vsib_size: u8,
}
impl UsedMemory {
#[must_use]
#[inline]
pub const fn new(
segment: Register, base: Register, index: Register, scale: u32, displacement: u64, memory_size: MemorySize, access: OpAccess,
) -> Self {
Self { segment, base, index, scale: scale as u8, displacement, memory_size, access, address_size: CodeSize::Unknown, vsib_size: 0 }
}
#[must_use]
#[inline]
pub fn new2(
segment: Register, base: Register, index: Register, scale: u32, displacement: u64, memory_size: MemorySize, access: OpAccess,
address_size: CodeSize, vsib_size: u32,
) -> Self {
debug_assert!(vsib_size == 0 || vsib_size == 4 || vsib_size == 8);
Self { segment, base, index, scale: scale as u8, displacement, memory_size, access, address_size, vsib_size: vsib_size as u8 }
}
#[must_use]
#[inline]
pub const fn segment(&self) -> Register {
self.segment
}
#[must_use]
#[inline]
pub const fn base(&self) -> Register {
self.base
}
#[must_use]
#[inline]
pub const fn index(&self) -> Register {
self.index
}
#[must_use]
#[inline]
pub const fn scale(&self) -> u32 {
self.scale as u32
}
#[must_use]
#[inline]
pub const fn displacement(&self) -> u64 {
self.displacement
}
#[must_use]
#[inline]
pub const fn memory_size(&self) -> MemorySize {
self.memory_size
}
#[must_use]
#[inline]
pub const fn access(&self) -> OpAccess {
self.access
}
#[must_use]
#[inline]
pub const fn address_size(&self) -> CodeSize {
self.address_size
}
#[must_use]
#[inline]
pub const fn vsib_size(&self) -> u32 {
self.vsib_size as u32
}
#[must_use]
#[inline]
pub fn virtual_address<F>(&self, element_index: usize, get_register_value: F) -> Option<u64>
where
F: FnMut(Register, usize, usize) -> Option<u64>,
{
self.try_virtual_address(element_index, get_register_value)
}
#[must_use]
#[inline]
#[doc(hidden)]
pub fn try_virtual_address<F>(&self, element_index: usize, mut get_register_value: F) -> Option<u64>
where
F: FnMut(Register, usize, usize) -> Option<u64>,
{
let mut effective = self.displacement;
match self.base {
Register::None => {}
_ => {
let base = get_register_value(self.base, 0, 0)?;
effective = effective.wrapping_add(base)
}
}
match self.index {
Register::None => {}
_ => {
let mut index = get_register_value(self.index, element_index, self.vsib_size as usize)?;
if self.vsib_size == 4 {
index = index as i32 as u64;
}
effective = effective.wrapping_add(index.wrapping_mul(self.scale as u64))
}
}
match self.address_size {
CodeSize::Code16 => effective = effective as u16 as u64,
CodeSize::Code32 => effective = effective as u32 as u64,
_ => {}
}
match self.segment {
Register::None => {}
_ => {
let segment_base = get_register_value(self.segment, 0, 0)?;
effective = effective.wrapping_add(segment_base)
}
}
Some(effective)
}
}
impl fmt::Debug for UsedMemory {
#[allow(clippy::missing_inline_in_public_items)]
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "[{:?}:", self.segment())?;
let mut need_plus = if self.base() != Register::None {
write!(f, "{:?}", self.base())?;
true
} else {
false
};
if self.index() != Register::None {
if need_plus {
write!(f, "+")?;
}
need_plus = true;
write!(f, "{:?}", self.index())?;
if self.scale() != 1 {
write!(f, "*{}", self.scale())?;
}
}
if self.displacement() != 0 || !need_plus {
if need_plus {
write!(f, "+")?;
}
if self.displacement() <= 9 {
write!(f, "{}", self.displacement())?;
} else {
write!(f, "0x{:X}", self.displacement())?;
}
}
write!(f, ";{:?};{:?}]", self.memory_size(), self.access())?;
Ok(())
}
}
#[derive(Debug, Clone)]
pub struct InstructionInfo {
used_registers: Vec<UsedRegister>,
used_memory_locations: Vec<UsedMemory>,
op_accesses: [OpAccess; IcedConstants::MAX_OP_COUNT],
}
impl InstructionInfo {
#[must_use]
#[inline(always)]
fn new(options: u32) -> Self {
use crate::info::enums::InstrInfoConstants;
Self {
used_registers: if (options & InstructionInfoOptions::NO_REGISTER_USAGE) == 0 {
Vec::with_capacity(InstrInfoConstants::DEFAULT_USED_REGISTER_COLL_CAPACITY)
} else {
Vec::new()
},
used_memory_locations: if (options & InstructionInfoOptions::NO_MEMORY_USAGE) == 0 {
Vec::with_capacity(InstrInfoConstants::DEFAULT_USED_MEMORY_COLL_CAPACITY)
} else {
Vec::new()
},
op_accesses: [OpAccess::default(); IcedConstants::MAX_OP_COUNT],
}
}
#[must_use]
#[inline]
pub fn used_registers(&self) -> &[UsedRegister] {
self.used_registers.as_slice()
}
#[must_use]
#[inline]
pub fn used_memory(&self) -> &[UsedMemory] {
self.used_memory_locations.as_slice()
}
#[must_use]
#[inline]
pub const fn op0_access(&self) -> OpAccess {
self.op_accesses[0]
}
#[must_use]
#[inline]
pub const fn op1_access(&self) -> OpAccess {
self.op_accesses[1]
}
#[must_use]
#[inline]
pub const fn op2_access(&self) -> OpAccess {
self.op_accesses[2]
}
#[must_use]
#[inline]
pub const fn op3_access(&self) -> OpAccess {
self.op_accesses[3]
}
#[must_use]
#[inline]
pub const fn op4_access(&self) -> OpAccess {
self.op_accesses[4]
}
#[must_use]
#[inline]
pub fn op_access(&self, operand: u32) -> OpAccess {
match self.op_accesses.get(operand as usize) {
Some(&value) => value,
None => {
debug_assert!(false, "Invalid operand: {}", operand);
OpAccess::default()
}
}
}
#[inline]
#[doc(hidden)]
pub fn try_op_access(&self, operand: u32) -> Result<OpAccess, IcedError> {
self.op_accesses.get(operand as usize).map_or_else(|| Err(IcedError::new("Invalid operand")), |&op_access| Ok(op_access))
}
}