use crate::address::{Address, AddressDelta, AddressSize};
use crate::error::{self, Error, Result, Status};
use std::ffi::CString;
use std::mem::MaybeUninit;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[repr(i32)]
pub enum OperandType {
None = 0,
Register = 1,
MemoryDirect = 2,
MemoryPhrase = 3,
MemoryDisplacement = 4,
Immediate = 5,
FarAddress = 6,
NearAddress = 7,
ProcessorSpecific0 = 8,
ProcessorSpecific1 = 9,
ProcessorSpecific2 = 10,
ProcessorSpecific3 = 11,
ProcessorSpecific4 = 12,
ProcessorSpecific5 = 13,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[repr(i32)]
pub enum OperandFormat {
Default = 0,
Hex = 1,
Decimal = 2,
Octal = 3,
Binary = 4,
Character = 5,
Float = 6,
Offset = 7,
StackVariable = 8,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[repr(i32)]
pub enum RegisterClass {
Unknown = 0,
GeneralPurpose = 1,
Segment = 2,
FloatingPoint = 3,
Vector = 4,
Mask = 5,
Control = 6,
Debug = 7,
Other = 8,
}
fn register_class_from_i32(value: i32) -> Result<RegisterClass> {
match value {
0 => Ok(RegisterClass::Unknown),
1 => Ok(RegisterClass::GeneralPurpose),
2 => Ok(RegisterClass::Segment),
3 => Ok(RegisterClass::FloatingPoint),
4 => Ok(RegisterClass::Vector),
5 => Ok(RegisterClass::Mask),
6 => Ok(RegisterClass::Control),
7 => Ok(RegisterClass::Debug),
8 => Ok(RegisterClass::Other),
_ => Err(Error::validation("invalid register class value")),
}
}
#[derive(Debug, Clone)]
pub struct StructOffsetPath {
pub structure_ids: Vec<u64>,
pub delta: AddressDelta,
}
#[derive(Debug, Clone)]
pub struct Operand {
index: i32,
op_type: OperandType,
reg: u16,
value: u64,
addr: Address,
byte_width: i32,
reg_name: String,
reg_class: RegisterClass,
}
impl Operand {
pub fn index(&self) -> i32 {
self.index
}
pub fn op_type(&self) -> OperandType {
self.op_type
}
pub fn is_register(&self) -> bool {
self.op_type == OperandType::Register
}
pub fn is_immediate(&self) -> bool {
self.op_type == OperandType::Immediate
}
pub fn is_memory(&self) -> bool {
matches!(
self.op_type,
OperandType::MemoryDirect | OperandType::MemoryPhrase | OperandType::MemoryDisplacement
)
}
pub fn register_id(&self) -> u16 {
self.reg
}
pub fn value(&self) -> u64 {
self.value
}
pub fn target_address(&self) -> Address {
self.addr
}
pub fn displacement(&self) -> i64 {
self.value as i64
}
pub fn byte_width(&self) -> i32 {
self.byte_width
}
pub fn register_name(&self) -> &str {
&self.reg_name
}
pub fn register_class(&self) -> RegisterClass {
self.reg_class
}
pub fn is_vector_register(&self) -> bool {
self.reg_class == RegisterClass::Vector
}
pub fn is_mask_register(&self) -> bool {
self.reg_class == RegisterClass::Mask
}
}
#[derive(Debug, Clone)]
pub struct Instruction {
ea: Address,
insn_size: AddressSize,
itype: u16,
insn_mnemonic: String,
operands: Vec<Operand>,
}
impl Instruction {
pub fn address(&self) -> Address {
self.ea
}
pub fn size(&self) -> AddressSize {
self.insn_size
}
pub fn opcode(&self) -> u16 {
self.itype
}
pub fn mnemonic(&self) -> &str {
&self.insn_mnemonic
}
pub fn operand_count(&self) -> usize {
self.operands.len()
}
pub fn operand(&self, index: usize) -> Result<&Operand> {
self.operands
.get(index)
.ok_or_else(|| Error::validation(format!("operand index {} out of range", index)))
}
pub fn operands(&self) -> &[Operand] {
&self.operands
}
}
unsafe fn instruction_from_ffi(raw: &idax_sys::IdaxInstruction) -> Result<Instruction> {
let insn_mnemonic = unsafe { error::cstr_to_string(raw.mnemonic, "mnemonic")? };
let mut operands = Vec::new();
if !raw.operands.is_null() && raw.operand_count > 0 {
let op_slice = unsafe { std::slice::from_raw_parts(raw.operands, raw.operand_count) };
for op in op_slice {
let op_type = match op.type_ {
0 => OperandType::None,
1 => OperandType::Register,
2 => OperandType::MemoryDirect,
3 => OperandType::MemoryPhrase,
4 => OperandType::MemoryDisplacement,
5 => OperandType::Immediate,
6 => OperandType::FarAddress,
7 => OperandType::NearAddress,
_ => OperandType::None,
};
let reg_class = match op.register_class {
0 => RegisterClass::Unknown,
1 => RegisterClass::GeneralPurpose,
2 => RegisterClass::Segment,
3 => RegisterClass::FloatingPoint,
4 => RegisterClass::Vector,
5 => RegisterClass::Mask,
6 => RegisterClass::Control,
7 => RegisterClass::Debug,
_ => RegisterClass::Other,
};
operands.push(Operand {
index: op.index,
op_type,
reg: op.register_id,
value: op.value,
addr: op.target_address,
byte_width: op.byte_width,
reg_name: unsafe {
error::cstr_to_string(op.register_name, "reg name").unwrap_or_default()
},
reg_class,
});
}
}
Ok(Instruction {
ea: raw.address,
insn_size: raw.size,
itype: raw.opcode,
insn_mnemonic,
operands,
})
}
pub fn decode(address: Address) -> Result<Instruction> {
unsafe {
let mut raw = MaybeUninit::<idax_sys::IdaxInstruction>::zeroed();
let ret = idax_sys::idax_instruction_decode(address, raw.as_mut_ptr());
if ret != 0 {
return Err(error::consume_last_error("instruction decode failed"));
}
let raw = raw.assume_init();
let result = instruction_from_ffi(&raw);
idax_sys::idax_instruction_free(&raw as *const _ as *mut _);
result
}
}
pub fn create(address: Address) -> Result<Instruction> {
unsafe {
let mut raw = MaybeUninit::<idax_sys::IdaxInstruction>::zeroed();
let ret = idax_sys::idax_instruction_create(address, raw.as_mut_ptr());
if ret != 0 {
return Err(error::consume_last_error("instruction create failed"));
}
let raw = raw.assume_init();
let result = instruction_from_ffi(&raw);
idax_sys::idax_instruction_free(&raw as *const _ as *mut _);
result
}
}
pub fn text(address: Address) -> Result<String> {
unsafe {
let mut out: *mut std::ffi::c_char = std::ptr::null_mut();
let ret = idax_sys::idax_instruction_text(address, &mut out);
if ret != 0 {
return Err(error::consume_last_error("instruction::text failed"));
}
Ok(error::consume_c_string(out))
}
}
pub fn set_operand_hex(address: Address, n: i32) -> Status {
let ret = unsafe { idax_sys::idax_instruction_set_operand_hex(address, n) };
error::int_to_status(ret, "set_operand_hex failed")
}
pub fn set_operand_decimal(address: Address, n: i32) -> Status {
let ret = unsafe { idax_sys::idax_instruction_set_operand_decimal(address, n) };
error::int_to_status(ret, "set_operand_decimal failed")
}
pub fn set_operand_octal(address: Address, n: i32) -> Status {
let ret = unsafe { idax_sys::idax_instruction_set_operand_octal(address, n) };
error::int_to_status(ret, "set_operand_octal failed")
}
pub fn set_operand_binary(address: Address, n: i32) -> Status {
let ret = unsafe { idax_sys::idax_instruction_set_operand_binary(address, n) };
error::int_to_status(ret, "set_operand_binary failed")
}
pub fn set_operand_character(address: Address, n: i32) -> Status {
let ret = unsafe { idax_sys::idax_instruction_set_operand_character(address, n) };
error::int_to_status(ret, "set_operand_character failed")
}
pub fn set_operand_float(address: Address, n: i32) -> Status {
let ret = unsafe { idax_sys::idax_instruction_set_operand_float(address, n) };
error::int_to_status(ret, "set_operand_float failed")
}
pub fn set_operand_format(
address: Address,
n: i32,
format: OperandFormat,
base: Address,
) -> Status {
let ret =
unsafe { idax_sys::idax_instruction_set_operand_format(address, n, format as i32, base) };
error::int_to_status(ret, "set_operand_format failed")
}
pub fn set_operand_offset(address: Address, n: i32, base: Address) -> Status {
let ret = unsafe { idax_sys::idax_instruction_set_operand_offset(address, n, base) };
error::int_to_status(ret, "set_operand_offset failed")
}
pub fn set_operand_struct_offset_by_name(
address: Address,
n: i32,
structure_name: &str,
delta: AddressDelta,
) -> Status {
let c_structure_name =
CString::new(structure_name).map_err(|_| Error::validation("invalid structure name"))?;
let ret = unsafe {
idax_sys::idax_instruction_set_operand_struct_offset_by_name(
address,
n,
c_structure_name.as_ptr(),
delta,
)
};
error::int_to_status(ret, "set_operand_struct_offset_by_name failed")
}
pub fn set_operand_struct_offset_by_id(
address: Address,
n: i32,
structure_id: u64,
delta: AddressDelta,
) -> Status {
let ret = unsafe {
idax_sys::idax_instruction_set_operand_struct_offset_by_id(address, n, structure_id, delta)
};
error::int_to_status(ret, "set_operand_struct_offset_by_id failed")
}
pub fn set_operand_based_struct_offset(
address: Address,
n: i32,
operand_value: Address,
base: Address,
) -> Status {
let ret = unsafe {
idax_sys::idax_instruction_set_operand_based_struct_offset(address, n, operand_value, base)
};
error::int_to_status(ret, "set_operand_based_struct_offset failed")
}
pub fn operand_struct_offset_path(address: Address, n: i32) -> Result<StructOffsetPath> {
unsafe {
let mut out_ids: *mut u64 = std::ptr::null_mut();
let mut out_count: usize = 0;
let mut out_delta: AddressDelta = 0;
let ret = idax_sys::idax_instruction_operand_struct_offset_path(
address,
n,
&mut out_ids,
&mut out_count,
&mut out_delta,
);
if ret != 0 {
return Err(error::consume_last_error(
"operand_struct_offset_path failed",
));
}
let structure_ids = if out_ids.is_null() || out_count == 0 {
Vec::new()
} else {
std::slice::from_raw_parts(out_ids, out_count).to_vec()
};
if !out_ids.is_null() {
idax_sys::idax_free_addresses(out_ids);
}
Ok(StructOffsetPath {
structure_ids,
delta: out_delta,
})
}
}
pub fn operand_struct_offset_path_names(address: Address, n: i32) -> Result<Vec<String>> {
unsafe {
let mut out: *mut *mut std::ffi::c_char = std::ptr::null_mut();
let mut count: usize = 0;
let ret = idax_sys::idax_instruction_operand_struct_offset_path_names(
address, n, &mut out, &mut count,
);
if ret != 0 {
return Err(error::consume_last_error(
"operand_struct_offset_path_names failed",
));
}
let values_result: Result<Vec<String>> = if out.is_null() || count == 0 {
Ok(Vec::new())
} else {
let raw = std::slice::from_raw_parts(out, count);
raw.iter()
.map(|v| error::cstr_to_string(*v, "struct offset path name"))
.collect()
};
if !out.is_null() {
idax_sys::idax_instruction_string_array_free(out, count);
}
values_result
}
}
pub fn set_operand_stack_variable(address: Address, n: i32) -> Status {
let ret = unsafe { idax_sys::idax_instruction_set_operand_stack_variable(address, n) };
error::int_to_status(ret, "set_operand_stack_variable failed")
}
pub fn clear_operand_representation(address: Address, n: i32) -> Status {
let ret = unsafe { idax_sys::idax_instruction_clear_operand_representation(address, n) };
error::int_to_status(ret, "clear_operand_representation failed")
}
pub fn set_forced_operand(address: Address, n: i32, text: &str) -> Status {
let c_text = CString::new(text).map_err(|_| Error::validation("invalid text"))?;
let ret = unsafe { idax_sys::idax_instruction_set_forced_operand(address, n, c_text.as_ptr()) };
error::int_to_status(ret, "set_forced_operand failed")
}
pub fn get_forced_operand(address: Address, n: i32) -> Result<String> {
unsafe {
let mut out: *mut std::ffi::c_char = std::ptr::null_mut();
let ret = idax_sys::idax_instruction_get_forced_operand(address, n, &mut out);
if ret != 0 {
return Err(error::consume_last_error("get_forced_operand failed"));
}
Ok(error::consume_c_string(out))
}
}
pub fn operand_text(address: Address, n: i32) -> Result<String> {
unsafe {
let mut out: *mut std::ffi::c_char = std::ptr::null_mut();
let ret = idax_sys::idax_instruction_operand_text(address, n, &mut out);
if ret != 0 {
return Err(error::consume_last_error("operand_text failed"));
}
Ok(error::consume_c_string(out))
}
}
pub fn operand_byte_width(address: Address, n: i32) -> Result<i32> {
let mut out: i32 = 0;
let ret = unsafe { idax_sys::idax_instruction_operand_byte_width(address, n, &mut out) };
if ret != 0 {
Err(error::consume_last_error("operand_byte_width failed"))
} else {
Ok(out)
}
}
pub fn operand_register_name(address: Address, n: i32) -> Result<String> {
unsafe {
let mut out: *mut std::ffi::c_char = std::ptr::null_mut();
let ret = idax_sys::idax_instruction_operand_register_name(address, n, &mut out);
if ret != 0 {
return Err(error::consume_last_error("operand_register_name failed"));
}
Ok(error::consume_c_string(out))
}
}
pub fn operand_register_class(address: Address, n: i32) -> Result<RegisterClass> {
let mut out: i32 = 0;
let ret = unsafe { idax_sys::idax_instruction_operand_register_class(address, n, &mut out) };
if ret != 0 {
return Err(error::consume_last_error("operand_register_class failed"));
}
register_class_from_i32(out)
}
pub fn toggle_operand_sign(address: Address, n: i32) -> Status {
let ret = unsafe { idax_sys::idax_instruction_toggle_operand_sign(address, n) };
error::int_to_status(ret, "toggle_operand_sign failed")
}
pub fn toggle_operand_negate(address: Address, n: i32) -> Status {
let ret = unsafe { idax_sys::idax_instruction_toggle_operand_negate(address, n) };
error::int_to_status(ret, "toggle_operand_negate failed")
}
pub fn code_refs_from(address: Address) -> Result<Vec<Address>> {
unsafe {
let mut count: usize = 0;
let mut addrs: *mut u64 = std::ptr::null_mut();
let ret = idax_sys::idax_instruction_code_refs_from(address, &mut addrs, &mut count);
if ret != 0 {
return Err(error::consume_last_error("code_refs_from failed"));
}
let result = if addrs.is_null() || count == 0 {
Vec::new()
} else {
std::slice::from_raw_parts(addrs, count).to_vec()
};
if !addrs.is_null() {
idax_sys::idax_free_addresses(addrs);
}
Ok(result)
}
}
pub fn data_refs_from(address: Address) -> Result<Vec<Address>> {
unsafe {
let mut count: usize = 0;
let mut addrs: *mut u64 = std::ptr::null_mut();
let ret = idax_sys::idax_instruction_data_refs_from(address, &mut addrs, &mut count);
if ret != 0 {
return Err(error::consume_last_error("data_refs_from failed"));
}
let result = if addrs.is_null() || count == 0 {
Vec::new()
} else {
std::slice::from_raw_parts(addrs, count).to_vec()
};
if !addrs.is_null() {
idax_sys::idax_free_addresses(addrs);
}
Ok(result)
}
}
pub fn call_targets(address: Address) -> Result<Vec<Address>> {
unsafe {
let mut count: usize = 0;
let mut addrs: *mut u64 = std::ptr::null_mut();
let ret = idax_sys::idax_instruction_call_targets(address, &mut addrs, &mut count);
if ret != 0 {
return Err(error::consume_last_error("call_targets failed"));
}
let result = if addrs.is_null() || count == 0 {
Vec::new()
} else {
std::slice::from_raw_parts(addrs, count).to_vec()
};
if !addrs.is_null() {
idax_sys::idax_free_addresses(addrs);
}
Ok(result)
}
}
pub fn jump_targets(address: Address) -> Result<Vec<Address>> {
unsafe {
let mut count: usize = 0;
let mut addrs: *mut u64 = std::ptr::null_mut();
let ret = idax_sys::idax_instruction_jump_targets(address, &mut addrs, &mut count);
if ret != 0 {
return Err(error::consume_last_error("jump_targets failed"));
}
let result = if addrs.is_null() || count == 0 {
Vec::new()
} else {
std::slice::from_raw_parts(addrs, count).to_vec()
};
if !addrs.is_null() {
idax_sys::idax_free_addresses(addrs);
}
Ok(result)
}
}
pub fn has_fall_through(address: Address) -> bool {
unsafe { idax_sys::idax_instruction_has_fall_through(address) != 0 }
}
pub fn is_call(address: Address) -> bool {
unsafe { idax_sys::idax_instruction_is_call(address) != 0 }
}
pub fn is_return(address: Address) -> bool {
unsafe { idax_sys::idax_instruction_is_return(address) != 0 }
}
pub fn is_jump(address: Address) -> bool {
unsafe { idax_sys::idax_instruction_is_jump(address) != 0 }
}
pub fn is_conditional_jump(address: Address) -> bool {
unsafe { idax_sys::idax_instruction_is_conditional_jump(address) != 0 }
}
pub fn next(address: Address) -> Result<Instruction> {
unsafe {
let mut raw = MaybeUninit::<idax_sys::IdaxInstruction>::zeroed();
let ret = idax_sys::idax_instruction_next(address, raw.as_mut_ptr());
if ret != 0 {
return Err(error::consume_last_error("instruction::next failed"));
}
let raw = raw.assume_init();
let result = instruction_from_ffi(&raw);
idax_sys::idax_instruction_free(&raw as *const _ as *mut _);
result
}
}
pub fn prev(address: Address) -> Result<Instruction> {
unsafe {
let mut raw = MaybeUninit::<idax_sys::IdaxInstruction>::zeroed();
let ret = idax_sys::idax_instruction_prev(address, raw.as_mut_ptr());
if ret != 0 {
return Err(error::consume_last_error("instruction::prev failed"));
}
let raw = raw.assume_init();
let result = instruction_from_ffi(&raw);
idax_sys::idax_instruction_free(&raw as *const _ as *mut _);
result
}
}