summaryrefslogtreecommitdiff
path: root/rust/src
diff options
context:
space:
mode:
authorGlenn Smith <glenn@vector35.com>2024-12-27 16:14:46 -0500
committerGlenn Smith <glenn@vector35.com>2025-01-30 17:20:05 -0500
commit8862696926173104957729683832591438161557 (patch)
tree78ba6d7dc8144430136086c8dc84726171eec8ab /rust/src
parent5a5426d030b6be26d4564ba1eba2d8a275533256 (diff)
Render Layers
Diffstat (limited to 'rust/src')
-rw-r--r--rust/src/basic_block.rs43
-rw-r--r--rust/src/binary_view.rs4
-rw-r--r--rust/src/collaboration/sync.rs6
-rw-r--r--rust/src/disassembly.rs7
-rw-r--r--rust/src/flowgraph.rs134
-rw-r--r--rust/src/function.rs6
-rw-r--r--rust/src/lib.rs1
-rw-r--r--rust/src/linear_view.rs164
-rw-r--r--rust/src/low_level_il.rs2
-rw-r--r--rust/src/medium_level_il/function.rs4
-rw-r--r--rust/src/render_layer.rs410
-rw-r--r--rust/src/workflow.rs4
12 files changed, 729 insertions, 56 deletions
diff --git a/rust/src/basic_block.rs b/rust/src/basic_block.rs
index b880d28f..8846a90b 100644
--- a/rust/src/basic_block.rs
+++ b/rust/src/basic_block.rs
@@ -19,6 +19,7 @@ use crate::BranchType;
use binaryninjacore_sys::*;
use std::fmt;
use std::fmt::Debug;
+use std::hash::{Hash, Hasher};
enum EdgeDirection {
Incoming,
@@ -97,7 +98,14 @@ pub trait BlockContext: Clone + Sync + Send + Sized {
fn iter(&self, block: &BasicBlock<Self>) -> Self::Iter;
}
-#[derive(PartialEq, Eq, Hash)]
+#[derive(Debug, PartialEq, Eq, Hash, Clone, Copy, PartialOrd, Ord)]
+pub enum BasicBlockType {
+ Native,
+ LowLevelIL,
+ MediumLevelIL,
+ HighLevelIL,
+}
+
pub struct BasicBlock<C: BlockContext> {
pub(crate) handle: *mut BNBasicBlock,
context: C,
@@ -127,6 +135,19 @@ impl<C: BlockContext> BasicBlock<C> {
}
}
+ pub fn block_type(&self) -> BasicBlockType {
+ if unsafe { !BNIsILBasicBlock(self.handle) } {
+ BasicBlockType::Native
+ } else if unsafe { BNIsLowLevelILBasicBlock(self.handle) } {
+ BasicBlockType::LowLevelIL
+ } else if unsafe { BNIsMediumLevelILBasicBlock(self.handle) } {
+ BasicBlockType::MediumLevelIL
+ } else {
+ // We checked all other IL levels, so this is safe.
+ BasicBlockType::HighLevelIL
+ }
+ }
+
pub fn iter(&self) -> C::Iter {
self.context.iter(self)
}
@@ -194,7 +215,7 @@ impl<C: BlockContext> BasicBlock<C> {
if block.is_null() {
return None;
}
- Some(Ref::new(BasicBlock::from_raw(block, self.context.clone())))
+ Some(BasicBlock::ref_from_raw(block, self.context.clone()))
}
}
@@ -237,6 +258,24 @@ impl<C: BlockContext> BasicBlock<C> {
// TODO iterated dominance frontier
}
+impl<C: BlockContext> Hash for BasicBlock<C> {
+ fn hash<H: Hasher>(&self, state: &mut H) {
+ self.function().hash(state);
+ self.block_type().hash(state);
+ state.write_usize(self.index());
+ }
+}
+
+impl<C: BlockContext> PartialEq for BasicBlock<C> {
+ fn eq(&self, other: &Self) -> bool {
+ self.function() == other.function()
+ && self.index() == other.index()
+ && self.block_type() == other.block_type()
+ }
+}
+
+impl<C: BlockContext> Eq for BasicBlock<C> {}
+
impl<C: BlockContext> IntoIterator for &BasicBlock<C> {
type Item = C::Instruction;
type IntoIter = C::Iter;
diff --git a/rust/src/binary_view.rs b/rust/src/binary_view.rs
index 43e913d5..af4709e7 100644
--- a/rust/src/binary_view.rs
+++ b/rust/src/binary_view.rs
@@ -1209,8 +1209,8 @@ pub trait BinaryViewExt: BinaryViewBase {
/// Retrieves a list of the next disassembly lines.
///
- /// `get_next_linear_disassembly_lines` retrieves an [Array] over [LinearDisassemblyLine] objects for the
- /// next disassembly lines, and updates the [LinearViewCursor] passed in. This function can be called
+ /// Retrieves an [`Array`] over [`LinearDisassemblyLine`] objects for the
+ /// next disassembly lines, and updates the [`LinearViewCursor`] passed in. This function can be called
/// repeatedly to get more lines of linear disassembly.
///
/// # Arguments
diff --git a/rust/src/collaboration/sync.rs b/rust/src/collaboration/sync.rs
index 14e18856..1c11b8f0 100644
--- a/rust/src/collaboration/sync.rs
+++ b/rust/src/collaboration/sync.rs
@@ -701,7 +701,7 @@ pub fn is_type_archive_snapshot_ignored<S: BnStrCompatible>(
pub fn download_type_archive<S: BnStrCompatible>(
file: &RemoteFile,
location: S,
-) -> Result<Option<TypeArchive>, ()> {
+) -> Result<Option<Ref<TypeArchive>>, ()> {
download_type_archive_with_progress(file, location, NoProgressCallback)
}
@@ -711,7 +711,7 @@ pub fn download_type_archive_with_progress<S: BnStrCompatible, F: ProgressCallba
file: &RemoteFile,
location: S,
mut progress: F,
-) -> Result<Option<TypeArchive>, ()> {
+) -> Result<Option<Ref<TypeArchive>>, ()> {
let mut value = std::ptr::null_mut();
let db_path = location.into_bytes_with_nul();
let success = unsafe {
@@ -724,7 +724,7 @@ pub fn download_type_archive_with_progress<S: BnStrCompatible, F: ProgressCallba
)
};
success
- .then(|| NonNull::new(value).map(|handle| unsafe { TypeArchive::from_raw(handle) }))
+ .then(|| NonNull::new(value).map(|handle| unsafe { TypeArchive::ref_from_raw(handle) }))
.ok_or(())
}
diff --git a/rust/src/disassembly.rs b/rust/src/disassembly.rs
index 99938e5f..5f9ed559 100644
--- a/rust/src/disassembly.rs
+++ b/rust/src/disassembly.rs
@@ -30,9 +30,10 @@ pub type DisassemblyOption = BNDisassemblyOption;
pub type InstructionTextTokenType = BNInstructionTextTokenType;
pub type StringType = BNStringType;
-#[derive(Clone, PartialEq, Debug, Default)]
+#[derive(Clone, PartialEq, Debug, Default, Eq)]
pub struct DisassemblyTextLine {
pub address: u64,
+ // TODO: This is not always available.
pub instruction_index: usize,
pub tokens: Vec<InstructionTextToken>,
pub highlight: HighlightColor,
@@ -234,7 +235,7 @@ impl DisassemblyTextLineTypeInfo {
}
}
-#[derive(Debug, Clone, PartialEq)]
+#[derive(Debug, Clone, PartialEq, Eq)]
pub struct InstructionTextToken {
pub address: u64,
pub text: String,
@@ -882,6 +883,8 @@ impl From<InstructionTextTokenKind> for BNInstructionTextTokenType {
}
}
+impl Eq for InstructionTextTokenKind {}
+
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
pub enum InstructionTextTokenContext {
Normal,
diff --git a/rust/src/flowgraph.rs b/rust/src/flowgraph.rs
index ead984f9..f5d4ed80 100644
--- a/rust/src/flowgraph.rs
+++ b/rust/src/flowgraph.rs
@@ -14,12 +14,15 @@
//! Interfaces for creating and displaying pretty CFGs in Binary Ninja.
-use binaryninjacore_sys::*;
-
use crate::disassembly::DisassemblyTextLine;
+use binaryninjacore_sys::*;
+use std::slice;
use crate::rc::*;
+use crate::basic_block::{BasicBlock, BlockContext};
+use crate::function::HighlightColor;
+use crate::render_layer::CoreRenderLayer;
use std::marker::PhantomData;
pub type BranchType = BNBranchType;
@@ -37,14 +40,61 @@ impl FlowGraph {
Self { handle: raw }
}
+ pub(crate) unsafe fn ref_from_raw(raw: *mut BNFlowGraph) -> Ref<Self> {
+ Ref::new(Self { handle: raw })
+ }
+
pub fn new() -> Ref<Self> {
- unsafe { Ref::new(FlowGraph::from_raw(BNCreateFlowGraph())) }
+ unsafe { FlowGraph::ref_from_raw(BNCreateFlowGraph()) }
+ }
+
+ pub fn nodes<'a>(&self) -> Vec<Ref<FlowGraphNode<'a>>> {
+ let mut count: usize = 0;
+ let nodes_ptr = unsafe { BNGetFlowGraphNodes(self.handle, &mut count as *mut usize) };
+
+ let nodes = unsafe { slice::from_raw_parts_mut(nodes_ptr, count) };
+
+ let mut result = vec![];
+ result.reserve(count);
+
+ for i in 0..count {
+ result.push(unsafe { RefCountable::inc_ref(&FlowGraphNode::from_raw(nodes[i])) });
+ }
+
+ unsafe { BNFreeFlowGraphNodeList(nodes_ptr, count) };
+
+ result
+ }
+
+ pub fn get_node<'a>(&self, i: usize) -> Option<Ref<FlowGraphNode<'a>>> {
+ let node_ptr = unsafe { BNGetFlowGraphNode(self.handle, i) };
+ if node_ptr.is_null() {
+ None
+ } else {
+ Some(unsafe { Ref::new(FlowGraphNode::from_raw(node_ptr)) })
+ }
+ }
+
+ pub fn get_node_count(&self) -> usize {
+ unsafe { BNGetFlowGraphNodeCount(self.handle) }
+ }
+
+ pub fn has_nodes(&self) -> bool {
+ unsafe { BNFlowGraphHasNodes(self.handle) }
}
pub fn append(&self, node: &FlowGraphNode) -> usize {
unsafe { BNAddFlowGraphNode(self.handle, node.handle) }
}
+ pub fn replace(&self, index: usize, node: &FlowGraphNode) {
+ unsafe { BNReplaceFlowGraphNode(self.handle, index, node.handle) }
+ }
+
+ pub fn clear(&self) {
+ unsafe { BNClearFlowGraphNodes(self.handle) }
+ }
+
pub fn set_option(&self, option: FlowGraphOption, value: bool) {
unsafe { BNSetFlowGraphOption(self.handle, option, value) }
}
@@ -52,6 +102,27 @@ impl FlowGraph {
pub fn is_option_set(&self, option: FlowGraphOption) -> bool {
unsafe { BNIsFlowGraphOptionSet(self.handle, option) }
}
+
+ /// A list of the currently applied [`CoreRenderLayer`]'s
+ pub fn render_layers(&self) -> Array<CoreRenderLayer> {
+ let mut count: usize = 0;
+ unsafe {
+ let handles = BNGetFlowGraphRenderLayers(self.handle, &mut count);
+ Array::new(handles, count, ())
+ }
+ }
+
+ /// Add a Render Layer to be applied to this [`FlowGraph`].
+ ///
+ /// NOTE: Layers will be applied in the order in which they are added.
+ pub fn add_render_layer(&self, layer: &CoreRenderLayer) {
+ unsafe { BNAddFlowGraphRenderLayer(self.handle, layer.handle.as_ptr()) };
+ }
+
+ /// Remove a Render Layer from being applied to this [`FlowGraph`].
+ pub fn remove_render_layer(&self, layer: &CoreRenderLayer) {
+ unsafe { BNRemoveFlowGraphRenderLayer(self.handle, layer.handle.as_ptr()) };
+ }
}
unsafe impl RefCountable for FlowGraph {
@@ -88,8 +159,37 @@ impl<'a> FlowGraphNode<'a> {
}
}
- pub fn new(graph: &FlowGraph) -> Self {
- unsafe { FlowGraphNode::from_raw(BNCreateFlowGraphNode(graph.handle)) }
+ pub(crate) unsafe fn ref_from_raw(raw: *mut BNFlowGraphNode) -> Ref<Self> {
+ Ref::new(Self {
+ handle: raw,
+ _data: PhantomData,
+ })
+ }
+
+ pub fn new(graph: &FlowGraph) -> Ref<Self> {
+ unsafe { FlowGraphNode::ref_from_raw(BNCreateFlowGraphNode(graph.handle)) }
+ }
+
+ pub fn basic_block<C: BlockContext>(&self, context: C) -> Option<Ref<BasicBlock<C>>> {
+ let block_ptr = unsafe { BNGetFlowGraphBasicBlock(self.handle) };
+ if block_ptr.is_null() {
+ return None;
+ }
+ Some(unsafe { BasicBlock::ref_from_raw(block_ptr, context) })
+ }
+
+ pub fn set_basic_block<C: BlockContext>(&self, block: Option<&BasicBlock<C>>) {
+ match block {
+ Some(block) => unsafe { BNSetFlowGraphBasicBlock(self.handle, block.handle) },
+ None => unsafe { BNSetFlowGraphBasicBlock(self.handle, std::ptr::null_mut()) },
+ }
+ }
+
+ pub fn lines(&self) -> Array<DisassemblyTextLine> {
+ let mut count = 0;
+ let result = unsafe { BNGetFlowGraphNodeLines(self.handle, &mut count) };
+ assert!(!result.is_null());
+ unsafe { Array::new(result, count, ()) }
}
pub fn set_lines(&self, lines: impl IntoIterator<Item = DisassemblyTextLine>) {
@@ -106,6 +206,30 @@ impl<'a> FlowGraphNode<'a> {
}
}
+ /// Returns the graph position of the node in X, Y form.
+ pub fn position(&self) -> (i32, i32) {
+ let pos_x = unsafe { BNGetFlowGraphNodeX(self.handle) };
+ let pos_y = unsafe { BNGetFlowGraphNodeY(self.handle) };
+ (pos_x, pos_y)
+ }
+
+ /// Sets the graph position of the node.
+ pub fn set_position(&self, x: i32, y: i32) {
+ unsafe { BNFlowGraphNodeSetX(self.handle, x) };
+ unsafe { BNFlowGraphNodeSetX(self.handle, y) };
+ }
+
+ pub fn highlight_color(&self) -> HighlightColor {
+ let raw = unsafe { BNGetFlowGraphNodeHighlight(self.handle) };
+ HighlightColor::from(raw)
+ }
+
+ pub fn set_highlight_color(&self, highlight: HighlightColor) {
+ unsafe { BNSetFlowGraphNodeHighlight(self.handle, highlight.into()) };
+ }
+
+ // TODO: Add getters and setters for edges
+
pub fn add_outgoing_edge(
&self,
type_: BranchType,
diff --git a/rust/src/function.rs b/rust/src/function.rs
index d4eb1d53..63ebc82e 100644
--- a/rust/src/function.rs
+++ b/rust/src/function.rs
@@ -128,7 +128,7 @@ impl Iterator for NativeBlockIter {
}
}
-#[derive(Clone)]
+#[derive(Clone, Debug, PartialEq, Eq)]
pub struct NativeBlock {
_priv: (),
}
@@ -2346,7 +2346,7 @@ impl Function {
/// Flow graph of unresolved stack adjustments
pub fn unresolved_stack_adjustment_graph(&self) -> Option<Ref<FlowGraph>> {
let graph = unsafe { BNGetUnresolvedStackAdjustmentGraph(self.handle) };
- (!graph.is_null()).then(|| unsafe { Ref::new(FlowGraph::from_raw(graph)) })
+ (!graph.is_null()).then(|| unsafe { FlowGraph::ref_from_raw(graph) })
}
pub fn create_graph(
@@ -2358,7 +2358,7 @@ impl Function {
let raw_view_type = FunctionViewType::into_raw(view_type);
let result = unsafe { BNCreateFunctionGraph(self.handle, raw_view_type, settings_raw) };
FunctionViewType::free_raw(raw_view_type);
- unsafe { Ref::new(FlowGraph::from_raw(result)) }
+ unsafe { FlowGraph::ref_from_raw(result) }
}
pub fn parent_components(&self) -> Array<Component> {
diff --git a/rust/src/lib.rs b/rust/src/lib.rs
index eb89ff0c..0cb0484e 100644
--- a/rust/src/lib.rs
+++ b/rust/src/lib.rs
@@ -68,6 +68,7 @@ pub mod project;
pub mod rc;
pub mod references;
pub mod relocation;
+pub mod render_layer;
pub mod section;
pub mod segment;
pub mod settings;
diff --git a/rust/src/linear_view.rs b/rust/src/linear_view.rs
index dc3ca597..6d2347b3 100644
--- a/rust/src/linear_view.rs
+++ b/rust/src/linear_view.rs
@@ -18,26 +18,37 @@ use binaryninjacore_sys::*;
use crate::binary_view::BinaryView;
use crate::disassembly::{DisassemblySettings, DisassemblyTextLine};
-use crate::function::Function;
+use crate::function::{Function, NativeBlock};
+use crate::basic_block::BasicBlock;
use crate::rc::*;
+use crate::render_layer::CoreRenderLayer;
+use crate::string::{raw_to_string, BnString};
use std::ops::Deref;
-use std::mem;
-
pub type LinearDisassemblyLineType = BNLinearDisassemblyLineType;
+pub type LinearViewObjectIdentifierType = BNLinearViewObjectIdentifierType;
-// TODO: Rename to LinearView?
pub struct LinearViewObject {
pub(crate) handle: *mut BNLinearViewObject,
}
impl LinearViewObject {
+ pub(crate) unsafe fn from_raw(handle: *mut BNLinearViewObject) -> Self {
+ debug_assert!(!handle.is_null());
+ Self { handle }
+ }
+
pub(crate) unsafe fn ref_from_raw(handle: *mut BNLinearViewObject) -> Ref<Self> {
debug_assert!(!handle.is_null());
Ref::new(Self { handle })
}
+ pub fn identifier(&self) -> LinearViewObjectIdentifier {
+ let raw = unsafe { BNGetLinearViewObjectIdentifier(self.handle) };
+ LinearViewObjectIdentifier::from_owned_raw(raw)
+ }
+
pub fn data_only(view: &BinaryView, settings: &DisassemblySettings) -> Ref<Self> {
unsafe {
let handle = BNCreateLinearViewDataOnly(view.handle, settings.handle);
@@ -214,6 +225,45 @@ impl ToOwned for LinearViewObject {
unsafe impl Send for LinearViewObject {}
unsafe impl Sync for LinearViewObject {}
+#[derive(Clone, PartialEq, Debug)]
+pub struct LinearViewObjectIdentifier {
+ pub name: String,
+ pub ty: LinearViewObjectIdentifierType,
+ pub start: u64,
+ pub end: u64,
+}
+
+impl LinearViewObjectIdentifier {
+ pub fn from_raw(value: &BNLinearViewObjectIdentifier) -> Self {
+ Self {
+ name: raw_to_string(value.name).unwrap(),
+ ty: value.type_,
+ start: value.start,
+ end: value.end,
+ }
+ }
+
+ pub fn from_owned_raw(value: BNLinearViewObjectIdentifier) -> Self {
+ let owned = Self::from_raw(&value);
+ Self::free_raw(value);
+ owned
+ }
+
+ pub fn into_raw(value: Self) -> BNLinearViewObjectIdentifier {
+ let bn_name = BnString::new(value.name);
+ BNLinearViewObjectIdentifier {
+ name: BnString::into_raw(bn_name),
+ type_: value.ty,
+ start: value.start,
+ end: value.end,
+ }
+ }
+
+ pub fn free_raw(value: BNLinearViewObjectIdentifier) {
+ let _ = unsafe { BnString::from_raw(value.name) };
+ }
+}
+
#[derive(Eq)]
pub struct LinearViewCursor {
pub(crate) handle: *mut BNLinearViewCursor,
@@ -252,15 +302,15 @@ impl LinearViewCursor {
!(self.before_begin() || self.after_end())
}
- pub fn seek_to_start(&self) {
+ pub fn seek_to_start(&mut self) {
unsafe { BNSeekLinearViewCursorToBegin(self.handle) }
}
- pub fn seek_to_end(&self) {
+ pub fn seek_to_end(&mut self) {
unsafe { BNSeekLinearViewCursorToEnd(self.handle) }
}
- pub fn seek_to_address(&self, address: u64) {
+ pub fn seek_to_address(&mut self, address: u64) {
unsafe { BNSeekLinearViewCursorToAddress(self.handle, address) }
}
@@ -275,15 +325,15 @@ impl LinearViewCursor {
unsafe { BNGetLinearViewCursorOrderingIndexTotal(self.handle) }
}
- pub fn seek_to_ordering_index(&self, idx: u64) {
+ pub fn seek_to_ordering_index(&mut self, idx: u64) {
unsafe { BNSeekLinearViewCursorToAddress(self.handle, idx) }
}
- pub fn previous(&self) -> bool {
+ pub fn previous(&mut self) -> bool {
unsafe { BNLinearViewCursorPrevious(self.handle) }
}
- pub fn next(&self) -> bool {
+ pub fn next(&mut self) -> bool {
unsafe { BNLinearViewCursorNext(self.handle) }
}
@@ -294,6 +344,27 @@ impl LinearViewCursor {
Array::new(handles, count, ())
}
}
+
+ /// A list of the currently applied [`CoreRenderLayer`]'s
+ pub fn render_layers(&self) -> Array<CoreRenderLayer> {
+ let mut count: usize = 0;
+ unsafe {
+ let handles = BNGetLinearViewCursorRenderLayers(self.handle, &mut count);
+ Array::new(handles, count, ())
+ }
+ }
+
+ /// Add a Render Layer to be applied to this [`LinearViewCursor`].
+ ///
+ /// NOTE: Layers will be applied in the order in which they are added.
+ pub fn add_render_layer(&self, layer: &CoreRenderLayer) {
+ unsafe { BNAddLinearViewCursorRenderLayer(self.handle, layer.handle.as_ptr()) };
+ }
+
+ /// Remove a Render Layer from being applied to this [`LinearViewCursor`].
+ pub fn remove_render_layer(&self, layer: &CoreRenderLayer) {
+ unsafe { BNRemoveLinearViewCursorRenderLayer(self.handle, layer.handle.as_ptr()) };
+ }
}
impl PartialEq for LinearViewCursor {
@@ -341,55 +412,81 @@ impl ToOwned for LinearViewCursor {
unsafe impl Send for LinearViewCursor {}
unsafe impl Sync for LinearViewCursor {}
+#[derive(Clone, PartialEq, Debug, Eq)]
pub struct LinearDisassemblyLine {
- t: LinearDisassemblyLineType,
-
- // These will be cleaned up by BNFreeLinearDisassemblyLines, so we
- // don't drop them in the relevant deconstructors.
- // TODO: This is insane!
- function: mem::ManuallyDrop<Ref<Function>>,
- contents: mem::ManuallyDrop<DisassemblyTextLine>,
+ pub ty: LinearDisassemblyLineType,
+ pub function: Option<Ref<Function>>,
+ pub basic_block: Option<Ref<BasicBlock<NativeBlock>>>,
+ pub contents: DisassemblyTextLine,
}
impl LinearDisassemblyLine {
- pub(crate) unsafe fn from_raw(raw: &BNLinearDisassemblyLine) -> Self {
- let linetype = raw.type_;
- // TODO: We must remove this behavior.
- let function = mem::ManuallyDrop::new(Function::ref_from_raw(raw.function));
- let contents = mem::ManuallyDrop::new(DisassemblyTextLine::from_raw(&raw.contents));
+ pub(crate) unsafe fn from_raw(value: &BNLinearDisassemblyLine) -> Self {
+ let function = if !value.function.is_null() {
+ Some(unsafe { Function::from_raw(value.function).to_owned() })
+ } else {
+ None
+ };
+ let basic_block = if !value.block.is_null() {
+ Some(unsafe { BasicBlock::from_raw(value.block, NativeBlock::new()).to_owned() })
+ } else {
+ None
+ };
Self {
- t: linetype,
+ ty: value.type_,
function,
- contents,
+ basic_block,
+ contents: DisassemblyTextLine::from_raw(&value.contents),
}
}
- pub fn function(&self) -> &Function {
- self.function.as_ref()
+ #[allow(unused)]
+ pub(crate) unsafe fn from_owned_raw(value: BNLinearDisassemblyLine) -> Self {
+ let owned = Self::from_raw(&value);
+ Self::free_raw(value);
+ owned
+ }
+
+ pub(crate) fn into_raw(value: Self) -> BNLinearDisassemblyLine {
+ let function_ptr = value
+ .function
+ .map(|f| unsafe { Ref::into_raw(f) }.handle)
+ .unwrap_or(std::ptr::null_mut());
+ let block_ptr = value
+ .basic_block
+ .map(|b| unsafe { Ref::into_raw(b) }.handle)
+ .unwrap_or(std::ptr::null_mut());
+ BNLinearDisassemblyLine {
+ type_: value.ty,
+ function: function_ptr,
+ block: block_ptr,
+ contents: DisassemblyTextLine::into_raw(value.contents),
+ }
}
- pub fn line_type(&self) -> LinearDisassemblyLineType {
- self.t
+ pub(crate) fn free_raw(value: BNLinearDisassemblyLine) {
+ let _ = unsafe { Function::ref_from_raw(value.function) };
+ DisassemblyTextLine::free_raw(value.contents);
}
}
impl Deref for LinearDisassemblyLine {
type Target = DisassemblyTextLine;
fn deref(&self) -> &Self::Target {
- self.contents.deref()
+ &self.contents
}
}
impl std::fmt::Display for LinearDisassemblyLine {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
- write!(f, "{}", self.deref())
+ write!(f, "{}", self.contents)
}
}
impl CoreArrayProvider for LinearDisassemblyLine {
type Raw = BNLinearDisassemblyLine;
type Context = ();
- type Wrapped<'a> = Guard<'a, LinearDisassemblyLine>;
+ type Wrapped<'a> = LinearDisassemblyLine;
}
unsafe impl CoreArrayProviderInner for LinearDisassemblyLine {
@@ -397,8 +494,7 @@ unsafe impl CoreArrayProviderInner for LinearDisassemblyLine {
BNFreeLinearDisassemblyLines(raw, count);
}
- unsafe fn wrap_raw<'a>(raw: &'a Self::Raw, context: &'a Self::Context) -> Self::Wrapped<'a> {
- // TODO: Cant remove this guard until we remove those manual drops... INSANE!
- Guard::new(Self::from_raw(raw), context)
+ unsafe fn wrap_raw<'a>(raw: &'a Self::Raw, _context: &'a Self::Context) -> Self::Wrapped<'a> {
+ Self::from_raw(raw)
}
}
diff --git a/rust/src/low_level_il.rs b/rust/src/low_level_il.rs
index 5df7252b..750bcfd1 100644
--- a/rust/src/low_level_il.rs
+++ b/rust/src/low_level_il.rs
@@ -24,7 +24,7 @@ use crate::architecture::Register as ArchReg;
use crate::architecture::{Architecture, RegisterId};
use crate::function::Location;
-mod block;
+pub mod block;
pub mod expression;
pub mod function;
pub mod instruction;
diff --git a/rust/src/medium_level_il/function.rs b/rust/src/medium_level_il/function.rs
index a9802005..8f91c731 100644
--- a/rust/src/medium_level_il/function.rs
+++ b/rust/src/medium_level_il/function.rs
@@ -548,10 +548,10 @@ impl MediumLevelILFunction {
unsafe { BNGetMediumLevelILSSAVarValue(self.handle, &raw_var, ssa_variable.version) }.into()
}
- pub fn create_graph(&self, settings: Option<DisassemblySettings>) -> FlowGraph {
+ pub fn create_graph(&self, settings: Option<DisassemblySettings>) -> Ref<FlowGraph> {
let settings = settings.map(|x| x.handle).unwrap_or(std::ptr::null_mut());
let graph = unsafe { BNCreateMediumLevelILFunctionGraph(self.handle, settings) };
- unsafe { FlowGraph::from_raw(graph) }
+ unsafe { FlowGraph::ref_from_raw(graph) }
}
/// This gets just the MLIL variables - you may be interested in the union
diff --git a/rust/src/render_layer.rs b/rust/src/render_layer.rs
new file mode 100644
index 00000000..fa617fe7
--- /dev/null
+++ b/rust/src/render_layer.rs
@@ -0,0 +1,410 @@
+//! Customize the presentation of Linear and Graph view output.
+
+use crate::basic_block::{BasicBlock, BasicBlockType};
+use crate::disassembly::DisassemblyTextLine;
+use crate::flowgraph::FlowGraph;
+use crate::function::{Function, NativeBlock};
+use crate::linear_view::{LinearDisassemblyLine, LinearDisassemblyLineType, LinearViewObject};
+use crate::rc::{Array, CoreArrayProvider, CoreArrayProviderInner};
+use crate::string::BnStrCompatible;
+use binaryninjacore_sys::*;
+use std::ffi::{c_char, c_void};
+use std::ptr::NonNull;
+
+pub type RenderLayerDefaultEnableState = BNRenderLayerDefaultEnableState;
+
+/// Register a [`RenderLayer`] with the API.
+pub fn register_render_layer<S: BnStrCompatible, T: RenderLayer>(
+ name: S,
+ render_layer: T,
+ enable_state: RenderLayerDefaultEnableState,
+) -> (&'static mut T, CoreRenderLayer) {
+ let render_layer = Box::leak(Box::new(render_layer));
+ let mut callback = BNRenderLayerCallbacks {
+ context: render_layer as *mut _ as *mut c_void,
+ applyToFlowGraph: Some(cb_apply_to_flow_graph::<T>),
+ applyToLinearViewObject: Some(cb_apply_to_linear_view_object::<T>),
+ freeLines: Some(cb_free_lines),
+ };
+ let result = unsafe {
+ BNRegisterRenderLayer(
+ name.into_bytes_with_nul().as_ref().as_ptr() as *const _,
+ &mut callback,
+ enable_state,
+ )
+ };
+ let core = CoreRenderLayer::from_raw(NonNull::new(result).unwrap());
+ (render_layer, core)
+}
+
+pub trait RenderLayer: Sized {
+ /// Apply this Render Layer to a Flow Graph.
+ fn apply_to_flow_graph(&self, graph: &mut FlowGraph) {
+ for node in graph.nodes() {
+ if let Some(block) = node.basic_block(NativeBlock::new()) {
+ let new_lines = self.apply_to_block(&block, node.lines().to_vec());
+ node.set_lines(new_lines);
+ }
+ }
+ }
+
+ /// Apply this Render Layer to the lines produced by a LinearViewObject for rendering in Linear View.
+ fn apply_to_linear_object(
+ &self,
+ object: &mut LinearViewObject,
+ _prev_object: Option<&mut LinearViewObject>,
+ _next_object: Option<&mut LinearViewObject>,
+ lines: Vec<LinearDisassemblyLine>,
+ ) -> Vec<LinearDisassemblyLine> {
+ let text_to_lines =
+ |function: &Function, block: &BasicBlock<NativeBlock>, text: DisassemblyTextLine| {
+ LinearDisassemblyLine {
+ ty: LinearDisassemblyLineType::CodeDisassemblyLineType,
+ function: Some(function.to_owned()),
+ basic_block: Some(block.to_owned()),
+ contents: text,
+ }
+ };
+
+ // Hack: HLIL bodies don't have basic blocks.
+ let obj_ident = object.identifier();
+ if !lines.is_empty()
+ && (obj_ident.name.starts_with("HLIL") || obj_ident.name.starts_with("Language"))
+ {
+ // Apply to HLIL body.
+ let function = lines[0]
+ .function
+ .to_owned()
+ .expect("HLIL body has no function");
+ return self.apply_to_hlil_body(&function, lines);
+ }
+
+ // Collect the "line blocks".
+ // Line blocks are contiguous lines with the same backing basic block (or lack thereof).
+ // Line blocks also group by line type.
+ let mut line_blocks: Vec<Vec<LinearDisassemblyLine>> = Vec::new();
+ for line in lines {
+ let Some(last_block) = line_blocks.last_mut() else {
+ // No last block, create the first block.
+ line_blocks.push(vec![line]);
+ continue;
+ };
+
+ let Some(last_line) = last_block.last() else {
+ // No last line, create the first line.
+ last_block.push(line);
+ continue;
+ };
+
+ // TODO: If we want to allow a block with multiple line types we need to specifically check
+ // TODO: If the last line type was Code, if it is and the last line is not we make a new block.
+ if last_line.basic_block == line.basic_block && last_line.ty == line.ty {
+ // Same basic block and line type, this is a part of the same line block.
+ last_block.push(line);
+ } else {
+ // Not the same line block, create a new block.
+ line_blocks.push(vec![line]);
+ }
+ }
+
+ line_blocks
+ .into_iter()
+ .filter_map(|line_block| {
+ let probe_line = line_block.first()?;
+ Some((probe_line.ty, probe_line.basic_block.to_owned(), line_block))
+ })
+ .map(|(line_ty, basic_block, lines)| {
+ match line_ty {
+ LinearDisassemblyLineType::CodeDisassemblyLineType => {
+ // Dealing with code lines.
+ let block = basic_block.expect("Code line has no basic block");
+ let function = block.function();
+ let text_lines = lines.into_iter().map(|line| line.contents).collect();
+ let new_text_lines = self.apply_to_block(&block, text_lines);
+ let new_lines = new_text_lines
+ .into_iter()
+ .map(|line| text_to_lines(&function, &block, line))
+ .collect();
+ new_lines
+ }
+ _ => {
+ // Dealing with misc lines.
+ self.apply_to_misc_lines(
+ object,
+ _prev_object.as_deref(),
+ _next_object.as_deref(),
+ lines,
+ )
+ }
+ }
+ })
+ .flatten()
+ .collect()
+ }
+
+ /// Apply this Render Layer to a single Basic Block of Disassembly lines.
+ ///
+ /// Modify the lines to change the presentation of the block.
+ fn apply_to_disassembly_block(
+ &self,
+ _block: &BasicBlock<NativeBlock>,
+ lines: Vec<DisassemblyTextLine>,
+ ) -> Vec<DisassemblyTextLine> {
+ lines
+ }
+
+ /// Apply this Render Layer to a single Basic Block of Low Level IL lines.
+ ///
+ /// Modify the lines to change the presentation of the block.
+ fn apply_to_llil_block(
+ &self,
+ _block: &BasicBlock<NativeBlock>,
+ lines: Vec<DisassemblyTextLine>,
+ ) -> Vec<DisassemblyTextLine> {
+ lines
+ }
+
+ /// Apply this Render Layer to a single Basic Block of Medium Level IL lines.
+ ///
+ /// Modify the lines to change the presentation of the block.
+ fn apply_to_mlil_block(
+ &self,
+ _block: &BasicBlock<NativeBlock>,
+ lines: Vec<DisassemblyTextLine>,
+ ) -> Vec<DisassemblyTextLine> {
+ lines
+ }
+
+ /// Apply this Render Layer to a single Basic Block of High Level IL lines.
+ ///
+ /// Modify the lines to change the presentation of the block.
+ ///
+ /// This function will NOT apply to High Level IL bodies as displayed in Linear View!
+ /// Those are handled by [`RenderLayer::apply_to_hlil_body`] instead as they do not
+ /// have a [`BasicBlock`] associated with them.
+ fn apply_to_hlil_block(
+ &self,
+ _block: &BasicBlock<NativeBlock>,
+ lines: Vec<DisassemblyTextLine>,
+ ) -> Vec<DisassemblyTextLine> {
+ lines
+ }
+
+ /// Apply this Render Layer to the entire body of a High Level IL function.
+ ///
+ /// Modify the lines to change the presentation of the block.
+ ///
+ /// This function only applies to Linear View, and not to Graph View! If you want to
+ /// handle Graph View too, you will need to use [`RenderLayer::apply_to_hlil_block`] and handle
+ /// the lines one block at a time.
+ fn apply_to_hlil_body(
+ &self,
+ _function: &Function,
+ lines: Vec<LinearDisassemblyLine>,
+ ) -> Vec<LinearDisassemblyLine> {
+ lines
+ }
+
+ // TODO: We might want to just go ahead and pass the line type.
+ /// Apply to lines generated by Linear View that are not part of a function.
+ ///
+ /// Modify the lines to change the presentation of the block.
+ fn apply_to_misc_lines(
+ &self,
+ _object: &mut LinearViewObject,
+ _prev_object: Option<&LinearViewObject>,
+ _next_object: Option<&LinearViewObject>,
+ lines: Vec<LinearDisassemblyLine>,
+ ) -> Vec<LinearDisassemblyLine> {
+ lines
+ }
+
+ /// Apply this Render Layer to all IL blocks and disassembly blocks.
+ ///
+ /// If not implemented this will handle calling the view specific apply functions:
+ ///
+ /// - [`RenderLayer::apply_to_disassembly_block`]
+ /// - [`RenderLayer::apply_to_llil_block`]
+ /// - [`RenderLayer::apply_to_mlil_block`]
+ /// - [`RenderLayer::apply_to_hlil_block`]
+ ///
+ /// Modify the lines to change the presentation of the block.
+ fn apply_to_block(
+ &self,
+ block: &BasicBlock<NativeBlock>,
+ lines: Vec<DisassemblyTextLine>,
+ ) -> Vec<DisassemblyTextLine> {
+ match block.block_type() {
+ BasicBlockType::Native => self.apply_to_disassembly_block(block, lines),
+ BasicBlockType::LowLevelIL => self.apply_to_llil_block(block, lines),
+ BasicBlockType::MediumLevelIL => self.apply_to_mlil_block(block, lines),
+ BasicBlockType::HighLevelIL => self.apply_to_hlil_block(block, lines),
+ }
+ }
+}
+
+#[repr(transparent)]
+pub struct CoreRenderLayer {
+ pub(crate) handle: NonNull<BNRenderLayer>,
+}
+
+impl CoreRenderLayer {
+ pub fn from_raw(handle: NonNull<BNRenderLayer>) -> Self {
+ Self { handle }
+ }
+
+ pub fn render_layers() -> Array<CoreRenderLayer> {
+ let mut count = 0;
+ let result = unsafe { BNGetRenderLayerList(&mut count) };
+ unsafe { Array::new(result, count, ()) }
+ }
+
+ pub fn render_layer_by_name<S: BnStrCompatible>(name: S) -> Option<CoreRenderLayer> {
+ let name_raw = name.into_bytes_with_nul();
+ let result = unsafe { BNGetRenderLayerByName(name_raw.as_ref().as_ptr() as *const c_char) };
+ NonNull::new(result).map(|x| Self::from_raw(x))
+ }
+
+ pub fn default_enable_state(&self) -> RenderLayerDefaultEnableState {
+ unsafe { BNGetRenderLayerDefaultEnableState(self.handle.as_ptr()) }
+ }
+
+ pub fn apply_to_flow_graph(&self, graph: &FlowGraph) {
+ unsafe { BNApplyRenderLayerToFlowGraph(self.handle.as_ptr(), graph.handle) }
+ }
+
+ pub fn apply_to_linear_view_object(
+ &self,
+ object: &LinearViewObject,
+ prev_object: Option<&LinearViewObject>,
+ next_object: Option<&LinearViewObject>,
+ lines: Vec<LinearDisassemblyLine>,
+ ) -> Vec<LinearDisassemblyLine> {
+ let mut lines_raw: Vec<_> = lines
+ .into_iter()
+ // NOTE: Freed after the core call
+ .map(LinearDisassemblyLine::into_raw)
+ .collect();
+
+ let prev_object_ptr = prev_object
+ .map(|o| o.handle)
+ .unwrap_or(std::ptr::null_mut());
+ let next_object_ptr = next_object
+ .map(|o| o.handle)
+ .unwrap_or(std::ptr::null_mut());
+
+ let mut new_lines = std::ptr::null_mut();
+ let mut new_line_count = 0;
+
+ unsafe {
+ BNApplyRenderLayerToLinearViewObject(
+ self.handle.as_ptr(),
+ object.handle,
+ prev_object_ptr,
+ next_object_ptr,
+ lines_raw.as_mut_ptr(),
+ lines_raw.len(),
+ &mut new_lines,
+ &mut new_line_count,
+ )
+ };
+
+ for line in lines_raw {
+ LinearDisassemblyLine::free_raw(line);
+ }
+
+ let raw: Array<LinearDisassemblyLine> =
+ unsafe { Array::new(new_lines, new_line_count, ()) };
+ raw.to_vec()
+ }
+}
+
+impl CoreArrayProvider for CoreRenderLayer {
+ type Raw = *mut BNRenderLayer;
+ type Context = ();
+ type Wrapped<'a> = Self;
+}
+
+unsafe impl CoreArrayProviderInner for CoreRenderLayer {
+ unsafe fn free(raw: *mut Self::Raw, _count: usize, _context: &Self::Context) {
+ BNFreeRenderLayerList(raw)
+ }
+
+ unsafe fn wrap_raw<'a>(raw: &'a Self::Raw, _context: &'a Self::Context) -> Self::Wrapped<'a> {
+ // TODO: Because handle is a NonNull we should prob make Self::Raw that as well...
+ let handle = NonNull::new(*raw).unwrap();
+ CoreRenderLayer::from_raw(handle)
+ }
+}
+
+unsafe extern "C" fn cb_apply_to_flow_graph<T: RenderLayer>(
+ ctxt: *mut c_void,
+ graph: *mut BNFlowGraph,
+) {
+ let ctxt: &mut T = &mut *(ctxt as *mut T);
+ // SAFETY: We do not own the flowgraph, do not take it as Ref.
+ let mut flow_graph = FlowGraph::from_raw(graph);
+ ctxt.apply_to_flow_graph(&mut flow_graph);
+}
+
+unsafe extern "C" fn cb_apply_to_linear_view_object<T: RenderLayer>(
+ ctxt: *mut c_void,
+ object: *mut BNLinearViewObject,
+ prev: *mut BNLinearViewObject,
+ next: *mut BNLinearViewObject,
+ in_lines: *mut BNLinearDisassemblyLine,
+ in_line_count: usize,
+ out_lines: *mut *mut BNLinearDisassemblyLine,
+ out_line_count: *mut usize,
+) {
+ let ctxt: &mut T = &mut *(ctxt as *mut T);
+ // SAFETY: We do not own the flowgraph, do not take it as Ref.
+ let mut object = LinearViewObject::from_raw(object);
+ let mut prev_object = if !prev.is_null() {
+ Some(LinearViewObject::from_raw(prev))
+ } else {
+ None
+ };
+ let mut next_object = if !next.is_null() {
+ Some(LinearViewObject::from_raw(next))
+ } else {
+ None
+ };
+
+ let raw_lines = std::slice::from_raw_parts(in_lines, in_line_count);
+ // NOTE: The caller is owned of the inLines.
+ let lines: Vec<_> = raw_lines
+ .iter()
+ .map(|line| LinearDisassemblyLine::from_raw(line))
+ .collect();
+
+ let new_lines = ctxt.apply_to_linear_object(
+ &mut object,
+ prev_object.as_mut(),
+ next_object.as_mut(),
+ lines,
+ );
+
+ unsafe {
+ *out_line_count = new_lines.len();
+ let boxed_new_lines: Box<[_]> = new_lines
+ .into_iter()
+ // NOTE: Freed by cb_free_lines
+ .map(LinearDisassemblyLine::into_raw)
+ .collect();
+ // NOTE: Dropped by cb_free_lines
+ *out_lines = Box::leak(boxed_new_lines).as_mut_ptr();
+ }
+}
+
+unsafe extern "C" fn cb_free_lines(
+ _ctxt: *mut c_void,
+ lines: *mut BNLinearDisassemblyLine,
+ line_count: usize,
+) {
+ let lines_ptr = std::ptr::slice_from_raw_parts_mut(lines, line_count);
+ let boxed_lines = Box::from_raw(lines_ptr);
+ for line in boxed_lines {
+ LinearDisassemblyLine::free_raw(line);
+ }
+}
diff --git a/rust/src/workflow.rs b/rust/src/workflow.rs
index 68cd3e18..e95beb48 100644
--- a/rust/src/workflow.rs
+++ b/rust/src/workflow.rs
@@ -565,7 +565,7 @@ impl Workflow {
&self,
activity: A,
sequential: Option<bool>,
- ) -> Option<FlowGraph> {
+ ) -> Option<Ref<FlowGraph>> {
let sequential = sequential.unwrap_or(false);
let activity_name = activity.into_bytes_with_nul();
let graph = unsafe {
@@ -578,7 +578,7 @@ impl Workflow {
if graph.is_null() {
return None;
}
- Some(unsafe { FlowGraph::from_raw(graph) })
+ Some(unsafe { FlowGraph::ref_from_raw(graph) })
}
/// Not yet implemented.