summaryrefslogtreecommitdiff
path: root/rust/src/medium_level_il
diff options
context:
space:
mode:
Diffstat (limited to 'rust/src/medium_level_il')
-rw-r--r--rust/src/medium_level_il/block.rs62
-rw-r--r--rust/src/medium_level_il/function.rs718
-rw-r--r--rust/src/medium_level_il/instruction.rs1657
-rw-r--r--rust/src/medium_level_il/lift.rs510
-rw-r--r--rust/src/medium_level_il/operation.rs581
5 files changed, 3528 insertions, 0 deletions
diff --git a/rust/src/medium_level_il/block.rs b/rust/src/medium_level_il/block.rs
new file mode 100644
index 00000000..1fcd51ca
--- /dev/null
+++ b/rust/src/medium_level_il/block.rs
@@ -0,0 +1,62 @@
+use crate::basic_block::{BasicBlock, BlockContext};
+use crate::rc::Ref;
+use std::ops::Range;
+
+use super::{MediumLevelILFunction, MediumLevelILInstruction, MediumLevelInstructionIndex};
+
+pub struct MediumLevelILBlock {
+ pub(crate) function: Ref<MediumLevelILFunction>,
+}
+
+impl BlockContext for MediumLevelILBlock {
+ type Instruction = MediumLevelILInstruction;
+ type InstructionIndex = MediumLevelInstructionIndex;
+ type Iter = MediumLevelILBlockIter;
+
+ fn start(&self, block: &BasicBlock<Self>) -> MediumLevelILInstruction {
+ // TODO: instruction_from_index says that it is not mapped and will do the call
+ // TODO: What if this IS already MAPPED!?!?!?
+ self.function
+ .instruction_from_index(block.start_index())
+ .unwrap()
+ }
+
+ fn iter(&self, block: &BasicBlock<Self>) -> MediumLevelILBlockIter {
+ MediumLevelILBlockIter {
+ function: self.function.to_owned(),
+ range: block.start_index().0..block.end_index().0,
+ }
+ }
+}
+
+impl std::fmt::Debug for MediumLevelILBlock {
+ fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
+ f.debug_struct("MediumLevelILBlock")
+ .field("function", &self.function)
+ .finish()
+ }
+}
+
+impl Clone for MediumLevelILBlock {
+ fn clone(&self) -> Self {
+ MediumLevelILBlock {
+ function: self.function.to_owned(),
+ }
+ }
+}
+
+pub struct MediumLevelILBlockIter {
+ function: Ref<MediumLevelILFunction>,
+ range: Range<usize>,
+}
+
+impl Iterator for MediumLevelILBlockIter {
+ type Item = MediumLevelILInstruction;
+
+ fn next(&mut self) -> Option<Self::Item> {
+ self.range
+ .next()
+ .map(MediumLevelInstructionIndex)
+ .and_then(|i| self.function.instruction_from_index(i))
+ }
+}
diff --git a/rust/src/medium_level_il/function.rs b/rust/src/medium_level_il/function.rs
new file mode 100644
index 00000000..a9802005
--- /dev/null
+++ b/rust/src/medium_level_il/function.rs
@@ -0,0 +1,718 @@
+use binaryninjacore_sys::*;
+use std::ffi::c_char;
+use std::fmt::{Debug, Formatter};
+use std::hash::{Hash, Hasher};
+
+use super::{MediumLevelILBlock, MediumLevelILInstruction, MediumLevelInstructionIndex};
+use crate::architecture::CoreArchitecture;
+use crate::basic_block::BasicBlock;
+use crate::confidence::Conf;
+use crate::disassembly::DisassemblySettings;
+use crate::flowgraph::FlowGraph;
+use crate::function::{Function, Location};
+use crate::rc::{Array, CoreArrayProvider, CoreArrayProviderInner, Ref, RefCountable};
+use crate::string::BnStrCompatible;
+use crate::types::Type;
+use crate::variable::{PossibleValueSet, RegisterValue, SSAVariable, UserVariableValue, Variable};
+
+// TODO: Does this belong here?
+pub use binaryninjacore_sys::BNFunctionGraphType as FunctionGraphType;
+
+pub struct MediumLevelILFunction {
+ pub(crate) handle: *mut BNMediumLevelILFunction,
+}
+
+impl MediumLevelILFunction {
+ pub(crate) unsafe fn from_raw(handle: *mut BNMediumLevelILFunction) -> Self {
+ debug_assert!(!handle.is_null());
+ Self { handle }
+ }
+
+ pub(crate) unsafe fn ref_from_raw(handle: *mut BNMediumLevelILFunction) -> Ref<Self> {
+ debug_assert!(!handle.is_null());
+ Ref::new(Self::from_raw(handle))
+ }
+
+ pub fn instruction_at<L: Into<Location>>(&self, loc: L) -> Option<MediumLevelILInstruction> {
+ Some(MediumLevelILInstruction::new(
+ self.to_owned(),
+ self.instruction_index_at(loc)?,
+ ))
+ }
+
+ pub fn instruction_index_at<L: Into<Location>>(
+ &self,
+ loc: L,
+ ) -> Option<MediumLevelInstructionIndex> {
+ let loc: Location = loc.into();
+ let arch = loc
+ .arch
+ .map(|a| a.handle)
+ .unwrap_or_else(std::ptr::null_mut);
+ let instr_idx = unsafe { BNMediumLevelILGetInstructionStart(self.handle, arch, loc.addr) };
+ // `instr_idx` will equal self.instruction_count() if the instruction is not valid.
+ if instr_idx >= self.instruction_count() {
+ None
+ } else {
+ Some(MediumLevelInstructionIndex(instr_idx))
+ }
+ }
+
+ pub fn instruction_from_index(
+ &self,
+ index: MediumLevelInstructionIndex,
+ ) -> Option<MediumLevelILInstruction> {
+ if index.0 >= self.instruction_count() {
+ None
+ } else {
+ Some(MediumLevelILInstruction::new(self.to_owned(), index))
+ }
+ }
+
+ pub fn instruction_from_expr_index(
+ &self,
+ expr_index: MediumLevelInstructionIndex,
+ ) -> Option<MediumLevelILInstruction> {
+ if expr_index.0 >= self.expression_count() {
+ None
+ } else {
+ Some(MediumLevelILInstruction::new_expr(
+ self.to_owned(),
+ expr_index,
+ ))
+ }
+ }
+
+ pub fn instruction_count(&self) -> usize {
+ unsafe { BNGetMediumLevelILInstructionCount(self.handle) }
+ }
+
+ pub fn expression_count(&self) -> usize {
+ unsafe { BNGetMediumLevelILExprCount(self.handle) }
+ }
+
+ pub fn ssa_form(&self) -> MediumLevelILFunction {
+ let ssa = unsafe { BNGetMediumLevelILSSAForm(self.handle) };
+ assert!(!ssa.is_null());
+ MediumLevelILFunction { handle: ssa }
+ }
+
+ pub fn function(&self) -> Ref<Function> {
+ unsafe {
+ let func = BNGetMediumLevelILOwnerFunction(self.handle);
+ Function::ref_from_raw(func)
+ }
+ }
+
+ pub fn basic_blocks(&self) -> Array<BasicBlock<MediumLevelILBlock>> {
+ let mut count = 0;
+ let blocks = unsafe { BNGetMediumLevelILBasicBlockList(self.handle, &mut count) };
+ let context = MediumLevelILBlock {
+ function: self.to_owned(),
+ };
+ unsafe { Array::new(blocks, count, context) }
+ }
+
+ pub fn var_definitions(&self, var: &Variable) -> Array<MediumLevelILInstruction> {
+ let mut count = 0;
+ let raw_var = BNVariable::from(var);
+ let raw_instr_idxs =
+ unsafe { BNGetMediumLevelILVariableDefinitions(self.handle, &raw_var, &mut count) };
+ assert!(!raw_instr_idxs.is_null());
+ unsafe { Array::new(raw_instr_idxs, count, self.to_owned()) }
+ }
+
+ pub fn create_user_stack_var<'a, S: BnStrCompatible, C: Into<Conf<&'a Type>>>(
+ self,
+ offset: i64,
+ var_type: C,
+ name: S,
+ ) {
+ let mut owned_raw_var_ty = Conf::<&Type>::into_raw(var_type.into());
+ let name = name.into_bytes_with_nul();
+ unsafe {
+ BNCreateUserStackVariable(
+ self.function().handle,
+ offset,
+ &mut owned_raw_var_ty,
+ name.as_ref().as_ptr() as *const c_char,
+ )
+ }
+ }
+
+ pub fn delete_user_stack_var(self, offset: i64) {
+ unsafe { BNDeleteUserStackVariable(self.function().handle, offset) }
+ }
+
+ pub fn create_user_var<'a, S: BnStrCompatible, C: Into<Conf<&'a Type>>>(
+ &self,
+ var: &Variable,
+ var_type: C,
+ name: S,
+ ignore_disjoint_uses: bool,
+ ) {
+ let raw_var = BNVariable::from(var);
+ let mut owned_raw_var_ty = Conf::<&Type>::into_raw(var_type.into());
+ let name = name.into_bytes_with_nul();
+ unsafe {
+ BNCreateUserVariable(
+ self.function().handle,
+ &raw_var,
+ &mut owned_raw_var_ty,
+ name.as_ref().as_ptr() as *const _,
+ ignore_disjoint_uses,
+ )
+ }
+ }
+
+ pub fn delete_user_var(&self, var: &Variable) {
+ let raw_var = BNVariable::from(var);
+ unsafe { BNDeleteUserVariable(self.function().handle, &raw_var) }
+ }
+
+ pub fn is_var_user_defined(&self, var: &Variable) -> bool {
+ let raw_var = BNVariable::from(var);
+ unsafe { BNIsVariableUserDefined(self.function().handle, &raw_var) }
+ }
+
+ /// Allows the user to specify a PossibleValueSet value for an MLIL
+ /// variable at its definition site.
+ ///
+ /// .. warning:: Setting the variable value, triggers a reanalysis of the
+ /// function and allows the dataflow to compute and propagate values which
+ /// depend on the current variable. This implies that branch conditions
+ /// whose values can be determined statically will be computed, leading to
+ /// potential branch elimination at the HLIL layer.
+ ///
+ /// * `var` - Variable for which the value is to be set
+ /// * `addr` - Address of the definition site of the variable
+ /// * `value` - Informed value of the variable
+ ///
+ /// # Example
+ /// ```no_run
+ /// # use binaryninja::medium_level_il::MediumLevelILFunction;
+ /// # use binaryninja::variable::PossibleValueSet;
+ /// # let mlil_fun: MediumLevelILFunction = todo!();
+ /// let user_var_val = mlil_fun.user_var_values().iter().next().unwrap();
+ /// let def_address = user_var_val.def_site.addr;
+ /// let var_value = PossibleValueSet::ConstantValue { value: 5 };
+ /// mlil_fun
+ /// .set_user_var_value(&user_var_val.variable, def_address, var_value)
+ /// .unwrap();
+ /// ```
+ pub fn set_user_var_value(
+ &self,
+ var: &Variable,
+ addr: u64,
+ value: PossibleValueSet,
+ ) -> Result<(), ()> {
+ let Some(_def_site) = self
+ .var_definitions(var)
+ .iter()
+ .find(|def| def.address == addr)
+ else {
+ // Error "No definition for Variable found at given address"
+ return Err(());
+ };
+ let function = self.function();
+ let def_site = BNArchitectureAndAddress {
+ arch: function.arch().handle,
+ address: addr,
+ };
+ let raw_var = BNVariable::from(var);
+ let raw_value = PossibleValueSet::into_raw(value);
+ unsafe { BNSetUserVariableValue(function.handle, &raw_var, &def_site, &raw_value) }
+ PossibleValueSet::free_owned_raw(raw_value);
+ Ok(())
+ }
+
+ /// Clears a previously defined user variable value.
+ ///
+ /// * `var` - Variable for which the value was informed
+ /// * `def_addr` - Address of the definition site of the variable
+ pub fn clear_user_var_value(&self, var: &Variable, addr: u64) -> Result<(), ()> {
+ let Some(_var_def) = self
+ .var_definitions(var)
+ .iter()
+ .find(|site| site.address == addr)
+ else {
+ //error "Could not get definition for Variable"
+ return Err(());
+ };
+
+ let function = self.function();
+ let raw_var = BNVariable::from(var);
+ let def_site = BNArchitectureAndAddress {
+ arch: function.arch().handle,
+ address: addr,
+ };
+
+ unsafe { BNClearUserVariableValue(function.handle, &raw_var, &def_site) };
+ Ok(())
+ }
+
+ /// Returns a map of current defined user variable values.
+ /// Returns a Map of user current defined user variable values and their definition sites.
+ pub fn user_var_values(&self) -> Array<UserVariableValue> {
+ let mut count = 0;
+ let function = self.function();
+ let var_values = unsafe { BNGetAllUserVariableValues(function.handle, &mut count) };
+ assert!(!var_values.is_null());
+ unsafe { Array::new(var_values, count, ()) }
+ }
+
+ /// Clear all user defined variable values.
+ pub fn clear_user_var_values(&self) -> Result<(), ()> {
+ for user_var_val in &self.user_var_values() {
+ self.clear_user_var_value(&user_var_val.variable, user_var_val.def_site.addr)?;
+ }
+ Ok(())
+ }
+
+ pub fn create_auto_stack_var<'a, T: Into<Conf<&'a Type>>, S: BnStrCompatible>(
+ &self,
+ offset: i64,
+ var_type: T,
+ name: S,
+ ) {
+ let mut owned_raw_var_ty = Conf::<&Type>::into_raw(var_type.into());
+ let name = name.into_bytes_with_nul();
+ let name_c_str = name.as_ref();
+ unsafe {
+ BNCreateAutoStackVariable(
+ self.function().handle,
+ offset,
+ &mut owned_raw_var_ty,
+ name_c_str.as_ptr() as *const c_char,
+ )
+ }
+ }
+
+ pub fn delete_auto_stack_var(&self, offset: i64) {
+ unsafe { BNDeleteAutoStackVariable(self.function().handle, offset) }
+ }
+
+ pub fn create_auto_var<'a, S: BnStrCompatible, C: Into<Conf<&'a Type>>>(
+ &self,
+ var: &Variable,
+ var_type: C,
+ name: S,
+ ignore_disjoint_uses: bool,
+ ) {
+ let raw_var = BNVariable::from(var);
+ let mut owned_raw_var_ty = Conf::<&Type>::into_raw(var_type.into());
+ let name = name.into_bytes_with_nul();
+ let name_c_str = name.as_ref();
+ unsafe {
+ BNCreateAutoVariable(
+ self.function().handle,
+ &raw_var,
+ &mut owned_raw_var_ty,
+ name_c_str.as_ptr() as *const c_char,
+ ignore_disjoint_uses,
+ )
+ }
+ }
+
+ /// Returns a list of ILReferenceSource objects (IL xrefs or cross-references)
+ /// that reference the given variable. The variable is a local variable that can be either on the stack,
+ /// in a register, or in a flag.
+ /// This function is related to get_hlil_var_refs(), which returns variable references collected
+ /// from HLIL. The two can be different in several cases, e.g., multiple variables in MLIL can be merged
+ /// into a single variable in HLIL.
+ ///
+ /// * `var` - Variable for which to query the xref
+ ///
+ /// # Example
+ /// ```no_run
+ /// # use binaryninja::medium_level_il::MediumLevelILFunction;
+ /// # use binaryninja::variable::Variable;
+ /// # let mlil_fun: MediumLevelILFunction = todo!();
+ /// # let mlil_var: Variable = todo!();
+ /// let instr_idx = mlil_fun.var_refs(&mlil_var).get(0).expr_idx;
+ /// ```
+ pub fn var_refs(&self, var: &Variable) -> Array<ILReferenceSource> {
+ let mut count = 0;
+ let mut raw_var = BNVariable::from(var);
+ let refs = unsafe {
+ BNGetMediumLevelILVariableReferences(self.function().handle, &mut raw_var, &mut count)
+ };
+ assert!(!refs.is_null());
+ unsafe { Array::new(refs, count, ()) }
+ }
+
+ /// Retrieves variable references from a specified location or range within a medium-level IL function.
+ ///
+ /// Passing in a `length` will query a range for variable references, instead of just the address
+ /// specified in `location`.
+ pub fn var_refs_from(
+ &self,
+ location: impl Into<Location>,
+ length: Option<u64>,
+ ) -> Array<VariableReferenceSource> {
+ let location = location.into();
+ let raw_arch = location
+ .arch
+ .map(|a| a.handle)
+ .unwrap_or(std::ptr::null_mut());
+ let function = self.function();
+ let mut count = 0;
+
+ let refs = if let Some(length) = length {
+ unsafe {
+ BNGetMediumLevelILVariableReferencesInRange(
+ function.handle,
+ raw_arch,
+ location.addr,
+ length,
+ &mut count,
+ )
+ }
+ } else {
+ unsafe {
+ BNGetMediumLevelILVariableReferencesFrom(
+ function.handle,
+ raw_arch,
+ location.addr,
+ &mut count,
+ )
+ }
+ };
+ assert!(!refs.is_null());
+ unsafe { Array::new(refs, count, ()) }
+ }
+
+ // TODO: Rename to `current_location`?
+ /// Current IL Address
+ pub fn current_address(&self) -> Location {
+ let addr = unsafe { BNMediumLevelILGetCurrentAddress(self.handle) };
+ Location::from(addr)
+ }
+
+ // TODO: Rename to `set_current_location`?
+ /// Set the current IL Address
+ pub fn set_current_address(&self, location: impl Into<Location>) {
+ let location = location.into();
+ let arch = location
+ .arch
+ .map(|a| a.handle)
+ .unwrap_or(std::ptr::null_mut());
+ unsafe { BNMediumLevelILSetCurrentAddress(self.handle, arch, location.addr) }
+ }
+
+ /// Returns the [`BasicBlock`] at the given instruction `index`.
+ ///
+ /// You can also retrieve this using [`MediumLevelILInstruction::basic_block`].
+ pub fn basic_block_containing_index(
+ &self,
+ index: MediumLevelInstructionIndex,
+ ) -> Option<Ref<BasicBlock<MediumLevelILBlock>>> {
+ let context = MediumLevelILBlock {
+ function: self.to_owned(),
+ };
+ // TODO: If we can guarantee self.index is valid we can omit the wrapped Option.
+ let basic_block_ptr =
+ unsafe { BNGetMediumLevelILBasicBlockForInstruction(self.handle, index.0) };
+ match basic_block_ptr.is_null() {
+ false => Some(unsafe { BasicBlock::ref_from_raw(basic_block_ptr, context) }),
+ true => None,
+ }
+ }
+
+ /// Ends the function and computes the list of basic blocks.
+ ///
+ /// NOTE: This should be called after updating MLIL.
+ pub fn finalize(&self) {
+ unsafe { BNFinalizeMediumLevelILFunction(self.handle) }
+ }
+
+ /// Generate SSA form given the current MLIL.
+ ///
+ /// NOTE: This should be called after updating MLIL.
+ ///
+ /// * `analyze_conditionals` - whether to analyze conditionals
+ /// * `handle_aliases` - whether to handle aliases
+ /// * `non_aliased_vars` - optional list of variables known to be not aliased
+ /// * `aliased_vars` - optional list of variables known to be aliased
+ pub fn generate_ssa_form(
+ &self,
+ analyze_conditionals: bool,
+ handle_aliases: bool,
+ non_aliased_vars: impl IntoIterator<Item = Variable>,
+ aliased_vars: impl IntoIterator<Item = Variable>,
+ ) {
+ let raw_non_aliased_vars: Vec<BNVariable> =
+ non_aliased_vars.into_iter().map(Into::into).collect();
+ let raw_aliased_vars: Vec<BNVariable> = aliased_vars.into_iter().map(Into::into).collect();
+ unsafe {
+ BNGenerateMediumLevelILSSAForm(
+ self.handle,
+ analyze_conditionals,
+ handle_aliases,
+ raw_non_aliased_vars.as_ptr() as *mut _,
+ raw_non_aliased_vars.len(),
+ raw_aliased_vars.as_ptr() as *mut _,
+ raw_aliased_vars.len(),
+ )
+ }
+ }
+
+ /// Gets the instruction that contains the given SSA variable's definition.
+ ///
+ /// Since SSA variables can only be defined once, this will return the single instruction where that occurs.
+ /// For SSA variable version 0s, which don't have definitions, this will return `None` instead.
+ pub fn ssa_variable_definition(
+ &self,
+ ssa_variable: &SSAVariable,
+ ) -> Option<MediumLevelILInstruction> {
+ let raw_var = BNVariable::from(ssa_variable.variable);
+ let result = unsafe {
+ BNGetMediumLevelILSSAVarDefinition(self.handle, &raw_var, ssa_variable.version)
+ };
+ // TODO: Does this return the expression or instruction index? Also we dont diff and this prob doesnt work.
+ self.instruction_from_index(MediumLevelInstructionIndex(result))
+ }
+
+ pub fn ssa_memory_definition(&self, version: usize) -> Option<MediumLevelILInstruction> {
+ let result = unsafe { BNGetMediumLevelILSSAMemoryDefinition(self.handle, version) };
+ // TODO: Does this return the expression or instruction index? Also we dont diff and this prob doesnt work.
+ self.instruction_from_index(MediumLevelInstructionIndex(result))
+ }
+
+ /// Gets all the instructions that use the given SSA variable.
+ pub fn ssa_variable_uses(&self, ssa_variable: &SSAVariable) -> Array<MediumLevelILInstruction> {
+ let mut count = 0;
+ let raw_var = BNVariable::from(ssa_variable.variable);
+ let uses = unsafe {
+ BNGetMediumLevelILSSAVarUses(self.handle, &raw_var, ssa_variable.version, &mut count)
+ };
+ assert!(!uses.is_null());
+ unsafe { Array::new(uses, count, self.to_owned()) }
+ }
+
+ pub fn ssa_memory_uses(&self, version: usize) -> Array<MediumLevelILInstruction> {
+ let mut count = 0;
+ let uses = unsafe { BNGetMediumLevelILSSAMemoryUses(self.handle, version, &mut count) };
+ assert!(!uses.is_null());
+ unsafe { Array::new(uses, count, self.to_owned()) }
+ }
+
+ /// Determines if `variable` is live at any point in the function
+ pub fn is_ssa_variable_live(&self, ssa_variable: &SSAVariable) -> bool {
+ let raw_var = BNVariable::from(ssa_variable.variable);
+ unsafe { BNIsMediumLevelILSSAVarLive(self.handle, &raw_var, ssa_variable.version) }
+ }
+
+ pub fn variable_definitions(&self, variable: &Variable) -> Array<MediumLevelILInstruction> {
+ let mut count = 0;
+ let raw_var = BNVariable::from(variable);
+ let defs =
+ unsafe { BNGetMediumLevelILVariableDefinitions(self.handle, &raw_var, &mut count) };
+ unsafe { Array::new(defs, count, self.to_owned()) }
+ }
+
+ pub fn variable_uses(&self, variable: &Variable) -> Array<MediumLevelILInstruction> {
+ let mut count = 0;
+ let raw_var = BNVariable::from(variable);
+ let uses = unsafe { BNGetMediumLevelILVariableUses(self.handle, &raw_var, &mut count) };
+ unsafe { Array::new(uses, count, self.to_owned()) }
+ }
+
+ /// Computes the list of instructions for which `var` is live.
+ /// If `include_last_use` is false, the last use of the variable will not be included in the
+ /// list (this allows for easier computation of overlaps in liveness between two variables).
+ /// If the variable is never used, this function will return an empty list.
+ ///
+ /// `var` - the variable to query
+ /// `include_last_use` - whether to include the last use of the variable in the list of instructions
+ pub fn live_instruction_for_variable(
+ &self,
+ variable: &Variable,
+ include_last_user: bool,
+ ) -> Array<MediumLevelILInstruction> {
+ let mut count = 0;
+ let raw_var = BNVariable::from(variable);
+ let uses = unsafe {
+ BNGetMediumLevelILLiveInstructionsForVariable(
+ self.handle,
+ &raw_var,
+ include_last_user,
+ &mut count,
+ )
+ };
+ unsafe { Array::new(uses, count, self.to_owned()) }
+ }
+
+ pub fn ssa_variable_value(&self, ssa_variable: &SSAVariable) -> RegisterValue {
+ let raw_var = BNVariable::from(ssa_variable.variable);
+ unsafe { BNGetMediumLevelILSSAVarValue(self.handle, &raw_var, ssa_variable.version) }.into()
+ }
+
+ pub fn create_graph(&self, settings: Option<DisassemblySettings>) -> FlowGraph {
+ let settings = settings.map(|x| x.handle).unwrap_or(std::ptr::null_mut());
+ let graph = unsafe { BNCreateMediumLevelILFunctionGraph(self.handle, settings) };
+ unsafe { FlowGraph::from_raw(graph) }
+ }
+
+ /// This gets just the MLIL variables - you may be interested in the union
+ /// of [`MediumLevelILFunction::aliased_variables`] and [`Function::parameter_variables`] for
+ /// all the variables used in the function
+ pub fn variables(&self) -> Array<Variable> {
+ let mut count = 0;
+ let uses = unsafe { BNGetMediumLevelILVariables(self.handle, &mut count) };
+ unsafe { Array::new(uses, count, ()) }
+ }
+
+ /// This returns a list of Variables that are taken reference to and used
+ /// elsewhere. You may also wish to consider [`MediumLevelILFunction::variables`]
+ /// and [`Function::parameter_variables`]
+ pub fn aliased_variables(&self) -> Array<Variable> {
+ let mut count = 0;
+ let uses = unsafe { BNGetMediumLevelILAliasedVariables(self.handle, &mut count) };
+ unsafe { Array::new(uses, count, ()) }
+ }
+
+ /// This gets the MLIL SSA variables for a given [`Variable`].
+ pub fn ssa_variables(&self, variable: &Variable) -> Array<SSAVariable> {
+ let mut count = 0;
+ let raw_variable = BNVariable::from(variable);
+ let versions = unsafe {
+ BNGetMediumLevelILVariableSSAVersions(self.handle, &raw_variable, &mut count)
+ };
+ unsafe { Array::new(versions, count, *variable) }
+ }
+}
+
+impl ToOwned for MediumLevelILFunction {
+ type Owned = Ref<Self>;
+
+ fn to_owned(&self) -> Self::Owned {
+ unsafe { RefCountable::inc_ref(self) }
+ }
+}
+
+unsafe impl RefCountable for MediumLevelILFunction {
+ unsafe fn inc_ref(handle: &Self) -> Ref<Self> {
+ Ref::new(Self {
+ handle: BNNewMediumLevelILFunctionReference(handle.handle),
+ })
+ }
+
+ unsafe fn dec_ref(handle: &Self) {
+ BNFreeMediumLevelILFunction(handle.handle);
+ }
+}
+
+impl Debug for MediumLevelILFunction {
+ fn fmt(&self, f: &mut Formatter) -> std::fmt::Result {
+ f.debug_struct("MediumLevelILFunction")
+ .field("arch", &self.function().arch())
+ .field("instruction_count", &self.instruction_count())
+ .finish()
+ }
+}
+
+unsafe impl Send for MediumLevelILFunction {}
+unsafe impl Sync for MediumLevelILFunction {}
+
+impl Eq for MediumLevelILFunction {}
+impl PartialEq for MediumLevelILFunction {
+ fn eq(&self, rhs: &Self) -> bool {
+ self.function().eq(&rhs.function())
+ }
+}
+
+impl Hash for MediumLevelILFunction {
+ fn hash<H: Hasher>(&self, state: &mut H) {
+ self.function().hash(state)
+ }
+}
+
+pub struct ILReferenceSource {
+ pub function: Ref<Function>,
+ pub arch: CoreArchitecture,
+ pub addr: u64,
+ pub graph_type: FunctionGraphType,
+ pub expr_idx: usize,
+}
+
+impl From<BNILReferenceSource> for ILReferenceSource {
+ fn from(value: BNILReferenceSource) -> Self {
+ Self {
+ function: unsafe { Function::ref_from_raw(value.func) },
+ arch: unsafe { CoreArchitecture::from_raw(value.arch) },
+ addr: value.addr,
+ graph_type: value.type_,
+ expr_idx: value.exprId,
+ }
+ }
+}
+
+impl From<&BNILReferenceSource> for ILReferenceSource {
+ fn from(value: &BNILReferenceSource) -> Self {
+ Self {
+ function: unsafe { Function::from_raw(value.func).to_owned() },
+ arch: unsafe { CoreArchitecture::from_raw(value.arch) },
+ addr: value.addr,
+ graph_type: value.type_,
+ expr_idx: value.exprId,
+ }
+ }
+}
+
+impl CoreArrayProvider for ILReferenceSource {
+ type Raw = BNILReferenceSource;
+ type Context = ();
+ type Wrapped<'a> = Self;
+}
+
+unsafe impl CoreArrayProviderInner for ILReferenceSource {
+ unsafe fn free(raw: *mut Self::Raw, count: usize, _context: &Self::Context) {
+ BNFreeILReferences(raw, count)
+ }
+
+ unsafe fn wrap_raw<'a>(raw: &'a Self::Raw, _context: &'a Self::Context) -> Self::Wrapped<'a> {
+ raw.into()
+ }
+}
+
+pub struct VariableReferenceSource {
+ pub variable: Variable,
+ pub source: ILReferenceSource,
+}
+
+impl From<BNVariableReferenceSource> for VariableReferenceSource {
+ fn from(value: BNVariableReferenceSource) -> Self {
+ Self {
+ variable: Variable::from(value.var),
+ source: value.source.into(),
+ }
+ }
+}
+
+impl From<&BNVariableReferenceSource> for VariableReferenceSource {
+ fn from(value: &BNVariableReferenceSource) -> Self {
+ Self {
+ variable: Variable::from(value.var),
+ // TODO: We really need to document this better, or have some other facility for this.
+ // NOTE: We take this as a ref to increment the function ref.
+ source: ILReferenceSource::from(&value.source),
+ }
+ }
+}
+
+impl CoreArrayProvider for VariableReferenceSource {
+ type Raw = BNVariableReferenceSource;
+ type Context = ();
+ type Wrapped<'a> = Self;
+}
+
+unsafe impl CoreArrayProviderInner for VariableReferenceSource {
+ unsafe fn free(raw: *mut Self::Raw, count: usize, _context: &Self::Context) {
+ BNFreeVariableReferenceSourceList(raw, count)
+ }
+
+ unsafe fn wrap_raw<'a>(raw: &'a Self::Raw, _context: &'a Self::Context) -> Self::Wrapped<'a> {
+ raw.into()
+ }
+}
diff --git a/rust/src/medium_level_il/instruction.rs b/rust/src/medium_level_il/instruction.rs
new file mode 100644
index 00000000..bd07da94
--- /dev/null
+++ b/rust/src/medium_level_il/instruction.rs
@@ -0,0 +1,1657 @@
+use super::lift::*;
+use super::operation::*;
+use super::{MediumLevelILBlock, MediumLevelILFunction};
+use crate::architecture::{CoreIntrinsic, FlagId, IntrinsicId, RegisterId};
+use crate::basic_block::BasicBlock;
+use crate::confidence::Conf;
+use crate::disassembly::InstructionTextToken;
+use crate::operand_iter::OperandIter;
+use crate::rc::{Array, CoreArrayProvider, CoreArrayProviderInner, Ref};
+use crate::types::Type;
+use crate::variable::{ConstantData, PossibleValueSet, RegisterValue, SSAVariable, Variable};
+use crate::{DataFlowQueryOption, ILBranchDependence};
+use binaryninjacore_sys::*;
+use std::fmt;
+use std::fmt::{Debug, Display, Formatter};
+
+#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
+pub struct MediumLevelInstructionIndex(pub usize);
+
+impl MediumLevelInstructionIndex {
+ pub fn next(&self) -> Self {
+ Self(self.0 + 1)
+ }
+}
+
+impl From<usize> for MediumLevelInstructionIndex {
+ fn from(index: usize) -> Self {
+ Self(index)
+ }
+}
+
+impl From<u64> for MediumLevelInstructionIndex {
+ fn from(index: u64) -> Self {
+ Self(index as usize)
+ }
+}
+
+impl Display for MediumLevelInstructionIndex {
+ fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
+ f.write_fmt(format_args!("{}", self.0))
+ }
+}
+
+#[derive(Clone)]
+pub struct MediumLevelILInstruction {
+ pub function: Ref<MediumLevelILFunction>,
+ pub address: u64,
+ // TODO; Because this structure is incorrectly named instruction, we want to make it clear that we actually have the expression index.
+ pub expr_index: MediumLevelInstructionIndex,
+ pub size: usize,
+ pub kind: MediumLevelILInstructionKind,
+}
+
+impl MediumLevelILInstruction {
+ pub(crate) fn new(
+ function: Ref<MediumLevelILFunction>,
+ index: MediumLevelInstructionIndex,
+ ) -> Self {
+ // TODO: If op.sourceOperation == BN_INVALID_OPERAND && op.operation == MLIL_NOP return None
+ let expr_index = unsafe { BNGetMediumLevelILIndexForInstruction(function.handle, index.0) };
+ Self::new_expr(function, MediumLevelInstructionIndex(expr_index))
+ }
+
+ // TODO: I need MediumLevelILExpression YESTERDAY!!!!
+ pub(crate) fn new_expr(
+ function: Ref<MediumLevelILFunction>,
+ expr_index: MediumLevelInstructionIndex,
+ ) -> Self {
+ // TODO: If op.sourceOperation == BN_INVALID_OPERAND && op.operation == MLIL_NOP return None
+ let op = unsafe { BNGetMediumLevelILByIndex(function.handle, expr_index.0) };
+ use BNMediumLevelILOperation::*;
+ use MediumLevelILInstructionKind as Op;
+ let kind = match op.operation {
+ MLIL_NOP => Op::Nop,
+ MLIL_NORET => Op::Noret,
+ MLIL_BP => Op::Bp,
+ MLIL_UNDEF => Op::Undef,
+ MLIL_UNIMPL => Op::Unimpl,
+ MLIL_IF => Op::If(MediumLevelILOperationIf {
+ condition: op.operands[0] as usize,
+ dest_true: MediumLevelInstructionIndex(op.operands[1] as usize),
+ dest_false: MediumLevelInstructionIndex(op.operands[2] as usize),
+ }),
+ MLIL_FLOAT_CONST => Op::FloatConst(FloatConst {
+ constant: get_float(op.operands[0], op.size),
+ }),
+ MLIL_CONST => Op::Const(Constant {
+ constant: op.operands[0],
+ }),
+ MLIL_CONST_PTR => Op::ConstPtr(Constant {
+ constant: op.operands[0],
+ }),
+ MLIL_IMPORT => Op::Import(Constant {
+ constant: op.operands[0],
+ }),
+ MLIL_EXTERN_PTR => Op::ExternPtr(ExternPtr {
+ constant: op.operands[0],
+ offset: op.operands[1],
+ }),
+ MLIL_CONST_DATA => Op::ConstData(ConstData {
+ constant_data_kind: op.operands[0] as u32,
+ constant_data_value: op.operands[1] as i64,
+ size: op.size,
+ }),
+ MLIL_JUMP => Op::Jump(Jump {
+ dest: op.operands[0] as usize,
+ }),
+ MLIL_RET_HINT => Op::RetHint(Jump {
+ dest: op.operands[0] as usize,
+ }),
+ MLIL_STORE_SSA => Op::StoreSsa(StoreSsa {
+ dest: op.operands[0] as usize,
+ dest_memory: op.operands[1],
+ src_memory: op.operands[2],
+ src: op.operands[3] as usize,
+ }),
+ MLIL_STORE_STRUCT_SSA => Op::StoreStructSsa(StoreStructSsa {
+ dest: op.operands[0] as usize,
+ offset: op.operands[1],
+ dest_memory: op.operands[2],
+ src_memory: op.operands[3],
+ src: op.operands[4] as usize,
+ }),
+ MLIL_STORE_STRUCT => Op::StoreStruct(StoreStruct {
+ dest: op.operands[0] as usize,
+ offset: op.operands[1],
+ src: op.operands[2] as usize,
+ }),
+ MLIL_STORE => Op::Store(Store {
+ dest: op.operands[0] as usize,
+ src: op.operands[1] as usize,
+ }),
+ MLIL_JUMP_TO => Op::JumpTo(JumpTo {
+ dest: op.operands[0] as usize,
+ num_operands: op.operands[1] as usize,
+ first_operand: op.operands[2] as usize,
+ }),
+ MLIL_GOTO => Op::Goto(Goto {
+ dest: MediumLevelInstructionIndex(op.operands[0] as usize),
+ }),
+ MLIL_FREE_VAR_SLOT => Op::FreeVarSlot(FreeVarSlot {
+ dest: get_var(op.operands[0]),
+ }),
+ MLIL_SET_VAR_FIELD => Op::SetVarField(SetVarField {
+ dest: get_var(op.operands[0]),
+ offset: op.operands[1],
+ src: op.operands[2] as usize,
+ }),
+ MLIL_SET_VAR => Op::SetVar(SetVar {
+ dest: get_var(op.operands[0]),
+ src: op.operands[1] as usize,
+ }),
+ MLIL_FREE_VAR_SLOT_SSA => Op::FreeVarSlotSsa(FreeVarSlotSsa {
+ dest: get_var_ssa(op.operands[0], op.operands[1] as usize),
+ prev: get_var_ssa(op.operands[0], op.operands[2] as usize),
+ }),
+ MLIL_SET_VAR_SSA_FIELD => Op::SetVarSsaField(SetVarSsaField {
+ dest: get_var_ssa(op.operands[0], op.operands[1] as usize),
+ prev: get_var_ssa(op.operands[0], op.operands[2] as usize),
+ offset: op.operands[3],
+ src: op.operands[4] as usize,
+ }),
+ MLIL_SET_VAR_ALIASED_FIELD => Op::SetVarAliasedField(SetVarSsaField {
+ dest: get_var_ssa(op.operands[0], op.operands[1] as usize),
+ prev: get_var_ssa(op.operands[0], op.operands[2] as usize),
+ offset: op.operands[3],
+ src: op.operands[4] as usize,
+ }),
+ MLIL_SET_VAR_ALIASED => Op::SetVarAliased(SetVarAliased {
+ dest: get_var_ssa(op.operands[0], op.operands[1] as usize),
+ prev: get_var_ssa(op.operands[0], op.operands[2] as usize),
+ src: op.operands[3] as usize,
+ }),
+ MLIL_SET_VAR_SSA => Op::SetVarSsa(SetVarSsa {
+ dest: get_var_ssa(op.operands[0], op.operands[1] as usize),
+ src: op.operands[2] as usize,
+ }),
+ MLIL_VAR_PHI => Op::VarPhi(VarPhi {
+ dest: get_var_ssa(op.operands[0], op.operands[1] as usize),
+ num_operands: op.operands[2] as usize,
+ first_operand: op.operands[3] as usize,
+ }),
+ MLIL_MEM_PHI => Op::MemPhi(MemPhi {
+ dest_memory: op.operands[0],
+ num_operands: op.operands[1] as usize,
+ first_operand: op.operands[2] as usize,
+ }),
+ MLIL_VAR_SPLIT => Op::VarSplit(VarSplit {
+ high: get_var(op.operands[0]),
+ low: get_var(op.operands[1]),
+ }),
+ MLIL_SET_VAR_SPLIT => Op::SetVarSplit(SetVarSplit {
+ high: get_var(op.operands[0]),
+ low: get_var(op.operands[1]),
+ src: op.operands[2] as usize,
+ }),
+ MLIL_VAR_SPLIT_SSA => Op::VarSplitSsa(VarSplitSsa {
+ high: get_var_ssa(op.operands[0], op.operands[1] as usize),
+ low: get_var_ssa(op.operands[2], op.operands[3] as usize),
+ }),
+ MLIL_SET_VAR_SPLIT_SSA => Op::SetVarSplitSsa(SetVarSplitSsa {
+ high: get_var_ssa(op.operands[0], op.operands[1] as usize),
+ low: get_var_ssa(op.operands[2], op.operands[3] as usize),
+ src: op.operands[4] as usize,
+ }),
+ MLIL_ADD => Op::Add(BinaryOp {
+ left: op.operands[0] as usize,
+ right: op.operands[1] as usize,
+ }),
+ MLIL_SUB => Op::Sub(BinaryOp {
+ left: op.operands[0] as usize,
+ right: op.operands[1] as usize,
+ }),
+ MLIL_AND => Op::And(BinaryOp {
+ left: op.operands[0] as usize,
+ right: op.operands[1] as usize,
+ }),
+ MLIL_OR => Op::Or(BinaryOp {
+ left: op.operands[0] as usize,
+ right: op.operands[1] as usize,
+ }),
+ MLIL_XOR => Op::Xor(BinaryOp {
+ left: op.operands[0] as usize,
+ right: op.operands[1] as usize,
+ }),
+ MLIL_LSL => Op::Lsl(BinaryOp {
+ left: op.operands[0] as usize,
+ right: op.operands[1] as usize,
+ }),
+ MLIL_LSR => Op::Lsr(BinaryOp {
+ left: op.operands[0] as usize,
+ right: op.operands[1] as usize,
+ }),
+ MLIL_ASR => Op::Asr(BinaryOp {
+ left: op.operands[0] as usize,
+ right: op.operands[1] as usize,
+ }),
+ MLIL_ROL => Op::Rol(BinaryOp {
+ left: op.operands[0] as usize,
+ right: op.operands[1] as usize,
+ }),
+ MLIL_ROR => Op::Ror(BinaryOp {
+ left: op.operands[0] as usize,
+ right: op.operands[1] as usize,
+ }),
+ MLIL_MUL => Op::Mul(BinaryOp {
+ left: op.operands[0] as usize,
+ right: op.operands[1] as usize,
+ }),
+ MLIL_MULU_DP => Op::MuluDp(BinaryOp {
+ left: op.operands[0] as usize,
+ right: op.operands[1] as usize,
+ }),
+ MLIL_MULS_DP => Op::MulsDp(BinaryOp {
+ left: op.operands[0] as usize,
+ right: op.operands[1] as usize,
+ }),
+ MLIL_DIVU => Op::Divu(BinaryOp {
+ left: op.operands[0] as usize,
+ right: op.operands[1] as usize,
+ }),
+ MLIL_DIVU_DP => Op::DivuDp(BinaryOp {
+ left: op.operands[0] as usize,
+ right: op.operands[1] as usize,
+ }),
+ MLIL_DIVS => Op::Divs(BinaryOp {
+ left: op.operands[0] as usize,
+ right: op.operands[1] as usize,
+ }),
+ MLIL_DIVS_DP => Op::DivsDp(BinaryOp {
+ left: op.operands[0] as usize,
+ right: op.operands[1] as usize,
+ }),
+ MLIL_MODU => Op::Modu(BinaryOp {
+ left: op.operands[0] as usize,
+ right: op.operands[1] as usize,
+ }),
+ MLIL_MODU_DP => Op::ModuDp(BinaryOp {
+ left: op.operands[0] as usize,
+ right: op.operands[1] as usize,
+ }),
+ MLIL_MODS => Op::Mods(BinaryOp {
+ left: op.operands[0] as usize,
+ right: op.operands[1] as usize,
+ }),
+ MLIL_MODS_DP => Op::ModsDp(BinaryOp {
+ left: op.operands[0] as usize,
+ right: op.operands[1] as usize,
+ }),
+ MLIL_CMP_E => Op::CmpE(BinaryOp {
+ left: op.operands[0] as usize,
+ right: op.operands[1] as usize,
+ }),
+ MLIL_CMP_NE => Op::CmpNe(BinaryOp {
+ left: op.operands[0] as usize,
+ right: op.operands[1] as usize,
+ }),
+ MLIL_CMP_SLT => Op::CmpSlt(BinaryOp {
+ left: op.operands[0] as usize,
+ right: op.operands[1] as usize,
+ }),
+ MLIL_CMP_ULT => Op::CmpUlt(BinaryOp {
+ left: op.operands[0] as usize,
+ right: op.operands[1] as usize,
+ }),
+ MLIL_CMP_SLE => Op::CmpSle(BinaryOp {
+ left: op.operands[0] as usize,
+ right: op.operands[1] as usize,
+ }),
+ MLIL_CMP_ULE => Op::CmpUle(BinaryOp {
+ left: op.operands[0] as usize,
+ right: op.operands[1] as usize,
+ }),
+ MLIL_CMP_SGE => Op::CmpSge(BinaryOp {
+ left: op.operands[0] as usize,
+ right: op.operands[1] as usize,
+ }),
+ MLIL_CMP_UGE => Op::CmpUge(BinaryOp {
+ left: op.operands[0] as usize,
+ right: op.operands[1] as usize,
+ }),
+ MLIL_CMP_SGT => Op::CmpSgt(BinaryOp {
+ left: op.operands[0] as usize,
+ right: op.operands[1] as usize,
+ }),
+ MLIL_CMP_UGT => Op::CmpUgt(BinaryOp {
+ left: op.operands[0] as usize,
+ right: op.operands[1] as usize,
+ }),
+ MLIL_TEST_BIT => Op::TestBit(BinaryOp {
+ left: op.operands[0] as usize,
+ right: op.operands[1] as usize,
+ }),
+ MLIL_ADD_OVERFLOW => Op::AddOverflow(BinaryOp {
+ left: op.operands[0] as usize,
+ right: op.operands[1] as usize,
+ }),
+ MLIL_FCMP_E => Op::FcmpE(BinaryOp {
+ left: op.operands[0] as usize,
+ right: op.operands[1] as usize,
+ }),
+ MLIL_FCMP_NE => Op::FcmpNe(BinaryOp {
+ left: op.operands[0] as usize,
+ right: op.operands[1] as usize,
+ }),
+ MLIL_FCMP_LT => Op::FcmpLt(BinaryOp {
+ left: op.operands[0] as usize,
+ right: op.operands[1] as usize,
+ }),
+ MLIL_FCMP_LE => Op::FcmpLe(BinaryOp {
+ left: op.operands[0] as usize,
+ right: op.operands[1] as usize,
+ }),
+ MLIL_FCMP_GE => Op::FcmpGe(BinaryOp {
+ left: op.operands[0] as usize,
+ right: op.operands[1] as usize,
+ }),
+ MLIL_FCMP_GT => Op::FcmpGt(BinaryOp {
+ left: op.operands[0] as usize,
+ right: op.operands[1] as usize,
+ }),
+ MLIL_FCMP_O => Op::FcmpO(BinaryOp {
+ left: op.operands[0] as usize,
+ right: op.operands[1] as usize,
+ }),
+ MLIL_FCMP_UO => Op::FcmpUo(BinaryOp {
+ left: op.operands[0] as usize,
+ right: op.operands[1] as usize,
+ }),
+ MLIL_FADD => Op::Fadd(BinaryOp {
+ left: op.operands[0] as usize,
+ right: op.operands[1] as usize,
+ }),
+ MLIL_FSUB => Op::Fsub(BinaryOp {
+ left: op.operands[0] as usize,
+ right: op.operands[1] as usize,
+ }),
+ MLIL_FMUL => Op::Fmul(BinaryOp {
+ left: op.operands[0] as usize,
+ right: op.operands[1] as usize,
+ }),
+ MLIL_FDIV => Op::Fdiv(BinaryOp {
+ left: op.operands[0] as usize,
+ right: op.operands[1] as usize,
+ }),
+ MLIL_ADC => Op::Adc(BinaryOpCarry {
+ left: op.operands[0] as usize,
+ right: op.operands[1] as usize,
+ carry: op.operands[2] as usize,
+ }),
+ MLIL_SBB => Op::Sbb(BinaryOpCarry {
+ left: op.operands[0] as usize,
+ right: op.operands[1] as usize,
+ carry: op.operands[2] as usize,
+ }),
+ MLIL_RLC => Op::Rlc(BinaryOpCarry {
+ left: op.operands[0] as usize,
+ right: op.operands[1] as usize,
+ carry: op.operands[2] as usize,
+ }),
+ MLIL_RRC => Op::Rrc(BinaryOpCarry {
+ left: op.operands[0] as usize,
+ right: op.operands[1] as usize,
+ carry: op.operands[2] as usize,
+ }),
+ MLIL_CALL => Op::Call(Call {
+ num_outputs: op.operands[0] as usize,
+ first_output: op.operands[1] as usize,
+ dest: op.operands[2] as usize,
+ num_params: op.operands[3] as usize,
+ first_param: op.operands[4] as usize,
+ }),
+ MLIL_TAILCALL => Op::Tailcall(Call {
+ num_outputs: op.operands[0] as usize,
+ first_output: op.operands[1] as usize,
+ dest: op.operands[2] as usize,
+ num_params: op.operands[3] as usize,
+ first_param: op.operands[4] as usize,
+ }),
+ MLIL_SYSCALL => Op::Syscall(Syscall {
+ num_outputs: op.operands[0] as usize,
+ first_output: op.operands[1] as usize,
+ num_params: op.operands[2] as usize,
+ first_param: op.operands[3] as usize,
+ }),
+ MLIL_INTRINSIC => Op::Intrinsic(Intrinsic {
+ num_outputs: op.operands[0] as usize,
+ first_output: op.operands[1] as usize,
+ intrinsic: op.operands[2] as u32,
+ num_params: op.operands[3] as usize,
+ first_param: op.operands[4] as usize,
+ }),
+ MLIL_INTRINSIC_SSA => Op::IntrinsicSsa(IntrinsicSsa {
+ num_outputs: op.operands[0] as usize,
+ first_output: op.operands[1] as usize,
+ intrinsic: op.operands[2] as u32,
+ num_params: op.operands[3] as usize,
+ first_param: op.operands[4] as usize,
+ }),
+ MLIL_CALL_SSA => Op::CallSsa(CallSsa {
+ output: op.operands[0] as usize,
+ dest: op.operands[1] as usize,
+ num_params: op.operands[2] as usize,
+ first_param: op.operands[3] as usize,
+ src_memory: op.operands[4],
+ }),
+ MLIL_TAILCALL_SSA => Op::TailcallSsa(CallSsa {
+ output: op.operands[0] as usize,
+ dest: op.operands[1] as usize,
+ num_params: op.operands[2] as usize,
+ first_param: op.operands[3] as usize,
+ src_memory: op.operands[4],
+ }),
+ MLIL_CALL_UNTYPED_SSA => Op::CallUntypedSsa(CallUntypedSsa {
+ output: op.operands[0] as usize,
+ dest: op.operands[1] as usize,
+ params: op.operands[2] as usize,
+ stack: op.operands[3] as usize,
+ }),
+ MLIL_TAILCALL_UNTYPED_SSA => Op::TailcallUntypedSsa(CallUntypedSsa {
+ output: op.operands[0] as usize,
+ dest: op.operands[1] as usize,
+ params: op.operands[2] as usize,
+ stack: op.operands[3] as usize,
+ }),
+ MLIL_SYSCALL_SSA => Op::SyscallSsa(SyscallSsa {
+ output: op.operands[0] as usize,
+ num_params: op.operands[1] as usize,
+ first_param: op.operands[2] as usize,
+ src_memory: op.operands[3],
+ }),
+ MLIL_SYSCALL_UNTYPED_SSA => Op::SyscallUntypedSsa(SyscallUntypedSsa {
+ output: op.operands[0] as usize,
+ params: op.operands[1] as usize,
+ stack: op.operands[2] as usize,
+ }),
+ MLIL_CALL_UNTYPED => Op::CallUntyped(CallUntyped {
+ output: op.operands[0] as usize,
+ dest: op.operands[1] as usize,
+ params: op.operands[2] as usize,
+ stack: op.operands[3] as usize,
+ }),
+ MLIL_TAILCALL_UNTYPED => Op::TailcallUntyped(CallUntyped {
+ output: op.operands[0] as usize,
+ dest: op.operands[1] as usize,
+ params: op.operands[2] as usize,
+ stack: op.operands[3] as usize,
+ }),
+ MLIL_SYSCALL_UNTYPED => Op::SyscallUntyped(SyscallUntyped {
+ output: op.operands[0] as usize,
+ params: op.operands[1] as usize,
+ stack: op.operands[2] as usize,
+ }),
+ MLIL_NEG => Op::Neg(UnaryOp {
+ src: op.operands[0] as usize,
+ }),
+ MLIL_NOT => Op::Not(UnaryOp {
+ src: op.operands[0] as usize,
+ }),
+ MLIL_SX => Op::Sx(UnaryOp {
+ src: op.operands[0] as usize,
+ }),
+ MLIL_ZX => Op::Zx(UnaryOp {
+ src: op.operands[0] as usize,
+ }),
+ MLIL_LOW_PART => Op::LowPart(UnaryOp {
+ src: op.operands[0] as usize,
+ }),
+ MLIL_BOOL_TO_INT => Op::BoolToInt(UnaryOp {
+ src: op.operands[0] as usize,
+ }),
+ MLIL_UNIMPL_MEM => Op::UnimplMem(UnaryOp {
+ src: op.operands[0] as usize,
+ }),
+ MLIL_FSQRT => Op::Fsqrt(UnaryOp {
+ src: op.operands[0] as usize,
+ }),
+ MLIL_FNEG => Op::Fneg(UnaryOp {
+ src: op.operands[0] as usize,
+ }),
+ MLIL_FABS => Op::Fabs(UnaryOp {
+ src: op.operands[0] as usize,
+ }),
+ MLIL_FLOAT_TO_INT => Op::FloatToInt(UnaryOp {
+ src: op.operands[0] as usize,
+ }),
+ MLIL_INT_TO_FLOAT => Op::IntToFloat(UnaryOp {
+ src: op.operands[0] as usize,
+ }),
+ MLIL_FLOAT_CONV => Op::FloatConv(UnaryOp {
+ src: op.operands[0] as usize,
+ }),
+ MLIL_ROUND_TO_INT => Op::RoundToInt(UnaryOp {
+ src: op.operands[0] as usize,
+ }),
+ MLIL_FLOOR => Op::Floor(UnaryOp {
+ src: op.operands[0] as usize,
+ }),
+ MLIL_CEIL => Op::Ceil(UnaryOp {
+ src: op.operands[0] as usize,
+ }),
+ MLIL_FTRUNC => Op::Ftrunc(UnaryOp {
+ src: op.operands[0] as usize,
+ }),
+ MLIL_LOAD => Op::Load(UnaryOp {
+ src: op.operands[0] as usize,
+ }),
+ MLIL_LOAD_STRUCT => Op::LoadStruct(LoadStruct {
+ src: op.operands[0] as usize,
+ offset: op.operands[1],
+ }),
+ MLIL_LOAD_STRUCT_SSA => Op::LoadStructSsa(LoadStructSsa {
+ src: op.operands[0] as usize,
+ offset: op.operands[1],
+ src_memory: op.operands[2],
+ }),
+ MLIL_LOAD_SSA => Op::LoadSsa(LoadSsa {
+ src: op.operands[0] as usize,
+ src_memory: op.operands[1],
+ }),
+ MLIL_RET => Op::Ret(Ret {
+ num_operands: op.operands[0] as usize,
+ first_operand: op.operands[1] as usize,
+ }),
+ MLIL_SEPARATE_PARAM_LIST => Op::SeparateParamList(SeparateParamList {
+ num_params: op.operands[0] as usize,
+ first_param: op.operands[1] as usize,
+ }),
+ MLIL_SHARED_PARAM_SLOT => Op::SharedParamSlot(SharedParamSlot {
+ num_params: op.operands[0] as usize,
+ first_param: op.operands[1] as usize,
+ }),
+ MLIL_VAR => Op::Var(Var {
+ src: get_var(op.operands[0]),
+ }),
+ MLIL_ADDRESS_OF => Op::AddressOf(Var {
+ src: get_var(op.operands[0]),
+ }),
+ MLIL_VAR_FIELD => Op::VarField(Field {
+ src: get_var(op.operands[0]),
+ offset: op.operands[1],
+ }),
+ MLIL_ADDRESS_OF_FIELD => Op::AddressOfField(Field {
+ src: get_var(op.operands[0]),
+ offset: op.operands[1],
+ }),
+ MLIL_VAR_SSA => Op::VarSsa(VarSsa {
+ src: get_var_ssa(op.operands[0], op.operands[1] as usize),
+ }),
+ MLIL_VAR_ALIASED => Op::VarAliased(VarSsa {
+ src: get_var_ssa(op.operands[0], op.operands[1] as usize),
+ }),
+ MLIL_VAR_SSA_FIELD => Op::VarSsaField(VarSsaField {
+ src: get_var_ssa(op.operands[0], op.operands[1] as usize),
+ offset: op.operands[2],
+ }),
+ MLIL_VAR_ALIASED_FIELD => Op::VarAliasedField(VarSsaField {
+ src: get_var_ssa(op.operands[0], op.operands[1] as usize),
+ offset: op.operands[2],
+ }),
+ MLIL_TRAP => Op::Trap(Trap {
+ vector: op.operands[0],
+ }),
+ // translated directly into a list for Expression or Variables
+ // TODO MLIL_MEMORY_INTRINSIC_SSA needs to be handled properly
+ MLIL_CALL_OUTPUT
+ | MLIL_CALL_PARAM
+ | MLIL_CALL_PARAM_SSA
+ | MLIL_CALL_OUTPUT_SSA
+ | MLIL_MEMORY_INTRINSIC_OUTPUT_SSA
+ | MLIL_MEMORY_INTRINSIC_SSA => {
+ unimplemented!()
+ }
+ };
+
+ Self {
+ function,
+ address: op.address,
+ expr_index,
+ size: op.size,
+ kind,
+ }
+ }
+
+ pub fn lift(&self) -> MediumLevelILLiftedInstruction {
+ use MediumLevelILInstructionKind::*;
+ use MediumLevelILLiftedInstructionKind as Lifted;
+
+ let kind = match self.kind {
+ Nop => Lifted::Nop,
+ Noret => Lifted::Noret,
+ Bp => Lifted::Bp,
+ Undef => Lifted::Undef,
+ Unimpl => Lifted::Unimpl,
+ If(op) => Lifted::If(LiftedIf {
+ condition: self.lift_operand(op.condition),
+ dest_true: op.dest_true,
+ dest_false: op.dest_false,
+ }),
+
+ FloatConst(op) => Lifted::FloatConst(op),
+ Const(op) => Lifted::Const(op),
+ ConstPtr(op) => Lifted::ConstPtr(op),
+ Import(op) => Lifted::Import(op),
+ ExternPtr(op) => Lifted::ExternPtr(op),
+
+ ConstData(op) => Lifted::ConstData(LiftedConstData {
+ constant_data: ConstantData::new(
+ self.function.function(),
+ RegisterValue {
+ // TODO: Replace with a From<u32> for RegisterValueType.
+ // TODO: We might also want to change the type of `op.constant_data_kind`
+ // TODO: To RegisterValueType and do the conversion when creating instruction.
+ state: unsafe {
+ std::mem::transmute::<u32, BNRegisterValueType>(op.constant_data_kind)
+ },
+ value: op.constant_data_value,
+ offset: 0,
+ size: op.size,
+ },
+ ),
+ }),
+ Jump(op) => Lifted::Jump(LiftedJump {
+ dest: self.lift_operand(op.dest),
+ }),
+ RetHint(op) => Lifted::RetHint(LiftedJump {
+ dest: self.lift_operand(op.dest),
+ }),
+ StoreSsa(op) => Lifted::StoreSsa(LiftedStoreSsa {
+ dest: self.lift_operand(op.dest),
+ dest_memory: op.dest_memory,
+ src_memory: op.src_memory,
+ src: self.lift_operand(op.src),
+ }),
+ StoreStructSsa(op) => Lifted::StoreStructSsa(LiftedStoreStructSsa {
+ dest: self.lift_operand(op.dest),
+ offset: op.offset,
+ dest_memory: op.dest_memory,
+ src_memory: op.src_memory,
+ src: self.lift_operand(op.src),
+ }),
+ StoreStruct(op) => Lifted::StoreStruct(LiftedStoreStruct {
+ dest: self.lift_operand(op.dest),
+ offset: op.offset,
+ src: self.lift_operand(op.src),
+ }),
+ Store(op) => Lifted::Store(LiftedStore {
+ dest: self.lift_operand(op.dest),
+ src: self.lift_operand(op.src),
+ }),
+ JumpTo(op) => Lifted::JumpTo(LiftedJumpTo {
+ dest: self.lift_operand(op.dest),
+ targets: OperandIter::new(&*self.function, op.first_operand, op.num_operands)
+ .pairs()
+ .map(|(addr, instr_idx)| {
+ (addr, MediumLevelInstructionIndex(instr_idx as usize))
+ })
+ .collect(),
+ }),
+ Goto(op) => Lifted::Goto(op),
+ FreeVarSlot(op) => Lifted::FreeVarSlot(op),
+ SetVarField(op) => Lifted::SetVarField(LiftedSetVarField {
+ dest: op.dest,
+ offset: op.offset,
+ src: self.lift_operand(op.src),
+ }),
+ SetVar(op) => Lifted::SetVar(LiftedSetVar {
+ dest: op.dest,
+ src: self.lift_operand(op.src),
+ }),
+ FreeVarSlotSsa(op) => Lifted::FreeVarSlotSsa(op),
+ SetVarSsaField(op) => Lifted::SetVarSsaField(LiftedSetVarSsaField {
+ dest: op.dest,
+ prev: op.prev,
+ offset: op.offset,
+ src: self.lift_operand(op.src),
+ }),
+ SetVarAliasedField(op) => Lifted::SetVarAliasedField(LiftedSetVarSsaField {
+ dest: op.dest,
+ prev: op.prev,
+ offset: op.offset,
+ src: self.lift_operand(op.src),
+ }),
+ SetVarAliased(op) => Lifted::SetVarAliased(LiftedSetVarAliased {
+ dest: op.dest,
+ prev: op.prev,
+ src: self.lift_operand(op.src),
+ }),
+ SetVarSsa(op) => Lifted::SetVarSsa(LiftedSetVarSsa {
+ dest: op.dest,
+ src: self.lift_operand(op.src),
+ }),
+ VarPhi(op) => Lifted::VarPhi(LiftedVarPhi {
+ dest: op.dest,
+ src: OperandIter::new(&*self.function, op.first_operand, op.num_operands)
+ .ssa_vars()
+ .collect(),
+ }),
+ MemPhi(op) => Lifted::MemPhi(LiftedMemPhi {
+ dest_memory: op.dest_memory,
+ src_memory: OperandIter::new(&*self.function, op.first_operand, op.num_operands)
+ .collect(),
+ }),
+ VarSplit(op) => Lifted::VarSplit(op),
+ SetVarSplit(op) => Lifted::SetVarSplit(LiftedSetVarSplit {
+ high: op.high,
+ low: op.low,
+ src: self.lift_operand(op.src),
+ }),
+ VarSplitSsa(op) => Lifted::VarSplitSsa(op),
+ SetVarSplitSsa(op) => Lifted::SetVarSplitSsa(LiftedSetVarSplitSsa {
+ high: op.high,
+ low: op.low,
+ src: self.lift_operand(op.src),
+ }),
+
+ Add(op) => Lifted::Add(self.lift_binary_op(op)),
+ Sub(op) => Lifted::Sub(self.lift_binary_op(op)),
+ And(op) => Lifted::And(self.lift_binary_op(op)),
+ Or(op) => Lifted::Or(self.lift_binary_op(op)),
+ Xor(op) => Lifted::Xor(self.lift_binary_op(op)),
+ Lsl(op) => Lifted::Lsl(self.lift_binary_op(op)),
+ Lsr(op) => Lifted::Lsr(self.lift_binary_op(op)),
+ Asr(op) => Lifted::Asr(self.lift_binary_op(op)),
+ Rol(op) => Lifted::Rol(self.lift_binary_op(op)),
+ Ror(op) => Lifted::Ror(self.lift_binary_op(op)),
+ Mul(op) => Lifted::Mul(self.lift_binary_op(op)),
+ MuluDp(op) => Lifted::MuluDp(self.lift_binary_op(op)),
+ MulsDp(op) => Lifted::MulsDp(self.lift_binary_op(op)),
+ Divu(op) => Lifted::Divu(self.lift_binary_op(op)),
+ DivuDp(op) => Lifted::DivuDp(self.lift_binary_op(op)),
+ Divs(op) => Lifted::Divs(self.lift_binary_op(op)),
+ DivsDp(op) => Lifted::DivsDp(self.lift_binary_op(op)),
+ Modu(op) => Lifted::Modu(self.lift_binary_op(op)),
+ ModuDp(op) => Lifted::ModuDp(self.lift_binary_op(op)),
+ Mods(op) => Lifted::Mods(self.lift_binary_op(op)),
+ ModsDp(op) => Lifted::ModsDp(self.lift_binary_op(op)),
+ CmpE(op) => Lifted::CmpE(self.lift_binary_op(op)),
+ CmpNe(op) => Lifted::CmpNe(self.lift_binary_op(op)),
+ CmpSlt(op) => Lifted::CmpSlt(self.lift_binary_op(op)),
+ CmpUlt(op) => Lifted::CmpUlt(self.lift_binary_op(op)),
+ CmpSle(op) => Lifted::CmpSle(self.lift_binary_op(op)),
+ CmpUle(op) => Lifted::CmpUle(self.lift_binary_op(op)),
+ CmpSge(op) => Lifted::CmpSge(self.lift_binary_op(op)),
+ CmpUge(op) => Lifted::CmpUge(self.lift_binary_op(op)),
+ CmpSgt(op) => Lifted::CmpSgt(self.lift_binary_op(op)),
+ CmpUgt(op) => Lifted::CmpUgt(self.lift_binary_op(op)),
+ TestBit(op) => Lifted::TestBit(self.lift_binary_op(op)),
+ AddOverflow(op) => Lifted::AddOverflow(self.lift_binary_op(op)),
+ FcmpE(op) => Lifted::FcmpE(self.lift_binary_op(op)),
+ FcmpNe(op) => Lifted::FcmpNe(self.lift_binary_op(op)),
+ FcmpLt(op) => Lifted::FcmpLt(self.lift_binary_op(op)),
+ FcmpLe(op) => Lifted::FcmpLe(self.lift_binary_op(op)),
+ FcmpGe(op) => Lifted::FcmpGe(self.lift_binary_op(op)),
+ FcmpGt(op) => Lifted::FcmpGt(self.lift_binary_op(op)),
+ FcmpO(op) => Lifted::FcmpO(self.lift_binary_op(op)),
+ FcmpUo(op) => Lifted::FcmpUo(self.lift_binary_op(op)),
+ Fadd(op) => Lifted::Fadd(self.lift_binary_op(op)),
+ Fsub(op) => Lifted::Fsub(self.lift_binary_op(op)),
+ Fmul(op) => Lifted::Fmul(self.lift_binary_op(op)),
+ Fdiv(op) => Lifted::Fdiv(self.lift_binary_op(op)),
+
+ Adc(op) => Lifted::Adc(self.lift_binary_op_carry(op)),
+ Sbb(op) => Lifted::Sbb(self.lift_binary_op_carry(op)),
+ Rlc(op) => Lifted::Rlc(self.lift_binary_op_carry(op)),
+ Rrc(op) => Lifted::Rrc(self.lift_binary_op_carry(op)),
+
+ Call(op) => Lifted::Call(self.lift_call(op)),
+ Tailcall(op) => Lifted::Tailcall(self.lift_call(op)),
+
+ Intrinsic(op) => Lifted::Intrinsic(LiftedIntrinsic {
+ output: OperandIter::new(&*self.function, op.first_output, op.num_outputs)
+ .vars()
+ .collect(),
+ intrinsic: CoreIntrinsic::new(
+ self.function.function().arch(),
+ IntrinsicId(op.intrinsic),
+ )
+ .expect("Valid intrinsic"),
+ params: OperandIter::new(&*self.function, op.first_param, op.num_params)
+ .exprs()
+ .map(|expr| expr.lift())
+ .collect(),
+ }),
+ Syscall(op) => Lifted::Syscall(LiftedSyscallCall {
+ output: OperandIter::new(&*self.function, op.first_output, op.num_outputs)
+ .vars()
+ .collect(),
+ params: OperandIter::new(&*self.function, op.first_param, op.num_params)
+ .exprs()
+ .map(|expr| expr.lift())
+ .collect(),
+ }),
+ IntrinsicSsa(op) => Lifted::IntrinsicSsa(LiftedIntrinsicSsa {
+ output: OperandIter::new(&*self.function, op.first_output, op.num_outputs)
+ .ssa_vars()
+ .collect(),
+ intrinsic: CoreIntrinsic::new(
+ self.function.function().arch(),
+ IntrinsicId(op.intrinsic),
+ )
+ .expect("Valid intrinsic"),
+ params: OperandIter::new(&*self.function, op.first_param, op.num_params)
+ .exprs()
+ .map(|expr| expr.lift())
+ .collect(),
+ }),
+
+ CallSsa(op) => Lifted::CallSsa(self.lift_call_ssa(op)),
+ TailcallSsa(op) => Lifted::TailcallSsa(self.lift_call_ssa(op)),
+
+ CallUntypedSsa(op) => Lifted::CallUntypedSsa(self.lift_call_untyped_ssa(op)),
+ TailcallUntypedSsa(op) => Lifted::TailcallUntypedSsa(self.lift_call_untyped_ssa(op)),
+
+ SyscallSsa(op) => Lifted::SyscallSsa(LiftedSyscallSsa {
+ output: get_call_output_ssa(&self.function, op.output).collect(),
+ params: OperandIter::new(&*self.function, op.first_param, op.num_params)
+ .exprs()
+ .map(|expr| expr.lift())
+ .collect(),
+ src_memory: op.src_memory,
+ }),
+ SyscallUntypedSsa(op) => Lifted::SyscallUntypedSsa(LiftedSyscallUntypedSsa {
+ output: get_call_output_ssa(&self.function, op.output).collect(),
+ params: get_call_params_ssa(&self.function, op.params)
+ .map(|param| param.lift())
+ .collect(),
+ stack: self.lift_operand(op.stack),
+ }),
+
+ CallUntyped(op) => Lifted::CallUntyped(self.lift_call_untyped(op)),
+ TailcallUntyped(op) => Lifted::TailcallUntyped(self.lift_call_untyped(op)),
+ SyscallUntyped(op) => Lifted::SyscallUntyped(LiftedSyscallUntyped {
+ output: get_call_output(&self.function, op.output).collect(),
+ params: get_call_params(&self.function, op.params)
+ .map(|param| param.lift())
+ .collect(),
+ stack: self.lift_operand(op.stack),
+ }),
+
+ Neg(op) => Lifted::Neg(self.lift_unary_op(op)),
+ Not(op) => Lifted::Not(self.lift_unary_op(op)),
+ Sx(op) => Lifted::Sx(self.lift_unary_op(op)),
+ Zx(op) => Lifted::Zx(self.lift_unary_op(op)),
+ LowPart(op) => Lifted::LowPart(self.lift_unary_op(op)),
+ BoolToInt(op) => Lifted::BoolToInt(self.lift_unary_op(op)),
+ UnimplMem(op) => Lifted::UnimplMem(self.lift_unary_op(op)),
+ Fsqrt(op) => Lifted::Fsqrt(self.lift_unary_op(op)),
+ Fneg(op) => Lifted::Fneg(self.lift_unary_op(op)),
+ Fabs(op) => Lifted::Fabs(self.lift_unary_op(op)),
+ FloatToInt(op) => Lifted::FloatToInt(self.lift_unary_op(op)),
+ IntToFloat(op) => Lifted::IntToFloat(self.lift_unary_op(op)),
+ FloatConv(op) => Lifted::FloatConv(self.lift_unary_op(op)),
+ RoundToInt(op) => Lifted::RoundToInt(self.lift_unary_op(op)),
+ Floor(op) => Lifted::Floor(self.lift_unary_op(op)),
+ Ceil(op) => Lifted::Ceil(self.lift_unary_op(op)),
+ Ftrunc(op) => Lifted::Ftrunc(self.lift_unary_op(op)),
+ Load(op) => Lifted::Load(self.lift_unary_op(op)),
+
+ LoadStruct(op) => Lifted::LoadStruct(LiftedLoadStruct {
+ src: self.lift_operand(op.src),
+ offset: op.offset,
+ }),
+ LoadStructSsa(op) => Lifted::LoadStructSsa(LiftedLoadStructSsa {
+ src: self.lift_operand(op.src),
+ offset: op.offset,
+ src_memory: op.src_memory,
+ }),
+ LoadSsa(op) => Lifted::LoadSsa(LiftedLoadSsa {
+ src: self.lift_operand(op.src),
+ src_memory: op.src_memory,
+ }),
+ Ret(op) => Lifted::Ret(LiftedRet {
+ src: OperandIter::new(&*self.function, op.first_operand, op.num_operands)
+ .exprs()
+ .map(|expr| expr.lift())
+ .collect(),
+ }),
+ SeparateParamList(op) => Lifted::SeparateParamList(LiftedSeparateParamList {
+ params: OperandIter::new(&*self.function, op.first_param, op.num_params)
+ .exprs()
+ .map(|expr| expr.lift())
+ .collect(),
+ }),
+ SharedParamSlot(op) => Lifted::SharedParamSlot(LiftedSharedParamSlot {
+ params: OperandIter::new(&*self.function, op.first_param, op.num_params)
+ .exprs()
+ .map(|expr| expr.lift())
+ .collect(),
+ }),
+ Var(op) => Lifted::Var(op),
+ AddressOf(op) => Lifted::AddressOf(op),
+ VarField(op) => Lifted::VarField(op),
+ AddressOfField(op) => Lifted::AddressOfField(op),
+ VarSsa(op) => Lifted::VarSsa(op),
+ VarAliased(op) => Lifted::VarAliased(op),
+ VarSsaField(op) => Lifted::VarSsaField(op),
+ VarAliasedField(op) => Lifted::VarAliasedField(op),
+ Trap(op) => Lifted::Trap(op),
+ };
+
+ MediumLevelILLiftedInstruction {
+ function: self.function.clone(),
+ address: self.address,
+ index: self.expr_index,
+ size: self.size,
+ kind,
+ }
+ }
+
+ pub fn tokens(&self) -> Array<InstructionTextToken> {
+ let mut count = 0;
+ let mut tokens = core::ptr::null_mut();
+ assert!(unsafe {
+ BNGetMediumLevelILExprText(
+ self.function.handle,
+ self.function.function().arch().handle,
+ self.expr_index.0,
+ &mut tokens,
+ &mut count,
+ core::ptr::null_mut(),
+ )
+ });
+ unsafe { Array::new(tokens, count, ()) }
+ }
+
+ /// Value of expression if constant or a known value
+ pub fn value(&self) -> RegisterValue {
+ unsafe { BNGetMediumLevelILExprValue(self.function.handle, self.expr_index.0) }.into()
+ }
+
+ /// Returns the [`BasicBlock`] containing the given [`MediumLevelILInstruction`].
+ pub fn basic_block(&self) -> Option<Ref<BasicBlock<MediumLevelILBlock>>> {
+ // TODO: We might be able to .expect this if we guarantee that self.index is valid.
+ self.function.basic_block_containing_index(self.expr_index)
+ }
+
+ /// Possible values of expression using path-sensitive static data flow analysis
+ pub fn possible_values(&self) -> PossibleValueSet {
+ self.possible_values_with_opts(&[])
+ }
+
+ /// Possible values of expression using path-sensitive static data flow analysis
+ pub fn possible_values_with_opts(&self, options: &[DataFlowQueryOption]) -> PossibleValueSet {
+ let value = unsafe {
+ BNGetMediumLevelILPossibleExprValues(
+ self.function.handle,
+ self.expr_index.0,
+ options.as_ptr() as *mut _,
+ options.len(),
+ )
+ };
+ PossibleValueSet::from_owned_raw(value)
+ }
+
+ pub fn possible_ssa_variable_values(&self, ssa_var: &SSAVariable) -> PossibleValueSet {
+ self.possible_ssa_variable_values_with_opts(ssa_var, &[])
+ }
+
+ pub fn possible_ssa_variable_values_with_opts(
+ &self,
+ ssa_var: &SSAVariable,
+ options: &[DataFlowQueryOption],
+ ) -> PossibleValueSet {
+ let raw_var = BNVariable::from(ssa_var.variable);
+ let value = unsafe {
+ BNGetMediumLevelILPossibleSSAVarValues(
+ self.function.handle,
+ &raw_var,
+ ssa_var.version,
+ self.expr_index.0,
+ options.as_ptr() as *mut _,
+ options.len(),
+ )
+ };
+ PossibleValueSet::from_owned_raw(value)
+ }
+
+ /// Return the ssa version of a [`Variable`] at the given instruction.
+ pub fn ssa_variable_version(&self, var: Variable) -> SSAVariable {
+ let raw_var = BNVariable::from(var);
+ let version = unsafe {
+ BNGetMediumLevelILSSAVarVersionAtILInstruction(
+ self.function.handle,
+ &raw_var,
+ self.expr_index.0,
+ )
+ };
+ SSAVariable::new(var, version)
+ }
+
+ /// Set of branching instructions that must take the true or false path to reach this instruction
+ pub fn branch_dependencies(&self) -> Array<BranchDependence> {
+ let mut count = 0;
+ let deps = unsafe {
+ BNGetAllMediumLevelILBranchDependence(
+ self.function.handle,
+ self.expr_index.0,
+ &mut count,
+ )
+ };
+ assert!(!deps.is_null());
+ unsafe { Array::new(deps, count, self.function.clone()) }
+ }
+
+ pub fn branch_dependence_at(&self, instruction: MediumLevelILInstruction) -> BranchDependence {
+ let deps = unsafe {
+ BNGetMediumLevelILBranchDependence(
+ self.function.handle,
+ self.expr_index.0,
+ instruction.expr_index.0,
+ )
+ };
+ BranchDependence {
+ instruction,
+ dependence: deps,
+ }
+ }
+
+ /// Version of active memory contents in SSA form for this instruction
+ pub fn ssa_memory_version(&self) -> usize {
+ unsafe {
+ BNGetMediumLevelILSSAMemoryVersionAtILInstruction(
+ self.function.handle,
+ self.expr_index.0,
+ )
+ }
+ }
+
+ /// Type of expression
+ pub fn expr_type(&self) -> Option<Conf<Ref<Type>>> {
+ let result = unsafe { BNGetMediumLevelILExprType(self.function.handle, self.expr_index.0) };
+ (!result.type_.is_null()).then(|| Conf::<Ref<Type>>::from_owned_raw(result))
+ }
+
+ /// Set type of expression
+ ///
+ /// This API is only meant for workflows or for debugging purposes, since the changes they make are not persistent
+ /// and get lost after a database save and reload. To make persistent changes to the analysis, one should use other
+ /// APIs to, for example, change the type of variables. The analysis will then propagate the type of the variable
+ /// and update the type of related expressions.
+ pub fn set_expr_type<'a, T: Into<Conf<&'a Type>>>(&self, ty: T) {
+ let mut ty: BNTypeWithConfidence = Conf::<&Type>::into_raw(ty.into());
+ unsafe { BNSetMediumLevelILExprType(self.function.handle, self.expr_index.0, &mut ty) }
+ }
+
+ pub fn variable_for_register(&self, reg_id: RegisterId) -> Variable {
+ let result = unsafe {
+ BNGetMediumLevelILVariableForRegisterAtInstruction(
+ self.function.handle,
+ reg_id.0,
+ self.expr_index.0,
+ )
+ };
+ Variable::from(result)
+ }
+
+ pub fn variable_for_flag(&self, flag_id: FlagId) -> Variable {
+ let result = unsafe {
+ BNGetMediumLevelILVariableForFlagAtInstruction(
+ self.function.handle,
+ flag_id.0,
+ self.expr_index.0,
+ )
+ };
+ Variable::from(result)
+ }
+
+ pub fn variable_for_stack_location(&self, offset: i64) -> Variable {
+ let result = unsafe {
+ BNGetMediumLevelILVariableForStackLocationAtInstruction(
+ self.function.handle,
+ offset,
+ self.expr_index.0,
+ )
+ };
+ Variable::from(result)
+ }
+
+ pub fn register_value(&self, reg_id: RegisterId) -> RegisterValue {
+ unsafe {
+ BNGetMediumLevelILRegisterValueAtInstruction(
+ self.function.handle,
+ reg_id.0,
+ self.expr_index.0,
+ )
+ }
+ .into()
+ }
+
+ pub fn register_value_after(&self, reg_id: RegisterId) -> RegisterValue {
+ unsafe {
+ BNGetMediumLevelILRegisterValueAfterInstruction(
+ self.function.handle,
+ reg_id.0,
+ self.expr_index.0,
+ )
+ }
+ .into()
+ }
+
+ pub fn possible_register_values(&self, reg_id: RegisterId) -> PossibleValueSet {
+ self.possible_register_values_with_opts(reg_id, &[])
+ }
+
+ pub fn possible_register_values_with_opts(
+ &self,
+ reg_id: RegisterId,
+ options: &[DataFlowQueryOption],
+ ) -> PossibleValueSet {
+ let value = unsafe {
+ BNGetMediumLevelILPossibleRegisterValuesAtInstruction(
+ self.function.handle,
+ reg_id.0,
+ self.expr_index.0,
+ options.as_ptr() as *mut _,
+ options.len(),
+ )
+ };
+ PossibleValueSet::from_owned_raw(value)
+ }
+
+ pub fn possible_register_values_after(&self, reg_id: RegisterId) -> PossibleValueSet {
+ self.possible_register_values_after_with_opts(reg_id, &[])
+ }
+
+ pub fn possible_register_values_after_with_opts(
+ &self,
+ reg_id: RegisterId,
+ options: &[DataFlowQueryOption],
+ ) -> PossibleValueSet {
+ let value = unsafe {
+ BNGetMediumLevelILPossibleRegisterValuesAfterInstruction(
+ self.function.handle,
+ reg_id.0,
+ self.expr_index.0,
+ options.as_ptr() as *mut _,
+ options.len(),
+ )
+ };
+ PossibleValueSet::from_owned_raw(value)
+ }
+
+ pub fn flag_value(&self, flag_id: FlagId) -> RegisterValue {
+ unsafe {
+ BNGetMediumLevelILFlagValueAtInstruction(
+ self.function.handle,
+ flag_id.0,
+ self.expr_index.0,
+ )
+ }
+ .into()
+ }
+
+ pub fn flag_value_after(&self, flag_id: FlagId) -> RegisterValue {
+ unsafe {
+ BNGetMediumLevelILFlagValueAfterInstruction(
+ self.function.handle,
+ flag_id.0,
+ self.expr_index.0,
+ )
+ }
+ .into()
+ }
+
+ pub fn possible_flag_values(&self, flag_id: FlagId) -> PossibleValueSet {
+ self.possible_flag_values_with_opts(flag_id, &[])
+ }
+
+ pub fn possible_flag_values_with_opts(
+ &self,
+ flag_id: FlagId,
+ options: &[DataFlowQueryOption],
+ ) -> PossibleValueSet {
+ let value = unsafe {
+ BNGetMediumLevelILPossibleFlagValuesAtInstruction(
+ self.function.handle,
+ flag_id.0,
+ self.expr_index.0,
+ options.as_ptr() as *mut _,
+ options.len(),
+ )
+ };
+ PossibleValueSet::from_owned_raw(value)
+ }
+
+ pub fn possible_flag_values_after_with_opts(
+ &self,
+ flag_id: FlagId,
+ options: &[DataFlowQueryOption],
+ ) -> PossibleValueSet {
+ let value = unsafe {
+ BNGetMediumLevelILPossibleFlagValuesAfterInstruction(
+ self.function.handle,
+ flag_id.0,
+ self.expr_index.0,
+ options.as_ptr() as *mut _,
+ options.len(),
+ )
+ };
+ PossibleValueSet::from_owned_raw(value)
+ }
+
+ pub fn stack_contents(&self, offset: i64, size: usize) -> RegisterValue {
+ unsafe {
+ BNGetMediumLevelILStackContentsAtInstruction(
+ self.function.handle,
+ offset,
+ size,
+ self.expr_index.0,
+ )
+ }
+ .into()
+ }
+
+ pub fn stack_contents_after(&self, offset: i64, size: usize) -> RegisterValue {
+ unsafe {
+ BNGetMediumLevelILStackContentsAfterInstruction(
+ self.function.handle,
+ offset,
+ size,
+ self.expr_index.0,
+ )
+ }
+ .into()
+ }
+
+ pub fn possible_stack_contents_with_opts(
+ &self,
+ offset: i64,
+ size: usize,
+ options: &[DataFlowQueryOption],
+ ) -> PossibleValueSet {
+ let value = unsafe {
+ BNGetMediumLevelILPossibleStackContentsAtInstruction(
+ self.function.handle,
+ offset,
+ size,
+ self.expr_index.0,
+ options.as_ptr() as *mut _,
+ options.len(),
+ )
+ };
+ PossibleValueSet::from_owned_raw(value)
+ }
+
+ pub fn possible_stack_contents_after_with_opts(
+ &self,
+ offset: i64,
+ size: usize,
+ options: &[DataFlowQueryOption],
+ ) -> PossibleValueSet {
+ let value = unsafe {
+ BNGetMediumLevelILPossibleStackContentsAfterInstruction(
+ self.function.handle,
+ offset,
+ size,
+ self.expr_index.0,
+ options.as_ptr() as *mut _,
+ options.len(),
+ )
+ };
+ PossibleValueSet::from_owned_raw(value)
+ }
+
+ /// Gets the unique variable for a definition instruction. This unique variable can be passed
+ /// to [crate::function::Function::split_variable] to split a variable at a definition. The given `var` is the
+ /// assigned variable to query.
+ ///
+ /// * `var` - variable to query
+ pub fn split_var_for_definition(&self, var: &Variable) -> Variable {
+ let raw_var = BNVariable::from(var);
+ let index = unsafe {
+ BNGetDefaultIndexForMediumLevelILVariableDefinition(
+ self.function.handle,
+ &raw_var,
+ self.expr_index.0,
+ )
+ };
+ Variable::new(var.ty, index, var.storage)
+ }
+
+ /// alias for [MediumLevelILInstruction::split_var_for_definition]
+ #[inline]
+ pub fn get_split_var_for_definition(&self, var: &Variable) -> Variable {
+ self.split_var_for_definition(var)
+ }
+
+ fn lift_operand(&self, expr_idx: usize) -> Box<MediumLevelILLiftedInstruction> {
+ // TODO: UGH, if your gonna call it expr_idx, call the instruction and expression!!!!!
+ // TODO: We dont even need to say instruction in the type!
+ // TODO: IF you want to have an instruction type, there needs to be a separate expression type
+ // TODO: See the lowlevelil module.
+ let expr_idx_is_really_instr_idx = MediumLevelInstructionIndex(expr_idx);
+ // TODO: See the comment in the unchecked function, ugh, i hate this..
+ let operand_instr = self
+ .function
+ .instruction_from_expr_index(expr_idx_is_really_instr_idx)
+ .unwrap();
+ Box::new(operand_instr.lift())
+ }
+
+ fn lift_binary_op(&self, op: BinaryOp) -> LiftedBinaryOp {
+ LiftedBinaryOp {
+ left: self.lift_operand(op.left),
+ right: self.lift_operand(op.right),
+ }
+ }
+
+ fn lift_binary_op_carry(&self, op: BinaryOpCarry) -> LiftedBinaryOpCarry {
+ LiftedBinaryOpCarry {
+ left: self.lift_operand(op.left),
+ right: self.lift_operand(op.right),
+ carry: self.lift_operand(op.carry),
+ }
+ }
+
+ fn lift_unary_op(&self, op: UnaryOp) -> LiftedUnaryOp {
+ LiftedUnaryOp {
+ src: self.lift_operand(op.src),
+ }
+ }
+
+ fn lift_call(&self, op: Call) -> LiftedCall {
+ LiftedCall {
+ output: OperandIter::new(&*self.function, op.first_output, op.num_outputs)
+ .vars()
+ .collect(),
+ dest: self.lift_operand(op.dest),
+ params: OperandIter::new(&*self.function, op.first_param, op.num_params)
+ .exprs()
+ .map(|expr| expr.lift())
+ .collect(),
+ }
+ }
+
+ fn lift_call_untyped(&self, op: CallUntyped) -> LiftedCallUntyped {
+ LiftedCallUntyped {
+ output: get_call_output(&self.function, op.output).collect(),
+ dest: self.lift_operand(op.dest),
+ params: get_call_params(&self.function, op.params)
+ .map(|expr| expr.lift())
+ .collect(),
+ stack: self.lift_operand(op.stack),
+ }
+ }
+
+ fn lift_call_ssa(&self, op: CallSsa) -> LiftedCallSsa {
+ LiftedCallSsa {
+ output: get_call_output_ssa(&self.function, op.output).collect(),
+ dest: self.lift_operand(op.dest),
+ params: OperandIter::new(&*self.function, op.first_param, op.num_params)
+ .exprs()
+ .map(|expr| expr.lift())
+ .collect(),
+ src_memory: op.src_memory,
+ }
+ }
+
+ fn lift_call_untyped_ssa(&self, op: CallUntypedSsa) -> LiftedCallUntypedSsa {
+ LiftedCallUntypedSsa {
+ output: get_call_output_ssa(&self.function, op.output).collect(),
+ dest: self.lift_operand(op.dest),
+ params: get_call_params_ssa(&self.function, op.params)
+ .map(|param| param.lift())
+ .collect(),
+ stack: self.lift_operand(op.stack),
+ }
+ }
+}
+
+impl Debug for MediumLevelILInstruction {
+ fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
+ f.debug_struct("MediumLevelILInstruction")
+ .field("address", &self.address)
+ .field("index", &self.expr_index)
+ .field("size", &self.size)
+ .field("kind", &self.kind)
+ .finish()
+ }
+}
+
+impl CoreArrayProvider for MediumLevelILInstruction {
+ type Raw = usize;
+ type Context = Ref<MediumLevelILFunction>;
+ type Wrapped<'a> = Self;
+}
+
+unsafe impl CoreArrayProviderInner for MediumLevelILInstruction {
+ unsafe fn free(raw: *mut Self::Raw, _count: usize, _context: &Self::Context) {
+ BNFreeILInstructionList(raw)
+ }
+
+ unsafe fn wrap_raw<'a>(raw: &'a Self::Raw, context: &'a Self::Context) -> Self::Wrapped<'a> {
+ // TODO: This needs to be tested!!!!
+ // TODO: What if this does not need to be mapped!!!!
+ context
+ .instruction_from_index(MediumLevelInstructionIndex(*raw))
+ .unwrap()
+ }
+}
+
+#[derive(Debug, Copy, Clone)]
+pub enum MediumLevelILInstructionKind {
+ Nop,
+ Noret,
+ Bp,
+ Undef,
+ Unimpl,
+ If(MediumLevelILOperationIf),
+ FloatConst(FloatConst),
+ Const(Constant),
+ ConstPtr(Constant),
+ Import(Constant),
+ ExternPtr(ExternPtr),
+ ConstData(ConstData),
+ Jump(Jump),
+ RetHint(Jump),
+ StoreSsa(StoreSsa),
+ StoreStructSsa(StoreStructSsa),
+ StoreStruct(StoreStruct),
+ Store(Store),
+ JumpTo(JumpTo),
+ Goto(Goto),
+ FreeVarSlot(FreeVarSlot),
+ SetVarField(SetVarField),
+ SetVar(SetVar),
+ FreeVarSlotSsa(FreeVarSlotSsa),
+ SetVarSsaField(SetVarSsaField),
+ SetVarAliasedField(SetVarSsaField),
+ SetVarAliased(SetVarAliased),
+ SetVarSsa(SetVarSsa),
+ VarPhi(VarPhi),
+ MemPhi(MemPhi),
+ VarSplit(VarSplit),
+ SetVarSplit(SetVarSplit),
+ VarSplitSsa(VarSplitSsa),
+ SetVarSplitSsa(SetVarSplitSsa),
+ Add(BinaryOp),
+ Sub(BinaryOp),
+ And(BinaryOp),
+ Or(BinaryOp),
+ Xor(BinaryOp),
+ Lsl(BinaryOp),
+ Lsr(BinaryOp),
+ Asr(BinaryOp),
+ Rol(BinaryOp),
+ Ror(BinaryOp),
+ Mul(BinaryOp),
+ MuluDp(BinaryOp),
+ MulsDp(BinaryOp),
+ Divu(BinaryOp),
+ DivuDp(BinaryOp),
+ Divs(BinaryOp),
+ DivsDp(BinaryOp),
+ Modu(BinaryOp),
+ ModuDp(BinaryOp),
+ Mods(BinaryOp),
+ ModsDp(BinaryOp),
+ CmpE(BinaryOp),
+ CmpNe(BinaryOp),
+ CmpSlt(BinaryOp),
+ CmpUlt(BinaryOp),
+ CmpSle(BinaryOp),
+ CmpUle(BinaryOp),
+ CmpSge(BinaryOp),
+ CmpUge(BinaryOp),
+ CmpSgt(BinaryOp),
+ CmpUgt(BinaryOp),
+ TestBit(BinaryOp),
+ AddOverflow(BinaryOp),
+ FcmpE(BinaryOp),
+ FcmpNe(BinaryOp),
+ FcmpLt(BinaryOp),
+ FcmpLe(BinaryOp),
+ FcmpGe(BinaryOp),
+ FcmpGt(BinaryOp),
+ FcmpO(BinaryOp),
+ FcmpUo(BinaryOp),
+ Fadd(BinaryOp),
+ Fsub(BinaryOp),
+ Fmul(BinaryOp),
+ Fdiv(BinaryOp),
+ Adc(BinaryOpCarry),
+ Sbb(BinaryOpCarry),
+ Rlc(BinaryOpCarry),
+ Rrc(BinaryOpCarry),
+ Call(Call),
+ Tailcall(Call),
+ Syscall(Syscall),
+ Intrinsic(Intrinsic),
+ IntrinsicSsa(IntrinsicSsa),
+ CallSsa(CallSsa),
+ TailcallSsa(CallSsa),
+ CallUntypedSsa(CallUntypedSsa),
+ TailcallUntypedSsa(CallUntypedSsa),
+ SyscallSsa(SyscallSsa),
+ SyscallUntypedSsa(SyscallUntypedSsa),
+ CallUntyped(CallUntyped),
+ TailcallUntyped(CallUntyped),
+ SyscallUntyped(SyscallUntyped),
+ SeparateParamList(SeparateParamList),
+ SharedParamSlot(SharedParamSlot),
+ Neg(UnaryOp),
+ Not(UnaryOp),
+ Sx(UnaryOp),
+ Zx(UnaryOp),
+ LowPart(UnaryOp),
+ BoolToInt(UnaryOp),
+ UnimplMem(UnaryOp),
+ Fsqrt(UnaryOp),
+ Fneg(UnaryOp),
+ Fabs(UnaryOp),
+ FloatToInt(UnaryOp),
+ IntToFloat(UnaryOp),
+ FloatConv(UnaryOp),
+ RoundToInt(UnaryOp),
+ Floor(UnaryOp),
+ Ceil(UnaryOp),
+ Ftrunc(UnaryOp),
+ Load(UnaryOp),
+ LoadStruct(LoadStruct),
+ LoadStructSsa(LoadStructSsa),
+ LoadSsa(LoadSsa),
+ Ret(Ret),
+ Var(Var),
+ AddressOf(Var),
+ VarField(Field),
+ AddressOfField(Field),
+ VarSsa(VarSsa),
+ VarAliased(VarSsa),
+ VarSsaField(VarSsaField),
+ VarAliasedField(VarSsaField),
+ Trap(Trap),
+}
+
+fn get_float(value: u64, size: usize) -> f64 {
+ match size {
+ 4 => f32::from_bits(value as u32) as f64,
+ 8 => f64::from_bits(value),
+ // TODO how to handle this value?
+ size => todo!("float size {}", size),
+ }
+}
+
+fn get_raw_operation(function: &MediumLevelILFunction, idx: usize) -> BNMediumLevelILInstruction {
+ unsafe { BNGetMediumLevelILByIndex(function.handle, idx) }
+}
+
+fn get_var(id: u64) -> Variable {
+ Variable::from_identifier(id)
+}
+
+fn get_var_ssa(id: u64, version: usize) -> SSAVariable {
+ SSAVariable::new(get_var(id), version)
+}
+
+fn get_call_output(function: &MediumLevelILFunction, idx: usize) -> impl Iterator<Item = Variable> {
+ let op = get_raw_operation(function, idx);
+ assert_eq!(op.operation, BNMediumLevelILOperation::MLIL_CALL_OUTPUT);
+ OperandIter::new(function, op.operands[1] as usize, op.operands[0] as usize).vars()
+}
+
+fn get_call_params(
+ function: &MediumLevelILFunction,
+ idx: usize,
+) -> impl Iterator<Item = MediumLevelILInstruction> {
+ let op = get_raw_operation(function, idx);
+ assert_eq!(op.operation, BNMediumLevelILOperation::MLIL_CALL_PARAM);
+ OperandIter::new(function, op.operands[1] as usize, op.operands[0] as usize).exprs()
+}
+
+fn get_call_output_ssa(
+ function: &MediumLevelILFunction,
+ idx: usize,
+) -> impl Iterator<Item = SSAVariable> {
+ let op = get_raw_operation(function, idx);
+ assert_eq!(op.operation, BNMediumLevelILOperation::MLIL_CALL_OUTPUT_SSA);
+ OperandIter::new(function, op.operands[2] as usize, op.operands[1] as usize).ssa_vars()
+}
+
+fn get_call_params_ssa(
+ function: &MediumLevelILFunction,
+ idx: usize,
+) -> impl Iterator<Item = MediumLevelILInstruction> {
+ let op = get_raw_operation(function, idx);
+ assert_eq!(op.operation, BNMediumLevelILOperation::MLIL_CALL_PARAM_SSA);
+ OperandIter::new(function, op.operands[2] as usize, op.operands[1] as usize).exprs()
+}
+
+/// Conditional branching instruction and an expected conditional result
+pub struct BranchDependence {
+ pub instruction: MediumLevelILInstruction,
+ pub dependence: ILBranchDependence,
+}
+
+impl CoreArrayProvider for BranchDependence {
+ type Raw = BNILBranchInstructionAndDependence;
+ type Context = Ref<MediumLevelILFunction>;
+ type Wrapped<'a> = Self;
+}
+
+unsafe impl CoreArrayProviderInner for BranchDependence {
+ unsafe fn free(raw: *mut Self::Raw, _count: usize, _context: &Self::Context) {
+ unsafe { BNFreeILBranchDependenceList(raw) };
+ }
+
+ unsafe fn wrap_raw<'a>(raw: &'a Self::Raw, context: &'a Self::Context) -> Self::Wrapped<'a> {
+ Self {
+ instruction: MediumLevelILInstruction::new(
+ context.clone(),
+ MediumLevelInstructionIndex(raw.branch),
+ ),
+ dependence: raw.dependence,
+ }
+ }
+}
diff --git a/rust/src/medium_level_il/lift.rs b/rust/src/medium_level_il/lift.rs
new file mode 100644
index 00000000..d6cceb18
--- /dev/null
+++ b/rust/src/medium_level_il/lift.rs
@@ -0,0 +1,510 @@
+use std::collections::BTreeMap;
+
+use super::operation::*;
+use super::{MediumLevelILFunction, MediumLevelInstructionIndex};
+use crate::architecture::CoreIntrinsic;
+use crate::rc::Ref;
+use crate::variable::{ConstantData, SSAVariable, Variable};
+
+#[derive(Clone)]
+pub enum MediumLevelILLiftedOperand {
+ ConstantData(ConstantData),
+ Intrinsic(CoreIntrinsic),
+ Expr(MediumLevelILLiftedInstruction),
+ ExprList(Vec<MediumLevelILLiftedInstruction>),
+ Float(f64),
+ Int(u64),
+ IntList(Vec<u64>),
+ TargetMap(BTreeMap<u64, MediumLevelInstructionIndex>),
+ Var(Variable),
+ VarList(Vec<Variable>),
+ VarSsa(SSAVariable),
+ VarSsaList(Vec<SSAVariable>),
+ InstructionIndex(MediumLevelInstructionIndex),
+}
+
+#[derive(Clone, Debug, PartialEq)]
+pub struct MediumLevelILLiftedInstruction {
+ pub function: Ref<MediumLevelILFunction>,
+ pub address: u64,
+ pub index: MediumLevelInstructionIndex,
+ pub size: usize,
+ pub kind: MediumLevelILLiftedInstructionKind,
+}
+
+#[derive(Clone, Debug, PartialEq)]
+pub enum MediumLevelILLiftedInstructionKind {
+ Nop,
+ Noret,
+ Bp,
+ Undef,
+ Unimpl,
+ If(LiftedIf),
+ FloatConst(FloatConst),
+ Const(Constant),
+ ConstPtr(Constant),
+ Import(Constant),
+ ExternPtr(ExternPtr),
+ ConstData(LiftedConstData),
+ Jump(LiftedJump),
+ RetHint(LiftedJump),
+ StoreSsa(LiftedStoreSsa),
+ StoreStructSsa(LiftedStoreStructSsa),
+ StoreStruct(LiftedStoreStruct),
+ Store(LiftedStore),
+ JumpTo(LiftedJumpTo),
+ Goto(Goto),
+ FreeVarSlot(FreeVarSlot),
+ SetVarField(LiftedSetVarField),
+ SetVar(LiftedSetVar),
+ FreeVarSlotSsa(FreeVarSlotSsa),
+ SetVarSsaField(LiftedSetVarSsaField),
+ SetVarAliasedField(LiftedSetVarSsaField),
+ SetVarAliased(LiftedSetVarAliased),
+ SetVarSsa(LiftedSetVarSsa),
+ VarPhi(LiftedVarPhi),
+ MemPhi(LiftedMemPhi),
+ VarSplit(VarSplit),
+ SetVarSplit(LiftedSetVarSplit),
+ VarSplitSsa(VarSplitSsa),
+ SetVarSplitSsa(LiftedSetVarSplitSsa),
+ Add(LiftedBinaryOp),
+ Sub(LiftedBinaryOp),
+ And(LiftedBinaryOp),
+ Or(LiftedBinaryOp),
+ Xor(LiftedBinaryOp),
+ Lsl(LiftedBinaryOp),
+ Lsr(LiftedBinaryOp),
+ Asr(LiftedBinaryOp),
+ Rol(LiftedBinaryOp),
+ Ror(LiftedBinaryOp),
+ Mul(LiftedBinaryOp),
+ MuluDp(LiftedBinaryOp),
+ MulsDp(LiftedBinaryOp),
+ Divu(LiftedBinaryOp),
+ DivuDp(LiftedBinaryOp),
+ Divs(LiftedBinaryOp),
+ DivsDp(LiftedBinaryOp),
+ Modu(LiftedBinaryOp),
+ ModuDp(LiftedBinaryOp),
+ Mods(LiftedBinaryOp),
+ ModsDp(LiftedBinaryOp),
+ CmpE(LiftedBinaryOp),
+ CmpNe(LiftedBinaryOp),
+ CmpSlt(LiftedBinaryOp),
+ CmpUlt(LiftedBinaryOp),
+ CmpSle(LiftedBinaryOp),
+ CmpUle(LiftedBinaryOp),
+ CmpSge(LiftedBinaryOp),
+ CmpUge(LiftedBinaryOp),
+ CmpSgt(LiftedBinaryOp),
+ CmpUgt(LiftedBinaryOp),
+ TestBit(LiftedBinaryOp),
+ AddOverflow(LiftedBinaryOp),
+ FcmpE(LiftedBinaryOp),
+ FcmpNe(LiftedBinaryOp),
+ FcmpLt(LiftedBinaryOp),
+ FcmpLe(LiftedBinaryOp),
+ FcmpGe(LiftedBinaryOp),
+ FcmpGt(LiftedBinaryOp),
+ FcmpO(LiftedBinaryOp),
+ FcmpUo(LiftedBinaryOp),
+ Fadd(LiftedBinaryOp),
+ Fsub(LiftedBinaryOp),
+ Fmul(LiftedBinaryOp),
+ Fdiv(LiftedBinaryOp),
+ Adc(LiftedBinaryOpCarry),
+ Sbb(LiftedBinaryOpCarry),
+ Rlc(LiftedBinaryOpCarry),
+ Rrc(LiftedBinaryOpCarry),
+ Call(LiftedCall),
+ Tailcall(LiftedCall),
+ Intrinsic(LiftedIntrinsic),
+ Syscall(LiftedSyscallCall),
+ IntrinsicSsa(LiftedIntrinsicSsa),
+ CallSsa(LiftedCallSsa),
+ TailcallSsa(LiftedCallSsa),
+ CallUntypedSsa(LiftedCallUntypedSsa),
+ TailcallUntypedSsa(LiftedCallUntypedSsa),
+ SyscallSsa(LiftedSyscallSsa),
+ SyscallUntypedSsa(LiftedSyscallUntypedSsa),
+ CallUntyped(LiftedCallUntyped),
+ TailcallUntyped(LiftedCallUntyped),
+ SyscallUntyped(LiftedSyscallUntyped),
+ SeparateParamList(LiftedSeparateParamList),
+ SharedParamSlot(LiftedSharedParamSlot),
+ Neg(LiftedUnaryOp),
+ Not(LiftedUnaryOp),
+ Sx(LiftedUnaryOp),
+ Zx(LiftedUnaryOp),
+ LowPart(LiftedUnaryOp),
+ BoolToInt(LiftedUnaryOp),
+ UnimplMem(LiftedUnaryOp),
+ Fsqrt(LiftedUnaryOp),
+ Fneg(LiftedUnaryOp),
+ Fabs(LiftedUnaryOp),
+ FloatToInt(LiftedUnaryOp),
+ IntToFloat(LiftedUnaryOp),
+ FloatConv(LiftedUnaryOp),
+ RoundToInt(LiftedUnaryOp),
+ Floor(LiftedUnaryOp),
+ Ceil(LiftedUnaryOp),
+ Ftrunc(LiftedUnaryOp),
+ Load(LiftedUnaryOp),
+ LoadStruct(LiftedLoadStruct),
+ LoadStructSsa(LiftedLoadStructSsa),
+ LoadSsa(LiftedLoadSsa),
+ Ret(LiftedRet),
+ Var(Var),
+ AddressOf(Var),
+ VarField(Field),
+ AddressOfField(Field),
+ VarSsa(VarSsa),
+ VarAliased(VarSsa),
+ VarSsaField(VarSsaField),
+ VarAliasedField(VarSsaField),
+ Trap(Trap),
+}
+
+impl MediumLevelILLiftedInstruction {
+ pub fn name(&self) -> &'static str {
+ use MediumLevelILLiftedInstructionKind::*;
+ match self.kind {
+ Nop => "Nop",
+ Noret => "Noret",
+ Bp => "Bp",
+ Undef => "Undef",
+ Unimpl => "Unimpl",
+ If(_) => "If",
+ FloatConst(_) => "FloatConst",
+ Const(_) => "Const",
+ ConstPtr(_) => "ConstPtr",
+ Import(_) => "Import",
+ ExternPtr(_) => "ExternPtr",
+ ConstData(_) => "ConstData",
+ Jump(_) => "Jump",
+ RetHint(_) => "RetHint",
+ StoreSsa(_) => "StoreSsa",
+ StoreStructSsa(_) => "StoreStructSsa",
+ StoreStruct(_) => "StoreStruct",
+ Store(_) => "Store",
+ JumpTo(_) => "JumpTo",
+ Goto(_) => "Goto",
+ FreeVarSlot(_) => "FreeVarSlot",
+ SetVarField(_) => "SetVarField",
+ SetVar(_) => "SetVar",
+ FreeVarSlotSsa(_) => "FreeVarSlotSsa",
+ SetVarSsaField(_) => "SetVarSsaField",
+ SetVarAliasedField(_) => "SetVarAliasedField",
+ SetVarAliased(_) => "SetVarAliased",
+ SetVarSsa(_) => "SetVarSsa",
+ VarPhi(_) => "VarPhi",
+ MemPhi(_) => "MemPhi",
+ VarSplit(_) => "VarSplit",
+ SetVarSplit(_) => "SetVarSplit",
+ VarSplitSsa(_) => "VarSplitSsa",
+ SetVarSplitSsa(_) => "SetVarSplitSsa",
+ Add(_) => "Add",
+ Sub(_) => "Sub",
+ And(_) => "And",
+ Or(_) => "Or",
+ Xor(_) => "Xor",
+ Lsl(_) => "Lsl",
+ Lsr(_) => "Lsr",
+ Asr(_) => "Asr",
+ Rol(_) => "Rol",
+ Ror(_) => "Ror",
+ Mul(_) => "Mul",
+ MuluDp(_) => "MuluDp",
+ MulsDp(_) => "MulsDp",
+ Divu(_) => "Divu",
+ DivuDp(_) => "DivuDp",
+ Divs(_) => "Divs",
+ DivsDp(_) => "DivsDp",
+ Modu(_) => "Modu",
+ ModuDp(_) => "ModuDp",
+ Mods(_) => "Mods",
+ ModsDp(_) => "ModsDp",
+ CmpE(_) => "CmpE",
+ CmpNe(_) => "CmpNe",
+ CmpSlt(_) => "CmpSlt",
+ CmpUlt(_) => "CmpUlt",
+ CmpSle(_) => "CmpSle",
+ CmpUle(_) => "CmpUle",
+ CmpSge(_) => "CmpSge",
+ CmpUge(_) => "CmpUge",
+ CmpSgt(_) => "CmpSgt",
+ CmpUgt(_) => "CmpUgt",
+ TestBit(_) => "TestBit",
+ AddOverflow(_) => "AddOverflow",
+ FcmpE(_) => "FcmpE",
+ FcmpNe(_) => "FcmpNe",
+ FcmpLt(_) => "FcmpLt",
+ FcmpLe(_) => "FcmpLe",
+ FcmpGe(_) => "FcmpGe",
+ FcmpGt(_) => "FcmpGt",
+ FcmpO(_) => "FcmpO",
+ FcmpUo(_) => "FcmpUo",
+ Fadd(_) => "Fadd",
+ Fsub(_) => "Fsub",
+ Fmul(_) => "Fmul",
+ Fdiv(_) => "Fdiv",
+ Adc(_) => "Adc",
+ Sbb(_) => "Sbb",
+ Rlc(_) => "Rlc",
+ Rrc(_) => "Rrc",
+ Call(_) => "Call",
+ Tailcall(_) => "Tailcall",
+ Syscall(_) => "Syscall",
+ Intrinsic(_) => "Intrinsic",
+ IntrinsicSsa(_) => "IntrinsicSsa",
+ CallSsa(_) => "CallSsa",
+ TailcallSsa(_) => "TailcallSsa",
+ CallUntypedSsa(_) => "CallUntypedSsa",
+ TailcallUntypedSsa(_) => "TailcallUntypedSsa",
+ SyscallSsa(_) => "SyscallSsa",
+ SyscallUntypedSsa(_) => "SyscallUntypedSsa",
+ CallUntyped(_) => "CallUntyped",
+ TailcallUntyped(_) => "TailcallUntyped",
+ SyscallUntyped(_) => "SyscallUntyped",
+ SeparateParamList(_) => "SeparateParamList",
+ SharedParamSlot(_) => "SharedParamSlot",
+ Neg(_) => "Neg",
+ Not(_) => "Not",
+ Sx(_) => "Sx",
+ Zx(_) => "Zx",
+ LowPart(_) => "LowPart",
+ BoolToInt(_) => "BoolToInt",
+ UnimplMem(_) => "UnimplMem",
+ Fsqrt(_) => "Fsqrt",
+ Fneg(_) => "Fneg",
+ Fabs(_) => "Fabs",
+ FloatToInt(_) => "FloatToInt",
+ IntToFloat(_) => "IntToFloat",
+ FloatConv(_) => "FloatConv",
+ RoundToInt(_) => "RoundToInt",
+ Floor(_) => "Floor",
+ Ceil(_) => "Ceil",
+ Ftrunc(_) => "Ftrunc",
+ Load(_) => "Load",
+ LoadStruct(_) => "LoadStruct",
+ LoadStructSsa(_) => "LoadStructSsa",
+ LoadSsa(_) => "LoadSsa",
+ Ret(_) => "Ret",
+ Var(_) => "Var",
+ AddressOf(_) => "AddressOf",
+ VarField(_) => "VarField",
+ AddressOfField(_) => "AddressOfField",
+ VarSsa(_) => "VarSsa",
+ VarAliased(_) => "VarAliased",
+ VarSsaField(_) => "VarSsaField",
+ VarAliasedField(_) => "VarAliasedField",
+ Trap(_) => "Trap",
+ }
+ }
+
+ pub fn operands(&self) -> Vec<(&'static str, MediumLevelILLiftedOperand)> {
+ use MediumLevelILLiftedInstructionKind::*;
+ use MediumLevelILLiftedOperand as Operand;
+ match &self.kind {
+ Nop | Noret | Bp | Undef | Unimpl => vec![],
+ If(op) => vec![
+ ("condition", Operand::Expr(*op.condition.clone())),
+ ("dest_true", Operand::InstructionIndex(op.dest_true)),
+ ("dest_false", Operand::InstructionIndex(op.dest_false)),
+ ],
+ FloatConst(op) => vec![("constant", Operand::Float(op.constant))],
+ Const(op) | ConstPtr(op) | Import(op) => vec![("constant", Operand::Int(op.constant))],
+ ExternPtr(op) => vec![
+ ("constant", Operand::Int(op.constant)),
+ ("offset", Operand::Int(op.offset)),
+ ],
+ ConstData(op) => vec![(
+ "constant_data",
+ Operand::ConstantData(op.constant_data.clone()),
+ )],
+ Jump(op) | RetHint(op) => vec![("dest", Operand::Expr(*op.dest.clone()))],
+ StoreSsa(op) => vec![
+ ("dest", Operand::Expr(*op.dest.clone())),
+ ("dest_memory", Operand::Int(op.dest_memory)),
+ ("src_memory", Operand::Int(op.src_memory)),
+ ("src", Operand::Expr(*op.src.clone())),
+ ],
+ StoreStructSsa(op) => vec![
+ ("dest", Operand::Expr(*op.dest.clone())),
+ ("offset", Operand::Int(op.offset)),
+ ("dest_memory", Operand::Int(op.dest_memory)),
+ ("src_memory", Operand::Int(op.src_memory)),
+ ("src", Operand::Expr(*op.src.clone())),
+ ],
+ StoreStruct(op) => vec![
+ ("dest", Operand::Expr(*op.dest.clone())),
+ ("offset", Operand::Int(op.offset)),
+ ("src", Operand::Expr(*op.src.clone())),
+ ],
+ Store(op) => vec![
+ ("dest", Operand::Expr(*op.dest.clone())),
+ ("src", Operand::Expr(*op.src.clone())),
+ ],
+ JumpTo(op) => vec![
+ ("dest", Operand::Expr(*op.dest.clone())),
+ ("targets", Operand::TargetMap(op.targets.clone())),
+ ],
+ Goto(op) => vec![("dest", Operand::InstructionIndex(op.dest))],
+ FreeVarSlot(op) => vec![("dest", Operand::Var(op.dest))],
+ SetVarField(op) => vec![
+ ("dest", Operand::Var(op.dest)),
+ ("offset", Operand::Int(op.offset)),
+ ("src", Operand::Expr(*op.src.clone())),
+ ],
+ SetVar(op) => vec![
+ ("dest", Operand::Var(op.dest)),
+ ("src", Operand::Expr(*op.src.clone())),
+ ],
+ FreeVarSlotSsa(op) => vec![
+ ("dest", Operand::VarSsa(op.dest)),
+ ("prev", Operand::VarSsa(op.prev)),
+ ],
+ SetVarSsaField(op) | SetVarAliasedField(op) => vec![
+ ("dest", Operand::VarSsa(op.dest)),
+ ("prev", Operand::VarSsa(op.prev)),
+ ("offset", Operand::Int(op.offset)),
+ ("src", Operand::Expr(*op.src.clone())),
+ ],
+ SetVarAliased(op) => vec![
+ ("dest", Operand::VarSsa(op.dest)),
+ ("prev", Operand::VarSsa(op.prev)),
+ ("src", Operand::Expr(*op.src.clone())),
+ ],
+ SetVarSsa(op) => vec![
+ ("dest", Operand::VarSsa(op.dest)),
+ ("src", Operand::Expr(*op.src.clone())),
+ ],
+ VarPhi(op) => vec![
+ ("dest", Operand::VarSsa(op.dest)),
+ ("src", Operand::VarSsaList(op.src.clone())),
+ ],
+ MemPhi(op) => vec![
+ ("dest_memory", Operand::Int(op.dest_memory)),
+ ("src_memory", Operand::IntList(op.src_memory.clone())),
+ ],
+ VarSplit(op) => vec![
+ ("high", Operand::Var(op.high)),
+ ("low", Operand::Var(op.low)),
+ ],
+ SetVarSplit(op) => vec![
+ ("high", Operand::Var(op.high)),
+ ("low", Operand::Var(op.low)),
+ ("src", Operand::Expr(*op.src.clone())),
+ ],
+ VarSplitSsa(op) => vec![
+ ("high", Operand::VarSsa(op.high)),
+ ("low", Operand::VarSsa(op.low)),
+ ],
+ SetVarSplitSsa(op) => vec![
+ ("high", Operand::VarSsa(op.high)),
+ ("low", Operand::VarSsa(op.low)),
+ ("src", Operand::Expr(*op.src.clone())),
+ ],
+ Add(op) | Sub(op) | And(op) | Or(op) | Xor(op) | Lsl(op) | Lsr(op) | Asr(op)
+ | Rol(op) | Ror(op) | Mul(op) | MuluDp(op) | MulsDp(op) | Divu(op) | DivuDp(op)
+ | Divs(op) | DivsDp(op) | Modu(op) | ModuDp(op) | Mods(op) | ModsDp(op) | CmpE(op)
+ | CmpNe(op) | CmpSlt(op) | CmpUlt(op) | CmpSle(op) | CmpUle(op) | CmpSge(op)
+ | CmpUge(op) | CmpSgt(op) | CmpUgt(op) | TestBit(op) | AddOverflow(op) | FcmpE(op)
+ | FcmpNe(op) | FcmpLt(op) | FcmpLe(op) | FcmpGe(op) | FcmpGt(op) | FcmpO(op)
+ | FcmpUo(op) | Fadd(op) | Fsub(op) | Fmul(op) | Fdiv(op) => vec![
+ ("left", Operand::Expr(*op.left.clone())),
+ ("right", Operand::Expr(*op.right.clone())),
+ ],
+ Adc(op) | Sbb(op) | Rlc(op) | Rrc(op) => vec![
+ ("left", Operand::Expr(*op.left.clone())),
+ ("right", Operand::Expr(*op.right.clone())),
+ ("carry", Operand::Expr(*op.carry.clone())),
+ ],
+ Call(op) | Tailcall(op) => vec![
+ ("output", Operand::VarList(op.output.clone())),
+ ("dest", Operand::Expr(*op.dest.clone())),
+ ("params", Operand::ExprList(op.params.clone())),
+ ],
+ Syscall(op) => vec![
+ ("output", Operand::VarList(op.output.clone())),
+ ("params", Operand::ExprList(op.params.clone())),
+ ],
+ Intrinsic(op) => vec![
+ ("output", Operand::VarList(op.output.clone())),
+ ("intrinsic", Operand::Intrinsic(op.intrinsic)),
+ ("params", Operand::ExprList(op.params.clone())),
+ ],
+ IntrinsicSsa(op) => vec![
+ ("output", Operand::VarSsaList(op.output.clone())),
+ ("intrinsic", Operand::Intrinsic(op.intrinsic)),
+ ("params", Operand::ExprList(op.params.clone())),
+ ],
+ CallSsa(op) | TailcallSsa(op) => vec![
+ ("output", Operand::VarSsaList(op.output.clone())),
+ ("dest", Operand::Expr(*op.dest.clone())),
+ ("params", Operand::ExprList(op.params.clone())),
+ ("src_memory", Operand::Int(op.src_memory)),
+ ],
+ CallUntypedSsa(op) | TailcallUntypedSsa(op) => vec![
+ ("output", Operand::VarSsaList(op.output.clone())),
+ ("dest", Operand::Expr(*op.dest.clone())),
+ ("params", Operand::ExprList(op.params.clone())),
+ ("stack", Operand::Expr(*op.stack.clone())),
+ ],
+ SyscallSsa(op) => vec![
+ ("output", Operand::VarSsaList(op.output.clone())),
+ ("params", Operand::ExprList(op.params.clone())),
+ ("src_memory", Operand::Int(op.src_memory)),
+ ],
+ SyscallUntypedSsa(op) => vec![
+ ("output", Operand::VarSsaList(op.output.clone())),
+ ("params", Operand::ExprList(op.params.clone())),
+ ("stack", Operand::Expr(*op.stack.clone())),
+ ],
+ CallUntyped(op) | TailcallUntyped(op) => vec![
+ ("output", Operand::VarList(op.output.clone())),
+ ("dest", Operand::Expr(*op.dest.clone())),
+ ("params", Operand::ExprList(op.params.clone())),
+ ("stack", Operand::Expr(*op.stack.clone())),
+ ],
+ SyscallUntyped(op) => vec![
+ ("output", Operand::VarList(op.output.clone())),
+ ("params", Operand::ExprList(op.params.clone())),
+ ("stack", Operand::Expr(*op.stack.clone())),
+ ],
+ Neg(op) | Not(op) | Sx(op) | Zx(op) | LowPart(op) | BoolToInt(op) | UnimplMem(op)
+ | Fsqrt(op) | Fneg(op) | Fabs(op) | FloatToInt(op) | IntToFloat(op) | FloatConv(op)
+ | RoundToInt(op) | Floor(op) | Ceil(op) | Ftrunc(op) | Load(op) => {
+ vec![("src", Operand::Expr(*op.src.clone()))]
+ }
+ LoadStruct(op) => vec![
+ ("src", Operand::Expr(*op.src.clone())),
+ ("offset", Operand::Int(op.offset)),
+ ],
+ LoadStructSsa(op) => vec![
+ ("src", Operand::Expr(*op.src.clone())),
+ ("offset", Operand::Int(op.offset)),
+ ("src_memory", Operand::Int(op.src_memory)),
+ ],
+ LoadSsa(op) => vec![
+ ("src", Operand::Expr(*op.src.clone())),
+ ("src_memory", Operand::Int(op.src_memory)),
+ ],
+ Ret(op) => vec![("src", Operand::ExprList(op.src.clone()))],
+ SeparateParamList(op) => vec![("params", Operand::ExprList(op.params.clone()))],
+ SharedParamSlot(op) => vec![("params", Operand::ExprList(op.params.clone()))],
+ Var(op) | AddressOf(op) => vec![("src", Operand::Var(op.src))],
+ VarField(op) | AddressOfField(op) => vec![
+ ("src", Operand::Var(op.src)),
+ ("offset", Operand::Int(op.offset)),
+ ],
+ VarSsa(op) | VarAliased(op) => vec![("src", Operand::VarSsa(op.src))],
+ VarSsaField(op) | VarAliasedField(op) => vec![
+ ("src", Operand::VarSsa(op.src)),
+ ("offset", Operand::Int(op.offset)),
+ ],
+ Trap(op) => vec![("vector", Operand::Int(op.vector))],
+ }
+ }
+}
diff --git a/rust/src/medium_level_il/operation.rs b/rust/src/medium_level_il/operation.rs
new file mode 100644
index 00000000..e11f59c8
--- /dev/null
+++ b/rust/src/medium_level_il/operation.rs
@@ -0,0 +1,581 @@
+use super::{MediumLevelILLiftedInstruction, MediumLevelInstructionIndex};
+use crate::architecture::CoreIntrinsic;
+use crate::variable::{ConstantData, SSAVariable, Variable};
+use std::collections::BTreeMap;
+
+// IF
+#[derive(Debug, Copy, Clone)]
+pub struct MediumLevelILOperationIf {
+ pub condition: usize,
+ pub dest_true: MediumLevelInstructionIndex,
+ pub dest_false: MediumLevelInstructionIndex,
+}
+#[derive(Clone, Debug, PartialEq)]
+pub struct LiftedIf {
+ pub condition: Box<MediumLevelILLiftedInstruction>,
+ pub dest_true: MediumLevelInstructionIndex,
+ pub dest_false: MediumLevelInstructionIndex,
+}
+
+// FLOAT_CONST
+#[derive(Copy, Clone, Debug, PartialEq)]
+pub struct FloatConst {
+ pub constant: f64,
+}
+
+// CONST, CONST_PTR, IMPORT
+#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)]
+pub struct Constant {
+ pub constant: u64,
+}
+
+// EXTERN_PTR
+#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)]
+pub struct ExternPtr {
+ pub constant: u64,
+ pub offset: u64,
+}
+
+// CONST_DATA
+#[derive(Debug, Copy, Clone)]
+pub struct ConstData {
+ pub constant_data_kind: u32,
+ pub constant_data_value: i64,
+ pub size: usize,
+}
+#[derive(Clone, Debug, Hash, PartialEq)]
+pub struct LiftedConstData {
+ pub constant_data: ConstantData,
+}
+
+// JUMP, RET_HINT
+#[derive(Debug, Copy, Clone)]
+pub struct Jump {
+ pub dest: usize,
+}
+#[derive(Clone, Debug, PartialEq)]
+pub struct LiftedJump {
+ pub dest: Box<MediumLevelILLiftedInstruction>,
+}
+
+// STORE_SSA
+#[derive(Debug, Copy, Clone)]
+pub struct StoreSsa {
+ pub dest: usize,
+ pub dest_memory: u64,
+ pub src_memory: u64,
+ pub src: usize,
+}
+#[derive(Clone, Debug, PartialEq)]
+pub struct LiftedStoreSsa {
+ pub dest: Box<MediumLevelILLiftedInstruction>,
+ pub dest_memory: u64,
+ pub src_memory: u64,
+ pub src: Box<MediumLevelILLiftedInstruction>,
+}
+
+// STORE_STRUCT_SSA
+#[derive(Debug, Copy, Clone)]
+pub struct StoreStructSsa {
+ pub dest: usize,
+ pub offset: u64,
+ pub dest_memory: u64,
+ pub src_memory: u64,
+ pub src: usize,
+}
+#[derive(Clone, Debug, PartialEq)]
+pub struct LiftedStoreStructSsa {
+ pub dest: Box<MediumLevelILLiftedInstruction>,
+ pub offset: u64,
+ pub dest_memory: u64,
+ pub src_memory: u64,
+ pub src: Box<MediumLevelILLiftedInstruction>,
+}
+
+// STORE_STRUCT
+#[derive(Debug, Copy, Clone)]
+pub struct StoreStruct {
+ pub dest: usize,
+ pub offset: u64,
+ pub src: usize,
+}
+#[derive(Clone, Debug, PartialEq)]
+pub struct LiftedStoreStruct {
+ pub dest: Box<MediumLevelILLiftedInstruction>,
+ pub offset: u64,
+ pub src: Box<MediumLevelILLiftedInstruction>,
+}
+
+// STORE
+#[derive(Debug, Copy, Clone)]
+pub struct Store {
+ pub dest: usize,
+ pub src: usize,
+}
+#[derive(Clone, Debug, PartialEq)]
+pub struct LiftedStore {
+ pub dest: Box<MediumLevelILLiftedInstruction>,
+ pub src: Box<MediumLevelILLiftedInstruction>,
+}
+
+// JUMP_TO
+#[derive(Debug, Copy, Clone)]
+pub struct JumpTo {
+ pub dest: usize,
+ pub first_operand: usize,
+ pub num_operands: usize,
+}
+#[derive(Clone, Debug, PartialEq)]
+pub struct LiftedJumpTo {
+ pub dest: Box<MediumLevelILLiftedInstruction>,
+ pub targets: BTreeMap<u64, MediumLevelInstructionIndex>,
+}
+
+// GOTO
+#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)]
+pub struct Goto {
+ pub dest: MediumLevelInstructionIndex,
+}
+
+// FREE_VAR_SLOT
+#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)]
+pub struct FreeVarSlot {
+ pub dest: Variable,
+}
+
+// SET_VAR_FIELD
+#[derive(Debug, Copy, Clone)]
+pub struct SetVarField {
+ pub dest: Variable,
+ pub offset: u64,
+ pub src: usize,
+}
+#[derive(Clone, Debug, PartialEq)]
+pub struct LiftedSetVarField {
+ pub dest: Variable,
+ pub offset: u64,
+ pub src: Box<MediumLevelILLiftedInstruction>,
+}
+
+// SET_VAR
+#[derive(Debug, Copy, Clone)]
+pub struct SetVar {
+ pub dest: Variable,
+ // TODO: Expression?
+ pub src: usize,
+}
+#[derive(Clone, Debug, PartialEq)]
+pub struct LiftedSetVar {
+ pub dest: Variable,
+ pub src: Box<MediumLevelILLiftedInstruction>,
+}
+
+// FREE_VAR_SLOT_SSA
+#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)]
+pub struct FreeVarSlotSsa {
+ pub dest: SSAVariable,
+ pub prev: SSAVariable,
+}
+
+// SET_VAR_SSA_FIELD, SET_VAR_ALIASED_FIELD
+#[derive(Debug, Copy, Clone)]
+pub struct SetVarSsaField {
+ pub dest: SSAVariable,
+ pub prev: SSAVariable,
+ pub offset: u64,
+ pub src: usize,
+}
+#[derive(Clone, Debug, PartialEq)]
+pub struct LiftedSetVarSsaField {
+ pub dest: SSAVariable,
+ pub prev: SSAVariable,
+ pub offset: u64,
+ pub src: Box<MediumLevelILLiftedInstruction>,
+}
+
+// SET_VAR_ALIASED
+#[derive(Debug, Copy, Clone)]
+pub struct SetVarAliased {
+ pub dest: SSAVariable,
+ pub prev: SSAVariable,
+ pub src: usize,
+}
+#[derive(Clone, Debug, PartialEq)]
+pub struct LiftedSetVarAliased {
+ pub dest: SSAVariable,
+ pub prev: SSAVariable,
+ pub src: Box<MediumLevelILLiftedInstruction>,
+}
+
+// SET_VAR_SSA
+#[derive(Debug, Copy, Clone)]
+pub struct SetVarSsa {
+ pub dest: SSAVariable,
+ pub src: usize,
+}
+#[derive(Clone, Debug, PartialEq)]
+pub struct LiftedSetVarSsa {
+ pub dest: SSAVariable,
+ pub src: Box<MediumLevelILLiftedInstruction>,
+}
+
+// VAR_PHI
+#[derive(Debug, Copy, Clone)]
+pub struct VarPhi {
+ pub dest: SSAVariable,
+ pub first_operand: usize,
+ pub num_operands: usize,
+}
+#[derive(Clone, Debug, Hash, PartialEq, Eq)]
+pub struct LiftedVarPhi {
+ pub dest: SSAVariable,
+ pub src: Vec<SSAVariable>,
+}
+
+// MEM_PHI
+#[derive(Debug, Copy, Clone)]
+pub struct MemPhi {
+ pub dest_memory: u64,
+ pub first_operand: usize,
+ pub num_operands: usize,
+}
+#[derive(Clone, Debug, Hash, PartialEq, Eq)]
+pub struct LiftedMemPhi {
+ pub dest_memory: u64,
+ pub src_memory: Vec<u64>,
+}
+
+// VAR_SPLIT
+#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)]
+pub struct VarSplit {
+ pub high: Variable,
+ pub low: Variable,
+}
+
+// SET_VAR_SPLIT
+#[derive(Debug, Copy, Clone)]
+pub struct SetVarSplit {
+ pub high: Variable,
+ pub low: Variable,
+ pub src: usize,
+}
+#[derive(Clone, Debug, PartialEq)]
+pub struct LiftedSetVarSplit {
+ pub high: Variable,
+ pub low: Variable,
+ pub src: Box<MediumLevelILLiftedInstruction>,
+}
+
+// VAR_SPLIT_SSA
+#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)]
+pub struct VarSplitSsa {
+ pub high: SSAVariable,
+ pub low: SSAVariable,
+}
+
+// SET_VAR_SPLIT_SSA
+#[derive(Debug, Copy, Clone)]
+pub struct SetVarSplitSsa {
+ pub high: SSAVariable,
+ pub low: SSAVariable,
+ pub src: usize,
+}
+#[derive(Clone, Debug, PartialEq)]
+pub struct LiftedSetVarSplitSsa {
+ pub high: SSAVariable,
+ pub low: SSAVariable,
+ pub src: Box<MediumLevelILLiftedInstruction>,
+}
+
+// ADD, SUB, AND, OR, XOR, LSL, LSR, ASR, ROL, ROR, MUL, MULU_DP, MULS_DP, DIVU, DIVU_DP, DIVS, DIVS_DP, MODU, MODU_DP, MODS, MODS_DP, CMP_E, CMP_NE, CMP_SLT, CMP_ULT, CMP_SLE, CMP_ULE, CMP_SGE, CMP_UGE, CMP_SGT, CMP_UGT, TEST_BIT, ADD_OVERFLOW, FCMP_E, FCMP_NE, FCMP_LT, FCMP_LE, FCMP_GE, FCMP_GT, FCMP_O, FCMP_UO, FADD, FSUB, FMUL, FDIV
+#[derive(Debug, Copy, Clone)]
+pub struct BinaryOp {
+ pub left: usize,
+ pub right: usize,
+}
+#[derive(Clone, Debug, PartialEq)]
+pub struct LiftedBinaryOp {
+ pub left: Box<MediumLevelILLiftedInstruction>,
+ pub right: Box<MediumLevelILLiftedInstruction>,
+}
+
+// ADC, SBB, RLC, RRC
+#[derive(Debug, Copy, Clone)]
+pub struct BinaryOpCarry {
+ pub left: usize,
+ pub right: usize,
+ pub carry: usize,
+}
+#[derive(Clone, Debug, PartialEq)]
+pub struct LiftedBinaryOpCarry {
+ pub left: Box<MediumLevelILLiftedInstruction>,
+ pub right: Box<MediumLevelILLiftedInstruction>,
+ pub carry: Box<MediumLevelILLiftedInstruction>,
+}
+
+// CALL, TAILCALL
+#[derive(Debug, Copy, Clone)]
+pub struct Call {
+ pub first_output: usize,
+ pub num_outputs: usize,
+ pub dest: usize,
+ pub first_param: usize,
+ pub num_params: usize,
+}
+#[derive(Clone, Debug, PartialEq)]
+pub struct LiftedCall {
+ pub output: Vec<Variable>,
+ pub dest: Box<MediumLevelILLiftedInstruction>,
+ pub params: Vec<MediumLevelILLiftedInstruction>,
+}
+
+// SYSCALL
+#[derive(Debug, Copy, Clone)]
+pub struct Syscall {
+ pub first_output: usize,
+ pub num_outputs: usize,
+ pub first_param: usize,
+ pub num_params: usize,
+}
+#[derive(Clone, Debug, PartialEq)]
+pub struct LiftedSyscallCall {
+ pub output: Vec<Variable>,
+ pub params: Vec<MediumLevelILLiftedInstruction>,
+}
+
+// INTRINSIC
+#[derive(Debug, Copy, Clone)]
+pub struct Intrinsic {
+ pub first_output: usize,
+ pub num_outputs: usize,
+ pub intrinsic: u32,
+ pub first_param: usize,
+ pub num_params: usize,
+}
+#[derive(Clone, Debug, PartialEq)]
+pub struct LiftedIntrinsic {
+ pub output: Vec<Variable>,
+ pub intrinsic: CoreIntrinsic,
+ pub params: Vec<MediumLevelILLiftedInstruction>,
+}
+
+// INTRINSIC_SSA
+#[derive(Debug, Copy, Clone)]
+pub struct IntrinsicSsa {
+ pub first_output: usize,
+ pub num_outputs: usize,
+ pub intrinsic: u32,
+ pub first_param: usize,
+ pub num_params: usize,
+}
+#[derive(Clone, Debug, PartialEq)]
+pub struct LiftedIntrinsicSsa {
+ pub output: Vec<SSAVariable>,
+ pub intrinsic: CoreIntrinsic,
+ pub params: Vec<MediumLevelILLiftedInstruction>,
+}
+
+// CALL_SSA, TAILCALL_SSA
+#[derive(Debug, Copy, Clone)]
+pub struct CallSsa {
+ pub output: usize,
+ pub dest: usize,
+ pub first_param: usize,
+ pub num_params: usize,
+ pub src_memory: u64,
+}
+#[derive(Clone, Debug, PartialEq)]
+pub struct LiftedCallSsa {
+ pub output: Vec<SSAVariable>,
+ pub dest: Box<MediumLevelILLiftedInstruction>,
+ pub params: Vec<MediumLevelILLiftedInstruction>,
+ pub src_memory: u64,
+}
+
+// CALL_UNTYPED_SSA, TAILCALL_UNTYPED_SSA
+#[derive(Debug, Copy, Clone)]
+pub struct CallUntypedSsa {
+ pub output: usize,
+ pub dest: usize,
+ pub params: usize,
+ pub stack: usize,
+}
+#[derive(Clone, Debug, PartialEq)]
+pub struct LiftedCallUntypedSsa {
+ pub output: Vec<SSAVariable>,
+ pub dest: Box<MediumLevelILLiftedInstruction>,
+ pub params: Vec<MediumLevelILLiftedInstruction>,
+ pub stack: Box<MediumLevelILLiftedInstruction>,
+}
+
+// SYSCALL_SSA
+#[derive(Debug, Copy, Clone)]
+pub struct SyscallSsa {
+ pub output: usize,
+ pub first_param: usize,
+ pub num_params: usize,
+ pub src_memory: u64,
+}
+#[derive(Clone, Debug, PartialEq)]
+pub struct LiftedSyscallSsa {
+ pub output: Vec<SSAVariable>,
+ pub params: Vec<MediumLevelILLiftedInstruction>,
+ pub src_memory: u64,
+}
+
+// SYSCALL_UNTYPED_SSA
+#[derive(Debug, Copy, Clone)]
+pub struct SyscallUntypedSsa {
+ pub output: usize,
+ pub params: usize,
+ pub stack: usize,
+}
+#[derive(Clone, Debug, PartialEq)]
+pub struct LiftedSyscallUntypedSsa {
+ pub output: Vec<SSAVariable>,
+ pub params: Vec<MediumLevelILLiftedInstruction>,
+ pub stack: Box<MediumLevelILLiftedInstruction>,
+}
+
+// CALL_UNTYPED, TAILCALL_UNTYPED
+#[derive(Debug, Copy, Clone)]
+pub struct CallUntyped {
+ pub output: usize,
+ pub dest: usize,
+ pub params: usize,
+ pub stack: usize,
+}
+#[derive(Clone, Debug, PartialEq)]
+pub struct LiftedCallUntyped {
+ pub output: Vec<Variable>,
+ pub dest: Box<MediumLevelILLiftedInstruction>,
+ pub params: Vec<MediumLevelILLiftedInstruction>,
+ pub stack: Box<MediumLevelILLiftedInstruction>,
+}
+
+// SYSCALL_UNTYPED
+#[derive(Debug, Copy, Clone)]
+pub struct SyscallUntyped {
+ pub output: usize,
+ pub params: usize,
+ pub stack: usize,
+}
+#[derive(Clone, Debug, PartialEq)]
+pub struct LiftedSyscallUntyped {
+ pub output: Vec<Variable>,
+ pub params: Vec<MediumLevelILLiftedInstruction>,
+ pub stack: Box<MediumLevelILLiftedInstruction>,
+}
+
+// NEG, NOT, SX, ZX, LOW_PART, BOOL_TO_INT, UNIMPL_MEM, FSQRT, FNEG, FABS, FLOAT_TO_INT, INT_TO_FLOAT, FLOAT_CONV, ROUND_TO_INT, FLOOR, CEIL, FTRUNC, LOAD
+#[derive(Debug, Copy, Clone)]
+pub struct UnaryOp {
+ pub src: usize,
+}
+#[derive(Clone, Debug, PartialEq)]
+pub struct LiftedUnaryOp {
+ pub src: Box<MediumLevelILLiftedInstruction>,
+}
+
+// LOAD_STRUCT
+#[derive(Debug, Copy, Clone)]
+pub struct LoadStruct {
+ pub src: usize,
+ pub offset: u64,
+}
+#[derive(Clone, Debug, PartialEq)]
+pub struct LiftedLoadStruct {
+ pub src: Box<MediumLevelILLiftedInstruction>,
+ pub offset: u64,
+}
+
+// LOAD_STRUCT_SSA
+#[derive(Debug, Copy, Clone)]
+pub struct LoadStructSsa {
+ pub src: usize,
+ pub offset: u64,
+ pub src_memory: u64,
+}
+#[derive(Clone, Debug, PartialEq)]
+pub struct LiftedLoadStructSsa {
+ pub src: Box<MediumLevelILLiftedInstruction>,
+ pub offset: u64,
+ pub src_memory: u64,
+}
+
+// LOAD_SSA
+#[derive(Debug, Copy, Clone)]
+pub struct LoadSsa {
+ pub src: usize,
+ pub src_memory: u64,
+}
+#[derive(Clone, Debug, PartialEq)]
+pub struct LiftedLoadSsa {
+ pub src: Box<MediumLevelILLiftedInstruction>,
+ pub src_memory: u64,
+}
+
+// RET
+#[derive(Debug, Copy, Clone)]
+pub struct Ret {
+ pub first_operand: usize,
+ pub num_operands: usize,
+}
+#[derive(Clone, Debug, PartialEq)]
+pub struct LiftedRet {
+ pub src: Vec<MediumLevelILLiftedInstruction>,
+}
+
+// SEPARATE_PARAM_LIST
+#[derive(Debug, Copy, Clone)]
+pub struct SeparateParamList {
+ pub first_param: usize,
+ pub num_params: usize,
+}
+#[derive(Clone, Debug, PartialEq)]
+pub struct LiftedSeparateParamList {
+ pub params: Vec<MediumLevelILLiftedInstruction>,
+}
+
+// SHARED_PARAM_SLOT
+#[derive(Debug, Copy, Clone)]
+pub struct SharedParamSlot {
+ pub first_param: usize,
+ pub num_params: usize,
+}
+#[derive(Clone, Debug, PartialEq)]
+pub struct LiftedSharedParamSlot {
+ pub params: Vec<MediumLevelILLiftedInstruction>,
+}
+
+// VAR, ADDRESS_OF
+#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)]
+pub struct Var {
+ pub src: Variable,
+}
+
+// VAR_FIELD, ADDRESS_OF_FIELD
+#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)]
+pub struct Field {
+ pub src: Variable,
+ pub offset: u64,
+}
+
+// VAR_SSA, VAR_ALIASED
+#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)]
+pub struct VarSsa {
+ pub src: SSAVariable,
+}
+
+// VAR_SSA_FIELD, VAR_ALIASED_FIELD
+#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)]
+pub struct VarSsaField {
+ pub src: SSAVariable,
+ pub offset: u64,
+}
+
+// TRAP
+#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)]
+pub struct Trap {
+ pub vector: u64,
+}