diff options
| author | Mason Reed <mason@vector35.com> | 2026-02-22 17:08:21 -0800 |
|---|---|---|
| committer | Mason Reed <35282038+emesare@users.noreply.github.com> | 2026-02-23 00:09:44 -0800 |
| commit | b18bdad6c94a8e234108d531f0c480c7104abebe (patch) | |
| tree | b0edb86cfae52c37635ef0b5cac491220bc3ef24 /rust | |
| parent | dbd54d67a6d523f64615f653d82d8224cd09870a (diff) | |
[Rust] Misc documentation and cleanup
Diffstat (limited to 'rust')
| -rw-r--r-- | rust/examples/dump_type_library.rs | 3 | ||||
| -rw-r--r-- | rust/src/binary_view.rs | 107 | ||||
| -rw-r--r-- | rust/src/custom_binary_view.rs | 18 | ||||
| -rw-r--r-- | rust/src/file_metadata.rs | 172 | ||||
| -rw-r--r-- | rust/src/main_thread.rs | 8 | ||||
| -rw-r--r-- | rust/src/types/printer.rs | 1 | ||||
| -rw-r--r-- | rust/src/workflow.rs | 227 | ||||
| -rw-r--r-- | rust/tests/binary_view.rs | 5 | ||||
| -rw-r--r-- | rust/tests/collaboration.rs | 5 | ||||
| -rw-r--r-- | rust/tests/function.rs | 2 | ||||
| -rw-r--r-- | rust/tests/initialization.rs | 6 | ||||
| -rw-r--r-- | rust/tests/type_container.rs | 6 | ||||
| -rw-r--r-- | rust/tests/types.rs | 3 |
13 files changed, 231 insertions, 332 deletions
diff --git a/rust/examples/dump_type_library.rs b/rust/examples/dump_type_library.rs index 9e6b19c2..6dd51ece 100644 --- a/rust/examples/dump_type_library.rs +++ b/rust/examples/dump_type_library.rs @@ -36,8 +36,7 @@ fn main() { type_lib_header_path ); let type_printer = CoreTypePrinter::default(); - let empty_bv = - BinaryView::from_data(&FileMetadata::new(), &[]).expect("Failed to create empty view"); + let empty_bv = BinaryView::from_data(&FileMetadata::new(), &[]); let printed_types = type_printer .print_all_types( all_types, diff --git a/rust/src/binary_view.rs b/rust/src/binary_view.rs index 25ec6d48..93819098 100644 --- a/rust/src/binary_view.rs +++ b/rust/src/binary_view.rs @@ -12,14 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -//! A view on binary data and queryable interface of a binary file. -//! -//! One key job of BinaryView is file format parsing which allows Binary Ninja to read, write, -//! insert, remove portions of the file given a virtual address. -//! -//! For the purposes of this documentation we define a virtual address as the memory address that -//! the various pieces of the physical file will be loaded at. -//! TODO : Mirror the Python docs for this +//! A view on binary data and queryable interface of a binary files analysis. use binaryninjacore_sys::*; @@ -104,6 +97,7 @@ pub trait BinaryViewBase: AsRef<BinaryView> { 0 } + /// Check if the offset is valid for the current view. fn offset_valid(&self, offset: u64) -> bool { let mut buf = [0u8; 1]; @@ -112,22 +106,28 @@ pub trait BinaryViewBase: AsRef<BinaryView> { self.as_ref().read(&mut buf[..], offset) == buf.len() } + /// Check if the offset is readable for the current view. fn offset_readable(&self, offset: u64) -> bool { self.offset_valid(offset) } + /// Check if the offset is writable for the current view. fn offset_writable(&self, offset: u64) -> bool { self.offset_valid(offset) } + /// Check if the offset is executable for the current view. fn offset_executable(&self, offset: u64) -> bool { self.offset_valid(offset) } + /// Check if the offset is backed by the original file and not added after the fact. fn offset_backed_by_file(&self, offset: u64) -> bool { self.offset_valid(offset) } + /// Get the next valid offset after the provided `offset`, useful if you need to iterate over all + /// readable offsets in the view. fn next_valid_offset_after(&self, offset: u64) -> u64 { let start = self.as_ref().start(); @@ -138,15 +138,17 @@ pub trait BinaryViewBase: AsRef<BinaryView> { } } - #[allow(unused)] - fn modification_status(&self, offset: u64) -> ModificationStatus { + /// Whether the data at the given `offset` been modified (patched). + fn modification_status(&self, _offset: u64) -> ModificationStatus { ModificationStatus::Original } + /// The lowest address in the view. fn start(&self) -> u64 { 0 } + /// The length of the view. fn len(&self) -> u64 { 0 } @@ -546,8 +548,6 @@ pub trait BinaryViewExt: BinaryViewBase { } /// The highest address in the view. - /// - /// NOTE: If operating within a [`Workflow`], consider using [`AnalysisContext::end`]. fn end(&self) -> u64 { unsafe { BNGetEndOffset(self.as_ref().handle) } } @@ -2454,6 +2454,26 @@ pub trait BinaryViewExt: BinaryViewBase { impl<T: BinaryViewBase> BinaryViewExt for T {} +/// Represents the "whole view" of the binary and its analysis. +/// +/// Analysis information: +/// +/// - [`BinaryViewExt::functions`] +/// - [`BinaryViewExt::data_variables`] +/// - [`BinaryViewExt::strings`] +/// +/// Annotation information: +/// +/// - [`BinaryViewExt::symbols`] +/// - [`BinaryViewExt::tags_all_scopes`] +/// - [`BinaryViewExt::comments`] +/// +/// Data representation and binary information: +/// +/// - [`BinaryViewExt::types`] +/// - [`BinaryViewExt::segments`] +/// - [`BinaryViewExt::sections`] +/// /// # Cleaning up /// /// [`BinaryView`] has a cyclic relationship with the associated [`FileMetadata`], each holds a strong @@ -2476,9 +2496,10 @@ impl BinaryView { Ref::new(Self { handle }) } - /// Construct the raw binary view from the given metadata. Before calling this make sure you have - /// a valid file path set for the [`FileMetadata`]. It is required that the [`FileMetadata::file_path`] - /// exist on the local filesystem. + /// Construct the raw binary view from the given metadata. + /// + /// Before calling this, make sure you have a valid file path set for the [`FileMetadata`]. It is + /// required that the [`FileMetadata::file_path`] exist in the local filesystem. pub fn from_metadata(meta: &FileMetadata) -> Result<Ref<Self>> { if !meta.file_path().exists() { return Err(()); @@ -2486,11 +2507,9 @@ impl BinaryView { let file = meta.file_path().to_cstr(); let handle = unsafe { BNCreateBinaryDataViewFromFilename(meta.handle, file.as_ptr() as *mut _) }; - if handle.is_null() { return Err(()); } - unsafe { Ok(Ref::new(Self { handle })) } } @@ -2503,29 +2522,34 @@ impl BinaryView { Self::from_metadata(meta) } - pub fn from_accessor<A: Accessor>( + // TODO: Provide an API that manages the lifetime of the accessor and the view. + /// Construct the raw binary view from the given `accessor` and metadata. + /// + /// It is the responsibility of the caller to keep the accessor alive for the lifetime of the view; + /// because of this, we mark the function as unsafe. + pub unsafe fn from_accessor<A: Accessor>( meta: &FileMetadata, - file: &mut FileAccessor<A>, + accessor: &mut FileAccessor<A>, ) -> Result<Ref<Self>> { - let handle = unsafe { BNCreateBinaryDataViewFromFile(meta.handle, &mut file.raw) }; - + let handle = unsafe { BNCreateBinaryDataViewFromFile(meta.handle, &mut accessor.raw) }; if handle.is_null() { return Err(()); } - unsafe { Ok(Ref::new(Self { handle })) } } - pub fn from_data(meta: &FileMetadata, data: &[u8]) -> Result<Ref<Self>> { + /// Construct the raw binary view from the given `data` and metadata. + /// + /// The data will be copied into the view, so the caller does not need to keep the data alive. + pub fn from_data(meta: &FileMetadata, data: &[u8]) -> Ref<Self> { let handle = unsafe { BNCreateBinaryDataViewFromData(meta.handle, data.as_ptr() as *mut _, data.len()) }; - - if handle.is_null() { - return Err(()); - } - - unsafe { Ok(Ref::new(Self { handle })) } + assert!( + !handle.is_null(), + "BNCreateBinaryDataViewFromData should always succeed" + ); + unsafe { Ref::new(Self { handle }) } } /// Save the original binary file to the provided `file_path` along with any modifications. @@ -2571,45 +2595,26 @@ impl BinaryViewBase for BinaryView { unsafe { BNRemoveViewData(self.handle, offset, len as u64) } } - /// Check if the offset is valid for the current view. - /// - /// NOTE: If operating within a [`Workflow`], consider using [`AnalysisContext::is_offset_valid`]. fn offset_valid(&self, offset: u64) -> bool { unsafe { BNIsValidOffset(self.handle, offset) } } - /// Check if the offset is readable for the current view. - /// - /// NOTE: If operating within a [`Workflow`], consider using [`AnalysisContext::is_offset_valid`]. fn offset_readable(&self, offset: u64) -> bool { unsafe { BNIsOffsetReadable(self.handle, offset) } } - /// Check if the offset is writable for the current view. - /// - /// NOTE: If operating within a [`Workflow`], consider using [`AnalysisContext::is_offset_writable`]. fn offset_writable(&self, offset: u64) -> bool { unsafe { BNIsOffsetWritable(self.handle, offset) } } - /// Check if the offset is executable for the current view. - /// - /// NOTE: If operating within a [`Workflow`], consider using [`AnalysisContext::is_offset_executable`]. fn offset_executable(&self, offset: u64) -> bool { unsafe { BNIsOffsetExecutable(self.handle, offset) } } - /// Check if the offset is backed by the original file and not added after the fact. - /// - /// NOTE: If operating within a [`Workflow`], consider using [`AnalysisContext::is_offset_backed_by_file`]. fn offset_backed_by_file(&self, offset: u64) -> bool { unsafe { BNIsOffsetBackedByFile(self.handle, offset) } } - /// Get the next valid offset after the provided `offset`, useful if you need to iterate over all - /// readable offsets in the view. - /// - /// NOTE: If operating within a [`Workflow`], consider using [`AnalysisContext::next_valid_offset`]. fn next_valid_offset_after(&self, offset: u64) -> u64 { unsafe { BNGetNextValidOffset(self.handle, offset) } } @@ -2618,16 +2623,10 @@ impl BinaryViewBase for BinaryView { unsafe { BNGetModification(self.handle, offset) } } - /// The lowest address in the view. - /// - /// NOTE: If operating within a [`Workflow`], consider using [`AnalysisContext::start`]. fn start(&self) -> u64 { unsafe { BNGetStartOffset(self.handle) } } - /// The length of the view, lowest to highest address. - /// - /// NOTE: If operating within a [`Workflow`], consider using [`AnalysisContext::length`]. fn len(&self) -> u64 { unsafe { BNGetViewLength(self.handle) } } diff --git a/rust/src/custom_binary_view.rs b/rust/src/custom_binary_view.rs index 4cd49931..4d21ce89 100644 --- a/rust/src/custom_binary_view.rs +++ b/rust/src/custom_binary_view.rs @@ -178,12 +178,23 @@ where } pub trait BinaryViewTypeBase: AsRef<BinaryViewType> { + /// Is this [`BinaryViewType`] valid for the given the raw [`BinaryView`]? + /// + /// Typical implementations will read the magic bytes (e.g. 'MZ'), this is a performance-sensitive + /// path so prefer inexpensive checks rather than comprehensive ones. fn is_valid_for(&self, data: &BinaryView) -> bool; + /// Is this [`BinaryViewType`] deprecated and should not be used? + /// + /// We specify this such that the view type may still be used by existing databases, but not + /// newly created views. fn is_deprecated(&self) -> bool { false } + /// Is this [`BinaryViewType`] able to be loaded forcefully? + /// + /// If so, it will be shown in the drop-down when a user opens a file with options. fn is_force_loadable(&self) -> bool { false } @@ -319,6 +330,9 @@ pub trait BinaryViewTypeExt: BinaryViewTypeBase { impl<T: BinaryViewTypeBase> BinaryViewTypeExt for T {} +/// A [`BinaryViewType`] acts as a factory for [`BinaryView`] objects. +/// +/// Each file format will have its own type, such as PE, ELF, or Mach-O. #[derive(Copy, Clone, PartialEq, Eq, Hash)] pub struct BinaryViewType { pub handle: *mut BNBinaryViewType, @@ -338,7 +352,9 @@ impl BinaryViewType { } } - pub fn list_valid_types_for(data: &BinaryView) -> Array<BinaryViewType> { + /// Enumerates all view types and checks to see if the given raw [`BinaryView`] is valid, + /// returning only those that are. + pub fn valid_types_for_data(data: &BinaryView) -> Array<BinaryViewType> { unsafe { let mut count: usize = 0; let types = BNGetBinaryViewTypesForData(data.handle, &mut count as *mut _); diff --git a/rust/src/file_metadata.rs b/rust/src/file_metadata.rs index bb783158..5862647f 100644 --- a/rust/src/file_metadata.rs +++ b/rust/src/file_metadata.rs @@ -12,6 +12,8 @@ // See the License for the specific language governing permissions and // limitations under the License. +//! The [`FileMetadata`] struct provides information about a file and owns its available [`BinaryView`]s. + use crate::binary_view::BinaryView; use crate::database::Database; use crate::rc::*; @@ -22,12 +24,90 @@ use std::ffi::c_void; use std::fmt::{Debug, Display, Formatter}; use std::path::{Path, PathBuf}; -use crate::progress::ProgressCallback; +use crate::progress::{NoProgressCallback, ProgressCallback}; use crate::project::file::ProjectFile; -use std::ptr::{self, NonNull}; +use std::ptr::NonNull; + +#[allow(unused_imports)] +use crate::custom_binary_view::BinaryViewType; new_id_type!(SessionId, usize); +pub type SaveOption = BNSaveOption; + +/// Settings to alter the behavior of creating snapshots saved within a [`Database`]. +pub struct SaveSettings { + pub(crate) handle: *mut BNSaveSettings, +} + +impl SaveSettings { + pub fn new() -> Ref<Self> { + Self::ref_from_raw(unsafe { BNCreateSaveSettings() }) + } + + fn ref_from_raw(handle: *mut BNSaveSettings) -> Ref<Self> { + unsafe { Ref::new(Self { handle }) } + } + + /// Sets the specified `option` to `true` and returns a ref counted `SaveSettings` that can + /// continued to be chained. + pub fn with_option(&self, option: SaveOption) -> Ref<Self> { + self.set_option(option, true); + self.to_owned() + } + + pub fn set_option(&self, option: SaveOption, value: bool) { + unsafe { BNSetSaveSettingsOption(self.handle, option, value) } + } + + pub fn option(&self, option: SaveOption) -> bool { + unsafe { BNIsSaveSettingsOptionSet(self.handle, option) } + } + + /// When saving an automatic snapshot via [`FileMetadata::save_auto_snapshot`] this name will be + /// used for the newly written snapshot. + pub fn snapshot_name(&self) -> String { + unsafe { BnString::into_string(BNGetSaveSettingsName(self.handle)) } + } + + pub fn set_snapshot_name(&self, name: &str) { + let name = name.to_cstr(); + unsafe { BNSetSaveSettingsName(self.handle, name.as_ptr()) } + } +} + +unsafe impl Send for SaveSettings {} +unsafe impl Sync for SaveSettings {} + +impl ToOwned for SaveSettings { + type Owned = Ref<Self>; + + fn to_owned(&self) -> Self::Owned { + unsafe { RefCountable::inc_ref(self) } + } +} + +unsafe impl RefCountable for SaveSettings { + unsafe fn inc_ref(handle: &Self) -> Ref<Self> { + Ref::new(Self { + handle: BNNewSaveSettingsReference(handle.handle), + }) + } + + unsafe fn dec_ref(handle: &Self) { + BNFreeSaveSettings(handle.handle); + } +} + +/// File metadata provides information about a file in the context of Binary Ninja. It contains no +/// analysis information, only information useful for identifying a file, such as the [`FileMetadata::file_path`]. +/// +/// Another responsibility of the [`FileMetadata`] is to own the available [`BinaryView`]s for the +/// file, such as the "Raw" view and any other views that may be created for the file. +/// +/// **Important**: Because [`FileMetadata`] holds a strong reference to the [`BinaryView`]s and those +/// views hold a strong reference to the file metadata, to end the cyclic reference a call to the +/// [`FileMetadata::close`] is required. #[derive(PartialEq, Eq, Hash)] pub struct FileMetadata { pub(crate) handle: *mut BNFileMetadata, @@ -42,12 +122,14 @@ impl FileMetadata { unsafe { Ref::new(Self { handle }) } } + /// Create an empty [`FileMetadata`] with no associated file path. + /// + /// Unless you are creating an ephemeral file with no backing, prefer [`FileMetadata::with_file_path`]. pub fn new() -> Ref<Self> { Self::ref_from_raw(unsafe { BNCreateFileMetadata() }) } - /// Build a [`FileMetadata`] with the given `path`, this is uncommon as you are likely to want to - /// open a [`BinaryView`] + /// Build a [`FileMetadata`] with the given `path`. pub fn with_file_path(path: &Path) -> Ref<Self> { let ret = FileMetadata::new(); ret.set_file_path(path); @@ -61,6 +143,7 @@ impl FileMetadata { } } + /// An id unique to this [`FileMetadata`], mostly used for associating logs with a specific file. pub fn session_id(&self) -> SessionId { let raw = unsafe { BNFileMetadataGetSessionId(self.handle) }; SessionId(raw) @@ -187,16 +270,23 @@ impl FileMetadata { } } + /// Whether the file is currently flagged as modified. + /// + /// When this returns `true`, the UI will prompt to save the database on close, as well as display + /// a dot in the files tab. pub fn is_modified(&self) -> bool { unsafe { BNIsFileModified(self.handle) } } + /// Marks the file as modified such that we can prompt to save the database on close. pub fn mark_modified(&self) { unsafe { BNMarkFileModified(self.handle); } } + /// Marks the file as saved such that [`FileMetadata::is_modified`] and [`FileMetadata::is_analysis_changed`] + /// will return `false` and the undo buffer associated with this [`FileMetadata`] will be updated. pub fn mark_saved(&self) { unsafe { BNMarkFileSaved(self.handle); @@ -207,13 +297,19 @@ impl FileMetadata { unsafe { BNIsAnalysisChanged(self.handle) } } + /// Checks to see if the database exists for the file. pub fn is_database_backed(&self) -> bool { + // TODO: This seems to be a useless function. Replace with a call to file.database().is_some()? self.is_database_backed_for_view_type("") } + /// Checks to see if the file metadata has a [`Database`], and then checks to see if the `view_type` + /// is available. + /// + /// NOTE: Passing an empty string will simply check if the database exists. pub fn is_database_backed_for_view_type(&self, view_type: &str) -> bool { let view_type = view_type.to_cstr(); - + // TODO: This seems to be a useless function. Replace with a call to file.database().is_some()? unsafe { BNIsBackedByDatabase(self.handle, view_type.as_ref().as_ptr() as *const _) } } @@ -310,10 +406,23 @@ impl FileMetadata { } } + /// Retrieve the raw view for the file, this should always be present. + /// + /// The "Raw" view is a special [`BinaryView`] that holds data required for updating and creating + /// [`Database`]s such as the view and load settings. + pub fn raw_view(&self) -> Ref<BinaryView> { + self.view_of_type("Raw") + .expect("Raw view should always be present") + } + + /// The current view for the file. + /// + /// For example, opening a PE file and navigating to the linear view will return "Linear:PE". pub fn current_view(&self) -> String { unsafe { BnString::into_string(BNGetCurrentView(self.handle)) } } + /// The current offset navigated to within the [`FileMetadata::current_view`]. pub fn current_offset(&self) -> u64 { unsafe { BNGetCurrentOffset(self.handle) } } @@ -360,6 +469,9 @@ impl FileMetadata { } } + /// The [`BinaryViewType`]s associated with this file. + /// + /// For example, opening a PE binary will have the following: "Raw", "PE". pub fn view_types(&self) -> Array<BnString> { let mut count = 0; unsafe { @@ -376,32 +488,26 @@ impl FileMetadata { } } - pub fn create_database(&self, file_path: impl AsRef<Path>) -> bool { - // Databases are created with the root view (Raw). - let Some(raw_view) = self.view_of_type("Raw") else { - return false; - }; - - let file_path = file_path.as_ref().to_cstr(); - unsafe { - BNCreateDatabase( - raw_view.handle, - file_path.as_ptr() as *mut _, - ptr::null_mut(), - ) - } + /// Create a database for the file and its views at `file_path`. + /// + /// NOTE: Calling this while analysis is running will flag the next load of the database to + /// regenerate the current analysis. + pub fn create_database(&self, file_path: impl AsRef<Path>, settings: &SaveSettings) -> bool { + self.create_database_with_progress(file_path, settings, NoProgressCallback) } - // TODO: Pass settings? + /// Create a database for the file and its views at `file_path`, with a progress callback. + /// + /// NOTE: Calling this while analysis is running will flag the next load of the database to + /// regenerate the current analysis. pub fn create_database_with_progress<P: ProgressCallback>( &self, file_path: impl AsRef<Path>, + settings: &SaveSettings, mut progress: P, ) -> bool { // Databases are created with the root view (Raw). - let Some(raw_view) = self.view_of_type("Raw") else { - return false; - }; + let raw_view = self.raw_view(); let file_path = file_path.as_ref().to_cstr(); unsafe { BNCreateDatabaseWithProgress( @@ -409,20 +515,22 @@ impl FileMetadata { file_path.as_ptr() as *mut _, &mut progress as *mut P as *mut c_void, Some(P::cb_progress_callback), - ptr::null_mut(), + settings.handle, ) } } + /// Save a new snapshot of the current file. + /// + /// NOTE: Calling this while analysis is running will flag the next load of the database to + /// regenerate the current analysis. pub fn save_auto_snapshot(&self) -> bool { // Snapshots are saved with the root view (Raw). - let Some(raw_view) = self.view_of_type("Raw") else { - return false; - }; - - unsafe { BNSaveAutoSnapshot(raw_view.handle, ptr::null_mut() as *mut _) } + let raw_view = self.raw_view(); + unsafe { BNSaveAutoSnapshot(raw_view.handle, std::ptr::null_mut() as *mut _) } } + // TODO: Deprecate this function? Does not seem to do anything different than `open_database`. pub fn open_database_for_configuration(&self, file: &Path) -> Result<Ref<BinaryView>, ()> { let file = file.to_cstr(); unsafe { @@ -437,6 +545,7 @@ impl FileMetadata { } } + // TODO: How this relates to `BNLoadFilename`? pub fn open_database(&self, file: &Path) -> Result<Ref<BinaryView>, ()> { let file = file.to_cstr(); let view = unsafe { BNOpenExistingDatabase(self.handle, file.as_ptr()) }; @@ -448,6 +557,7 @@ impl FileMetadata { } } + // TODO: How this relates to `BNLoadFilename`? pub fn open_database_with_progress<P: ProgressCallback>( &self, file: &Path, @@ -471,7 +581,9 @@ impl FileMetadata { } } - /// Get the current database + /// Get the database attached to this file. + /// + /// Only available if this file is a database, or has called [`FileMetadata::create_database`]. pub fn database(&self) -> Option<Ref<Database>> { let result = unsafe { BNGetFileMetadataDatabase(self.handle) }; NonNull::new(result).map(|handle| unsafe { Database::ref_from_raw(handle) }) diff --git a/rust/src/main_thread.rs b/rust/src/main_thread.rs index f6fdc7cf..cf4c37f1 100644 --- a/rust/src/main_thread.rs +++ b/rust/src/main_thread.rs @@ -21,9 +21,9 @@ impl MainThreadActionExecutor { } } -/// Execute passed function on the main thread. Returns `None` if already running on the main thread. +/// Execute the passed function on the main thread. Returns `None` if already running on the main thread. /// -/// When not running in headless this will block the UI. +/// When not running in headless, this will block the UI. pub fn execute_on_main_thread<F: Fn() + 'static>(f: F) -> Option<Ref<MainThreadAction>> { let boxed_executor = Box::new(MainThreadActionExecutor { func: Box::new(f) }); let raw_executor = Box::into_raw(boxed_executor); @@ -39,9 +39,9 @@ pub fn execute_on_main_thread<F: Fn() + 'static>(f: F) -> Option<Ref<MainThreadA } } -/// Execute passed function on the main thread and wait until the function is finished. +/// Execute the passed function on the main thread and wait until the function is finished. /// -/// When not running in headless this will block the UI. +/// When not running in headless, this will block the UI. pub fn execute_on_main_thread_and_wait<F: Fn() + 'static>(f: F) { let boxed_executor = Box::new(MainThreadActionExecutor { func: Box::new(f) }); let raw_executor = Box::into_raw(boxed_executor); diff --git a/rust/src/types/printer.rs b/rust/src/types/printer.rs index d168434e..51845df7 100644 --- a/rust/src/types/printer.rs +++ b/rust/src/types/printer.rs @@ -338,6 +338,7 @@ impl CoreTypePrinter { raw_names.as_mut_ptr(), raw_types.as_mut_ptr(), raw_types.len(), + // TODO: Add a print_all_types that accepts a set of type containers. data.handle, padding_cols as c_int, escaping, diff --git a/rust/src/workflow.rs b/rust/src/workflow.rs index d06890f1..e53e0001 100644 --- a/rust/src/workflow.rs +++ b/rust/src/workflow.rs @@ -5,15 +5,13 @@ use binaryninjacore_sys::*; use crate::binary_view::{memory_map::MemoryMap, BinaryViewBase, BinaryViewExt}; use crate::basic_block::BasicBlock; -use crate::binary_view::{AddressRange, BinaryView}; +use crate::binary_view::BinaryView; use crate::flowgraph::FlowGraph; use crate::function::{Function, NativeBlock}; use crate::high_level_il::HighLevelILFunction; use crate::low_level_il::{LowLevelILMutableFunction, LowLevelILRegularFunction}; use crate::medium_level_il::MediumLevelILFunction; use crate::rc::{Array, CoreArrayProvider, CoreArrayProviderInner, Guard, Ref, RefCountable}; -use crate::section::Section; -use crate::segment::{Segment, SegmentFlags}; use crate::string::{BnString, IntoCStr}; use std::ffi::c_char; use std::ptr; @@ -131,229 +129,6 @@ impl AnalysisContext { blocks.iter().map(|block| block.handle).collect(); unsafe { BNSetBasicBlockList(self.handle.as_ptr(), blocks_raw.as_mut_ptr(), blocks.len()) } } - - // Settings cache access - lock-free access to cached settings - - /// Get a boolean setting from the cached settings - pub fn get_setting_bool(&self, key: &str) -> bool { - let key = key.to_cstr(); - unsafe { BNAnalysisContextGetSettingBool(self.handle.as_ptr(), key.as_ptr()) } - } - - /// Get a double setting from the cached settings - pub fn get_setting_double(&self, key: &str) -> f64 { - let key = key.to_cstr(); - unsafe { BNAnalysisContextGetSettingDouble(self.handle.as_ptr(), key.as_ptr()) } - } - - /// Get a signed 64-bit integer setting from the cached settings - pub fn get_setting_int64(&self, key: &str) -> i64 { - let key = key.to_cstr(); - unsafe { BNAnalysisContextGetSettingInt64(self.handle.as_ptr(), key.as_ptr()) } - } - - /// Get an unsigned 64-bit integer setting from the cached settings - pub fn get_setting_uint64(&self, key: &str) -> u64 { - let key = key.to_cstr(); - unsafe { BNAnalysisContextGetSettingUInt64(self.handle.as_ptr(), key.as_ptr()) } - } - - /// Get a string setting from the cached settings - pub fn get_setting_string(&self, key: &str) -> BnString { - let key = key.to_cstr(); - unsafe { - let result = BNAnalysisContextGetSettingString(self.handle.as_ptr(), key.as_ptr()); - BnString::from_raw(result) - } - } - - /// Get a string list setting from the cached settings - pub fn get_setting_string_list(&self, key: &str) -> Array<BnString> { - let key = key.to_cstr(); - unsafe { - let mut count = 0; - let result = BNAnalysisContextGetSettingStringList( - self.handle.as_ptr(), - key.as_ptr(), - &mut count, - ); - Array::new(result, count, ()) - } - } - - /// Check if an offset is mapped in the cached [`MemoryMap`]. - /// - /// NOTE: This is a lock-free alternative to [`BinaryView::offset_valid`]. - pub fn is_offset_valid(&self, offset: u64) -> bool { - unsafe { BNAnalysisContextIsValidOffset(self.handle.as_ptr(), offset) } - } - - /// Check if an offset is readable in the cached [`MemoryMap`]. - /// - /// NOTE: This is a lock-free alternative to [`BinaryView::offset_readable`]. - pub fn is_offset_readable(&self, offset: u64) -> bool { - unsafe { BNAnalysisContextIsOffsetReadable(self.handle.as_ptr(), offset) } - } - - /// Check if an offset is writable in the cached [`MemoryMap`]. - /// - /// NOTE: This is a lock-free alternative to [`BinaryView::offset_writable`]. - pub fn is_offset_writable(&self, offset: u64) -> bool { - unsafe { BNAnalysisContextIsOffsetWritable(self.handle.as_ptr(), offset) } - } - - /// Check if an offset is executable in the cached [`MemoryMap`]. - /// - /// NOTE: This is a lock-free alternative to [`BinaryView::offset_executable`]. - pub fn is_offset_executable(&self, offset: u64) -> bool { - unsafe { BNAnalysisContextIsOffsetExecutable(self.handle.as_ptr(), offset) } - } - - /// Check if an offset is backed by file in the cached [`MemoryMap`]. - /// - /// NOTE: This is a lock-free alternative to [`BinaryView::offset_backed_by_file`]. - pub fn is_offset_backed_by_file(&self, offset: u64) -> bool { - unsafe { BNAnalysisContextIsOffsetBackedByFile(self.handle.as_ptr(), offset) } - } - - /// Check if an offset has code semantics in the cached section map. - /// - /// NOTE: This is a lock-free alternative to [`BinaryViewExt::offset_has_code_semantics`]. - pub fn is_offset_code_semantics(&self, offset: u64) -> bool { - unsafe { BNAnalysisContextIsOffsetCodeSemantics(self.handle.as_ptr(), offset) } - } - - /// Check if an offset has external semantics in the cached section map. - /// - /// NOTE: This is a lock-free alternative to [`BinaryViewExt::offset_has_extern_semantics`]. - pub fn is_offset_extern_semantics(&self, offset: u64) -> bool { - unsafe { BNAnalysisContextIsOffsetExternSemantics(self.handle.as_ptr(), offset) } - } - - /// Check if an offset has writable semantics in the cached section map. - /// - /// NOTE: This is a lock-free alternative to [`BinaryViewExt::offset_has_writable_semantics`]. - pub fn is_offset_writable_semantics(&self, offset: u64) -> bool { - unsafe { BNAnalysisContextIsOffsetWritableSemantics(self.handle.as_ptr(), offset) } - } - - /// Check if an offset has read-only semantics in the cached section map. - /// - /// NOTE: This is a lock-free alternative to [`BinaryViewExt::offset_has_read_only_semantics`]. - pub fn is_offset_readonly_semantics(&self, offset: u64) -> bool { - unsafe { BNAnalysisContextIsOffsetReadOnlySemantics(self.handle.as_ptr(), offset) } - } - - /// Get all sections from the cached section map. - /// - /// NOTE: This is a lock-free alternative to [`BinaryViewExt::sections`]. - pub fn sections(&self) -> Array<Section> { - unsafe { - let mut count = 0; - let sections = BNAnalysisContextGetSections(self.handle.as_ptr(), &mut count); - Array::new(sections, count, ()) - } - } - - /// Get a section by name from the cached section map. - /// - /// NOTE: This is a lock-free alternative to [`BinaryViewExt::section_by_name`]. - pub fn section_by_name(&self, name: impl IntoCStr) -> Option<Ref<Section>> { - unsafe { - let raw_name = name.to_cstr(); - let name_ptr = raw_name.as_ptr(); - let raw_section_ptr = BNAnalysisContextGetSectionByName(self.handle.as_ptr(), name_ptr); - match raw_section_ptr.is_null() { - false => Some(Section::ref_from_raw(raw_section_ptr)), - true => None, - } - } - } - - /// Get all sections containing the given address from the cached section map. - /// - /// NOTE: This is a lock-free alternative to [`BinaryViewExt::sections_at`]. - pub fn sections_at(&self, addr: u64) -> Array<Section> { - unsafe { - let mut count = 0; - let sections = BNAnalysisContextGetSectionsAt(self.handle.as_ptr(), addr, &mut count); - Array::new(sections, count, ()) - } - } - - /// Get the start address (the lowest address) from the cached [`MemoryMap`]. - /// - /// NOTE: This is a lock-free alternative to [`BinaryView::start`]. - pub fn start(&self) -> u64 { - unsafe { BNAnalysisContextGetStart(self.handle.as_ptr()) } - } - - /// Get the end address (the highest address) from the cached [`MemoryMap`]. - /// - /// NOTE: This is a lock-free alternative to [`BinaryViewExt::end`]. - pub fn end(&self) -> u64 { - unsafe { BNAnalysisContextGetEnd(self.handle.as_ptr()) } - } - - /// Get the length of the cached [`MemoryMap`]. - /// - /// NOTE: This is a lock-free alternative to [`BinaryViewBase::len`]. - pub fn length(&self) -> u64 { - unsafe { BNAnalysisContextGetLength(self.handle.as_ptr()) } - } - - /// Get the next valid offset after the given offset from the cached [`MemoryMap`]. - /// - /// NOTE: This is a lock-free alternative to [`BinaryView::next_valid_offset_after`]. - pub fn next_valid_offset(&self, offset: u64) -> u64 { - unsafe { BNAnalysisContextGetNextValidOffset(self.handle.as_ptr(), offset) } - } - - /// Get the next mapped address after the given address from the cached [`MemoryMap`]. - pub fn next_mapped_address(&self, addr: u64, flags: &SegmentFlags) -> u64 { - unsafe { - BNAnalysisContextGetNextMappedAddress(self.handle.as_ptr(), addr, flags.into_raw()) - } - } - - /// Get the next backed address after the given address from the cached [`MemoryMap`]. - pub fn next_backed_address(&self, addr: u64, flags: &SegmentFlags) -> u64 { - unsafe { - BNAnalysisContextGetNextBackedAddress(self.handle.as_ptr(), addr, flags.into_raw()) - } - } - - /// Get the segment containing the given address from the cached [`MemoryMap`]. - /// - /// NOTE: This is a lock-free alternative to [`BinaryViewExt::segment_at`]. - pub fn segment_at(&self, addr: u64) -> Option<Ref<Segment>> { - unsafe { - let result = BNAnalysisContextGetSegmentAt(self.handle.as_ptr(), addr); - if result.is_null() { - None - } else { - Some(Segment::ref_from_raw(result)) - } - } - } - - /// Get all mapped address ranges from the cached [`MemoryMap`]. - pub fn mapped_address_ranges(&self) -> Array<AddressRange> { - unsafe { - let mut count = 0; - let ranges = BNAnalysisContextGetMappedAddressRanges(self.handle.as_ptr(), &mut count); - Array::new(ranges, count, ()) - } - } - - /// Get all backed address ranges from the cached [`MemoryMap`]. - pub fn backed_address_ranges(&self) -> Array<AddressRange> { - unsafe { - let mut count = 0; - let ranges = BNAnalysisContextGetBackedAddressRanges(self.handle.as_ptr(), &mut count); - Array::new(ranges, count, ()) - } - } } impl ToOwned for AnalysisContext { diff --git a/rust/tests/binary_view.rs b/rust/tests/binary_view.rs index 7a0a3299..11caf3d0 100644 --- a/rust/tests/binary_view.rs +++ b/rust/tests/binary_view.rs @@ -3,6 +3,7 @@ use binaryninja::binary_view::{ AnalysisProgress, AnalysisState, BinaryViewBase, BinaryViewExt, StringType, }; use binaryninja::data_buffer::DataBuffer; +use binaryninja::file_metadata::SaveSettings; use binaryninja::function::{Function, FunctionViewType}; use binaryninja::headless::Session; use binaryninja::main_thread::execute_on_main_thread_and_wait; @@ -69,7 +70,9 @@ fn test_binary_saving_database() { // Save the modified database. let temp_dir = tempfile::tempdir().expect("Failed to create temporary directory"); let temp_path = temp_dir.path().join("atox.obj.bndb"); - assert!(view.file().create_database(&temp_path)); + assert!(view + .file() + .create_database(&temp_path, &SaveSettings::new())); // Verify that the file exists and is modified. let new_view = binaryninja::load(temp_path).expect("Failed to load new view"); let new_entry_function = new_view diff --git a/rust/tests/collaboration.rs b/rust/tests/collaboration.rs index 529e185b..914935b5 100644 --- a/rust/tests/collaboration.rs +++ b/rust/tests/collaboration.rs @@ -1,5 +1,6 @@ use binaryninja::binary_view::BinaryViewExt; use binaryninja::collaboration::{NoNameChangeset, Remote, RemoteFileType, RemoteProject}; +use binaryninja::file_metadata::SaveSettings; use binaryninja::headless::Session; use binaryninja::rc::Ref; use binaryninja::symbol::{SymbolBuilder, SymbolType}; @@ -7,7 +8,6 @@ use rstest::*; use serial_test::serial; use std::env; use std::path::PathBuf; - // TODO: Why cant we create_project for the same project name? why does that fail. // TODO: Remote connection / disconnection is NOT thread safe, the core needs to lock on each. // TODO: Because of this we run these tests serially, this isnt _really_ an issue for real code, as @@ -182,7 +182,8 @@ fn test_project_sync() { let view_type = view.view_type(); // Save the view to local database so that we can upload it assert!( - view.file().create_database(out_dir.join("atox.obj.bndb")), + view.file() + .create_database(out_dir.join("atox.obj.bndb"), &SaveSettings::new()), "Failed to create local database" ); // We should have a single snapshot. diff --git a/rust/tests/function.rs b/rust/tests/function.rs index ac15fd91..507b405d 100644 --- a/rust/tests/function.rs +++ b/rust/tests/function.rs @@ -116,7 +116,7 @@ fn add_function() { // Make sure you cannot add a function without a default platform. let code = &[0xa1, 0xfa, 0xf8, 0xf0, 0x99, 0x83, 0xc0, 0x37, 0xc3]; - let view = BinaryView::from_data(&FileMetadata::new(), code).expect("Failed to create view"); + let view = BinaryView::from_data(&FileMetadata::new(), code); assert!(view.add_user_function(0).is_none()); assert!(view.add_auto_function(0).is_none()); diff --git a/rust/tests/initialization.rs b/rust/tests/initialization.rs index 69c0c191..e50a5c77 100644 --- a/rust/tests/initialization.rs +++ b/rust/tests/initialization.rs @@ -27,10 +27,6 @@ fn test_license_validation() { // Actually make sure we can initialize. init().expect("Failed to initialize, make sure you have a license before trying to run tests!"); // Open an empty binary and make sure it succeeds. - let view = BinaryView::from_data(&FileMetadata::new(), &[]); - assert!( - view.is_ok(), - "Failed to open empty binary, core initialization failed!" - ); + let _ = BinaryView::from_data(&FileMetadata::new(), &[]); shutdown(); } diff --git a/rust/tests/type_container.rs b/rust/tests/type_container.rs index 96f3bc4a..a0916ef4 100644 --- a/rust/tests/type_container.rs +++ b/rust/tests/type_container.rs @@ -37,8 +37,7 @@ fn test_type_id() { #[test] fn test_add_delete_type() { let _session = Session::new().expect("Failed to initialize session"); - let empty_view = - BinaryView::from_data(&FileMetadata::new(), &[]).expect("Failed to create view"); + let empty_view = BinaryView::from_data(&FileMetadata::new(), &[]); let view_type_container = empty_view.type_container(); let test_type = Type::int(4, true); assert!( @@ -106,8 +105,7 @@ fn test_parse_type() { fn test_container_lifetime() { let _session = Session::new().expect("Failed to initialize session"); let platform = Platform::by_name("windows-x86_64").expect("windows-x86_64 exists"); - let empty_view = - BinaryView::from_data(&FileMetadata::new(), &[]).expect("Failed to create view"); + let empty_view = BinaryView::from_data(&FileMetadata::new(), &[]); let plat_type_container_dropped = platform.type_container(); let view_type_container_dropped = empty_view.type_container(); let _plat_types_dropped = plat_type_container_dropped.types(); diff --git a/rust/tests/types.rs b/rust/tests/types.rs index 962df630..c13a1c88 100644 --- a/rust/tests/types.rs +++ b/rust/tests/types.rs @@ -61,8 +61,7 @@ fn test_structure_builder() { #[test] fn add_type_to_view() { let _session = Session::new().expect("Failed to initialize session"); - let empty_view = - BinaryView::from_data(&FileMetadata::new(), &[]).expect("Failed to create view"); + let empty_view = BinaryView::from_data(&FileMetadata::new(), &[]); let test_type = Type::int(4, true); empty_view.define_auto_type("test", "me", &test_type); assert!(empty_view.type_by_name("test").is_some()); |
