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