binaryninja/
flowgraph.rs

1// Copyright 2021-2026 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//! Interfaces for creating and displaying pretty CFGs in Binary Ninja.
16
17use crate::high_level_il::HighLevelILFunction;
18use crate::low_level_il::LowLevelILRegularFunction;
19use crate::medium_level_il::MediumLevelILFunction;
20use crate::rc::*;
21use crate::render_layer::CoreRenderLayer;
22use binaryninjacore_sys::*;
23use std::ffi::c_void;
24use std::ptr::NonNull;
25use std::time::Duration;
26
27pub mod edge;
28pub mod layout;
29pub mod node;
30
31use crate::binary_view::BinaryView;
32use crate::flowgraph::layout::FlowGraphLayoutRequest;
33use crate::function::Function;
34use crate::string::IntoCStr;
35pub use edge::EdgeStyle;
36pub use edge::FlowGraphEdge;
37pub use node::FlowGraphNode;
38
39pub type EdgePenStyle = BNEdgePenStyle;
40pub type ThemeColor = BNThemeColor;
41pub type FlowGraphOption = BNFlowGraphOption;
42
43#[repr(transparent)]
44#[derive(PartialEq, Eq, Hash)]
45pub struct FlowGraph {
46    pub(crate) handle: *mut BNFlowGraph,
47}
48
49impl FlowGraph {
50    pub(crate) unsafe fn from_raw(raw: *mut BNFlowGraph) -> Self {
51        Self { handle: raw }
52    }
53
54    pub(crate) unsafe fn ref_from_raw(raw: *mut BNFlowGraph) -> Ref<Self> {
55        Ref::new(Self { handle: raw })
56    }
57
58    /// Create an empty flowgraph.
59    ///
60    /// If you instead want to create a flowgraph of a given [`Function`], use [`Function::create_graph`].
61    pub fn new() -> Ref<Self> {
62        unsafe { FlowGraph::ref_from_raw(BNCreateFlowGraph()) }
63    }
64
65    /// Requests the flowgraph to be laid out, positioning nodes and routing edges.
66    ///
67    /// This function returns immediately, with `on_complete` being called when the layout has been
68    /// completed, to wait for the request to be completed use [`FlowGraph::request_layout_and_wait`].
69    pub fn request_layout<C: FnOnce() + Send + 'static>(
70        &self,
71        on_complete: C,
72    ) -> Ref<FlowGraphLayoutRequest> {
73        let context = Box::into_raw(Box::new(on_complete));
74        let request_raw_ptr = unsafe {
75            BNStartFlowGraphLayout(self.handle, context as *mut _, Some(cb_on_complete::<C>))
76        };
77        let request_ptr =
78            NonNull::new(request_raw_ptr).expect("BNStartFlowGraphLayout returned null");
79        unsafe { FlowGraphLayoutRequest::ref_from_raw(request_ptr) }
80    }
81
82    /// Blocks until the flow graph layout is complete or until the `timeout` has elapsed, returning
83    /// `true` if the layout completed within the timeout, `false` otherwise.
84    ///
85    /// Use [`FlowGraph::request_layout`] instead if you want to provide a callback when the layout
86    /// has been completed and return immediately.
87    pub fn request_layout_and_wait(&self, timeout: Duration) -> bool {
88        let (tx, rx) = std::sync::mpsc::channel();
89        // IMPORTANT: named `_request` to keep from dropping before function return.
90        let _request = self.request_layout(move || {
91            let _ = tx.send(());
92        });
93        rx.recv_timeout(timeout).is_ok()
94    }
95
96    pub fn has_updates(&self) -> bool {
97        let query_mode = unsafe { BNFlowGraphUpdateQueryMode(self.handle) };
98        match query_mode {
99            true => unsafe { BNFlowGraphHasUpdates(self.handle) },
100            false => false,
101        }
102    }
103
104    pub fn update(&self) -> Option<Ref<Self>> {
105        let new_graph = unsafe { BNUpdateFlowGraph(self.handle) };
106        if new_graph.is_null() {
107            return None;
108        }
109        Some(unsafe { FlowGraph::ref_from_raw(new_graph) })
110    }
111
112    /// Sends the [`FlowGraph`] to the interaction handlers to display.
113    ///
114    /// - On headless this is a no-op unless you register a [`crate::interaction::handler::InteractionHandler`].
115    /// - On UI this will create a new tab to display the graph.
116    pub fn show(&self, title: &str) {
117        let raw_title = title.to_cstr();
118        match self.view() {
119            None => unsafe {
120                BNShowGraphReport(std::ptr::null_mut(), raw_title.as_ptr(), self.handle);
121            },
122            Some(view) => unsafe {
123                BNShowGraphReport(view.handle, raw_title.as_ptr(), self.handle);
124            },
125        }
126    }
127
128    /// Whether the flow graph layout is complete.
129    pub fn is_layout_complete(&self) -> bool {
130        unsafe { BNIsFlowGraphLayoutComplete(self.handle) }
131    }
132
133    // TODO: A [`FlowGraphLayoutRequest::abort`] does not actually abort the layout, it sets a flag
134    // TODO: in the associated [`FlowGraph`], but we have no way to observe that flag. See the
135    // TODO: issue filed here: https://github.com/Vector35/binaryninja-api/issues/7826.
136    // pub fn is_aborted(&self) -> bool {}
137
138    pub fn nodes(&self) -> Array<FlowGraphNode> {
139        let mut count: usize = 0;
140        let nodes_ptr = unsafe { BNGetFlowGraphNodes(self.handle, &mut count) };
141        unsafe { Array::new(nodes_ptr, count, ()) }
142    }
143
144    /// Returns the nodes that are partially or fully visible within the given region.
145    ///
146    /// The node visibility region is set with [`FlowGraphNode::set_visibility_region`] when laying
147    /// out the graph.
148    pub fn visible_nodes(
149        &self,
150        left: i32,
151        top: i32,
152        right: i32,
153        bottom: i32,
154    ) -> Array<FlowGraphNode> {
155        let mut count: usize = 0;
156        let nodes_ptr = unsafe {
157            BNGetFlowGraphNodesInRegion(self.handle, left, top, right, bottom, &mut count)
158        };
159        unsafe { Array::new(nodes_ptr, count, ()) }
160    }
161
162    pub fn function(&self) -> Option<Ref<Function>> {
163        unsafe {
164            let func_ptr = BNGetFunctionForFlowGraph(self.handle);
165            match func_ptr.is_null() {
166                false => Some(Function::ref_from_raw(func_ptr)),
167                true => None,
168            }
169        }
170    }
171
172    pub fn set_function(&self, func: Option<&Function>) {
173        let func_ptr = func.map(|f| f.handle).unwrap_or(std::ptr::null_mut());
174        unsafe { BNSetFunctionForFlowGraph(self.handle, func_ptr) }
175    }
176
177    pub fn view(&self) -> Option<Ref<BinaryView>> {
178        unsafe {
179            let view_ptr = BNGetViewForFlowGraph(self.handle);
180            match view_ptr.is_null() {
181                false => Some(BinaryView::ref_from_raw(view_ptr)),
182                true => None,
183            }
184        }
185    }
186
187    pub fn set_view(&self, view: Option<&BinaryView>) {
188        let view_ptr = view.map(|v| v.handle).unwrap_or(std::ptr::null_mut());
189        unsafe { BNSetViewForFlowGraph(self.handle, view_ptr) }
190    }
191
192    pub fn lifted_il(&self) -> Option<Ref<LowLevelILRegularFunction>> {
193        self.function()?.lifted_il().ok()
194    }
195
196    pub fn low_level_il(&self) -> Option<Ref<LowLevelILRegularFunction>> {
197        unsafe {
198            let llil_ptr = BNGetFlowGraphLowLevelILFunction(self.handle);
199            match llil_ptr.is_null() {
200                false => Some(LowLevelILRegularFunction::ref_from_raw(llil_ptr)),
201                true => None,
202            }
203        }
204    }
205
206    pub fn medium_level_il(&self) -> Option<Ref<MediumLevelILFunction>> {
207        unsafe {
208            let mlil_ptr = BNGetFlowGraphMediumLevelILFunction(self.handle);
209            match mlil_ptr.is_null() {
210                false => Some(MediumLevelILFunction::ref_from_raw(mlil_ptr)),
211                true => None,
212            }
213        }
214    }
215
216    pub fn high_level_il(&self, full_ast: bool) -> Option<Ref<HighLevelILFunction>> {
217        unsafe {
218            let hlil_ptr = BNGetFlowGraphHighLevelILFunction(self.handle);
219            match hlil_ptr.is_null() {
220                false => Some(HighLevelILFunction::ref_from_raw(hlil_ptr, full_ast)),
221                true => None,
222            }
223        }
224    }
225
226    pub fn get_node(&self, i: usize) -> Option<Ref<FlowGraphNode>> {
227        let node_ptr = unsafe { BNGetFlowGraphNode(self.handle, i) };
228        if node_ptr.is_null() {
229            None
230        } else {
231            Some(unsafe { FlowGraphNode::ref_from_raw(node_ptr) })
232        }
233    }
234
235    pub fn get_node_count(&self) -> usize {
236        unsafe { BNGetFlowGraphNodeCount(self.handle) }
237    }
238
239    pub fn has_nodes(&self) -> bool {
240        unsafe { BNFlowGraphHasNodes(self.handle) }
241    }
242
243    /// Returns the graph size in width, height form.
244    pub fn size(&self) -> (i32, i32) {
245        let width = unsafe { BNGetFlowGraphWidth(self.handle) };
246        let height = unsafe { BNGetFlowGraphHeight(self.handle) };
247        (width, height)
248    }
249
250    /// Set the size of the graph.
251    pub fn set_size(&self, width: i32, height: i32) {
252        unsafe { BNFlowGraphSetWidth(self.handle, width) };
253        unsafe { BNFlowGraphSetHeight(self.handle, height) };
254    }
255
256    /// Returns the graph margins between nodes.
257    pub fn node_margins(&self) -> (i32, i32) {
258        let horizontal = unsafe { BNGetHorizontalFlowGraphNodeMargin(self.handle) };
259        let vertical = unsafe { BNGetVerticalFlowGraphNodeMargin(self.handle) };
260        (horizontal, vertical)
261    }
262
263    /// Sets the graph margins between nodes.
264    pub fn set_node_margins(&self, horizontal: i32, vertical: i32) {
265        unsafe { BNSetFlowGraphNodeMargins(self.handle, horizontal, vertical) };
266    }
267
268    pub fn is_node_valid(&self, node: &FlowGraphNode) -> bool {
269        unsafe { BNIsNodeValidForFlowGraph(self.handle, node.handle) }
270    }
271
272    /// Add a [`FlowGraphNode`] to the graph, returning its index.
273    ///
274    /// This only works before the flow graph layout is complete, inside [`layout::FlowGraphLayout::layout`].
275    pub fn append(&self, node: &FlowGraphNode) -> usize {
276        unsafe { BNAddFlowGraphNode(self.handle, node.handle) }
277    }
278
279    /// Replaces the node at the given index with the provided [`FlowGraphNode`].
280    ///
281    /// This only works before the flow graph layout is complete, inside [`layout::FlowGraphLayout::layout`].
282    pub fn replace(&self, index: usize, node: &FlowGraphNode) {
283        unsafe { BNReplaceFlowGraphNode(self.handle, index, node.handle) }
284    }
285
286    /// Removes all nodes from the graph.
287    ///
288    /// This only works before the flow graph layout is complete, inside [`layout::FlowGraphLayout::layout`].
289    pub fn clear(&self) {
290        unsafe { BNClearFlowGraphNodes(self.handle) }
291    }
292
293    pub fn set_option(&self, option: FlowGraphOption, value: bool) {
294        unsafe { BNSetFlowGraphOption(self.handle, option, value) }
295    }
296
297    pub fn is_option_set(&self, option: FlowGraphOption) -> bool {
298        unsafe { BNIsFlowGraphOptionSet(self.handle, option) }
299    }
300
301    /// A list of the currently applied [`CoreRenderLayer`]'s
302    pub fn render_layers(&self) -> Array<CoreRenderLayer> {
303        let mut count: usize = 0;
304        unsafe {
305            let handles = BNGetFlowGraphRenderLayers(self.handle, &mut count);
306            Array::new(handles, count, ())
307        }
308    }
309
310    /// Add a Render Layer to be applied to this [`FlowGraph`].
311    ///
312    /// NOTE: Layers will be applied in the order in which they are added.
313    pub fn add_render_layer(&self, layer: &CoreRenderLayer) {
314        unsafe { BNAddFlowGraphRenderLayer(self.handle, layer.handle.as_ptr()) };
315    }
316
317    /// Remove a Render Layer from being applied to this [`FlowGraph`].
318    pub fn remove_render_layer(&self, layer: &CoreRenderLayer) {
319        unsafe { BNRemoveFlowGraphRenderLayer(self.handle, layer.handle.as_ptr()) };
320    }
321}
322
323unsafe impl RefCountable for FlowGraph {
324    unsafe fn inc_ref(handle: &Self) -> Ref<Self> {
325        Ref::new(Self {
326            handle: BNNewFlowGraphReference(handle.handle),
327        })
328    }
329
330    unsafe fn dec_ref(handle: &Self) {
331        BNFreeFlowGraph(handle.handle);
332    }
333}
334
335impl ToOwned for FlowGraph {
336    type Owned = Ref<Self>;
337
338    fn to_owned(&self) -> Self::Owned {
339        unsafe { RefCountable::inc_ref(self) }
340    }
341}
342
343unsafe extern "C" fn cb_on_complete<C: FnOnce()>(ctxt: *mut c_void) {
344    // Take ownership of the ctxt so that we do not leak, we assume this callback to always
345    // be called so that the ctxt may be freed.
346    let ctxt: Box<C> = unsafe { Box::from_raw(ctxt as *mut C) };
347    ctxt();
348}