binaryninja/
custom_binary_view.rs

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