summaryrefslogtreecommitdiff
path: root/rust/src
diff options
context:
space:
mode:
authorRubens Brandao <git@rubens.io>2024-05-07 09:52:19 -0300
committerKyle Martin <krm504@nyu.edu>2024-05-20 15:43:59 -0400
commit27bc08fa90d4381b4fd5d896dda991824504b0fd (patch)
tree842d477d932439765e94783f65f46dc66afd661c /rust/src
parent82dd5d4b6fb97f4754d80aefe514d74b61c79054 (diff)
Implement python Function methods to rust
Squashed: add Function::add_tag method add Function::{add_user_code_ref,remove_user_code_ref} methods add Function::{add_user_type_field_ref, remove_user_type_field_ref} methods add Function::{add_user_type_ref, remove_user_type_ref} methods add Function::apply_auto_discovered_type add Function/MediumLevelILFunction user_var_values and related functions simplify UserVariableValues::values_from_variable cargo fmt fix fix doc and QualifiedName params fix MediumLevelILFunction::is_var_user_defined parameter Remove unecessary Safety comment add Function::{create_auto_stack_var,delete_auto_stack_var} methods add MediumLevelILFunction::create_auto_var method move Function::{create_auto_stack_var,delete_auto_stack_var} methods to MediumLevelILFunction add MediumLevelILFunction::{create_user_stack_var,delete_user_stack_var} methods add Function::block_annotations method add Function::{call_stack_adjustment, call_type_adjustment} method add Function::{constant_data, constants_referenced_by, constants_referenced_by_address_if_available} methods add Function::function_tags method add Function::{indirect_branches, indirect_branches_at} methods add Function::instr_highlight method add Function::instruction_containing_address method add Function::{int_display_type,int_enum_display_typeid,int_display_type_and_typeid} methods add MediumLevelILFunction::{var_refs, var_refs_from} methods add Function::{parameter_at, parameter_at_low_level_il_instruction} methods rebase GAT changes add Function::{reg_value_at, reg_value_after, reg_value_at_exit} methods Add alpha to HighlightColor::NoHighlightColor add Function::{regs_read_by, regs_written_by} methods add Function::{stack_contents_at, stack_contents_after} methods add Function::{stack_vars_referenced_by, stack_vars_referenced_by_address_if_available} methods add Function::{tags_at, tags_at_range} methods add Function::type_tokens method add Function::variables method add Function::{is_call_instruction, is_var_user_defined} methods add Function::{mark_updates_required, mark_caller_updates_required, mark_recent_use} methods add Function::{merge_vars, unmerge_vars, split_var, unsplit_var} methods add Function::reanalyze method add Function::request_debug_report method add Function::remove_tag method add Function::remove_tags_of_type method add Function::analysis_performance_info method add Function::call_sites method add Function::caller_sites method add Function::{calling_convention, set_calling_convention} methods add Function::{can_return, set_can_return} methods add Function::comments method add Function::{clobbered_regs, set_clobbered_regs} methods add Function::{has_explicitly_defined_type, has_user_annotations, has_user_type, has_variable_arguments} methods fix Function::set_can_return_user function add Function::{high_level_il_if_available, medium_level_il_if_available, low_level_il_if_available, lifted_il_if_available} methods add Function::{global_pointer_value, has_unresolved_indirect_branches} methods add Function::lowest_address method add Function::{inline_during_analysis, set_auto_inline_during_analysis, set_user_inline_during_analysis} methods add Function::{is_pure, is_too_large, is_update_needed} methods add Function::{provenance, stack_adjustment, set_user_stack_adjustment, set_auto_stack_adjustment} methods add Function::{set_user_pure, set_auto_pure} methods add Function::set_auto_type method add Function::{return_regs, set_return_regs} methods add Function::{set_auto_return_type, set_user_return_type} methods add Function::unresolved_stack_adjustment_graph method add Function::create_graph method add Function::tags method add Function::split_variables method add Function::{reg_stack_adjustments, set_reg_stack_adjustments} methods add Function::{mapped_medium_level_il, merged_variables} methods add Function::{set_auto_instr_highlight, set_user_instr_highlight} methods add stack_adjustments methods add Function::set_user_call_stack_adjustment add Function::set_auto_call_stack_adjustment add Function::call_type_adjustment add Function::set_user_call_type_adjustment add Function::set_auto_call_type_adjustment add Function::call_reg_stack_adjustment add Function::set_user_call_reg_stack_adjustment add Function::set_auto_call_reg_stack_adjustment add Function::call_reg_stack_adjustment_for_reg_stack add Function::set_user_call_reg_stack_adjustment_for_reg_stack add Function::set_auto_call_reg_stack_adjustment_for_reg_stack add Function::set_user_reg_stack_adjustments add Function::set_auto_reg_stack_adjustments add Function::set_auto_reg_stack_adjustments add Function::set_auto_calling_convention method add Function::set_auto_can_return method add Function::{set_user_has_variable_arguments, set_auto_has_variable_arguments} methods add Function::{set_user_indirect_branches, set_auto_indirect_branches} methods add Function::set_int_display_type method add Function::{set_user_parameter_variables, set_auto_parameter_variables} methods fix documentation add Function::unresolved_indirect_branches method add Function::mapped_medium_level_il_if_available method change methods `Function::{set_user_type, set_auto_type}` to receive `&Type` merge RegisterList and RegisterSet into CoreRegister impl fix function names fix function names fix documentation syntax fix documentation syntax fix Function documentation by adding MediumLevelILInstruction::get_split_var_for_definition fix documentation links rename `VariableDefinitions` to `MediumLevelILInstructionList` fix `PossibleValueSet` switched `NotInSetOfValues` and `ReturnAddressValue`
Diffstat (limited to 'rust/src')
-rw-r--r--rust/src/architecture.rs20
-rw-r--r--rust/src/disassembly.rs44
-rw-r--r--rust/src/function.rs1854
-rw-r--r--rust/src/hlil/instruction.rs3
-rw-r--r--rust/src/mlil/function.rs411
-rw-r--r--rust/src/mlil/instruction.rs20
-rw-r--r--rust/src/operand_iter.rs3
-rw-r--r--rust/src/tags.rs74
-rw-r--r--rust/src/types.rs872
9 files changed, 3261 insertions, 40 deletions
diff --git a/rust/src/architecture.rs b/rust/src/architecture.rs
index 713e8bf1..6831844e 100644
--- a/rust/src/architecture.rs
+++ b/rust/src/architecture.rs
@@ -715,6 +715,21 @@ impl Register for CoreRegister {
}
}
+impl CoreArrayProvider for CoreRegister {
+ type Raw = u32;
+ type Context = CoreArchitecture;
+ type Wrapped<'a> = Self;
+}
+
+unsafe impl CoreArrayProviderInner for CoreRegister {
+ unsafe fn free(raw: *mut Self::Raw, _count: usize, _context: &Self::Context) {
+ BNFreeRegisterList(raw)
+ }
+ unsafe fn wrap_raw<'a>(raw: &'a Self::Raw, context: &'a Self::Context) -> Self::Wrapped<'a> {
+ Self(context.0, *raw)
+ }
+}
+
pub struct CoreRegisterStackInfo(*mut BNArchitecture, BNRegisterStackInfo);
impl RegisterStackInfo for CoreRegisterStackInfo {
@@ -2419,7 +2434,10 @@ where
};
let inputs = intrinsic.inputs();
- let mut res: Box<[_]> = inputs.into_iter().map(|input| unsafe { Ref::into_raw(input) }.0).collect();
+ let mut res: Box<[_]> = inputs
+ .into_iter()
+ .map(|input| unsafe { Ref::into_raw(input) }.0)
+ .collect();
unsafe {
*count = res.len();
diff --git a/rust/src/disassembly.rs b/rust/src/disassembly.rs
index 855807b1..1fd51cee 100644
--- a/rust/src/disassembly.rs
+++ b/rust/src/disassembly.rs
@@ -265,6 +265,35 @@ impl Drop for InstructionTextToken {
}
}
+impl CoreArrayProvider for InstructionTextToken {
+ type Raw = BNInstructionTextToken;
+ type Context = ();
+ type Wrapped<'a> = Self;
+}
+unsafe impl CoreArrayProviderInner for InstructionTextToken {
+ unsafe fn free(raw: *mut Self::Raw, count: usize, _context: &Self::Context) {
+ BNFreeInstructionText(raw, count)
+ }
+ unsafe fn wrap_raw<'a>(raw: &'a Self::Raw, _context: &'a Self::Context) -> Self::Wrapped<'a> {
+ Self(*raw)
+ }
+}
+
+impl CoreArrayProvider for Array<InstructionTextToken> {
+ type Raw = BNInstructionTextLine;
+ type Context = ();
+ type Wrapped<'a> = Self;
+}
+unsafe impl CoreArrayProviderInner for Array<InstructionTextToken> {
+ unsafe fn free(raw: *mut Self::Raw, count: usize, _context: &Self::Context) {
+ BNFreeInstructionTextLines(raw, count)
+ }
+ unsafe fn wrap_raw<'a>(raw: &'a Self::Raw, _context: &'a Self::Context) -> Self::Wrapped<'a> {
+ Self::new(raw.tokens, raw.count, ())
+ }
+}
+
+#[repr(transparent)]
pub struct DisassemblyTextLine(pub(crate) BNDisassemblyTextLine);
impl DisassemblyTextLine {
@@ -422,6 +451,21 @@ impl Drop for DisassemblyTextLine {
}
}
+impl CoreArrayProvider for DisassemblyTextLine {
+ type Raw = BNDisassemblyTextLine;
+ type Context = ();
+ type Wrapped<'a> = &'a Self;
+}
+
+unsafe impl CoreArrayProviderInner for DisassemblyTextLine {
+ unsafe fn free(raw: *mut Self::Raw, count: usize, _context: &Self::Context) {
+ BNFreeDisassemblyTextLines(raw, count)
+ }
+ unsafe fn wrap_raw<'a>(raw: &'a Self::Raw, _context: &'a Self::Context) -> Self::Wrapped<'a> {
+ core::mem::transmute(raw)
+ }
+}
+
pub type DisassemblyOption = BNDisassemblyOption;
#[derive(PartialEq, Eq, Hash)]
diff --git a/rust/src/function.rs b/rust/src/function.rs
index 04be898f..78b7da9f 100644
--- a/rust/src/function.rs
+++ b/rust/src/function.rs
@@ -14,24 +14,33 @@
use binaryninjacore_sys::*;
-use crate::rc::*;
-use crate::string::*;
-use crate::types::Variable;
use crate::{
- architecture::CoreArchitecture,
+ architecture::{Architecture, CoreArchitecture, CoreRegister, Register},
basicblock::{BasicBlock, BlockContext},
binaryview::{BinaryView, BinaryViewExt},
- hlil, llil, mlil,
+ callingconvention::CallingConvention,
+ disassembly::{DisassemblySettings, DisassemblyTextLine},
+ flowgraph::FlowGraph,
+ hlil, llil,
+ mlil::{self, FunctionGraphType},
platform::Platform,
+ references::CodeReference,
+ string::*,
symbol::Symbol,
- types::{Conf, NamedTypedVariable, Type},
+ tags::{Tag, TagReference, TagType},
+ types::{
+ Conf, ConstantReference, HighlightColor, IndirectBranchInfo, IntegerDisplayType,
+ MergedVariable, NamedTypedVariable, QualifiedName, RegisterStackAdjustment, RegisterValue,
+ RegisterValueType, StackVariableReference, Type, UnresolvedIndirectBranches, Variable,
+ },
};
+use crate::{databuffer::DataBuffer, disassembly::InstructionTextToken, rc::*};
pub use binaryninjacore_sys::BNAnalysisSkipReason as AnalysisSkipReason;
pub use binaryninjacore_sys::BNFunctionAnalysisSkipOverride as FunctionAnalysisSkipOverride;
pub use binaryninjacore_sys::BNFunctionUpdateType as FunctionUpdateType;
-use std::hash::Hash;
use std::{fmt, mem};
+use std::{hash::Hash, ops::Range};
pub struct Location {
pub arch: Option<CoreArchitecture>,
@@ -157,6 +166,10 @@ impl Function {
unsafe { BNGetFunctionStart(self.handle) }
}
+ pub fn lowest_address(&self) -> u64 {
+ unsafe { BNGetFunctionLowestAddress(self.handle) }
+ }
+
pub fn highest_address(&self) -> u64 {
unsafe { BNGetFunctionHighestAddress(self.handle) }
}
@@ -189,7 +202,7 @@ impl Function {
pub fn set_can_return_user<T: Into<Conf<bool>>>(&self, can_return: T) {
let mut bool_with_confidence = can_return.into().into();
- unsafe { BNSetAutoFunctionCanReturn(self.handle, &mut bool_with_confidence) }
+ unsafe { BNSetUserFunctionCanReturn(self.handle, &mut bool_with_confidence) }
}
pub fn comment_at(&self, addr: u64) -> BnString {
@@ -204,6 +217,13 @@ impl Function {
}
}
+ /// All comments in the function
+ pub fn comments(&self) -> Array<Comments> {
+ let mut count = 0;
+ let lines = unsafe { BNGetCommentedAddresses(self.handle, &mut count) };
+ unsafe { Array::new(lines, count, self.to_owned()) }
+ }
+
pub fn basic_blocks(&self) -> Array<BasicBlock<NativeBlock>> {
unsafe {
let mut count = 0;
@@ -214,11 +234,23 @@ impl Function {
}
}
+ /// Returns the BasicBlock that contains the given address `addr`.
+ ///
+ /// * `addr` - Address of the BasicBlock to retrieve.
+ /// * `arch` - Architecture of the basic block if different from the Function's self.arch
+ ///
+ /// # Example
+ /// ```no_run
+ /// # use binaryninja::function::Function;
+ /// # let fun: Function = todo!();
+ /// let blocks = fun.basic_block_containing(0x1000, None);
+ /// ```
pub fn basic_block_containing(
&self,
- arch: &CoreArchitecture,
addr: u64,
+ arch: Option<CoreArchitecture>,
) -> Option<Ref<BasicBlock<NativeBlock>>> {
+ let arch = arch.unwrap_or_else(|| self.arch());
unsafe {
let block = BNGetFunctionBasicBlockAtAddress(self.handle, arch.0, addr);
let context = NativeBlock { _priv: () };
@@ -231,6 +263,18 @@ impl Function {
}
}
+ pub fn block_annotations(
+ &self,
+ addr: u64,
+ arch: Option<CoreArchitecture>,
+ ) -> Array<Array<InstructionTextToken>> {
+ let arch = arch.unwrap_or_else(|| self.arch());
+ let mut count = 0;
+ let lines = unsafe { BNGetFunctionBlockAnnotations(self.handle, arch.0, addr, &mut count) };
+ assert!(!lines.is_null());
+ unsafe { Array::new(lines, count, ()) }
+ }
+
pub fn get_variable_name(&self, var: &Variable) -> BnString {
unsafe {
let raw_var = var.raw();
@@ -251,6 +295,30 @@ impl Function {
}
}
+ pub fn high_level_il_if_available(&self) -> Option<Ref<hlil::HighLevelILFunction>> {
+ let hlil = unsafe { BNGetFunctionHighLevelILIfAvailable(self.handle) };
+ (!hlil.is_null()).then(|| unsafe { hlil::HighLevelILFunction::ref_from_raw(hlil, true) })
+ }
+
+ /// MediumLevelILFunction used to represent Function mapped medium level IL
+ pub fn mapped_medium_level_il(&self) -> Result<Ref<mlil::MediumLevelILFunction>, ()> {
+ let mlil = unsafe { BNGetFunctionMappedMediumLevelIL(self.handle) };
+ if mlil.is_null() {
+ return Err(());
+ }
+ Ok(unsafe { mlil::MediumLevelILFunction::ref_from_raw(mlil) })
+ }
+
+ pub fn mapped_medium_level_il_if_available(
+ &self,
+ ) -> Result<Ref<mlil::MediumLevelILFunction>, ()> {
+ let mlil = unsafe { BNGetFunctionMappedMediumLevelILIfAvailable(self.handle) };
+ if mlil.is_null() {
+ return Err(());
+ }
+ Ok(unsafe { mlil::MediumLevelILFunction::ref_from_raw(mlil) })
+ }
+
pub fn medium_level_il(&self) -> Result<Ref<mlil::MediumLevelILFunction>, ()> {
unsafe {
let mlil = BNGetFunctionMediumLevelIL(self.handle);
@@ -263,6 +331,11 @@ impl Function {
}
}
+ pub fn medium_level_il_if_available(&self) -> Option<Ref<mlil::MediumLevelILFunction>> {
+ let mlil = unsafe { BNGetFunctionMediumLevelILIfAvailable(self.handle) };
+ (!mlil.is_null()).then(|| unsafe { mlil::MediumLevelILFunction::ref_from_raw(mlil) })
+ }
+
pub fn low_level_il(&self) -> Result<Ref<llil::RegularFunction<CoreArchitecture>>, ()> {
unsafe {
let llil = BNGetFunctionLowLevelIL(self.handle);
@@ -275,6 +348,13 @@ impl Function {
}
}
+ pub fn low_level_il_if_available(
+ &self,
+ ) -> Option<Ref<llil::RegularFunction<CoreArchitecture>>> {
+ let llil = unsafe { BNGetFunctionLowLevelILIfAvailable(self.handle) };
+ (!llil.is_null()).then(|| unsafe { llil::RegularFunction::from_raw(self.arch(), llil) })
+ }
+
pub fn lifted_il(&self) -> Result<Ref<llil::LiftedFunction<CoreArchitecture>>, ()> {
unsafe {
let llil = BNGetFunctionLiftedIL(self.handle);
@@ -287,6 +367,11 @@ impl Function {
}
}
+ pub fn lifted_il_if_available(&self) -> Option<Ref<llil::LiftedFunction<CoreArchitecture>>> {
+ let llil = unsafe { BNGetFunctionLiftedILIfAvailable(self.handle) };
+ (!llil.is_null()).then(|| unsafe { llil::LiftedFunction::from_raw(self.arch(), llil) })
+ }
+
pub fn return_type(&self) -> Conf<Ref<Type>> {
let result = unsafe { BNGetFunctionReturnType(self.handle) };
@@ -296,16 +381,54 @@ impl Function {
)
}
- pub fn function_type(&self) -> Ref<Type> {
- unsafe { Type::ref_from_raw(BNGetFunctionType(self.handle)) }
+ pub fn set_auto_return_type<'a, C>(&self, return_type: C)
+ where
+ C: Into<Conf<&'a Type>>,
+ {
+ let return_type: Conf<&Type> = return_type.into();
+ unsafe {
+ BNSetAutoFunctionReturnType(
+ self.handle,
+ &mut BNTypeWithConfidence {
+ type_: return_type.contents.handle,
+ confidence: return_type.confidence,
+ },
+ )
+ }
}
- pub fn set_user_type(&self, t: Type) {
+ pub fn set_user_return_type<'a, C>(&self, return_type: C)
+ where
+ C: Into<Conf<&'a Type>>,
+ {
+ let return_type: Conf<&Type> = return_type.into();
unsafe {
- BNSetFunctionUserType(self.handle, t.handle);
+ BNSetUserFunctionReturnType(
+ self.handle,
+ &mut BNTypeWithConfidence {
+ type_: return_type.contents.handle,
+ confidence: return_type.confidence,
+ },
+ )
}
}
+ pub fn function_type(&self) -> Ref<Type> {
+ unsafe { Type::ref_from_raw(BNGetFunctionType(self.handle)) }
+ }
+
+ pub fn has_user_type(&self) -> bool {
+ unsafe { BNFunctionHasUserType(self.handle) }
+ }
+
+ pub fn set_user_type(&self, t: &Type) {
+ unsafe { BNSetFunctionUserType(self.handle, t.handle) }
+ }
+
+ pub fn set_auto_type(&self, t: &Type) {
+ unsafe { BNSetFunctionAutoType(self.handle, t.handle) }
+ }
+
pub fn stack_layout(&self) -> Array<NamedTypedVariable> {
let mut count = 0;
unsafe {
@@ -314,6 +437,317 @@ impl Function {
}
}
+ /// Gets number of bytes removed from the stack after return
+ pub fn stack_adjustment(&self) -> Conf<i64> {
+ unsafe { BNGetFunctionStackAdjustment(self.handle) }.into()
+ }
+
+ /// Sets number of bytes removed from the stack after return
+ pub fn set_user_stack_adjustment<C>(&self, value: C)
+ where
+ C: Into<Conf<i64>>,
+ {
+ let value: Conf<i64> = value.into();
+ let mut value_raw = value.into();
+ unsafe { BNSetUserFunctionStackAdjustment(self.handle, &mut value_raw) }
+ }
+
+ /// Sets number of bytes removed from the stack after return
+ pub fn set_auto_stack_adjustment<C>(&self, value: C)
+ where
+ C: Into<Conf<i64>>,
+ {
+ let value: Conf<i64> = value.into();
+ let mut value_raw = value.into();
+ unsafe { BNSetAutoFunctionStackAdjustment(self.handle, &mut value_raw) }
+ }
+
+ pub fn call_stack_adjustment(&self, addr: u64, arch: Option<CoreArchitecture>) -> Conf<i64> {
+ let arch = arch.unwrap_or_else(|| self.arch());
+ let result = unsafe { BNGetCallStackAdjustment(self.handle, arch.0, addr) };
+ result.into()
+ }
+
+ pub fn set_user_call_stack_adjustment<I>(
+ &self,
+ addr: u64,
+ adjust: I,
+ arch: Option<CoreArchitecture>,
+ ) where
+ I: Into<Conf<i64>>,
+ {
+ let arch = arch.unwrap_or_else(|| self.arch());
+ let adjust: Conf<i64> = adjust.into();
+ unsafe {
+ BNSetUserCallStackAdjustment(
+ self.handle,
+ arch.0,
+ addr,
+ adjust.contents,
+ adjust.confidence,
+ )
+ }
+ }
+
+ pub fn set_auto_call_stack_adjustment<I>(
+ &self,
+ addr: u64,
+ adjust: I,
+ arch: Option<CoreArchitecture>,
+ ) where
+ I: Into<Conf<i64>>,
+ {
+ let arch = arch.unwrap_or_else(|| self.arch());
+ let adjust: Conf<i64> = adjust.into();
+ unsafe {
+ BNSetAutoCallStackAdjustment(
+ self.handle,
+ arch.0,
+ addr,
+ adjust.contents,
+ adjust.confidence,
+ )
+ }
+ }
+
+ pub fn call_type_adjustment(
+ &self,
+ addr: u64,
+ arch: Option<CoreArchitecture>,
+ ) -> Option<Conf<Ref<Type>>> {
+ let arch = arch.unwrap_or_else(|| self.arch());
+ let result = unsafe { BNGetCallTypeAdjustment(self.handle, arch.0, addr) };
+ (!result.type_.is_null())
+ .then(|| unsafe { Conf::new(Type::ref_from_raw(result.type_), result.confidence) })
+ }
+
+ /// Sets or removes the call type override at a call site to the given type.
+ ///
+ /// * `addr` - virtual address of the call instruction to adjust
+ /// * `adjust_type` - (optional) overridden call type, or `None` to remove an existing adjustment
+ /// * `arch` - (optional) Architecture of the instruction if different from self.arch
+ pub fn set_user_call_type_adjustment<'a, I>(
+ &self,
+ addr: u64,
+ adjust_type: Option<I>,
+ arch: Option<CoreArchitecture>,
+ ) where
+ I: Into<Conf<&'a Type>>,
+ {
+ let arch = arch.unwrap_or_else(|| self.arch());
+ let mut adjust_type = adjust_type.map(|adjust_type| {
+ let adjust_type = adjust_type.into();
+ BNTypeWithConfidence {
+ type_: adjust_type.contents.handle,
+ confidence: adjust_type.confidence,
+ }
+ });
+ let adjust_ptr = adjust_type
+ .as_mut()
+ .map(|x| x as *mut _)
+ .unwrap_or(core::ptr::null_mut());
+ unsafe { BNSetUserCallTypeAdjustment(self.handle, arch.0, addr, adjust_ptr) }
+ }
+
+ pub fn set_auto_call_type_adjustment<'a, I>(
+ &self,
+ addr: u64,
+ adjust_type: I,
+ arch: Option<CoreArchitecture>,
+ ) where
+ I: Into<Conf<&'a Type>>,
+ {
+ let arch = arch.unwrap_or_else(|| self.arch());
+ let adjust_type: Conf<&Type> = adjust_type.into();
+ unsafe {
+ BNSetAutoCallTypeAdjustment(
+ self.handle,
+ arch.0,
+ addr,
+ &mut BNTypeWithConfidence {
+ type_: adjust_type.contents.handle,
+ confidence: adjust_type.confidence,
+ },
+ )
+ }
+ }
+
+ pub fn call_reg_stack_adjustment(
+ &self,
+ addr: u64,
+ arch: Option<CoreArchitecture>,
+ ) -> Array<RegisterStackAdjustment<CoreArchitecture>> {
+ let arch = arch.unwrap_or_else(|| self.arch());
+ let mut count = 0;
+ let adjust =
+ unsafe { BNGetCallRegisterStackAdjustment(self.handle, arch.0, addr, &mut count) };
+ assert!(!adjust.is_null());
+ unsafe { Array::new(adjust, count, arch.handle()) }
+ }
+
+ pub fn set_user_call_reg_stack_adjustment<I>(
+ self,
+ addr: u64,
+ adjust: I,
+ arch: Option<CoreArchitecture>,
+ ) where
+ I: IntoIterator<Item = RegisterStackAdjustment<CoreArchitecture>>,
+ {
+ let arch = arch.unwrap_or_else(|| self.arch());
+ let mut adjust_buf: Box<[BNRegisterStackAdjustment]> =
+ adjust.into_iter().map(|adjust| adjust.into_raw()).collect();
+ unsafe {
+ BNSetUserCallRegisterStackAdjustment(
+ self.handle,
+ arch.0,
+ addr,
+ adjust_buf.as_mut_ptr(),
+ adjust_buf.len(),
+ )
+ }
+ }
+
+ pub fn set_auto_call_reg_stack_adjustment<I>(
+ &self,
+ addr: u64,
+ adjust: I,
+ arch: Option<CoreArchitecture>,
+ ) where
+ I: IntoIterator<Item = RegisterStackAdjustment<CoreArchitecture>>,
+ {
+ let arch = arch.unwrap_or_else(|| self.arch());
+ let mut adjust_buf: Box<[BNRegisterStackAdjustment]> =
+ adjust.into_iter().map(|reg| reg.into_raw()).collect();
+
+ unsafe {
+ BNSetAutoCallRegisterStackAdjustment(
+ self.handle,
+ arch.0,
+ addr,
+ adjust_buf.as_mut_ptr(),
+ adjust_buf.len(),
+ )
+ }
+ }
+
+ pub fn call_reg_stack_adjustment_for_reg_stack(
+ &self,
+ addr: u64,
+ reg_stack_id: u32,
+ arch: Option<CoreArchitecture>,
+ ) -> RegisterStackAdjustment<CoreArchitecture> {
+ let arch = arch.unwrap_or_else(|| self.arch());
+ let adjust = unsafe {
+ BNGetCallRegisterStackAdjustmentForRegisterStack(
+ self.handle,
+ arch.0,
+ addr,
+ reg_stack_id,
+ )
+ };
+ unsafe { RegisterStackAdjustment::from_raw(adjust, arch) }
+ }
+
+ pub fn set_user_call_reg_stack_adjustment_for_reg_stack<I>(
+ &self,
+ addr: u64,
+ reg_stack_id: u32,
+ adjust: I,
+ arch: Option<CoreArchitecture>,
+ ) where
+ I: Into<Conf<i32>>,
+ {
+ let arch = arch.unwrap_or_else(|| self.arch());
+ let adjust: Conf<i32> = adjust.into();
+ unsafe {
+ BNSetUserCallRegisterStackAdjustmentForRegisterStack(
+ self.handle,
+ arch.0,
+ addr,
+ reg_stack_id,
+ adjust.contents,
+ adjust.confidence,
+ )
+ }
+ }
+
+ pub fn set_auto_call_reg_stack_adjustment_for_reg_stack<I>(
+ &self,
+ addr: u64,
+ reg_stack_id: u32,
+ adjust: I,
+ arch: Option<CoreArchitecture>,
+ ) where
+ I: Into<Conf<i32>>,
+ {
+ let arch = arch.unwrap_or_else(|| self.arch());
+ let adjust: Conf<i32> = adjust.into();
+ unsafe {
+ BNSetAutoCallRegisterStackAdjustmentForRegisterStack(
+ self.handle,
+ arch.0,
+ addr,
+ reg_stack_id,
+ adjust.contents,
+ adjust.confidence,
+ )
+ }
+ }
+
+ pub fn reg_stack_adjustments(&self) -> Array<RegisterStackAdjustment<CoreArchitecture>> {
+ let mut count = 0;
+ let adjust = unsafe { BNGetFunctionRegisterStackAdjustments(self.handle, &mut count) };
+ assert!(!adjust.is_null());
+ unsafe { Array::new(adjust, count, self.arch().handle()) }
+ }
+
+ pub fn set_user_reg_stack_adjustments<I, A>(&self, values: I)
+ where
+ I: IntoIterator<Item = RegisterStackAdjustment<A>>,
+ A: Architecture,
+ {
+ let mut values: Box<[BNRegisterStackAdjustment]> =
+ values.into_iter().map(|r| r.into_raw()).collect();
+ unsafe {
+ BNSetUserFunctionRegisterStackAdjustments(
+ self.handle,
+ values.as_mut_ptr(),
+ values.len(),
+ )
+ }
+ }
+
+ pub fn set_auto_reg_stack_adjustments<I, A>(&self, values: I)
+ where
+ I: IntoIterator<Item = RegisterStackAdjustment<A>>,
+ A: Architecture,
+ {
+ let mut values: Box<[BNRegisterStackAdjustment]> =
+ values.into_iter().map(|r| r.into_raw()).collect();
+ unsafe {
+ BNSetAutoFunctionRegisterStackAdjustments(
+ self.handle,
+ values.as_mut_ptr(),
+ values.len(),
+ )
+ }
+ }
+
+ /// List of function variables: including name, variable and type
+ pub fn variables(&self) -> Array<(&str, Variable, &Type)> {
+ let mut count = 0;
+ let vars = unsafe { BNGetFunctionVariables(self.handle, &mut count) };
+ assert!(!vars.is_null());
+ unsafe { Array::new(vars, count, ()) }
+ }
+
+ pub fn split_variables(&self) -> Array<Variable> {
+ let mut count = 0;
+ let vars = unsafe { BNGetSplitVariables(self.handle, &mut count) };
+ assert!(!vars.is_null());
+ unsafe { Array::new(vars, count, ()) }
+ }
+
pub fn parameter_variables(&self) -> Conf<Vec<Variable>> {
unsafe {
let mut variables = BNGetFunctionParameterVariables(self.handle);
@@ -330,6 +764,66 @@ impl Function {
}
}
+ pub fn set_user_parameter_variables<I>(&self, values: I, confidence: u8)
+ where
+ I: IntoIterator<Item = Variable>,
+ {
+ let mut vars: Box<[BNVariable]> = values.into_iter().map(|var| var.raw()).collect();
+ unsafe {
+ BNSetUserFunctionParameterVariables(
+ self.handle,
+ &mut BNParameterVariablesWithConfidence {
+ vars: vars.as_mut_ptr(),
+ count: vars.len(),
+ confidence,
+ },
+ )
+ }
+ }
+
+ pub fn set_auto_parameter_variables<I>(&self, values: I, confidence: u8)
+ where
+ I: IntoIterator<Item = Variable>,
+ {
+ let mut vars: Box<[BNVariable]> = values.into_iter().map(|var| var.raw()).collect();
+ unsafe {
+ BNSetAutoFunctionParameterVariables(
+ self.handle,
+ &mut BNParameterVariablesWithConfidence {
+ vars: vars.as_mut_ptr(),
+ count: vars.len(),
+ confidence,
+ },
+ )
+ }
+ }
+
+ pub fn parameter_at(
+ &self,
+ addr: u64,
+ func_type: Option<&Type>,
+ i: usize,
+ arch: Option<CoreArchitecture>,
+ ) -> RegisterValue {
+ let arch = arch.unwrap_or_else(|| self.arch());
+ let func_type = func_type.map(|f| f.handle).unwrap_or(core::ptr::null_mut());
+ let value =
+ unsafe { BNGetParameterValueAtInstruction(self.handle, arch.0, addr, func_type, i) };
+ value.into()
+ }
+
+ pub fn parameter_at_low_level_il_instruction(
+ &self,
+ instr: usize,
+ func_type: &Type,
+ i: usize,
+ ) -> RegisterValue {
+ let value = unsafe {
+ BNGetParameterValueAtLowLevelILInstruction(self.handle, instr, func_type.handle, i)
+ };
+ value.into()
+ }
+
pub fn apply_imported_types(&self, sym: &Symbol, t: Option<&Type>) {
unsafe {
BNApplyImportedTypes(
@@ -344,6 +838,12 @@ impl Function {
}
}
+ pub fn apply_auto_discovered_type(&self, func_type: &Type) {
+ unsafe { BNApplyAutoDiscoveredFunctionType(self.handle, func_type.handle) }
+ }
+
+ /// Whether automatic analysis was skipped for this function.
+ /// Can be set to false to re-enable analysis.
pub fn analysis_skipped(&self) -> bool {
unsafe { BNIsFunctionAnalysisSkipped(self.handle) }
}
@@ -377,6 +877,1267 @@ impl Function {
pub fn set_analysis_skip_override(&self, override_: FunctionAnalysisSkipOverride) {
unsafe { BNSetFunctionAnalysisSkipOverride(self.handle, override_) }
}
+
+ ///Whether the function's IL should be inlined into all callers' IL
+ pub fn inline_during_analysis(&self) -> Conf<bool> {
+ let result = unsafe { BNIsFunctionInlinedDuringAnalysis(self.handle) };
+ result.into()
+ }
+
+ pub fn set_auto_inline_during_analysis<C>(&self, value: C)
+ where
+ C: Into<Conf<bool>>,
+ {
+ let value: Conf<bool> = value.into();
+ unsafe {
+ BNSetAutoFunctionInlinedDuringAnalysis(
+ self.handle,
+ BNBoolWithConfidence {
+ value: value.contents,
+ confidence: value.confidence,
+ },
+ )
+ }
+ }
+
+ pub fn set_user_inline_during_analysis<C>(&self, value: C)
+ where
+ C: Into<Conf<bool>>,
+ {
+ let value: Conf<bool> = value.into();
+ unsafe {
+ BNSetUserFunctionInlinedDuringAnalysis(
+ self.handle,
+ BNBoolWithConfidence {
+ value: value.contents,
+ confidence: value.confidence,
+ },
+ )
+ }
+ }
+
+ pub fn analysis_performance_info(&self) -> Array<PerformanceInfo> {
+ let mut count = 0;
+ let info = unsafe { BNGetFunctionAnalysisPerformanceInfo(self.handle, &mut count) };
+ assert!(!info.is_null());
+ unsafe { Array::new(info, count, ()) }
+ }
+
+ /// Creates and adds a [Tag] object on either a function, or on
+ /// an address inside of a function.
+ ///
+ /// "Function tags" appear at the top of a function and are a good way to label an
+ /// entire function with some information. If you include an address when you call
+ /// Function.add_tag, you'll create an "address tag". These are good for labeling
+ /// specific instructions.
+ ///
+ /// For tagging arbitrary data, consider [BinaryViewExt::add_tag].
+ ///
+ /// * `tag_type_name` - The name of the tag type for this Tag.
+ /// * `data` - Additional data for the Tag.
+ /// * `addr` - Address at which to add the tag.
+ /// * `user` - Whether or not a user tag.
+ ///
+ /// # Example
+ ///
+ /// ```no_run
+ /// # use binaryninja::binaryview::{BinaryView, BinaryViewExt};
+ /// # use binaryninja::function::Function;
+ /// # let fun: Function = todo!();
+ /// # let bv: BinaryView = todo!();
+ /// let important = bv.create_tag_type("Important", "⚠️");
+ /// fun.add_tag(&important, "I think this is the main function", None, false, None);
+ /// let crash = bv.create_tag_type("Crashes", "🎯");
+ /// fun.add_tag(&crash, "Nullpointer dereference", Some(0x1337), false, None);
+ /// ```
+ pub fn add_tag<S: BnStrCompatible>(
+ &self,
+ tag_type: &TagType,
+ data: S,
+ addr: Option<u64>,
+ user: bool,
+ arch: Option<CoreArchitecture>,
+ ) {
+ let arch = arch.unwrap_or_else(|| self.arch());
+
+ // Create tag
+ let tag = Tag::new(tag_type, data);
+ let binaryview = unsafe { BinaryView::from_raw(BNGetFunctionData(self.handle)) };
+ unsafe { BNAddTag(binaryview.handle, tag.handle, user) };
+
+ unsafe {
+ match (user, addr) {
+ (false, None) => BNAddAutoFunctionTag(self.handle, tag.handle),
+ (false, Some(addr)) => BNAddAutoAddressTag(self.handle, arch.0, addr, tag.handle),
+ (true, None) => BNAddUserFunctionTag(self.handle, tag.handle),
+ (true, Some(addr)) => BNAddUserAddressTag(self.handle, arch.0, addr, tag.handle),
+ }
+ }
+ }
+
+ /// Remove [Tag] object on either a function, or on an address inside of a function.
+ ///
+ /// * `tag` - The tag to remove.
+ /// * `addr` - (optional) Address at which to remove the tag.
+ /// * `user` - Whether or not a user tag.
+ pub fn remove_tag(
+ &self,
+ tag: &Tag,
+ addr: Option<u64>,
+ user: bool,
+ arch: Option<CoreArchitecture>,
+ ) {
+ let arch = arch.unwrap_or_else(|| self.arch());
+ unsafe {
+ match (user, addr) {
+ (false, None) => BNRemoveAutoFunctionTag(self.handle, tag.handle),
+ (false, Some(addr)) => {
+ BNRemoveAutoAddressTag(self.handle, arch.0, addr, tag.handle)
+ }
+ (true, None) => BNRemoveUserFunctionTag(self.handle, tag.handle),
+ (true, Some(addr)) => BNRemoveUserAddressTag(self.handle, arch.0, addr, tag.handle),
+ }
+ }
+ }
+
+ /// Remove [Tag] object of type on either a function, or on an address
+ /// inside of a function.
+ ///
+ /// * `tag_type` - The type of the to remove.
+ /// * `addr` - Address at which to add the tag.
+ /// * `user` - Whether or not a user tag.
+ pub fn remove_tags_of_type(
+ &self,
+ tag_type: &TagType,
+ addr: Option<u64>,
+ user: bool,
+ arch: Option<CoreArchitecture>,
+ ) {
+ let arch = arch.unwrap_or_else(|| self.arch());
+ unsafe {
+ match (user, addr) {
+ (false, None) => BNRemoveAutoFunctionTagsOfType(self.handle, tag_type.handle),
+ (false, Some(addr)) => {
+ BNRemoveAutoAddressTagsOfType(self.handle, arch.0, addr, tag_type.handle)
+ }
+ (true, None) => BNRemoveUserFunctionTagsOfType(self.handle, tag_type.handle),
+ (true, Some(addr)) => {
+ BNRemoveUserAddressTagsOfType(self.handle, arch.0, addr, tag_type.handle)
+ }
+ }
+ }
+ }
+
+ /// Places a user-defined cross-reference from the instruction at
+ /// the given address and architecture to the specified target address. If the specified
+ /// source instruction is not contained within this function, no action is performed.
+ /// To remove the reference, use [Function::remove_user_code_ref].
+ ///
+ /// * `from_addr` - Virtual address of the source instruction.
+ /// * `to_addr` - Virtual address of the xref's destination.
+ /// * `arch` - Architecture of the source instruction.
+ ///
+ /// # Example
+ ///
+ /// ```no_run
+ /// # use binaryninja::function::Function;
+ /// # let fun: Function = todo!();
+ /// fun.add_user_code_ref(0x1337, 0x400000, None);
+ /// ```
+ pub fn add_user_code_ref(&self, from_addr: u64, to_addr: u64, arch: Option<CoreArchitecture>) {
+ let arch = arch.unwrap_or_else(|| self.arch());
+ unsafe { BNAddUserCodeReference(self.handle, arch.0, from_addr, to_addr) }
+ }
+
+ /// Removes a user-defined cross-reference.
+ /// If the given address is not contained within this function, or if there is no
+ /// such user-defined cross-reference, no action is performed.
+ ///
+ /// * `from_addr` - virtual address of the source instruction
+ /// * `to_addr` - virtual address of the xref's destination.
+ /// * `arch` - architecture of the source instruction
+ ///
+ /// #Example
+ ///
+ /// ```no_run
+ /// # use binaryninja::function::Function;
+ /// # let fun: Function = todo!();
+ /// fun.remove_user_code_ref(0x1337, 0x400000, None);
+ /// ```
+ pub fn remove_user_code_ref(
+ self,
+ from_addr: u64,
+ to_addr: u64,
+ arch: Option<CoreArchitecture>,
+ ) {
+ let arch = arch.unwrap_or_else(|| self.arch());
+ unsafe { BNRemoveUserCodeReference(self.handle, arch.0, from_addr, to_addr) }
+ }
+
+ /// Places a user-defined type cross-reference from the instruction at
+ /// the given address and architecture to the specified type. If the specified
+ /// source instruction is not contained within this function, no action is performed.
+ /// To remove the reference, use [Function::remove_user_type_ref].
+ ///
+ /// * `from_addr` - Virtual address of the source instruction.
+ /// * `name` - Name of the referenced type.
+ /// * `arch` - Architecture of the source instruction.
+ ///
+ /// # Example
+ /// ```no_run
+ /// # use binaryninja::function::Function;
+ /// # let fun: Function = todo!();
+ /// fun.add_user_type_ref(0x1337, &"A".into(), None);
+ /// ```
+ pub fn add_user_type_ref(
+ &self,
+ from_addr: u64,
+ name: &QualifiedName,
+ arch: Option<CoreArchitecture>,
+ ) {
+ let arch = arch.unwrap_or_else(|| self.arch());
+ let name_ptr = &name.0 as *const BNQualifiedName as *mut _;
+ unsafe { BNAddUserTypeReference(self.handle, arch.0, from_addr, name_ptr) }
+ }
+
+ /// Removes a user-defined type cross-reference.
+ /// If the given address is not contained within this function, or if there is no
+ /// such user-defined cross-reference, no action is performed.
+ ///
+ /// * `from_addr` - Virtual address of the source instruction.
+ /// * `name` - Name of the referenced type.
+ /// * `from_arch` - Architecture of the source instruction.
+ ///
+ /// # Example
+ /// ```no_run
+ /// # use binaryninja::function::Function;
+ /// # let fun: Function = todo!();
+ /// fun.remove_user_type_ref(0x1337, &"A".into(), None);
+ /// ```
+ pub fn remove_user_type_ref(
+ &self,
+ from_addr: u64,
+ name: &QualifiedName,
+ arch: Option<CoreArchitecture>,
+ ) {
+ let arch = arch.unwrap_or_else(|| self.arch());
+ let name_ptr = &name.0 as *const BNQualifiedName as *mut _;
+ unsafe { BNRemoveUserTypeReference(self.handle, arch.0, from_addr, name_ptr) }
+ }
+
+ /// Places a user-defined type field cross-reference from the
+ /// instruction at the given address and architecture to the specified type. If the specified
+ /// source instruction is not contained within this function, no action is performed.
+ /// To remove the reference, use [Function::remove_user_type_field_ref].
+ ///
+ /// * `from_addr` - Virtual address of the source instruction.
+ /// * `name` - Name of the referenced type.
+ /// * `offset` - Offset of the field, relative to the type.
+ /// * `arch` - Architecture of the source instruction.
+ /// * `size` - The size of the access.
+ ///
+ /// # Example
+ /// ```no_run
+ /// # use binaryninja::function::Function;
+ /// # let fun: Function = todo!();
+ /// fun.add_user_type_field_ref(0x1337, &"A".into(), 0x8, None, None);
+ /// ```
+ pub fn add_user_type_field_ref(
+ &self,
+ from_addr: u64,
+ name: &QualifiedName,
+ offset: u64,
+ arch: Option<CoreArchitecture>,
+ size: Option<usize>,
+ ) {
+ let size = size.unwrap_or(0);
+ let arch = arch.unwrap_or_else(|| self.arch());
+ let name_ptr = &name.0 as *const _ as *mut _;
+ unsafe {
+ BNAddUserTypeFieldReference(self.handle, arch.0, from_addr, name_ptr, offset, size)
+ }
+ }
+
+ /// Removes a user-defined type field cross-reference.
+ /// If the given address is not contained within this function, or if there is no
+ /// such user-defined cross-reference, no action is performed.
+ ///
+ /// * `from_addr` - Virtual address of the source instruction
+ /// * `name` - Name of the referenced type
+ /// * `offset` - Offset of the field, relative to the type
+ /// * `arch` - Architecture of the source instruction
+ /// * `size` - The size of the access
+ ///
+ /// # Example
+ /// ```no_run
+ /// # use binaryninja::function::Function;
+ /// # let fun: Function = todo!();
+ /// fun.remove_user_type_field_ref(0x1337, &"A".into(), 0x8, None, None);
+ /// ```
+ pub fn remove_user_type_field_ref(
+ &self,
+ from_addr: u64,
+ name: &QualifiedName,
+ offset: u64,
+ arch: Option<CoreArchitecture>,
+ size: Option<usize>,
+ ) {
+ let size = size.unwrap_or(0);
+ let arch = arch.unwrap_or_else(|| self.arch());
+ let name_ptr = &name.0 as *const _ as *mut _;
+ unsafe {
+ BNRemoveUserTypeFieldReference(self.handle, arch.0, from_addr, name_ptr, offset, size)
+ }
+ }
+
+ pub fn constant_data(
+ &self,
+ state: RegisterValueType,
+ value: u64,
+ size: Option<usize>,
+ ) -> DataBuffer {
+ let size = size.unwrap_or(0);
+ let state_raw = state.into_raw_value();
+ DataBuffer::from_raw(unsafe { BNGetConstantData(self.handle, state_raw, value, size) })
+ }
+
+ pub fn constants_referenced_by(
+ &self,
+ addr: u64,
+ arch: Option<CoreArchitecture>,
+ ) -> Array<ConstantReference> {
+ let arch = arch.unwrap_or_else(|| self.arch());
+ let mut count = 0;
+ let refs =
+ unsafe { BNGetConstantsReferencedByInstruction(self.handle, arch.0, addr, &mut count) };
+ assert!(!refs.is_null());
+ unsafe { Array::new(refs, count, ()) }
+ }
+
+ pub fn constants_referenced_by_address_if_available(
+ &self,
+ addr: u64,
+ arch: Option<CoreArchitecture>,
+ ) -> Array<ConstantReference> {
+ let arch = arch.unwrap_or_else(|| self.arch());
+ let mut count = 0;
+ let refs = unsafe {
+ BNGetConstantsReferencedByInstructionIfAvailable(self.handle, arch.0, addr, &mut count)
+ };
+ assert!(!refs.is_null());
+ unsafe { Array::new(refs, count, ()) }
+ }
+
+ /// Returns a list of function Tags for the function.
+ ///
+ /// `auto` - If `None`, gets all tags, if `true`, gets auto tags, if `false`, gets user tags
+ /// `tag_type` - If `None`, gets all tags, otherwise only gets tags of the given type
+ pub fn function_tags(&self, auto: Option<bool>, tag_type: Option<&str>) -> Array<Tag> {
+ let mut count = 0;
+
+ let tag_type = tag_type.map(|tag_type| self.view().get_tag_type(tag_type));
+
+ let tags = unsafe {
+ match (tag_type, auto) {
+ // received a tag_type, BinaryView found none
+ (Some(None), _) => return Array::new(core::ptr::null_mut(), 0, ()),
+
+ // with tag_type
+ (Some(Some(tag_type)), None) => {
+ BNGetFunctionTagsOfType(self.handle, tag_type.handle, &mut count)
+ }
+ (Some(Some(tag_type)), Some(true)) => {
+ BNGetAutoFunctionTagsOfType(self.handle, tag_type.handle, &mut count)
+ }
+ (Some(Some(tag_type)), Some(false)) => {
+ BNGetUserFunctionTagsOfType(self.handle, tag_type.handle, &mut count)
+ }
+ // without tag_type
+ (None, None) => BNGetFunctionTags(self.handle, &mut count),
+ (None, Some(true)) => BNGetAutoFunctionTags(self.handle, &mut count),
+ (None, Some(false)) => BNGetUserFunctionTags(self.handle, &mut count),
+ }
+ };
+ assert!(!tags.is_null());
+
+ unsafe { Array::new(tags, count, ()) }
+ }
+
+ pub fn tags(&self) -> Array<TagReference> {
+ let mut count = 0;
+ let tags = unsafe { BNGetAddressTagReferences(self.handle, &mut count) };
+ unsafe { Array::new(tags, count, ()) }
+ }
+
+ /// Gets a list of Tags at the address.
+ ///
+ /// * `addr` - Address to get tags from.
+ /// * `auto` - If `None`, gets all tags, if `true`, gets auto tags, if `false`, gets user tags
+ pub fn tags_at(
+ &self,
+ addr: u64,
+ auto: Option<bool>,
+ arch: Option<CoreArchitecture>,
+ ) -> Array<Tag> {
+ let arch = arch.unwrap_or_else(|| self.arch());
+ let mut count = 0;
+
+ let tags = match auto {
+ None => unsafe { BNGetAddressTags(self.handle, arch.0, addr, &mut count) },
+ Some(true) => unsafe { BNGetAutoAddressTags(self.handle, arch.0, addr, &mut count) },
+ Some(false) => unsafe { BNGetUserAddressTags(self.handle, arch.0, addr, &mut count) },
+ };
+ assert!(!tags.is_null());
+ unsafe { Array::new(tags, count, ()) }
+ }
+
+ /// Gets a list of Tags in the address range.
+ ///
+ /// * `addr` - Address to get tags from.
+ /// * `auto` - If `None`, gets all tags, if `true`, gets auto tags, if `false`, gets user tags
+ pub fn tags_in_range(
+ &self,
+ range: Range<u64>,
+ auto: Option<bool>,
+ arch: Option<CoreArchitecture>,
+ ) -> Array<TagReference> {
+ let arch = arch.unwrap_or_else(|| self.arch());
+ let mut count = 0;
+
+ let tags = match auto {
+ None => unsafe {
+ BNGetAddressTagsInRange(self.handle, arch.0, range.start, range.end, &mut count)
+ },
+ Some(true) => unsafe {
+ BNGetAutoAddressTagsInRange(self.handle, arch.0, range.start, range.end, &mut count)
+ },
+ Some(false) => unsafe {
+ BNGetUserAddressTagsInRange(self.handle, arch.0, range.start, range.end, &mut count)
+ },
+ };
+ assert!(!tags.is_null());
+ unsafe { Array::new(tags, count, ()) }
+ }
+
+ /// List of indirect branches
+ pub fn indirect_branches(&self) -> Array<IndirectBranchInfo> {
+ let mut count = 0;
+ let branches = unsafe { BNGetIndirectBranches(self.handle, &mut count) };
+ assert!(!branches.is_null());
+ unsafe { Array::new(branches, count, ()) }
+ }
+
+ pub fn set_user_indirect_branches<I>(
+ &self,
+ source: u64,
+ branches: I,
+ arch: Option<CoreArchitecture>,
+ ) where
+ I: IntoIterator<Item = u64>,
+ {
+ let arch = arch.unwrap_or_else(|| self.arch());
+ let mut branches: Box<[BNArchitectureAndAddress]> = branches
+ .into_iter()
+ .map(|address| BNArchitectureAndAddress {
+ address,
+ arch: arch.0,
+ })
+ .collect();
+ unsafe {
+ BNSetUserIndirectBranches(
+ self.handle,
+ arch.0,
+ source,
+ branches.as_mut_ptr(),
+ branches.len(),
+ )
+ }
+ }
+
+ pub fn set_auto_indirect_branches<I>(
+ &self,
+ source: u64,
+ branches: I,
+ arch: Option<CoreArchitecture>,
+ ) where
+ I: IntoIterator<Item = u64>,
+ {
+ let arch = arch.unwrap_or_else(|| self.arch());
+ let mut branches: Box<[BNArchitectureAndAddress]> = branches
+ .into_iter()
+ .map(|address| BNArchitectureAndAddress {
+ address,
+ arch: arch.0,
+ })
+ .collect();
+ unsafe {
+ BNSetAutoIndirectBranches(
+ self.handle,
+ arch.0,
+ source,
+ branches.as_mut_ptr(),
+ branches.len(),
+ )
+ }
+ }
+
+ /// List of indirect branches at this address
+ pub fn indirect_branches_at(
+ &self,
+ addr: u64,
+ arch: Option<CoreArchitecture>,
+ ) -> Array<IndirectBranchInfo> {
+ let arch = arch.unwrap_or_else(|| self.arch());
+ let mut count = 0;
+ let branches = unsafe { BNGetIndirectBranchesAt(self.handle, arch.0, addr, &mut count) };
+ assert!(!branches.is_null());
+ unsafe { Array::new(branches, count, ()) }
+ }
+
+ /// # Example
+ /// ```no_run
+ /// # let fun: binaryninja::function::Function = todo!();
+ /// let color = fun.instr_highlight(0x1337, None);
+ /// ```
+ pub fn instr_highlight(&self, addr: u64, arch: Option<CoreArchitecture>) -> HighlightColor {
+ let arch = arch.unwrap_or_else(|| self.arch());
+ let color = unsafe { BNGetInstructionHighlight(self.handle, arch.0, addr) };
+ HighlightColor::from_raw(color)
+ }
+
+ /// Sets the highlights the instruction at the specified address with the supplied color
+ ///
+ /// <div class="warning">Use only in analysis plugins. Do not use in regular plugins, as colors won't be saved to the database.</div>
+ ///
+ /// * `addr` - virtual address of the instruction to be highlighted
+ /// * `color` - Color value to use for highlighting
+ /// * `arch` - (optional) Architecture of the instruction if different from self.arch
+ pub fn set_auto_instr_highlight(
+ &self,
+ addr: u64,
+ color: HighlightColor,
+ arch: Option<CoreArchitecture>,
+ ) {
+ let arch = arch.unwrap_or_else(|| self.arch());
+ let color_raw = color.into_raw();
+ unsafe { BNSetAutoInstructionHighlight(self.handle, arch.0, addr, color_raw) }
+ }
+
+ /// Sets the highlights the instruction at the specified address with the supplied color
+ ///
+ /// * `addr` - virtual address of the instruction to be highlighted
+ /// * `color` - Color value to use for highlighting
+ /// * `arch` - (optional) Architecture of the instruction if different from self.arch
+ ///
+ /// # Example
+ /// ```no_run
+ /// # use binaryninja::types::HighlightColor;
+ /// # let fun: binaryninja::function::Function = todo!();
+ /// let color = HighlightColor::NoHighlightColor { alpha: u8::MAX };
+ /// fun.set_user_instr_highlight(0x1337, color, None);
+ /// ```
+ pub fn set_user_instr_highlight(
+ &self,
+ addr: u64,
+ color: HighlightColor,
+ arch: Option<CoreArchitecture>,
+ ) {
+ let arch = arch.unwrap_or_else(|| self.arch());
+ let color_raw = color.into_raw();
+ unsafe { BNSetUserInstructionHighlight(self.handle, arch.0, addr, color_raw) }
+ }
+
+ /// return the address, if any, of the instruction that contains the
+ /// provided address
+ pub fn instruction_containing_address(
+ &self,
+ addr: u64,
+ arch: Option<CoreArchitecture>,
+ ) -> Option<u64> {
+ let arch = arch.unwrap_or_else(|| self.arch());
+ let mut start = 0;
+ unsafe { BNGetInstructionContainingAddress(self.handle, arch.0, addr, &mut start) }
+ .then_some(start)
+ }
+
+ /// Get the current text display type for an integer token in the disassembly or IL views
+ ///
+ /// See also see [Function::int_display_type_and_typeid]
+ ///
+ /// * `instr_addr` - Address of the instruction or IL line containing the token
+ /// * `value` - field of the InstructionTextToken object for the token, usually the constant displayed
+ /// * `operand` - Operand index of the token, defined as the number of OperandSeparatorTokens in the disassembly line before the token
+ /// * `arch` - (optional) Architecture of the instruction or IL line containing the token
+ pub fn int_display_type(
+ &self,
+ instr_addr: u64,
+ value: u64,
+ operand: usize,
+ arch: Option<CoreArchitecture>,
+ ) -> IntegerDisplayType {
+ let arch = arch.unwrap_or_else(|| self.arch());
+ unsafe { BNGetIntegerConstantDisplayType(self.handle, arch.0, instr_addr, value, operand) }
+ }
+
+ /// Change the text display type for an integer token in the disassembly or IL views
+ ///
+ /// * `instr_addr` - Address of the instruction or IL line containing the token
+ /// * `value` - Field of the InstructionTextToken object for the token, usually the constant displayed
+ /// * `operand` - Operand index of the token, defined as the number of OperandSeparatorTokens in the disassembly line before the token
+ /// * `display_type` - Desired display type
+ /// * `arch` - (optional) Architecture of the instruction or IL line containing the token
+ /// * `enum_display_typeid` - (optional) Whenever passing EnumDisplayType to `display_type`, passing a type ID here will specify the Enumeration display type. Must be a valid type ID and resolve to an enumeration type.
+ pub fn set_int_display_type(
+ &self,
+ instr_addr: u64,
+ value: u64,
+ operand: usize,
+ display_type: IntegerDisplayType,
+ arch: Option<CoreArchitecture>,
+ enum_display_typeid: Option<impl BnStrCompatible>,
+ ) {
+ let arch = arch.unwrap_or_else(|| self.arch());
+ let enum_display_typeid = enum_display_typeid.map(BnStrCompatible::into_bytes_with_nul);
+ let enum_display_typeid_ptr = enum_display_typeid
+ .map(|x| x.as_ref().as_ptr() as *const i8)
+ .unwrap_or(core::ptr::null());
+ unsafe {
+ BNSetIntegerConstantDisplayType(
+ self.handle,
+ arch.0,
+ instr_addr,
+ value,
+ operand,
+ display_type,
+ enum_display_typeid_ptr,
+ )
+ }
+ }
+
+ /// Get the current text display enum type for an integer token in the disassembly or IL views.
+ ///
+ /// See also see [Function::int_display_type_and_typeid]
+ ///
+ /// * `instr_addr` - Address of the instruction or IL line containing the token
+ /// * `value` - field of the InstructionTextToken object for the token, usually the constant displayed
+ /// * `operand` - Operand index of the token, defined as the number of OperandSeparatorTokens in the disassembly line before the token
+ /// * `arch` - (optional) Architecture of the instruction or IL line containing the token
+ pub fn int_enum_display_typeid(
+ &self,
+ instr_addr: u64,
+ value: u64,
+ operand: usize,
+ arch: Option<CoreArchitecture>,
+ ) -> BnString {
+ let arch = arch.unwrap_or_else(|| self.arch());
+ unsafe {
+ BnString::from_raw(BNGetIntegerConstantDisplayTypeEnumerationType(
+ self.handle,
+ arch.0,
+ instr_addr,
+ value,
+ operand,
+ ))
+ }
+ }
+
+ /// Get the current text display type for an integer token in the disassembly or IL views
+ ///
+ /// * `instr_addr` - Address of the instruction or IL line containing the token
+ /// * `value` - field of the InstructionTextToken object for the token, usually the constant displayed
+ /// * `operand` - Operand index of the token, defined as the number of OperandSeparatorTokens in the disassembly line before the token
+ /// * `arch` - (optional) Architecture of the instruction or IL line containing the token
+ pub fn int_display_type_and_typeid(
+ &self,
+ instr_addr: u64,
+ value: u64,
+ operand: usize,
+ arch: Option<CoreArchitecture>,
+ ) -> (IntegerDisplayType, BnString) {
+ let arch = arch.unwrap_or_else(|| self.arch());
+ let name = self.int_enum_display_typeid(instr_addr, value, operand, Some(arch));
+ let display = self.int_display_type(instr_addr, value, operand, Some(arch));
+ (display, name)
+ }
+
+ /// Get the value the provided string register address corresponding to the given virtual address
+ ///
+ /// * `addr` - virtual address of the instruction to query
+ /// * `reg` - string value of native register to query
+ /// * `arch` - (optional) Architecture for the given function
+ ///
+ /// # Example
+ /// ```no_run
+ /// # use binaryninja::architecture::{ArchitectureExt, Register};
+ /// # let fun: binaryninja::function::Function = todo!();
+ /// let reg = fun.arch().register_by_name("rdi").unwrap();
+ /// let value = fun.register_value_at(0x400dbe, reg.id(), None);
+ /// ```
+ pub fn register_value_at(
+ &self,
+ addr: u64,
+ reg: u32,
+ arch: Option<CoreArchitecture>,
+ ) -> RegisterValue {
+ let arch = arch.unwrap_or_else(|| self.arch());
+ let register = unsafe { BNGetRegisterValueAtInstruction(self.handle, arch.0, addr, reg) };
+ register.into()
+ }
+
+ /// Gets the value instruction address corresponding to the given virtual address
+ ///
+ /// * `addr` - virtual address of the instruction to query
+ /// * `reg` - string value of native register to query
+ /// * `arch` - (optional) Architecture for the given function
+ ///
+ /// # Example
+ /// ```no_run
+ /// # use binaryninja::architecture::{ArchitectureExt, Register};
+ /// # let fun: binaryninja::function::Function = todo!();
+ /// let reg = fun.arch().register_by_name("rdi").unwrap();
+ /// let value = fun.register_value_after(0x400dbe, reg.id(), None);
+ /// ```
+ pub fn register_value_after(
+ &self,
+ addr: u64,
+ reg: u32,
+ arch: Option<CoreArchitecture>,
+ ) -> RegisterValue {
+ let arch = arch.unwrap_or_else(|| self.arch());
+ let register =
+ unsafe { BNGetRegisterValueAfterInstruction(self.handle, arch.0, addr, reg) };
+ register.into()
+ }
+
+ pub fn register_value_at_exit(&self, reg: u32) -> Conf<RegisterValue> {
+ let register = unsafe { BNGetFunctionRegisterValueAtExit(self.handle, reg) };
+ Conf::new(register.value.into(), register.confidence)
+ }
+
+ pub fn registers_read_by(
+ &self,
+ addr: u64,
+ arch: Option<CoreArchitecture>,
+ ) -> Array<CoreRegister> {
+ let arch = arch.unwrap_or_else(|| self.arch());
+ let mut count = 0;
+ let regs =
+ unsafe { BNGetRegistersReadByInstruction(self.handle, arch.0, addr, &mut count) };
+ assert!(!regs.is_null());
+ unsafe { Array::new(regs, count, arch) }
+ }
+
+ pub fn registers_written_by(
+ &self,
+ addr: u64,
+ arch: Option<CoreArchitecture>,
+ ) -> Array<CoreRegister> {
+ let arch = arch.unwrap_or_else(|| self.arch());
+ let mut count = 0;
+ let regs =
+ unsafe { BNGetRegistersWrittenByInstruction(self.handle, arch.0, addr, &mut count) };
+ assert!(!regs.is_null());
+ unsafe { Array::new(regs, count, arch) }
+ }
+
+ /// Registers that are modified by this function
+ pub fn clobbered_registers(&self) -> Conf<Array<CoreRegister>> {
+ let result = unsafe { BNGetFunctionClobberedRegisters(self.handle) };
+
+ let reg_set = unsafe { Array::new(result.regs, result.count, self.arch().handle()) };
+ Conf::new(reg_set, result.confidence)
+ }
+
+ pub fn set_user_clobbered_registers<I>(&self, registers: I, confidence: u8)
+ where
+ I: IntoIterator<Item = CoreRegister>,
+ {
+ let mut regs: Box<[u32]> = registers.into_iter().map(|reg| reg.id()).collect();
+ let mut regs = BNRegisterSetWithConfidence {
+ regs: regs.as_mut_ptr(),
+ count: regs.len(),
+ confidence,
+ };
+ unsafe { BNSetUserFunctionClobberedRegisters(self.handle, &mut regs) }
+ }
+
+ pub fn set_auto_clobbered_registers<I>(&self, registers: I, confidence: u8)
+ where
+ I: IntoIterator<Item = CoreRegister>,
+ {
+ let mut regs: Box<[u32]> = registers.into_iter().map(|reg| reg.id()).collect();
+ let mut regs = BNRegisterSetWithConfidence {
+ regs: regs.as_mut_ptr(),
+ count: regs.len(),
+ confidence,
+ };
+ unsafe { BNSetAutoFunctionClobberedRegisters(self.handle, &mut regs) }
+ }
+
+ pub fn stack_contents_at(
+ &self,
+ addr: u64,
+ offset: i64,
+ size: usize,
+ arch: Option<CoreArchitecture>,
+ ) -> RegisterValue {
+ let arch = arch.unwrap_or_else(|| self.arch());
+ let value =
+ unsafe { BNGetStackContentsAtInstruction(self.handle, arch.0, addr, offset, size) };
+ value.into()
+ }
+
+ pub fn stack_contents_after(
+ &self,
+ addr: u64,
+ offset: i64,
+ size: usize,
+ arch: Option<CoreArchitecture>,
+ ) -> RegisterValue {
+ let arch = arch.unwrap_or_else(|| self.arch());
+ let value =
+ unsafe { BNGetStackContentsAfterInstruction(self.handle, arch.0, addr, offset, size) };
+ value.into()
+ }
+
+ pub fn stack_var_at_frame_offset(
+ &self,
+ addr: u64,
+ offset: i64,
+ arch: Option<CoreArchitecture>,
+ ) -> Option<(Variable, BnString, Conf<Ref<Type>>)> {
+ let arch = arch.unwrap_or_else(|| self.arch());
+ let mut found_value: BNVariableNameAndType = unsafe { mem::zeroed() };
+ let found = unsafe {
+ BNGetStackVariableAtFrameOffset(self.handle, arch.0, addr, offset, &mut found_value)
+ };
+ if !found {
+ return None;
+ }
+ let var = unsafe { Variable::from_raw(found_value.var) };
+ let name = unsafe { BnString::from_raw(found_value.name) };
+ let var_type = Conf::new(
+ unsafe { Type::ref_from_raw(found_value.type_) },
+ found_value.typeConfidence,
+ );
+ Some((var, name, var_type))
+ }
+
+ pub fn stack_variables_referenced_by(
+ &self,
+ addr: u64,
+ arch: Option<CoreArchitecture>,
+ ) -> Array<StackVariableReference> {
+ let arch = arch.unwrap_or_else(|| self.arch());
+ let mut count = 0;
+ let refs = unsafe {
+ BNGetStackVariablesReferencedByInstruction(self.handle, arch.0, addr, &mut count)
+ };
+ assert!(!refs.is_null());
+ unsafe { Array::new(refs, count, ()) }
+ }
+
+ pub fn stack_variables_referenced_by_address_if_available(
+ &self,
+ addr: u64,
+ arch: Option<CoreArchitecture>,
+ ) -> Array<StackVariableReference> {
+ let arch = arch.unwrap_or_else(|| self.arch());
+ let mut count = 0;
+ let refs = unsafe {
+ BNGetStackVariablesReferencedByInstructionIfAvailable(
+ self.handle,
+ arch.0,
+ addr,
+ &mut count,
+ )
+ };
+ assert!(!refs.is_null());
+ unsafe { Array::new(refs, count, ()) }
+ }
+
+ /// Discovered value of the global pointer register, if the function uses one
+ pub fn global_pointer_value(&self) -> Conf<RegisterValue> {
+ let result = unsafe { BNGetFunctionGlobalPointerValue(self.handle) };
+ Conf::new(result.value.into(), result.confidence)
+ }
+
+ pub fn type_tokens(
+ &self,
+ settings: Option<&DisassemblySettings>,
+ ) -> Array<DisassemblyTextLine> {
+ let settings = settings.map(|s| s.handle).unwrap_or(core::ptr::null_mut());
+ let mut count = 0;
+ let lines = unsafe { BNGetFunctionTypeTokens(self.handle, settings, &mut count) };
+ assert!(!lines.is_null());
+ unsafe { Array::new(lines, count, ()) }
+ }
+
+ pub fn is_call_instruction(&self, addr: u64, arch: Option<CoreArchitecture>) -> bool {
+ let arch = arch.unwrap_or_else(|| self.arch());
+ unsafe { BNIsCallInstruction(self.handle, arch.0, addr) }
+ }
+
+ pub fn is_variable_user_defined(&self, var: &Variable) -> bool {
+ unsafe { BNIsVariableUserDefined(self.handle, &var.raw()) }
+ }
+
+ pub fn is_pure(&self) -> Conf<bool> {
+ unsafe { BNIsFunctionPure(self.handle) }.into()
+ }
+
+ pub fn set_user_pure<C>(&self, value: C)
+ where
+ C: Into<Conf<bool>>,
+ {
+ let value: Conf<bool> = value.into();
+ let mut value_raw = value.into();
+ unsafe { BNSetUserFunctionPure(self.handle, &mut value_raw) };
+ }
+
+ pub fn set_auto_pure<C>(&self, value: C)
+ where
+ C: Into<Conf<bool>>,
+ {
+ let value: Conf<bool> = value.into();
+ let mut value_raw = value.into();
+ unsafe { BNSetAutoFunctionPure(self.handle, &mut value_raw) };
+ }
+
+ pub fn is_too_large(&self) -> bool {
+ unsafe { BNIsFunctionTooLarge(self.handle) }
+ }
+
+ pub fn is_update_needed(&self) -> bool {
+ unsafe { BNIsFunctionUpdateNeeded(self.handle) }
+ }
+
+ /// Indicates that this function needs to be reanalyzed during the next update cycle
+ ///
+ /// * `update_type` - Desired update type
+ pub fn mark_updates_required(&self, update_type: FunctionUpdateType) {
+ unsafe { BNMarkUpdatesRequired(self.handle, update_type) }
+ }
+
+ /// Indicates that callers of this function need to be reanalyzed during the next update cycle
+ ///
+ /// * `uppdate_type` - Desired update type
+ pub fn mark_caller_updates_required(&self, update_type: FunctionUpdateType) {
+ unsafe { BNMarkCallerUpdatesRequired(self.handle, update_type) }
+ }
+
+ pub fn mark_recent_use(&self) {
+ unsafe { BNMarkFunctionAsRecentlyUsed(self.handle) }
+ }
+
+ // Gets the list of merged variables
+ pub fn merged_variables(&self) -> Array<MergedVariable> {
+ let mut count = 0;
+ let vars = unsafe { BNGetMergedVariables(self.handle, &mut count) };
+ assert!(!vars.is_null());
+ unsafe { Array::new(vars, count, ()) }
+ }
+
+ /// Merge one or more varibles in `sources` into the `target` variable. All
+ /// variable accesses to the variables in `sources` will be rewritten to use `target`.
+ ///
+ /// * `target` - target variable
+ /// * `sources` - list of source variables
+ pub fn merge_variables<'a>(
+ &self,
+ target: &Variable,
+ sources: impl IntoIterator<Item = &'a Variable>,
+ ) {
+ let sources_raw: Box<[BNVariable]> = sources.into_iter().map(|s| s.raw()).collect();
+ unsafe {
+ BNMergeVariables(
+ self.handle,
+ &target.raw(),
+ sources_raw.as_ptr(),
+ sources_raw.len(),
+ )
+ }
+ }
+
+ /// Undoes variable merging performed with [Function::merge_variables]. The variables in
+ /// `sources` will no longer be merged into the `target` variable.
+ ///
+ /// * `target` - target variable
+ /// * `sources` - list of source variables
+ pub fn unmerge_variables<'a>(
+ &self,
+ target: &Variable,
+ sources: impl IntoIterator<Item = &'a Variable>,
+ ) {
+ let sources_raw: Box<[BNVariable]> = sources.into_iter().map(|s| s.raw()).collect();
+ unsafe {
+ BNUnmergeVariables(
+ self.handle,
+ &target.raw(),
+ sources_raw.as_ptr(),
+ sources_raw.len(),
+ )
+ }
+ }
+
+ /// Splits a varible at the definition site. The given `var` must be the
+ /// variable unique to the definition and should be obtained by using
+ /// [mlil::MediumLevelILInstruction::get_split_var_for_definition] at the definition site.
+ ///
+ /// This function is not meant to split variables that have been previously merged. Use
+ /// [Function::unmerge_variables] to split previously merged variables.
+ ///
+ /// <div class="warning">
+ ///
+ /// Binary Ninja automatically splits all variables that the analysis determines
+ /// to be safely splittable. Splitting a variable manually with [Function::split_variable] can cause
+ /// IL and decompilation to be incorrect. There are some patterns where variables can be safely
+ /// split semantically but analysis cannot determine that it is safe. This function is provided
+ /// to allow variable splitting to be performed in these cases by plugins or by the user.
+ ///
+ /// </div>
+ ///
+ /// * `var` - variable to split
+ pub fn split_variable(&self, var: &Variable) {
+ unsafe { BNSplitVariable(self.handle, &var.raw()) }
+ }
+
+ /// Undoes varible splitting performed with [Function::split_variable]. The given `var`
+ /// must be the variable unique to the definition and should be obtained by using
+ /// [mlil::MediumLevelILInstruction::get_split_var_for_definition] at the definition site.
+ ///
+ /// * `var` - variable to unsplit
+ pub fn unsplit_variable(&self, var: &Variable) {
+ unsafe { BNUnsplitVariable(self.handle, &var.raw()) }
+ }
+
+ /// Causes this function to be reanalyzed. This function does not wait for the analysis to finish.
+ ///
+ /// * `update_type` - Desired update type
+ ///
+ /// <div class="warning">
+ ///
+ /// If analysis_skipped is `true`, using this API will not trigger
+ /// re-analysis. Instead, use [Function::set_analysis_skipped] with `false`.
+ ///
+ /// </div>
+ pub fn reanalyze(&self, update_type: FunctionUpdateType) {
+ unsafe { BNReanalyzeFunction(self.handle, update_type) }
+ }
+
+ /// Generate internal debug reports for a variety of analysis.
+ /// Current list of possible values include:
+ ///
+ /// - mlil_translator
+ /// - stack_adjust_graph
+ /// - high_level_il
+ ///
+ /// * `name` - Name of the debug report
+ pub fn request_debug_report(&self, name: &str) {
+ const DEBUG_REPORT_ALIAS: &[(&str, &str)] = &[
+ ("stack", "stack_adjust_graph\x00"),
+ ("mlil", "mlil_translator\x00"),
+ ("hlil", "high_level_il\x00"),
+ ];
+
+ if let Some(alias_idx) = DEBUG_REPORT_ALIAS
+ .iter()
+ .position(|(alias, _value)| *alias == name)
+ {
+ let name = DEBUG_REPORT_ALIAS[alias_idx].1.as_ptr() as *const i8;
+ unsafe { BNRequestFunctionDebugReport(self.handle, name) }
+ } else {
+ let name = std::ffi::CString::new(name.to_string()).unwrap();
+ unsafe { BNRequestFunctionDebugReport(self.handle, name.as_ptr()) }
+ }
+
+ self.view().update_analysis()
+ }
+
+ /// Whether function was automatically discovered s a result of some creation of a 'user' function.
+ /// 'user' functions may or may not have been created by a user through the or API. For instance the entry point
+ /// into a function is always created a 'user' function. 'user' functions should be considered the root of auto
+ /// analysis.
+ pub fn auto(&self) -> bool {
+ unsafe { BNWasFunctionAutomaticallyDiscovered(self.handle) }
+ }
+
+ /// Returns a list of possible call sites contained in this function.
+ /// This includes ordinary calls, tail calls, and indirect jumps. Not all of
+ /// the returned call sites are necessarily true call sites; some may simply
+ /// be unresolved indirect jumps, for example.
+ pub fn call_sites(&self) -> Array<CodeReference> {
+ let mut count = 0;
+ let refs = unsafe { BNGetFunctionCallSites(self.handle, &mut count) };
+ assert!(!refs.is_null());
+ unsafe { Array::new(refs, count, ()) }
+ }
+
+ /// Returns a list of ReferenceSource objects corresponding to the addresses
+ /// in functions which reference this function
+ pub fn caller_sites(&self) -> Array<CodeReference> {
+ self.view().get_code_refs(self.start())
+ }
+
+ /// Calling convention used by the function
+ pub fn calling_convention(&self) -> Option<Conf<Ref<CallingConvention<CoreArchitecture>>>> {
+ let result = unsafe { BNGetFunctionCallingConvention(self.handle) };
+ (!result.convention.is_null()).then(|| {
+ Conf::new(
+ unsafe { CallingConvention::ref_from_raw(result.convention, self.arch()) },
+ result.confidence,
+ )
+ })
+ }
+
+ /// Set the User calling convention used by the function
+ pub fn set_user_calling_convention<'a, I>(&self, value: Option<I>)
+ where
+ I: Into<Conf<&'a CallingConvention<CoreArchitecture>>>,
+ {
+ let mut conv_conf: BNCallingConventionWithConfidence = unsafe { mem::zeroed() };
+ if let Some(value) = value {
+ let value = value.into();
+ conv_conf.convention = value.contents.handle;
+ conv_conf.confidence = value.confidence;
+ }
+ unsafe { BNSetUserFunctionCallingConvention(self.handle, &mut conv_conf) }
+ }
+
+ /// Set the calling convention used by the function
+ pub fn set_auto_calling_convention<'a, I>(&self, value: Option<I>)
+ where
+ I: Into<Conf<&'a CallingConvention<CoreArchitecture>>>,
+ {
+ let mut conv_conf: BNCallingConventionWithConfidence = unsafe { mem::zeroed() };
+ if let Some(value) = value {
+ let value = value.into();
+ conv_conf.convention = value.contents.handle;
+ conv_conf.confidence = value.confidence;
+ }
+ unsafe { BNSetAutoFunctionCallingConvention(self.handle, &mut conv_conf) }
+ }
+
+ pub fn can_return(&self) -> Conf<bool> {
+ unsafe { BNCanFunctionReturn(self.handle) }.into()
+ }
+
+ pub fn set_user_can_return<I>(&self, value: I)
+ where
+ I: Into<Conf<bool>>,
+ {
+ let value: Conf<bool> = value.into();
+ let mut value_raw: BNBoolWithConfidence = value.into();
+ unsafe { BNSetUserFunctionCanReturn(self.handle, &mut value_raw) }
+ }
+
+ pub fn set_auto_can_return<I>(&self, value: I)
+ where
+ I: Into<Conf<bool>>,
+ {
+ let value: Conf<bool> = value.into();
+ let mut value_raw: BNBoolWithConfidence = value.into();
+ unsafe { BNSetAutoFunctionCanReturn(self.handle, &mut value_raw) }
+ }
+
+ /// Whether function has explicitly defined types
+ pub fn has_explicitly_defined_type(&self) -> bool {
+ unsafe { BNFunctionHasExplicitlyDefinedType(self.handle) }
+ }
+
+ pub fn has_user_annotations(&self) -> bool {
+ unsafe { BNFunctionHasUserAnnotations(self.handle) }
+ }
+
+ pub fn has_variable_arguments(&self) -> Conf<bool> {
+ unsafe { BNFunctionHasVariableArguments(self.handle) }.into()
+ }
+
+ pub fn set_user_has_variable_arguments<I>(&self, value: I)
+ where
+ I: Into<Conf<bool>>,
+ {
+ let bc: Conf<bool> = value.into();
+ let mut bc = bc.into();
+ unsafe { BNSetUserFunctionHasVariableArguments(self.handle, &mut bc) }
+ }
+
+ pub fn set_auto_has_variable_arguments<I>(&self, value: I)
+ where
+ I: Into<Conf<bool>>,
+ {
+ let bc: Conf<bool> = value.into();
+ let mut bc = bc.into();
+ unsafe { BNSetAutoFunctionHasVariableArguments(self.handle, &mut bc) }
+ }
+
+ /// Has unresolved indirect branches
+ pub fn has_unresolved_indirect_branches(&self) -> bool {
+ unsafe { BNHasUnresolvedIndirectBranches(self.handle) }
+ }
+
+ /// List of address of unresolved indirect branches
+ pub fn unresolved_indirect_branches(&self) -> Array<UnresolvedIndirectBranches> {
+ let mut count = 0;
+ let result = unsafe { BNGetUnresolvedIndirectBranches(self.handle, &mut count) };
+ unsafe { Array::new(result, count, ()) }
+ }
+
+ /// Returns a string representing the provenance. This portion of the API
+ /// is under develoment. Currently the provenance information is
+ /// undocumented, not persistent, and not saved to a database.
+ pub fn provenance(&self) -> BnString {
+ unsafe { BnString::from_raw(BNGetProvenanceString(self.handle)) }
+ }
+
+ /// Get registers that are used for the return value
+ pub fn return_registers(&self) -> Conf<Array<CoreRegister>> {
+ let result = unsafe { BNGetFunctionReturnRegisters(self.handle) };
+ let regs = unsafe { Array::new(result.regs, result.count, self.arch().handle()) };
+ Conf::new(regs, result.confidence)
+ }
+
+ pub fn set_user_return_registers<I>(&self, values: I, confidence: u8)
+ where
+ I: IntoIterator<Item = CoreRegister>,
+ {
+ let mut regs: Box<[u32]> = values.into_iter().map(|reg| reg.id()).collect();
+ let mut regs = BNRegisterSetWithConfidence {
+ regs: regs.as_mut_ptr(),
+ count: regs.len(),
+ confidence,
+ };
+ unsafe { BNSetUserFunctionReturnRegisters(self.handle, &mut regs) }
+ }
+
+ pub fn set_auto_return_registers<I>(&self, values: I, confidence: u8)
+ where
+ I: IntoIterator<Item = CoreRegister>,
+ {
+ let mut regs: Box<[u32]> = values.into_iter().map(|reg| reg.id()).collect();
+ let mut regs = BNRegisterSetWithConfidence {
+ regs: regs.as_mut_ptr(),
+ count: regs.len(),
+ confidence,
+ };
+ unsafe { BNSetAutoFunctionReturnRegisters(self.handle, &mut regs) }
+ }
+
+ /// Flow graph of unresolved stack adjustments
+ pub fn unresolved_stack_adjustment_graph(&self) -> Option<Ref<FlowGraph>> {
+ let graph = unsafe { BNGetUnresolvedStackAdjustmentGraph(self.handle) };
+ (!graph.is_null()).then(|| unsafe { Ref::new(FlowGraph::from_raw(graph)) })
+ }
+
+ pub fn create_graph(
+ &self,
+ graph_type: FunctionGraphType,
+ settings: Option<DisassemblySettings>,
+ ) -> Ref<FlowGraph> {
+ let settings_raw = settings.map(|s| s.handle).unwrap_or(core::ptr::null_mut());
+ let result = unsafe { BNCreateFunctionGraph(self.handle, graph_type, settings_raw) };
+ unsafe { Ref::new(FlowGraph::from_raw(result)) }
+ }
}
impl fmt::Debug for Function {
@@ -475,3 +2236,70 @@ unsafe impl CoreArrayProviderInner for AddressRange {
mem::transmute(raw)
}
}
+
+/////////////////
+// PerformanceInfo
+
+// NOTE only exists as Array<PerformanceInfo>, cant be owned
+#[repr(transparent)]
+pub struct PerformanceInfo(BNPerformanceInfo);
+
+impl PerformanceInfo {
+ pub fn name(&self) -> &str {
+ unsafe { std::ffi::CStr::from_ptr(self.0.name) }
+ .to_str()
+ .unwrap()
+ }
+ pub fn seconds(&self) -> f64 {
+ self.0.seconds
+ }
+}
+
+impl CoreArrayProvider for PerformanceInfo {
+ type Raw = BNPerformanceInfo;
+ type Context = ();
+ type Wrapped<'a> = Guard<'a, PerformanceInfo>;
+}
+unsafe impl CoreArrayProviderInner for PerformanceInfo {
+ unsafe fn free(raw: *mut Self::Raw, count: usize, _context: &Self::Context) {
+ BNFreeAnalysisPerformanceInfo(raw, count);
+ }
+ unsafe fn wrap_raw<'a>(raw: &'a Self::Raw, context: &'a Self::Context) -> Self::Wrapped<'a> {
+ Guard::new(Self(*raw), context)
+ }
+}
+
+/////////////////
+// Comments
+
+// NOTE only exists as Array<Comments>, cant be owned
+pub struct Comments {
+ addr: u64,
+ comment: BnString,
+}
+
+impl Comments {
+ pub fn address(&self) -> u64 {
+ self.addr
+ }
+ pub fn comment(&self) -> &str {
+ self.comment.as_str()
+ }
+}
+
+impl CoreArrayProvider for Comments {
+ type Raw = u64;
+ type Context = Ref<Function>;
+ type Wrapped<'a> = Comments;
+}
+unsafe impl CoreArrayProviderInner for Comments {
+ unsafe fn free(raw: *mut Self::Raw, _count: usize, _context: &Self::Context) {
+ BNFreeAddressList(raw);
+ }
+ unsafe fn wrap_raw<'a>(raw: &'a Self::Raw, function: &'a Self::Context) -> Self::Wrapped<'a> {
+ Comments {
+ addr: *raw,
+ comment: function.comment_at(*raw),
+ }
+ }
+}
diff --git a/rust/src/hlil/instruction.rs b/rust/src/hlil/instruction.rs
index 7e77e379..f98b1884 100644
--- a/rust/src/hlil/instruction.rs
+++ b/rust/src/hlil/instruction.rs
@@ -1,4 +1,3 @@
-use binaryninjacore_sys::BNFromVariableIdentifier;
use binaryninjacore_sys::BNGetHighLevelILByIndex;
use binaryninjacore_sys::BNHighLevelILOperation;
@@ -985,7 +984,7 @@ fn get_float(value: u64, size: usize) -> f64 {
}
fn get_var(id: u64) -> Variable {
- unsafe { Variable::from_raw(BNFromVariableIdentifier(id)) }
+ unsafe { Variable::from_identifier(id) }
}
fn get_member_index(idx: u64) -> Option<usize> {
diff --git a/rust/src/mlil/function.rs b/rust/src/mlil/function.rs
index 0b662578..e0856b93 100644
--- a/rust/src/mlil/function.rs
+++ b/rust/src/mlil/function.rs
@@ -1,19 +1,13 @@
use core::hash::{Hash, Hasher};
-use binaryninjacore_sys::BNFreeMediumLevelILFunction;
-use binaryninjacore_sys::BNGetMediumLevelILBasicBlockList;
-use binaryninjacore_sys::BNGetMediumLevelILIndexForInstruction;
-use binaryninjacore_sys::BNGetMediumLevelILInstructionCount;
-use binaryninjacore_sys::BNGetMediumLevelILOwnerFunction;
-use binaryninjacore_sys::BNGetMediumLevelILSSAForm;
-use binaryninjacore_sys::BNMediumLevelILFunction;
-use binaryninjacore_sys::BNMediumLevelILGetInstructionStart;
-use binaryninjacore_sys::BNNewMediumLevelILFunctionReference;
+use binaryninjacore_sys::*;
+use crate::architecture::CoreArchitecture;
use crate::basicblock::BasicBlock;
-use crate::function::Function;
-use crate::function::Location;
-use crate::rc::{Array, Ref, RefCountable};
+use crate::function::{Function, Location};
+use crate::rc::{Array, CoreArrayProvider, CoreArrayProviderInner, Ref, RefCountable};
+use crate::string::BnStrCompatible;
+use crate::types::{Conf, PossibleValueSet, Type, UserVariableValues, Variable};
use super::{MediumLevelILBlock, MediumLevelILInstruction, MediumLevelILLiftedInstruction};
@@ -105,6 +99,277 @@ impl MediumLevelILFunction {
unsafe { Array::new(blocks, count, context) }
}
+
+ pub fn get_var_definitions<'a>(&'a self, var: &Variable) -> MediumLevelILInstructionList<'a> {
+ let mut count = 0;
+ let raw_instrs =
+ unsafe { BNGetMediumLevelILVariableDefinitions(self.handle, &var.raw(), &mut count) };
+ assert!(!raw_instrs.is_null());
+ let instrs = unsafe { core::slice::from_raw_parts(raw_instrs, count) };
+ MediumLevelILInstructionList {
+ mlil: self,
+ ptr: raw_instrs,
+ instr_idxs: instrs.iter(),
+ }
+ }
+
+ pub fn create_user_stack_var<'a, S: BnStrCompatible, C: Into<Conf<&'a Type>>>(
+ self,
+ offset: i64,
+ var_type: C,
+ name: S,
+ ) {
+ let var_type = var_type.into();
+ let mut raw_var_type: BNTypeWithConfidence = var_type.into();
+ let name = name.into_bytes_with_nul();
+ unsafe {
+ BNCreateUserStackVariable(
+ self.get_function().handle,
+ offset,
+ &mut raw_var_type,
+ name.as_ref().as_ptr() as *const i8,
+ )
+ }
+ }
+
+ pub fn delete_user_stack_var(self, offset: i64) {
+ unsafe { BNDeleteUserStackVariable(self.get_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 var_type = var_type.into();
+ let raw_var_type: BNTypeWithConfidence = var_type.into();
+ let name = name.into_bytes_with_nul();
+ unsafe {
+ BNCreateUserVariable(
+ self.get_function().handle,
+ &var.raw(),
+ &raw_var_type as *const _ as *mut _,
+ name.as_ref().as_ptr() as *const _,
+ ignore_disjoint_uses,
+ )
+ }
+ }
+
+ pub fn delete_user_var(&self, var: &Variable) {
+ unsafe { BNDeleteUserVariable(self.get_function().handle, &var.raw()) }
+ }
+
+ pub fn is_var_user_defined(&self, var: &Variable) -> bool {
+ unsafe { BNIsVariableUserDefined(self.get_function().handle, &var.raw()) }
+ }
+
+ /// 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::mlil::MediumLevelILFunction;
+ /// # use binaryninja::types::PossibleValueSet;
+ /// # let mlil_fun: MediumLevelILFunction = todo!();
+ /// let (mlil_var, arch_addr, _val) = mlil_fun.user_var_values().all().next().unwrap();
+ /// let def_address = arch_addr.address;
+ /// let var_value = PossibleValueSet::ConstantValue{value: 5};
+ /// mlil_fun.set_user_var_value(&mlil_var, def_address, var_value).unwrap();
+ /// ```
+ pub fn set_user_var_value(
+ &self,
+ var: &Variable,
+ addr: u64,
+ value: PossibleValueSet,
+ ) -> Result<(), ()> {
+ let Some(_def_site) = self
+ .get_var_definitions(var)
+ .find(|def| def.address == addr)
+ else {
+ // Error "No definition for Variable found at given address"
+ return Err(());
+ };
+ let function = self.get_function();
+ let def_site = BNArchitectureAndAddress {
+ arch: function.arch().0,
+ address: addr,
+ };
+ let value = value.into_raw();
+
+ unsafe { BNSetUserVariableValue(function.handle, &var.raw(), &def_site, value.as_ffi()) }
+ 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
+ .get_var_definitions(var)
+ .find(|site| site.address == addr)
+ else {
+ //error "Could not get definition for Variable"
+ return Err(());
+ };
+
+ let function = self.get_function();
+ let def_site = BNArchitectureAndAddress {
+ arch: function.arch().0,
+ address: addr,
+ };
+
+ unsafe { BNClearUserVariableValue(function.handle, &var.raw(), &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) -> UserVariableValues {
+ let mut count = 0;
+ let function = self.get_function();
+ let var_values = unsafe { BNGetAllUserVariableValues(function.handle, &mut count) };
+ assert!(!var_values.is_null());
+ UserVariableValues {
+ vars: core::ptr::slice_from_raw_parts(var_values, count),
+ }
+ }
+
+ /// Clear all user defined variable values.
+ pub fn clear_user_var_values(&self) -> Result<(), ()> {
+ for (var, arch_and_addr, _value) in self.user_var_values().all() {
+ self.clear_user_var_value(&var, arch_and_addr.address)?;
+ }
+ Ok(())
+ }
+
+ pub fn create_auto_stack_var<'a, T: Into<Conf<&'a Type>>, S: BnStrCompatible>(
+ &self,
+ offset: i64,
+ var_type: T,
+ name: S,
+ ) {
+ let var_type: Conf<&Type> = var_type.into();
+ let mut var_type = var_type.into();
+ let name = name.into_bytes_with_nul();
+ let name_c_str = name.as_ref();
+ unsafe {
+ BNCreateAutoStackVariable(
+ self.get_function().handle,
+ offset,
+ &mut var_type,
+ name_c_str.as_ptr() as *const i8,
+ )
+ }
+ }
+
+ pub fn delete_auto_stack_var(&self, offset: i64) {
+ unsafe { BNDeleteAutoStackVariable(self.get_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 var_type: Conf<&Type> = var_type.into();
+ let mut var_type = var_type.into();
+ let name = name.into_bytes_with_nul();
+ let name_c_str = name.as_ref();
+ unsafe {
+ BNCreateAutoVariable(
+ self.get_function().handle,
+ &var.raw(),
+ &mut var_type,
+ name_c_str.as_ptr() as *const i8,
+ 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::mlil::MediumLevelILFunction;
+ /// # use binaryninja::types::Variable;
+ /// # let mlil_fun: MediumLevelILFunction = todo!();
+ /// # let mlil_var: Variable = todo!();
+ /// let instr = mlil_fun.var_refs(&mlil_var).get(0).expr();
+ /// ```
+ pub fn var_refs(&self, var: &Variable) -> Array<ILReferenceSource> {
+ let mut count = 0;
+ let refs = unsafe {
+ BNGetMediumLevelILVariableReferences(
+ self.get_function().handle,
+ &mut var.raw(),
+ &mut count,
+ )
+ };
+ assert!(!refs.is_null());
+ unsafe { Array::new(refs, count, self.to_owned()) }
+ }
+
+ /// Returns a list of variables referenced by code in the function ``func``,
+ /// of the architecture ``arch``, and at the address ``addr``. If no function is specified, references from
+ /// all functions and containing the address will be returned. If no architecture is specified, the
+ /// architecture of the function will be used.
+ /// This function is related to get_hlil_var_refs_from(), 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.
+ ///
+ /// * `addr` - virtual address to query for variable references
+ /// * `length` - optional length of query
+ /// * `arch` - optional architecture of query
+ pub fn var_refs_from(
+ &self,
+ addr: u64,
+ length: Option<u64>,
+ arch: Option<CoreArchitecture>,
+ ) -> Array<VariableReferenceSource> {
+ let function = self.get_function();
+ let arch = arch.unwrap_or_else(|| function.arch());
+ let mut count = 0;
+
+ let refs = if let Some(length) = length {
+ unsafe {
+ BNGetMediumLevelILVariableReferencesInRange(
+ function.handle,
+ arch.0,
+ addr,
+ length,
+ &mut count,
+ )
+ }
+ } else {
+ unsafe {
+ BNGetMediumLevelILVariableReferencesFrom(function.handle, arch.0, addr, &mut count)
+ }
+ };
+ assert!(!refs.is_null());
+ unsafe { Array::new(refs, count, self.to_owned()) }
+ }
}
impl ToOwned for MediumLevelILFunction {
@@ -132,3 +397,125 @@ impl core::fmt::Debug for MediumLevelILFunction {
write!(f, "<mlil func handle {:p}>", self.handle)
}
}
+
+#[derive(Clone, Debug)]
+pub struct MediumLevelILInstructionList<'a> {
+ mlil: &'a MediumLevelILFunction,
+ ptr: *mut usize,
+ instr_idxs: core::slice::Iter<'a, usize>,
+}
+
+impl Drop for MediumLevelILInstructionList<'_> {
+ fn drop(&mut self) {
+ unsafe { BNFreeILInstructionList(self.ptr) };
+ }
+}
+
+impl Iterator for MediumLevelILInstructionList<'_> {
+ type Item = MediumLevelILInstruction;
+
+ fn next(&mut self) -> Option<Self::Item> {
+ self.instr_idxs
+ .next()
+ .map(|i| self.mlil.instruction_from_instruction_idx(*i))
+ }
+}
+
+impl DoubleEndedIterator for MediumLevelILInstructionList<'_> {
+ fn next_back(&mut self) -> Option<Self::Item> {
+ self.instr_idxs
+ .next_back()
+ .map(|i| self.mlil.instruction_from_instruction_idx(*i))
+ }
+}
+
+impl ExactSizeIterator for MediumLevelILInstructionList<'_> {}
+impl core::iter::FusedIterator for MediumLevelILInstructionList<'_> {}
+
+/////////////////////////
+// FunctionGraphType
+
+pub type FunctionGraphType = binaryninjacore_sys::BNFunctionGraphType;
+
+/////////////////////////
+// ILReferenceSource
+
+pub struct ILReferenceSource {
+ mlil: Ref<MediumLevelILFunction>,
+ _func: Ref<Function>,
+ _arch: CoreArchitecture,
+ addr: u64,
+ type_: FunctionGraphType,
+ expr_id: usize,
+}
+
+impl ILReferenceSource {
+ unsafe fn from_raw(value: BNILReferenceSource, mlil: Ref<MediumLevelILFunction>) -> Self {
+ Self {
+ mlil,
+ _func: Function::from_raw(value.func),
+ _arch: CoreArchitecture::from_raw(value.arch),
+ addr: value.addr,
+ type_: value.type_,
+ expr_id: value.exprId,
+ }
+ }
+ pub fn addr(&self) -> u64 {
+ self.addr
+ }
+ pub fn graph_type(&self) -> FunctionGraphType {
+ self.type_
+ }
+ pub fn expr(&self) -> MediumLevelILInstruction {
+ self.mlil.instruction_from_idx(self.expr_id)
+ }
+}
+
+impl CoreArrayProvider for ILReferenceSource {
+ type Raw = BNILReferenceSource;
+ type Context = Ref<MediumLevelILFunction>;
+ 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> {
+ Self::from_raw(*raw, context.to_owned())
+ }
+}
+
+/////////////////////////
+// VariableReferenceSource
+
+pub struct VariableReferenceSource {
+ var: Variable,
+ source: ILReferenceSource,
+}
+
+impl VariableReferenceSource {
+ pub fn variable(&self) -> &Variable {
+ &self.var
+ }
+ pub fn source(&self) -> &ILReferenceSource {
+ &self.source
+ }
+}
+
+impl CoreArrayProvider for VariableReferenceSource {
+ type Raw = BNVariableReferenceSource;
+ type Context = Ref<MediumLevelILFunction>;
+ 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> {
+ Self {
+ var: Variable::from_raw(raw.var),
+ source: ILReferenceSource::from_raw(raw.source, context.to_owned()),
+ }
+ }
+}
diff --git a/rust/src/mlil/instruction.rs b/rust/src/mlil/instruction.rs
index bd2cc717..cb762885 100644
--- a/rust/src/mlil/instruction.rs
+++ b/rust/src/mlil/instruction.rs
@@ -1,4 +1,4 @@
-use binaryninjacore_sys::BNFromVariableIdentifier;
+use binaryninjacore_sys::BNGetDefaultIndexForMediumLevelILVariableDefinition;
use binaryninjacore_sys::BNGetMediumLevelILByIndex;
use binaryninjacore_sys::BNMediumLevelILInstruction;
use binaryninjacore_sys::BNMediumLevelILOperation;
@@ -1034,6 +1034,22 @@ impl MediumLevelILInstruction {
}
}
+ /// 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 get_split_var_for_definition(&self, var: &Variable) -> Variable {
+ let new_index = unsafe {
+ BNGetDefaultIndexForMediumLevelILVariableDefinition(
+ self.function.handle,
+ &var.raw(),
+ self.index,
+ )
+ };
+ Variable::new(var.t, new_index, var.storage)
+ }
+
fn lift_operand(&self, expr_idx: usize) -> Box<MediumLevelILLiftedInstruction> {
Box::new(self.function.lifted_instruction_from_idx(expr_idx))
}
@@ -1121,7 +1137,7 @@ fn get_raw_operation(function: &MediumLevelILFunction, idx: usize) -> BNMediumLe
}
fn get_var(id: u64) -> Variable {
- unsafe { Variable::from_raw(BNFromVariableIdentifier(id)) }
+ unsafe { Variable::from_identifier(id) }
}
fn get_var_ssa(id: u64, version: usize) -> SSAVariable {
diff --git a/rust/src/operand_iter.rs b/rust/src/operand_iter.rs
index 5f0fbd8e..0d24fd49 100644
--- a/rust/src/operand_iter.rs
+++ b/rust/src/operand_iter.rs
@@ -1,4 +1,3 @@
-use binaryninjacore_sys::BNFromVariableIdentifier;
use binaryninjacore_sys::BNGetHighLevelILByIndex;
use binaryninjacore_sys::BNGetMediumLevelILByIndex;
use binaryninjacore_sys::BNHighLevelILOperation;
@@ -215,7 +214,7 @@ impl<F: ILFunction + RefCountable> ExactSizeIterator for OperandSSAVarIter<F> {
}
pub fn get_var(id: u64) -> Variable {
- unsafe { Variable::from_raw(BNFromVariableIdentifier(id)) }
+ unsafe { Variable::from_identifier(id) }
}
pub fn get_var_ssa(id: u64, version: usize) -> SSAVariable {
diff --git a/rust/src/tags.rs b/rust/src/tags.rs
index 65680b8c..912ee81b 100644
--- a/rust/src/tags.rs
+++ b/rust/src/tags.rs
@@ -16,8 +16,10 @@
use binaryninjacore_sys::*;
+use crate::architecture::CoreArchitecture;
use crate::binaryview::BinaryView;
+use crate::function::Function;
use crate::rc::*;
use crate::string::*;
@@ -77,6 +79,21 @@ impl ToOwned for Tag {
}
}
+impl CoreArrayProvider for Tag {
+ type Raw = *mut BNTag;
+ type Context = ();
+ type Wrapped<'a> = Guard<'a, Self>;
+}
+
+unsafe impl CoreArrayProviderInner for Tag {
+ unsafe fn free(raw: *mut Self::Raw, count: usize, _context: &Self::Context) {
+ BNFreeTagList(raw, count)
+ }
+ unsafe fn wrap_raw<'a>(raw: &'a Self::Raw, context: &'a Self::Context) -> Self::Wrapped<'a> {
+ Guard::new(Self { handle: *raw }, &context)
+ }
+}
+
unsafe impl Send for Tag {}
unsafe impl Sync for Tag {}
@@ -176,3 +193,60 @@ impl ToOwned for TagType {
unsafe impl Send for TagType {}
unsafe impl Sync for TagType {}
+
+pub type TagReferenceType = BNTagReferenceType;
+
+pub struct TagReference {
+ ref_type: TagReferenceType,
+ auto_defined: bool,
+ tag: Ref<Tag>,
+ arch: CoreArchitecture,
+ func: Ref<Function>,
+ addr: u64,
+}
+
+impl TagReference {
+ unsafe fn from_borrowed_raw(value: &BNTagReference) -> Self {
+ Self {
+ ref_type: value.refType,
+ auto_defined: value.autoDefined,
+ tag: Tag { handle: value.tag }.to_owned(),
+ arch: CoreArchitecture::from_raw(value.arch),
+ func: Function { handle: value.func }.to_owned(),
+ addr: value.addr,
+ }
+ }
+ pub fn ref_type(&self) -> TagReferenceType {
+ self.ref_type
+ }
+ pub fn auto(&self) -> bool {
+ self.auto_defined
+ }
+ pub fn tag(&self) -> &Tag {
+ &self.tag
+ }
+ pub fn arch(&self) -> CoreArchitecture {
+ self.arch
+ }
+ pub fn functions(&self) -> &Function {
+ &self.func
+ }
+ pub fn address(&self) -> u64 {
+ self.addr
+ }
+}
+
+impl CoreArrayProvider for TagReference {
+ type Raw = BNTagReference;
+ type Context = ();
+ type Wrapped<'a> = Self;
+}
+
+unsafe impl CoreArrayProviderInner for TagReference {
+ unsafe fn free(raw: *mut Self::Raw, count: usize, _context: &Self::Context) {
+ BNFreeTagReferences(raw, count)
+ }
+ unsafe fn wrap_raw<'a>(raw: &'a Self::Raw, _context: &'a Self::Context) -> Self::Wrapped<'a> {
+ Self::from_borrowed_raw(raw)
+ }
+}
diff --git a/rust/src/types.rs b/rust/src/types.rs
index 8292db07..1a591801 100644
--- a/rust/src/types.rs
+++ b/rust/src/types.rs
@@ -31,15 +31,14 @@ use crate::{
use lazy_static::lazy_static;
use std::{
- borrow::Cow,
- collections::HashSet,
+ borrow::{Borrow, Cow},
+ collections::{HashMap, HashSet},
ffi::CStr,
- fmt,
- fmt::{Debug, Display, Formatter},
+ fmt::{self, Debug, Display, Formatter},
hash::{Hash, Hasher},
iter::{zip, IntoIterator},
- mem,
- mem::ManuallyDrop,
+ mem::{self, ManuallyDrop},
+ ops::Range,
os::raw::c_char,
ptr, result, slice,
sync::Mutex,
@@ -215,6 +214,8 @@ impl<T: Clone> Clone for Conf<T> {
}
}
+impl<T: Copy> Copy for Conf<T> {}
+
impl<T> From<T> for Conf<T> {
fn from(contents: T) -> Self {
Self::new(contents, max_confidence())
@@ -1378,6 +1379,9 @@ impl Variable {
storage: var.storage,
}
}
+ pub(crate) unsafe fn from_identifier(var: u64) -> Self {
+ Self::from_raw(unsafe { BNFromVariableIdentifier(var) })
+ }
pub(crate) fn raw(&self) -> BNVariable {
BNVariable {
@@ -1388,6 +1392,43 @@ impl Variable {
}
}
+impl CoreArrayProvider for Variable {
+ type Raw = BNVariable;
+ type Context = ();
+ type Wrapped<'a> = Self;
+}
+
+unsafe impl CoreArrayProviderInner for Variable {
+ unsafe fn free(raw: *mut Self::Raw, _count: usize, _context: &Self::Context) {
+ BNFreeVariableList(raw)
+ }
+ unsafe fn wrap_raw<'a>(raw: &'a Self::Raw, _context: &'a Self::Context) -> Self::Wrapped<'a> {
+ Variable::from_raw(*raw)
+ }
+}
+
+// Name, Variable and Type
+impl CoreArrayProvider for (&str, Variable, &Type) {
+ type Raw = BNVariableNameAndType;
+ type Context = ();
+ type Wrapped<'a> = (&'a str, Variable, &'a Type) where Self: 'a;
+}
+
+unsafe impl CoreArrayProviderInner for (&str, Variable, &Type) {
+ unsafe fn free(raw: *mut Self::Raw, count: usize, _context: &Self::Context) {
+ BNFreeVariableNameAndTypeList(raw, count)
+ }
+ unsafe fn wrap_raw<'a>(
+ raw: &'a Self::Raw,
+ _context: &'a Self::Context,
+ ) -> (&'a str, Variable, &'a Type) {
+ let name = CStr::from_ptr(raw.name).to_str().unwrap();
+ let var = Variable::from_raw(raw.var);
+ let var_type = core::mem::transmute(&raw.type_);
+ (name, var, var_type)
+ }
+}
+
//////////////
// SSAVariable
@@ -1805,7 +1846,7 @@ impl StructureBuilder {
&self,
members: impl IntoIterator<Item = (T, S)>,
) -> &Self {
- for (t, name) in members.into_iter() {
+ for (t, name) in members {
self.append(t, name, MemberAccess::NoAccess, MemberScope::NoScope);
}
self
@@ -2496,7 +2537,7 @@ pub struct NameAndType(pub(crate) BNNameAndType);
impl NameAndType {
pub(crate) unsafe fn from_raw(raw: &BNNameAndType) -> Self {
- Self ( *raw )
+ Self(*raw)
}
}
@@ -2851,3 +2892,818 @@ impl ConstantData {
// mem::transmute(raw)
// }
// }
+
+/////////////////////////
+// ValueRange
+
+#[repr(transparent)]
+#[derive(Copy, Clone, Debug)]
+pub struct ValueRange<T> {
+ raw: BNValueRange,
+ _t: core::marker::PhantomData<T>,
+}
+
+impl<T> ValueRange<T> {
+ fn from_raw(value: BNValueRange) -> Self {
+ Self {
+ raw: value,
+ _t: core::marker::PhantomData,
+ }
+ }
+ fn into_raw(self) -> BNValueRange {
+ self.raw
+ }
+}
+
+impl IntoIterator for ValueRange<u64> {
+ type Item = u64;
+ type IntoIter = core::iter::StepBy<Range<u64>>;
+
+ fn into_iter(self) -> Self::IntoIter {
+ (self.raw.start..self.raw.end).step_by(self.raw.step.try_into().unwrap())
+ }
+}
+impl IntoIterator for ValueRange<i64> {
+ type Item = i64;
+ type IntoIter = core::iter::StepBy<Range<i64>>;
+
+ fn into_iter(self) -> Self::IntoIter {
+ (self.raw.start as i64..self.raw.end as i64).step_by(self.raw.step.try_into().unwrap())
+ }
+}
+
+/////////////////////////
+// PossibleValueSet
+
+#[derive(Clone, Debug)]
+pub enum PossibleValueSet {
+ UndeterminedValue,
+ EntryValue {
+ reg: i64,
+ },
+ ConstantValue {
+ value: i64,
+ },
+ ConstantPointerValue {
+ value: i64,
+ },
+ ExternalPointerValue,
+ StackFrameOffset {
+ offset: i64,
+ },
+ ReturnAddressValue,
+ ImportedAddressValue,
+ SignedRangeValue {
+ offset: i64,
+ ranges: Vec<ValueRange<i64>>,
+ },
+ UnsignedRangeValue {
+ offset: i64,
+ ranges: Vec<ValueRange<u64>>,
+ },
+ LookupTableValue {
+ tables: Vec<LookupTableEntry>,
+ },
+ InSetOfValues {
+ values: HashSet<i64>,
+ },
+ NotInSetOfValues {
+ values: HashSet<i64>,
+ },
+ ConstantDataValue {
+ value_type: ConstantDataType,
+ value: i64,
+ },
+}
+
+#[derive(Copy, Clone, Debug)]
+pub enum ConstantDataType {
+ Value,
+ ZeroExtend,
+ SignExtend,
+ Aggregate,
+}
+
+impl PossibleValueSet {
+ pub(crate) unsafe fn from_raw(value: BNPossibleValueSet) -> Self {
+ unsafe fn from_range<T>(value: BNPossibleValueSet) -> Vec<ValueRange<T>> {
+ core::slice::from_raw_parts(value.ranges, value.count)
+ .iter()
+ .copied()
+ .map(|range| ValueRange::from_raw(range))
+ .collect()
+ }
+ let from_sets = |value: BNPossibleValueSet| {
+ unsafe { core::slice::from_raw_parts(value.valueSet, value.count) }
+ .iter()
+ .copied()
+ .collect()
+ };
+ use BNRegisterValueType::*;
+ match value.state {
+ UndeterminedValue => Self::UndeterminedValue,
+ EntryValue => Self::EntryValue { reg: value.value },
+ ConstantValue => Self::ConstantValue { value: value.value },
+ ConstantPointerValue => Self::ConstantPointerValue { value: value.value },
+ StackFrameOffset => Self::StackFrameOffset {
+ offset: value.value,
+ },
+ ConstantDataValue => Self::ConstantDataValue {
+ value_type: ConstantDataType::Value,
+ value: value.value,
+ },
+ ConstantDataZeroExtendValue => Self::ConstantDataValue {
+ value_type: ConstantDataType::ZeroExtend,
+ value: value.value,
+ },
+ ConstantDataSignExtendValue => Self::ConstantDataValue {
+ value_type: ConstantDataType::SignExtend,
+ value: value.value,
+ },
+ ConstantDataAggregateValue => Self::ConstantDataValue {
+ value_type: ConstantDataType::Aggregate,
+ value: value.value,
+ },
+ SignedRangeValue => Self::SignedRangeValue {
+ offset: value.value,
+ ranges: from_range(value),
+ },
+ UnsignedRangeValue => Self::UnsignedRangeValue {
+ offset: value.value,
+ ranges: from_range(value),
+ },
+ LookupTableValue => {
+ let raw_tables = unsafe { core::slice::from_raw_parts(value.table, value.count) };
+ let raw_from_tables = |i: &BNLookupTableEntry| unsafe {
+ core::slice::from_raw_parts(i.fromValues, i.fromCount)
+ };
+ let tables = raw_tables
+ .iter()
+ .map(|table| LookupTableEntry {
+ from_values: raw_from_tables(table).to_vec(),
+ to_value: table.toValue,
+ })
+ .collect();
+ Self::LookupTableValue { tables }
+ }
+ NotInSetOfValues => Self::NotInSetOfValues {
+ values: from_sets(value),
+ },
+ InSetOfValues => Self::InSetOfValues {
+ values: from_sets(value),
+ },
+ ImportedAddressValue => Self::ImportedAddressValue,
+ ReturnAddressValue => Self::ReturnAddressValue,
+ ExternalPointerValue => Self::ExternalPointerValue,
+ }
+ }
+ pub(crate) fn into_raw(self) -> PossibleValueSetRaw {
+ let mut raw: BNPossibleValueSet = unsafe { core::mem::zeroed() };
+ // set the state field
+ raw.state = self.value_type().into_raw_value();
+ // set all other fields
+ match self {
+ PossibleValueSet::UndeterminedValue
+ | PossibleValueSet::ExternalPointerValue
+ | PossibleValueSet::ReturnAddressValue
+ | PossibleValueSet::ImportedAddressValue => {}
+ PossibleValueSet::EntryValue { reg: value }
+ | PossibleValueSet::ConstantValue { value }
+ | PossibleValueSet::ConstantPointerValue { value }
+ | PossibleValueSet::ConstantDataValue { value, .. }
+ | PossibleValueSet::StackFrameOffset { offset: value } => raw.value = value,
+ PossibleValueSet::NotInSetOfValues { values }
+ | PossibleValueSet::InSetOfValues { values } => {
+ let values = Box::leak(values.into_iter().collect());
+ raw.valueSet = values.as_mut_ptr();
+ raw.count = values.len();
+ }
+ PossibleValueSet::SignedRangeValue { offset, ranges } => {
+ let ranges = Box::leak(ranges.into_iter().map(|x| x.into_raw()).collect());
+ raw.value = offset;
+ raw.ranges = ranges.as_mut_ptr();
+ raw.count = ranges.len();
+ }
+ PossibleValueSet::UnsignedRangeValue { offset, ranges } => {
+ let ranges = Box::leak(ranges.into_iter().map(|x| x.into_raw()).collect());
+ raw.value = offset;
+ raw.ranges = ranges.as_mut_ptr();
+ raw.count = ranges.len();
+ }
+ PossibleValueSet::LookupTableValue { tables } => {
+ let tables = Box::leak(tables.into_iter().map(|table| table.into_raw()).collect());
+ // SAFETY: BNLookupTableEntry and LookupTableEntryRaw are transparent
+ raw.table = tables.as_mut_ptr() as *mut BNLookupTableEntry;
+ raw.count = tables.len();
+ }
+ }
+ PossibleValueSetRaw(raw)
+ }
+
+ pub fn value_type(&self) -> RegisterValueType {
+ use RegisterValueType::*;
+ match self {
+ PossibleValueSet::UndeterminedValue => UndeterminedValue,
+ PossibleValueSet::EntryValue { .. } => EntryValue,
+ PossibleValueSet::ConstantValue { .. } => ConstantValue,
+ PossibleValueSet::ConstantPointerValue { .. } => ConstantPointerValue,
+ PossibleValueSet::ExternalPointerValue => ExternalPointerValue,
+ PossibleValueSet::StackFrameOffset { .. } => StackFrameOffset,
+ PossibleValueSet::ReturnAddressValue => ReturnAddressValue,
+ PossibleValueSet::ImportedAddressValue => ImportedAddressValue,
+ PossibleValueSet::SignedRangeValue { .. } => SignedRangeValue,
+ PossibleValueSet::UnsignedRangeValue { .. } => UnsignedRangeValue,
+ PossibleValueSet::LookupTableValue { .. } => LookupTableValue,
+ PossibleValueSet::InSetOfValues { .. } => InSetOfValues,
+ PossibleValueSet::NotInSetOfValues { .. } => NotInSetOfValues,
+ PossibleValueSet::ConstantDataValue {
+ value_type: ConstantDataType::Value,
+ ..
+ } => ConstantDataValue,
+ PossibleValueSet::ConstantDataValue {
+ value_type: ConstantDataType::ZeroExtend,
+ ..
+ } => ConstantDataZeroExtendValue,
+ PossibleValueSet::ConstantDataValue {
+ value_type: ConstantDataType::SignExtend,
+ ..
+ } => ConstantDataSignExtendValue,
+ PossibleValueSet::ConstantDataValue {
+ value_type: ConstantDataType::Aggregate,
+ ..
+ } => ConstantDataAggregateValue,
+ }
+ }
+}
+
+/// The owned version of the BNPossibleValueSet
+#[repr(transparent)]
+pub(crate) struct PossibleValueSetRaw(BNPossibleValueSet);
+
+impl PossibleValueSetRaw {
+ pub fn as_ffi(&self) -> &BNPossibleValueSet {
+ &self.0
+ }
+}
+
+impl Drop for PossibleValueSetRaw {
+ fn drop(&mut self) {
+ use BNRegisterValueType::*;
+ match self.0.state {
+ UndeterminedValue
+ | ExternalPointerValue
+ | ReturnAddressValue
+ | ImportedAddressValue
+ | EntryValue
+ | ConstantValue
+ | ConstantPointerValue
+ | StackFrameOffset
+ | ConstantDataValue
+ | ConstantDataZeroExtendValue
+ | ConstantDataSignExtendValue
+ | ConstantDataAggregateValue => {}
+ InSetOfValues | NotInSetOfValues => {
+ let _values: Box<[i64]> = unsafe {
+ Box::from_raw(ptr::slice_from_raw_parts_mut(self.0.valueSet, self.0.count))
+ };
+ }
+ SignedRangeValue | UnsignedRangeValue => {
+ let _ranges: Box<[BNValueRange]> = unsafe {
+ Box::from_raw(ptr::slice_from_raw_parts_mut(self.0.ranges, self.0.count))
+ };
+ }
+ LookupTableValue => {
+ // SAFETY: LookupTableEntryRaw and BNLookupTableEntry can be safely transmuted
+ let table_ptr = self.0.table as *mut LookupTableEntryRaw;
+ let _table: Box<[LookupTableEntryRaw]> = unsafe {
+ Box::from_raw(ptr::slice_from_raw_parts_mut(table_ptr, self.0.count))
+ };
+ }
+ }
+ }
+}
+
+/////////////////////////
+// LookupTableEntry
+
+#[derive(Clone, Debug)]
+pub struct LookupTableEntry {
+ pub from_values: Vec<i64>,
+ pub to_value: i64,
+}
+
+impl LookupTableEntry {
+ fn into_raw(self) -> LookupTableEntryRaw {
+ let from_value = Box::leak(self.from_values.into_boxed_slice());
+ LookupTableEntryRaw(BNLookupTableEntry {
+ toValue: self.to_value,
+ fromValues: from_value.as_mut_ptr(),
+ fromCount: from_value.len(),
+ })
+ }
+}
+
+/// The owned version of the BNLookupTableEntry
+#[repr(transparent)]
+struct LookupTableEntryRaw(BNLookupTableEntry);
+impl Drop for LookupTableEntryRaw {
+ fn drop(&mut self) {
+ let _from_value: Box<[i64]> = unsafe {
+ Box::from_raw(ptr::slice_from_raw_parts_mut(
+ self.0.fromValues,
+ self.0.fromCount,
+ ))
+ };
+ }
+}
+
+/////////////////////////
+// ArchAndAddr
+
+#[derive(Copy, Clone, Eq, Hash, PartialEq)]
+pub struct ArchAndAddr {
+ pub arch: CoreArchitecture,
+ pub address: u64,
+}
+
+/////////////////////////
+// UserVariableValues
+
+pub struct UserVariableValues {
+ pub(crate) vars: *const [BNUserVariableValue],
+}
+
+impl UserVariableValues {
+ pub fn into_hashmap(self) -> HashMap<Variable, HashMap<ArchAndAddr, PossibleValueSet>> {
+ let mut result: HashMap<Variable, HashMap<ArchAndAddr, PossibleValueSet>> = HashMap::new();
+ for (var, def_site, possible_val) in self.all() {
+ result
+ .entry(var)
+ .or_default()
+ .entry(def_site)
+ .or_insert(possible_val);
+ }
+ result
+ }
+ pub fn all(&self) -> impl Iterator<Item = (Variable, ArchAndAddr, PossibleValueSet)> {
+ unsafe { &*self.vars }.iter().map(|var_val| {
+ let var = unsafe { Variable::from_raw(var_val.var) };
+ let def_site = ArchAndAddr {
+ arch: unsafe { CoreArchitecture::from_raw(var_val.defSite.arch) },
+ address: var_val.defSite.address,
+ };
+ let possible_val = unsafe { PossibleValueSet::from_raw(var_val.value) };
+ (var, def_site, possible_val)
+ })
+ }
+ pub fn values_from_variable(
+ &self,
+ var: Variable,
+ ) -> impl Iterator<Item = (ArchAndAddr, PossibleValueSet)> {
+ self.all()
+ .filter(move |(t_var, _, _)| t_var == &var)
+ .map(|(_var, def_site, possible_val)| (def_site, possible_val))
+ }
+}
+
+impl Drop for UserVariableValues {
+ fn drop(&mut self) {
+ unsafe { BNFreeUserVariableValues(self.vars as *mut BNUserVariableValue) };
+ }
+}
+
+/////////////////////////
+// ConstantReference
+
+#[derive(Copy, Clone, Eq, Hash, PartialEq)]
+pub struct ConstantReference {
+ pub value: i64,
+ pub size: usize,
+ pub pointer: bool,
+ pub intermediate: bool,
+}
+
+impl ConstantReference {
+ pub fn from_raw(value: BNConstantReference) -> Self {
+ Self {
+ value: value.value,
+ size: value.size,
+ pointer: value.pointer,
+ intermediate: value.intermediate,
+ }
+ }
+ pub fn into_raw(self) -> BNConstantReference {
+ BNConstantReference {
+ value: self.value,
+ size: self.size,
+ pointer: self.pointer,
+ intermediate: self.intermediate,
+ }
+ }
+}
+
+impl CoreArrayProvider for ConstantReference {
+ type Raw = BNConstantReference;
+ type Context = ();
+ type Wrapped<'a> = Self;
+}
+
+unsafe impl CoreArrayProviderInner for ConstantReference {
+ unsafe fn free(raw: *mut Self::Raw, _count: usize, _context: &Self::Context) {
+ BNFreeConstantReferenceList(raw)
+ }
+ unsafe fn wrap_raw<'a>(raw: &'a Self::Raw, _context: &'a Self::Context) -> Self::Wrapped<'a> {
+ Self::from_raw(*raw)
+ }
+}
+
+/////////////////////////
+// IndirectBranchInfo
+
+pub struct IndirectBranchInfo {
+ pub source_arch: CoreArchitecture,
+ pub source_addr: u64,
+ pub dest_arch: CoreArchitecture,
+ pub dest_addr: u64,
+ pub auto_defined: bool,
+}
+
+impl IndirectBranchInfo {
+ pub fn from_raw(value: BNIndirectBranchInfo) -> Self {
+ Self {
+ source_arch: unsafe { CoreArchitecture::from_raw(value.sourceArch) },
+ source_addr: value.sourceAddr,
+ dest_arch: unsafe { CoreArchitecture::from_raw(value.destArch) },
+ dest_addr: value.destAddr,
+ auto_defined: value.autoDefined,
+ }
+ }
+ pub fn into_raw(self) -> BNIndirectBranchInfo {
+ BNIndirectBranchInfo {
+ sourceArch: self.source_arch.0,
+ sourceAddr: self.source_addr,
+ destArch: self.dest_arch.0,
+ destAddr: self.dest_addr,
+ autoDefined: self.auto_defined,
+ }
+ }
+}
+
+impl CoreArrayProvider for IndirectBranchInfo {
+ type Raw = BNIndirectBranchInfo;
+ type Context = ();
+ type Wrapped<'a> = Self;
+}
+
+unsafe impl CoreArrayProviderInner for IndirectBranchInfo {
+ unsafe fn free(raw: *mut Self::Raw, _count: usize, _context: &Self::Context) {
+ BNFreeIndirectBranchList(raw)
+ }
+ unsafe fn wrap_raw<'a>(raw: &'a Self::Raw, _context: &'a Self::Context) -> Self::Wrapped<'a> {
+ Self::from_raw(*raw)
+ }
+}
+
+/////////////////////////
+// HighlightStandardColor
+
+#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)]
+pub enum HighlightStandardColor {
+ //NoHighlightColor,
+ BlueHighlightColor,
+ GreenHighlightColor,
+ CyanHighlightColor,
+ RedHighlightColor,
+ MagentaHighlightColor,
+ YellowHighlightColor,
+ OrangeHighlightColor,
+ WhiteHighlightColor,
+ BlackHighlightColor,
+}
+
+impl HighlightStandardColor {
+ pub fn from_raw(value: BNHighlightStandardColor) -> Option<Self> {
+ Some(match value {
+ BNHighlightStandardColor::NoHighlightColor => return None,
+ BNHighlightStandardColor::BlueHighlightColor => Self::BlueHighlightColor,
+ BNHighlightStandardColor::GreenHighlightColor => Self::GreenHighlightColor,
+ BNHighlightStandardColor::CyanHighlightColor => Self::CyanHighlightColor,
+ BNHighlightStandardColor::RedHighlightColor => Self::RedHighlightColor,
+ BNHighlightStandardColor::MagentaHighlightColor => Self::MagentaHighlightColor,
+ BNHighlightStandardColor::YellowHighlightColor => Self::YellowHighlightColor,
+ BNHighlightStandardColor::OrangeHighlightColor => Self::OrangeHighlightColor,
+ BNHighlightStandardColor::WhiteHighlightColor => Self::WhiteHighlightColor,
+ BNHighlightStandardColor::BlackHighlightColor => Self::BlackHighlightColor,
+ })
+ }
+ pub fn into_raw(self) -> BNHighlightStandardColor {
+ match self {
+ //Self::NoHighlightColor => BNHighlightStandardColor::NoHighlightColor,
+ Self::BlueHighlightColor => BNHighlightStandardColor::BlueHighlightColor,
+ Self::GreenHighlightColor => BNHighlightStandardColor::GreenHighlightColor,
+ Self::CyanHighlightColor => BNHighlightStandardColor::CyanHighlightColor,
+ Self::RedHighlightColor => BNHighlightStandardColor::RedHighlightColor,
+ Self::MagentaHighlightColor => BNHighlightStandardColor::MagentaHighlightColor,
+ Self::YellowHighlightColor => BNHighlightStandardColor::YellowHighlightColor,
+ Self::OrangeHighlightColor => BNHighlightStandardColor::OrangeHighlightColor,
+ Self::WhiteHighlightColor => BNHighlightStandardColor::WhiteHighlightColor,
+ Self::BlackHighlightColor => BNHighlightStandardColor::BlackHighlightColor,
+ }
+ }
+}
+
+/////////////////////////
+// HighlightColor
+
+#[derive(Debug, Copy, Clone)]
+pub enum HighlightColor {
+ NoHighlightColor {
+ alpha: u8,
+ },
+ StandardHighlightColor {
+ color: HighlightStandardColor,
+ alpha: u8,
+ },
+ MixedHighlightColor {
+ color: HighlightStandardColor,
+ mix_color: HighlightStandardColor,
+ mix: u8,
+ alpha: u8,
+ },
+ CustomHighlightColor {
+ r: u8,
+ g: u8,
+ b: u8,
+ alpha: u8,
+ },
+}
+
+impl HighlightColor {
+ pub fn from_raw(raw: BNHighlightColor) -> Self {
+ const HIGHLIGHT_COLOR: u32 = BNHighlightColorStyle::StandardHighlightColor as u32;
+ const MIXED_HIGHLIGHT_COLOR: u32 = BNHighlightColorStyle::MixedHighlightColor as u32;
+ const CUSTOM_HIGHLIHGT_COLOR: u32 = BNHighlightColorStyle::CustomHighlightColor as u32;
+ match raw.style as u32 {
+ HIGHLIGHT_COLOR => {
+ let Some(color) = HighlightStandardColor::from_raw(raw.color) else {
+ // StandardHighlightColor with NoHighlightColor, is no color
+ return Self::NoHighlightColor { alpha: raw.alpha };
+ };
+ Self::StandardHighlightColor {
+ color,
+ alpha: raw.alpha,
+ }
+ }
+ MIXED_HIGHLIGHT_COLOR => {
+ let Some(color) = HighlightStandardColor::from_raw(raw.color) else {
+ panic!("Highlight mixed color with no color");
+ };
+ let Some(mix_color) = HighlightStandardColor::from_raw(raw.mixColor) else {
+ panic!("Highlight mixed color with no mix_color");
+ };
+ Self::MixedHighlightColor {
+ color,
+ mix_color,
+ mix: raw.mix,
+ alpha: raw.alpha,
+ }
+ }
+ CUSTOM_HIGHLIHGT_COLOR => Self::CustomHighlightColor {
+ r: raw.r,
+ g: raw.g,
+ b: raw.b,
+ alpha: raw.alpha,
+ },
+ // other color style is just no color
+ _ => Self::NoHighlightColor { alpha: u8::MAX },
+ }
+ }
+
+ pub fn into_raw(self) -> BNHighlightColor {
+ let zeroed: BNHighlightColor = unsafe { core::mem::zeroed() };
+ match self {
+ Self::NoHighlightColor { alpha } => BNHighlightColor {
+ style: BNHighlightColorStyle::StandardHighlightColor,
+ color: BNHighlightStandardColor::NoHighlightColor,
+ alpha,
+ ..zeroed
+ },
+ Self::StandardHighlightColor { color, alpha } => BNHighlightColor {
+ style: BNHighlightColorStyle::StandardHighlightColor,
+ color: color.into_raw(),
+ alpha,
+ ..zeroed
+ },
+ Self::MixedHighlightColor {
+ color,
+ mix_color,
+ mix,
+ alpha,
+ } => BNHighlightColor {
+ color: color.into_raw(),
+ mixColor: mix_color.into_raw(),
+ mix,
+ alpha,
+ ..zeroed
+ },
+ Self::CustomHighlightColor { r, g, b, alpha } => BNHighlightColor {
+ r,
+ g,
+ b,
+ alpha,
+ ..zeroed
+ },
+ }
+ }
+}
+
+/////////////////////////
+// IntegerDisplayType
+
+pub type IntegerDisplayType = binaryninjacore_sys::BNIntegerDisplayType;
+
+/////////////////////////
+// StackVariableReference
+
+#[derive(Debug, Clone)]
+pub struct StackVariableReference {
+ _source_operand: u32,
+ var_type: Conf<Ref<Type>>,
+ name: BnString,
+ var: Variable,
+ offset: i64,
+ size: usize,
+}
+
+impl StackVariableReference {
+ pub fn from_raw(value: BNStackVariableReference) -> Self {
+ let var_type = Conf::new(
+ unsafe { Type::ref_from_raw(value.type_) },
+ value.typeConfidence,
+ );
+ let name = unsafe { BnString::from_raw(value.name) };
+ let var = unsafe { Variable::from_identifier(value.varIdentifier) };
+ let offset = value.referencedOffset;
+ let size = value.size;
+ Self {
+ _source_operand: value.sourceOperand,
+ var_type,
+ name,
+ var,
+ offset,
+ size,
+ }
+ }
+ pub fn variable(&self) -> &Variable {
+ &self.var
+ }
+ pub fn variable_type(&self) -> Conf<&Type> {
+ self.var_type.as_ref()
+ }
+ pub fn name(&self) -> &str {
+ self.name.as_str()
+ }
+ pub fn offset(&self) -> i64 {
+ self.offset
+ }
+ pub fn size(&self) -> usize {
+ self.size
+ }
+}
+
+impl CoreArrayProvider for StackVariableReference {
+ type Raw = BNStackVariableReference;
+ type Context = ();
+ type Wrapped<'a> = Guard<'a, Self>;
+}
+
+unsafe impl CoreArrayProviderInner for StackVariableReference {
+ unsafe fn free(raw: *mut Self::Raw, count: usize, _context: &Self::Context) {
+ BNFreeStackVariableReferenceList(raw, count)
+ }
+ unsafe fn wrap_raw<'a>(raw: &'a Self::Raw, context: &'a Self::Context) -> Self::Wrapped<'a> {
+ Guard::new(Self::from_raw(*raw), context)
+ }
+}
+
+/////////////////////////
+// RegisterStackAdjustment
+
+#[derive(Debug, Copy, Clone)]
+pub struct RegisterStackAdjustment<A: Architecture> {
+ reg_id: u32,
+ adjustment: Conf<i32>,
+ arch: A::Handle,
+}
+
+impl<A: Architecture> RegisterStackAdjustment<A> {
+ pub(crate) unsafe fn from_raw(value: BNRegisterStackAdjustment, arch: A::Handle) -> Self {
+ RegisterStackAdjustment {
+ reg_id: value.regStack,
+ adjustment: Conf::new(value.adjustment, value.confidence),
+ arch,
+ }
+ }
+ pub(crate) fn into_raw(self) -> BNRegisterStackAdjustment {
+ BNRegisterStackAdjustment {
+ regStack: self.reg_id,
+ adjustment: self.adjustment.contents,
+ confidence: self.adjustment.confidence,
+ }
+ }
+ pub fn new<I>(reg_id: u32, adjustment: I, arch_handle: A::Handle) -> Self
+ where
+ I: Into<Conf<i32>>,
+ {
+ Self {
+ reg_id,
+ adjustment: adjustment.into(),
+ arch: arch_handle,
+ }
+ }
+ pub const fn register_id(&self) -> u32 {
+ self.reg_id
+ }
+ pub fn register(&self) -> A::Register {
+ self.arch.borrow().register_from_id(self.reg_id).unwrap()
+ }
+}
+
+impl<A: Architecture> CoreArrayProvider for RegisterStackAdjustment<A> {
+ type Raw = BNRegisterStackAdjustment;
+ type Context = A::Handle;
+ type Wrapped<'a> = Self;
+}
+
+unsafe impl<A: Architecture> CoreArrayProviderInner for RegisterStackAdjustment<A> {
+ unsafe fn free(raw: *mut Self::Raw, _count: usize, _context: &Self::Context) {
+ BNFreeRegisterStackAdjustments(raw)
+ }
+ unsafe fn wrap_raw<'a>(raw: &'a Self::Raw, context: &'a Self::Context) -> Self::Wrapped<'a> {
+ Self::from_raw(*raw, context.clone())
+ }
+}
+
+/////////////////////////
+// RegisterStackAdjustment
+
+// NOTE only exists as part of an Array, never owned
+pub struct MergedVariable {
+ target: Variable,
+ // droped by the CoreArrayProviderInner::free
+ sources: ManuallyDrop<Array<Variable>>,
+}
+
+impl MergedVariable {
+ pub fn target(&self) -> Variable {
+ self.target
+ }
+ pub fn sources(&self) -> &Array<Variable> {
+ &self.sources
+ }
+}
+
+impl CoreArrayProvider for MergedVariable {
+ type Raw = BNMergedVariable;
+ type Context = ();
+ type Wrapped<'a> = Self;
+}
+
+unsafe impl CoreArrayProviderInner for MergedVariable {
+ unsafe fn free(raw: *mut Self::Raw, count: usize, _context: &Self::Context) {
+ BNFreeMergedVariableList(raw, count)
+ }
+ unsafe fn wrap_raw<'a>(raw: &'a Self::Raw, _context: &'a Self::Context) -> Self::Wrapped<'a> {
+ Self {
+ target: Variable::from_raw(raw.target),
+ sources: ManuallyDrop::new(Array::new(raw.sources, raw.sourceCount, ())),
+ }
+ }
+}
+
+/////////////////////////
+// UnresolvedIndirectBranches
+
+// NOTE only exists as part of an Array, never owned
+pub struct UnresolvedIndirectBranches(u64);
+
+impl UnresolvedIndirectBranches {
+ pub fn address(&self) -> u64 {
+ self.0
+ }
+}
+
+impl CoreArrayProvider for UnresolvedIndirectBranches {
+ type Raw = u64;
+ type Context = ();
+ type Wrapped<'a> = Self;
+}
+
+unsafe impl CoreArrayProviderInner for UnresolvedIndirectBranches {
+ unsafe fn free(raw: *mut Self::Raw, _count: usize, _context: &Self::Context) {
+ BNFreeAddressList(raw)
+ }
+ unsafe fn wrap_raw<'a>(raw: &'a Self::Raw, _context: &'a Self::Context) -> Self::Wrapped<'a> {
+ Self(*raw)
+ }
+}