summaryrefslogtreecommitdiff
path: root/rust/src/binary_view.rs
diff options
context:
space:
mode:
Diffstat (limited to 'rust/src/binary_view.rs')
-rw-r--r--rust/src/binary_view.rs2076
1 files changed, 2076 insertions, 0 deletions
diff --git a/rust/src/binary_view.rs b/rust/src/binary_view.rs
new file mode 100644
index 00000000..b508fa8b
--- /dev/null
+++ b/rust/src/binary_view.rs
@@ -0,0 +1,2076 @@
+// Copyright 2021-2024 Vector 35 Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+//! A view on binary data and queryable interface of a binary file.
+//!
+//! One key job of BinaryView is file format parsing which allows Binary Ninja to read, write,
+//! insert, remove portions of the file given a virtual address.
+//!
+//! For the purposes of this documentation we define a virtual address as the memory address that
+//! the various pieces of the physical file will be loaded at.
+//! TODO : Mirror the Python docs for this
+
+use binaryninjacore_sys::*;
+
+use crate::architecture::{Architecture, CoreArchitecture};
+use crate::basic_block::BasicBlock;
+use crate::component::{Component, IntoComponentGuid};
+use crate::confidence::Conf;
+use crate::data_buffer::DataBuffer;
+use crate::debuginfo::DebugInfo;
+use crate::external_library::{ExternalLibrary, ExternalLocation};
+use crate::file_accessor::FileAccessor;
+use crate::file_metadata::FileMetadata;
+use crate::flowgraph::FlowGraph;
+use crate::function::{Function, NativeBlock};
+use crate::linear_view::{LinearDisassemblyLine, LinearViewCursor};
+use crate::metadata::Metadata;
+use crate::platform::Platform;
+use crate::progress::{NoProgressCallback, ProgressCallback};
+use crate::project::file::ProjectFile;
+use crate::rc::*;
+use crate::references::{CodeReference, DataReference};
+use crate::relocation::Relocation;
+use crate::section::{Section, SectionBuilder};
+use crate::segment::{Segment, SegmentBuilder};
+use crate::settings::Settings;
+use crate::string::*;
+use crate::symbol::{Symbol, SymbolType};
+use crate::tags::{Tag, TagType};
+use crate::type_container::TypeContainer;
+use crate::type_library::TypeLibrary;
+use crate::types::{
+ NamedTypeReference, QualifiedName, QualifiedNameAndType, QualifiedNameTypeAndId, Type,
+};
+use crate::variable::DataVariable;
+use crate::Endianness;
+use std::collections::HashMap;
+use std::ffi::{c_char, c_void};
+use std::ops::Range;
+use std::path::Path;
+use std::ptr::NonNull;
+use std::{result, slice};
+// TODO : general reorg of modules related to bv
+
+pub type Result<R> = result::Result<R, ()>;
+pub type BinaryViewEventType = BNBinaryViewEventType;
+pub type AnalysisState = BNAnalysisState;
+pub type ModificationStatus = BNModificationStatus;
+
+#[allow(clippy::len_without_is_empty)]
+pub trait BinaryViewBase: AsRef<BinaryView> {
+ fn read(&self, _buf: &mut [u8], _offset: u64) -> usize {
+ 0
+ }
+
+ fn write(&self, _offset: u64, _data: &[u8]) -> usize {
+ 0
+ }
+
+ fn insert(&self, _offset: u64, _data: &[u8]) -> usize {
+ 0
+ }
+
+ fn remove(&self, _offset: u64, _len: usize) -> usize {
+ 0
+ }
+
+ fn offset_valid(&self, offset: u64) -> bool {
+ let mut buf = [0u8; 1];
+
+ // don't use self.read so that if segments were used we
+ // check against those as well
+ self.as_ref().read(&mut buf[..], offset) == buf.len()
+ }
+
+ fn offset_readable(&self, offset: u64) -> bool {
+ self.offset_valid(offset)
+ }
+
+ fn offset_writable(&self, offset: u64) -> bool {
+ self.offset_valid(offset)
+ }
+
+ fn offset_executable(&self, offset: u64) -> bool {
+ self.offset_valid(offset)
+ }
+
+ fn offset_backed_by_file(&self, offset: u64) -> bool {
+ self.offset_valid(offset)
+ }
+
+ fn next_valid_offset_after(&self, offset: u64) -> u64 {
+ let start = self.as_ref().start();
+
+ if offset < start {
+ start
+ } else {
+ offset
+ }
+ }
+
+ #[allow(unused)]
+ fn modification_status(&self, offset: u64) -> ModificationStatus {
+ ModificationStatus::Original
+ }
+
+ fn start(&self) -> u64 {
+ 0
+ }
+
+ fn len(&self) -> u64 {
+ 0
+ }
+
+ fn executable(&self) -> bool {
+ true
+ }
+
+ fn relocatable(&self) -> bool {
+ true
+ }
+
+ fn entry_point(&self) -> u64;
+ fn default_endianness(&self) -> Endianness;
+ fn address_size(&self) -> usize;
+
+ fn save(&self) -> bool {
+ self.as_ref()
+ .parent_view()
+ .map(|view| view.save())
+ .unwrap_or(false)
+ }
+}
+
+#[derive(Debug, Clone)]
+pub struct ActiveAnalysisInfo {
+ pub func: Ref<Function>,
+ pub analysis_time: u64,
+ pub update_count: usize,
+ pub submit_count: usize,
+}
+
+#[derive(Debug, Clone)]
+pub struct AnalysisInfo {
+ pub state: AnalysisState,
+ pub analysis_time: u64,
+ pub active_info: Vec<ActiveAnalysisInfo>,
+}
+
+#[derive(Debug, Clone)]
+pub struct AnalysisProgress {
+ pub state: AnalysisState,
+ pub count: usize,
+ pub total: usize,
+}
+
+pub trait BinaryViewExt: BinaryViewBase {
+ fn file(&self) -> Ref<FileMetadata> {
+ unsafe {
+ let raw = BNGetFileForView(self.as_ref().handle);
+ FileMetadata::ref_from_raw(raw)
+ }
+ }
+
+ fn type_name(&self) -> BnString {
+ let ptr: *mut c_char = unsafe { BNGetViewType(self.as_ref().handle) };
+ unsafe { BnString::from_raw(ptr) }
+ }
+
+ fn parent_view(&self) -> Option<Ref<BinaryView>> {
+ let raw_view_ptr = unsafe { BNGetParentView(self.as_ref().handle) };
+ match raw_view_ptr.is_null() {
+ false => Some(unsafe { BinaryView::ref_from_raw(raw_view_ptr) }),
+ true => None,
+ }
+ }
+
+ fn raw_view(&self) -> Option<Ref<BinaryView>> {
+ self.file().view_of_type("Raw")
+ }
+
+ fn view_type(&self) -> BnString {
+ let ptr: *mut c_char = unsafe { BNGetViewType(self.as_ref().handle) };
+ unsafe { BnString::from_raw(ptr) }
+ }
+
+ /// Reads up to `len` bytes from address `offset`
+ fn read_vec(&self, offset: u64, len: usize) -> Vec<u8> {
+ let mut ret = vec![0; len];
+
+ let size = self.read(&mut ret, offset);
+ ret.truncate(size);
+
+ ret
+ }
+
+ /// Appends up to `len` bytes from address `offset` into `dest`
+ fn read_into_vec(&self, dest: &mut Vec<u8>, offset: u64, len: usize) -> usize {
+ let starting_len = dest.len();
+ dest.resize(starting_len + len, 0);
+ let read_size = self.read(&mut dest[starting_len..], offset);
+ dest.truncate(starting_len + read_size);
+ read_size
+ }
+
+ fn notify_data_written(&self, offset: u64, len: usize) {
+ unsafe {
+ BNNotifyDataWritten(self.as_ref().handle, offset, len);
+ }
+ }
+
+ fn notify_data_inserted(&self, offset: u64, len: usize) {
+ unsafe {
+ BNNotifyDataInserted(self.as_ref().handle, offset, len);
+ }
+ }
+
+ fn notify_data_removed(&self, offset: u64, len: usize) {
+ unsafe {
+ BNNotifyDataRemoved(self.as_ref().handle, offset, len as u64);
+ }
+ }
+
+ fn offset_has_code_semantics(&self, offset: u64) -> bool {
+ unsafe { BNIsOffsetCodeSemantics(self.as_ref().handle, offset) }
+ }
+
+ fn offset_has_writable_semantics(&self, offset: u64) -> bool {
+ unsafe { BNIsOffsetWritableSemantics(self.as_ref().handle, offset) }
+ }
+
+ fn original_image_base(&self) -> u64 {
+ unsafe { BNGetOriginalImageBase(self.as_ref().handle) }
+ }
+
+ fn set_original_image_base(&self, image_base: u64) {
+ unsafe { BNSetOriginalImageBase(self.as_ref().handle, image_base) }
+ }
+
+ fn end(&self) -> u64 {
+ unsafe { BNGetEndOffset(self.as_ref().handle) }
+ }
+
+ fn add_analysis_option(&self, name: impl BnStrCompatible) {
+ unsafe {
+ BNAddAnalysisOption(
+ self.as_ref().handle,
+ name.into_bytes_with_nul().as_ref().as_ptr() as *mut _,
+ )
+ }
+ }
+
+ fn has_initial_analysis(&self) -> bool {
+ unsafe { BNHasInitialAnalysis(self.as_ref().handle) }
+ }
+
+ fn set_analysis_hold(&self, enable: bool) {
+ unsafe { BNSetAnalysisHold(self.as_ref().handle, enable) }
+ }
+
+ fn update_analysis(&self) {
+ unsafe {
+ BNUpdateAnalysis(self.as_ref().handle);
+ }
+ }
+
+ fn update_analysis_and_wait(&self) {
+ unsafe {
+ BNUpdateAnalysisAndWait(self.as_ref().handle);
+ }
+ }
+
+ fn abort_analysis(&self) {
+ unsafe { BNAbortAnalysis(self.as_ref().handle) }
+ }
+
+ fn analysis_info(&self) -> Result<AnalysisInfo> {
+ let info_ref = unsafe { BNGetAnalysisInfo(self.as_ref().handle) };
+ if info_ref.is_null() {
+ return Err(());
+ }
+ let info = unsafe { *info_ref };
+ let active_infos = unsafe { slice::from_raw_parts(info.activeInfo, info.count) };
+
+ let mut active_info_list = vec![];
+ for active_info in active_infos {
+ let func = unsafe { Function::ref_from_raw(active_info.func) };
+ active_info_list.push(ActiveAnalysisInfo {
+ func,
+ analysis_time: active_info.analysisTime,
+ update_count: active_info.updateCount,
+ submit_count: active_info.submitCount,
+ });
+ }
+
+ let result = AnalysisInfo {
+ state: info.state,
+ analysis_time: info.analysisTime,
+ active_info: vec![],
+ };
+
+ unsafe { BNFreeAnalysisInfo(info_ref) };
+ Ok(result)
+ }
+
+ fn analysis_progress(&self) -> AnalysisProgress {
+ let progress = unsafe { BNGetAnalysisProgress(self.as_ref().handle) };
+ AnalysisProgress {
+ state: progress.state,
+ count: progress.count,
+ total: progress.total,
+ }
+ }
+
+ fn default_arch(&self) -> Option<CoreArchitecture> {
+ unsafe {
+ let raw = BNGetDefaultArchitecture(self.as_ref().handle);
+
+ if raw.is_null() {
+ return None;
+ }
+
+ Some(CoreArchitecture::from_raw(raw))
+ }
+ }
+
+ fn set_default_arch<A: Architecture>(&self, arch: &A) {
+ unsafe {
+ BNSetDefaultArchitecture(self.as_ref().handle, arch.as_ref().handle);
+ }
+ }
+
+ fn default_platform(&self) -> Option<Ref<Platform>> {
+ unsafe {
+ let raw = BNGetDefaultPlatform(self.as_ref().handle);
+
+ if raw.is_null() {
+ return None;
+ }
+
+ Some(Platform::ref_from_raw(raw))
+ }
+ }
+
+ fn set_default_platform(&self, plat: &Platform) {
+ unsafe {
+ BNSetDefaultPlatform(self.as_ref().handle, plat.handle);
+ }
+ }
+
+ fn instruction_len<A: Architecture>(&self, arch: &A, addr: u64) -> Option<usize> {
+ unsafe {
+ let size = BNGetInstructionLength(self.as_ref().handle, arch.as_ref().handle, addr);
+
+ if size > 0 {
+ Some(size)
+ } else {
+ None
+ }
+ }
+ }
+
+ fn symbol_by_address(&self, addr: u64) -> Option<Ref<Symbol>> {
+ unsafe {
+ let raw_sym_ptr =
+ BNGetSymbolByAddress(self.as_ref().handle, addr, std::ptr::null_mut());
+ match raw_sym_ptr.is_null() {
+ false => Some(Symbol::ref_from_raw(raw_sym_ptr)),
+ true => None,
+ }
+ }
+ }
+
+ fn symbol_by_raw_name<S: BnStrCompatible>(&self, raw_name: S) -> Option<Ref<Symbol>> {
+ let raw_name = raw_name.into_bytes_with_nul();
+
+ unsafe {
+ let raw_sym_ptr = BNGetSymbolByRawName(
+ self.as_ref().handle,
+ raw_name.as_ref().as_ptr() as *mut _,
+ std::ptr::null_mut(),
+ );
+ match raw_sym_ptr.is_null() {
+ false => Some(Symbol::ref_from_raw(raw_sym_ptr)),
+ true => None,
+ }
+ }
+ }
+
+ fn symbols(&self) -> Array<Symbol> {
+ unsafe {
+ let mut count = 0;
+ let handles = BNGetSymbols(self.as_ref().handle, &mut count, std::ptr::null_mut());
+
+ Array::new(handles, count, ())
+ }
+ }
+
+ fn symbols_by_name<S: BnStrCompatible>(&self, name: S) -> Array<Symbol> {
+ let raw_name = name.into_bytes_with_nul();
+
+ unsafe {
+ let mut count = 0;
+ let handles = BNGetSymbolsByName(
+ self.as_ref().handle,
+ raw_name.as_ref().as_ptr() as *mut _,
+ &mut count,
+ std::ptr::null_mut(),
+ );
+
+ Array::new(handles, count, ())
+ }
+ }
+
+ fn symbols_in_range(&self, range: Range<u64>) -> Array<Symbol> {
+ unsafe {
+ let mut count = 0;
+ let len = range.end.wrapping_sub(range.start);
+ let handles = BNGetSymbolsInRange(
+ self.as_ref().handle,
+ range.start,
+ len,
+ &mut count,
+ std::ptr::null_mut(),
+ );
+
+ Array::new(handles, count, ())
+ }
+ }
+
+ fn symbols_of_type(&self, ty: SymbolType) -> Array<Symbol> {
+ unsafe {
+ let mut count = 0;
+ let handles = BNGetSymbolsOfType(
+ self.as_ref().handle,
+ ty.into(),
+ &mut count,
+ std::ptr::null_mut(),
+ );
+
+ Array::new(handles, count, ())
+ }
+ }
+
+ fn symbols_of_type_in_range(&self, ty: SymbolType, range: Range<u64>) -> Array<Symbol> {
+ unsafe {
+ let mut count = 0;
+ let len = range.end.wrapping_sub(range.start);
+ let handles = BNGetSymbolsOfTypeInRange(
+ self.as_ref().handle,
+ ty.into(),
+ range.start,
+ len,
+ &mut count,
+ std::ptr::null_mut(),
+ );
+
+ Array::new(handles, count, ())
+ }
+ }
+
+ fn define_auto_symbol(&self, sym: &Symbol) {
+ unsafe {
+ BNDefineAutoSymbol(self.as_ref().handle, sym.handle);
+ }
+ }
+
+ fn define_auto_symbol_with_type<'a, T: Into<Option<&'a Type>>>(
+ &self,
+ sym: &Symbol,
+ plat: &Platform,
+ ty: T,
+ ) -> Result<Ref<Symbol>> {
+ let raw_type = if let Some(t) = ty.into() {
+ t.handle
+ } else {
+ std::ptr::null_mut()
+ };
+
+ unsafe {
+ let raw_sym = BNDefineAutoSymbolAndVariableOrFunction(
+ self.as_ref().handle,
+ plat.handle,
+ sym.handle,
+ raw_type,
+ );
+
+ if raw_sym.is_null() {
+ return Err(());
+ }
+
+ Ok(Symbol::ref_from_raw(raw_sym))
+ }
+ }
+
+ fn undefine_auto_symbol(&self, sym: &Symbol) {
+ unsafe {
+ BNUndefineAutoSymbol(self.as_ref().handle, sym.handle);
+ }
+ }
+
+ fn define_user_symbol(&self, sym: &Symbol) {
+ unsafe {
+ BNDefineUserSymbol(self.as_ref().handle, sym.handle);
+ }
+ }
+
+ fn undefine_user_symbol(&self, sym: &Symbol) {
+ unsafe {
+ BNUndefineUserSymbol(self.as_ref().handle, sym.handle);
+ }
+ }
+
+ fn data_variables(&self) -> Array<DataVariable> {
+ unsafe {
+ let mut count = 0;
+ let vars = BNGetDataVariables(self.as_ref().handle, &mut count);
+ Array::new(vars, count, ())
+ }
+ }
+
+ fn data_variable_at_address(&self, addr: u64) -> Option<DataVariable> {
+ let mut dv = BNDataVariable::default();
+ unsafe {
+ if BNGetDataVariableAtAddress(self.as_ref().handle, addr, &mut dv) {
+ Some(DataVariable::from_owned_raw(dv))
+ } else {
+ None
+ }
+ }
+ }
+
+ fn define_auto_data_var<'a, T: Into<Conf<&'a Type>>>(&self, addr: u64, ty: T) {
+ let mut owned_raw_ty = Conf::<&Type>::into_raw(ty.into());
+ unsafe {
+ BNDefineDataVariable(self.as_ref().handle, addr, &mut owned_raw_ty);
+ }
+ }
+
+ /// You likely would also like to call [`Self::define_user_symbol`] to bind this data variable with a name
+ fn define_user_data_var<'a, T: Into<Conf<&'a Type>>>(&self, addr: u64, ty: T) {
+ let mut owned_raw_ty = Conf::<&Type>::into_raw(ty.into());
+ unsafe {
+ BNDefineUserDataVariable(self.as_ref().handle, addr, &mut owned_raw_ty);
+ }
+ }
+
+ fn undefine_auto_data_var(&self, addr: u64, blacklist: Option<bool>) {
+ unsafe {
+ BNUndefineDataVariable(self.as_ref().handle, addr, blacklist.unwrap_or(true));
+ }
+ }
+
+ fn undefine_user_data_var(&self, addr: u64) {
+ unsafe {
+ BNUndefineUserDataVariable(self.as_ref().handle, addr);
+ }
+ }
+
+ fn define_auto_type<T: Into<QualifiedName>, S: BnStrCompatible>(
+ &self,
+ name: T,
+ source: S,
+ type_obj: &Type,
+ ) -> QualifiedName {
+ let mut raw_name = QualifiedName::into_raw(name.into());
+ let source_str = source.into_bytes_with_nul();
+ let name_handle = unsafe {
+ let id_str =
+ BNGenerateAutoTypeId(source_str.as_ref().as_ptr() as *const _, &mut raw_name);
+ BNDefineAnalysisType(self.as_ref().handle, id_str, &mut raw_name, type_obj.handle)
+ };
+ QualifiedName::free_raw(raw_name);
+ QualifiedName::from_owned_raw(name_handle)
+ }
+
+ fn define_auto_type_with_id<T: Into<QualifiedName>, S: BnStrCompatible>(
+ &self,
+ name: T,
+ id: S,
+ type_obj: &Type,
+ ) -> QualifiedName {
+ let mut raw_name = QualifiedName::into_raw(name.into());
+ let id_str = id.into_bytes_with_nul();
+ let result_raw_name = unsafe {
+ BNDefineAnalysisType(
+ self.as_ref().handle,
+ id_str.as_ref().as_ptr() as *const _,
+ &mut raw_name,
+ type_obj.handle,
+ )
+ };
+ QualifiedName::free_raw(raw_name);
+ QualifiedName::from_owned_raw(result_raw_name)
+ }
+
+ fn define_user_type<T: Into<QualifiedName>>(&self, name: T, type_obj: &Type) {
+ let mut raw_name = QualifiedName::into_raw(name.into());
+ unsafe { BNDefineUserAnalysisType(self.as_ref().handle, &mut raw_name, type_obj.handle) }
+ QualifiedName::free_raw(raw_name);
+ }
+
+ fn define_auto_types<T, I>(&self, names_sources_and_types: T) -> HashMap<String, QualifiedName>
+ where
+ T: Iterator<Item = I>,
+ I: Into<QualifiedNameTypeAndId>,
+ {
+ self.define_auto_types_with_progress(names_sources_and_types, NoProgressCallback)
+ }
+
+ fn define_auto_types_with_progress<T, I, P>(
+ &self,
+ names_sources_and_types: T,
+ mut progress: P,
+ ) -> HashMap<String, QualifiedName>
+ where
+ T: Iterator<Item = I>,
+ I: Into<QualifiedNameTypeAndId>,
+ P: ProgressCallback,
+ {
+ let mut types: Vec<BNQualifiedNameTypeAndId> = names_sources_and_types
+ .map(Into::into)
+ .map(QualifiedNameTypeAndId::into_raw)
+ .collect();
+ let mut result_ids: *mut *mut c_char = std::ptr::null_mut();
+ let mut result_names: *mut BNQualifiedName = std::ptr::null_mut();
+
+ let result_count = unsafe {
+ BNDefineAnalysisTypes(
+ self.as_ref().handle,
+ types.as_mut_ptr(),
+ types.len(),
+ Some(P::cb_progress_callback),
+ &mut progress as *mut P as *mut c_void,
+ &mut result_ids as *mut _,
+ &mut result_names as *mut _,
+ )
+ };
+
+ for ty in types {
+ QualifiedNameTypeAndId::free_raw(ty);
+ }
+
+ let id_array = unsafe { Array::<BnString>::new(result_ids, result_count, ()) };
+ let name_array = unsafe { Array::<QualifiedName>::new(result_names, result_count, ()) };
+ id_array
+ .into_iter()
+ .zip(&name_array)
+ .map(|(id, name)| (id.to_owned(), name))
+ .collect()
+ }
+
+ fn define_user_types<T, I>(&self, names_and_types: T)
+ where
+ T: Iterator<Item = I>,
+ I: Into<QualifiedNameAndType>,
+ {
+ self.define_user_types_with_progress(names_and_types, NoProgressCallback);
+ }
+
+ fn define_user_types_with_progress<T, I, P>(&self, names_and_types: T, mut progress: P)
+ where
+ T: Iterator<Item = I>,
+ I: Into<QualifiedNameAndType>,
+ P: ProgressCallback,
+ {
+ let mut types: Vec<BNQualifiedNameAndType> = names_and_types
+ .map(Into::into)
+ .map(QualifiedNameAndType::into_raw)
+ .collect();
+
+ unsafe {
+ BNDefineUserAnalysisTypes(
+ self.as_ref().handle,
+ types.as_mut_ptr(),
+ types.len(),
+ Some(P::cb_progress_callback),
+ &mut progress as *mut P as *mut c_void,
+ )
+ };
+
+ for ty in types {
+ QualifiedNameAndType::free_raw(ty);
+ }
+ }
+
+ fn undefine_auto_type<S: BnStrCompatible>(&self, id: S) {
+ let id_str = id.into_bytes_with_nul();
+ unsafe {
+ BNUndefineAnalysisType(self.as_ref().handle, id_str.as_ref().as_ptr() as *const _);
+ }
+ }
+
+ fn undefine_user_type<T: Into<QualifiedName>>(&self, name: T) {
+ let mut raw_name = QualifiedName::into_raw(name.into());
+ unsafe { BNUndefineUserAnalysisType(self.as_ref().handle, &mut raw_name) }
+ QualifiedName::free_raw(raw_name);
+ }
+
+ fn types(&self) -> Array<QualifiedNameAndType> {
+ unsafe {
+ let mut count = 0usize;
+ let types = BNGetAnalysisTypeList(self.as_ref().handle, &mut count);
+ Array::new(types, count, ())
+ }
+ }
+
+ fn dependency_sorted_types(&self) -> Array<QualifiedNameAndType> {
+ unsafe {
+ let mut count = 0usize;
+ let types = BNGetAnalysisDependencySortedTypeList(self.as_ref().handle, &mut count);
+ Array::new(types, count, ())
+ }
+ }
+
+ fn type_by_name<T: Into<QualifiedName>>(&self, name: T) -> Option<Ref<Type>> {
+ let mut raw_name = QualifiedName::into_raw(name.into());
+ unsafe {
+ let type_handle = BNGetAnalysisTypeByName(self.as_ref().handle, &mut raw_name);
+ QualifiedName::free_raw(raw_name);
+ if type_handle.is_null() {
+ return None;
+ }
+ Some(Type::ref_from_raw(type_handle))
+ }
+ }
+
+ fn type_by_ref(&self, ref_: &NamedTypeReference) -> Option<Ref<Type>> {
+ unsafe {
+ let type_handle = BNGetAnalysisTypeByRef(self.as_ref().handle, ref_.handle);
+ if type_handle.is_null() {
+ return None;
+ }
+ Some(Type::ref_from_raw(type_handle))
+ }
+ }
+
+ fn type_by_id<S: BnStrCompatible>(&self, id: S) -> Option<Ref<Type>> {
+ unsafe {
+ let id_str = id.into_bytes_with_nul();
+ let type_handle =
+ BNGetAnalysisTypeById(self.as_ref().handle, id_str.as_ref().as_ptr() as *mut _);
+ if type_handle.is_null() {
+ return None;
+ }
+ Some(Type::ref_from_raw(type_handle))
+ }
+ }
+
+ fn type_name_by_id<S: BnStrCompatible>(&self, id: S) -> Option<QualifiedName> {
+ unsafe {
+ let id_str = id.into_bytes_with_nul();
+ let name_handle =
+ BNGetAnalysisTypeNameById(self.as_ref().handle, id_str.as_ref().as_ptr() as *mut _);
+ let name = QualifiedName::from_owned_raw(name_handle);
+ // The core will return an empty qualified name if no type name was found.
+ match name.items.is_empty() {
+ true => None,
+ false => Some(name),
+ }
+ }
+ }
+
+ fn type_id_by_name<T: Into<QualifiedName>>(&self, name: T) -> Option<BnString> {
+ let mut raw_name = QualifiedName::into_raw(name.into());
+ unsafe {
+ let id_cstr = BNGetAnalysisTypeId(self.as_ref().handle, &mut raw_name);
+ QualifiedName::free_raw(raw_name);
+ let id = BnString::from_raw(id_cstr);
+ match id.is_empty() {
+ true => None,
+ false => Some(id),
+ }
+ }
+ }
+
+ fn is_type_auto_defined<T: Into<QualifiedName>>(&self, name: T) -> bool {
+ let mut raw_name = QualifiedName::into_raw(name.into());
+ let result = unsafe { BNIsAnalysisTypeAutoDefined(self.as_ref().handle, &mut raw_name) };
+ QualifiedName::free_raw(raw_name);
+ result
+ }
+
+ fn segments(&self) -> Array<Segment> {
+ unsafe {
+ let mut count = 0;
+ let raw_segments = BNGetSegments(self.as_ref().handle, &mut count);
+ Array::new(raw_segments, count, ())
+ }
+ }
+
+ fn segment_at(&self, addr: u64) -> Option<Ref<Segment>> {
+ unsafe {
+ let raw_seg = BNGetSegmentAt(self.as_ref().handle, addr);
+ match raw_seg.is_null() {
+ false => Some(Segment::ref_from_raw(raw_seg)),
+ true => None,
+ }
+ }
+ }
+
+ /// Adds a segment to the view.
+ ///
+ /// NOTE: Consider using [BinaryViewExt::begin_bulk_add_segments] and [BinaryViewExt::end_bulk_add_segments]
+ /// if you plan on adding a number of segments all at once, to avoid unnecessary MemoryMap updates.
+ fn add_segment(&self, segment: SegmentBuilder) {
+ segment.create(self.as_ref());
+ }
+
+ // TODO: Replace with BulkModify guard.
+ /// Start adding segments in bulk. Useful for adding large numbers of segments.
+ ///
+ /// After calling this any call to [BinaryViewExt::add_segment] will be uncommitted until a call to
+ /// [BinaryViewExt::end_bulk_add_segments]
+ ///
+ /// If you wish to discard the uncommitted segments you can call [BinaryViewExt::cancel_bulk_add_segments].
+ ///
+ /// NOTE: This **must** be paired with a later call to [BinaryViewExt::end_bulk_add_segments] or
+ /// [BinaryViewExt::cancel_bulk_add_segments], otherwise segments added after this call will stay uncommitted.
+ fn begin_bulk_add_segments(&self) {
+ unsafe { BNBeginBulkAddSegments(self.as_ref().handle) }
+ }
+
+ // TODO: Replace with BulkModify guard.
+ /// Commit all auto and user segments that have been added since the call to [Self::begin_bulk_add_segments].
+ ///
+ /// NOTE: This **must** be paired with a prior call to [Self::begin_bulk_add_segments], otherwise this
+ /// does nothing and segments are added individually.
+ fn end_bulk_add_segments(&self) {
+ unsafe { BNEndBulkAddSegments(self.as_ref().handle) }
+ }
+
+ // TODO: Replace with BulkModify guard.
+ /// Flushes the auto and user segments that have yet to be committed.
+ ///
+ /// This is to be used in conjunction with [Self::begin_bulk_add_segments]
+ /// and [Self::end_bulk_add_segments], where the latter will commit the segments
+ /// which have been added since [Self::begin_bulk_add_segments], this function
+ /// will discard them so that they do not get added to the view.
+ fn cancel_bulk_add_segments(&self) {
+ unsafe { BNCancelBulkAddSegments(self.as_ref().handle) }
+ }
+
+ fn add_section<S: BnStrCompatible>(&self, section: SectionBuilder<S>) {
+ section.create(self.as_ref());
+ }
+
+ fn remove_auto_section<S: BnStrCompatible>(&self, name: S) {
+ let raw_name = name.into_bytes_with_nul();
+ let raw_name_ptr = raw_name.as_ref().as_ptr() as *mut _;
+ unsafe {
+ BNRemoveAutoSection(self.as_ref().handle, raw_name_ptr);
+ }
+ }
+
+ fn remove_user_section<S: BnStrCompatible>(&self, name: S) {
+ let raw_name = name.into_bytes_with_nul();
+ let raw_name_ptr = raw_name.as_ref().as_ptr() as *mut _;
+ unsafe {
+ BNRemoveUserSection(self.as_ref().handle, raw_name_ptr);
+ }
+ }
+
+ fn section_by_name<S: BnStrCompatible>(&self, name: S) -> Option<Ref<Section>> {
+ unsafe {
+ let raw_name = name.into_bytes_with_nul();
+ let name_ptr = raw_name.as_ref().as_ptr() as *mut _;
+ let raw_section_ptr = BNGetSectionByName(self.as_ref().handle, name_ptr);
+ match raw_section_ptr.is_null() {
+ false => Some(Section::ref_from_raw(raw_section_ptr)),
+ true => None,
+ }
+ }
+ }
+
+ fn sections(&self) -> Array<Section> {
+ unsafe {
+ let mut count = 0;
+ let sections = BNGetSections(self.as_ref().handle, &mut count);
+ Array::new(sections, count, ())
+ }
+ }
+
+ fn sections_at(&self, addr: u64) -> Array<Section> {
+ unsafe {
+ let mut count = 0;
+ let sections = BNGetSectionsAt(self.as_ref().handle, addr, &mut count);
+ Array::new(sections, count, ())
+ }
+ }
+
+ fn add_auto_function(&self, plat: &Platform, addr: u64) -> Option<Ref<Function>> {
+ unsafe {
+ let handle = BNAddFunctionForAnalysis(
+ self.as_ref().handle,
+ plat.handle,
+ addr,
+ false,
+ std::ptr::null_mut(),
+ );
+
+ if handle.is_null() {
+ return None;
+ }
+
+ Some(Function::ref_from_raw(handle))
+ }
+ }
+
+ fn add_function_with_type(
+ &self,
+ plat: &Platform,
+ addr: u64,
+ auto_discovered: bool,
+ func_type: Option<&Type>,
+ ) -> Option<Ref<Function>> {
+ unsafe {
+ let func_type = match func_type {
+ Some(func_type) => func_type.handle,
+ None => std::ptr::null_mut(),
+ };
+
+ let handle = BNAddFunctionForAnalysis(
+ self.as_ref().handle,
+ plat.handle,
+ addr,
+ auto_discovered,
+ func_type,
+ );
+
+ if handle.is_null() {
+ return None;
+ }
+
+ Some(Function::ref_from_raw(handle))
+ }
+ }
+
+ fn add_entry_point(&self, plat: &Platform, addr: u64) {
+ unsafe {
+ BNAddEntryPointForAnalysis(self.as_ref().handle, plat.handle, addr);
+ }
+ }
+
+ fn create_user_function(&self, plat: &Platform, addr: u64) -> Result<Ref<Function>> {
+ unsafe {
+ let func = BNCreateUserFunction(self.as_ref().handle, plat.handle, addr);
+
+ if func.is_null() {
+ return Err(());
+ }
+
+ Ok(Function::ref_from_raw(func))
+ }
+ }
+
+ fn has_functions(&self) -> bool {
+ unsafe { BNHasFunctions(self.as_ref().handle) }
+ }
+
+ fn entry_point_function(&self) -> Option<Ref<Function>> {
+ unsafe {
+ let raw_func_ptr = BNGetAnalysisEntryPoint(self.as_ref().handle);
+ match raw_func_ptr.is_null() {
+ false => Some(Function::ref_from_raw(raw_func_ptr)),
+ true => None,
+ }
+ }
+ }
+
+ fn entry_point_functions(&self) -> Array<Function> {
+ unsafe {
+ let mut count = 0;
+ let functions = BNGetAllEntryFunctions(self.as_ref().handle, &mut count);
+
+ Array::new(functions, count, ())
+ }
+ }
+
+ fn functions(&self) -> Array<Function> {
+ unsafe {
+ let mut count = 0;
+ let functions = BNGetAnalysisFunctionList(self.as_ref().handle, &mut count);
+
+ Array::new(functions, count, ())
+ }
+ }
+
+ /// List of functions *starting* at `addr`
+ fn functions_at(&self, addr: u64) -> Array<Function> {
+ unsafe {
+ let mut count = 0;
+ let functions =
+ BNGetAnalysisFunctionsForAddress(self.as_ref().handle, addr, &mut count);
+
+ Array::new(functions, count, ())
+ }
+ }
+
+ // List of functions containing `addr`
+ fn functions_containing(&self, addr: u64) -> Array<Function> {
+ unsafe {
+ let mut count = 0;
+ let functions =
+ BNGetAnalysisFunctionsContainingAddress(self.as_ref().handle, addr, &mut count);
+
+ Array::new(functions, count, ())
+ }
+ }
+
+ fn function_at(&self, platform: &Platform, addr: u64) -> Option<Ref<Function>> {
+ unsafe {
+ let raw_func_ptr = BNGetAnalysisFunction(self.as_ref().handle, platform.handle, addr);
+ match raw_func_ptr.is_null() {
+ false => Some(Function::ref_from_raw(raw_func_ptr)),
+ true => None,
+ }
+ }
+ }
+
+ fn function_start_before(&self, addr: u64) -> u64 {
+ unsafe { BNGetPreviousFunctionStartBeforeAddress(self.as_ref().handle, addr) }
+ }
+
+ fn function_start_after(&self, addr: u64) -> u64 {
+ unsafe { BNGetNextFunctionStartAfterAddress(self.as_ref().handle, addr) }
+ }
+
+ fn basic_blocks_containing(&self, addr: u64) -> Array<BasicBlock<NativeBlock>> {
+ unsafe {
+ let mut count = 0;
+ let blocks = BNGetBasicBlocksForAddress(self.as_ref().handle, addr, &mut count);
+ Array::new(blocks, count, NativeBlock::new())
+ }
+ }
+
+ fn basic_blocks_starting_at(&self, addr: u64) -> Array<BasicBlock<NativeBlock>> {
+ unsafe {
+ let mut count = 0;
+ let blocks = BNGetBasicBlocksStartingAtAddress(self.as_ref().handle, addr, &mut count);
+ Array::new(blocks, count, NativeBlock::new())
+ }
+ }
+
+ fn is_new_auto_function_analysis_suppressed(&self) -> bool {
+ unsafe { BNGetNewAutoFunctionAnalysisSuppressed(self.as_ref().handle) }
+ }
+
+ fn set_new_auto_function_analysis_suppressed(&self, suppress: bool) {
+ unsafe {
+ BNSetNewAutoFunctionAnalysisSuppressed(self.as_ref().handle, suppress);
+ }
+ }
+
+ fn read_buffer(&self, offset: u64, len: usize) -> Result<DataBuffer> {
+ let read_buffer = unsafe { BNReadViewBuffer(self.as_ref().handle, offset, len) };
+ if read_buffer.is_null() {
+ Err(())
+ } else {
+ Ok(DataBuffer::from_raw(read_buffer))
+ }
+ }
+
+ fn debug_info(&self) -> Ref<DebugInfo> {
+ unsafe { DebugInfo::ref_from_raw(BNGetDebugInfo(self.as_ref().handle)) }
+ }
+
+ fn set_debug_info(&self, debug_info: &DebugInfo) {
+ unsafe { BNSetDebugInfo(self.as_ref().handle, debug_info.handle) }
+ }
+
+ fn apply_debug_info(&self, debug_info: &DebugInfo) {
+ unsafe { BNApplyDebugInfo(self.as_ref().handle, debug_info.handle) }
+ }
+
+ fn show_graph_report<S: BnStrCompatible>(&self, raw_name: S, graph: &FlowGraph) {
+ let raw_name = raw_name.into_bytes_with_nul();
+ unsafe {
+ BNShowGraphReport(
+ self.as_ref().handle,
+ raw_name.as_ref().as_ptr() as *mut _,
+ graph.handle,
+ );
+ }
+ }
+
+ fn load_settings<S: BnStrCompatible>(&self, view_type_name: S) -> Result<Ref<Settings>> {
+ let view_type_name = view_type_name.into_bytes_with_nul();
+ let settings_handle = unsafe {
+ BNBinaryViewGetLoadSettings(
+ self.as_ref().handle,
+ view_type_name.as_ref().as_ptr() as *mut _,
+ )
+ };
+
+ if settings_handle.is_null() {
+ Err(())
+ } else {
+ Ok(unsafe { Settings::from_raw(settings_handle) })
+ }
+ }
+
+ fn set_load_settings<S: BnStrCompatible>(&self, view_type_name: S, settings: &Settings) {
+ let view_type_name = view_type_name.into_bytes_with_nul();
+
+ unsafe {
+ BNBinaryViewSetLoadSettings(
+ self.as_ref().handle,
+ view_type_name.as_ref().as_ptr() as *mut _,
+ settings.handle,
+ )
+ };
+ }
+
+ /// Creates a new [TagType] and adds it to the view.
+ ///
+ /// # Arguments
+ /// * `name` - the name for the tag
+ /// * `icon` - the icon (recommended 1 emoji or 2 chars) for the tag
+ fn create_tag_type<N: BnStrCompatible, I: BnStrCompatible>(
+ &self,
+ name: N,
+ icon: I,
+ ) -> Ref<TagType> {
+ let tag_type = TagType::create(self.as_ref(), name, icon);
+ unsafe {
+ BNAddTagType(self.as_ref().handle, tag_type.handle);
+ }
+ tag_type
+ }
+
+ /// Removes a [TagType] and all tags that use it
+ fn remove_tag_type(&self, tag_type: &TagType) {
+ unsafe { BNRemoveTagType(self.as_ref().handle, tag_type.handle) }
+ }
+
+ /// Get a tag type by its name.
+ fn tag_type_by_name<S: BnStrCompatible>(&self, name: S) -> Option<Ref<TagType>> {
+ let name = name.into_bytes_with_nul();
+ unsafe {
+ let handle = BNGetTagType(self.as_ref().handle, name.as_ref().as_ptr() as *mut _);
+ if handle.is_null() {
+ return None;
+ }
+ Some(TagType::ref_from_raw(handle))
+ }
+ }
+
+ /// Get a tag by its id.
+ ///
+ /// Note this does not tell you anything about where it is used.
+ fn tag_by_id<S: BnStrCompatible>(&self, id: S) -> Option<Ref<Tag>> {
+ let id = id.into_bytes_with_nul();
+ unsafe {
+ let handle = BNGetTag(self.as_ref().handle, id.as_ref().as_ptr() as *mut _);
+ if handle.is_null() {
+ return None;
+ }
+ Some(Tag::ref_from_raw(handle))
+ }
+ }
+
+ /// Creates and adds a tag to an address
+ ///
+ /// User tag creations will be added to the undo buffer
+ fn add_tag<S: BnStrCompatible>(&self, addr: u64, t: &TagType, data: S, user: bool) {
+ let tag = Tag::new(t, data);
+
+ unsafe { BNAddTag(self.as_ref().handle, tag.handle, user) }
+
+ if user {
+ unsafe { BNAddUserDataTag(self.as_ref().handle, addr, tag.handle) }
+ } else {
+ unsafe { BNAddAutoDataTag(self.as_ref().handle, addr, tag.handle) }
+ }
+ }
+
+ /// removes a Tag object at a data address.
+ fn remove_auto_data_tag(&self, addr: u64, tag: &Tag) {
+ unsafe { BNRemoveAutoDataTag(self.as_ref().handle, addr, tag.handle) }
+ }
+
+ /// removes a Tag object at a data address.
+ /// Since this removes a user tag, it will be added to the current undo buffer.
+ fn remove_user_data_tag(&self, addr: u64, tag: &Tag) {
+ unsafe { BNRemoveUserDataTag(self.as_ref().handle, addr, tag.handle) }
+ }
+
+ /// Retrieves a list of the next disassembly lines.
+ ///
+ /// `get_next_linear_disassembly_lines` retrieves an [Array] over [LinearDisassemblyLine] objects for the
+ /// next disassembly lines, and updates the [LinearViewCursor] passed in. This function can be called
+ /// repeatedly to get more lines of linear disassembly.
+ ///
+ /// # Arguments
+ /// * `pos` - Position to retrieve linear disassembly lines from
+ fn get_next_linear_disassembly_lines(
+ &self,
+ pos: &mut LinearViewCursor,
+ ) -> Array<LinearDisassemblyLine> {
+ let mut result = unsafe { Array::new(std::ptr::null_mut(), 0, ()) };
+
+ while result.is_empty() {
+ result = pos.lines();
+ if !pos.next() {
+ return result;
+ }
+ }
+
+ result
+ }
+
+ /// Retrieves a list of the previous disassembly lines.
+ ///
+ /// `get_previous_linear_disassembly_lines` retrieves an [Array] over [LinearDisassemblyLine] objects for the
+ /// previous disassembly lines, and updates the [LinearViewCursor] passed in. This function can be called
+ /// repeatedly to get more lines of linear disassembly.
+ ///
+ /// # Arguments
+ /// * `pos` - Position to retrieve linear disassembly lines relative to
+ fn get_previous_linear_disassembly_lines(
+ &self,
+ pos: &mut LinearViewCursor,
+ ) -> Array<LinearDisassemblyLine> {
+ let mut result = unsafe { Array::new(std::ptr::null_mut(), 0, ()) };
+ while result.is_empty() {
+ if !pos.previous() {
+ return result;
+ }
+
+ result = pos.lines();
+ }
+
+ result
+ }
+
+ fn query_metadata<S: BnStrCompatible>(&self, key: S) -> Option<Ref<Metadata>> {
+ let value: *mut BNMetadata = unsafe {
+ BNBinaryViewQueryMetadata(
+ self.as_ref().handle,
+ key.into_bytes_with_nul().as_ref().as_ptr() as *const c_char,
+ )
+ };
+ if value.is_null() {
+ None
+ } else {
+ Some(unsafe { Metadata::ref_from_raw(value) })
+ }
+ }
+
+ fn get_metadata<T, S: BnStrCompatible>(&self, key: S) -> Option<Result<T>>
+ where
+ T: for<'a> TryFrom<&'a Metadata>,
+ {
+ self.query_metadata(key)
+ .map(|md| T::try_from(md.as_ref()).map_err(|_| ()))
+ }
+
+ fn store_metadata<V, S: BnStrCompatible>(&self, key: S, value: V, is_auto: bool)
+ where
+ V: Into<Ref<Metadata>>,
+ {
+ let md = value.into();
+ unsafe {
+ BNBinaryViewStoreMetadata(
+ self.as_ref().handle,
+ key.into_bytes_with_nul().as_ref().as_ptr() as *const c_char,
+ md.as_ref().handle,
+ is_auto,
+ )
+ };
+ }
+
+ fn remove_metadata<S: BnStrCompatible>(&self, key: S) {
+ unsafe {
+ BNBinaryViewRemoveMetadata(
+ self.as_ref().handle,
+ key.into_bytes_with_nul().as_ref().as_ptr() as *const c_char,
+ )
+ };
+ }
+
+ /// Retrieves a list of [CodeReference]s pointing to a given address.
+ fn code_refs_to_addr(&self, addr: u64) -> Array<CodeReference> {
+ unsafe {
+ let mut count = 0;
+ let handle = BNGetCodeReferences(self.as_ref().handle, addr, &mut count);
+ Array::new(handle, count, ())
+ }
+ }
+
+ /// Retrieves a list of [CodeReference]s pointing into a given [Range].
+ fn code_refs_into_range(&self, range: Range<u64>) -> Array<CodeReference> {
+ unsafe {
+ let mut count = 0;
+ let handle = BNGetCodeReferencesInRange(
+ self.as_ref().handle,
+ range.start,
+ range.end - range.start,
+ &mut count,
+ );
+ Array::new(handle, count, ())
+ }
+ }
+
+ /// Retrieves a list of addresses pointed to by a given address.
+ fn code_refs_from_addr(&self, addr: u64, func: Option<&Function>) -> Vec<u64> {
+ unsafe {
+ let mut count = 0;
+ let code_ref =
+ CodeReference::new(addr, func.map(|f| f.to_owned()), func.map(|f| f.arch()));
+ let mut raw_code_ref = CodeReference::into_owned_raw(&code_ref);
+ let addresses =
+ BNGetCodeReferencesFrom(self.as_ref().handle, &mut raw_code_ref, &mut count);
+ let res = std::slice::from_raw_parts(addresses, count).to_vec();
+ BNFreeAddressList(addresses);
+ res
+ }
+ }
+
+ /// Retrieves a list of [DataReference]s pointing to a given address.
+ fn data_refs_to_addr(&self, addr: u64) -> Array<DataReference> {
+ unsafe {
+ let mut count = 0;
+ let handle = BNGetDataReferences(self.as_ref().handle, addr, &mut count);
+ Array::new(handle, count, ())
+ }
+ }
+
+ /// Retrieves a list of [DataReference]s pointing into a given [Range].
+ fn data_refs_into_range(&self, range: Range<u64>) -> Array<DataReference> {
+ unsafe {
+ let mut count = 0;
+ let handle = BNGetDataReferencesInRange(
+ self.as_ref().handle,
+ range.start,
+ range.end - range.start,
+ &mut count,
+ );
+ Array::new(handle, count, ())
+ }
+ }
+
+ /// Retrieves a list of [DataReference]s originating from a given address.
+ fn data_refs_from_addr(&self, addr: u64) -> Array<DataReference> {
+ unsafe {
+ let mut count = 0;
+ let handle = BNGetDataReferencesFrom(self.as_ref().handle, addr, &mut count);
+ Array::new(handle, count, ())
+ }
+ }
+
+ /// Retrieves a list of [CodeReference]s for locations in code that use a given named type.
+ fn code_refs_using_type_name<T: Into<QualifiedName>>(&self, name: T) -> Array<CodeReference> {
+ let mut raw_name = QualifiedName::into_raw(name.into());
+ unsafe {
+ let mut count = 0;
+ let handle =
+ BNGetCodeReferencesForType(self.as_ref().handle, &mut raw_name, &mut count);
+ QualifiedName::free_raw(raw_name);
+ Array::new(handle, count, ())
+ }
+ }
+
+ /// Retrieves a list of [DataReference]s for locations in data that use a given named type.
+ fn data_refs_using_type_name<T: Into<QualifiedName>>(&self, name: T) -> Array<DataReference> {
+ let mut raw_name = QualifiedName::into_raw(name.into());
+ unsafe {
+ let mut count = 0;
+ let handle =
+ BNGetDataReferencesForType(self.as_ref().handle, &mut raw_name, &mut count);
+ QualifiedName::free_raw(raw_name);
+ Array::new(handle, count, ())
+ }
+ }
+
+ fn relocations_at(&self, addr: u64) -> Array<Relocation> {
+ unsafe {
+ let mut count = 0;
+ let handle = BNGetRelocationsAt(self.as_ref().handle, addr, &mut count);
+ Array::new(handle, count, ())
+ }
+ }
+
+ fn relocation_ranges(&self) -> Vec<Range<u64>> {
+ let ranges = unsafe {
+ let mut count = 0;
+ let reloc_ranges_ptr = BNGetRelocationRanges(self.as_ref().handle, &mut count);
+ let ranges = std::slice::from_raw_parts(reloc_ranges_ptr, count).to_vec();
+ BNFreeRelocationRanges(reloc_ranges_ptr);
+ ranges
+ };
+
+ // TODO: impl From BNRange for Range?
+ ranges
+ .iter()
+ .map(|range| Range {
+ start: range.start,
+ end: range.end,
+ })
+ .collect()
+ }
+
+ fn component_by_guid<S: BnStrCompatible>(&self, guid: S) -> Option<Ref<Component>> {
+ let name = guid.into_bytes_with_nul();
+ let result = unsafe {
+ BNGetComponentByGuid(
+ self.as_ref().handle,
+ name.as_ref().as_ptr() as *const c_char,
+ )
+ };
+ NonNull::new(result).map(|h| unsafe { Component::ref_from_raw(h) })
+ }
+
+ fn root_component(&self) -> Option<Ref<Component>> {
+ let result = unsafe { BNGetRootComponent(self.as_ref().handle) };
+ NonNull::new(result).map(|h| unsafe { Component::ref_from_raw(h) })
+ }
+
+ fn component_by_path<P: BnStrCompatible>(&self, path: P) -> Option<Ref<Component>> {
+ let path = path.into_bytes_with_nul();
+ let result = unsafe {
+ BNGetComponentByPath(
+ self.as_ref().handle,
+ path.as_ref().as_ptr() as *const c_char,
+ )
+ };
+ NonNull::new(result).map(|h| unsafe { Component::ref_from_raw(h) })
+ }
+
+ fn remove_component(&self, component: &Component) -> bool {
+ unsafe { BNRemoveComponent(self.as_ref().handle, component.handle.as_ptr()) }
+ }
+
+ fn remove_component_by_guid<P: IntoComponentGuid>(&self, guid: P) -> bool {
+ let path = guid.component_guid();
+ unsafe { BNRemoveComponentByGuid(self.as_ref().handle, path.as_ptr()) }
+ }
+
+ fn data_variable_parent_components(&self, data_variable: &DataVariable) -> Array<Component> {
+ let mut count = 0;
+ let result = unsafe {
+ BNGetDataVariableParentComponents(
+ self.as_ref().handle,
+ data_variable.address,
+ &mut count,
+ )
+ };
+ unsafe { Array::new(result, count, ()) }
+ }
+
+ fn external_libraries(&self) -> Array<ExternalLibrary> {
+ let mut count = 0;
+ let result = unsafe { BNBinaryViewGetExternalLibraries(self.as_ref().handle, &mut count) };
+ unsafe { Array::new(result, count, ()) }
+ }
+
+ fn external_library<S: BnStrCompatible>(&self, name: S) -> Option<Ref<ExternalLibrary>> {
+ let name_ptr = name.into_bytes_with_nul();
+ let result = unsafe {
+ BNBinaryViewGetExternalLibrary(
+ self.as_ref().handle,
+ name_ptr.as_ref().as_ptr() as *const c_char,
+ )
+ };
+ let result_ptr = NonNull::new(result)?;
+ Some(unsafe { ExternalLibrary::ref_from_raw(result_ptr) })
+ }
+
+ fn remove_external_library<S: BnStrCompatible>(&self, name: S) {
+ let name_ptr = name.into_bytes_with_nul();
+ unsafe {
+ BNBinaryViewRemoveExternalLibrary(
+ self.as_ref().handle,
+ name_ptr.as_ref().as_ptr() as *const c_char,
+ )
+ };
+ }
+
+ fn add_external_library<S: BnStrCompatible>(
+ &self,
+ name: S,
+ backing_file: Option<&ProjectFile>,
+ auto: bool,
+ ) -> Option<Ref<ExternalLibrary>> {
+ let name_ptr = name.into_bytes_with_nul();
+ let result = unsafe {
+ BNBinaryViewAddExternalLibrary(
+ self.as_ref().handle,
+ name_ptr.as_ref().as_ptr() as *const c_char,
+ backing_file
+ .map(|b| b.handle.as_ptr())
+ .unwrap_or(std::ptr::null_mut()),
+ auto,
+ )
+ };
+ NonNull::new(result).map(|h| unsafe { ExternalLibrary::ref_from_raw(h) })
+ }
+
+ fn external_locations(&self) -> Array<ExternalLocation> {
+ let mut count = 0;
+ let result = unsafe { BNBinaryViewGetExternalLocations(self.as_ref().handle, &mut count) };
+ unsafe { Array::new(result, count, ()) }
+ }
+
+ fn external_location_from_symbol(&self, symbol: &Symbol) -> Option<Ref<ExternalLocation>> {
+ let result =
+ unsafe { BNBinaryViewGetExternalLocation(self.as_ref().handle, symbol.handle) };
+ let result_ptr = NonNull::new(result)?;
+ Some(unsafe { ExternalLocation::ref_from_raw(result_ptr) })
+ }
+
+ fn remove_external_location(&self, location: &ExternalLocation) {
+ self.remove_external_location_from_symbol(&location.source_symbol())
+ }
+
+ fn remove_external_location_from_symbol(&self, symbol: &Symbol) {
+ unsafe { BNBinaryViewRemoveExternalLocation(self.as_ref().handle, symbol.handle) };
+ }
+
+ // TODO: This is awful, rewrite this.
+ fn add_external_location<S: BnStrCompatible>(
+ &self,
+ symbol: &Symbol,
+ library: &ExternalLibrary,
+ target_symbol_name: S,
+ target_address: Option<u64>,
+ target_is_auto: bool,
+ ) -> Option<Ref<ExternalLocation>> {
+ let target_symbol_name = target_symbol_name.into_bytes_with_nul();
+ let target_address_ptr = target_address
+ .map(|a| a as *mut u64)
+ .unwrap_or(std::ptr::null_mut());
+ let result = unsafe {
+ BNBinaryViewAddExternalLocation(
+ self.as_ref().handle,
+ symbol.handle,
+ library.handle.as_ptr(),
+ target_symbol_name.as_ref().as_ptr() as *const c_char,
+ target_address_ptr,
+ target_is_auto,
+ )
+ };
+ NonNull::new(result).map(|h| unsafe { ExternalLocation::ref_from_raw(h) })
+ }
+
+ /// Type container for all types (user and auto) in the Binary View.
+ ///
+ /// NOTE: Modifying an auto type will promote it to a user type.
+ fn type_container(&self) -> TypeContainer {
+ let type_container_ptr =
+ NonNull::new(unsafe { BNGetAnalysisTypeContainer(self.as_ref().handle) });
+ // NOTE: I have no idea how this isn't a UAF, see the note in `TypeContainer::from_raw`
+ unsafe { TypeContainer::from_raw(type_container_ptr.unwrap()) }
+ }
+
+ /// Type container for user types in the Binary View.
+ fn user_type_container(&self) -> TypeContainer {
+ let type_container_ptr =
+ NonNull::new(unsafe { BNGetAnalysisUserTypeContainer(self.as_ref().handle) });
+ // NOTE: I have no idea how this isn't a UAF, see the note in `TypeContainer::from_raw`
+ unsafe { TypeContainer::from_raw(type_container_ptr.unwrap()) }
+ }
+
+ /// Type container for auto types in the Binary View.
+ ///
+ /// NOTE: Unlike [`Self::type_container`] modification of auto types will **NOT** promote it to a user type.
+ fn auto_type_container(&self) -> TypeContainer {
+ let type_container_ptr =
+ NonNull::new(unsafe { BNGetAnalysisAutoTypeContainer(self.as_ref().handle) });
+ // NOTE: I have no idea how this isn't a UAF, see the note in `TypeContainer::from_raw`
+ unsafe { TypeContainer::from_raw(type_container_ptr.unwrap()) }
+ }
+
+ /// Make the contents of a type library available for type/import resolution
+ fn add_type_library(&self, library: &TypeLibrary) {
+ unsafe { BNAddBinaryViewTypeLibrary(self.as_ref().handle, library.as_raw()) }
+ }
+
+ fn type_library_by_name<S: BnStrCompatible>(&self, name: S) -> Option<TypeLibrary> {
+ let name = name.into_bytes_with_nul();
+ let result = unsafe {
+ BNGetBinaryViewTypeLibrary(
+ self.as_ref().handle,
+ name.as_ref().as_ptr() as *const c_char,
+ )
+ };
+ NonNull::new(result).map(|h| unsafe { TypeLibrary::from_raw(h) })
+ }
+
+ /// Should be called by custom py:py:class:`BinaryView` implementations
+ /// when they have successfully imported an object from a type library (eg a symbol's type).
+ /// Values recorded with this function will then be queryable via [BinaryViewExt::lookup_imported_object_library].
+ ///
+ /// * `lib` - Type Library containing the imported type
+ /// * `name` - Name of the object in the type library
+ /// * `addr` - address of symbol at import site
+ /// * `platform` - Platform of symbol at import site
+ fn record_imported_object_library<T: Into<QualifiedName>>(
+ &self,
+ lib: &TypeLibrary,
+ name: T,
+ addr: u64,
+ platform: &Platform,
+ ) {
+ let mut raw_name = QualifiedName::into_raw(name.into());
+ unsafe {
+ BNBinaryViewRecordImportedObjectLibrary(
+ self.as_ref().handle,
+ platform.handle,
+ addr,
+ lib.as_raw(),
+ &mut raw_name,
+ )
+ }
+ QualifiedName::free_raw(raw_name);
+ }
+
+ /// Recursively imports a type from the specified type library, or, if
+ /// no library was explicitly provided, the first type library associated with the current [BinaryView]
+ /// that provides the name requested.
+ ///
+ /// This may have the impact of loading other type libraries as dependencies on other type libraries are lazily resolved
+ /// when references to types provided by them are first encountered.
+ ///
+ /// Note that the name actually inserted into the view may not match the name as it exists in the type library in
+ /// the event of a name conflict. To aid in this, the [Type] object returned is a `NamedTypeReference` to
+ /// the deconflicted name used.
+ fn import_type_library<T: Into<QualifiedName>>(
+ &self,
+ name: T,
+ mut lib: Option<TypeLibrary>,
+ ) -> Option<Ref<Type>> {
+ let mut lib_ref = lib
+ .as_mut()
+ .map(|l| unsafe { l.as_raw() } as *mut _)
+ .unwrap_or(std::ptr::null_mut());
+ let mut raw_name = QualifiedName::into_raw(name.into());
+ let result = unsafe {
+ BNBinaryViewImportTypeLibraryType(self.as_ref().handle, &mut lib_ref, &mut raw_name)
+ };
+ QualifiedName::free_raw(raw_name);
+ (!result.is_null()).then(|| unsafe { Type::ref_from_raw(result) })
+ }
+
+ /// Recursively imports an object from the specified type library, or, if
+ /// no library was explicitly provided, the first type library associated with the current [BinaryView]
+ /// that provides the name requested.
+ ///
+ /// This may have the impact of loading other type libraries as dependencies on other type libraries are lazily resolved
+ /// when references to types provided by them are first encountered.
+ ///
+ /// .. note:: If you are implementing a custom BinaryView and use this method to import object types,
+ /// you should then call [BinaryViewExt::record_imported_object_library] with the details of where the object is located.
+ fn import_type_object<T: Into<QualifiedName>>(
+ &self,
+ name: T,
+ mut lib: Option<TypeLibrary>,
+ ) -> Option<Ref<Type>> {
+ let mut lib_ref = lib
+ .as_mut()
+ .map(|l| unsafe { l.as_raw() } as *mut _)
+ .unwrap_or(std::ptr::null_mut());
+ let mut raw_name = QualifiedName::into_raw(name.into());
+ let result = unsafe {
+ BNBinaryViewImportTypeLibraryObject(self.as_ref().handle, &mut lib_ref, &mut raw_name)
+ };
+ QualifiedName::free_raw(raw_name);
+ (!result.is_null()).then(|| unsafe { Type::ref_from_raw(result) })
+ }
+
+ /// Recursively imports a type interface given its GUID.
+ ///
+ /// .. note:: To support this type of lookup a type library must have
+ /// contain a metadata key called "type_guids" which is a map
+ /// Dict[string_guid, string_type_name] or
+ /// Dict[string_guid, Tuple[string_type_name, type_library_name]]
+ fn import_type_by_guid<S: BnStrCompatible>(&self, guid: S) -> Option<Ref<Type>> {
+ let guid = guid.into_bytes_with_nul();
+ let result = unsafe {
+ BNBinaryViewImportTypeLibraryTypeByGuid(
+ self.as_ref().handle,
+ guid.as_ref().as_ptr() as *const c_char,
+ )
+ };
+ (!result.is_null()).then(|| unsafe { Type::ref_from_raw(result) })
+ }
+
+ /// Recursively exports `type_obj` into `lib` as a type with name `name`
+ ///
+ /// As other referenced types are encountered, they are either copied into the destination type library or
+ /// else the type library that provided the referenced type is added as a dependency for the destination library.
+ fn export_type_to_library<T: Into<QualifiedName>>(
+ &self,
+ lib: &TypeLibrary,
+ name: T,
+ type_obj: &Type,
+ ) {
+ let mut raw_name = QualifiedName::into_raw(name.into());
+ unsafe {
+ BNBinaryViewExportTypeToTypeLibrary(
+ self.as_ref().handle,
+ lib.as_raw(),
+ &mut raw_name,
+ type_obj.handle,
+ )
+ }
+ QualifiedName::free_raw(raw_name);
+ }
+
+ /// Recursively exports `type_obj` into `lib` as a type with name `name`
+ ///
+ /// As other referenced types are encountered, they are either copied into the destination type library or
+ /// else the type library that provided the referenced type is added as a dependency for the destination library.
+ fn export_object_to_library<T: Into<QualifiedName>>(
+ &self,
+ lib: &TypeLibrary,
+ name: T,
+ type_obj: &Type,
+ ) {
+ let mut raw_name = QualifiedName::into_raw(name.into());
+ unsafe {
+ BNBinaryViewExportObjectToTypeLibrary(
+ self.as_ref().handle,
+ lib.as_raw(),
+ &mut raw_name,
+ type_obj.handle,
+ )
+ }
+ QualifiedName::free_raw(raw_name);
+ }
+
+ /// Gives you details of which type library and name was used to determine
+ /// the type of a symbol at a given address
+ ///
+ /// * `addr` - address of symbol at import site
+ /// * `platform` - Platform of symbol at import site
+ fn lookup_imported_object_library(
+ &self,
+ addr: u64,
+ platform: &Platform,
+ ) -> Option<(TypeLibrary, QualifiedName)> {
+ let mut result_lib = std::ptr::null_mut();
+ let mut result_name = BNQualifiedName::default();
+ let success = unsafe {
+ BNBinaryViewLookupImportedObjectLibrary(
+ self.as_ref().handle,
+ platform.handle,
+ addr,
+ &mut result_lib,
+ &mut result_name,
+ )
+ };
+ if !success {
+ return None;
+ }
+ let lib = unsafe { TypeLibrary::from_raw(NonNull::new(result_lib)?) };
+ let name = QualifiedName::from_owned_raw(result_name);
+ Some((lib, name))
+ }
+
+ /// Gives you details of from which type library and name a given type in the analysis was imported.
+ ///
+ /// * `name` - Name of type in analysis
+ fn lookup_imported_type_library<T: Into<QualifiedName>>(
+ &self,
+ name: T,
+ ) -> Option<(TypeLibrary, QualifiedName)> {
+ let raw_name = QualifiedName::into_raw(name.into());
+ let mut result_lib = std::ptr::null_mut();
+ let mut result_name = BNQualifiedName::default();
+ let success = unsafe {
+ BNBinaryViewLookupImportedTypeLibrary(
+ self.as_ref().handle,
+ &raw_name,
+ &mut result_lib,
+ &mut result_name,
+ )
+ };
+ QualifiedName::free_raw(raw_name);
+ if !success {
+ return None;
+ }
+ let lib = unsafe { TypeLibrary::from_raw(NonNull::new(result_lib)?) };
+ let name = QualifiedName::from_owned_raw(result_name);
+ Some((lib, name))
+ }
+ //
+ // fn type_archives(&self) -> Array<TypeArchive> {
+ // let mut ids: *mut *mut c_char = std::ptr::null_mut();
+ // let mut paths: *mut *mut c_char = std::ptr::null_mut();
+ // let count = unsafe { BNBinaryViewGetTypeArchives(self.as_ref().handle, &mut ids, &mut paths) };
+ // let path_list = unsafe { Array::<BnString>::new(paths, count, ()) };
+ // let ids_list = unsafe { std::slice::from_raw_parts(ids, count).to_vec() };
+ // let archives = ids_list.iter().filter_map(|id| {
+ // let archive_raw = unsafe { BNBinaryViewGetTypeArchive(self.as_ref().handle, *id) };
+ // match archive_raw.is_null() {
+ // true => None,
+ // false => Some(archive_raw)
+ // }
+ // }).collect();
+ // unsafe { BNFreeStringList(ids, count) };
+ // Array::new(archives)
+ // }
+}
+
+impl<T: BinaryViewBase> BinaryViewExt for T {}
+
+#[derive(PartialEq, Eq, Hash)]
+pub struct BinaryView {
+ pub(crate) handle: *mut BNBinaryView,
+}
+
+impl BinaryView {
+ pub(crate) unsafe fn from_raw(handle: *mut BNBinaryView) -> Self {
+ debug_assert!(!handle.is_null());
+ Self { handle }
+ }
+
+ pub(crate) unsafe fn ref_from_raw(handle: *mut BNBinaryView) -> Ref<Self> {
+ debug_assert!(!handle.is_null());
+ Ref::new(Self { handle })
+ }
+
+ pub fn from_path(meta: &mut FileMetadata, file_path: impl AsRef<Path>) -> Result<Ref<Self>> {
+ let file = file_path.as_ref().into_bytes_with_nul();
+ let handle =
+ unsafe { BNCreateBinaryDataViewFromFilename(meta.handle, file.as_ptr() as *mut _) };
+
+ if handle.is_null() {
+ return Err(());
+ }
+
+ unsafe { Ok(Ref::new(Self { handle })) }
+ }
+
+ pub fn from_accessor(meta: &FileMetadata, file: &mut FileAccessor) -> Result<Ref<Self>> {
+ let handle = unsafe { BNCreateBinaryDataViewFromFile(meta.handle, &mut file.api_object) };
+
+ if handle.is_null() {
+ return Err(());
+ }
+
+ unsafe { Ok(Ref::new(Self { handle })) }
+ }
+
+ pub fn from_data(meta: &FileMetadata, data: &[u8]) -> Result<Ref<Self>> {
+ let handle = unsafe {
+ BNCreateBinaryDataViewFromData(meta.handle, data.as_ptr() as *mut _, data.len())
+ };
+
+ if handle.is_null() {
+ return Err(());
+ }
+
+ unsafe { Ok(Ref::new(Self { handle })) }
+ }
+
+ /// Save the original binary file to the provided `file_path` along with any modifications.
+ pub fn save_to_path(&self, file_path: impl AsRef<Path>) -> bool {
+ let file = file_path.as_ref().into_bytes_with_nul();
+ unsafe { BNSaveToFilename(self.handle, file.as_ptr() as *mut _) }
+ }
+
+ /// Save the original binary file to the provided [`FileAccessor`] along with any modifications.
+ pub fn save_to_accessor(&self, file: &mut FileAccessor) -> bool {
+ unsafe { BNSaveToFile(self.handle, &mut file.api_object) }
+ }
+}
+
+impl BinaryViewBase for BinaryView {
+ fn read(&self, buf: &mut [u8], offset: u64) -> usize {
+ unsafe { BNReadViewData(self.handle, buf.as_mut_ptr() as *mut _, offset, buf.len()) }
+ }
+
+ fn write(&self, offset: u64, data: &[u8]) -> usize {
+ unsafe { BNWriteViewData(self.handle, offset, data.as_ptr() as *const _, data.len()) }
+ }
+
+ fn insert(&self, offset: u64, data: &[u8]) -> usize {
+ unsafe { BNInsertViewData(self.handle, offset, data.as_ptr() as *const _, data.len()) }
+ }
+
+ fn remove(&self, offset: u64, len: usize) -> usize {
+ unsafe { BNRemoveViewData(self.handle, offset, len as u64) }
+ }
+
+ fn offset_valid(&self, offset: u64) -> bool {
+ unsafe { BNIsValidOffset(self.handle, offset) }
+ }
+
+ fn offset_readable(&self, offset: u64) -> bool {
+ unsafe { BNIsOffsetReadable(self.handle, offset) }
+ }
+
+ fn offset_writable(&self, offset: u64) -> bool {
+ unsafe { BNIsOffsetWritable(self.handle, offset) }
+ }
+
+ fn offset_executable(&self, offset: u64) -> bool {
+ unsafe { BNIsOffsetExecutable(self.handle, offset) }
+ }
+
+ fn offset_backed_by_file(&self, offset: u64) -> bool {
+ unsafe { BNIsOffsetBackedByFile(self.handle, offset) }
+ }
+
+ fn next_valid_offset_after(&self, offset: u64) -> u64 {
+ unsafe { BNGetNextValidOffset(self.handle, offset) }
+ }
+
+ fn modification_status(&self, offset: u64) -> ModificationStatus {
+ unsafe { BNGetModification(self.handle, offset) }
+ }
+
+ fn start(&self) -> u64 {
+ unsafe { BNGetStartOffset(self.handle) }
+ }
+
+ fn len(&self) -> u64 {
+ unsafe { BNGetViewLength(self.handle) }
+ }
+
+ fn executable(&self) -> bool {
+ unsafe { BNIsExecutableView(self.handle) }
+ }
+
+ fn relocatable(&self) -> bool {
+ unsafe { BNIsRelocatable(self.handle) }
+ }
+
+ fn entry_point(&self) -> u64 {
+ unsafe { BNGetEntryPoint(self.handle) }
+ }
+
+ fn default_endianness(&self) -> Endianness {
+ unsafe { BNGetDefaultEndianness(self.handle) }
+ }
+
+ fn address_size(&self) -> usize {
+ unsafe { BNGetViewAddressSize(self.handle) }
+ }
+}
+
+unsafe impl RefCountable for BinaryView {
+ unsafe fn inc_ref(handle: &Self) -> Ref<Self> {
+ Ref::new(Self {
+ handle: BNNewViewReference(handle.handle),
+ })
+ }
+
+ unsafe fn dec_ref(handle: &Self) {
+ BNFreeBinaryView(handle.handle);
+ }
+}
+
+impl AsRef<BinaryView> for BinaryView {
+ fn as_ref(&self) -> &Self {
+ self
+ }
+}
+
+impl ToOwned for BinaryView {
+ type Owned = Ref<Self>;
+
+ fn to_owned(&self) -> Self::Owned {
+ unsafe { RefCountable::inc_ref(self) }
+ }
+}
+
+unsafe impl Send for BinaryView {}
+unsafe impl Sync for BinaryView {}
+
+impl std::fmt::Debug for BinaryView {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ f.debug_struct("BinaryView")
+ .field("type_name", &self.type_name())
+ .field("file", &self.file())
+ .field("original_image_base", &self.original_image_base())
+ .field("start", &self.start())
+ .field("end", &self.end())
+ .field("len", &self.len())
+ .field("default_platform", &self.default_platform())
+ .field("default_arch", &self.default_arch())
+ .field("default_endianness", &self.default_endianness())
+ .field("entry_point", &self.entry_point())
+ .field(
+ "entry_point_functions",
+ &self.entry_point_functions().to_vec(),
+ )
+ .field("address_size", &self.address_size())
+ .field("sections", &self.sections().to_vec())
+ .field("segments", &self.segments().to_vec())
+ .finish()
+ }
+}
+
+pub trait BinaryViewEventHandler: 'static + Sync {
+ fn on_event(&self, binary_view: &BinaryView);
+}
+
+/// Registers an event listener for binary view events.
+///
+/// # Example
+///
+/// ```no_run
+/// use binaryninja::binary_view::{
+/// register_binary_view_event, BinaryView, BinaryViewEventHandler, BinaryViewEventType,
+/// };
+///
+/// struct EventHandlerContext {
+/// // Context holding state available to event handler
+/// }
+///
+/// impl BinaryViewEventHandler for EventHandlerContext {
+/// fn on_event(&self, binary_view: &BinaryView) {
+/// // handle event
+/// }
+/// }
+///
+/// #[no_mangle]
+/// pub extern "C" fn CorePluginInit() {
+/// let context = EventHandlerContext {};
+///
+/// register_binary_view_event(
+/// BinaryViewEventType::BinaryViewInitialAnalysisCompletionEvent,
+/// context,
+/// );
+/// }
+/// ```
+pub fn register_binary_view_event<Handler>(event_type: BinaryViewEventType, handler: Handler)
+where
+ Handler: BinaryViewEventHandler,
+{
+ unsafe extern "C" fn on_event<Handler: BinaryViewEventHandler>(
+ ctx: *mut ::std::os::raw::c_void,
+ view: *mut BNBinaryView,
+ ) {
+ ffi_wrap!("EventHandler::on_event", {
+ let context = unsafe { &*(ctx as *const Handler) };
+ context.on_event(&BinaryView::ref_from_raw(BNNewViewReference(view)));
+ })
+ }
+
+ let boxed = Box::new(handler);
+ let raw = Box::into_raw(boxed);
+
+ unsafe {
+ BNRegisterBinaryViewEvent(
+ event_type,
+ Some(on_event::<Handler>),
+ raw as *mut ::std::os::raw::c_void,
+ );
+ }
+}