// Copyright 2021-2026 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. //! An interface for providing your own [BinaryView]s to Binary Ninja. use binaryninjacore_sys::*; pub use binaryninjacore_sys::BNModificationStatus as ModificationStatus; use std::fmt::Debug; use std::marker::PhantomData; use std::mem::MaybeUninit; use std::os::raw::c_void; use std::ptr; use std::slice; use crate::architecture::Architecture; use crate::binary_view::{BinaryView, BinaryViewBase, BinaryViewExt, Result}; use crate::metadata::Metadata; use crate::platform::Platform; use crate::rc::*; use crate::settings::Settings; use crate::string::*; use crate::Endianness; /// Registers a custom `BinaryViewType` with the core. /// /// The `constructor` argument is called immediately after successful registration of the type with /// the core. The `BinaryViewType` argument passed to `constructor` is the object that the /// `AsRef` /// implementation of the `CustomBinaryViewType` must return. pub fn register_view_type(name: &str, long_name: &str, constructor: F) -> &'static T where T: CustomBinaryViewType, F: FnOnce(BinaryViewType) -> T, { extern "C" fn cb_valid(ctxt: *mut c_void, data: *mut BNBinaryView) -> bool where T: CustomBinaryViewType, { let view_type = unsafe { &*(ctxt as *mut T) }; let data = unsafe { BinaryView::ref_from_raw(BNNewViewReference(data)) }; let _span = ffi_span!("BinaryViewTypeBase::is_valid_for", data); view_type.is_valid_for(&data) } extern "C" fn cb_deprecated(ctxt: *mut c_void) -> bool where T: CustomBinaryViewType, { ffi_wrap!("BinaryViewTypeBase::is_deprecated", unsafe { let view_type = &*(ctxt as *mut T); view_type.is_deprecated() }) } extern "C" fn cb_force_loadable(ctxt: *mut c_void) -> bool where T: CustomBinaryViewType, { ffi_wrap!("BinaryViewTypeBase::is_force_loadable", unsafe { let view_type = &*(ctxt as *mut T); view_type.is_force_loadable() }) } extern "C" fn cb_has_no_initial_content(ctxt: *mut c_void) -> bool where T: CustomBinaryViewType, { ffi_wrap!("BinaryViewTypeBase::has_no_initial_content", unsafe { let view_type = &*(ctxt as *mut T); view_type.has_no_initial_content() }) } extern "C" fn cb_create(ctxt: *mut c_void, data: *mut BNBinaryView) -> *mut BNBinaryView where T: CustomBinaryViewType, { ffi_wrap!("BinaryViewTypeBase::create", unsafe { let view_type = &*(ctxt as *mut T); let data = BinaryView::ref_from_raw(BNNewViewReference(data)); let builder = CustomViewBuilder { view_type, actual_parent: &data, }; let _span = ffi_span!("BinaryViewTypeBase::create", data); match view_type.create_custom_view(&data, builder) { Ok(bv) => { // force a leak of the Ref; failure to do this would result // in the refcount going to 0 in the process of returning it // to the core -- we're transferring ownership of the Ref here Ref::into_raw(bv.handle).handle } Err(_) => ptr::null_mut(), } }) } extern "C" fn cb_parse(ctxt: *mut c_void, data: *mut BNBinaryView) -> *mut BNBinaryView where T: CustomBinaryViewType, { ffi_wrap!("BinaryViewTypeBase::parse", unsafe { let view_type = &*(ctxt as *mut T); let data = BinaryView::ref_from_raw(BNNewViewReference(data)); let builder = CustomViewBuilder { view_type, actual_parent: &data, }; let _span = ffi_span!("BinaryViewTypeBase::parse", data); match view_type.parse_custom_view(&data, builder) { Ok(bv) => { // force a leak of the Ref; failure to do this would result // in the refcount going to 0 in the process of returning it // to the core -- we're transferring ownership of the Ref here Ref::into_raw(bv.handle).handle } Err(_) => ptr::null_mut(), } }) } extern "C" fn cb_load_settings(ctxt: *mut c_void, data: *mut BNBinaryView) -> *mut BNSettings where T: CustomBinaryViewType, { ffi_wrap!("BinaryViewTypeBase::load_settings", unsafe { let view_type = &*(ctxt as *mut T); let data = BinaryView::ref_from_raw(BNNewViewReference(data)); let _span = ffi_span!("BinaryViewTypeBase::load_settings", data); match view_type.load_settings_for_data(&data) { Some(settings) => Ref::into_raw(settings).handle, None => ptr::null_mut() as *mut _, } }) } let name = name.to_cstr(); let name_ptr = name.as_ptr(); let long_name = long_name.to_cstr(); let long_name_ptr = long_name.as_ptr(); let ctxt = Box::leak(Box::new(MaybeUninit::zeroed())); let mut bn_obj = BNCustomBinaryViewType { context: ctxt.as_mut_ptr() as *mut _, create: Some(cb_create::), parse: Some(cb_parse::), isValidForData: Some(cb_valid::), isDeprecated: Some(cb_deprecated::), isForceLoadable: Some(cb_force_loadable::), getLoadSettingsForData: Some(cb_load_settings::), hasNoInitialContent: Some(cb_has_no_initial_content::), }; unsafe { let handle = BNRegisterBinaryViewType(name_ptr, long_name_ptr, &mut bn_obj as *mut _); if handle.is_null() { // avoid leaking the space allocated for the type, but also // avoid running its Drop impl (if any -- not that there should // be one since view types live for the life of the process) as // MaybeUninit suppress the Drop implementation of it's inner type drop(Box::from_raw(ctxt)); panic!("bvt registration failed"); } ctxt.write(constructor(BinaryViewType { handle })); ctxt.assume_init_mut() } } pub trait BinaryViewTypeBase: AsRef { /// Is this [`BinaryViewType`] valid for the given the raw [`BinaryView`]? /// /// Typical implementations will read the magic bytes (e.g. 'MZ'), this is a performance-sensitive /// path so prefer inexpensive checks rather than comprehensive ones. fn is_valid_for(&self, data: &BinaryView) -> bool; /// Is this [`BinaryViewType`] deprecated and should not be used? /// /// We specify this such that the view type may still be used by existing databases, but not /// newly created views. fn is_deprecated(&self) -> bool { false } /// Is this [`BinaryViewType`] able to be loaded forcefully? /// /// If so, it will be shown in the drop-down when a user opens a file with options. fn is_force_loadable(&self) -> bool { false } /// Do instances of this [`BinaryViewType`] start with no loaded content? /// /// When true, the view has no meaningful default state: the user must make a /// selection (e.g. load images from a shared cache) before any content exists. /// Callers can use this to suppress restoring previously-saved view state for /// files not being loaded from a database, since a saved layout would reference /// content that isn't available on reopen. fn has_no_initial_content(&self) -> bool { false } fn default_load_settings_for_data(&self, data: &BinaryView) -> Option> { let settings_handle = unsafe { BNGetBinaryViewDefaultLoadSettingsForData(self.as_ref().handle, data.handle) }; if settings_handle.is_null() { None } else { unsafe { Some(Settings::ref_from_raw(settings_handle)) } } } fn load_settings_for_data(&self, _data: &BinaryView) -> Option> { None } } pub trait BinaryViewTypeExt: BinaryViewTypeBase { fn name(&self) -> String { unsafe { BnString::into_string(BNGetBinaryViewTypeName(self.as_ref().handle)) } } fn long_name(&self) -> String { unsafe { BnString::into_string(BNGetBinaryViewTypeLongName(self.as_ref().handle)) } } fn register_arch(&self, id: u32, endianness: Endianness, arch: &A) { unsafe { BNRegisterArchitectureForViewType( self.as_ref().handle, id, endianness, arch.as_ref().handle, ); } } fn register_platform(&self, id: u32, plat: &Platform) { let arch = plat.arch(); unsafe { BNRegisterPlatformForViewType(self.as_ref().handle, id, arch.handle, plat.handle); } } /// Expanded identification of [`Platform`] for [`BinaryViewType`]'s. Supersedes [`BinaryViewTypeExt::register_arch`] /// and [`BinaryViewTypeExt::register_platform`], as these have certain edge cases (overloaded elf families, for example) /// that can't be represented. /// /// The callback returns a [`Platform`] object or `None` (failure), and most recently added callbacks are called first /// to allow plugins to override any default behaviors. When a callback returns a platform, architecture will be /// derived from the identified platform. /// /// The [`BinaryView`] is the *parent* view (usually 'Raw') that the [`BinaryView`] is being created for. This /// means that generally speaking the callbacks need to be aware of the underlying file format, however the /// [`BinaryView`] implementation may have created datavars in the 'Raw' view by the time the callback is invoked. /// Behavior regarding when this callback is invoked and what has been made available in the [`BinaryView`] passed as an /// argument to the callback is up to the discretion of the [`BinaryView`] implementation. /// /// The `id` ind `endian` arguments are used as a filter to determine which registered [`Platform`] recognizer callbacks /// are invoked. /// /// Support for this API tentatively requires explicit support in the [`BinaryView`] implementation. fn register_platform_recognizer(&self, id: u32, endian: Endianness, recognizer: R) where R: 'static + Fn(&BinaryView, &Metadata) -> Option> + Send + Sync, { #[repr(C)] struct PlatformRecognizerHandlerContext where R: 'static + Fn(&BinaryView, &Metadata) -> Option> + Send + Sync, { recognizer: R, } extern "C" fn cb_recognize_low_level_il( ctxt: *mut c_void, bv: *mut BNBinaryView, metadata: *mut BNMetadata, ) -> *mut BNPlatform where R: 'static + Fn(&BinaryView, &Metadata) -> Option> + Send + Sync, { let context = unsafe { &*(ctxt as *mut PlatformRecognizerHandlerContext) }; let bv = unsafe { BinaryView::from_raw(bv).to_owned() }; let metadata = unsafe { Metadata::from_raw(metadata).to_owned() }; match (context.recognizer)(&bv, &metadata) { Some(plat) => unsafe { Ref::into_raw(plat).handle }, None => std::ptr::null_mut(), } } let recognizer = PlatformRecognizerHandlerContext { recognizer }; // TODO: Currently we leak `recognizer`. let raw = Box::into_raw(Box::new(recognizer)); unsafe { BNRegisterPlatformRecognizerForViewType( self.as_ref().handle, id as u64, endian, Some(cb_recognize_low_level_il::), raw as *mut c_void, ) } } fn open(&self, data: &BinaryView) -> Result> { let handle = unsafe { BNCreateBinaryViewOfType(self.as_ref().handle, data.handle) }; if handle.is_null() { // TODO: Proper Result, possibly introduce BNSetError to populate. return Err(()); } unsafe { Ok(BinaryView::ref_from_raw(handle)) } } fn parse(&self, data: &BinaryView) -> Result> { let handle = unsafe { BNParseBinaryViewOfType(self.as_ref().handle, data.handle) }; if handle.is_null() { // TODO: Proper Result, possibly introduce BNSetError to populate. return Err(()); } unsafe { Ok(BinaryView::ref_from_raw(handle)) } } } impl BinaryViewTypeExt for T {} /// A [`BinaryViewType`] acts as a factory for [`BinaryView`] objects. /// /// Each file format will have its own type, such as PE, ELF, or Mach-O. #[derive(Copy, Clone, PartialEq, Eq, Hash)] pub struct BinaryViewType { pub handle: *mut BNBinaryViewType, } impl BinaryViewType { pub(crate) unsafe fn from_raw(handle: *mut BNBinaryViewType) -> Self { debug_assert!(!handle.is_null()); Self { handle } } pub fn list_all() -> Array { unsafe { let mut count: usize = 0; let types = BNGetBinaryViewTypes(&mut count as *mut _); Array::new(types, count, ()) } } /// Enumerates all view types and checks to see if the given raw [`BinaryView`] is valid, /// returning only those that are. pub fn valid_types_for_data(data: &BinaryView) -> Array { unsafe { let mut count: usize = 0; let types = BNGetBinaryViewTypesForData(data.handle, &mut count as *mut _); Array::new(types, count, ()) } } /// Looks up a BinaryViewType by its short name pub fn by_name(name: &str) -> Result { let bytes = name.to_cstr(); let handle = unsafe { BNGetBinaryViewTypeByName(bytes.as_ref().as_ptr() as *const _) }; match handle.is_null() { false => Ok(unsafe { BinaryViewType::from_raw(handle) }), true => Err(()), } } } impl BinaryViewTypeBase for BinaryViewType { fn is_valid_for(&self, data: &BinaryView) -> bool { unsafe { BNIsBinaryViewTypeValidForData(self.handle, data.handle) } } fn is_deprecated(&self) -> bool { unsafe { BNIsBinaryViewTypeDeprecated(self.handle) } } fn is_force_loadable(&self) -> bool { unsafe { BNIsBinaryViewTypeForceLoadable(self.handle) } } fn has_no_initial_content(&self) -> bool { unsafe { BNBinaryViewTypeHasNoInitialContent(self.handle) } } fn load_settings_for_data(&self, data: &BinaryView) -> Option> { let settings_handle = unsafe { BNGetBinaryViewLoadSettingsForData(self.handle, data.handle) }; if settings_handle.is_null() { None } else { unsafe { Some(Settings::ref_from_raw(settings_handle)) } } } } impl Debug for BinaryViewType { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { f.debug_struct("BinaryViewType") .field("name", &self.name()) .field("long_name", &self.long_name()) .finish() } } impl CoreArrayProvider for BinaryViewType { type Raw = *mut BNBinaryViewType; type Context = (); type Wrapped<'a> = Guard<'a, BinaryViewType>; } unsafe impl CoreArrayProviderInner for BinaryViewType { unsafe fn free(raw: *mut Self::Raw, _count: usize, _context: &Self::Context) { BNFreeBinaryViewTypeList(raw); } unsafe fn wrap_raw<'a>(raw: &'a Self::Raw, _context: &'a Self::Context) -> Self::Wrapped<'a> { Guard::new(BinaryViewType::from_raw(*raw), &()) } } impl AsRef for BinaryViewType { fn as_ref(&self) -> &Self { self } } unsafe impl Send for BinaryViewType {} unsafe impl Sync for BinaryViewType {} pub trait CustomBinaryViewType: 'static + BinaryViewTypeBase + Sync { fn create_custom_view<'builder>( &self, data: &BinaryView, builder: CustomViewBuilder<'builder, Self>, ) -> Result>; fn parse_custom_view<'builder>( &self, data: &BinaryView, builder: CustomViewBuilder<'builder, Self>, ) -> Result> { // TODO: Check to make sure data.type_name is not Self::type_name ? self.create_custom_view(data, builder) } } /// Represents a request from the core to instantiate a custom BinaryView pub struct CustomViewBuilder<'a, T: CustomBinaryViewType + ?Sized> { view_type: &'a T, actual_parent: &'a BinaryView, } pub unsafe trait CustomBinaryView: 'static + BinaryViewBase + Sync + Sized { type Args: Send; fn new(handle: &BinaryView, args: &Self::Args) -> Result; fn init(&mut self, args: Self::Args) -> Result<()>; fn on_after_snapshot_data_applied(&mut self) {} } /// Represents a partially initialized custom `BinaryView` that should be returned to the core /// from the `create_custom_view` method of a `CustomBinaryViewType`. #[must_use] pub struct CustomView<'builder> { // this object can't actually be treated like a real // BinaryView as it isn't fully initialized until the // core receives it from the BNCustomBinaryViewType::create // callback. handle: Ref, _builder: PhantomData<&'builder ()>, } impl<'a, T: CustomBinaryViewType> CustomViewBuilder<'a, T> { /// Begins creating a custom BinaryView. /// /// This function may only be called from the `create_custom_view` function of a /// `CustomBinaryViewType`. /// /// `parent` specifies the view that the core will treat as the parent view, that /// Segments created against the created view will be backed by `parent`. It will /// usually be (but is not required to be) the `data` argument of the `create_custom_view` /// callback. /// /// `constructor` will not be called until well after the value returned by this function /// has been returned by `create_custom_view` callback to the core, and may not ever /// be called if the value returned by this function is dropped or leaked. /// /// # Errors /// /// This function will fail if the `FileMetadata` object associated with the *expected* parent /// (i.e., the `data` argument passed to the `create_custom_view` function) already has an /// associated `BinaryView` of the same `CustomBinaryViewType`. Multiple `BinaryView` objects /// of the same `BinaryViewType` belonging to the same `FileMetadata` object is prohibited and /// can cause strange, delayed segmentation faults. /// /// # Safety /// /// `constructor` should avoid doing anything with the object it returns, especially anything /// that would cause the core to invoke any of the `BinaryViewBase` methods. The core isn't /// going to consider the object fully initialized until after that callback has run. /// /// The `BinaryView` argument passed to the constructor function is the object that is expected /// to be returned by the `AsRef` implementation required by the `BinaryViewBase` trait. /// TODO FIXME whelp this is broke going to need 2 init callbacks pub fn create(self, parent: &BinaryView, view_args: V::Args) -> Result> where V: CustomBinaryView, { let file = self.actual_parent.file(); let view_type = self.view_type; let view_name = view_type.name(); if let Some(bv) = file.view_of_type(&view_name) { // while it seems to work most of the time, you can get really unlucky // if a free of the existing view of the same type kicks off while // BNCreateBinaryViewOfType is still running. the freeObject callback // will run for the new view before we've even finished initializing, // and that's all she wrote. // // even if we deal with it gracefully in cb_free_object, // BNCreateBinaryViewOfType is still going to crash, so we're just // going to try and stop this from happening in the first place. tracing::error!( "attempt to create duplicate view of type '{}' (existing: {:?})", view_name, bv.handle ); return Err(()); } // struct representing the context of a BNCustomBinaryView. Can be safely // dropped at any moment. struct CustomViewContext where V: CustomBinaryView, { raw_handle: *mut BNBinaryView, state: CustomViewContextState, } enum CustomViewContextState where V: CustomBinaryView, { Uninitialized { args: V::Args }, Initialized { view: V }, // dummy state, used as a helper to change states, only happen if the // `new` or `init` function fails. None, } impl CustomViewContext { fn assume_init_ref(&self) -> &V { let CustomViewContextState::Initialized { view } = &self.state else { panic!("CustomViewContextState in invalid state"); }; view } } extern "C" fn cb_init(ctxt: *mut c_void) -> bool where V: CustomBinaryView, { ffi_wrap!("BinaryViewBase::init", unsafe { let context = &mut *(ctxt as *mut CustomViewContext); let handle = BinaryView::ref_from_raw(context.raw_handle); // take the uninitialized state and use the args to call init let mut state = CustomViewContextState::None; core::mem::swap(&mut context.state, &mut state); let CustomViewContextState::Uninitialized { args } = state else { panic!("CustomViewContextState in invalid state"); }; match V::new(handle.as_ref(), &args) { Ok(mut view) => match view.init(args) { Ok(_) => { // put the initialized state context.state = CustomViewContextState::Initialized { view }; true } Err(_) => { tracing::error!( "CustomBinaryView::init failed; custom view returned Err" ); false } }, Err(_) => { tracing::error!("CustomBinaryView::new failed; custom view returned Err"); false } } }) } extern "C" fn cb_on_after_snapshot_data_applied(ctxt: *mut c_void) where V: CustomBinaryView, { ffi_wrap!("BinaryViewBase::onAfterSnapshotDataApplied", unsafe { let context = &mut *(ctxt as *mut CustomViewContext); if let CustomViewContextState::Initialized { view } = &mut context.state { view.on_after_snapshot_data_applied(); } }) } extern "C" fn cb_free_object(ctxt: *mut c_void) where V: CustomBinaryView, { ffi_wrap!("BinaryViewBase::freeObject", unsafe { let context = ctxt as *mut CustomViewContext; let context = Box::from_raw(context); if context.raw_handle.is_null() { // being called here is essentially a guarantee that BNCreateBinaryViewOfType // is above above us on the call stack somewhere -- no matter what we do, a crash // is pretty much certain at this point. // // this has been observed when two views of the same BinaryViewType are created // against the same BNFileMetaData object, and one of the views gets freed while // the second one is being initialized -- somehow the partially initialized one // gets freed before BNCreateBinaryViewOfType returns. // // multiples views of the same BinaryViewType in a BNFileMetaData object are // prohibited, so an API contract was violated in order to get here. // // if we're here, it's too late to do anything about it, though we can at least not // run the destructor on the custom view since that memory is uninitialized. tracing::error!( "BinaryViewBase::freeObject called on partially initialized object! crash imminent!" ); } else if matches!( &context.state, CustomViewContextState::None | CustomViewContextState::Uninitialized { .. } ) { // making it here means somebody went out of their way to leak a BinaryView // after calling BNCreateCustomView and never gave the BNBinaryView handle // to the core (which would have called cb_init) // // the result is a half-initialized BinaryView that the core will happily hand out // references to via BNGetFileViewofType even though it was never initialized // all the way. // // TODO update when this corner case gets fixed in the core? // // we can't do anything to prevent this, but we can at least have the crash // not be our fault. tracing::error!("BinaryViewBase::freeObject called on leaked/never initialized custom view!"); } }) } extern "C" fn cb_read( ctxt: *mut c_void, dest: *mut c_void, offset: u64, len: usize, ) -> usize where V: CustomBinaryView, { ffi_wrap!("BinaryViewBase::read", unsafe { let context = &*(ctxt as *mut CustomViewContext); let dest = slice::from_raw_parts_mut(dest as *mut u8, len); context.assume_init_ref().read(dest, offset) }) } extern "C" fn cb_write( ctxt: *mut c_void, offset: u64, src: *const c_void, len: usize, ) -> usize where V: CustomBinaryView, { ffi_wrap!("BinaryViewBase::write", unsafe { let context = &*(ctxt as *mut CustomViewContext); let src = slice::from_raw_parts(src as *const u8, len); context.assume_init_ref().write(offset, src) }) } extern "C" fn cb_insert( ctxt: *mut c_void, offset: u64, src: *const c_void, len: usize, ) -> usize where V: CustomBinaryView, { ffi_wrap!("BinaryViewBase::insert", unsafe { let context = &*(ctxt as *mut CustomViewContext); let src = slice::from_raw_parts(src as *const u8, len); context.assume_init_ref().insert(offset, src) }) } extern "C" fn cb_remove(ctxt: *mut c_void, offset: u64, len: u64) -> usize where V: CustomBinaryView, { ffi_wrap!("BinaryViewBase::remove", unsafe { let context = &*(ctxt as *mut CustomViewContext); context.assume_init_ref().remove(offset, len as usize) }) } extern "C" fn cb_modification(ctxt: *mut c_void, offset: u64) -> ModificationStatus where V: CustomBinaryView, { ffi_wrap!("BinaryViewBase::modification_status", unsafe { let context = &*(ctxt as *mut CustomViewContext); context.assume_init_ref().modification_status(offset) }) } extern "C" fn cb_offset_valid(ctxt: *mut c_void, offset: u64) -> bool where V: CustomBinaryView, { ffi_wrap!("BinaryViewBase::offset_valid", unsafe { let context = &*(ctxt as *mut CustomViewContext); context.assume_init_ref().offset_valid(offset) }) } extern "C" fn cb_offset_readable(ctxt: *mut c_void, offset: u64) -> bool where V: CustomBinaryView, { ffi_wrap!("BinaryViewBase::readable", unsafe { let context = &*(ctxt as *mut CustomViewContext); context.assume_init_ref().offset_readable(offset) }) } extern "C" fn cb_offset_writable(ctxt: *mut c_void, offset: u64) -> bool where V: CustomBinaryView, { ffi_wrap!("BinaryViewBase::writable", unsafe { let context = &*(ctxt as *mut CustomViewContext); context.assume_init_ref().offset_writable(offset) }) } extern "C" fn cb_offset_executable(ctxt: *mut c_void, offset: u64) -> bool where V: CustomBinaryView, { ffi_wrap!("BinaryViewBase::offset_executable", unsafe { let context = &*(ctxt as *mut CustomViewContext); context.assume_init_ref().offset_executable(offset) }) } extern "C" fn cb_offset_backed_by_file(ctxt: *mut c_void, offset: u64) -> bool where V: CustomBinaryView, { ffi_wrap!("BinaryViewBase::offset_backed_by_file", unsafe { let context = &*(ctxt as *mut CustomViewContext); context.assume_init_ref().offset_backed_by_file(offset) }) } extern "C" fn cb_next_valid_offset(ctxt: *mut c_void, offset: u64) -> u64 where V: CustomBinaryView, { ffi_wrap!("BinaryViewBase::next_valid_offset_after", unsafe { let context = &*(ctxt as *mut CustomViewContext); context.assume_init_ref().next_valid_offset_after(offset) }) } extern "C" fn cb_start(ctxt: *mut c_void) -> u64 where V: CustomBinaryView, { ffi_wrap!("BinaryViewBase::start", unsafe { let context = &*(ctxt as *mut CustomViewContext); context.assume_init_ref().start() }) } extern "C" fn cb_length(ctxt: *mut c_void) -> u64 where V: CustomBinaryView, { ffi_wrap!("BinaryViewBase::len", unsafe { let context = &*(ctxt as *mut CustomViewContext); context.assume_init_ref().len() }) } extern "C" fn cb_entry_point(ctxt: *mut c_void) -> u64 where V: CustomBinaryView, { ffi_wrap!("BinaryViewBase::entry_point", unsafe { let context = &*(ctxt as *mut CustomViewContext); context.assume_init_ref().entry_point() }) } extern "C" fn cb_executable(ctxt: *mut c_void) -> bool where V: CustomBinaryView, { ffi_wrap!("BinaryViewBase::executable", unsafe { let context = &*(ctxt as *mut CustomViewContext); context.assume_init_ref().executable() }) } extern "C" fn cb_endianness(ctxt: *mut c_void) -> Endianness where V: CustomBinaryView, { ffi_wrap!("BinaryViewBase::default_endianness", unsafe { let context = &*(ctxt as *mut CustomViewContext); context.assume_init_ref().default_endianness() }) } extern "C" fn cb_relocatable(ctxt: *mut c_void) -> bool where V: CustomBinaryView, { ffi_wrap!("BinaryViewBase::relocatable", unsafe { let context = &*(ctxt as *mut CustomViewContext); context.assume_init_ref().relocatable() }) } extern "C" fn cb_address_size(ctxt: *mut c_void) -> usize where V: CustomBinaryView, { ffi_wrap!("BinaryViewBase::address_size", unsafe { let context = &*(ctxt as *mut CustomViewContext); context.assume_init_ref().address_size() }) } extern "C" fn cb_save(ctxt: *mut c_void, _fa: *mut BNFileAccessor) -> bool where V: CustomBinaryView, { ffi_wrap!("BinaryViewBase::save", unsafe { let _context = &*(ctxt as *mut CustomViewContext); false }) } let ctxt = Box::new(CustomViewContext:: { raw_handle: ptr::null_mut(), state: CustomViewContextState::Uninitialized { args: view_args }, }); let ctxt = Box::into_raw(ctxt); let mut bn_obj = BNCustomBinaryView { context: ctxt as *mut _, init: Some(cb_init::), freeObject: Some(cb_free_object::), externalRefTaken: None, externalRefReleased: None, read: Some(cb_read::), write: Some(cb_write::), insert: Some(cb_insert::), remove: Some(cb_remove::), getModification: Some(cb_modification::), isValidOffset: Some(cb_offset_valid::), isOffsetReadable: Some(cb_offset_readable::), isOffsetWritable: Some(cb_offset_writable::), isOffsetExecutable: Some(cb_offset_executable::), isOffsetBackedByFile: Some(cb_offset_backed_by_file::), getNextValidOffset: Some(cb_next_valid_offset::), getStart: Some(cb_start::), getLength: Some(cb_length::), getEntryPoint: Some(cb_entry_point::), isExecutable: Some(cb_executable::), getDefaultEndianness: Some(cb_endianness::), isRelocatable: Some(cb_relocatable::), getAddressSize: Some(cb_address_size::), save: Some(cb_save::), onAfterSnapshotDataApplied: Some(cb_on_after_snapshot_data_applied::), }; let view_name = view_name.to_cstr(); unsafe { let res = BNCreateCustomBinaryView( view_name.as_ptr(), file.handle, parent.handle, &mut bn_obj, ); assert!( !res.is_null(), "BNCreateCustomBinaryView cannot return null" ); (*ctxt).raw_handle = res; Ok(CustomView { handle: BinaryView::ref_from_raw(res), _builder: PhantomData, }) } } pub fn wrap_existing(self, wrapped_view: Ref) -> Result> { Ok(CustomView { handle: wrapped_view, _builder: PhantomData, }) } }