summaryrefslogtreecommitdiff
path: root/rust/src/custom_binary_view.rs
diff options
context:
space:
mode:
Diffstat (limited to 'rust/src/custom_binary_view.rs')
-rw-r--r--rust/src/custom_binary_view.rs961
1 files changed, 0 insertions, 961 deletions
diff --git a/rust/src/custom_binary_view.rs b/rust/src/custom_binary_view.rs
deleted file mode 100644
index 84f39171..00000000
--- a/rust/src/custom_binary_view.rs
+++ /dev/null
@@ -1,961 +0,0 @@
-// 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<BinaryViewType>`
-/// implementation of the `CustomBinaryViewType` must return.
-pub fn register_view_type<T, F>(name: &str, long_name: &str, constructor: F) -> &'static T
-where
- T: CustomBinaryViewType,
- F: FnOnce(BinaryViewType) -> T,
-{
- extern "C" fn cb_valid<T>(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<T>(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<T>(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<T>(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<T>(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<T>(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<T>(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::<T>),
- parse: Some(cb_parse::<T>),
- isValidForData: Some(cb_valid::<T>),
- isDeprecated: Some(cb_deprecated::<T>),
- isForceLoadable: Some(cb_force_loadable::<T>),
- getLoadSettingsForData: Some(cb_load_settings::<T>),
- hasNoInitialContent: Some(cb_has_no_initial_content::<T>),
- };
-
- 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<BinaryViewType> {
- /// 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<Ref<Settings>> {
- 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<Ref<Settings>> {
- 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<A: Architecture>(&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<R>(&self, id: u32, endian: Endianness, recognizer: R)
- where
- R: 'static + Fn(&BinaryView, &Metadata) -> Option<Ref<Platform>> + Send + Sync,
- {
- #[repr(C)]
- struct PlatformRecognizerHandlerContext<R>
- where
- R: 'static + Fn(&BinaryView, &Metadata) -> Option<Ref<Platform>> + Send + Sync,
- {
- recognizer: R,
- }
-
- extern "C" fn cb_recognize_low_level_il<R>(
- ctxt: *mut c_void,
- bv: *mut BNBinaryView,
- metadata: *mut BNMetadata,
- ) -> *mut BNPlatform
- where
- R: 'static + Fn(&BinaryView, &Metadata) -> Option<Ref<Platform>> + Send + Sync,
- {
- let context = unsafe { &*(ctxt as *mut PlatformRecognizerHandlerContext<R>) };
- 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::<R>),
- raw as *mut c_void,
- )
- }
- }
-
- fn open(&self, data: &BinaryView) -> Result<Ref<BinaryView>> {
- 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<Ref<BinaryView>> {
- 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<T: BinaryViewTypeBase> 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<BinaryViewType> {
- 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<BinaryViewType> {
- 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<Self> {
- 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<Ref<Settings>> {
- 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<BinaryViewType> 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<CustomView<'builder>>;
-
- fn parse_custom_view<'builder>(
- &self,
- data: &BinaryView,
- builder: CustomViewBuilder<'builder, Self>,
- ) -> Result<CustomView<'builder>> {
- // 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<Self>;
- 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<BinaryView>,
- _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<BinaryView>` implementation required by the `BinaryViewBase` trait.
- /// TODO FIXME whelp this is broke going to need 2 init callbacks
- pub fn create<V>(self, parent: &BinaryView, view_args: V::Args) -> Result<CustomView<'a>>
- 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<V>
- where
- V: CustomBinaryView,
- {
- raw_handle: *mut BNBinaryView,
- state: CustomViewContextState<V>,
- }
-
- enum CustomViewContextState<V>
- 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<V: CustomBinaryView> CustomViewContext<V> {
- fn assume_init_ref(&self) -> &V {
- let CustomViewContextState::Initialized { view } = &self.state else {
- panic!("CustomViewContextState in invalid state");
- };
- view
- }
- }
-
- extern "C" fn cb_init<V>(ctxt: *mut c_void) -> bool
- where
- V: CustomBinaryView,
- {
- ffi_wrap!("BinaryViewBase::init", unsafe {
- let context = &mut *(ctxt as *mut CustomViewContext<V>);
- 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<V>(ctxt: *mut c_void)
- where
- V: CustomBinaryView,
- {
- ffi_wrap!("BinaryViewBase::onAfterSnapshotDataApplied", unsafe {
- let context = &mut *(ctxt as *mut CustomViewContext<V>);
- if let CustomViewContextState::Initialized { view } = &mut context.state {
- view.on_after_snapshot_data_applied();
- }
- })
- }
-
- extern "C" fn cb_free_object<V>(ctxt: *mut c_void)
- where
- V: CustomBinaryView,
- {
- ffi_wrap!("BinaryViewBase::freeObject", unsafe {
- let context = ctxt as *mut CustomViewContext<V>;
- 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<V>(
- 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<V>);
- let dest = slice::from_raw_parts_mut(dest as *mut u8, len);
- context.assume_init_ref().read(dest, offset)
- })
- }
-
- extern "C" fn cb_write<V>(
- 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<V>);
- let src = slice::from_raw_parts(src as *const u8, len);
- context.assume_init_ref().write(offset, src)
- })
- }
-
- extern "C" fn cb_insert<V>(
- 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<V>);
- let src = slice::from_raw_parts(src as *const u8, len);
- context.assume_init_ref().insert(offset, src)
- })
- }
-
- extern "C" fn cb_remove<V>(ctxt: *mut c_void, offset: u64, len: u64) -> usize
- where
- V: CustomBinaryView,
- {
- ffi_wrap!("BinaryViewBase::remove", unsafe {
- let context = &*(ctxt as *mut CustomViewContext<V>);
- context.assume_init_ref().remove(offset, len as usize)
- })
- }
-
- extern "C" fn cb_modification<V>(ctxt: *mut c_void, offset: u64) -> ModificationStatus
- where
- V: CustomBinaryView,
- {
- ffi_wrap!("BinaryViewBase::modification_status", unsafe {
- let context = &*(ctxt as *mut CustomViewContext<V>);
- context.assume_init_ref().modification_status(offset)
- })
- }
-
- extern "C" fn cb_offset_valid<V>(ctxt: *mut c_void, offset: u64) -> bool
- where
- V: CustomBinaryView,
- {
- ffi_wrap!("BinaryViewBase::offset_valid", unsafe {
- let context = &*(ctxt as *mut CustomViewContext<V>);
- context.assume_init_ref().offset_valid(offset)
- })
- }
-
- extern "C" fn cb_offset_readable<V>(ctxt: *mut c_void, offset: u64) -> bool
- where
- V: CustomBinaryView,
- {
- ffi_wrap!("BinaryViewBase::readable", unsafe {
- let context = &*(ctxt as *mut CustomViewContext<V>);
- context.assume_init_ref().offset_readable(offset)
- })
- }
-
- extern "C" fn cb_offset_writable<V>(ctxt: *mut c_void, offset: u64) -> bool
- where
- V: CustomBinaryView,
- {
- ffi_wrap!("BinaryViewBase::writable", unsafe {
- let context = &*(ctxt as *mut CustomViewContext<V>);
- context.assume_init_ref().offset_writable(offset)
- })
- }
-
- extern "C" fn cb_offset_executable<V>(ctxt: *mut c_void, offset: u64) -> bool
- where
- V: CustomBinaryView,
- {
- ffi_wrap!("BinaryViewBase::offset_executable", unsafe {
- let context = &*(ctxt as *mut CustomViewContext<V>);
- context.assume_init_ref().offset_executable(offset)
- })
- }
-
- extern "C" fn cb_offset_backed_by_file<V>(ctxt: *mut c_void, offset: u64) -> bool
- where
- V: CustomBinaryView,
- {
- ffi_wrap!("BinaryViewBase::offset_backed_by_file", unsafe {
- let context = &*(ctxt as *mut CustomViewContext<V>);
- context.assume_init_ref().offset_backed_by_file(offset)
- })
- }
-
- extern "C" fn cb_next_valid_offset<V>(ctxt: *mut c_void, offset: u64) -> u64
- where
- V: CustomBinaryView,
- {
- ffi_wrap!("BinaryViewBase::next_valid_offset_after", unsafe {
- let context = &*(ctxt as *mut CustomViewContext<V>);
- context.assume_init_ref().next_valid_offset_after(offset)
- })
- }
-
- extern "C" fn cb_start<V>(ctxt: *mut c_void) -> u64
- where
- V: CustomBinaryView,
- {
- ffi_wrap!("BinaryViewBase::start", unsafe {
- let context = &*(ctxt as *mut CustomViewContext<V>);
- context.assume_init_ref().start()
- })
- }
-
- extern "C" fn cb_length<V>(ctxt: *mut c_void) -> u64
- where
- V: CustomBinaryView,
- {
- ffi_wrap!("BinaryViewBase::len", unsafe {
- let context = &*(ctxt as *mut CustomViewContext<V>);
- context.assume_init_ref().len()
- })
- }
-
- extern "C" fn cb_entry_point<V>(ctxt: *mut c_void) -> u64
- where
- V: CustomBinaryView,
- {
- ffi_wrap!("BinaryViewBase::entry_point", unsafe {
- let context = &*(ctxt as *mut CustomViewContext<V>);
- context.assume_init_ref().entry_point()
- })
- }
-
- extern "C" fn cb_executable<V>(ctxt: *mut c_void) -> bool
- where
- V: CustomBinaryView,
- {
- ffi_wrap!("BinaryViewBase::executable", unsafe {
- let context = &*(ctxt as *mut CustomViewContext<V>);
- context.assume_init_ref().executable()
- })
- }
-
- extern "C" fn cb_endianness<V>(ctxt: *mut c_void) -> Endianness
- where
- V: CustomBinaryView,
- {
- ffi_wrap!("BinaryViewBase::default_endianness", unsafe {
- let context = &*(ctxt as *mut CustomViewContext<V>);
-
- context.assume_init_ref().default_endianness()
- })
- }
-
- extern "C" fn cb_relocatable<V>(ctxt: *mut c_void) -> bool
- where
- V: CustomBinaryView,
- {
- ffi_wrap!("BinaryViewBase::relocatable", unsafe {
- let context = &*(ctxt as *mut CustomViewContext<V>);
-
- context.assume_init_ref().relocatable()
- })
- }
-
- extern "C" fn cb_address_size<V>(ctxt: *mut c_void) -> usize
- where
- V: CustomBinaryView,
- {
- ffi_wrap!("BinaryViewBase::address_size", unsafe {
- let context = &*(ctxt as *mut CustomViewContext<V>);
-
- context.assume_init_ref().address_size()
- })
- }
-
- extern "C" fn cb_save<V>(ctxt: *mut c_void, _fa: *mut BNFileAccessor) -> bool
- where
- V: CustomBinaryView,
- {
- ffi_wrap!("BinaryViewBase::save", unsafe {
- let _context = &*(ctxt as *mut CustomViewContext<V>);
- false
- })
- }
-
- let ctxt = Box::new(CustomViewContext::<V> {
- 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::<V>),
- freeObject: Some(cb_free_object::<V>),
- externalRefTaken: None,
- externalRefReleased: None,
- read: Some(cb_read::<V>),
- write: Some(cb_write::<V>),
- insert: Some(cb_insert::<V>),
- remove: Some(cb_remove::<V>),
- getModification: Some(cb_modification::<V>),
- isValidOffset: Some(cb_offset_valid::<V>),
- isOffsetReadable: Some(cb_offset_readable::<V>),
- isOffsetWritable: Some(cb_offset_writable::<V>),
- isOffsetExecutable: Some(cb_offset_executable::<V>),
- isOffsetBackedByFile: Some(cb_offset_backed_by_file::<V>),
- getNextValidOffset: Some(cb_next_valid_offset::<V>),
- getStart: Some(cb_start::<V>),
- getLength: Some(cb_length::<V>),
- getEntryPoint: Some(cb_entry_point::<V>),
- isExecutable: Some(cb_executable::<V>),
- getDefaultEndianness: Some(cb_endianness::<V>),
- isRelocatable: Some(cb_relocatable::<V>),
- getAddressSize: Some(cb_address_size::<V>),
- save: Some(cb_save::<V>),
- onAfterSnapshotDataApplied: Some(cb_on_after_snapshot_data_applied::<V>),
- };
-
- 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<BinaryView>) -> Result<CustomView<'a>> {
- Ok(CustomView {
- handle: wrapped_view,
- _builder: PhantomData,
- })
- }
-}