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