binaryninja/
custom_binary_view.rs1// 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}