|
| 1 | +use crate::cache::{cached_function_guid, try_cached_function_guid}; |
| 2 | +use binaryninja::background_task::BackgroundTask; |
| 3 | +use binaryninja::binary_view::{register_binary_view_event, BinaryView, BinaryViewExt}; |
| 4 | +use binaryninja::data_notification::DataNotificationClosure; |
| 5 | +use binaryninja::section::Section; |
| 6 | +use binaryninja::segment::Segment; |
| 7 | +use binaryninja::worker_thread::execute_on_worker_thread; |
| 8 | +use binaryninjacore_sys::BNBinaryViewEventType::BinaryViewFinalizationEvent; |
| 9 | +use rayon::iter::ParallelIterator; |
| 10 | +use std::time::Instant; |
| 11 | + |
| 12 | +pub fn register_view_listeners() { |
| 13 | + // When the view is finalized (constructed) we need to register some listeners so we can automatically |
| 14 | + // regenerate the function GUIDs when necessary. |
| 15 | + register_binary_view_event(BinaryViewFinalizationEvent, |view: &BinaryView| { |
| 16 | + let _handle = DataNotificationClosure::default() |
| 17 | + .rebased(|_old_view: &BinaryView, new_view: &BinaryView| { |
| 18 | + spawn_regenerate_guids(&new_view) |
| 19 | + }) |
| 20 | + .section_added(|view: &BinaryView, _section: &Section| spawn_regenerate_guids(&view)) |
| 21 | + .section_removed(|view: &BinaryView, _section: &Section| spawn_regenerate_guids(&view)) |
| 22 | + .segment_added(|view: &BinaryView, _segment: &Segment| spawn_regenerate_guids(&view)) |
| 23 | + .segment_removed(|view: &BinaryView, _segment: &Segment| spawn_regenerate_guids(&view)) |
| 24 | + .register_persisted(&view); |
| 25 | + }); |
| 26 | +} |
| 27 | + |
| 28 | +fn regenerate_guids(view: &BinaryView) { |
| 29 | + if !view.has_initial_analysis() { |
| 30 | + tracing::debug!( |
| 31 | + "Skipping regenerating GUIDs for view {} (not analyzed)", |
| 32 | + view.file() |
| 33 | + ); |
| 34 | + return; |
| 35 | + } |
| 36 | + tracing::info!("Regenerating GUIDs for view {}", view.file()); |
| 37 | + for running_task in &BackgroundTask::running_tasks() { |
| 38 | + if running_task |
| 39 | + .progress_text() |
| 40 | + .contains("Regenerating WARP Function GUIDs...") |
| 41 | + { |
| 42 | + tracing::debug!("Cancelling previous GUID regeneration task..."); |
| 43 | + running_task.cancel(); |
| 44 | + } |
| 45 | + } |
| 46 | + |
| 47 | + let task = BackgroundTask::new("Regenerating WARP Function GUIDs...", false).enter(); |
| 48 | + let time = Instant::now(); |
| 49 | + view.functions().par_iter().all(|function| { |
| 50 | + let before = try_cached_function_guid(&function); |
| 51 | + if before.is_some() { |
| 52 | + function.remove_metadata("warp_function_guid"); |
| 53 | + cached_function_guid(&function, || function.lifted_il().ok()); |
| 54 | + } |
| 55 | + // Short circuit if the task was terminated. |
| 56 | + !task.is_cancelled() |
| 57 | + }); |
| 58 | + tracing::debug!( |
| 59 | + "Regenerated GUIDs for view {} in {}ms", |
| 60 | + view.file(), |
| 61 | + time.elapsed().as_millis() |
| 62 | + ); |
| 63 | +} |
| 64 | + |
| 65 | +/// Spawn a background task to regenerate the GUIDs for the given view. |
| 66 | +fn spawn_regenerate_guids(view: &BinaryView) { |
| 67 | + // These events may be sent to us _before_ the view has finished analysis, we populate the guids |
| 68 | + // as a part of the initial function analysis already (see 'analysis.warp.guid') so we can skip. |
| 69 | + if !view.has_initial_analysis() { |
| 70 | + return; |
| 71 | + } |
| 72 | + let view_owned = view.to_owned(); |
| 73 | + // Run in the background, we don't want to block the UI. |
| 74 | + execute_on_worker_thread("WARP: Regenerate", move || regenerate_guids(&view_owned)); |
| 75 | +} |
0 commit comments