diff --git a/arch/msp430/src/architecture.rs b/arch/msp430/src/architecture.rs index 302aa18fa..55d39f0cc 100644 --- a/arch/msp430/src/architecture.rs +++ b/arch/msp430/src/architecture.rs @@ -7,7 +7,7 @@ use binaryninja::{ Architecture, CoreArchitecture, CustomArchitectureHandle, FlagCondition, InstructionInfo, UnusedIntrinsic, UnusedRegisterStack, UnusedRegisterStackInfo, }, - disassembly::{InstructionTextToken, InstructionTextTokenContents}, + disassembly::{InstructionTextToken, InstructionTextTokenKind}, llil::{LiftedExpr, Lifter}, Endianness, }; @@ -400,7 +400,7 @@ fn generate_tokens(inst: &Instruction, addr: u64) -> Vec { Instruction::Call(inst) => generate_single_operand_tokens(inst, addr, true), Instruction::Reti(_) => vec![InstructionTextToken::new( "reti", - InstructionTextTokenContents::Instruction, + InstructionTextTokenKind::Instruction, )], // Jxx instructions @@ -462,14 +462,14 @@ fn generate_single_operand_tokens( ) -> Vec { let mut res = vec![InstructionTextToken::new( inst.mnemonic(), - InstructionTextTokenContents::Instruction, + InstructionTextTokenKind::Instruction, )]; if inst.mnemonic().len() < MIN_MNEMONIC { let padding = " ".repeat(MIN_MNEMONIC - inst.mnemonic().len()); res.push(InstructionTextToken::new( &padding, - InstructionTextTokenContents::Text, + InstructionTextTokenKind::Text, )) } @@ -483,20 +483,23 @@ fn generate_jxx_tokens(inst: &impl Jxx, addr: u64) -> Vec let mut res = vec![InstructionTextToken::new( inst.mnemonic(), - InstructionTextTokenContents::Instruction, + InstructionTextTokenKind::Instruction, )]; if inst.mnemonic().len() < MIN_MNEMONIC { let padding = " ".repeat(MIN_MNEMONIC - inst.mnemonic().len()); res.push(InstructionTextToken::new( &padding, - InstructionTextTokenContents::Text, + InstructionTextTokenKind::Text, )) } res.push(InstructionTextToken::new( &format!("0x{fixed_addr:4x}"), - InstructionTextTokenContents::CodeRelativeAddress(fixed_addr), + InstructionTextTokenKind::CodeRelativeAddress { + value: fixed_addr, + size: None, + }, )); res @@ -505,21 +508,21 @@ fn generate_jxx_tokens(inst: &impl Jxx, addr: u64) -> Vec fn generate_two_operand_tokens(inst: &impl TwoOperand, addr: u64) -> Vec { let mut res = vec![InstructionTextToken::new( inst.mnemonic(), - InstructionTextTokenContents::Instruction, + InstructionTextTokenKind::Instruction, )]; if inst.mnemonic().len() < MIN_MNEMONIC { let padding = " ".repeat(MIN_MNEMONIC - inst.mnemonic().len()); res.push(InstructionTextToken::new( &padding, - InstructionTextTokenContents::Text, + InstructionTextTokenKind::Text, )) } res.extend_from_slice(&generate_operand_tokens(inst.source(), addr, false)); res.push(InstructionTextToken::new( ", ", - InstructionTextTokenContents::OperandSeparator, + InstructionTextTokenKind::OperandSeparator, )); res.extend_from_slice(&generate_operand_tokens(inst.destination(), addr, false)); @@ -533,14 +536,14 @@ fn generate_emulated_tokens( ) -> Vec { let mut res = vec![InstructionTextToken::new( inst.mnemonic(), - InstructionTextTokenContents::Instruction, + InstructionTextTokenKind::Instruction, )]; if inst.mnemonic().len() < MIN_MNEMONIC { let padding = " ".repeat(MIN_MNEMONIC - inst.mnemonic().len()); res.push(InstructionTextToken::new( &padding, - InstructionTextTokenContents::Text, + InstructionTextTokenKind::Text, )) } @@ -560,23 +563,23 @@ fn generate_operand_tokens(source: &Operand, addr: u64, call: bool) -> Vec match r { 0 => vec![InstructionTextToken::new( "pc", - InstructionTextTokenContents::Register, + InstructionTextTokenKind::Register, )], 1 => vec![InstructionTextToken::new( "sp", - InstructionTextTokenContents::Register, + InstructionTextTokenKind::Register, )], 2 => vec![InstructionTextToken::new( "sr", - InstructionTextTokenContents::Register, + InstructionTextTokenKind::Register, )], 3 => vec![InstructionTextToken::new( "cg", - InstructionTextTokenContents::Register, + InstructionTextTokenKind::Register, )], _ => vec![InstructionTextToken::new( &format!("r{r}"), - InstructionTextTokenContents::Register, + InstructionTextTokenKind::Register, )], }, Operand::Indexed((r, i)) => match r { @@ -589,11 +592,14 @@ fn generate_operand_tokens(source: &Operand, addr: u64, call: bool) -> Vec { @@ -605,11 +611,14 @@ fn generate_operand_tokens(source: &Operand, addr: u64, call: bool) -> Vec { @@ -621,11 +630,14 @@ fn generate_operand_tokens(source: &Operand, addr: u64, call: bool) -> Vec { @@ -637,11 +649,14 @@ fn generate_operand_tokens(source: &Operand, addr: u64, call: bool) -> Vec { @@ -653,14 +668,17 @@ fn generate_operand_tokens(source: &Operand, addr: u64, call: bool) -> Vec Vec { @@ -684,28 +702,37 @@ fn generate_operand_tokens(source: &Operand, addr: u64, call: bool) -> Vec { - let val = (addr as i64 + *i as i64) as u64; + let value = (addr as i64 + *i as i64) as u64; vec![InstructionTextToken::new( - &format!("{val:#x}"), - InstructionTextTokenContents::CodeRelativeAddress(val), + &format!("{value:#x}"), + InstructionTextTokenKind::CodeRelativeAddress { + value, + size: None, + }, )] } Operand::Immediate(i) => { if call { vec![InstructionTextToken::new( &format!("{i:#x}"), - InstructionTextTokenContents::CodeRelativeAddress(*i as u64), + InstructionTextTokenKind::CodeRelativeAddress { + value: *i as u64, + size: None, + }, )] } else { vec![InstructionTextToken::new( &format!("{i:#x}"), - InstructionTextTokenContents::PossibleAddress(*i as u64), + InstructionTextTokenKind::PossibleAddress { + value: *i as u64, + size: None, + }, )] } } @@ -713,12 +740,18 @@ fn generate_operand_tokens(source: &Operand, addr: u64, call: bool) -> Vec Vec architecture::Architecture fo addr: u64, ) -> Option<(usize, Vec)> { use riscv_dis::Operand; - use InstructionTextTokenContents::*; + use InstructionTextTokenKind::*; let inst = match D::decode(addr, data) { Ok(i) => i, @@ -977,12 +977,12 @@ impl architecture::Architecture fo Operand::R(r) => { let reg = self::Register::from(r); - res.push(InstructionTextToken::new(®.name(), Register)); + res.push(InstructionTextToken::new(reg.name(), Register)); } Operand::F(r) => { let reg = self::Register::from(r); - res.push(InstructionTextToken::new(®.name(), Register)); + res.push(InstructionTextToken::new(reg.name(), Register)); } Operand::I(i) => { match op { @@ -998,7 +998,10 @@ impl architecture::Architecture fo res.push(InstructionTextToken::new( &format!("0x{:x}", target), - CodeRelativeAddress(target), + CodeRelativeAddress { + value: target, + size: Some(self.address_size()), + }, )); } _ => { @@ -1007,7 +1010,10 @@ impl architecture::Architecture fo -0x8_0000..=-1 => format!("-0x{:x}", -i), _ => format!("0x{:x}", i), }, - Integer(i as u64), + Integer { + value: i as u64, + size: None, + }, )); } } @@ -1022,12 +1028,15 @@ impl architecture::Architecture fo } else { format!("0x{:x}", i) }, - Integer(i as u64), + Integer { + value: i as u64, + size: None, + }, )); - res.push(InstructionTextToken::new("(", Brace)); - res.push(InstructionTextToken::new(®.name(), Register)); - res.push(InstructionTextToken::new(")", Brace)); + res.push(InstructionTextToken::new("(", Brace { hash: None })); + res.push(InstructionTextToken::new(reg.name(), Register)); + res.push(InstructionTextToken::new(")", Brace { hash: None })); res.push(InstructionTextToken::new("", EndMemoryOperand)); } Operand::RM(r) => { diff --git a/plugins/dwarf/dwarfdump/src/lib.rs b/plugins/dwarf/dwarfdump/src/lib.rs index b1f60ea6b..c96fe06c5 100644 --- a/plugins/dwarf/dwarfdump/src/lib.rs +++ b/plugins/dwarf/dwarfdump/src/lib.rs @@ -15,7 +15,7 @@ use binaryninja::{ binaryview::{BinaryView, BinaryViewExt}, command::{register, Command}, - disassembly::{DisassemblyTextLine, InstructionTextToken, InstructionTextTokenContents}, + disassembly::{DisassemblyTextLine, InstructionTextToken, InstructionTextTokenKind}, flowgraph::{BranchType, EdgeStyle, FlowGraph, FlowGraphNode, FlowGraphOption}, }; use dwarfreader::is_valid; @@ -32,6 +32,7 @@ use gimli::{ Unit, UnitSectionOffset, }; +use binaryninja::disassembly::StringType; static PADDING: [&str; 23] = [ "", @@ -74,17 +75,19 @@ fn get_info_string( } .into_u64(); let label_string = format!("#0x{:08x}", label_value); - disassembly_lines.push(DisassemblyTextLine::from(vec![ + disassembly_lines.push(DisassemblyTextLine::new(vec![ InstructionTextToken::new( &label_string, - InstructionTextTokenContents::GotoLabel(label_value), + InstructionTextTokenKind::GotoLabel { + target: label_value, + }, ), - InstructionTextToken::new(":", InstructionTextTokenContents::Text), + InstructionTextToken::new(":", InstructionTextTokenKind::Text), ])); - disassembly_lines.push(DisassemblyTextLine::from(vec![InstructionTextToken::new( + disassembly_lines.push(DisassemblyTextLine::new(vec![InstructionTextToken::new( die_node.tag().static_string().unwrap(), - InstructionTextTokenContents::TypeName, // TODO : KeywordToken? + InstructionTextTokenKind::TypeName, // TODO : KeywordToken? )])); let mut attrs = die_node.attrs(); @@ -92,7 +95,7 @@ fn get_info_string( let mut attr_line: Vec = Vec::with_capacity(5); attr_line.push(InstructionTextToken::new( " ", - InstructionTextTokenContents::Indentation, + InstructionTextTokenKind::Indentation, )); let len; @@ -100,14 +103,22 @@ fn get_info_string( len = n.len(); attr_line.push(InstructionTextToken::new( n, - InstructionTextTokenContents::FieldName, + // TODO: Using field name for this is weird. + InstructionTextTokenKind::FieldName { + offset: 0, + type_names: vec![], + }, )); } else { // This is rather unlikely, I think len = 1; attr_line.push(InstructionTextToken::new( "?", - InstructionTextTokenContents::FieldName, + // TODO: Using field name for this is weird. + InstructionTextTokenKind::FieldName { + offset: 0, + type_names: vec![], + }, )); } @@ -115,40 +126,41 @@ fn get_info_string( if len < 18 { attr_line.push(InstructionTextToken::new( PADDING[18 - len], - InstructionTextTokenContents::Text, + InstructionTextTokenKind::Text, )); } attr_line.push(InstructionTextToken::new( " = ", - InstructionTextTokenContents::Text, + InstructionTextTokenKind::Text, )); if let Ok(Some(addr)) = dwarf.attr_address(unit, attr.value()) { let addr_string = format!("0x{:08x}", addr); attr_line.push(InstructionTextToken::new( &addr_string, - InstructionTextTokenContents::Integer(addr), + InstructionTextTokenKind::Integer { + value: addr, + size: None, + }, )); } else if let Ok(attr_reader) = dwarf.attr_string(unit, attr.value()) { if let Ok(attr_string) = attr_reader.to_string() { attr_line.push(InstructionTextToken::new( attr_string.as_ref(), - InstructionTextTokenContents::String({ - let (_, id, offset) = - dwarf.lookup_offset_id(attr_reader.offset_id()).unwrap(); - offset.into_u64() + view.section_by_name(id.name()).unwrap().start() - }), + InstructionTextTokenKind::String { + ty: StringType::Utf8String, + }, )); } else { attr_line.push(InstructionTextToken::new( "??", - InstructionTextTokenContents::Text, + InstructionTextTokenKind::Text, )); } } else if let Encoding(type_class) = attr.value() { attr_line.push(InstructionTextToken::new( type_class.static_string().unwrap(), - InstructionTextTokenContents::TypeName, + InstructionTextTokenKind::TypeName, )); } else if let UnitRef(offset) = attr.value() { let addr = match offset.to_unit_section_offset(unit) { @@ -159,17 +171,25 @@ fn get_info_string( let addr_string = format!("#0x{:08x}", addr); attr_line.push(InstructionTextToken::new( &addr_string, - InstructionTextTokenContents::GotoLabel(addr), + InstructionTextTokenKind::GotoLabel { + target: addr, + }, )); } else if let Flag(true) = attr.value() { attr_line.push(InstructionTextToken::new( "true", - InstructionTextTokenContents::Integer(1), + InstructionTextTokenKind::Integer { + value: 1, + size: None, + }, )); } else if let Flag(false) = attr.value() { attr_line.push(InstructionTextToken::new( "false", - InstructionTextTokenContents::Integer(1), + InstructionTextTokenKind::Integer { + value: 0, + size: None, + }, )); // Fall-back cases @@ -177,34 +197,46 @@ fn get_info_string( let value_string = format!("{}", value); attr_line.push(InstructionTextToken::new( &value_string, - InstructionTextTokenContents::Integer(value.into()), + InstructionTextTokenKind::Integer { + value: value as u64, + size: None, + }, )); } else if let Some(value) = attr.u16_value() { let value_string = format!("{}", value); attr_line.push(InstructionTextToken::new( &value_string, - InstructionTextTokenContents::Integer(value.into()), + InstructionTextTokenKind::Integer { + value: value as u64, + size: None, + }, )); } else if let Some(value) = attr.udata_value() { let value_string = format!("{}", value); attr_line.push(InstructionTextToken::new( &value_string, - InstructionTextTokenContents::Integer(value), + InstructionTextTokenKind::Integer { + value: value, + size: None, + }, )); } else if let Some(value) = attr.sdata_value() { let value_string = format!("{}", value); attr_line.push(InstructionTextToken::new( &value_string, - InstructionTextTokenContents::Integer(value as u64), + InstructionTextTokenKind::Integer { + value: value as u64, + size: None, + }, )); } else { let attr_string = format!("{:?}", attr.value()); attr_line.push(InstructionTextToken::new( &attr_string, - InstructionTextTokenContents::Text, + InstructionTextTokenKind::Text, )); } - disassembly_lines.push(DisassemblyTextLine::from(attr_line)); + disassembly_lines.push(DisassemblyTextLine::new(attr_line)); } disassembly_lines @@ -227,7 +259,7 @@ fn process_tree( let new_node = FlowGraphNode::new(graph); let attr_string = get_info_string(view, dwarf, unit, die_node.entry()); - new_node.set_disassembly_lines(&attr_string); + new_node.set_lines(attr_string); graph.append(&new_node); graph_parent.add_outgoing_edge( @@ -255,7 +287,7 @@ fn dump_dwarf(bv: &BinaryView) { graph.set_option(FlowGraphOption::FlowGraphUsesInstructionHighlights, true); let graph_root = FlowGraphNode::new(&graph); - graph_root.set_lines(vec!["Graph Root"]); + graph_root.set_lines(["Graph Root".into()]); graph.append(&graph_root); let endian = dwarfreader::get_endian(bv); diff --git a/plugins/warp/src/cache.rs b/plugins/warp/src/cache.rs index 2991644bb..ed1b78443 100644 --- a/plugins/warp/src/cache.rs +++ b/plugins/warp/src/cache.rs @@ -17,6 +17,7 @@ use std::sync::OnceLock; use warp::r#type::ComputedType; use warp::signature::function::constraints::FunctionConstraint; use warp::signature::function::{Function, FunctionGUID}; +use binaryninja::confidence::MAX_CONFIDENCE; pub static MATCHED_FUNCTION_CACHE: OnceLock> = OnceLock::new(); @@ -369,7 +370,7 @@ impl TypeRefCache { None => match type_ref.target(view) { Some(raw_ty) => { let computed_ty = - ComputedType::new(from_bn_type_internal(view, visited_refs, &raw_ty, 255)); + ComputedType::new(from_bn_type_internal(view, visited_refs, &raw_ty, MAX_CONFIDENCE)); self.cache .entry(ntr_id) .insert(Some(computed_ty)) diff --git a/plugins/warp/src/convert.rs b/plugins/warp/src/convert.rs index 2b89332e6..b5f935a36 100644 --- a/plugins/warp/src/convert.rs +++ b/plugins/warp/src/convert.rs @@ -6,7 +6,7 @@ use binaryninja::binaryview::{BinaryView, BinaryViewExt}; use binaryninja::callingconvention::CallingConvention as BNCallingConvention; use binaryninja::rc::Ref as BNRef; use binaryninja::symbol::{Symbol as BNSymbol, SymbolType as BNSymbolType}; -use binaryninja::confidence::Conf as BNConf; +use binaryninja::confidence::{Conf as BNConf, MAX_CONFIDENCE}; use binaryninja::types::{ BaseStructure as BNBaseStructure, EnumerationBuilder as BNEnumerationBuilder, FunctionParameter as BNFunctionParameter, MemberAccess as BNMemberAccess, MemberAccess, @@ -190,7 +190,7 @@ pub fn from_bn_type_internal( view, visited_refs, &BNType::named_type(&base_struct.ty), - 255, + MAX_CONFIDENCE, ); StructureMember { name: base_struct_ty.name.to_owned(), diff --git a/plugins/warp/src/lib.rs b/plugins/warp/src/lib.rs index ebe39b921..c805a712f 100644 --- a/plugins/warp/src/lib.rs +++ b/plugins/warp/src/lib.rs @@ -14,7 +14,7 @@ use std::path::PathBuf; use warp::signature::basic_block::BasicBlockGUID; use warp::signature::function::constraints::FunctionConstraints; use warp::signature::function::{Function, FunctionGUID}; - +use binaryninja::confidence::MAX_CONFIDENCE; use crate::cache::{ cached_adjacency_constraints, cached_call_site_constraints, cached_function_guid, }; @@ -49,7 +49,7 @@ pub fn build_function( Function { guid: cached_function_guid(func, llil), symbol: from_bn_symbol(&func.symbol()), - ty: from_bn_type(&func.view(), &bn_fn_ty, 255), + ty: from_bn_type(&func.view(), &bn_fn_ty, MAX_CONFIDENCE), constraints: FunctionConstraints { // NOTE: Adding adjacent only works if analysis is complete. // NOTE: We do not filter out adjacent functions here. diff --git a/rust/examples/flowgraph.rs b/rust/examples/flowgraph.rs index 8af66d319..2117e92fc 100644 --- a/rust/examples/flowgraph.rs +++ b/rust/examples/flowgraph.rs @@ -1,37 +1,37 @@ use binaryninja::{ binaryview::{BinaryView, BinaryViewExt}, - disassembly::{DisassemblyTextLine, InstructionTextToken, InstructionTextTokenContents}, + disassembly::{DisassemblyTextLine, InstructionTextToken, InstructionTextTokenKind}, flowgraph::{BranchType, EdgePenStyle, EdgeStyle, FlowGraph, FlowGraphNode, ThemeColor}, }; fn test_graph(view: &BinaryView) { let graph = FlowGraph::new(); - let disassembly_lines_a = vec![DisassemblyTextLine::from(vec![ - InstructionTextToken::new("Li", InstructionTextTokenContents::Text), - InstructionTextToken::new("ne", InstructionTextTokenContents::Text), - InstructionTextToken::new(" 1", InstructionTextTokenContents::Text), + let disassembly_lines_a = vec![DisassemblyTextLine::new(vec![ + InstructionTextToken::new("Li", InstructionTextTokenKind::Text), + InstructionTextToken::new("ne", InstructionTextTokenKind::Text), + InstructionTextToken::new(" 1", InstructionTextTokenKind::Text), ])]; let node_a = FlowGraphNode::new(&graph); - node_a.set_disassembly_lines(&disassembly_lines_a); + node_a.set_lines(disassembly_lines_a); let node_b = FlowGraphNode::new(&graph); - let disassembly_lines_b = vec![DisassemblyTextLine::from(&vec!["Li", "ne", " 2"])]; - node_b.set_disassembly_lines(&disassembly_lines_b); - - let node_c = FlowGraphNode::new(&graph); - node_c.set_lines(vec!["Line 3", "Line 4", "Line 5"]); + let disassembly_lines_b = vec![DisassemblyTextLine::new(vec![ + InstructionTextToken::new("Li", InstructionTextTokenKind::Text), + InstructionTextToken::new("ne", InstructionTextTokenKind::Text), + InstructionTextToken::new(" 2", InstructionTextTokenKind::Text), + ])]; + node_b.set_lines(disassembly_lines_b); graph.append(&node_a); graph.append(&node_b); - graph.append(&node_c); let edge = EdgeStyle::new(EdgePenStyle::DashDotDotLine, 2, ThemeColor::AddressColor); node_a.add_outgoing_edge(BranchType::UserDefinedBranch, &node_b, &edge); - node_a.add_outgoing_edge( + node_b.add_outgoing_edge( BranchType::UnconditionalBranch, - &node_c, + &node_a, &EdgeStyle::default(), ); diff --git a/rust/src/architecture.rs b/rust/src/architecture.rs index 64c7b5088..588901248 100644 --- a/rust/src/architecture.rs +++ b/rust/src/architecture.rs @@ -1481,7 +1481,7 @@ impl Architecture for CoreArchitecture { ) { let instr_text_tokens = std::slice::from_raw_parts(result, count) .iter() - .map(|x| InstructionTextToken::from_raw(x).to_owned()) + .map(Into::into) .collect(); BNFreeInstructionText(result, count); Some((consumed, instr_text_tokens)) @@ -2156,21 +2156,23 @@ where return false; }; - let res_tokens: Box<[_]> = res_tokens.into_boxed_slice(); + let res_tokens: Box<[BNInstructionTextToken]> = + res_tokens.into_iter().map(Into::into).collect(); unsafe { + // NOTE: Freed with `cb_free_instruction_text` let res_tokens = Box::leak(res_tokens); - let r_ptr = res_tokens.as_mut_ptr(); - let r_count = res_tokens.len(); - - *result = &mut (*r_ptr).0; - *count = r_count; + *result = res_tokens.as_mut_ptr(); + *count = res_tokens.len(); *len = res_size; } true } extern "C" fn cb_free_instruction_text(tokens: *mut BNInstructionTextToken, count: usize) { - let _tokens = unsafe { Box::from_raw(std::ptr::slice_from_raw_parts_mut(tokens, count)) }; + unsafe { + let raw_tokens = std::slice::from_raw_parts_mut(tokens, count); + let _ = Box::from_raw(raw_tokens); + } } extern "C" fn cb_instruction_llil( diff --git a/rust/src/binaryview.rs b/rust/src/binaryview.rs index c02ed2b00..47987f139 100644 --- a/rust/src/binaryview.rs +++ b/rust/src/binaryview.rs @@ -1160,7 +1160,7 @@ pub trait BinaryViewExt: BinaryViewBase { if handle.is_null() { return None; } - Some(TagType::from_raw(handle)) + Some(TagType::ref_from_raw(handle)) } } @@ -1174,7 +1174,7 @@ pub trait BinaryViewExt: BinaryViewBase { if handle.is_null() { return None; } - Some(Tag::from_raw(handle)) + Some(Tag::ref_from_raw(handle)) } } diff --git a/rust/src/disassembly.rs b/rust/src/disassembly.rs index 218ea750f..b34a01c6a 100644 --- a/rust/src/disassembly.rs +++ b/rust/src/disassembly.rs @@ -12,285 +12,320 @@ // See the License for the specific language governing permissions and // limitations under the License. -// TODO : Combine this with the architecture implementation - use binaryninjacore_sys::*; -use crate::string::BnString; -use crate::{BN_FULL_CONFIDENCE, BN_INVALID_EXPR}; +use crate::string::{raw_to_string, strings_to_string_list, BnString}; use crate::rc::*; +use crate::confidence::MAX_CONFIDENCE; +use crate::function::HighlightColor; +use crate::tags::Tag; +use crate::types::Type; use std::convert::From; -use std::ffi::CStr; -use std::fmt::Display; +use std::fmt::{Display, Formatter}; +pub type DisassemblyOption = BNDisassemblyOption; pub type InstructionTextTokenType = BNInstructionTextTokenType; -pub type InstructionTextTokenContext = BNInstructionTextTokenContext; - -// InstructionTextTokenType's; * = Implemented -// *TextToken = 0, -// *InstructionToken = 1, -// *OperandSeparatorToken = 2, -// *RegisterToken = 3, -// *IntegerToken = 4, -// *PossibleAddressToken = 5, -// *BeginMemoryOperandToken = 6, -// *EndMemoryOperandToken = 7, -// *FloatingPointToken = 8, -// AnnotationToken = 9, -// *CodeRelativeAddressToken = 10, -// ArgumentNameToken = 11, -// HexDumpByteValueToken = 12, -// HexDumpSkippedByteToken = 13, -// HexDumpInvalidByteToken = 14, -// HexDumpTextToken = 15, -// OpcodeToken = 16, -// *StringToken = 17, -// CharacterConstantToken = 18, -// *KeywordToken = 19, -// *TypeNameToken = 20, -// *FieldNameToken = 21, -// *NameSpaceToken = 22, -// NameSpaceSeparatorToken = 23, -// TagToken = 24, -// StructOffsetToken = 25, -// StructOffsetByteValueToken = 26, -// StructureHexDumpTextToken = 27, -// *GotoLabelToken = 28, -// CommentToken = 29, -// PossibleValueToken = 30, -// PossibleValueTypeToken = 31, -// ArrayIndexToken = 32, -// *IndentationToken = 33, -// UnknownMemoryToken = 34, -// CodeSymbolToken = 64, -// DataSymbolToken = 65, -// LocalVariableToken = 66, -// ImportToken = 67, -// AddressDisplayToken = 68, -// IndirectImportToken = 69, -// ExternalSymbolToken = 70, - -// TODO: How about we new type this pls. -#[repr(transparent)] -pub struct InstructionTextToken(pub(crate) BNInstructionTextToken); - -#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] -pub enum InstructionTextTokenContents { - Text, - Instruction, - OperandSeparator, - Register, - Integer(u64), // TODO size? - PossibleAddress(u64), // TODO size? - BeginMemoryOperand, - EndMemoryOperand, - FloatingPoint, - CodeRelativeAddress(u64), - String(u64), - Keyword, - TypeName, - FieldName, - NameSpace, - GotoLabel(u64), - Indentation, - Brace, +pub type StringType = BNStringType; + +#[derive(Clone, PartialEq, Debug, Default)] +pub struct DisassemblyTextLine { + pub address: u64, + pub instruction_index: usize, + pub tokens: Vec, + pub highlight: HighlightColor, + pub tags: Vec>, + pub type_info: DisassemblyTextLineTypeInfo, } -impl InstructionTextToken { - pub(crate) unsafe fn from_raw(raw: &BNInstructionTextToken) -> &Self { - // TODO: Insane!!! - std::mem::transmute(raw) +impl DisassemblyTextLine { + pub fn new(tokens: Vec) -> Self { + Self { + tokens, + ..Default::default() + } } - pub(crate) fn into_raw(self) -> BNInstructionTextToken { - // TODO: This is insane. - std::mem::ManuallyDrop::new(self).0 - } - - pub fn new(text: &str, contents: InstructionTextTokenContents) -> Self { - let (value, address) = match contents { - InstructionTextTokenContents::Integer(v) => (v, 0), - InstructionTextTokenContents::PossibleAddress(v) - | InstructionTextTokenContents::CodeRelativeAddress(v) - | InstructionTextTokenContents::GotoLabel(v) => (v, v), - InstructionTextTokenContents::String(v) => (v, 0), - _ => (0, 0), - }; - - let type_ = match contents { - InstructionTextTokenContents::Text => InstructionTextTokenType::TextToken, - InstructionTextTokenContents::Instruction => InstructionTextTokenType::InstructionToken, - InstructionTextTokenContents::OperandSeparator => { - InstructionTextTokenType::OperandSeparatorToken - } - InstructionTextTokenContents::Register => InstructionTextTokenType::RegisterToken, - InstructionTextTokenContents::Integer(_) => InstructionTextTokenType::IntegerToken, - InstructionTextTokenContents::PossibleAddress(_) => { - InstructionTextTokenType::PossibleAddressToken - } - InstructionTextTokenContents::BeginMemoryOperand => { - InstructionTextTokenType::BeginMemoryOperandToken - } - InstructionTextTokenContents::EndMemoryOperand => { - InstructionTextTokenType::EndMemoryOperandToken - } - InstructionTextTokenContents::FloatingPoint => { - InstructionTextTokenType::FloatingPointToken - } - InstructionTextTokenContents::CodeRelativeAddress(_) => { - InstructionTextTokenType::CodeRelativeAddressToken - } - InstructionTextTokenContents::String(_) => InstructionTextTokenType::StringToken, - InstructionTextTokenContents::Keyword => InstructionTextTokenType::KeywordToken, - InstructionTextTokenContents::TypeName => InstructionTextTokenType::TypeNameToken, - InstructionTextTokenContents::FieldName => InstructionTextTokenType::FieldNameToken, - InstructionTextTokenContents::NameSpace => InstructionTextTokenType::NameSpaceToken, - InstructionTextTokenContents::GotoLabel(_) => InstructionTextTokenType::GotoLabelToken, - InstructionTextTokenContents::Indentation => InstructionTextTokenType::IndentationToken, - InstructionTextTokenContents::Brace => InstructionTextTokenType::BraceToken, - }; - - let width = text.len() as u64; - - InstructionTextToken(BNInstructionTextToken { - type_, - text: BnString::new(text).into_raw(), - value, - width, - size: 0, - operand: 0xffff_ffff, - context: InstructionTextTokenContext::NoTokenContext, - confidence: BN_FULL_CONFIDENCE, - address, - typeNames: std::ptr::null_mut(), - namesCount: 0, - exprIndex: BN_INVALID_EXPR, - }) + // TODO: I dislike this API immensely, remove as soon as possible. + /// Convert into a raw [BNDisassemblyTextLine], use with caution. + /// + /// NOTE: The allocations here for tokens and tags MUST be freed by rust using [Self::free_raw]. + pub(crate) fn into_raw(self) -> BNDisassemblyTextLine { + // NOTE: The instruction text and type names fields are being leaked here. To be freed with [Self::free_raw]. + let tokens: Box<[BNInstructionTextToken]> = + self.tokens.into_iter().map(Into::into).collect(); + let tags: Box<[*mut BNTag]> = self + .tags + .into_iter() + .map(|t| { + // SAFETY: The tags ref will be temporarily incremented here, until [Self::free_raw] is called. + // SAFETY: This is so that tags lifetime is long enough, as we might be the last holders of the ref. + unsafe { BNNewTagReference(t.handle) }; + t.handle + }) + .collect(); + let tokens_len = tokens.len(); + let tags_len = tags.len(); + BNDisassemblyTextLine { + addr: self.address, + instrIndex: self.instruction_index, + // NOTE: Leaking tokens here to be freed with [Self::free_raw]. + tokens: Box::leak(tokens).as_mut_ptr(), + count: tokens_len, + highlight: self.highlight.into(), + // NOTE: Leaking tags here to be freed with [Self::free_raw]. + tags: Box::leak(tags).as_mut_ptr(), + tagCount: tags_len, + typeInfo: self.type_info.into(), + } } - pub fn set_value(&mut self, value: u64) { - self.0.value = value; + // TODO: I dislike this API immensely, remove as soon as possible. + /// Frees raw object created with [Self::into_raw], use with caution. + /// + /// NOTE: The allocations freed MUST have been created in rust using [Self::into_raw]. + pub(crate) unsafe fn free_raw(raw: BNDisassemblyTextLine) { + // Free the token list + let raw_tokens = std::slice::from_raw_parts_mut(raw.tokens, raw.count); + let boxed_tokens = Box::from_raw(raw_tokens); + for token in boxed_tokens { + // SAFETY: As we have leaked the token contents we need to now free them (text and typeNames). + InstructionTextToken::free_raw(token); + } + // Free the tag list + let raw_tags = std::slice::from_raw_parts_mut(raw.tags, raw.tagCount); + let boxed_tags = Box::from_raw(raw_tags); + for tag in boxed_tags { + // SAFETY: As we have incremented the tags ref in [Self::into_raw] we must now decrement. + unsafe { BNFreeTag(tag) }; + } } +} - pub fn set_context(&mut self, context: InstructionTextTokenContext) { - self.0.context = context; +impl From<&BNDisassemblyTextLine> for DisassemblyTextLine { + fn from(value: &BNDisassemblyTextLine) -> Self { + let raw_tokens = unsafe { std::slice::from_raw_parts(value.tokens, value.count) }; + let raw_tags = unsafe { std::slice::from_raw_parts(value.tags, value.tagCount) }; + let tokens: Vec<_> = raw_tokens.iter().map(Into::into).collect(); + // SAFETY: Increment the tag ref as we are going from ref to owned. + let tags: Vec<_> = raw_tags + .iter() + .map(|&t| unsafe { Tag::from_raw(t).to_owned() }) + .collect(); + Self { + address: value.addr, + instruction_index: value.instrIndex, + tokens, + highlight: value.highlight.into(), + tags, + type_info: value.typeInfo.into(), + } } +} - pub fn text(&self) -> &str { - unsafe { CStr::from_ptr(self.0.text) }.to_str().unwrap() +impl From<&str> for DisassemblyTextLine { + fn from(value: &str) -> Self { + Self::new(vec![InstructionTextToken::new( + value, + InstructionTextTokenKind::Text, + )]) } +} - pub fn contents(&self) -> InstructionTextTokenContents { - use self::BNInstructionTextTokenType::*; - use self::InstructionTextTokenContents::*; +impl From for DisassemblyTextLine { + fn from(value: String) -> Self { + Self::new(vec![InstructionTextToken::new( + value, + InstructionTextTokenKind::Text, + )]) + } +} - match self.0.type_ { - TextToken => Text, - InstructionToken => Instruction, - OperandSeparatorToken => OperandSeparator, - RegisterToken => Register, - IntegerToken => Integer(self.0.value), - PossibleAddressToken => PossibleAddress(self.0.value), - BeginMemoryOperandToken => BeginMemoryOperand, - EndMemoryOperandToken => EndMemoryOperand, - FloatingPointToken => FloatingPoint, - CodeRelativeAddressToken => CodeRelativeAddress(self.0.value), - _ => unimplemented!("woops"), +impl Display for DisassemblyTextLine { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + for token in &self.tokens { + write!(f, "{}", token)?; } + Ok(()) } +} - pub fn context(&self) -> InstructionTextTokenContext { - self.0.context +impl CoreArrayProvider for DisassemblyTextLine { + type Raw = BNDisassemblyTextLine; + type Context = (); + type Wrapped<'a> = Self; +} + +unsafe impl CoreArrayProviderInner for DisassemblyTextLine { + unsafe fn free(raw: *mut Self::Raw, count: usize, _context: &Self::Context) { + BNFreeDisassemblyTextLines(raw, count) } - pub fn size(&self) -> usize { - self.0.size + unsafe fn wrap_raw<'a>(raw: &'a Self::Raw, _context: &'a Self::Context) -> Self::Wrapped<'a> { + Self::from(raw) } +} - pub fn operand(&self) -> usize { - self.0.operand +#[derive(Default, Clone, PartialEq, Eq, Debug, Hash)] +pub struct DisassemblyTextLineTypeInfo { + pub has_type_info: bool, + pub parent_type: Option>, + pub field_index: usize, + pub offset: u64, +} + +impl From for DisassemblyTextLineTypeInfo { + fn from(value: BNDisassemblyTextLineTypeInfo) -> Self { + Self { + has_type_info: value.hasTypeInfo, + parent_type: match value.parentType.is_null() { + false => Some(unsafe { Type::ref_from_raw(value.parentType) }), + true => None, + }, + field_index: value.fieldIndex, + offset: value.offset, + } } +} - pub fn address(&self) -> u64 { - self.0.address +impl From<&BNDisassemblyTextLineTypeInfo> for DisassemblyTextLineTypeInfo { + fn from(value: &BNDisassemblyTextLineTypeInfo) -> Self { + Self { + has_type_info: value.hasTypeInfo, + parent_type: match value.parentType.is_null() { + false => Some(unsafe { Type::from_raw(value.parentType).to_owned() }), + true => None, + }, + field_index: value.fieldIndex, + offset: value.offset, + } } +} - pub fn expr_index(&self) -> usize { - self.0.exprIndex +impl From for BNDisassemblyTextLineTypeInfo { + fn from(value: DisassemblyTextLineTypeInfo) -> Self { + Self { + hasTypeInfo: value.has_type_info, + parentType: value + .parent_type + .map(|t| t.handle) + .unwrap_or(std::ptr::null_mut()), + fieldIndex: value.field_index, + offset: value.offset, + } } } -impl Default for InstructionTextToken { - fn default() -> Self { - InstructionTextToken(BNInstructionTextToken { - type_: InstructionTextTokenType::TextToken, - text: std::ptr::null_mut(), - value: 0, - width: 0, - size: 0, - operand: 0, - context: InstructionTextTokenContext::NoTokenContext, - confidence: BN_FULL_CONFIDENCE, +#[derive(Debug, Clone, PartialEq)] +pub struct InstructionTextToken { + pub address: u64, + pub text: String, + pub confidence: u8, + pub context: InstructionTextTokenContext, + // TODO: Document that this is not necessary to set and that this is valid in a limited context. + pub expr_index: usize, + pub kind: InstructionTextTokenKind, +} + +impl InstructionTextToken { + pub fn new(text: impl Into, kind: InstructionTextTokenKind) -> Self { + Self { address: 0, - typeNames: std::ptr::null_mut(), - namesCount: 0, - exprIndex: BN_INVALID_EXPR, - }) + text: text.into(), + confidence: MAX_CONFIDENCE, + context: InstructionTextTokenContext::Normal, + expr_index: 0, + kind, + } } -} -impl Clone for InstructionTextToken { - fn clone(&self) -> Self { - InstructionTextToken(BNInstructionTextToken { - type_: self.0.type_, - context: self.0.context, - address: self.0.address, - size: self.0.size, - operand: self.0.operand, - value: self.0.value, - width: 0, - text: BnString::new(self.text()).into_raw(), - confidence: 0xff, - typeNames: std::ptr::null_mut(), - namesCount: 0, - exprIndex: self.0.exprIndex, - }) + pub fn new_with_address( + address: u64, + text: impl Into, + kind: InstructionTextTokenKind, + ) -> Self { + Self { + address, + text: text.into(), + confidence: MAX_CONFIDENCE, + context: InstructionTextTokenContext::Normal, + expr_index: 0, + kind, + } + } + + pub(crate) unsafe fn free_raw(raw: BNInstructionTextToken) { + if !raw.text.is_null() { + BNFreeString(raw.text); + } + if !raw.typeNames.is_null() { + BNFreeStringList(raw.typeNames, raw.namesCount); + } } } -impl Display for InstructionTextToken { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "{}", self.text()) +impl From<&BNInstructionTextToken> for InstructionTextToken { + fn from(value: &BNInstructionTextToken) -> Self { + Self { + address: value.address, + text: raw_to_string(value.text).unwrap(), + confidence: value.confidence, + context: value.context.into(), + expr_index: value.exprIndex, + kind: InstructionTextTokenKind::from(value), + } } } -impl Drop for InstructionTextToken { - fn drop(&mut self) { - if !self.0.text.is_null() { - let _owned = unsafe { BnString::from_raw(self.0.text) }; - } - if !self.0.typeNames.is_null() && self.0.namesCount != 0 { - unsafe { BNFreeStringList(self.0.typeNames, self.0.namesCount) } +impl From for BNInstructionTextToken { + fn from(value: InstructionTextToken) -> Self { + let bn_text = BnString::new(value.text); + // These can be gathered from value.kind + let kind_value = value.kind.try_value().unwrap_or(0); + let operand = value.kind.try_operand().unwrap_or(0); + let size = value.kind.try_size().unwrap_or(0); + let type_names = value.kind.try_type_names().unwrap_or(vec![]); + Self { + type_: value.kind.into(), + // Expected to be freed with `InstructionTextToken::free_raw`. + text: bn_text.into_raw(), + value: kind_value, + // TODO: Where is this even used? + width: 0, + size, + operand, + context: value.context.into(), + confidence: value.confidence, + address: value.address, + // Expected to be freed with `InstructionTextToken::free_raw`. + typeNames: strings_to_string_list(&type_names), + namesCount: type_names.len(), + exprIndex: value.expr_index, } } } +impl Display for InstructionTextToken { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + self.text.fmt(f) + } +} + impl CoreArrayProvider for InstructionTextToken { type Raw = BNInstructionTextToken; type Context = (); - type Wrapped<'a> = &'a Self; + type Wrapped<'a> = Self; } unsafe impl CoreArrayProviderInner for InstructionTextToken { unsafe fn free(raw: *mut Self::Raw, count: usize, _context: &Self::Context) { + // SAFETY: The Array MUST have been allocated on the core side. This will `delete[] raw`. BNFreeInstructionText(raw, count) } unsafe fn wrap_raw<'a>(raw: &'a Self::Raw, _context: &'a Self::Context) -> Self::Wrapped<'a> { - // TODO: This MUST be removed. - std::mem::transmute(raw) + Self::from(raw) } } @@ -299,8 +334,10 @@ impl CoreArrayProvider for Array { type Context = (); type Wrapped<'a> = std::mem::ManuallyDrop; } + unsafe impl CoreArrayProviderInner for Array { unsafe fn free(raw: *mut Self::Raw, count: usize, _context: &Self::Context) { + // SAFETY: The Array MUST have been allocated on the core side. This will `delete[] raw`. BNFreeInstructionTextLines(raw, count) } @@ -310,187 +347,608 @@ unsafe impl CoreArrayProviderInner for Array { } } -// TODO: How about we new type this please! -#[repr(transparent)] -pub struct DisassemblyTextLine(pub(crate) BNDisassemblyTextLine); - -impl DisassemblyTextLine { - // TODO : this should probably be removed, though it doesn't actually hurt anything - pub fn debug_print(&self) { - println!("{}", self); - } - - pub fn addr(&self) -> u64 { - self.0.addr - } - - pub fn instr_idx(&self) -> usize { - self.0.instrIndex - } +#[derive(Clone, PartialEq, Debug)] +pub enum InstructionTextTokenKind { + Text, + Instruction, + OperandSeparator, + Register, + Integer { + value: u64, + /// Size of the integer + size: Option, + }, + PossibleAddress { + value: u64, + /// Size of the address + size: Option, + }, + BeginMemoryOperand, + EndMemoryOperand, + FloatingPoint { + value: f64, + /// Size of the floating point + size: Option, + }, + Annotation, + CodeRelativeAddress { + value: u64, + size: Option, + }, + ArgumentName { + // TODO: The argument index? + value: u64, + }, + HexDumpByteValue { + value: u8, + }, + HexDumpSkippedByte, + HexDumpInvalidByte, + HexDumpText { + // TODO: Explain what this does + width: u64, + }, + Opcode, + String { + ty: StringType, + }, + CharacterConstant, + Keyword, + TypeName, + FieldName { + /// Offset to this field in the respective structure + offset: u64, + /// Stores the type names for the referenced field name. + /// + /// This is typically just the members name. + /// For example MyStructure.my_field will have type_names be \["my_field"\]. + type_names: Vec, + }, + NameSpace, + NameSpaceSeparator, + Tag, + StructOffset { + /// Offset to this field in the respective structure + offset: u64, + // TODO: This makes no sense for struct offset, they dont have types? + /// Stores the type names for the referenced field name. + type_names: Vec, + }, + // TODO: Unused? + StructOffsetByteValue, + // TODO: Unused? + StructureHexDumpText { + // TODO: Explain what this does + width: u64, + }, + GotoLabel { + target: u64, + }, + Comment { + target: u64, + }, + PossibleValue { + value: u64, + }, + // TODO: This is weird, you pass the value type as the text, we should restrict this behavior and type it + PossibleValueType, + ArrayIndex { + index: u64, + }, + Indentation, + UnknownMemory, + EnumerationMember { + value: u64, + // TODO: Document where this type id comes from + // TODO: Can we type this to something other than a string? + /// The enumerations type id + type_id: Option, + }, + /// Operations like +, -, % + Operation, + BaseStructureName, + BaseStructureSeparator, + Brace { + // TODO: Explain what this is + hash: Option, + }, + CodeSymbol { + // TODO: Value of what? + value: u64, + // TODO: Size of what? + size: usize, // TODO: Operand? + }, + DataSymbol { + // TODO: Value of what? + value: u64, + // TODO: Size of what? + size: usize, // TODO: Operand? + }, + LocalVariable { + // This comes from the token.value + // TODO: Do we have a variable id type we can attach to this? + // TODO: Probably not considering this is used at multiple IL levels. + variable_id: u64, + /// NOTE: This is only valid in SSA form + ssa_version: usize, + }, + Import { + // TODO: Looks to be the target address from the import. + target: u64, + }, + AddressDisplay { + address: u64, + }, + // TODO: BAD + IndirectImport { + /// The address of the import + /// + /// If you want the address of the import token use [`InstructionTextToken::address`] instead. + target: u64, + /// Size of the instruction this token is apart of + size: usize, + // TODO: Type this + source_operand: usize, + }, + ExternalSymbol { + // TODO: Value of what? + value: u64, + }, + StackVariable { + // TODO: Do we have a variable id type we can attach to this? + // TODO: Probably not considering this is used at multiple IL levels. + variable_id: u64, + }, + AddressSeparator, + CollapsedInformation, + CollapseStateIndicator { + // TODO: Explain what this is + hash: Option, + }, +} - pub fn count(&self) -> usize { - self.0.count +impl InstructionTextTokenKind { + /// Mapping to the [`BNInstructionTextTokenType::value`] field. + fn try_value(&self) -> Option { + // TODO: Double check to make sure these are correct. + match self { + InstructionTextTokenKind::Integer { value, .. } => Some(*value), + InstructionTextTokenKind::PossibleAddress { value, .. } => Some(*value), + InstructionTextTokenKind::PossibleValue { value, .. } => Some(*value), + InstructionTextTokenKind::FloatingPoint { value, .. } => Some(*value as u64), + InstructionTextTokenKind::CodeRelativeAddress { value, .. } => Some(*value), + InstructionTextTokenKind::ArgumentName { value, .. } => Some(*value), + InstructionTextTokenKind::HexDumpByteValue { value, .. } => Some(*value as u64), + InstructionTextTokenKind::HexDumpText { width, .. } => Some(*width), + InstructionTextTokenKind::String { ty, .. } => Some(*ty as u64), + InstructionTextTokenKind::FieldName { offset, .. } => Some(*offset), + InstructionTextTokenKind::StructOffset { offset, .. } => Some(*offset), + InstructionTextTokenKind::StructureHexDumpText { width, .. } => Some(*width), + InstructionTextTokenKind::GotoLabel { target, .. } => Some(*target), + InstructionTextTokenKind::Comment { target, .. } => Some(*target), + InstructionTextTokenKind::ArrayIndex { index, .. } => Some(*index), + InstructionTextTokenKind::EnumerationMember { value, .. } => Some(*value), + InstructionTextTokenKind::LocalVariable { variable_id, .. } => Some(*variable_id), + InstructionTextTokenKind::Import { target, .. } => Some(*target), + InstructionTextTokenKind::AddressDisplay { address, .. } => Some(*address), + InstructionTextTokenKind::IndirectImport { target, .. } => Some(*target), + InstructionTextTokenKind::Brace { hash, .. } => *hash, + InstructionTextTokenKind::CodeSymbol { value, .. } => Some(*value), + InstructionTextTokenKind::DataSymbol { value, .. } => Some(*value), + InstructionTextTokenKind::ExternalSymbol { value, .. } => Some(*value), + InstructionTextTokenKind::StackVariable { variable_id, .. } => Some(*variable_id), + InstructionTextTokenKind::CollapseStateIndicator { hash, .. } => *hash, + _ => None, + } } - pub fn tag_count(&self) -> usize { - self.0.tagCount + /// Mapping to the [`BNInstructionTextTokenType::size`] field. + fn try_size(&self) -> Option { + match self { + InstructionTextTokenKind::Integer { size, .. } => *size, + InstructionTextTokenKind::FloatingPoint { size, .. } => *size, + InstructionTextTokenKind::PossibleAddress { size, .. } => *size, + InstructionTextTokenKind::CodeRelativeAddress { size, .. } => *size, + InstructionTextTokenKind::CodeSymbol { size, .. } => Some(*size), + InstructionTextTokenKind::DataSymbol { size, .. } => Some(*size), + InstructionTextTokenKind::IndirectImport { size, .. } => Some(*size), + _ => None, + } } - pub fn tokens(&self) -> Vec { - unsafe { - std::slice::from_raw_parts::(self.0.tokens, self.0.count) - .iter() - .map(|x| InstructionTextToken::from_raw(x).clone()) - .collect() + /// Mapping to the [`BNInstructionTextTokenType::operand`] field. + fn try_operand(&self) -> Option { + match self { + InstructionTextTokenKind::LocalVariable { ssa_version, .. } => Some(*ssa_version), + InstructionTextTokenKind::IndirectImport { source_operand, .. } => { + Some(*source_operand) + } + _ => None, } } -} -impl std::fmt::Display for DisassemblyTextLine { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - for token in self.tokens() { - write!(f, "{}", token.text())?; + /// Mapping to the [`BNInstructionTextTokenType::typeNames`] field. + fn try_type_names(&self) -> Option> { + match self { + InstructionTextTokenKind::FieldName { type_names, .. } => Some(type_names.clone()), + InstructionTextTokenKind::StructOffset { type_names, .. } => Some(type_names.clone()), + InstructionTextTokenKind::EnumerationMember { type_id, .. } => { + Some(vec![type_id.clone()?]) + } + _ => None, } - - Ok(()) } } -impl From> for DisassemblyTextLine { - fn from(tokens: Vec) -> Self { - let mut tokens: Box<[_]> = tokens.into(); - - // TODO: let (tokens_pointer, tokens_len, _) = unsafe { tokens.into_raw_parts() }; // Can't use for now...still a rust nightly feature - let tokens_pointer = tokens.as_mut_ptr(); - let tokens_len = tokens.len(); - std::mem::forget(tokens); - - DisassemblyTextLine(BNDisassemblyTextLine { - addr: 0, - instrIndex: BN_INVALID_EXPR, - tokens: tokens_pointer as *mut _, - count: tokens_len, - highlight: BNHighlightColor { - style: BNHighlightColorStyle::StandardHighlightColor, - color: BNHighlightStandardColor::NoHighlightColor, - mixColor: BNHighlightStandardColor::NoHighlightColor, - mix: 0, - r: 0, - g: 0, - b: 0, - alpha: 0, +impl From<&BNInstructionTextToken> for InstructionTextTokenKind { + fn from(value: &BNInstructionTextToken) -> Self { + match value.type_ { + BNInstructionTextTokenType::TextToken => Self::Text, + BNInstructionTextTokenType::InstructionToken => Self::Instruction, + BNInstructionTextTokenType::OperandSeparatorToken => Self::OperandSeparator, + BNInstructionTextTokenType::RegisterToken => Self::Register, + BNInstructionTextTokenType::IntegerToken => Self::Integer { + value: value.value, + size: match value.size { + 0 => None, + size => Some(size), + }, }, - tags: std::ptr::null_mut(), - tagCount: 0, - typeInfo: BNDisassemblyTextLineTypeInfo { - hasTypeInfo: false, - parentType: std::ptr::null_mut(), - fieldIndex: usize::MAX, - offset: 0, + BNInstructionTextTokenType::PossibleAddressToken => Self::PossibleAddress { + value: value.value, + size: match value.size { + 0 => None, + size => Some(size), + }, }, - }) - } -} - -impl From<&Vec<&str>> for DisassemblyTextLine { - fn from(string_tokens: &Vec<&str>) -> Self { - let mut tokens: Box<[BNInstructionTextToken]> = string_tokens - .iter() - .map(|&token| { - InstructionTextToken::new(token, InstructionTextTokenContents::Text).into_raw() - }) - .collect(); - - // let (tokens_pointer, tokens_len, _) = unsafe { tokens.into_raw_parts() }; // Can't use for now...still a rust nighly feature - let tokens_pointer = tokens.as_mut_ptr(); - let tokens_len = tokens.len(); - // TODO: ???? - std::mem::forget(tokens); - - DisassemblyTextLine(BNDisassemblyTextLine { - addr: 0, - instrIndex: BN_INVALID_EXPR, - tokens: tokens_pointer as *mut _, - count: tokens_len, - highlight: BNHighlightColor { - style: BNHighlightColorStyle::StandardHighlightColor, - color: BNHighlightStandardColor::NoHighlightColor, - mixColor: BNHighlightStandardColor::NoHighlightColor, - mix: 0, - r: 0, - g: 0, - b: 0, - alpha: 0, + BNInstructionTextTokenType::BeginMemoryOperandToken => Self::BeginMemoryOperand, + BNInstructionTextTokenType::EndMemoryOperandToken => Self::EndMemoryOperand, + BNInstructionTextTokenType::FloatingPointToken => Self::FloatingPoint { + value: value.value as f64, + size: match value.size { + 0 => None, + size => Some(size), + }, }, - tags: std::ptr::null_mut(), - tagCount: 0, - typeInfo: BNDisassemblyTextLineTypeInfo { - hasTypeInfo: false, - parentType: std::ptr::null_mut(), - fieldIndex: usize::MAX, - offset: 0, + BNInstructionTextTokenType::AnnotationToken => Self::Annotation, + BNInstructionTextTokenType::CodeRelativeAddressToken => Self::CodeRelativeAddress { + value: value.value, + size: match value.size { + 0 => None, + size => Some(size), + }, }, - }) - } -} - -impl Default for DisassemblyTextLine { - fn default() -> Self { - DisassemblyTextLine(BNDisassemblyTextLine { - addr: 0, - instrIndex: BN_INVALID_EXPR, - tokens: std::ptr::null_mut(), - count: 0, - highlight: BNHighlightColor { - style: BNHighlightColorStyle::StandardHighlightColor, - color: BNHighlightStandardColor::NoHighlightColor, - mixColor: BNHighlightStandardColor::NoHighlightColor, - mix: 0, - r: 0, - g: 0, - b: 0, - alpha: 0, + BNInstructionTextTokenType::ArgumentNameToken => { + Self::ArgumentName { value: value.value } + } + BNInstructionTextTokenType::HexDumpByteValueToken => Self::HexDumpByteValue { + value: value.value as u8, }, - tags: std::ptr::null_mut(), - tagCount: 0, - typeInfo: BNDisassemblyTextLineTypeInfo { - hasTypeInfo: false, - parentType: std::ptr::null_mut(), - fieldIndex: usize::MAX, - offset: 0, + BNInstructionTextTokenType::HexDumpSkippedByteToken => Self::HexDumpSkippedByte, + BNInstructionTextTokenType::HexDumpInvalidByteToken => Self::HexDumpInvalidByte, + BNInstructionTextTokenType::HexDumpTextToken => { + Self::HexDumpText { width: value.value } + } + BNInstructionTextTokenType::OpcodeToken => Self::Opcode, + BNInstructionTextTokenType::StringToken => Self::String { + ty: match value.value { + 0 => StringType::AsciiString, + 1 => StringType::Utf8String, + 2 => StringType::Utf16String, + 3 => StringType::Utf32String, + _ => unreachable!(), + }, }, - }) + BNInstructionTextTokenType::CharacterConstantToken => Self::CharacterConstant, + BNInstructionTextTokenType::KeywordToken => Self::Keyword, + BNInstructionTextTokenType::TypeNameToken => Self::TypeName, + BNInstructionTextTokenType::FieldNameToken => Self::FieldName { + offset: value.value, + type_names: { + // NOTE: Do not need to free, this is a part of the From<&> impl + let raw_names = + unsafe { std::slice::from_raw_parts(value.typeNames, value.namesCount) }; + raw_names.iter().filter_map(|&r| raw_to_string(r)).collect() + }, + }, + BNInstructionTextTokenType::NameSpaceToken => Self::NameSpace, + BNInstructionTextTokenType::NameSpaceSeparatorToken => Self::NameSpaceSeparator, + BNInstructionTextTokenType::TagToken => Self::Tag, + BNInstructionTextTokenType::StructOffsetToken => Self::StructOffset { + offset: value.value, + type_names: { + // NOTE: Do not need to free, this is a part of the From<&> impl + let raw_names = + unsafe { std::slice::from_raw_parts(value.typeNames, value.namesCount) }; + raw_names.iter().filter_map(|&r| raw_to_string(r)).collect() + }, + }, + BNInstructionTextTokenType::StructOffsetByteValueToken => Self::StructOffsetByteValue, + BNInstructionTextTokenType::StructureHexDumpTextToken => { + Self::StructureHexDumpText { width: value.value } + } + BNInstructionTextTokenType::GotoLabelToken => Self::GotoLabel { + target: value.value, + }, + BNInstructionTextTokenType::CommentToken => Self::Comment { + target: value.value, + }, + BNInstructionTextTokenType::PossibleValueToken => { + Self::PossibleValue { value: value.value } + } + // NOTE: See my comment about this type in [`Self::PossibleValueType`] + BNInstructionTextTokenType::PossibleValueTypeToken => Self::PossibleValueType, + BNInstructionTextTokenType::ArrayIndexToken => Self::ArrayIndex { index: value.value }, + BNInstructionTextTokenType::IndentationToken => Self::Indentation, + BNInstructionTextTokenType::UnknownMemoryToken => Self::UnknownMemory, + BNInstructionTextTokenType::EnumerationMemberToken => Self::EnumerationMember { + value: value.value, + type_id: { + // NOTE: Type id comes from value.typeNames, it should be the first one (hence the .next) + // NOTE: Do not need to free, this is a part of the From<&> impl + let raw_names = + unsafe { std::slice::from_raw_parts(value.typeNames, value.namesCount) }; + raw_names.iter().filter_map(|&r| raw_to_string(r)).next() + }, + }, + BNInstructionTextTokenType::OperationToken => Self::Operation, + BNInstructionTextTokenType::BaseStructureNameToken => Self::BaseStructureName, + BNInstructionTextTokenType::BaseStructureSeparatorToken => Self::BaseStructureSeparator, + BNInstructionTextTokenType::BraceToken => Self::Brace { + hash: match value.value { + 0 => None, + hash => Some(hash), + }, + }, + BNInstructionTextTokenType::CodeSymbolToken => Self::CodeSymbol { + value: value.value, + size: value.size, + }, + BNInstructionTextTokenType::DataSymbolToken => Self::DataSymbol { + value: value.value, + size: value.size, + }, + BNInstructionTextTokenType::LocalVariableToken => Self::LocalVariable { + variable_id: value.value, + ssa_version: value.operand, + }, + BNInstructionTextTokenType::ImportToken => Self::Import { + target: value.value, + }, + BNInstructionTextTokenType::AddressDisplayToken => Self::AddressDisplay { + address: value.value, + }, + BNInstructionTextTokenType::IndirectImportToken => Self::IndirectImport { + target: value.value, + size: value.size, + source_operand: value.operand, + }, + BNInstructionTextTokenType::ExternalSymbolToken => { + Self::ExternalSymbol { value: value.value } + } + BNInstructionTextTokenType::StackVariableToken => Self::StackVariable { + variable_id: value.value, + }, + BNInstructionTextTokenType::AddressSeparatorToken => Self::AddressSeparator, + BNInstructionTextTokenType::CollapsedInformationToken => Self::CollapsedInformation, + BNInstructionTextTokenType::CollapseStateIndicatorToken => { + Self::CollapseStateIndicator { + hash: match value.value { + 0 => None, + hash => Some(hash), + }, + } + } + } } } -impl Drop for DisassemblyTextLine { - fn drop(&mut self) { - if !self.0.tokens.is_null() { - let ptr = std::ptr::slice_from_raw_parts_mut(self.0.tokens, self.0.count); - let _ = unsafe { Box::from_raw(ptr) }; +impl From for BNInstructionTextTokenType { + fn from(value: InstructionTextTokenKind) -> Self { + match value { + InstructionTextTokenKind::Text => BNInstructionTextTokenType::TextToken, + InstructionTextTokenKind::Instruction => BNInstructionTextTokenType::InstructionToken, + InstructionTextTokenKind::OperandSeparator => { + BNInstructionTextTokenType::OperandSeparatorToken + } + InstructionTextTokenKind::Register => BNInstructionTextTokenType::RegisterToken, + InstructionTextTokenKind::Integer { .. } => BNInstructionTextTokenType::IntegerToken, + InstructionTextTokenKind::PossibleAddress { .. } => { + BNInstructionTextTokenType::PossibleAddressToken + } + InstructionTextTokenKind::BeginMemoryOperand => { + BNInstructionTextTokenType::BeginMemoryOperandToken + } + InstructionTextTokenKind::EndMemoryOperand => { + BNInstructionTextTokenType::EndMemoryOperandToken + } + InstructionTextTokenKind::FloatingPoint { .. } => { + BNInstructionTextTokenType::FloatingPointToken + } + InstructionTextTokenKind::Annotation => BNInstructionTextTokenType::AnnotationToken, + InstructionTextTokenKind::CodeRelativeAddress { .. } => { + BNInstructionTextTokenType::CodeRelativeAddressToken + } + InstructionTextTokenKind::ArgumentName { .. } => { + BNInstructionTextTokenType::ArgumentNameToken + } + InstructionTextTokenKind::HexDumpByteValue { .. } => { + BNInstructionTextTokenType::HexDumpByteValueToken + } + InstructionTextTokenKind::HexDumpSkippedByte => { + BNInstructionTextTokenType::HexDumpSkippedByteToken + } + InstructionTextTokenKind::HexDumpInvalidByte => { + BNInstructionTextTokenType::HexDumpInvalidByteToken + } + InstructionTextTokenKind::HexDumpText { .. } => { + BNInstructionTextTokenType::HexDumpTextToken + } + InstructionTextTokenKind::Opcode => BNInstructionTextTokenType::OpcodeToken, + InstructionTextTokenKind::String { .. } => BNInstructionTextTokenType::StringToken, + InstructionTextTokenKind::CharacterConstant => { + BNInstructionTextTokenType::CharacterConstantToken + } + InstructionTextTokenKind::Keyword => BNInstructionTextTokenType::KeywordToken, + InstructionTextTokenKind::TypeName => BNInstructionTextTokenType::TypeNameToken, + InstructionTextTokenKind::FieldName { .. } => { + BNInstructionTextTokenType::FieldNameToken + } + InstructionTextTokenKind::NameSpace => BNInstructionTextTokenType::NameSpaceToken, + InstructionTextTokenKind::NameSpaceSeparator => { + BNInstructionTextTokenType::NameSpaceSeparatorToken + } + InstructionTextTokenKind::Tag => BNInstructionTextTokenType::TagToken, + InstructionTextTokenKind::StructOffset { .. } => { + BNInstructionTextTokenType::StructOffsetToken + } + InstructionTextTokenKind::StructOffsetByteValue => { + BNInstructionTextTokenType::StructOffsetByteValueToken + } + InstructionTextTokenKind::StructureHexDumpText { .. } => { + BNInstructionTextTokenType::StructureHexDumpTextToken + } + InstructionTextTokenKind::GotoLabel { .. } => { + BNInstructionTextTokenType::GotoLabelToken + } + InstructionTextTokenKind::Comment { .. } => BNInstructionTextTokenType::CommentToken, + InstructionTextTokenKind::PossibleValue { .. } => { + BNInstructionTextTokenType::PossibleValueToken + } + InstructionTextTokenKind::PossibleValueType => { + BNInstructionTextTokenType::PossibleValueTypeToken + } + InstructionTextTokenKind::ArrayIndex { .. } => { + BNInstructionTextTokenType::ArrayIndexToken + } + InstructionTextTokenKind::Indentation => BNInstructionTextTokenType::IndentationToken, + InstructionTextTokenKind::UnknownMemory => { + BNInstructionTextTokenType::UnknownMemoryToken + } + InstructionTextTokenKind::EnumerationMember { .. } => { + BNInstructionTextTokenType::EnumerationMemberToken + } + InstructionTextTokenKind::Operation => BNInstructionTextTokenType::OperationToken, + InstructionTextTokenKind::BaseStructureName => { + BNInstructionTextTokenType::BaseStructureNameToken + } + InstructionTextTokenKind::BaseStructureSeparator => { + BNInstructionTextTokenType::BaseStructureSeparatorToken + } + InstructionTextTokenKind::Brace { .. } => BNInstructionTextTokenType::BraceToken, + InstructionTextTokenKind::CodeSymbol { .. } => { + BNInstructionTextTokenType::CodeSymbolToken + } + InstructionTextTokenKind::DataSymbol { .. } => { + BNInstructionTextTokenType::DataSymbolToken + } + InstructionTextTokenKind::LocalVariable { .. } => { + BNInstructionTextTokenType::LocalVariableToken + } + InstructionTextTokenKind::Import { .. } => BNInstructionTextTokenType::ImportToken, + InstructionTextTokenKind::AddressDisplay { .. } => { + BNInstructionTextTokenType::AddressDisplayToken + } + InstructionTextTokenKind::IndirectImport { .. } => { + BNInstructionTextTokenType::IndirectImportToken + } + InstructionTextTokenKind::ExternalSymbol { .. } => { + BNInstructionTextTokenType::ExternalSymbolToken + } + InstructionTextTokenKind::StackVariable { .. } => { + BNInstructionTextTokenType::StackVariableToken + } + InstructionTextTokenKind::AddressSeparator => { + BNInstructionTextTokenType::AddressSeparatorToken + } + InstructionTextTokenKind::CollapsedInformation => { + BNInstructionTextTokenType::CollapsedInformationToken + } + InstructionTextTokenKind::CollapseStateIndicator { .. } => { + BNInstructionTextTokenType::CollapseStateIndicatorToken + } } } } -impl CoreArrayProvider for DisassemblyTextLine { - type Raw = BNDisassemblyTextLine; - type Context = (); - type Wrapped<'a> = &'a Self; +#[derive(Clone, Copy, PartialEq, Eq, Debug)] +pub enum InstructionTextTokenContext { + Normal, + LocalVariable, + DataVariable, + FunctionReturn, + InstructionAddress, + ILInstructionIndex, + ConstData, + /// Use only with [`InstructionTextTokenKind::String`] + ConstStringData, + /// Use only with [`InstructionTextTokenKind::String`] + StringReference, + /// Use only with [`InstructionTextTokenKind::String`] + StringDataVariable, + /// For displaying strings which aren't associated with an address + /// + /// Use only with [`InstructionTextTokenKind::String`] + StringDisplay, + /// Use only with [`InstructionTextTokenKind::CollapseStateIndicator`] + Collapsed, + /// Use only with [`InstructionTextTokenKind::CollapseStateIndicator`] + Expanded, + /// Use only with [`InstructionTextTokenKind::CollapseStateIndicator`] + CollapsiblePadding, } -unsafe impl CoreArrayProviderInner for DisassemblyTextLine { - unsafe fn free(raw: *mut Self::Raw, count: usize, _context: &Self::Context) { - BNFreeDisassemblyTextLines(raw, count) +impl From for InstructionTextTokenContext { + fn from(value: BNInstructionTextTokenContext) -> Self { + match value { + BNInstructionTextTokenContext::NoTokenContext => Self::Normal, + BNInstructionTextTokenContext::LocalVariableTokenContext => Self::LocalVariable, + BNInstructionTextTokenContext::DataVariableTokenContext => Self::DataVariable, + BNInstructionTextTokenContext::FunctionReturnTokenContext => Self::FunctionReturn, + BNInstructionTextTokenContext::InstructionAddressTokenContext => { + Self::InstructionAddress + } + BNInstructionTextTokenContext::ILInstructionIndexTokenContext => { + Self::ILInstructionIndex + } + BNInstructionTextTokenContext::ConstDataTokenContext => Self::ConstData, + // For use with [`InstructionTextTokenKind::String`] + BNInstructionTextTokenContext::ConstStringDataTokenContext => Self::ConstStringData, + BNInstructionTextTokenContext::StringReferenceTokenContext => Self::StringReference, + BNInstructionTextTokenContext::StringDataVariableTokenContext => { + Self::StringDataVariable + } + BNInstructionTextTokenContext::StringDisplayTokenContext => Self::StringDisplay, + // For use with [`InstructionTextTokenKind::CollapseStateIndicator`] + BNInstructionTextTokenContext::ContentCollapsedContext => Self::Collapsed, + BNInstructionTextTokenContext::ContentExpandedContext => Self::Expanded, + BNInstructionTextTokenContext::ContentCollapsiblePadding => Self::CollapsiblePadding, + } } +} - unsafe fn wrap_raw<'a>(raw: &'a Self::Raw, _context: &'a Self::Context) -> Self::Wrapped<'a> { - // TODO: This MUST be removed. - std::mem::transmute(raw) +impl From for BNInstructionTextTokenContext { + fn from(value: InstructionTextTokenContext) -> Self { + match value { + InstructionTextTokenContext::Normal => Self::NoTokenContext, + InstructionTextTokenContext::LocalVariable => Self::LocalVariableTokenContext, + InstructionTextTokenContext::DataVariable => Self::DataVariableTokenContext, + InstructionTextTokenContext::FunctionReturn => Self::FunctionReturnTokenContext, + InstructionTextTokenContext::InstructionAddress => Self::InstructionAddressTokenContext, + InstructionTextTokenContext::ILInstructionIndex => Self::ILInstructionIndexTokenContext, + InstructionTextTokenContext::ConstData => Self::ConstDataTokenContext, + InstructionTextTokenContext::ConstStringData => Self::ConstStringDataTokenContext, + InstructionTextTokenContext::StringReference => Self::StringReferenceTokenContext, + InstructionTextTokenContext::StringDataVariable => Self::StringDataVariableTokenContext, + InstructionTextTokenContext::StringDisplay => Self::StringDisplayTokenContext, + InstructionTextTokenContext::Collapsed => Self::ContentCollapsedContext, + InstructionTextTokenContext::Expanded => Self::ContentExpandedContext, + InstructionTextTokenContext::CollapsiblePadding => Self::ContentCollapsiblePadding, + } } } -pub type DisassemblyOption = BNDisassemblyOption; - +// TODO: Make a builder for this. #[derive(PartialEq, Eq, Hash)] pub struct DisassemblySettings { pub(crate) handle: *mut BNDisassemblySettings, @@ -500,9 +958,7 @@ impl DisassemblySettings { pub fn new() -> Ref { unsafe { let handle = BNCreateDisassemblySettings(); - debug_assert!(!handle.is_null()); - Ref::new(Self { handle }) } } diff --git a/rust/src/flowgraph.rs b/rust/src/flowgraph.rs index c8505187a..0d490e577 100644 --- a/rust/src/flowgraph.rs +++ b/rust/src/flowgraph.rs @@ -68,21 +68,19 @@ impl<'a> FlowGraphNode<'a> { unsafe { FlowGraphNode::from_raw(BNCreateFlowGraphNode(graph.handle)) } } - pub fn set_disassembly_lines(&self, lines: &'a [DisassemblyTextLine]) { + pub fn set_lines(&self, lines: impl IntoIterator) { + // NOTE: This will create allocations and increment tag refs, we must call DisassemblyTextLine::free_raw + // TODO: This set of api's is really garbage, we should really do something about this. + let mut raw_lines: Vec = + lines.into_iter().map(|l| l.into_raw()).collect(); unsafe { - BNSetFlowGraphNodeLines(self.handle, lines.as_ptr() as *mut _, lines.len()); - // BNFreeDisassemblyTextLines(lines.as_ptr() as *mut _, lines.len()); // Shouldn't need...would be a double free? + BNSetFlowGraphNodeLines(self.handle, raw_lines.as_mut_ptr(), raw_lines.len()); + for raw_line in raw_lines { + DisassemblyTextLine::free_raw(raw_line); + } } } - pub fn set_lines(&self, lines: Vec<&str>) { - let lines = lines - .iter() - .map(|&line| DisassemblyTextLine::from(&vec![line])) - .collect::>(); - self.set_disassembly_lines(&lines); - } - pub fn add_outgoing_edge( &self, type_: BranchType, diff --git a/rust/src/function.rs b/rust/src/function.rs index 62b39a694..61f0df4b0 100644 --- a/rust/src/function.rs +++ b/rust/src/function.rs @@ -2671,6 +2671,15 @@ impl From for BNHighlightColor { } } +impl Default for HighlightColor { + fn default() -> Self { + Self::StandardHighlightColor { + color: HighlightStandardColor::NoHighlightColor, + alpha: 0, + } + } +} + // NOTE only exists as Array, cant be owned #[derive(Clone, Debug, Hash, Eq, PartialEq)] pub struct Comment { diff --git a/rust/src/linearview.rs b/rust/src/linearview.rs index 0aae4ba9e..ce0b39c2e 100644 --- a/rust/src/linearview.rs +++ b/rust/src/linearview.rs @@ -393,7 +393,7 @@ impl LinearDisassemblyLine { 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(raw.contents)); + let contents = mem::ManuallyDrop::new(DisassemblyTextLine::from(&raw.contents)); Self { t: linetype, function, diff --git a/rust/src/tags.rs b/rust/src/tags.rs index c7f32abf5..f0369625c 100644 --- a/rust/src/tags.rs +++ b/rust/src/tags.rs @@ -15,6 +15,7 @@ //! Interfaces for creating and modifying tags in a BinaryView. use binaryninjacore_sys::*; +use std::fmt::{Debug, Formatter}; use crate::architecture::CoreArchitecture; use crate::binaryview::BinaryView; @@ -23,20 +24,27 @@ use crate::function::Function; use crate::rc::*; use crate::string::*; +pub type TagTypeType = BNTagTypeType; +pub type TagReferenceType = BNTagReferenceType; + pub struct Tag { pub(crate) handle: *mut BNTag, } impl Tag { - pub(crate) unsafe fn from_raw(handle: *mut BNTag) -> Ref { + pub(crate) unsafe fn from_raw(handle: *mut BNTag) -> Self { debug_assert!(!handle.is_null()); + Self { handle } + } + pub(crate) unsafe fn ref_from_raw(handle: *mut BNTag) -> Ref { + debug_assert!(!handle.is_null()); Ref::new(Self { handle }) } pub fn new(t: &TagType, data: S) -> Ref { let data = data.into_bytes_with_nul(); - unsafe { Self::from_raw(BNCreateTag(t.handle, data.as_ref().as_ptr() as *mut _)) } + unsafe { Self::ref_from_raw(BNCreateTag(t.handle, data.as_ref().as_ptr() as *mut _)) } } pub fn id(&self) -> BnString { @@ -47,8 +55,8 @@ impl Tag { unsafe { BnString::from_raw(BNTagGetData(self.handle)) } } - pub fn t(&self) -> Ref { - unsafe { TagType::from_raw(BNTagGetType(self.handle)) } + pub fn ty(&self) -> Ref { + unsafe { TagType::ref_from_raw(BNTagGetType(self.handle)) } } pub fn set_data(&self, data: S) { @@ -59,6 +67,24 @@ impl Tag { } } +impl Debug for Tag { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + f.debug_struct("Tag") + .field("id", &self.id()) + .field("data", &self.data()) + .field("type", &self.ty()) + .finish() + } +} + +impl PartialEq for Tag { + fn eq(&self, other: &Self) -> bool { + self.id() == other.id() + } +} + +impl Eq for Tag {} + unsafe impl RefCountable for Tag { unsafe fn inc_ref(handle: &Self) -> Ref { Ref::new(Self { @@ -89,6 +115,7 @@ unsafe impl CoreArrayProviderInner for Tag { unsafe fn free(raw: *mut Self::Raw, count: usize, _context: &Self::Context) { BNFreeTagList(raw, count) } + unsafe fn wrap_raw<'a>(raw: &'a Self::Raw, context: &'a Self::Context) -> Self::Wrapped<'a> { Guard::new(Self { handle: *raw }, &context) } @@ -97,16 +124,13 @@ unsafe impl CoreArrayProviderInner for Tag { unsafe impl Send for Tag {} unsafe impl Sync for Tag {} -pub type TagTypeType = BNTagTypeType; - pub struct TagType { pub(crate) handle: *mut BNTagType, } impl TagType { - pub(crate) unsafe fn from_raw(handle: *mut BNTagType) -> Ref { + pub(crate) unsafe fn ref_from_raw(handle: *mut BNTagType) -> Ref { debug_assert!(!handle.is_null()); - Ref::new(Self { handle }) } @@ -115,7 +139,7 @@ impl TagType { name: N, icon: I, ) -> Ref { - let tag_type = unsafe { Self::from_raw(BNCreateTagType(view.handle)) }; + let tag_type = unsafe { Self::ref_from_raw(BNCreateTagType(view.handle)) }; tag_type.set_name(name); tag_type.set_icon(icon); tag_type @@ -155,7 +179,7 @@ impl TagType { unsafe { BNTagTypeSetVisible(self.handle, visible) } } - pub fn t(&self) -> TagTypeType { + pub fn ty(&self) -> TagTypeType { unsafe { BNTagTypeGetType(self.handle) } } @@ -171,6 +195,18 @@ impl TagType { } } +impl Debug for TagType { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + f.debug_struct("TagType") + .field("id", &self.id()) + .field("name", &self.name()) + .field("icon", &self.icon()) + .field("visible", &self.visible()) + .field("type", &self.ty()) + .finish() + } +} + unsafe impl RefCountable for TagType { unsafe fn inc_ref(handle: &Self) -> Ref { Ref::new(Self { @@ -194,15 +230,14 @@ impl ToOwned for TagType { unsafe impl Send for TagType {} unsafe impl Sync for TagType {} -pub type TagReferenceType = BNTagReferenceType; - +#[derive(Debug, Clone, PartialEq)] pub struct TagReference { - pub reference_type: TagReferenceType, - pub auto_defined: bool, - pub tag: Ref, pub arch: CoreArchitecture, pub func: Ref, pub addr: u64, + pub auto_defined: bool, + pub reference_type: TagReferenceType, + pub tag: Ref, } impl From<&BNTagReference> for TagReference { @@ -210,7 +245,7 @@ impl From<&BNTagReference> for TagReference { Self { reference_type: value.refType, auto_defined: value.autoDefined, - tag: unsafe { Tag::from_raw(value.tag).to_owned() }, + tag: unsafe { Tag::ref_from_raw(value.tag).to_owned() }, arch: unsafe { CoreArchitecture::from_raw(value.arch) }, func: unsafe { Function::from_raw(value.func).to_owned() }, addr: value.addr, diff --git a/rust/src/typeprinter.rs b/rust/src/typeprinter.rs index b9145b430..0f109e1d5 100644 --- a/rust/src/typeprinter.rs +++ b/rust/src/typeprinter.rs @@ -518,32 +518,35 @@ pub struct TypeDefinitionLine { pub field_index: usize, } -impl From for TypeDefinitionLine { - fn from(value: BNTypeDefinitionLine) -> Self { - Self { - line_type: value.lineType, - // TODO: Tokens are busted. - tokens: vec![], - ty: unsafe { Type::ref_from_raw(value.type_) }, - parent_type: match value.parentType.is_null() { - false => Some(unsafe { Type::ref_from_raw(value.parentType) }), - true => None, - }, - root_type: match value.rootType.is_null() { - false => Some(unsafe { Type::ref_from_raw(value.rootType) }), - true => None, - }, - root_type_name: match value.rootTypeName.is_null() { - false => Some(unsafe { BnString::from_raw(value.rootTypeName).to_string() }), - true => None, - }, - base_type: match value.baseType.is_null() { - false => Some(unsafe { NamedTypeReference::ref_from_raw(value.baseType) }), - true => None, - }, - base_offset: value.baseOffset, - offset: value.offset, - field_index: value.fieldIndex, +impl TypeDefinitionLine { + pub(crate) unsafe fn free_raw(raw: BNTypeDefinitionLine) { + if !raw.tokens.is_null() { + let tokens = std::ptr::slice_from_raw_parts_mut(raw.tokens, raw.count); + // SAFETY: raw.tokens must have been allocated by rust. + let boxed_tokens = Box::from_raw(tokens); + for token in boxed_tokens { + InstructionTextToken::free_raw(token); + } + } + if !raw.type_.is_null() { + // SAFETY: raw.type_ must have been ref incremented in conjunction with this free + BNFreeType(raw.type_); + } + if !raw.parentType.is_null() { + // SAFETY: raw.parentType must have been ref incremented in conjunction with this free + BNFreeType(raw.parentType); + } + if !raw.rootType.is_null() { + // SAFETY: raw.rootType must have been ref incremented in conjunction with this free + BNFreeType(raw.rootType); + } + if !raw.rootTypeName.is_null() { + // SAFETY: raw.rootTypeName must have been ref incremented in conjunction with this free + BNFreeString(raw.rootTypeName); + } + if !raw.baseType.is_null() { + // SAFETY: raw.baseType must have been ref incremented in conjunction with this free + BNFreeNamedTypeReference(raw.baseType); } } } @@ -552,8 +555,10 @@ impl From<&BNTypeDefinitionLine> for TypeDefinitionLine { fn from(value: &BNTypeDefinitionLine) -> Self { Self { line_type: value.lineType, - // TODO: Tokens are busted. - tokens: vec![], + tokens: { + let raw_tokens = unsafe { std::slice::from_raw_parts(value.tokens, value.count) }; + raw_tokens.iter().map(Into::into).collect() + }, ty: unsafe { Type::from_raw(value.type_).to_owned() }, parent_type: match value.parentType.is_null() { false => Some(unsafe { Type::from_raw(value.parentType).to_owned() }), @@ -580,26 +585,35 @@ impl From<&BNTypeDefinitionLine> for TypeDefinitionLine { impl From for BNTypeDefinitionLine { fn from(value: TypeDefinitionLine) -> Self { + // NOTE: This is leaking [BNInstructionTextToken::text], [BNInstructionTextToken::typeNames]. + let tokens: Box<[BNInstructionTextToken]> = + value.tokens.into_iter().map(Into::into).collect(); Self { lineType: value.line_type, - tokens: todo!("this is busted!"), - count: todo!("see above"), - type_: value.ty.handle, + count: tokens.len(), + // NOTE: This is leaking tokens. Must free with `cb_free_lines`. + tokens: Box::leak(tokens).as_mut_ptr(), + // NOTE: This is leaking a ref to ty. Must free with `cb_free_lines`. + type_: unsafe { BNNewTypeReference(value.ty.handle) }, + // NOTE: This is leaking a ref to parent_type. Must free with `cb_free_lines`. parentType: value .parent_type - .map(|t| t.handle) + .map(|t| unsafe { BNNewTypeReference(t.handle) }) .unwrap_or(std::ptr::null_mut()), + // NOTE: This is leaking a ref to root_type. Must free with `cb_free_lines`. rootType: value .root_type - .map(|t| t.handle) + .map(|t| unsafe { BNNewTypeReference(t.handle) }) .unwrap_or(std::ptr::null_mut()), + // NOTE: This is leaking root_type_name. Must free with `cb_free_lines`. rootTypeName: value .root_type_name .map(|s| BnString::new(s).into_raw()) .unwrap_or(std::ptr::null_mut()), + // NOTE: This is leaking a ref to base_type. Must free with `cb_free_lines`. baseType: value .base_type - .map(|t| t.handle) + .map(|t| unsafe { BNNewNamedTypeReference(t.handle) }) .unwrap_or(std::ptr::null_mut()), baseOffset: value.base_offset, offset: value.offset, @@ -647,14 +661,11 @@ unsafe extern "C" fn cb_get_type_tokens( escaping, ); if let Some(inner_result) = inner_result { - // SAFETY dropped by the cb_free_tokens - let inner_result = Box::leak(inner_result.into_boxed_slice()); - *result_count = inner_result.len(); - // TODO: At some point this will not be the case! - // SAFETY InstructionTextToken and BNInstructionTextToken are transparent - let inner_result_ptr = - inner_result.as_ptr() as *mut InstructionTextToken as *mut BNInstructionTextToken; - *result = inner_result_ptr; + let raw_text_tokens: Box<[BNInstructionTextToken]> = + inner_result.into_iter().map(Into::into).collect(); + *result_count = raw_text_tokens.len(); + // NOTE: Dropped by the cb_free_tokens + *result = Box::leak(raw_text_tokens).as_mut_ptr(); true } else { *result = std::ptr::null_mut(); @@ -688,13 +699,11 @@ unsafe extern "C" fn cb_get_type_tokens_before_name( escaping, ); if let Some(inner_result) = inner_result { - // SAFETY dropped by the cb_free_tokens - let inner_result = Box::leak(inner_result.into_boxed_slice()); - *result_count = inner_result.len(); - // SAFETY InstructionTextToken and BNInstructionTextToken are transparent - let inner_result_ptr = - inner_result.as_ptr() as *mut InstructionTextToken as *mut BNInstructionTextToken; - *result = inner_result_ptr; + let raw_text_tokens: Box<[BNInstructionTextToken]> = + inner_result.into_iter().map(Into::into).collect(); + *result_count = raw_text_tokens.len(); + // NOTE: Dropped by the cb_free_tokens + *result = Box::leak(raw_text_tokens).as_mut_ptr(); true } else { *result = std::ptr::null_mut(); @@ -728,14 +737,11 @@ unsafe extern "C" fn cb_get_type_tokens_after_name( escaping, ); if let Some(inner_result) = inner_result { - // NOTE: Dropped by `cb_free_tokens` - let inner_result = Box::leak(inner_result.into_boxed_slice()); - *result_count = inner_result.len(); - // TODO: At some point this will not be the case! - // SAFETY InstructionTextToken and BNInstructionTextToken are transparent - let inner_result_ptr = - inner_result.as_ptr() as *mut InstructionTextToken as *mut BNInstructionTextToken; - *result = inner_result_ptr; + let raw_text_tokens: Box<[BNInstructionTextToken]> = + inner_result.into_iter().map(Into::into).collect(); + *result_count = raw_text_tokens.len(); + // NOTE: Dropped by the cb_free_tokens + *result = Box::leak(raw_text_tokens).as_mut_ptr(); true } else { *result = std::ptr::null_mut(); @@ -852,8 +858,8 @@ unsafe extern "C" fn cb_get_type_lines( escaping, ); if let Some(inner_result) = inner_result { - *result_count = inner_result.len(); let boxed_raw_lines: Box<[_]> = inner_result.into_iter().map(Into::into).collect(); + *result_count = boxed_raw_lines.len(); // NOTE: Dropped by `cb_free_lines` *result = Box::leak(boxed_raw_lines).as_mut_ptr(); true @@ -907,8 +913,12 @@ unsafe extern "C" fn cb_free_tokens( tokens: *mut BNInstructionTextToken, count: usize, ) { - let errors = std::ptr::slice_from_raw_parts_mut(tokens, count); - let _ = Box::from_raw(errors); + let tokens = std::ptr::slice_from_raw_parts_mut(tokens, count); + // SAFETY: tokens must have been allocated by rust. + let boxed_tokens = Box::from_raw(tokens); + for token in boxed_tokens { + InstructionTextToken::free_raw(token); + } } unsafe extern "C" fn cb_free_lines( @@ -916,6 +926,9 @@ unsafe extern "C" fn cb_free_lines( lines: *mut BNTypeDefinitionLine, count: usize, ) { - let errors = std::ptr::slice_from_raw_parts_mut(lines, count); - let _ = Box::from_raw(errors); + let lines = std::ptr::slice_from_raw_parts_mut(lines, count); + let boxes_lines = Box::from_raw(lines); + for line in boxes_lines { + TypeDefinitionLine::free_raw(line); + } }