diff options
| author | Brian Potchik <brian@vector35.com> | 2025-11-23 16:18:34 -0500 |
|---|---|---|
| committer | Brian Potchik <brian@vector35.com> | 2025-11-23 16:18:34 -0500 |
| commit | 56ce6d3d63d6a5cf30ea1f326a91104db59555be (patch) | |
| tree | 175fd417c6084a40823fb7aa5d38ffd7e7406308 /rust/src | |
| parent | 5880769cbac1362aad46a34faec4c553fa9fcac6 (diff) | |
Unify SettingsCache and MemoryMap access under immutable snapshots for consistent, lock-free AnalysisContext queries.
Diffstat (limited to 'rust/src')
| -rw-r--r-- | rust/src/binary_view/memory_map.rs | 28 | ||||
| -rw-r--r-- | rust/src/workflow.rs | 147 |
2 files changed, 175 insertions, 0 deletions
diff --git a/rust/src/binary_view/memory_map.rs b/rust/src/binary_view/memory_map.rs index 914c3aee..bb7902b9 100644 --- a/rust/src/binary_view/memory_map.rs +++ b/rust/src/binary_view/memory_map.rs @@ -6,6 +6,22 @@ use crate::segment::SegmentFlags; use crate::string::{BnString, IntoCStr}; use binaryninjacore_sys::*; +/// MemoryMap provides access to the system-level memory map describing how a BinaryView is loaded into memory. +/// +/// # Architecture Note +/// +/// This Rust `MemoryMap` struct is a proxy that accesses the BinaryView's current MemoryMap state through +/// the FFI boundary. The proxy provides a simple mutable interface: when you call modification operations +/// (add_memory_region, remove_memory_region, etc.), the proxy automatically accesses the updated MemoryMap. +/// Internally, the core uses immutable copy-on-write data structures, but the proxy abstracts this away. +/// +/// When you access a BinaryView's MemoryMap, you always see the current state. For lock-free access during +/// analysis, AnalysisContext provides memory layout query methods (is_valid_offset, is_offset_readable, get_start, +/// get_length, etc.) that operate on an immutable snapshot of the MemoryMap cached when the analysis was initiated. +/// +/// A MemoryMap can contain multiple, arbitrarily overlapping memory regions. When modified, address space +/// segmentation is automatically managed. If multiple regions overlap, the most recently added region takes +/// precedence by default. #[derive(PartialEq, Eq, Hash)] pub struct MemoryMap { view: Ref<BinaryView>, @@ -41,6 +57,18 @@ impl MemoryMap { } /// Whether the memory map is activated for the associated view. + /// + /// Returns `true` if this MemoryMap represents a parsed BinaryView with real segments + /// (ELF, PE, Mach-O, etc.). Returns `false` for Raw BinaryViews or views that failed + /// to parse segments. + /// + /// This is determined by whether the BinaryView has a parent view - parsed views have a + /// parent Raw view, while Raw views have no parent. + /// + /// Use this to gate features that require parsed binary structure (sections, imports, + /// relocations, etc.). For basic analysis queries (start, length, is_offset_readable, etc.), + /// use the MemoryMap directly regardless of activation state - all BinaryViews have a + /// usable MemoryMap. pub fn is_activated(&self) -> bool { unsafe { BNIsMemoryMapActivated(self.view.handle) } } diff --git a/rust/src/workflow.rs b/rust/src/workflow.rs index 49c5760e..618a290c 100644 --- a/rust/src/workflow.rs +++ b/rust/src/workflow.rs @@ -8,6 +8,7 @@ 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::segment::Segment; use crate::string::{BnString, IntoCStr}; use std::ffi::c_char; use std::ptr; @@ -125,6 +126,152 @@ 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, ()) + } + } + + // Memory map access - lock-free access to cached MemoryMap + + /// Check if an offset is mapped in the cached memory map + pub fn is_valid_offset(&self, offset: u64) -> bool { + unsafe { BNAnalysisContextIsValidOffset(self.handle.as_ptr(), offset) } + } + + /// Check if an offset is readable in the cached memory map + 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 memory map + 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 memory map + 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 memory map + pub fn is_offset_backed_by_file(&self, offset: u64) -> bool { + unsafe { BNAnalysisContextIsOffsetBackedByFile(self.handle.as_ptr(), offset) } + } + + /// Get the start address from the cached memory map + pub fn get_start(&self) -> u64 { + unsafe { BNAnalysisContextGetStart(self.handle.as_ptr()) } + } + + /// Get the end address from the cached memory map + pub fn get_end(&self) -> u64 { + unsafe { BNAnalysisContextGetEnd(self.handle.as_ptr()) } + } + + /// Get the length of the cached memory map + pub fn get_length(&self) -> u64 { + unsafe { BNAnalysisContextGetLength(self.handle.as_ptr()) } + } + + /// Get the next valid offset after the given offset from the cached memory map + pub fn get_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 memory map + pub fn get_next_mapped_address(&self, addr: u64, flags: u32) -> u64 { + unsafe { BNAnalysisContextGetNextMappedAddress(self.handle.as_ptr(), addr, flags) } + } + + /// Get the next backed address after the given address from the cached memory map + pub fn get_next_backed_address(&self, addr: u64, flags: u32) -> u64 { + unsafe { BNAnalysisContextGetNextBackedAddress(self.handle.as_ptr(), addr, flags) } + } + + /// Get the segment containing the given address from the cached memory map + pub fn get_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 memory map + pub fn get_mapped_address_ranges(&self) -> Vec<(u64, u64)> { + unsafe { + let mut count = 0; + let ranges = BNAnalysisContextGetMappedAddressRanges(self.handle.as_ptr(), &mut count); + let result = (0..count) + .map(|i| { + let range = *ranges.add(i); + (range.start, range.end) + }) + .collect(); + BNFreeAddressRanges(ranges); + result + } + } + + /// Get all backed address ranges from the cached memory map + pub fn get_backed_address_ranges(&self) -> Vec<(u64, u64)> { + unsafe { + let mut count = 0; + let ranges = BNAnalysisContextGetBackedAddressRanges(self.handle.as_ptr(), &mut count); + let result = (0..count) + .map(|i| { + let range = *ranges.add(i); + (range.start, range.end) + }) + .collect(); + BNFreeAddressRanges(ranges); + result + } + } } impl ToOwned for AnalysisContext { |
