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