binaryninja/
object_destructor.rs

1//! Register callbacks for when core objects like [`BinaryView`]s or [`Function`]s are destroyed.
2
3use crate::binary_view::BinaryView;
4use crate::file_metadata::FileMetadata;
5use crate::function::Function;
6use binaryninjacore_sys::*;
7use std::ffi::c_void;
8
9/// Registers a destructor which will be called when certain core objects are about to be destroyed.
10///
11/// Returns a handle to the registered destructor. The destructor will be unregistered when the handle is dropped.
12///
13/// To keep the destructor alive forever, move the [`ObjectDestructorHandle`] into [`std::mem::ManuallyDrop`].
14#[must_use = "The destructor will be unregistered when the handle is dropped"]
15pub fn register_object_destructor<'a, D: ObjectDestructor>(
16    destructor: D,
17) -> ObjectDestructorHandle<'a, D> {
18    let destructor = Box::leak(Box::new(destructor));
19    let callbacks = BNObjectDestructionCallbacks {
20        context: destructor as *mut _ as *mut c_void,
21        destructBinaryView: Some(cb_destruct_binary_view::<D>),
22        destructFileMetadata: Some(cb_destruct_file_metadata::<D>),
23        destructFunction: Some(cb_destruct_function::<D>),
24    };
25    let mut handle = ObjectDestructorHandle {
26        callbacks,
27        _life: std::marker::PhantomData,
28    };
29    unsafe { BNRegisterObjectDestructionCallbacks(&mut handle.callbacks) };
30    handle
31}
32
33/// The handle for the [`ObjectDestructor`].
34///
35/// Once this handle is dropped, the destructor will be unregistered and the associated resources will be cleaned up.
36pub struct ObjectDestructorHandle<'a, D: ObjectDestructor> {
37    callbacks: BNObjectDestructionCallbacks,
38    _life: std::marker::PhantomData<&'a D>,
39}
40
41impl<D: ObjectDestructor> Drop for ObjectDestructorHandle<'_, D> {
42    fn drop(&mut self) {
43        unsafe { BNUnregisterObjectDestructionCallbacks(&mut self.callbacks) };
44        let _ = unsafe { Box::from_raw(self.callbacks.context as *mut D) };
45    }
46}
47
48/// The trait required for receiving core object destruction callbacks.
49///
50/// This is useful for cleaning up resources which are associated with a given core object.
51pub trait ObjectDestructor: 'static + Sync + Sized {
52    /// Called when a [`BinaryView`] is about to be destroyed.
53    fn destruct_view(&self, _view: &BinaryView) {}
54
55    /// Called when a [`FileMetadata`] is about to be destroyed.
56    fn destruct_file_metadata(&self, _metadata: &FileMetadata) {}
57
58    /// Called when a [`Function`] is about to be destroyed.
59    fn destruct_function(&self, _func: &Function) {}
60}
61
62unsafe extern "C" fn cb_destruct_binary_view<D: ObjectDestructor>(
63    ctxt: *mut c_void,
64    view: *mut BNBinaryView,
65) {
66    ffi_wrap!("ObjectDestructor::destruct_view", {
67        let destructor = &*(ctxt as *mut D);
68        let view = BinaryView { handle: view };
69        destructor.destruct_view(&view);
70    })
71}
72
73unsafe extern "C" fn cb_destruct_file_metadata<D: ObjectDestructor>(
74    ctxt: *mut c_void,
75    file: *mut BNFileMetadata,
76) {
77    ffi_wrap!("ObjectDestructor::destruct_file_metadata", {
78        let destructor = &*(ctxt as *mut D);
79        let file = FileMetadata::from_raw(file);
80        destructor.destruct_file_metadata(&file);
81    })
82}
83
84unsafe extern "C" fn cb_destruct_function<D: ObjectDestructor>(
85    ctxt: *mut c_void,
86    func: *mut BNFunction,
87) {
88    ffi_wrap!("ObjectDestructor::destruct_function", {
89        let destructor = &*(ctxt as *mut D);
90        let func = Function { handle: func };
91        destructor.destruct_function(&func);
92    })
93}