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}
464
465/// Represents a partially initialized custom `BinaryView` that should be returned to the core
466/// from the `create_custom_view` method of a `CustomBinaryViewType`.
467#[must_use]
468pub struct CustomView<'builder> {
469    // this object can't actually be treated like a real
470    // BinaryView as it isn't fully initialized until the
471    // core receives it from the BNCustomBinaryViewType::create
472    // callback.
473    handle: Ref<BinaryView>,
474    _builder: PhantomData<&'builder ()>,
475}
476
477impl<'a, T: CustomBinaryViewType> CustomViewBuilder<'a, T> {
478    /// Begins creating a custom BinaryView.
479    ///
480    /// This function may only be called from the `create_custom_view` function of a
481    /// `CustomBinaryViewType`.
482    ///
483    /// `parent` specifies the view that the core will treat as the parent view, that
484    /// Segments created against the created view will be backed by `parent`. It will
485    /// usually be (but is not required to be) the `data` argument of the `create_custom_view`
486    /// callback.
487    ///
488    /// `constructor` will not be called until well after the value returned by this function
489    /// has been returned by `create_custom_view` callback to the core, and may not ever
490    /// be called if the value returned by this function is dropped or leaked.
491    ///
492    /// # Errors
493    ///
494    /// This function will fail if the `FileMetadata` object associated with the *expected* parent
495    /// (i.e., the `data` argument passed to the `create_custom_view` function) already has an
496    /// associated `BinaryView` of the same `CustomBinaryViewType`. Multiple `BinaryView` objects
497    /// of the same `BinaryViewType` belonging to the same `FileMetadata` object is prohibited and
498    /// can cause strange, delayed segmentation faults.
499    ///
500    /// # Safety
501    ///
502    /// `constructor` should avoid doing anything with the object it returns, especially anything
503    /// that would cause the core to invoke any of the `BinaryViewBase` methods. The core isn't
504    /// going to consider the object fully initialized until after that callback has run.
505    ///
506    /// The `BinaryView` argument passed to the constructor function is the object that is expected
507    /// to be returned by the `AsRef<BinaryView>` implementation required by the `BinaryViewBase` trait.
508    ///  TODO FIXME whelp this is broke going to need 2 init callbacks
509    pub fn create<V>(self, parent: &BinaryView, view_args: V::Args) -> Result<CustomView<'a>>
510    where
511        V: CustomBinaryView,
512    {
513        let file = self.actual_parent.file();
514        let view_type = self.view_type;
515
516        let view_name = view_type.name();
517
518        if let Some(bv) = file.view_of_type(&view_name) {
519            // while it seems to work most of the time, you can get really unlucky
520            // if a free of the existing view of the same type kicks off while
521            // BNCreateBinaryViewOfType is still running. the freeObject callback
522            // will run for the new view before we've even finished initializing,
523            // and that's all she wrote.
524            //
525            // even if we deal with it gracefully in cb_free_object,
526            // BNCreateBinaryViewOfType is still going to crash, so we're just
527            // going to try and stop this from happening in the first place.
528            tracing::error!(
529                "attempt to create duplicate view of type '{}' (existing: {:?})",
530                view_name,
531                bv.handle
532            );
533
534            return Err(());
535        }
536
537        // struct representing the context of a BNCustomBinaryView. Can be safely
538        // dropped at any moment.
539        struct CustomViewContext<V>
540        where
541            V: CustomBinaryView,
542        {
543            raw_handle: *mut BNBinaryView,
544            state: CustomViewContextState<V>,
545        }
546
547        enum CustomViewContextState<V>
548        where
549            V: CustomBinaryView,
550        {
551            Uninitialized { args: V::Args },
552            Initialized { view: V },
553            // dummy state, used as a helper to change states, only happen if the
554            // `new` or `init` function fails.
555            None,
556        }
557
558        impl<V: CustomBinaryView> CustomViewContext<V> {
559            fn assume_init_ref(&self) -> &V {
560                let CustomViewContextState::Initialized { view } = &self.state else {
561                    panic!("CustomViewContextState in invalid state");
562                };
563                view
564            }
565        }
566
567        extern "C" fn cb_init<V>(ctxt: *mut c_void) -> bool
568        where
569            V: CustomBinaryView,
570        {
571            ffi_wrap!("BinaryViewBase::init", unsafe {
572                let context = &mut *(ctxt as *mut CustomViewContext<V>);
573                let handle = BinaryView::ref_from_raw(context.raw_handle);
574
575                // take the uninitialized state and use the args to call init
576                let mut state = CustomViewContextState::None;
577                core::mem::swap(&mut context.state, &mut state);
578                let CustomViewContextState::Uninitialized { args } = state else {
579                    panic!("CustomViewContextState in invalid state");
580                };
581                match V::new(handle.as_ref(), &args) {
582                    Ok(mut view) => match view.init(args) {
583                        Ok(_) => {
584                            // put the initialized state
585                            context.state = CustomViewContextState::Initialized { view };
586                            true
587                        }
588                        Err(_) => {
589                            tracing::error!(
590                                "CustomBinaryView::init failed; custom view returned Err"
591                            );
592                            false
593                        }
594                    },
595                    Err(_) => {
596                        tracing::error!("CustomBinaryView::new failed; custom view returned Err");
597                        false
598                    }
599                }
600            })
601        }
602
603        extern "C" fn cb_free_object<V>(ctxt: *mut c_void)
604        where
605            V: CustomBinaryView,
606        {
607            ffi_wrap!("BinaryViewBase::freeObject", unsafe {
608                let context = ctxt as *mut CustomViewContext<V>;
609                let context = Box::from_raw(context);
610
611                if context.raw_handle.is_null() {
612                    // being called here is essentially a guarantee that BNCreateBinaryViewOfType
613                    // is above above us on the call stack somewhere -- no matter what we do, a crash
614                    // is pretty much certain at this point.
615                    //
616                    // this has been observed when two views of the same BinaryViewType are created
617                    // against the same BNFileMetaData object, and one of the views gets freed while
618                    // the second one is being initialized -- somehow the partially initialized one
619                    // gets freed before BNCreateBinaryViewOfType returns.
620                    //
621                    // multiples views of the same BinaryViewType in a BNFileMetaData object are
622                    // prohibited, so an API contract was violated in order to get here.
623                    //
624                    // if we're here, it's too late to do anything about it, though we can at least not
625                    // run the destructor on the custom view since that memory is uninitialized.
626                    tracing::error!(
627                      "BinaryViewBase::freeObject called on partially initialized object! crash imminent!"
628                    );
629                } else if matches!(
630                    &context.state,
631                    CustomViewContextState::None | CustomViewContextState::Uninitialized { .. }
632                ) {
633                    // making it here means somebody went out of their way to leak a BinaryView
634                    // after calling BNCreateCustomView and never gave the BNBinaryView handle
635                    // to the core (which would have called cb_init)
636                    //
637                    // the result is a half-initialized BinaryView that the core will happily hand out
638                    // references to via BNGetFileViewofType even though it was never initialized
639                    // all the way.
640                    //
641                    // TODO update when this corner case gets fixed in the core?
642                    //
643                    // we can't do anything to prevent this, but we can at least have the crash
644                    // not be our fault.
645                    tracing::error!("BinaryViewBase::freeObject called on leaked/never initialized custom view!");
646                }
647            })
648        }
649
650        extern "C" fn cb_read<V>(
651            ctxt: *mut c_void,
652            dest: *mut c_void,
653            offset: u64,
654            len: usize,
655        ) -> usize
656        where
657            V: CustomBinaryView,
658        {
659            ffi_wrap!("BinaryViewBase::read", unsafe {
660                let context = &*(ctxt as *mut CustomViewContext<V>);
661                let dest = slice::from_raw_parts_mut(dest as *mut u8, len);
662                context.assume_init_ref().read(dest, offset)
663            })
664        }
665
666        extern "C" fn cb_write<V>(
667            ctxt: *mut c_void,
668            offset: u64,
669            src: *const c_void,
670            len: usize,
671        ) -> usize
672        where
673            V: CustomBinaryView,
674        {
675            ffi_wrap!("BinaryViewBase::write", unsafe {
676                let context = &*(ctxt as *mut CustomViewContext<V>);
677                let src = slice::from_raw_parts(src as *const u8, len);
678                context.assume_init_ref().write(offset, src)
679            })
680        }
681
682        extern "C" fn cb_insert<V>(
683            ctxt: *mut c_void,
684            offset: u64,
685            src: *const c_void,
686            len: usize,
687        ) -> usize
688        where
689            V: CustomBinaryView,
690        {
691            ffi_wrap!("BinaryViewBase::insert", unsafe {
692                let context = &*(ctxt as *mut CustomViewContext<V>);
693                let src = slice::from_raw_parts(src as *const u8, len);
694                context.assume_init_ref().insert(offset, src)
695            })
696        }
697
698        extern "C" fn cb_remove<V>(ctxt: *mut c_void, offset: u64, len: u64) -> usize
699        where
700            V: CustomBinaryView,
701        {
702            ffi_wrap!("BinaryViewBase::remove", unsafe {
703                let context = &*(ctxt as *mut CustomViewContext<V>);
704                context.assume_init_ref().remove(offset, len as usize)
705            })
706        }
707
708        extern "C" fn cb_modification<V>(ctxt: *mut c_void, offset: u64) -> ModificationStatus
709        where
710            V: CustomBinaryView,
711        {
712            ffi_wrap!("BinaryViewBase::modification_status", unsafe {
713                let context = &*(ctxt as *mut CustomViewContext<V>);
714                context.assume_init_ref().modification_status(offset)
715            })
716        }
717
718        extern "C" fn cb_offset_valid<V>(ctxt: *mut c_void, offset: u64) -> bool
719        where
720            V: CustomBinaryView,
721        {
722            ffi_wrap!("BinaryViewBase::offset_valid", unsafe {
723                let context = &*(ctxt as *mut CustomViewContext<V>);
724                context.assume_init_ref().offset_valid(offset)
725            })
726        }
727
728        extern "C" fn cb_offset_readable<V>(ctxt: *mut c_void, offset: u64) -> bool
729        where
730            V: CustomBinaryView,
731        {
732            ffi_wrap!("BinaryViewBase::readable", unsafe {
733                let context = &*(ctxt as *mut CustomViewContext<V>);
734                context.assume_init_ref().offset_readable(offset)
735            })
736        }
737
738        extern "C" fn cb_offset_writable<V>(ctxt: *mut c_void, offset: u64) -> bool
739        where
740            V: CustomBinaryView,
741        {
742            ffi_wrap!("BinaryViewBase::writable", unsafe {
743                let context = &*(ctxt as *mut CustomViewContext<V>);
744                context.assume_init_ref().offset_writable(offset)
745            })
746        }
747
748        extern "C" fn cb_offset_executable<V>(ctxt: *mut c_void, offset: u64) -> bool
749        where
750            V: CustomBinaryView,
751        {
752            ffi_wrap!("BinaryViewBase::offset_executable", unsafe {
753                let context = &*(ctxt as *mut CustomViewContext<V>);
754                context.assume_init_ref().offset_executable(offset)
755            })
756        }
757
758        extern "C" fn cb_offset_backed_by_file<V>(ctxt: *mut c_void, offset: u64) -> bool
759        where
760            V: CustomBinaryView,
761        {
762            ffi_wrap!("BinaryViewBase::offset_backed_by_file", unsafe {
763                let context = &*(ctxt as *mut CustomViewContext<V>);
764                context.assume_init_ref().offset_backed_by_file(offset)
765            })
766        }
767
768        extern "C" fn cb_next_valid_offset<V>(ctxt: *mut c_void, offset: u64) -> u64
769        where
770            V: CustomBinaryView,
771        {
772            ffi_wrap!("BinaryViewBase::next_valid_offset_after", unsafe {
773                let context = &*(ctxt as *mut CustomViewContext<V>);
774                context.assume_init_ref().next_valid_offset_after(offset)
775            })
776        }
777
778        extern "C" fn cb_start<V>(ctxt: *mut c_void) -> u64
779        where
780            V: CustomBinaryView,
781        {
782            ffi_wrap!("BinaryViewBase::start", unsafe {
783                let context = &*(ctxt as *mut CustomViewContext<V>);
784                context.assume_init_ref().start()
785            })
786        }
787
788        extern "C" fn cb_length<V>(ctxt: *mut c_void) -> u64
789        where
790            V: CustomBinaryView,
791        {
792            ffi_wrap!("BinaryViewBase::len", unsafe {
793                let context = &*(ctxt as *mut CustomViewContext<V>);
794                context.assume_init_ref().len()
795            })
796        }
797
798        extern "C" fn cb_entry_point<V>(ctxt: *mut c_void) -> u64
799        where
800            V: CustomBinaryView,
801        {
802            ffi_wrap!("BinaryViewBase::entry_point", unsafe {
803                let context = &*(ctxt as *mut CustomViewContext<V>);
804                context.assume_init_ref().entry_point()
805            })
806        }
807
808        extern "C" fn cb_executable<V>(ctxt: *mut c_void) -> bool
809        where
810            V: CustomBinaryView,
811        {
812            ffi_wrap!("BinaryViewBase::executable", unsafe {
813                let context = &*(ctxt as *mut CustomViewContext<V>);
814                context.assume_init_ref().executable()
815            })
816        }
817
818        extern "C" fn cb_endianness<V>(ctxt: *mut c_void) -> Endianness
819        where
820            V: CustomBinaryView,
821        {
822            ffi_wrap!("BinaryViewBase::default_endianness", unsafe {
823                let context = &*(ctxt as *mut CustomViewContext<V>);
824
825                context.assume_init_ref().default_endianness()
826            })
827        }
828
829        extern "C" fn cb_relocatable<V>(ctxt: *mut c_void) -> bool
830        where
831            V: CustomBinaryView,
832        {
833            ffi_wrap!("BinaryViewBase::relocatable", unsafe {
834                let context = &*(ctxt as *mut CustomViewContext<V>);
835
836                context.assume_init_ref().relocatable()
837            })
838        }
839
840        extern "C" fn cb_address_size<V>(ctxt: *mut c_void) -> usize
841        where
842            V: CustomBinaryView,
843        {
844            ffi_wrap!("BinaryViewBase::address_size", unsafe {
845                let context = &*(ctxt as *mut CustomViewContext<V>);
846
847                context.assume_init_ref().address_size()
848            })
849        }
850
851        extern "C" fn cb_save<V>(ctxt: *mut c_void, _fa: *mut BNFileAccessor) -> bool
852        where
853            V: CustomBinaryView,
854        {
855            ffi_wrap!("BinaryViewBase::save", unsafe {
856                let _context = &*(ctxt as *mut CustomViewContext<V>);
857                false
858            })
859        }
860
861        let ctxt = Box::new(CustomViewContext::<V> {
862            raw_handle: ptr::null_mut(),
863            state: CustomViewContextState::Uninitialized { args: view_args },
864        });
865
866        let ctxt = Box::into_raw(ctxt);
867
868        let mut bn_obj = BNCustomBinaryView {
869            context: ctxt as *mut _,
870            init: Some(cb_init::<V>),
871            freeObject: Some(cb_free_object::<V>),
872            externalRefTaken: None,
873            externalRefReleased: None,
874            read: Some(cb_read::<V>),
875            write: Some(cb_write::<V>),
876            insert: Some(cb_insert::<V>),
877            remove: Some(cb_remove::<V>),
878            getModification: Some(cb_modification::<V>),
879            isValidOffset: Some(cb_offset_valid::<V>),
880            isOffsetReadable: Some(cb_offset_readable::<V>),
881            isOffsetWritable: Some(cb_offset_writable::<V>),
882            isOffsetExecutable: Some(cb_offset_executable::<V>),
883            isOffsetBackedByFile: Some(cb_offset_backed_by_file::<V>),
884            getNextValidOffset: Some(cb_next_valid_offset::<V>),
885            getStart: Some(cb_start::<V>),
886            getLength: Some(cb_length::<V>),
887            getEntryPoint: Some(cb_entry_point::<V>),
888            isExecutable: Some(cb_executable::<V>),
889            getDefaultEndianness: Some(cb_endianness::<V>),
890            isRelocatable: Some(cb_relocatable::<V>),
891            getAddressSize: Some(cb_address_size::<V>),
892            save: Some(cb_save::<V>),
893        };
894
895        let view_name = view_name.to_cstr();
896        unsafe {
897            let res = BNCreateCustomBinaryView(
898                view_name.as_ptr(),
899                file.handle,
900                parent.handle,
901                &mut bn_obj,
902            );
903            assert!(
904                !res.is_null(),
905                "BNCreateCustomBinaryView cannot return null"
906            );
907            (*ctxt).raw_handle = res;
908            Ok(CustomView {
909                handle: BinaryView::ref_from_raw(res),
910                _builder: PhantomData,
911            })
912        }
913    }
914
915    pub fn wrap_existing(self, wrapped_view: Ref<BinaryView>) -> Result<CustomView<'a>> {
916        Ok(CustomView {
917            handle: wrapped_view,
918            _builder: PhantomData,
919        })
920    }
921}