binaryninja/binary_view/
memory_map.rs

1use crate::binary_view::BinaryView;
2use crate::data_buffer::DataBuffer;
3use crate::file_accessor::{Accessor, FileAccessor};
4use crate::rc::Ref;
5use crate::segment::SegmentFlags;
6use crate::string::{raw_to_string, BnString, IntoCStr};
7use binaryninjacore_sys::*;
8
9/// Snapshot of a memory region's properties at the time of query.
10///
11/// This is a value type — modifying the memory map will not update existing
12/// `MemoryRegionInfo` instances. To mutate a region, use the corresponding
13/// [`MemoryMap`] methods.
14#[derive(Debug, Clone, PartialEq, Eq, Hash)]
15pub struct MemoryRegionInfo {
16    pub name: String,
17    pub display_name: String,
18    pub start: u64,
19    pub length: u64,
20    pub flags: SegmentFlags,
21    pub enabled: bool,
22    pub rebaseable: bool,
23    pub fill: u8,
24    pub has_target: bool,
25    pub absolute_address_mode: bool,
26    pub local: bool,
27}
28
29impl MemoryRegionInfo {
30    pub fn end(&self) -> u64 {
31        self.start + self.length
32    }
33
34    fn from_raw(region: &BNMemoryRegionInfo) -> Self {
35        Self {
36            name: raw_to_string(region.name).unwrap_or_default(),
37            display_name: raw_to_string(region.displayName).unwrap_or_default(),
38            start: region.start,
39            length: region.length,
40            flags: SegmentFlags::from_raw(region.flags),
41            enabled: region.enabled,
42            rebaseable: region.rebaseable,
43            fill: region.fill,
44            has_target: region.hasTarget,
45            absolute_address_mode: region.absoluteAddressMode,
46            local: region.local,
47        }
48    }
49}
50
51/// A resolved, non-overlapping address range in the memory map.
52///
53/// Each range contains an ordered list of memory regions that overlap at this
54/// interval. The first region is the active (highest priority) one.
55///
56/// This is a snapshot value — it is not updated by later mutations to the
57/// memory map.
58#[derive(Debug, Clone, PartialEq, Eq, Hash)]
59pub struct ResolvedRange {
60    pub start: u64,
61    pub length: u64,
62    pub regions: Vec<MemoryRegionInfo>,
63}
64
65impl ResolvedRange {
66    pub fn end(&self) -> u64 {
67        self.start + self.length
68    }
69
70    /// The highest-priority region at this range.
71    pub fn active_region(&self) -> Option<&MemoryRegionInfo> {
72        self.regions.first()
73    }
74
75    /// Name of the active region, or `None` if empty.
76    pub fn name(&self) -> Option<&str> {
77        self.active_region().map(|r| r.name.as_str())
78    }
79
80    /// Flags of the active (highest-priority) region.
81    pub fn flags(&self) -> SegmentFlags {
82        self.active_region()
83            .map(|r| r.flags)
84            .unwrap_or(SegmentFlags::from_raw(0))
85    }
86}
87
88/// Live proxy to the memory map of a [`BinaryView`].
89///
90/// A `MemoryMap` describes how a [`BinaryView`] is loaded into memory. It
91/// contains *regions* — raw, possibly overlapping memory definitions — and
92/// exposes *resolved ranges* — a computed, disjoint view of the address space
93/// produced by splitting overlapping regions. The most recently added region
94/// takes precedence when regions overlap. Mutation is always by region name.
95///
96/// - [`regions()`](Self::regions) returns configured memory regions, including
97///   disabled ones.
98/// - [`ranges()`](Self::ranges) returns resolved, non-overlapping address
99///   ranges — the computed active view.
100/// - Both return snapshot values that are not updated after later mutations.
101///
102/// # Architecture Note
103///
104/// This Rust `MemoryMap` struct is a proxy that accesses the BinaryView's current MemoryMap state through
105/// the FFI boundary. The proxy provides a simple mutable interface: when you call modification operations
106/// (add_memory_region, remove_memory_region, etc.), the proxy automatically accesses the updated MemoryMap.
107/// Internally, the core uses immutable copy-on-write data structures, but the proxy abstracts this away.
108///
109/// When you access a BinaryView's MemoryMap, you always see the current state. For lock-free access during
110/// analysis, AnalysisContext provides memory layout query methods (is_valid_offset, is_offset_readable, get_start,
111/// get_length, etc.) that operate on an immutable snapshot of the MemoryMap cached when the analysis was initiated.
112///
113/// A MemoryMap can contain multiple, arbitrarily overlapping memory regions. When modified, address space
114/// segmentation is automatically managed. If multiple regions overlap, the most recently added region takes
115/// precedence by default.
116///
117/// All MemoryMap APIs support undo and redo operations. During BinaryView::Init, these APIs should be used
118/// conditionally:
119///
120/// * Initial load: Use the MemoryMap APIs to define the memory regions that compose the system.
121/// * Database load: Do not use the MemoryMap APIs, as the regions are already persisted and will be restored
122///   automatically.
123#[derive(PartialEq, Eq, Hash)]
124pub struct MemoryMap {
125    view: Ref<BinaryView>,
126}
127
128impl MemoryMap {
129    pub fn new(view: Ref<BinaryView>) -> Self {
130        Self { view }
131    }
132
133    /// Returns a snapshot of all configured memory regions, including disabled ones.
134    pub fn regions(&self) -> Vec<MemoryRegionInfo> {
135        let mut count: usize = 0;
136        let regions_raw = unsafe { BNGetMemoryRegions(self.view.handle, &mut count) };
137        if regions_raw.is_null() {
138            return Vec::new();
139        }
140        let mut result = Vec::with_capacity(count);
141        for i in 0..count {
142            let region = unsafe { &*regions_raw.add(i) };
143            result.push(MemoryRegionInfo::from_raw(region));
144        }
145        unsafe { BNFreeMemoryRegions(regions_raw, count) };
146        result
147    }
148
149    /// Returns a snapshot of the resolved, non-overlapping address ranges.
150    ///
151    /// Each range contains an ordered list of memory regions, with the first
152    /// being the active (highest priority) region at that interval.
153    pub fn ranges(&self) -> Vec<ResolvedRange> {
154        let mut count: usize = 0;
155        let ranges_raw = unsafe { BNGetResolvedMemoryRanges(self.view.handle, &mut count) };
156        if ranges_raw.is_null() {
157            return Vec::new();
158        }
159        let mut result = Vec::with_capacity(count);
160        for i in 0..count {
161            let range = unsafe { &*ranges_raw.add(i) };
162            let mut regions = Vec::with_capacity(range.regionCount);
163            for j in 0..range.regionCount {
164                let region = unsafe { &*range.regions.add(j) };
165                regions.push(MemoryRegionInfo::from_raw(region));
166            }
167            result.push(ResolvedRange {
168                start: range.start,
169                length: range.length,
170                regions,
171            });
172        }
173        unsafe { BNFreeResolvedMemoryRanges(ranges_raw, count) };
174        result
175    }
176
177    /// Look up a configured memory region by name.
178    ///
179    /// Returns a snapshot of the region's properties, or `None` if no region
180    /// with the given name exists.
181    pub fn get_region(&self, name: &str) -> Option<MemoryRegionInfo> {
182        let name_raw = name.to_cstr();
183        let mut result: BNMemoryRegionInfo = unsafe { std::mem::zeroed() };
184        let found =
185            unsafe { BNGetMemoryRegionInfo(self.view.handle, name_raw.as_ptr(), &mut result) };
186        if !found {
187            return None;
188        }
189        let info = MemoryRegionInfo::from_raw(&result);
190        unsafe { BNFreeMemoryRegionInfo(&mut result) };
191        Some(info)
192    }
193
194    /// Return the active region snapshot covering `addr`, or `None` if no
195    /// enabled region covers the address.
196    pub fn get_active_region_at(&self, addr: u64) -> Option<MemoryRegionInfo> {
197        let mut result: BNMemoryRegionInfo = unsafe { std::mem::zeroed() };
198        let found = unsafe { BNGetActiveMemoryRegionInfoAt(self.view.handle, addr, &mut result) };
199        if !found {
200            return None;
201        }
202        let info = MemoryRegionInfo::from_raw(&result);
203        unsafe { BNFreeMemoryRegionInfo(&mut result) };
204        Some(info)
205    }
206
207    /// Return the resolved range snapshot covering `addr`, or `None` if no
208    /// range covers the address.
209    pub fn get_resolved_range_at(&self, addr: u64) -> Option<ResolvedRange> {
210        let mut result: BNResolvedMemoryRange = unsafe { std::mem::zeroed() };
211        let found = unsafe { BNGetResolvedMemoryRangeAt(self.view.handle, addr, &mut result) };
212        if !found {
213            return None;
214        }
215        let mut regions = Vec::with_capacity(result.regionCount);
216        for j in 0..result.regionCount {
217            let region = unsafe { &*result.regions.add(j) };
218            regions.push(MemoryRegionInfo::from_raw(region));
219        }
220        let resolved = ResolvedRange {
221            start: result.start,
222            length: result.length,
223            regions,
224        };
225        unsafe { BNFreeResolvedMemoryRange(&mut result) };
226        Some(resolved)
227    }
228
229    /// JSON string representation of the base [`MemoryMap`], consisting of unresolved auto and user segments.
230    pub fn base_description(&self) -> String {
231        let desc_raw = unsafe { BNGetBaseMemoryMapDescription(self.view.handle) };
232        unsafe { BnString::into_string(desc_raw) }
233    }
234
235    /// JSON string representation of the [`MemoryMap`].
236    pub fn description(&self) -> String {
237        let desc_raw = unsafe { BNGetMemoryMapDescription(self.view.handle) };
238        unsafe { BnString::into_string(desc_raw) }
239    }
240
241    // When enabled, the memory map will present a simplified, logical view that merges and abstracts virtual memory
242    // regions based on criteria such as contiguity and flag consistency. This view is designed to provide a higher-level
243    // representation for user analysis, hiding underlying mapping details.
244    //
245    // When disabled, the memory map will revert to displaying the virtual view, which corresponds directly to the individual
246    // segments mapped from the raw file without any merging or abstraction.
247    pub fn set_logical_enabled(&mut self, enabled: bool) {
248        unsafe { BNSetLogicalMemoryMapEnabled(self.view.handle, enabled) };
249    }
250
251    /// Whether the memory map is activated for the associated view.
252    ///
253    /// Returns `true` if this MemoryMap represents a parsed BinaryView with real segments
254    /// (ELF, PE, Mach-O, etc.). Returns `false` for Raw BinaryViews or views that failed
255    /// to parse segments.
256    ///
257    /// This is determined by whether the BinaryView has a parent view - parsed views have a
258    /// parent Raw view, while Raw views have no parent.
259    ///
260    /// Use this to gate features that require parsed binary structure (sections, imports,
261    /// relocations, etc.). For basic analysis queries (start, length, is_offset_readable, etc.),
262    /// use the MemoryMap directly regardless of activation state - all BinaryViews have a
263    /// usable MemoryMap.
264    pub fn is_activated(&self) -> bool {
265        unsafe { BNIsMemoryMapActivated(self.view.handle) }
266    }
267
268    pub fn add_binary_memory_region(
269        &mut self,
270        name: &str,
271        start: u64,
272        view: &BinaryView,
273        segment_flags: Option<SegmentFlags>,
274    ) -> bool {
275        let name_raw = name.to_cstr();
276        unsafe {
277            BNAddBinaryMemoryRegion(
278                self.view.handle,
279                name_raw.as_ptr(),
280                start,
281                view.handle,
282                segment_flags.unwrap_or_default().into_raw(),
283            )
284        }
285    }
286
287    /// Adds the memory region using a [`DataBuffer`].
288    ///
289    /// This will add the contents of the [`DataBuffer`] to the database.
290    pub fn add_data_memory_region(
291        &mut self,
292        name: &str,
293        start: u64,
294        data: &DataBuffer,
295        segment_flags: Option<SegmentFlags>,
296    ) -> bool {
297        let name_raw = name.to_cstr();
298        unsafe {
299            BNAddDataMemoryRegion(
300                self.view.handle,
301                name_raw.as_ptr(),
302                start,
303                data.as_raw(),
304                segment_flags.unwrap_or_default().into_raw(),
305            )
306        }
307    }
308
309    // TODO: This really cant be safe until BNFileAccessor is ARC'd and can be freed. Probably need another thing
310    // TODO: Ontop of a file accessor in the core that would manage it. (I.e. BNFileAccessorHandle) or something.
311    /// Adds the memory region using a [`FileAccessor`].
312    ///
313    /// This does not add the region contents to the database, instead accesses to the contents
314    /// are done "remotely" to a [`FileAccessor`].
315    ///
316    /// NOTE: The [`FileAccessor`] MUST live as long as the region is available, currently there is no gurentee by
317    /// the type checker that the file accessor is tied to that of the memory region.
318    pub fn add_remote_memory_region<A: Accessor>(
319        &mut self,
320        name: &str,
321        start: u64,
322        accessor: &mut FileAccessor<A>,
323        segment_flags: Option<SegmentFlags>,
324    ) -> bool {
325        let name_raw = name.to_cstr();
326        unsafe {
327            BNAddRemoteMemoryRegion(
328                self.view.handle,
329                name_raw.as_ptr(),
330                start,
331                &mut accessor.raw,
332                segment_flags.unwrap_or_default().into_raw(),
333            )
334        }
335    }
336
337    /// Adds an unbacked memory region with a given length and fill byte.
338    pub fn add_unbacked_memory_region(
339        &mut self,
340        name: &str,
341        start: u64,
342        length: u64,
343        segment_flags: Option<SegmentFlags>,
344        fill: Option<u8>,
345    ) -> bool {
346        let name_raw = name.to_cstr();
347        unsafe {
348            BNAddUnbackedMemoryRegion(
349                self.view.handle,
350                name_raw.as_ptr(),
351                start,
352                length,
353                segment_flags.unwrap_or_default().into_raw(),
354                fill.unwrap_or_default(),
355            )
356        }
357    }
358
359    pub fn remove_memory_region(&mut self, name: &str) -> bool {
360        let name_raw = name.to_cstr();
361        unsafe { BNRemoveMemoryRegion(self.view.handle, name_raw.as_ptr()) }
362    }
363
364    /// Return the name of the active region at `addr`, or an empty string if
365    /// no region covers the address.
366    pub fn active_memory_region_at(&self, addr: u64) -> String {
367        unsafe {
368            let name_raw = BNGetActiveMemoryRegionAt(self.view.handle, addr);
369            BnString::into_string(name_raw)
370        }
371    }
372
373    pub fn memory_region_flags(&self, name: &str) -> SegmentFlags {
374        let name_raw = name.to_cstr();
375        let flags_raw = unsafe { BNGetMemoryRegionFlags(self.view.handle, name_raw.as_ptr()) };
376        SegmentFlags::from_raw(flags_raw)
377    }
378
379    pub fn set_memory_region_flags(&mut self, name: &str, flags: SegmentFlags) -> bool {
380        let name_raw = name.to_cstr();
381        unsafe { BNSetMemoryRegionFlags(self.view.handle, name_raw.as_ptr(), flags.into_raw()) }
382    }
383
384    pub fn is_memory_region_enabled(&self, name: &str) -> bool {
385        let name_raw = name.to_cstr();
386        unsafe { BNIsMemoryRegionEnabled(self.view.handle, name_raw.as_ptr()) }
387    }
388
389    pub fn set_memory_region_enabled(&mut self, name: &str, enabled: bool) -> bool {
390        let name_raw = name.to_cstr();
391        unsafe { BNSetMemoryRegionEnabled(self.view.handle, name_raw.as_ptr(), enabled) }
392    }
393
394    // TODO: Should we just call this is_memory_region_relocatable?
395    pub fn is_memory_region_rebaseable(&self, name: &str) -> bool {
396        let name_raw = name.to_cstr();
397        unsafe { BNIsMemoryRegionRebaseable(self.view.handle, name_raw.as_ptr()) }
398    }
399
400    pub fn set_memory_region_rebaseable(&mut self, name: &str, enabled: bool) -> bool {
401        let name_raw = name.to_cstr();
402        unsafe { BNSetMemoryRegionRebaseable(self.view.handle, name_raw.as_ptr(), enabled) }
403    }
404
405    pub fn memory_region_fill(&self, name: &str) -> u8 {
406        let name_raw = name.to_cstr();
407        unsafe { BNGetMemoryRegionFill(self.view.handle, name_raw.as_ptr()) }
408    }
409
410    pub fn set_memory_region_fill(&mut self, name: &str, fill: u8) -> bool {
411        let name_raw = name.to_cstr();
412        unsafe { BNSetMemoryRegionFill(self.view.handle, name_raw.as_ptr(), fill) }
413    }
414
415    pub fn reset(&mut self) {
416        unsafe { BNResetMemoryMap(self.view.handle) }
417    }
418}