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