binaryninja/
data_notification.rs

1//! Receive notifications for many types of core events.
2
3use std::ffi::{c_char, c_void, CStr};
4use std::ptr::NonNull;
5
6use binaryninjacore_sys::*;
7
8use crate::binary_view::{BinaryView, StringType};
9use crate::component::Component;
10use crate::database::undo::UndoEntry;
11use crate::external_library::{ExternalLibrary, ExternalLocation};
12use crate::function::Function;
13use crate::rc::Ref;
14use crate::section::Section;
15use crate::segment::Segment;
16use crate::symbol::Symbol;
17use crate::tags::{TagReference, TagType};
18use crate::types::{QualifiedName, Type, TypeArchive};
19use crate::variable::DataVariable;
20
21macro_rules! trait_handler {
22(
23    $(
24        $ffi_param_name:ident => $fun_name:ident(
25            $(
26                $arg_name:ident:
27                $raw_arg_type:ty:
28                $arg_type:ty =
29                $value_calculated:expr
30            ),* $(,)?
31        ) $(-> $ret_type:ty)?
32    ),* $(,)?
33) => {
34    /// Used to describe which call should be triggered. By default, all calls are disabled.
35    ///
36    /// Used by [`CustomDataNotification::register`]
37    #[derive(Default)]
38    pub struct DataNotificationTriggers {
39        $($fun_name: bool,)*
40    }
41
42    impl DataNotificationTriggers {
43    $(
44        pub fn $fun_name(mut self) -> Self {
45            self.$fun_name = true;
46            self
47        }
48    )*
49    }
50
51    pub trait CustomDataNotification: Sync + Send {
52        $(
53        #[inline]
54        #[allow(unused_variables)]
55        fn $fun_name(&mut self, $($arg_name: $arg_type),*) $(-> $ret_type)* {
56            $( <$ret_type as Default>::default() )*
57        }
58        )*
59
60        fn register<'a>(
61            self,
62            view: &BinaryView,
63            triggers: DataNotificationTriggers,
64        ) -> DataNotificationHandle<'a, Self> where Self: 'a + Sized {
65            register_data_notification(view, self, triggers)
66        }
67    }
68
69    $(
70    unsafe extern "C" fn $fun_name<H: CustomDataNotification>(
71        ctxt: *mut ::std::os::raw::c_void,
72        $($arg_name: $raw_arg_type),*
73    ) $(-> $ret_type)* {
74        let handle: &mut H = &mut *(ctxt as *mut H);
75        handle.$fun_name($($value_calculated),*)
76    }
77    )*
78
79    fn register_data_notification<'a, H: CustomDataNotification + 'a>(
80        view: &BinaryView,
81        notify: H,
82        triggers: DataNotificationTriggers,
83    ) -> DataNotificationHandle<'a, H> {
84        // SAFETY: this leak is undone on drop
85        let leak_notify = Box::leak(Box::new(notify));
86        let handle = BNBinaryDataNotification {
87            context: leak_notify as *mut _ as *mut c_void,
88            $($ffi_param_name: triggers.$fun_name.then_some($fun_name::<H>)),*,
89            // TODO: Require all BNBinaryDataNotification's to be implemented?
90            // Since core developers are not required to write Rust bindings (yet) we do not
91            // force new binary data notifications callbacks to be written here.of core developers who do not wish to write
92            ..Default::default()
93        };
94        // Box it to prevent a copy being returned in `DataNotificationHandle`.
95        let mut boxed_handle = Box::new(handle);
96        unsafe { BNRegisterDataNotification(view.handle, boxed_handle.as_mut()) };
97        DataNotificationHandle {
98            bv: view.to_owned(),
99            handle: boxed_handle,
100            _life: std::marker::PhantomData,
101        }
102    }
103
104    /// Implement closures that will be called by on the event of data modification.
105    ///
106    /// Once dropped the closures will stop being called.
107    ///
108    /// NOTE: Closures are not executed on the same thread as the event that occurred, you must not depend
109    /// on any serial behavior
110    ///
111    /// # Example
112    ///
113    /// ```no_run
114    /// # use binaryninja::data_notification::DataNotificationClosure;
115    /// # use binaryninja::function::Function;
116    /// # use binaryninja::binary_view::BinaryView;
117    /// # let bv: BinaryView = todo!();
118    /// let custom = DataNotificationClosure::default()
119    ///     .function_updated(|_bv: &BinaryView, _func: &Function| todo!() )
120    ///     // other calls should be added here
121    ///     .register(&bv);
122    /// ```
123    pub struct DataNotificationClosure<'a> {
124    $(
125        $fun_name: Option<Box<
126            dyn FnMut($($arg_type),*) $(-> $ret_type)* + Sync + Send + 'a
127        >>,
128    )*
129    }
130
131    impl Default for DataNotificationClosure<'_>  {
132        fn default() -> Self {
133            Self { $($fun_name: None,)* }
134        }
135    }
136
137    impl<'a> DataNotificationClosure<'a> {
138        pub fn new() -> Self {
139            Default::default()
140        }
141    $(
142        pub fn $fun_name<F: FnMut(
143            $($arg_type),*
144        ) $(-> $ret_type)* + Sync + Send + 'a>(mut self, param: F) -> Self {
145            self.$fun_name = Some(Box::new(param));
146            self
147        }
148    )*
149
150        /// Register the closures to be notified up until the [`DataNotificationHandle`] is dropped.
151        pub fn register(
152            self,
153            view: &BinaryView,
154        ) -> DataNotificationHandle<'a, Self> {
155            let mut triggers = DataNotificationTriggers::default();
156            $(
157            if self.$fun_name.is_some() {
158                triggers = triggers.$fun_name();
159            }
160            )*
161            register_data_notification(view, self, triggers)
162        }
163    }
164
165    impl CustomDataNotification for DataNotificationClosure<'_> {
166    $(
167        fn $fun_name(&mut self, $($arg_name: $arg_type),*) $(-> $ret_type)* {
168            let Some(handle) = self.$fun_name.as_mut() else {
169                unreachable!();
170            };
171            handle($($arg_name),*)
172        }
173    )*
174   }
175};
176}
177
178trait_handler! {
179    notificationBarrier => notification_barrier(
180        view: *mut BNBinaryView: &BinaryView = &BinaryView::from_raw(view),
181    ) -> u64,
182    dataWritten => data_written(
183        view: *mut BNBinaryView: &BinaryView = &BinaryView::from_raw(view),
184        offset: u64: u64 = offset,
185        len: usize: usize = len,
186    ),
187    dataInserted => data_inserted(
188        view: *mut BNBinaryView: &BinaryView = &BinaryView::from_raw(view),
189        offset: u64: u64 = offset,
190        len: usize: usize = len,
191    ),
192    dataRemoved => data_removed(
193        view: *mut BNBinaryView: &BinaryView = &BinaryView::from_raw(view),
194        offset: u64: u64 = offset,
195        // TODO why the len is u64 here? Maybe is a bug on CoreAPI
196        len: u64: u64 = len,
197    ),
198    functionAdded => function_added(
199        view: *mut BNBinaryView: &BinaryView = &BinaryView::from_raw(view),
200        func: *mut BNFunction: &Function = &Function::from_raw(func),
201    ),
202    functionRemoved => function_removed(
203        view: *mut BNBinaryView: &BinaryView = &BinaryView::from_raw(view),
204        func: *mut BNFunction: &Function = &Function::from_raw(func),
205    ),
206    functionUpdated => function_updated(
207        view: *mut BNBinaryView: &BinaryView = &BinaryView::from_raw(view),
208        func: *mut BNFunction: &Function = &Function::from_raw(func),
209    ),
210    functionUpdateRequested => function_update_requested(
211        view: *mut BNBinaryView: &BinaryView = &BinaryView::from_raw(view),
212        func: *mut BNFunction: &Function = &Function::from_raw(func),
213    ),
214    dataVariableAdded => data_variable_added(
215        view: *mut BNBinaryView: &BinaryView = &BinaryView::from_raw(view),
216        var: *mut BNDataVariable: &DataVariable = &DataVariable::from_raw(&*var),
217    ),
218    dataVariableRemoved => data_variable_removed(
219        view: *mut BNBinaryView: &BinaryView = &BinaryView::from_raw(view),
220        var: *mut BNDataVariable: &DataVariable = &DataVariable::from_raw(&*var),
221    ),
222    dataVariableUpdated => data_variable_updated(
223        view: *mut BNBinaryView: &BinaryView = &BinaryView::from_raw(view),
224        var: *mut BNDataVariable: &DataVariable = &DataVariable::from_raw(&*var),
225    ),
226    dataMetadataUpdated => data_metadata_updated(
227        view: *mut BNBinaryView: &BinaryView = &BinaryView::from_raw(view),
228        offset: u64: u64 = offset,
229    ),
230    tagTypeUpdated => tag_type_updated(
231        view: *mut BNBinaryView: &BinaryView = &BinaryView::from_raw(view),
232        tag_type: *mut BNTagType: &TagType = &TagType{ handle: tag_type },
233    ),
234    tagAdded => tag_added(
235        view: *mut BNBinaryView: &BinaryView = &BinaryView::from_raw(view),
236        tag_ref: *mut BNTagReference: &TagReference = &TagReference::from(&*tag_ref),
237    ),
238    tagRemoved => tag_removed(
239        view: *mut BNBinaryView: &BinaryView = &BinaryView::from_raw(view),
240        tag_ref: *mut BNTagReference: &TagReference = &TagReference::from(&*tag_ref),
241    ),
242    tagUpdated => tag_updated(
243        view: *mut BNBinaryView: &BinaryView = &BinaryView::from_raw(view),
244        tag_ref: *mut BNTagReference: &TagReference = &TagReference::from(&*tag_ref),
245    ),
246    symbolAdded => symbol_added(
247        view: *mut BNBinaryView: &BinaryView = &BinaryView::from_raw(view),
248        sym: *mut BNSymbol: &Symbol = &Symbol::from_raw(sym),
249    ),
250    symbolRemoved => symbol_removed(
251        view: *mut BNBinaryView: &BinaryView = &BinaryView::from_raw(view),
252        sym: *mut BNSymbol: &Symbol = &Symbol::from_raw(sym),
253    ),
254    symbolUpdated => symbol_updated(
255        view: *mut BNBinaryView: &BinaryView = &BinaryView::from_raw(view),
256        sym: *mut BNSymbol: &Symbol = &Symbol::from_raw(sym),
257    ),
258    stringFound => string_found(
259        view: *mut BNBinaryView: &BinaryView = &BinaryView::from_raw(view),
260        type_: BNStringType: StringType = type_,
261        offset: u64: u64 = offset,
262        len: usize: usize = len,
263    ),
264    stringRemoved => string_removed(
265        view: *mut BNBinaryView: &BinaryView = &BinaryView::from_raw(view),
266        type_: BNStringType: StringType = type_,
267        offset: u64: u64 = offset,
268        len: usize: usize = len,
269    ),
270    typeDefined => type_defined(
271        view: *mut BNBinaryView: &BinaryView = &BinaryView::from_raw(view),
272        name: *mut BNQualifiedName: &QualifiedName = &QualifiedName::from_raw(&*name),
273        type_: *mut BNType: &Type = &Type::from_raw(type_),
274    ),
275    typeUndefined => type_undefined(
276        view: *mut BNBinaryView: &BinaryView = &BinaryView::from_raw(view),
277        name: *mut BNQualifiedName: &QualifiedName = &QualifiedName::from_raw(&*name),
278        type_: *mut BNType: &Type = &Type::from_raw(type_),
279    ),
280    typeReferenceChanged => type_reference_changed(
281        view: *mut BNBinaryView: &BinaryView = &BinaryView::from_raw(view),
282        name: *mut BNQualifiedName: &QualifiedName = &QualifiedName::from_raw(&*name),
283        type_: *mut BNType: &Type = &Type::from_raw(type_),
284    ),
285    typeFieldReferenceChanged => type_field_reference_changed(
286        view: *mut BNBinaryView: &BinaryView = &BinaryView::from_raw(view),
287        name: *mut BNQualifiedName: &QualifiedName = &QualifiedName::from_raw(&*name),
288        offset: u64: u64 = offset,
289    ),
290    segmentAdded => segment_added(
291        view: *mut BNBinaryView: &BinaryView = &BinaryView::from_raw(view),
292        segment: *mut BNSegment: &Segment = &Segment::from_raw(segment),
293    ),
294    segmentRemoved => segment_removed(
295        view: *mut BNBinaryView: &BinaryView = &BinaryView::from_raw(view),
296        segment: *mut BNSegment: &Segment = &Segment::from_raw(segment),
297    ),
298    segmentUpdated => segment_updated(
299        view: *mut BNBinaryView: &BinaryView = &BinaryView::from_raw(view),
300        segment: *mut BNSegment: &Segment = &Segment::from_raw(segment),
301    ),
302    sectionAdded => section_added(
303        view: *mut BNBinaryView: &BinaryView = &BinaryView::from_raw(view),
304        section: *mut BNSection: &Section = &Section::from_raw(section),
305    ),
306    sectionRemoved => section_removed(
307        view: *mut BNBinaryView: &BinaryView = &BinaryView::from_raw(view),
308        section: *mut BNSection: &Section = &Section::from_raw(section),
309    ),
310    sectionUpdated => section_updated(
311        view: *mut BNBinaryView: &BinaryView = &BinaryView::from_raw(view),
312        section: *mut BNSection: &Section = &Section::from_raw(section),
313    ),
314    componentNameUpdated => component_name_updated(
315        view: *mut BNBinaryView: &BinaryView = &BinaryView::from_raw(view),
316        previous_name: *mut c_char: &str = CStr::from_ptr(previous_name).to_str().unwrap(),
317        component: *mut BNComponent: &Component = &Component::from_raw(NonNull::new(component).unwrap()),
318    ),
319    componentAdded => component_added(
320        view: *mut BNBinaryView: &BinaryView = &BinaryView::from_raw(view),
321        component: *mut BNComponent: &Component = &Component::from_raw(NonNull::new(component).unwrap()),
322    ),
323    componentMoved => component_moved(
324        view: *mut BNBinaryView: &BinaryView = &BinaryView::from_raw(view),
325        former_parent: *mut BNComponent: &Component = &Component::from_raw(NonNull::new(former_parent).unwrap()),
326        new_parent: *mut BNComponent: &Component = &Component::from_raw(NonNull::new(new_parent).unwrap()),
327        component: *mut BNComponent: &Component = &Component::from_raw(NonNull::new(component).unwrap()),
328    ),
329    componentRemoved => component_removed(
330        view: *mut BNBinaryView: &BinaryView = &BinaryView::from_raw(view),
331        former_parent: *mut BNComponent: &Component = &Component::from_raw(NonNull::new(former_parent).unwrap()),
332        component: *mut BNComponent: &Component = &Component::from_raw(NonNull::new(component).unwrap()),
333    ),
334    componentFunctionAdded => component_function_added(
335        view: *mut BNBinaryView: &BinaryView = &BinaryView::from_raw(view),
336        component: *mut BNComponent: &Component = &Component::from_raw(NonNull::new(component).unwrap()),
337        function: *mut BNFunction: &Function = &Function::from_raw(function),
338    ),
339    componentFunctionRemoved => component_function_removed(
340        view: *mut BNBinaryView: &BinaryView = &BinaryView::from_raw(view),
341        component: *mut BNComponent: &Component = &Component::from_raw(NonNull::new(component).unwrap()),
342        function: *mut BNFunction: &Function = &Function::from_raw(function),
343    ),
344    componentDataVariableAdded => component_data_variable_added(
345        view: *mut BNBinaryView: &BinaryView = &BinaryView::from_raw(view),
346        component: *mut BNComponent: &Component = &Component::from_raw(NonNull::new(component).unwrap()),
347        var: *mut BNDataVariable: &DataVariable = &DataVariable::from_raw(&*var),
348        ),
349    componentDataVariableRemoved => component_data_variable_removed(
350        view: *mut BNBinaryView: &BinaryView = &BinaryView::from_raw(view),
351        component: *mut BNComponent: &Component = &Component::from_raw(NonNull::new(component).unwrap()),
352        var: *mut BNDataVariable: &DataVariable = &DataVariable::from_raw(&*var),
353    ),
354    externalLibraryAdded => external_library_added(
355        data: *mut BNBinaryView: &BinaryView = &BinaryView::from_raw(data),
356        library: *mut BNExternalLibrary: &ExternalLibrary = &ExternalLibrary::from_raw(NonNull::new(library).unwrap()),
357    ),
358    externalLibraryUpdated => external_library_updated(
359        data: *mut BNBinaryView: &BinaryView = &BinaryView::from_raw(data),
360        library: *mut BNExternalLibrary: &ExternalLibrary = &ExternalLibrary::from_raw(NonNull::new(library).unwrap()),
361    ),
362    externalLibraryRemoved => external_library_removed(
363        data: *mut BNBinaryView: &BinaryView = &BinaryView::from_raw(data),
364        library: *mut BNExternalLibrary: &ExternalLibrary = &ExternalLibrary::from_raw(NonNull::new(library).unwrap()),
365    ),
366    externalLocationAdded => external_location_added(
367        data: *mut BNBinaryView: &BinaryView = &BinaryView::from_raw(data),
368        location: *mut BNExternalLocation: &ExternalLocation = &ExternalLocation::from_raw(NonNull::new(location).unwrap()),
369    ),
370    externalLocationUpdated => external_location_updated(
371        data: *mut BNBinaryView: &BinaryView = &BinaryView::from_raw(data),
372        location: *mut BNExternalLocation: &ExternalLocation = &ExternalLocation::from_raw(NonNull::new(location).unwrap()),
373    ),
374    externalLocationRemoved => external_location_removed(
375        data: *mut BNBinaryView: &BinaryView = &BinaryView::from_raw(data),
376        location: *mut BNExternalLocation: &ExternalLocation = &ExternalLocation::from_raw(NonNull::new(location).unwrap()),
377    ),
378    typeArchiveAttached => type_archive_attached(
379        view: *mut BNBinaryView: &BinaryView = &BinaryView::from_raw(view),
380        id: *const c_char: &str = CStr::from_ptr(id).to_str().unwrap(),
381        path: *const c_char: &[u8] = CStr::from_ptr(path).to_bytes(),
382    ),
383    typeArchiveDetached => type_archive_detached(
384        view: *mut BNBinaryView: &BinaryView = &BinaryView::from_raw(view),
385        id: *const c_char: &str = CStr::from_ptr(id).to_str().unwrap(),
386        path: *const c_char: &[u8] = CStr::from_ptr(path).to_bytes(),
387    ),
388    typeArchiveConnected => type_archive_connected(
389        view: *mut BNBinaryView: &BinaryView = &BinaryView::from_raw(view),
390        archive: *mut BNTypeArchive: &TypeArchive = &TypeArchive::from_raw(NonNull::new(archive).unwrap()),
391    ),
392    typeArchiveDisconnected => type_archive_disconnected(
393        view: *mut BNBinaryView: &BinaryView = &BinaryView::from_raw(view),
394        archive: *mut BNTypeArchive: &TypeArchive = &TypeArchive::from_raw(NonNull::new(archive).unwrap()),
395    ),
396    undoEntryAdded => undo_entry_added(
397        view: *mut BNBinaryView: &BinaryView = &BinaryView::from_raw(view),
398        entry: *mut BNUndoEntry: &UndoEntry = &UndoEntry::from_raw(NonNull::new(entry).unwrap()),
399    ),
400    undoEntryTaken => undo_entry_taken(
401        view: *mut BNBinaryView: &BinaryView = &BinaryView::from_raw(view),
402        entry: *mut BNUndoEntry: &UndoEntry = &UndoEntry::from_raw(NonNull::new(entry).unwrap()),
403    ),
404    redoEntryTaken => redo_entry_taken(
405        view: *mut BNBinaryView: &BinaryView = &BinaryView::from_raw(view),
406        entry: *mut BNUndoEntry: &UndoEntry = &UndoEntry::from_raw(NonNull::new(entry).unwrap()),
407    ),
408    rebased => rebased(
409        oldview: *mut BNBinaryView: &BinaryView = &BinaryView::from_raw(oldview),
410        newview: *mut BNBinaryView: &BinaryView = &BinaryView::from_raw(newview),
411    ),
412}
413
414pub struct DataNotificationHandle<'a, T: CustomDataNotification>
415where
416    T: 'a,
417{
418    bv: Ref<BinaryView>,
419    handle: Box<BNBinaryDataNotification>,
420    _life: std::marker::PhantomData<&'a T>,
421}
422
423impl<T: CustomDataNotification> DataNotificationHandle<'_, T> {
424    unsafe fn unregister_bv(&mut self) {
425        BNUnregisterDataNotification(self.bv.handle, self.handle.as_mut())
426    }
427
428    unsafe fn extract_context(&mut self) -> Box<T> {
429        Box::from_raw(self.handle.context as *mut T)
430    }
431
432    pub fn unregister(self) -> T {
433        // NOTE don't drop the ctxt, return it
434        let mut slf = core::mem::ManuallyDrop::new(self);
435        unsafe { slf.unregister_bv() };
436        unsafe { *slf.extract_context() }
437    }
438}
439
440impl<T: CustomDataNotification> Drop for DataNotificationHandle<'_, T> {
441    fn drop(&mut self) {
442        unsafe { self.unregister_bv() };
443        // drop context, avoid memory leak
444        let _ctxt = unsafe { self.extract_context() };
445    }
446}