binaryninja/
linear_view.rs

1// Copyright 2022-2025 Vector 35 Inc.
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15//! APIs for accessing Binary Ninja's linear view
16
17use binaryninjacore_sys::*;
18
19use crate::binary_view::BinaryView;
20use crate::disassembly::{DisassemblySettings, DisassemblyTextLine};
21use crate::function::{Function, NativeBlock};
22
23use crate::basic_block::BasicBlock;
24use crate::rc::*;
25use crate::render_layer::CoreRenderLayer;
26use crate::string::{raw_to_string, BnString};
27use std::ops::Deref;
28
29pub type LinearDisassemblyLineType = BNLinearDisassemblyLineType;
30pub type LinearViewObjectIdentifierType = BNLinearViewObjectIdentifierType;
31
32pub struct LinearViewObject {
33    pub(crate) handle: *mut BNLinearViewObject,
34}
35
36impl LinearViewObject {
37    pub(crate) unsafe fn from_raw(handle: *mut BNLinearViewObject) -> Self {
38        debug_assert!(!handle.is_null());
39        Self { handle }
40    }
41
42    pub(crate) unsafe fn ref_from_raw(handle: *mut BNLinearViewObject) -> Ref<Self> {
43        debug_assert!(!handle.is_null());
44        Ref::new(Self { handle })
45    }
46
47    pub fn identifier(&self) -> LinearViewObjectIdentifier {
48        let raw = unsafe { BNGetLinearViewObjectIdentifier(self.handle) };
49        LinearViewObjectIdentifier::from_owned_raw(raw)
50    }
51
52    pub fn data_only(view: &BinaryView, settings: &DisassemblySettings) -> Ref<Self> {
53        unsafe {
54            let handle = BNCreateLinearViewDataOnly(view.handle, settings.handle);
55            Self::ref_from_raw(handle)
56        }
57    }
58
59    pub fn disassembly(view: &BinaryView, settings: &DisassemblySettings) -> Ref<Self> {
60        unsafe {
61            let handle = BNCreateLinearViewDisassembly(view.handle, settings.handle);
62            Self::ref_from_raw(handle)
63        }
64    }
65
66    pub fn lifted_il(view: &BinaryView, settings: &DisassemblySettings) -> Ref<Self> {
67        unsafe {
68            let handle = BNCreateLinearViewLiftedIL(view.handle, settings.handle);
69            Self::ref_from_raw(handle)
70        }
71    }
72
73    pub fn llil(view: &BinaryView, settings: &DisassemblySettings) -> Ref<Self> {
74        unsafe {
75            let handle = BNCreateLinearViewLowLevelIL(view.handle, settings.handle);
76            Self::ref_from_raw(handle)
77        }
78    }
79
80    pub fn mlil(view: &BinaryView, settings: &DisassemblySettings) -> Ref<Self> {
81        unsafe {
82            let handle = BNCreateLinearViewMediumLevelIL(view.handle, settings.handle);
83            Self::ref_from_raw(handle)
84        }
85    }
86
87    pub fn mlil_ssa(view: &BinaryView, settings: &DisassemblySettings) -> Ref<Self> {
88        unsafe {
89            let handle = BNCreateLinearViewMediumLevelILSSAForm(view.handle, settings.handle);
90            Self::ref_from_raw(handle)
91        }
92    }
93
94    pub fn hlil(view: &BinaryView, settings: &DisassemblySettings) -> Ref<Self> {
95        unsafe {
96            let handle = BNCreateLinearViewHighLevelIL(view.handle, settings.handle);
97            Self::ref_from_raw(handle)
98        }
99    }
100
101    pub fn hlil_ssa(view: &BinaryView, settings: &DisassemblySettings) -> Ref<Self> {
102        unsafe {
103            let handle = BNCreateLinearViewHighLevelILSSAForm(view.handle, settings.handle);
104            Self::ref_from_raw(handle)
105        }
106    }
107
108    pub fn language_representation(
109        view: &BinaryView,
110        settings: &DisassemblySettings,
111        language: &str,
112    ) -> Ref<Self> {
113        unsafe {
114            let language = std::ffi::CString::new(language).unwrap();
115            let handle = BNCreateLinearViewLanguageRepresentation(
116                view.handle,
117                settings.handle,
118                language.as_ptr(),
119            );
120
121            Self::ref_from_raw(handle)
122        }
123    }
124
125    pub fn single_function_disassembly(
126        function: &Function,
127        settings: &DisassemblySettings,
128    ) -> Ref<Self> {
129        unsafe {
130            let handle =
131                BNCreateLinearViewSingleFunctionDisassembly(function.handle, settings.handle);
132            Self::ref_from_raw(handle)
133        }
134    }
135
136    pub fn single_function_lifted_il(
137        function: &Function,
138        settings: &DisassemblySettings,
139    ) -> Ref<Self> {
140        unsafe {
141            let handle = BNCreateLinearViewSingleFunctionLiftedIL(function.handle, settings.handle);
142            Self::ref_from_raw(handle)
143        }
144    }
145
146    pub fn single_function_mlil(function: &Function, settings: &DisassemblySettings) -> Ref<Self> {
147        unsafe {
148            let handle =
149                BNCreateLinearViewSingleFunctionMediumLevelIL(function.handle, settings.handle);
150            Self::ref_from_raw(handle)
151        }
152    }
153
154    pub fn single_function_mlil_ssa(
155        function: &Function,
156        settings: &DisassemblySettings,
157    ) -> Ref<Self> {
158        unsafe {
159            let handle = BNCreateLinearViewSingleFunctionMediumLevelILSSAForm(
160                function.handle,
161                settings.handle,
162            );
163            Self::ref_from_raw(handle)
164        }
165    }
166
167    pub fn single_function_hlil(function: &Function, settings: &DisassemblySettings) -> Ref<Self> {
168        unsafe {
169            let handle =
170                BNCreateLinearViewSingleFunctionHighLevelIL(function.handle, settings.handle);
171            Self::ref_from_raw(handle)
172        }
173    }
174
175    pub fn single_function_hlil_ssa(
176        function: &Function,
177        settings: &DisassemblySettings,
178    ) -> Ref<Self> {
179        unsafe {
180            let handle = BNCreateLinearViewSingleFunctionHighLevelILSSAForm(
181                function.handle,
182                settings.handle,
183            );
184            Self::ref_from_raw(handle)
185        }
186    }
187
188    pub fn single_function_language_representation(
189        function: &Function,
190        settings: &DisassemblySettings,
191        language: &str,
192    ) -> Ref<Self> {
193        unsafe {
194            let language = std::ffi::CString::new(language).unwrap();
195            let handle = BNCreateLinearViewSingleFunctionLanguageRepresentation(
196                function.handle,
197                settings.handle,
198                language.as_ptr(),
199            );
200            Self::ref_from_raw(handle)
201        }
202    }
203
204    pub fn create_cursor(&self) -> Ref<LinearViewCursor> {
205        unsafe {
206            let handle = BNCreateLinearViewCursor(self.handle);
207            LinearViewCursor::ref_from_raw(handle)
208        }
209    }
210}
211
212unsafe impl RefCountable for LinearViewObject {
213    unsafe fn inc_ref(handle: &Self) -> Ref<Self> {
214        Ref::new(Self {
215            handle: BNNewLinearViewObjectReference(handle.handle),
216        })
217    }
218
219    unsafe fn dec_ref(handle: &Self) {
220        BNFreeLinearViewObject(handle.handle);
221    }
222}
223
224impl ToOwned for LinearViewObject {
225    type Owned = Ref<Self>;
226
227    fn to_owned(&self) -> Self::Owned {
228        unsafe { RefCountable::inc_ref(self) }
229    }
230}
231
232unsafe impl Send for LinearViewObject {}
233unsafe impl Sync for LinearViewObject {}
234
235#[derive(Clone, PartialEq, Debug)]
236pub struct LinearViewObjectIdentifier {
237    pub name: String,
238    pub ty: LinearViewObjectIdentifierType,
239    pub start: u64,
240    pub end: u64,
241}
242
243impl LinearViewObjectIdentifier {
244    pub fn from_raw(value: &BNLinearViewObjectIdentifier) -> Self {
245        Self {
246            name: raw_to_string(value.name).unwrap(),
247            ty: value.type_,
248            start: value.start,
249            end: value.end,
250        }
251    }
252
253    pub fn from_owned_raw(value: BNLinearViewObjectIdentifier) -> Self {
254        let owned = Self::from_raw(&value);
255        Self::free_raw(value);
256        owned
257    }
258
259    pub fn into_raw(value: Self) -> BNLinearViewObjectIdentifier {
260        let bn_name = BnString::new(value.name);
261        BNLinearViewObjectIdentifier {
262            name: BnString::into_raw(bn_name),
263            type_: value.ty,
264            start: value.start,
265            end: value.end,
266        }
267    }
268
269    pub fn free_raw(value: BNLinearViewObjectIdentifier) {
270        unsafe { BnString::free_raw(value.name) };
271    }
272}
273
274// TODO: Impl iterator?
275#[derive(Eq)]
276pub struct LinearViewCursor {
277    pub(crate) handle: *mut BNLinearViewCursor,
278}
279
280impl LinearViewCursor {
281    pub(crate) unsafe fn ref_from_raw(handle: *mut BNLinearViewCursor) -> Ref<Self> {
282        debug_assert!(!handle.is_null());
283        Ref::new(Self { handle })
284    }
285
286    /// Gets the current [`LinearViewObject`] associated with this cursor.
287    pub fn current_object(&self) -> Ref<LinearViewObject> {
288        unsafe {
289            let handle = BNGetLinearViewCursorCurrentObject(self.handle);
290            LinearViewObject::ref_from_raw(handle)
291        }
292    }
293
294    pub fn duplicate(&self) -> Ref<Self> {
295        unsafe {
296            let handle = BNDuplicateLinearViewCursor(self.handle);
297            Self::ref_from_raw(handle)
298        }
299    }
300
301    pub fn before_begin(&self) -> bool {
302        unsafe { BNIsLinearViewCursorBeforeBegin(self.handle) }
303    }
304
305    pub fn after_end(&self) -> bool {
306        unsafe { BNIsLinearViewCursorAfterEnd(self.handle) }
307    }
308
309    pub fn valid(&self) -> bool {
310        !(self.before_begin() || self.after_end())
311    }
312
313    pub fn seek_to_start(&mut self) {
314        unsafe { BNSeekLinearViewCursorToBegin(self.handle) }
315    }
316
317    pub fn seek_to_end(&mut self) {
318        unsafe { BNSeekLinearViewCursorToEnd(self.handle) }
319    }
320
321    pub fn seek_to_address(&mut self, address: u64) {
322        unsafe { BNSeekLinearViewCursorToAddress(self.handle, address) }
323    }
324
325    pub fn ordering_index(&self) -> std::ops::Range<u64> {
326        unsafe {
327            let range = BNGetLinearViewCursorOrderingIndex(self.handle);
328            range.start..range.end
329        }
330    }
331
332    pub fn ordering_index_total(&self) -> u64 {
333        unsafe { BNGetLinearViewCursorOrderingIndexTotal(self.handle) }
334    }
335
336    pub fn seek_to_ordering_index(&mut self, idx: u64) {
337        unsafe { BNSeekLinearViewCursorToAddress(self.handle, idx) }
338    }
339
340    pub fn previous(&mut self) -> bool {
341        unsafe { BNLinearViewCursorPrevious(self.handle) }
342    }
343
344    // TODO: This clippy lint is probably right? Just a lot of work and it would
345    // TODO: make this API different from the python and C++ implementations.
346    #[allow(clippy::should_implement_trait)]
347    pub fn next(&mut self) -> bool {
348        unsafe { BNLinearViewCursorNext(self.handle) }
349    }
350
351    pub fn lines(&self) -> Array<LinearDisassemblyLine> {
352        let mut count: usize = 0;
353        unsafe {
354            let handles = BNGetLinearViewCursorLines(self.handle, &mut count);
355            Array::new(handles, count, ())
356        }
357    }
358
359    /// A list of the currently applied [`CoreRenderLayer`]'s
360    pub fn render_layers(&self) -> Array<CoreRenderLayer> {
361        let mut count: usize = 0;
362        unsafe {
363            let handles = BNGetLinearViewCursorRenderLayers(self.handle, &mut count);
364            Array::new(handles, count, ())
365        }
366    }
367
368    /// Add a Render Layer to be applied to this [`LinearViewCursor`].
369    ///
370    /// NOTE: Layers will be applied in the order in which they are added.
371    pub fn add_render_layer(&self, layer: &CoreRenderLayer) {
372        unsafe { BNAddLinearViewCursorRenderLayer(self.handle, layer.handle.as_ptr()) };
373    }
374
375    /// Remove a Render Layer from being applied to this [`LinearViewCursor`].
376    pub fn remove_render_layer(&self, layer: &CoreRenderLayer) {
377        unsafe { BNRemoveLinearViewCursorRenderLayer(self.handle, layer.handle.as_ptr()) };
378    }
379}
380
381impl PartialEq for LinearViewCursor {
382    fn eq(&self, other: &Self) -> bool {
383        unsafe { BNCompareLinearViewCursors(self.handle, other.handle) == 0 }
384    }
385}
386
387impl PartialOrd for LinearViewCursor {
388    fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
389        Some(self.cmp(other))
390    }
391}
392
393impl Ord for LinearViewCursor {
394    fn cmp(&self, other: &Self) -> std::cmp::Ordering {
395        match unsafe { BNCompareLinearViewCursors(self.handle, other.handle) } {
396            i if i < 0 => std::cmp::Ordering::Less,
397            i if i > 0 => std::cmp::Ordering::Greater,
398            _ => std::cmp::Ordering::Equal,
399        }
400    }
401}
402
403unsafe impl RefCountable for LinearViewCursor {
404    unsafe fn inc_ref(handle: &Self) -> Ref<Self> {
405        Ref::new(Self {
406            handle: BNNewLinearViewCursorReference(handle.handle),
407        })
408    }
409
410    unsafe fn dec_ref(handle: &Self) {
411        BNFreeLinearViewCursor(handle.handle);
412    }
413}
414
415impl ToOwned for LinearViewCursor {
416    type Owned = Ref<Self>;
417
418    fn to_owned(&self) -> Self::Owned {
419        unsafe { RefCountable::inc_ref(self) }
420    }
421}
422
423unsafe impl Send for LinearViewCursor {}
424unsafe impl Sync for LinearViewCursor {}
425
426#[derive(Clone, PartialEq, Debug, Eq)]
427pub struct LinearDisassemblyLine {
428    pub ty: LinearDisassemblyLineType,
429    pub function: Option<Ref<Function>>,
430    pub basic_block: Option<Ref<BasicBlock<NativeBlock>>>,
431    pub contents: DisassemblyTextLine,
432}
433
434impl LinearDisassemblyLine {
435    pub(crate) unsafe fn from_raw(value: &BNLinearDisassemblyLine) -> Self {
436        let function = if !value.function.is_null() {
437            Some(unsafe { Function::from_raw(value.function).to_owned() })
438        } else {
439            None
440        };
441        let basic_block = if !value.block.is_null() {
442            Some(unsafe { BasicBlock::from_raw(value.block, NativeBlock::new()).to_owned() })
443        } else {
444            None
445        };
446        Self {
447            ty: value.type_,
448            function,
449            basic_block,
450            contents: DisassemblyTextLine::from_raw(&value.contents),
451        }
452    }
453
454    #[allow(unused)]
455    pub(crate) unsafe fn from_owned_raw(value: BNLinearDisassemblyLine) -> Self {
456        let owned = Self::from_raw(&value);
457        Self::free_raw(value);
458        owned
459    }
460
461    pub(crate) fn into_raw(value: Self) -> BNLinearDisassemblyLine {
462        let function_ptr = value
463            .function
464            .map(|f| unsafe { Ref::into_raw(f) }.handle)
465            .unwrap_or(std::ptr::null_mut());
466        let block_ptr = value
467            .basic_block
468            .map(|b| unsafe { Ref::into_raw(b) }.handle)
469            .unwrap_or(std::ptr::null_mut());
470        BNLinearDisassemblyLine {
471            type_: value.ty,
472            function: function_ptr,
473            block: block_ptr,
474            contents: DisassemblyTextLine::into_raw(value.contents),
475        }
476    }
477
478    pub(crate) fn free_raw(value: BNLinearDisassemblyLine) {
479        if !value.function.is_null() {
480            let _ = unsafe { Function::ref_from_raw(value.function) };
481        }
482        if !value.block.is_null() {
483            let _ = unsafe { BasicBlock::ref_from_raw(value.block, NativeBlock::new()) };
484        }
485        DisassemblyTextLine::free_raw(value.contents);
486    }
487}
488
489impl Deref for LinearDisassemblyLine {
490    type Target = DisassemblyTextLine;
491    fn deref(&self) -> &Self::Target {
492        &self.contents
493    }
494}
495
496impl std::fmt::Display for LinearDisassemblyLine {
497    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
498        write!(f, "{}", self.contents)
499    }
500}
501
502impl CoreArrayProvider for LinearDisassemblyLine {
503    type Raw = BNLinearDisassemblyLine;
504    type Context = ();
505    type Wrapped<'a> = LinearDisassemblyLine;
506}
507
508unsafe impl CoreArrayProviderInner for LinearDisassemblyLine {
509    unsafe fn free(raw: *mut Self::Raw, count: usize, _context: &Self::Context) {
510        BNFreeLinearDisassemblyLines(raw, count);
511    }
512
513    unsafe fn wrap_raw<'a>(raw: &'a Self::Raw, _context: &'a Self::Context) -> Self::Wrapped<'a> {
514        Self::from_raw(raw)
515    }
516}