summaryrefslogtreecommitdiff
path: root/rust/src/function.rs
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/function.rs
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/function.rs')
-rw-r--r--rust/src/function.rs1854
1 files changed, 1841 insertions, 13 deletions
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),
+ }
+ }
+}