binaryninja/
custom_binary_view.rs

1// Copyright 2021-2026 Vector 35 Inc.
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15//! An interface for providing your own [BinaryView]s to Binary Ninja.
16
17use binaryninjacore_sys::*;
18
19pub use binaryninjacore_sys::BNModificationStatus as ModificationStatus;
20
21use std::fmt::Debug;
22use std::marker::PhantomData;
23use std::mem::MaybeUninit;
24use std::os::raw::c_void;
25use std::ptr;
26use std::slice;
27
28use crate::architecture::Architecture;
29use crate::binary_view::{BinaryView, BinaryViewBase, BinaryViewExt, Result};
30use crate::metadata::Metadata;
31use crate::platform::Platform;
32use crate::rc::*;
33use crate::settings::Settings;
34use crate::string::*;
35use crate::Endianness;
36
37/// Registers a custom `BinaryViewType` with the core.
38///
39/// The `constructor` argument is called immediately after successful registration of the type with
40/// the core. The `BinaryViewType` argument passed to `constructor` is the object that the
41/// `AsRef<BinaryViewType>`
42/// implementation of the `CustomBinaryViewType` must return.
43pub fn register_view_type<T, F>(name: &str, long_name: &str, constructor: F) -> &'static T
44where
45    T: CustomBinaryViewType,
46    F: FnOnce(BinaryViewType) -> T,
47{
48    extern "C" fn cb_valid<T>(ctxt: *mut c_void, data: *mut BNBinaryView) -> bool
49    where
50        T: CustomBinaryViewType,
51    {
52        let view_type = unsafe { &*(ctxt as *mut T) };
53        let data = unsafe { BinaryView::ref_from_raw(BNNewViewReference(data)) };
54        let _span = ffi_span!("BinaryViewTypeBase::is_valid_for", data);
55        view_type.is_valid_for(&data)
56    }
57
58    extern "C" fn cb_deprecated<T>(ctxt: *mut c_void) -> bool
59    where
60        T: CustomBinaryViewType,
61    {
62        ffi_wrap!("BinaryViewTypeBase::is_deprecated", unsafe {
63            let view_type = &*(ctxt as *mut T);
64            view_type.is_deprecated()
65        })
66    }
67
68    extern "C" fn cb_force_loadable<T>(ctxt: *mut c_void) -> bool
69    where
70        T: CustomBinaryViewType,
71    {
72        ffi_wrap!("BinaryViewTypeBase::is_force_loadable", unsafe {
73            let view_type = &*(ctxt as *mut T);
74            view_type.is_force_loadable()
75        })
76    }
77
78    extern "C" fn cb_create<T>(ctxt: *mut c_void, data: *mut BNBinaryView) -> *mut BNBinaryView
79    where
80        T: CustomBinaryViewType,
81    {
82        ffi_wrap!("BinaryViewTypeBase::create", unsafe {
83            let view_type = &*(ctxt as *mut T);
84            let data = BinaryView::ref_from_raw(BNNewViewReference(data));
85
86            let builder = CustomViewBuilder {
87                view_type,
88                actual_parent: &data,
89            };
90
91            let _span = ffi_span!("BinaryViewTypeBase::create", data);
92            match view_type.create_custom_view(&data, builder) {
93                Ok(bv) => {
94                    // force a leak of the Ref; failure to do this would result
95                    // in the refcount going to 0 in the process of returning it
96                    // to the core -- we're transferring ownership of the Ref here
97                    Ref::into_raw(bv.handle).handle
98                }
99                Err(_) => ptr::null_mut(),
100            }
101        })
102    }
103
104    extern "C" fn cb_parse<T>(ctxt: *mut c_void, data: *mut BNBinaryView) -> *mut BNBinaryView
105    where
106        T: CustomBinaryViewType,
107    {
108        ffi_wrap!("BinaryViewTypeBase::parse", unsafe {
109            let view_type = &*(ctxt as *mut T);
110            let data = BinaryView::ref_from_raw(BNNewViewReference(data));
111
112            let builder = CustomViewBuilder {
113                view_type,
114                actual_parent: &data,
115            };
116
117            let _span = ffi_span!("BinaryViewTypeBase::parse", data);
118            match view_type.parse_custom_view(&data, builder) {
119                Ok(bv) => {
120                    // force a leak of the Ref; failure to do this would result
121                    // in the refcount going to 0 in the process of returning it
122                    // to the core -- we're transferring ownership of the Ref here
123                    Ref::into_raw(bv.handle).handle
124                }
125                Err(_) => ptr::null_mut(),
126            }
127        })
128    }
129
130    extern "C" fn cb_load_settings<T>(ctxt: *mut c_void, data: *mut BNBinaryView) -> *mut BNSettings
131    where
132        T: CustomBinaryViewType,
133    {
134        ffi_wrap!("BinaryViewTypeBase::load_settings", unsafe {
135            let view_type = &*(ctxt as *mut T);
136            let data = BinaryView::ref_from_raw(BNNewViewReference(data));
137
138            let _span = ffi_span!("BinaryViewTypeBase::load_settings", data);
139            match view_type.load_settings_for_data(&data) {
140                Some(settings) => Ref::into_raw(settings).handle,
141                None => ptr::null_mut() as *mut _,
142            }
143        })
144    }
145
146    let name = name.to_cstr();
147    let name_ptr = name.as_ptr();
148
149    let long_name = long_name.to_cstr();
150    let long_name_ptr = long_name.as_ptr();
151
152    let ctxt = Box::leak(Box::new(MaybeUninit::zeroed()));
153
154    let mut bn_obj = BNCustomBinaryViewType {
155        context: ctxt.as_mut_ptr() as *mut _,
156        create: Some(cb_create::<T>),
157        parse: Some(cb_parse::<T>),
158        isValidForData: Some(cb_valid::<T>),
159        isDeprecated: Some(cb_deprecated::<T>),
160        isForceLoadable: Some(cb_force_loadable::<T>),
161        getLoadSettingsForData: Some(cb_load_settings::<T>),
162    };
163
164    unsafe {
165        let handle = BNRegisterBinaryViewType(name_ptr, long_name_ptr, &mut bn_obj as *mut _);
166        if handle.is_null() {
167            // avoid leaking the space allocated for the type, but also
168            // avoid running its Drop impl (if any -- not that there should
169            // be one since view types live for the life of the process) as
170            // MaybeUninit suppress the Drop implementation of it's inner type
171            drop(Box::from_raw(ctxt));
172            panic!("bvt registration failed");
173        }
174
175        ctxt.write(constructor(BinaryViewType { handle }));
176        ctxt.assume_init_mut()
177    }
178}
179
180pub trait BinaryViewTypeBase: AsRef<BinaryViewType> {
181    /// Is this [`BinaryViewType`] valid for the given the raw [`BinaryView`]?
182    ///
183    /// Typical implementations will read the magic bytes (e.g. 'MZ'), this is a performance-sensitive
184    /// path so prefer inexpensive checks rather than comprehensive ones.
185    fn is_valid_for(&self, data: &BinaryView) -> bool;
186
187    /// Is this [`BinaryViewType`] deprecated and should not be used?
188    ///
189    /// We specify this such that the view type may still be used by existing databases, but not
190    /// newly created views.
191    fn is_deprecated(&self) -> bool {
192        false
193    }
194
195    /// Is this [`BinaryViewType`] able to be loaded forcefully?
196    ///
197    /// If so, it will be shown in the drop-down when a user opens a file with options.
198    fn is_force_loadable(&self) -> bool {
199        false
200    }
201
202    fn default_load_settings_for_data(&self, data: &BinaryView) -> Option<Ref<Settings>> {
203        let settings_handle =
204            unsafe { BNGetBinaryViewDefaultLoadSettingsForData(self.as_ref().handle, data.handle) };
205
206        if settings_handle.is_null() {
207            None
208        } else {
209            unsafe { Some(Settings::ref_from_raw(settings_handle)) }
210        }
211    }
212
213    fn load_settings_for_data(&self, _data: &BinaryView) -> Option<Ref<Settings>> {
214        None
215    }
216}
217
218pub trait BinaryViewTypeExt: BinaryViewTypeBase {
219    fn name(&self) -> String {
220        unsafe { BnString::into_string(BNGetBinaryViewTypeName(self.as_ref().handle)) }
221    }
222
223    fn long_name(&self) -> String {
224        unsafe { BnString::into_string(BNGetBinaryViewTypeLongName(self.as_ref().handle)) }
225    }
226
227    fn register_arch<A: Architecture>(&self, id: u32, endianness: Endianness, arch: &A) {
228        unsafe {
229            BNRegisterArchitectureForViewType(
230                self.as_ref().handle,
231                id,
232                endianness,
233                arch.as_ref().handle,
234            );
235        }
236    }
237
238    fn register_platform(&self, id: u32, plat: &Platform) {
239        let arch = plat.arch();
240
241        unsafe {
242            BNRegisterPlatformForViewType(self.as_ref().handle, id, arch.handle, plat.handle);
243        }
244    }
245
246    /// Expanded identification of [`Platform`] for [`BinaryViewType`]'s. Supersedes [`BinaryViewTypeExt::register_arch`]
247    /// and [`BinaryViewTypeExt::register_platform`], as these have certain edge cases (overloaded elf families, for example)
248    /// that can't be represented.
249    ///
250    /// The callback returns a [`Platform`] object or `None` (failure), and most recently added callbacks are called first
251    /// to allow plugins to override any default behaviors. When a callback returns a platform, architecture will be
252    /// derived from the identified platform.
253    ///
254    /// The [`BinaryView`] is the *parent* view (usually 'Raw') that the [`BinaryView`] is being created for. This
255    /// means that generally speaking the callbacks need to be aware of the underlying file format, however the
256    /// [`BinaryView`] implementation may have created datavars in the 'Raw' view by the time the callback is invoked.
257    /// Behavior regarding when this callback is invoked and what has been made available in the [`BinaryView`] passed as an
258    /// argument to the callback is up to the discretion of the [`BinaryView`] implementation.
259    ///
260    /// The `id` ind `endian` arguments are used as a filter to determine which registered [`Platform`] recognizer callbacks
261    /// are invoked.
262    ///
263    /// Support for this API tentatively requires explicit support in the [`BinaryView`] implementation.
264    fn register_platform_recognizer<R>(&self, id: u32, endian: Endianness, recognizer: R)
265    where
266        R: 'static + Fn(&BinaryView, &Metadata) -> Option<Ref<Platform>> + Send + Sync,
267    {
268        #[repr(C)]
269        struct PlatformRecognizerHandlerContext<R>
270        where
271            R: 'static + Fn(&BinaryView, &Metadata) -> Option<Ref<Platform>> + Send + Sync,
272        {
273            recognizer: R,
274        }
275
276        extern "C" fn cb_recognize_low_level_il<R>(
277            ctxt: *mut c_void,
278            bv: *mut BNBinaryView,
279            metadata: *mut BNMetadata,
280        ) -> *mut BNPlatform
281        where
282            R: 'static + Fn(&BinaryView, &Metadata) -> Option<Ref<Platform>> + Send + Sync,
283        {
284            let context = unsafe { &*(ctxt as *mut PlatformRecognizerHandlerContext<R>) };
285            let bv = unsafe { BinaryView::from_raw(bv).to_owned() };
286            let metadata = unsafe { Metadata::from_raw(metadata).to_owned() };
287            match (context.recognizer)(&bv, &metadata) {
288                Some(plat) => unsafe { Ref::into_raw(plat).handle },
289                None => std::ptr::null_mut(),
290            }
291        }
292
293        let recognizer = PlatformRecognizerHandlerContext { recognizer };
294        // TODO: Currently we leak `recognizer`.
295        let raw = Box::into_raw(Box::new(recognizer));
296
297        unsafe {
298            BNRegisterPlatformRecognizerForViewType(
299                self.as_ref().handle,
300                id as u64,
301                endian,
302                Some(cb_recognize_low_level_il::<R>),
303                raw as *mut c_void,
304            )
305        }
306    }
307
308    fn open(&self, data: &BinaryView) -> Result<Ref<BinaryView>> {
309        let handle = unsafe { BNCreateBinaryViewOfType(self.as_ref().handle, data.handle) };
310
311        if handle.is_null() {
312            // TODO: Proper Result, possibly introduce BNSetError to populate.
313            return Err(());
314        }
315
316        unsafe { Ok(BinaryView::ref_from_raw(handle)) }
317    }
318
319    fn parse(&self, data: &BinaryView) -> Result<Ref<BinaryView>> {
320        let handle = unsafe { BNParseBinaryViewOfType(self.as_ref().handle, data.handle) };
321
322        if handle.is_null() {
323            // TODO: Proper Result, possibly introduce BNSetError to populate.
324            return Err(());
325        }
326
327        unsafe { Ok(BinaryView::ref_from_raw(handle)) }
328    }
329}
330
331impl<T: BinaryViewTypeBase> BinaryViewTypeExt for T {}
332
333/// A [`BinaryViewType`] acts as a factory for [`BinaryView`] objects.
334///
335/// Each file format will have its own type, such as PE, ELF, or Mach-O.
336#[derive(Copy, Clone, PartialEq, Eq, Hash)]
337pub struct BinaryViewType {
338    pub handle: *mut BNBinaryViewType,
339}
340
341impl BinaryViewType {
342    pub(crate) unsafe fn from_raw(handle: *mut BNBinaryViewType) -> Self {
343        debug_assert!(!handle.is_null());
344        Self { handle }
345    }
346
347    pub fn list_all() -> Array<BinaryViewType> {
348        unsafe {
349            let mut count: usize = 0;
350            let types = BNGetBinaryViewTypes(&mut count as *mut _);
351            Array::new(types, count, ())
352        }
353    }
354
355    /// Enumerates all view types and checks to see if the given raw [`BinaryView`] is valid,
356    /// returning only those that are.
357    pub fn valid_types_for_data(data: &BinaryView) -> Array<BinaryViewType> {
358        unsafe {
359            let mut count: usize = 0;
360            let types = BNGetBinaryViewTypesForData(data.handle, &mut count as *mut _);
361            Array::new(types, count, ())
362        }
363    }
364
365    /// Looks up a BinaryViewType by its short name
366    pub fn by_name(name: &str) -> Result<Self> {
367        let bytes = name.to_cstr();
368        let handle = unsafe { BNGetBinaryViewTypeByName(bytes.as_ref().as_ptr() as *const _) };
369        match handle.is_null() {
370            false => Ok(unsafe { BinaryViewType::from_raw(handle) }),
371            true => Err(()),
372        }
373    }
374}
375
376impl BinaryViewTypeBase for BinaryViewType {
377    fn is_valid_for(&self, data: &BinaryView) -> bool {
378        unsafe { BNIsBinaryViewTypeValidForData(self.handle, data.handle) }
379    }
380
381    fn is_deprecated(&self) -> bool {
382        unsafe { BNIsBinaryViewTypeDeprecated(self.handle) }
383    }
384
385    fn is_force_loadable(&self) -> bool {
386        unsafe { BNIsBinaryViewTypeForceLoadable(self.handle) }
387    }
388
389    fn load_settings_for_data(&self, data: &BinaryView) -> Option<Ref<Settings>> {
390        let settings_handle =
391            unsafe { BNGetBinaryViewLoadSettingsForData(self.handle, data.handle) };
392
393        if settings_handle.is_null() {
394            None
395        } else {
396            unsafe { Some(Settings::ref_from_raw(settings_handle)) }
397        }
398    }
399}
400
401impl Debug for BinaryViewType {
402    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
403        f.debug_struct("BinaryViewType")
404            .field("name", &self.name())
405            .field("long_name", &self.long_name())
406            .finish()
407    }
408}
409
410impl CoreArrayProvider for BinaryViewType {
411    type Raw = *mut BNBinaryViewType;
412    type Context = ();
413    type Wrapped<'a> = Guard<'a, BinaryViewType>;
414}
415
416unsafe impl CoreArrayProviderInner for BinaryViewType {
417    unsafe fn free(raw: *mut Self::Raw, _count: usize, _context: &Self::Context) {
418        BNFreeBinaryViewTypeList(raw);
419    }
420
421    unsafe fn wrap_raw<'a>(raw: &'a Self::Raw, _context: &'a Self::Context) -> Self::Wrapped<'a> {
422        Guard::new(BinaryViewType::from_raw(*raw), &())
423    }
424}
425
426impl AsRef<BinaryViewType> for BinaryViewType {
427    fn as_ref(&self) -> &Self {
428        self
429    }
430}
431
432unsafe impl Send for BinaryViewType {}
433unsafe impl Sync for BinaryViewType {}
434
435pub trait CustomBinaryViewType: 'static + BinaryViewTypeBase + Sync {
436    fn create_custom_view<'builder>(
437        &self,
438        data: &BinaryView,
439        builder: CustomViewBuilder<'builder, Self>,
440    ) -> Result<CustomView<'builder>>;
441
442    fn parse_custom_view<'builder>(
443        &self,
444        data: &BinaryView,
445        builder: CustomViewBuilder<'builder, Self>,
446    ) -> Result<CustomView<'builder>> {
447        // TODO: Check to make sure data.type_name is not Self::type_name ?
448        self.create_custom_view(data, builder)
449    }
450}
451
452/// Represents a request from the core to instantiate a custom BinaryView
453pub struct CustomViewBuilder<'a, T: CustomBinaryViewType + ?Sized> {
454    view_type: &'a T,
455    actual_parent: &'a BinaryView,
456}
457
458pub unsafe trait CustomBinaryView: 'static + BinaryViewBase + Sync + Sized {
459    type Args: Send;
460
461    fn new(handle: &BinaryView, args: &Self::Args) -> Result<Self>;
462    fn init(&mut self, args: Self::Args) -> Result<()>;
463    fn on_after_snapshot_data_applied(&mut self) {}
464}
465
466/// Represents a partially initialized custom `BinaryView` that should be returned to the core
467/// from the `create_custom_view` method of a `CustomBinaryViewType`.
468#[must_use]
469pub struct CustomView<'builder> {
470    // this object can't actually be treated like a real
471    // BinaryView as it isn't fully initialized until the
472    // core receives it from the BNCustomBinaryViewType::create
473    // callback.
474    handle: Ref<BinaryView>,
475    _builder: PhantomData<&'builder ()>,
476}
477
478impl<'a, T: CustomBinaryViewType> CustomViewBuilder<'a, T> {
479    /// Begins creating a custom BinaryView.
480    ///
481    /// This function may only be called from the `create_custom_view` function of a
482    /// `CustomBinaryViewType`.
483    ///
484    /// `parent` specifies the view that the core will treat as the parent view, that
485    /// Segments created against the created view will be backed by `parent`. It will
486    /// usually be (but is not required to be) the `data` argument of the `create_custom_view`
487    /// callback.
488    ///
489    /// `constructor` will not be called until well after the value returned by this function
490    /// has been returned by `create_custom_view` callback to the core, and may not ever
491    /// be called if the value returned by this function is dropped or leaked.
492    ///
493    /// # Errors
494    ///
495    /// This function will fail if the `FileMetadata` object associated with the *expected* parent
496    /// (i.e., the `data` argument passed to the `create_custom_view` function) already has an
497    /// associated `BinaryView` of the same `CustomBinaryViewType`. Multiple `BinaryView` objects
498    /// of the same `BinaryViewType` belonging to the same `FileMetadata` object is prohibited and
499    /// can cause strange, delayed segmentation faults.
500    ///
501    /// # Safety
502    ///
503    /// `constructor` should avoid doing anything with the object it returns, especially anything
504    /// that would cause the core to invoke any of the `BinaryViewBase` methods. The core isn't
505    /// going to consider the object fully initialized until after that callback has run.
506    ///
507    /// The `BinaryView` argument passed to the constructor function is the object that is expected
508    /// to be returned by the `AsRef<BinaryView>` implementation required by the `BinaryViewBase` trait.
509    ///  TODO FIXME whelp this is broke going to need 2 init callbacks
510    pub fn create<V>(self, parent: &BinaryView, view_args: V::Args) -> Result<CustomView<'a>>
511    where
512        V: CustomBinaryView,
513    {
514        let file = self.actual_parent.file();
515        let view_type = self.view_type;
516
517        let view_name = view_type.name();
518
519        if let Some(bv) = file.view_of_type(&view_name) {
520            // while it seems to work most of the time, you can get really unlucky
521            // if a free of the existing view of the same type kicks off while
522            // BNCreateBinaryViewOfType is still running. the freeObject callback
523            // will run for the new view before we've even finished initializing,
524            // and that's all she wrote.
525            //
526            // even if we deal with it gracefully in cb_free_object,
527            // BNCreateBinaryViewOfType is still going to crash, so we're just
528            // going to try and stop this from happening in the first place.
529            tracing::error!(
530                "attempt to create duplicate view of type '{}' (existing: {:?})",
531                view_name,
532                bv.handle
533            );
534
535            return Err(());
536        }
537
538        // struct representing the context of a BNCustomBinaryView. Can be safely
539        // dropped at any moment.
540        struct CustomViewContext<V>
541        where
542            V: CustomBinaryView,
543        {
544            raw_handle: *mut BNBinaryView,
545            state: CustomViewContextState<V>,
546        }
547
548        enum CustomViewContextState<V>
549        where
550            V: CustomBinaryView,
551        {
552            Uninitialized { args: V::Args },
553            Initialized { view: V },
554            // dummy state, used as a helper to change states, only happen if the
555            // `new` or `init` function fails.
556            None,
557        }
558
559        impl<V: CustomBinaryView> CustomViewContext<V> {
560            fn assume_init_ref(&self) -> &V {
561                let CustomViewContextState::Initialized { view } = &self.state else {
562                    panic!("CustomViewContextState in invalid state");
563                };
564                view
565            }
566        }
567
568        extern "C" fn cb_init<V>(ctxt: *mut c_void) -> bool
569        where
570            V: CustomBinaryView,
571        {
572            ffi_wrap!("BinaryViewBase::init", unsafe {
573                let context = &mut *(ctxt as *mut CustomViewContext<V>);
574                let handle = BinaryView::ref_from_raw(context.raw_handle);
575
576                // take the uninitialized state and use the args to call init
577                let mut state = CustomViewContextState::None;
578                core::mem::swap(&mut context.state, &mut state);
579                let CustomViewContextState::Uninitialized { args } = state else {
580                    panic!("CustomViewContextState in invalid state");
581                };
582                match V::new(handle.as_ref(), &args) {
583                    Ok(mut view) => match view.init(args) {
584                        Ok(_) => {
585                            // put the initialized state
586                            context.state = CustomViewContextState::Initialized { view };
587                            true
588                        }
589                        Err(_) => {
590                            tracing::error!(
591                                "CustomBinaryView::init failed; custom view returned Err"
592                            );
593                            false
594                        }
595                    },
596                    Err(_) => {
597                        tracing::error!("CustomBinaryView::new failed; custom view returned Err");
598                        false
599                    }
600                }
601            })
602        }
603
604        extern "C" fn cb_on_after_snapshot_data_applied<V>(ctxt: *mut c_void)
605        where
606            V: CustomBinaryView,
607        {
608            ffi_wrap!("BinaryViewBase::onAfterSnapshotDataApplied", unsafe {
609                let context = &mut *(ctxt as *mut CustomViewContext<V>);
610                if let CustomViewContextState::Initialized { view } = &mut context.state {
611                    view.on_after_snapshot_data_applied();
612                }
613            })
614        }
615
616        extern "C" fn cb_free_object<V>(ctxt: *mut c_void)
617        where
618            V: CustomBinaryView,
619        {
620            ffi_wrap!("BinaryViewBase::freeObject", unsafe {
621                let context = ctxt as *mut CustomViewContext<V>;
622                let context = Box::from_raw(context);
623
624                if context.raw_handle.is_null() {
625                    // being called here is essentially a guarantee that BNCreateBinaryViewOfType
626                    // is above above us on the call stack somewhere -- no matter what we do, a crash
627                    // is pretty much certain at this point.
628                    //
629                    // this has been observed when two views of the same BinaryViewType are created
630                    // against the same BNFileMetaData object, and one of the views gets freed while
631                    // the second one is being initialized -- somehow the partially initialized one
632                    // gets freed before BNCreateBinaryViewOfType returns.
633                    //
634                    // multiples views of the same BinaryViewType in a BNFileMetaData object are
635                    // prohibited, so an API contract was violated in order to get here.
636                    //
637                    // if we're here, it's too late to do anything about it, though we can at least not
638                    // run the destructor on the custom view since that memory is uninitialized.
639                    tracing::error!(
640                      "BinaryViewBase::freeObject called on partially initialized object! crash imminent!"
641                    );
642                } else if matches!(
643                    &context.state,
644                    CustomViewContextState::None | CustomViewContextState::Uninitialized { .. }
645                ) {
646                    // making it here means somebody went out of their way to leak a BinaryView
647                    // after calling BNCreateCustomView and never gave the BNBinaryView handle
648                    // to the core (which would have called cb_init)
649                    //
650                    // the result is a half-initialized BinaryView that the core will happily hand out
651                    // references to via BNGetFileViewofType even though it was never initialized
652                    // all the way.
653                    //
654                    // TODO update when this corner case gets fixed in the core?
655                    //
656                    // we can't do anything to prevent this, but we can at least have the crash
657                    // not be our fault.
658                    tracing::error!("BinaryViewBase::freeObject called on leaked/never initialized custom view!");
659                }
660            })
661        }
662
663        extern "C" fn cb_read<V>(
664            ctxt: *mut c_void,
665            dest: *mut c_void,
666            offset: u64,
667            len: usize,
668        ) -> usize
669        where
670            V: CustomBinaryView,
671        {
672            ffi_wrap!("BinaryViewBase::read", unsafe {
673                let context = &*(ctxt as *mut CustomViewContext<V>);
674                let dest = slice::from_raw_parts_mut(dest as *mut u8, len);
675                context.assume_init_ref().read(dest, offset)
676            })
677        }
678
679        extern "C" fn cb_write<V>(
680            ctxt: *mut c_void,
681            offset: u64,
682            src: *const c_void,
683            len: usize,
684        ) -> usize
685        where
686            V: CustomBinaryView,
687        {
688            ffi_wrap!("BinaryViewBase::write", unsafe {
689                let context = &*(ctxt as *mut CustomViewContext<V>);
690                let src = slice::from_raw_parts(src as *const u8, len);
691                context.assume_init_ref().write(offset, src)
692            })
693        }
694
695        extern "C" fn cb_insert<V>(
696            ctxt: *mut c_void,
697            offset: u64,
698            src: *const c_void,
699            len: usize,
700        ) -> usize
701        where
702            V: CustomBinaryView,
703        {
704            ffi_wrap!("BinaryViewBase::insert", unsafe {
705                let context = &*(ctxt as *mut CustomViewContext<V>);
706                let src = slice::from_raw_parts(src as *const u8, len);
707                context.assume_init_ref().insert(offset, src)
708            })
709        }
710
711        extern "C" fn cb_remove<V>(ctxt: *mut c_void, offset: u64, len: u64) -> usize
712        where
713            V: CustomBinaryView,
714        {
715            ffi_wrap!("BinaryViewBase::remove", unsafe {
716                let context = &*(ctxt as *mut CustomViewContext<V>);
717                context.assume_init_ref().remove(offset, len as usize)
718            })
719        }
720
721        extern "C" fn cb_modification<V>(ctxt: *mut c_void, offset: u64) -> ModificationStatus
722        where
723            V: CustomBinaryView,
724        {
725            ffi_wrap!("BinaryViewBase::modification_status", unsafe {
726                let context = &*(ctxt as *mut CustomViewContext<V>);
727                context.assume_init_ref().modification_status(offset)
728            })
729        }
730
731        extern "C" fn cb_offset_valid<V>(ctxt: *mut c_void, offset: u64) -> bool
732        where
733            V: CustomBinaryView,
734        {
735            ffi_wrap!("BinaryViewBase::offset_valid", unsafe {
736                let context = &*(ctxt as *mut CustomViewContext<V>);
737                context.assume_init_ref().offset_valid(offset)
738            })
739        }
740
741        extern "C" fn cb_offset_readable<V>(ctxt: *mut c_void, offset: u64) -> bool
742        where
743            V: CustomBinaryView,
744        {
745            ffi_wrap!("BinaryViewBase::readable", unsafe {
746                let context = &*(ctxt as *mut CustomViewContext<V>);
747                context.assume_init_ref().offset_readable(offset)
748            })
749        }
750
751        extern "C" fn cb_offset_writable<V>(ctxt: *mut c_void, offset: u64) -> bool
752        where
753            V: CustomBinaryView,
754        {
755            ffi_wrap!("BinaryViewBase::writable", unsafe {
756                let context = &*(ctxt as *mut CustomViewContext<V>);
757                context.assume_init_ref().offset_writable(offset)
758            })
759        }
760
761        extern "C" fn cb_offset_executable<V>(ctxt: *mut c_void, offset: u64) -> bool
762        where
763            V: CustomBinaryView,
764        {
765            ffi_wrap!("BinaryViewBase::offset_executable", unsafe {
766                let context = &*(ctxt as *mut CustomViewContext<V>);
767                context.assume_init_ref().offset_executable(offset)
768            })
769        }
770
771        extern "C" fn cb_offset_backed_by_file<V>(ctxt: *mut c_void, offset: u64) -> bool
772        where
773            V: CustomBinaryView,
774        {
775            ffi_wrap!("BinaryViewBase::offset_backed_by_file", unsafe {
776                let context = &*(ctxt as *mut CustomViewContext<V>);
777                context.assume_init_ref().offset_backed_by_file(offset)
778            })
779        }
780
781        extern "C" fn cb_next_valid_offset<V>(ctxt: *mut c_void, offset: u64) -> u64
782        where
783            V: CustomBinaryView,
784        {
785            ffi_wrap!("BinaryViewBase::next_valid_offset_after", unsafe {
786                let context = &*(ctxt as *mut CustomViewContext<V>);
787                context.assume_init_ref().next_valid_offset_after(offset)
788            })
789        }
790
791        extern "C" fn cb_start<V>(ctxt: *mut c_void) -> u64
792        where
793            V: CustomBinaryView,
794        {
795            ffi_wrap!("BinaryViewBase::start", unsafe {
796                let context = &*(ctxt as *mut CustomViewContext<V>);
797                context.assume_init_ref().start()
798            })
799        }
800
801        extern "C" fn cb_length<V>(ctxt: *mut c_void) -> u64
802        where
803            V: CustomBinaryView,
804        {
805            ffi_wrap!("BinaryViewBase::len", unsafe {
806                let context = &*(ctxt as *mut CustomViewContext<V>);
807                context.assume_init_ref().len()
808            })
809        }
810
811        extern "C" fn cb_entry_point<V>(ctxt: *mut c_void) -> u64
812        where
813            V: CustomBinaryView,
814        {
815            ffi_wrap!("BinaryViewBase::entry_point", unsafe {
816                let context = &*(ctxt as *mut CustomViewContext<V>);
817                context.assume_init_ref().entry_point()
818            })
819        }
820
821        extern "C" fn cb_executable<V>(ctxt: *mut c_void) -> bool
822        where
823            V: CustomBinaryView,
824        {
825            ffi_wrap!("BinaryViewBase::executable", unsafe {
826                let context = &*(ctxt as *mut CustomViewContext<V>);
827                context.assume_init_ref().executable()
828            })
829        }
830
831        extern "C" fn cb_endianness<V>(ctxt: *mut c_void) -> Endianness
832        where
833            V: CustomBinaryView,
834        {
835            ffi_wrap!("BinaryViewBase::default_endianness", unsafe {
836                let context = &*(ctxt as *mut CustomViewContext<V>);
837
838                context.assume_init_ref().default_endianness()
839            })
840        }
841
842        extern "C" fn cb_relocatable<V>(ctxt: *mut c_void) -> bool
843        where
844            V: CustomBinaryView,
845        {
846            ffi_wrap!("BinaryViewBase::relocatable", unsafe {
847                let context = &*(ctxt as *mut CustomViewContext<V>);
848
849                context.assume_init_ref().relocatable()
850            })
851        }
852
853        extern "C" fn cb_address_size<V>(ctxt: *mut c_void) -> usize
854        where
855            V: CustomBinaryView,
856        {
857            ffi_wrap!("BinaryViewBase::address_size", unsafe {
858                let context = &*(ctxt as *mut CustomViewContext<V>);
859
860                context.assume_init_ref().address_size()
861            })
862        }
863
864        extern "C" fn cb_save<V>(ctxt: *mut c_void, _fa: *mut BNFileAccessor) -> bool
865        where
866            V: CustomBinaryView,
867        {
868            ffi_wrap!("BinaryViewBase::save", unsafe {
869                let _context = &*(ctxt as *mut CustomViewContext<V>);
870                false
871            })
872        }
873
874        let ctxt = Box::new(CustomViewContext::<V> {
875            raw_handle: ptr::null_mut(),
876            state: CustomViewContextState::Uninitialized { args: view_args },
877        });
878
879        let ctxt = Box::into_raw(ctxt);
880
881        let mut bn_obj = BNCustomBinaryView {
882            context: ctxt as *mut _,
883            init: Some(cb_init::<V>),
884            freeObject: Some(cb_free_object::<V>),
885            externalRefTaken: None,
886            externalRefReleased: None,
887            read: Some(cb_read::<V>),
888            write: Some(cb_write::<V>),
889            insert: Some(cb_insert::<V>),
890            remove: Some(cb_remove::<V>),
891            getModification: Some(cb_modification::<V>),
892            isValidOffset: Some(cb_offset_valid::<V>),
893            isOffsetReadable: Some(cb_offset_readable::<V>),
894            isOffsetWritable: Some(cb_offset_writable::<V>),
895            isOffsetExecutable: Some(cb_offset_executable::<V>),
896            isOffsetBackedByFile: Some(cb_offset_backed_by_file::<V>),
897            getNextValidOffset: Some(cb_next_valid_offset::<V>),
898            getStart: Some(cb_start::<V>),
899            getLength: Some(cb_length::<V>),
900            getEntryPoint: Some(cb_entry_point::<V>),
901            isExecutable: Some(cb_executable::<V>),
902            getDefaultEndianness: Some(cb_endianness::<V>),
903            isRelocatable: Some(cb_relocatable::<V>),
904            getAddressSize: Some(cb_address_size::<V>),
905            save: Some(cb_save::<V>),
906            onAfterSnapshotDataApplied: Some(cb_on_after_snapshot_data_applied::<V>),
907        };
908
909        let view_name = view_name.to_cstr();
910        unsafe {
911            let res = BNCreateCustomBinaryView(
912                view_name.as_ptr(),
913                file.handle,
914                parent.handle,
915                &mut bn_obj,
916            );
917            assert!(
918                !res.is_null(),
919                "BNCreateCustomBinaryView cannot return null"
920            );
921            (*ctxt).raw_handle = res;
922            Ok(CustomView {
923                handle: BinaryView::ref_from_raw(res),
924                _builder: PhantomData,
925            })
926        }
927    }
928
929    pub fn wrap_existing(self, wrapped_view: Ref<BinaryView>) -> Result<CustomView<'a>> {
930        Ok(CustomView {
931            handle: wrapped_view,
932            _builder: PhantomData,
933        })
934    }
935}