summaryrefslogtreecommitdiff
path: root/rust/src/medium_level_il/function.rs
diff options
context:
space:
mode:
Diffstat (limited to 'rust/src/medium_level_il/function.rs')
-rw-r--r--rust/src/medium_level_il/function.rs718
1 files changed, 718 insertions, 0 deletions
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()
+ }
+}