binaryninja/
render_layer.rs

1//! Customize the presentation of Linear and Graph view output.
2
3use crate::basic_block::{BasicBlock, BasicBlockType};
4use crate::disassembly::DisassemblyTextLine;
5use crate::flowgraph::FlowGraph;
6use crate::function::{Function, NativeBlock};
7use crate::linear_view::{LinearDisassemblyLine, LinearDisassemblyLineType, LinearViewObject};
8use crate::rc::{Array, CoreArrayProvider, CoreArrayProviderInner};
9use crate::string::IntoCStr;
10use binaryninjacore_sys::*;
11use std::ffi::c_void;
12use std::ptr::NonNull;
13
14/// The state in which the [`RenderLayer`] will be registered with.
15#[repr(u32)]
16#[derive(Clone, Copy, Debug, PartialEq, Default)]
17pub enum RenderLayerDefaultState {
18    /// Register the [`RenderLayer`] as disabled, the user must then enable it via the UI.
19    ///
20    /// This is the default registration value.
21    #[default]
22    Disabled = 0,
23    /// Register the [`RenderLayer`] as enabled, the user must then disable it via the UI.
24    Enabled = 1,
25    /// Use this if you do not want the render layer to be adjustable via the UI.
26    AlwaysEnabled = 2,
27}
28
29impl From<BNRenderLayerDefaultEnableState> for RenderLayerDefaultState {
30    fn from(value: BNRenderLayerDefaultEnableState) -> Self {
31        match value {
32            BNRenderLayerDefaultEnableState::DisabledByDefaultRenderLayerDefaultEnableState => {
33                Self::Disabled
34            }
35            BNRenderLayerDefaultEnableState::EnabledByDefaultRenderLayerDefaultEnableState => {
36                Self::Enabled
37            }
38            BNRenderLayerDefaultEnableState::AlwaysEnabledRenderLayerDefaultEnableState => {
39                Self::AlwaysEnabled
40            }
41        }
42    }
43}
44
45impl From<RenderLayerDefaultState> for BNRenderLayerDefaultEnableState {
46    fn from(value: RenderLayerDefaultState) -> Self {
47        match value {
48            RenderLayerDefaultState::Disabled => {
49                Self::DisabledByDefaultRenderLayerDefaultEnableState
50            }
51            RenderLayerDefaultState::Enabled => Self::EnabledByDefaultRenderLayerDefaultEnableState,
52            RenderLayerDefaultState::AlwaysEnabled => {
53                Self::AlwaysEnabledRenderLayerDefaultEnableState
54            }
55        }
56    }
57}
58
59/// Register a [`RenderLayer`] with the API.
60pub fn register_render_layer<T: RenderLayer>(
61    name: &str,
62    render_layer: T,
63    default_state: RenderLayerDefaultState,
64) -> (&'static mut T, CoreRenderLayer) {
65    let render_layer = Box::leak(Box::new(render_layer));
66    let mut callback = BNRenderLayerCallbacks {
67        context: render_layer as *mut _ as *mut c_void,
68        applyToFlowGraph: Some(cb_apply_to_flow_graph::<T>),
69        applyToLinearViewObject: Some(cb_apply_to_linear_view_object::<T>),
70        freeLines: Some(cb_free_lines),
71    };
72    let name = name.to_cstr();
73    let result =
74        unsafe { BNRegisterRenderLayer(name.as_ptr(), &mut callback, default_state.into()) };
75    let core = CoreRenderLayer::from_raw(NonNull::new(result).unwrap());
76    (render_layer, core)
77}
78
79pub trait RenderLayer: Sized {
80    /// Apply this Render Layer to a Flow Graph.
81    fn apply_to_flow_graph(&self, graph: &mut FlowGraph) {
82        for node in &graph.nodes() {
83            if let Some(block) = node.basic_block(NativeBlock::new()) {
84                let new_lines = self.apply_to_block(&block, node.lines().to_vec());
85                node.set_lines(new_lines);
86            }
87        }
88    }
89
90    /// Apply this Render Layer to the lines produced by a LinearViewObject for rendering in Linear View.
91    fn apply_to_linear_object(
92        &self,
93        object: &mut LinearViewObject,
94        _prev_object: Option<&mut LinearViewObject>,
95        _next_object: Option<&mut LinearViewObject>,
96        lines: Vec<LinearDisassemblyLine>,
97    ) -> Vec<LinearDisassemblyLine> {
98        let text_to_lines =
99            |function: &Function, block: &BasicBlock<NativeBlock>, text: DisassemblyTextLine| {
100                LinearDisassemblyLine {
101                    ty: LinearDisassemblyLineType::CodeDisassemblyLineType,
102                    function: Some(function.to_owned()),
103                    basic_block: Some(block.to_owned()),
104                    contents: text,
105                }
106            };
107
108        // Hack: HLIL bodies don't have basic blocks.
109        let obj_ident = object.identifier();
110        if !lines.is_empty()
111            && (obj_ident.name.starts_with("HLIL") || obj_ident.name.starts_with("Language"))
112        {
113            // Apply to HLIL body.
114            let function = lines[0]
115                .function
116                .to_owned()
117                .expect("HLIL body has no function");
118            return self.apply_to_hlil_body(&function, lines);
119        }
120
121        // Collect the "line blocks".
122        // Line blocks are contiguous lines with the same backing basic block (or lack thereof).
123        // Line blocks also group by line type.
124        let mut line_blocks: Vec<Vec<LinearDisassemblyLine>> = Vec::new();
125        for line in lines {
126            let Some(last_block) = line_blocks.last_mut() else {
127                // No last block, create the first block.
128                line_blocks.push(vec![line]);
129                continue;
130            };
131
132            let Some(last_line) = last_block.last() else {
133                // No last line, create the first line.
134                last_block.push(line);
135                continue;
136            };
137
138            // TODO: If we want to allow a block with multiple line types we need to specifically check
139            // TODO: If the last line type was Code, if it is and the last line is not we make a new block.
140            if last_line.basic_block == line.basic_block && last_line.ty == line.ty {
141                // Same basic block and line type, this is a part of the same line block.
142                last_block.push(line);
143            } else {
144                // Not the same line block, create a new block.
145                line_blocks.push(vec![line]);
146            }
147        }
148
149        line_blocks
150            .into_iter()
151            .filter_map(|line_block| {
152                let probe_line = line_block.first()?;
153                Some((probe_line.ty, probe_line.basic_block.to_owned(), line_block))
154            })
155            .flat_map(|(line_ty, basic_block, lines)| {
156                match (basic_block, line_ty) {
157                    (Some(block), LinearDisassemblyLineType::CodeDisassemblyLineType) => {
158                        // Dealing with code lines.
159                        let function = block.function();
160                        let text_lines = lines.into_iter().map(|line| line.contents).collect();
161                        let new_text_lines = self.apply_to_block(&block, text_lines);
162                        new_text_lines
163                            .into_iter()
164                            .map(|line| text_to_lines(&function, &block, line))
165                            .collect()
166                    }
167                    _ => {
168                        // Dealing with misc lines.
169                        self.apply_to_misc_lines(
170                            object,
171                            _prev_object.as_deref(),
172                            _next_object.as_deref(),
173                            lines,
174                        )
175                    }
176                }
177            })
178            .collect()
179    }
180
181    /// Apply this Render Layer to a single Basic Block of Disassembly lines.
182    ///
183    /// Modify the lines to change the presentation of the block.
184    fn apply_to_disassembly_block(
185        &self,
186        _block: &BasicBlock<NativeBlock>,
187        lines: Vec<DisassemblyTextLine>,
188    ) -> Vec<DisassemblyTextLine> {
189        lines
190    }
191
192    /// Apply this Render Layer to a single Basic Block of Low Level IL lines.
193    ///
194    /// Modify the lines to change the presentation of the block.
195    fn apply_to_llil_block(
196        &self,
197        _block: &BasicBlock<NativeBlock>,
198        lines: Vec<DisassemblyTextLine>,
199    ) -> Vec<DisassemblyTextLine> {
200        lines
201    }
202
203    /// Apply this Render Layer to a single Basic Block of Medium Level IL lines.
204    ///
205    /// Modify the lines to change the presentation of the block.
206    fn apply_to_mlil_block(
207        &self,
208        _block: &BasicBlock<NativeBlock>,
209        lines: Vec<DisassemblyTextLine>,
210    ) -> Vec<DisassemblyTextLine> {
211        lines
212    }
213
214    /// Apply this Render Layer to a single Basic Block of High Level IL lines.
215    ///
216    /// Modify the lines to change the presentation of the block.
217    ///
218    /// This function will NOT apply to High Level IL bodies as displayed in Linear View!
219    /// Those are handled by [`RenderLayer::apply_to_hlil_body`] instead as they do not
220    /// have a [`BasicBlock`] associated with them.
221    fn apply_to_hlil_block(
222        &self,
223        _block: &BasicBlock<NativeBlock>,
224        lines: Vec<DisassemblyTextLine>,
225    ) -> Vec<DisassemblyTextLine> {
226        lines
227    }
228
229    /// Apply this Render Layer to the entire body of a High Level IL function.
230    ///
231    /// Modify the lines to change the presentation of the block.
232    ///
233    /// This function only applies to Linear View, and not to Graph View! If you want to
234    /// handle Graph View too, you will need to use [`RenderLayer::apply_to_hlil_block`] and handle
235    /// the lines one block at a time.
236    fn apply_to_hlil_body(
237        &self,
238        _function: &Function,
239        lines: Vec<LinearDisassemblyLine>,
240    ) -> Vec<LinearDisassemblyLine> {
241        lines
242    }
243
244    // TODO: We might want to just go ahead and pass the line type.
245    /// Apply to lines generated by Linear View that are not part of a function.
246    ///
247    /// Modify the lines to change the presentation of the block.
248    fn apply_to_misc_lines(
249        &self,
250        _object: &mut LinearViewObject,
251        _prev_object: Option<&LinearViewObject>,
252        _next_object: Option<&LinearViewObject>,
253        lines: Vec<LinearDisassemblyLine>,
254    ) -> Vec<LinearDisassemblyLine> {
255        lines
256    }
257
258    /// Apply this Render Layer to all IL blocks and disassembly blocks.
259    ///
260    /// If not implemented this will handle calling the view specific apply functions:
261    ///
262    /// - [`RenderLayer::apply_to_disassembly_block`]
263    /// - [`RenderLayer::apply_to_llil_block`]
264    /// - [`RenderLayer::apply_to_mlil_block`]
265    /// - [`RenderLayer::apply_to_hlil_block`]
266    ///
267    /// Modify the lines to change the presentation of the block.
268    fn apply_to_block(
269        &self,
270        block: &BasicBlock<NativeBlock>,
271        lines: Vec<DisassemblyTextLine>,
272    ) -> Vec<DisassemblyTextLine> {
273        match block.block_type() {
274            BasicBlockType::Native => self.apply_to_disassembly_block(block, lines),
275            BasicBlockType::LowLevelIL => self.apply_to_llil_block(block, lines),
276            BasicBlockType::MediumLevelIL => self.apply_to_mlil_block(block, lines),
277            BasicBlockType::HighLevelIL => self.apply_to_hlil_block(block, lines),
278        }
279    }
280}
281
282#[repr(transparent)]
283pub struct CoreRenderLayer {
284    pub(crate) handle: NonNull<BNRenderLayer>,
285}
286
287impl CoreRenderLayer {
288    pub fn from_raw(handle: NonNull<BNRenderLayer>) -> Self {
289        Self { handle }
290    }
291
292    pub fn all() -> Array<CoreRenderLayer> {
293        let mut count = 0;
294        let result = unsafe { BNGetRenderLayerList(&mut count) };
295        unsafe { Array::new(result, count, ()) }
296    }
297
298    pub fn from_name(name: &str) -> Option<CoreRenderLayer> {
299        let name_raw = name.to_cstr();
300        let result = unsafe { BNGetRenderLayerByName(name_raw.as_ptr()) };
301        NonNull::new(result).map(Self::from_raw)
302    }
303
304    pub fn default_state(&self) -> RenderLayerDefaultState {
305        let raw = unsafe { BNGetRenderLayerDefaultEnableState(self.handle.as_ptr()) };
306        RenderLayerDefaultState::from(raw)
307    }
308
309    pub fn apply_to_flow_graph(&self, graph: &FlowGraph) {
310        unsafe { BNApplyRenderLayerToFlowGraph(self.handle.as_ptr(), graph.handle) }
311    }
312
313    pub fn apply_to_linear_view_object(
314        &self,
315        object: &LinearViewObject,
316        prev_object: Option<&LinearViewObject>,
317        next_object: Option<&LinearViewObject>,
318        lines: Vec<LinearDisassemblyLine>,
319    ) -> Vec<LinearDisassemblyLine> {
320        let mut lines_raw: Vec<_> = lines
321            .into_iter()
322            // NOTE: Freed after the core call
323            .map(LinearDisassemblyLine::into_raw)
324            .collect();
325
326        let prev_object_ptr = prev_object
327            .map(|o| o.handle)
328            .unwrap_or(std::ptr::null_mut());
329        let next_object_ptr = next_object
330            .map(|o| o.handle)
331            .unwrap_or(std::ptr::null_mut());
332
333        let mut new_lines = std::ptr::null_mut();
334        let mut new_line_count = 0;
335
336        unsafe {
337            BNApplyRenderLayerToLinearViewObject(
338                self.handle.as_ptr(),
339                object.handle,
340                prev_object_ptr,
341                next_object_ptr,
342                lines_raw.as_mut_ptr(),
343                lines_raw.len(),
344                &mut new_lines,
345                &mut new_line_count,
346            )
347        };
348
349        for line in lines_raw {
350            LinearDisassemblyLine::free_raw(line);
351        }
352
353        let raw: Array<LinearDisassemblyLine> =
354            unsafe { Array::new(new_lines, new_line_count, ()) };
355        raw.to_vec()
356    }
357}
358
359impl CoreArrayProvider for CoreRenderLayer {
360    type Raw = *mut BNRenderLayer;
361    type Context = ();
362    type Wrapped<'a> = Self;
363}
364
365unsafe impl CoreArrayProviderInner for CoreRenderLayer {
366    unsafe fn free(raw: *mut Self::Raw, _count: usize, _context: &Self::Context) {
367        BNFreeRenderLayerList(raw)
368    }
369
370    unsafe fn wrap_raw<'a>(raw: &'a Self::Raw, _context: &'a Self::Context) -> Self::Wrapped<'a> {
371        // TODO: Because handle is a NonNull we should prob make Self::Raw that as well...
372        let handle = NonNull::new(*raw).unwrap();
373        CoreRenderLayer::from_raw(handle)
374    }
375}
376
377unsafe extern "C" fn cb_apply_to_flow_graph<T: RenderLayer>(
378    ctxt: *mut c_void,
379    graph: *mut BNFlowGraph,
380) {
381    let ctxt: &mut T = &mut *(ctxt as *mut T);
382    // SAFETY: We do not own the flowgraph, do not take it as Ref.
383    let mut flow_graph = FlowGraph::from_raw(graph);
384    ctxt.apply_to_flow_graph(&mut flow_graph);
385}
386
387unsafe extern "C" fn cb_apply_to_linear_view_object<T: RenderLayer>(
388    ctxt: *mut c_void,
389    object: *mut BNLinearViewObject,
390    prev: *mut BNLinearViewObject,
391    next: *mut BNLinearViewObject,
392    in_lines: *mut BNLinearDisassemblyLine,
393    in_line_count: usize,
394    out_lines: *mut *mut BNLinearDisassemblyLine,
395    out_line_count: *mut usize,
396) {
397    let ctxt: &mut T = &mut *(ctxt as *mut T);
398    // SAFETY: We do not own the flowgraph, do not take it as Ref.
399    let mut object = LinearViewObject::from_raw(object);
400    let mut prev_object = if !prev.is_null() {
401        Some(LinearViewObject::from_raw(prev))
402    } else {
403        None
404    };
405    let mut next_object = if !next.is_null() {
406        Some(LinearViewObject::from_raw(next))
407    } else {
408        None
409    };
410
411    let raw_lines = std::slice::from_raw_parts(in_lines, in_line_count);
412    // NOTE: The caller is owned of the inLines.
413    let lines: Vec<_> = raw_lines
414        .iter()
415        .map(|line| LinearDisassemblyLine::from_raw(line))
416        .collect();
417
418    let new_lines = ctxt.apply_to_linear_object(
419        &mut object,
420        prev_object.as_mut(),
421        next_object.as_mut(),
422        lines,
423    );
424
425    unsafe {
426        *out_line_count = new_lines.len();
427        let boxed_new_lines: Box<[_]> = new_lines
428            .into_iter()
429            // NOTE: Freed by cb_free_lines
430            .map(LinearDisassemblyLine::into_raw)
431            .collect();
432        // NOTE: Dropped by cb_free_lines
433        *out_lines = Box::leak(boxed_new_lines).as_mut_ptr();
434    }
435}
436
437unsafe extern "C" fn cb_free_lines(
438    _ctxt: *mut c_void,
439    lines: *mut BNLinearDisassemblyLine,
440    line_count: usize,
441) {
442    let lines_ptr = std::ptr::slice_from_raw_parts_mut(lines, line_count);
443    let boxed_lines = Box::from_raw(lines_ptr);
444    for line in boxed_lines {
445        LinearDisassemblyLine::free_raw(line);
446    }
447}