From eab64f366a56d75981c027652d2b8122a122a8aa Mon Sep 17 00:00:00 2001 From: Mason Reed Date: Sun, 22 Dec 2024 01:21:02 -0500 Subject: [PATCH 01/93] Cleanup rust api Moves a bunch of stuff out of src/types.rs that did not belong: - Confidence - Variable - Function specific stuff - Refactored InstructionInfo, see the msp430 and riscv examples. - Renamed Function::from_raw to Function::ref_from_raw and fixed places where the ref was incremented twice - Fixed FunctionRecognizer leaking functions (see above) - Fixed some apis incorrectly returning Result where Option is clearer - Started to move destructured types to the From trait for converting to an from ffi types, see Location for an example - Started to remove bad module level imports (importing std modules like mem all over the place) - Moved some wrapper handle types to named handle field (this improves readability), see CoreArchitecture for an example - Removed some unintuitive getters, this is bad practice for Rust code, just access the field directly, see DataVariable for an example - General code cleanup, purposely did not run rustfmt, that will be a single seperate commit --- arch/msp430/src/architecture.rs | 83 +- arch/riscv/src/lib.rs | 72 +- plugins/warp/benches/convert.rs | 1 - plugins/warp/src/cache.rs | 6 +- plugins/warp/src/convert.rs | 23 +- plugins/warp/src/lib.rs | 2 +- rust/examples/dwarf/dwarf_export/src/lib.rs | 38 +- .../dwarf/dwarf_import/src/die_handlers.rs | 3 +- .../dwarf/dwarf_import/src/dwarfdebuginfo.rs | 17 +- .../dwarf/dwarf_import/src/helpers.rs | 4 +- rust/examples/dwarf/dwarf_import/src/lib.rs | 18 +- rust/examples/dwarf/dwarfdump/src/lib.rs | 2 +- rust/examples/dwarf/shared/src/lib.rs | 28 +- rust/examples/hlil_visitor/src/main.rs | 2 +- rust/examples/idb_import/src/types.rs | 10 +- rust/examples/mlil_visitor/src/main.rs | 2 +- rust/examples/pdb-ng/src/lib.rs | 2 +- rust/examples/pdb-ng/src/parser.rs | 37 +- rust/examples/pdb-ng/src/struct_grouper.rs | 9 +- rust/examples/pdb-ng/src/symbol_parser.rs | 58 +- rust/examples/pdb-ng/src/type_parser.rs | 109 +- rust/src/architecture.rs | 937 ++++--- rust/src/basicblock.rs | 6 +- rust/src/binaryview.rs | 121 +- rust/src/callingconvention.rs | 93 +- rust/src/component.rs | 14 +- rust/src/confidence.rs | 270 ++ rust/src/custombinaryview.rs | 64 +- rust/src/databuffer.rs | 5 +- rust/src/debuginfo.rs | 197 +- rust/src/demangle.rs | 16 +- rust/src/filemetadata.rs | 18 +- rust/src/function.rs | 566 ++-- rust/src/functionrecognizer.rs | 11 +- rust/src/hlil/function.rs | 2 +- rust/src/hlil/instruction.rs | 14 +- rust/src/hlil/lift.rs | 2 +- rust/src/hlil/operation.rs | 7 +- rust/src/lib.rs | 169 +- rust/src/linearview.rs | 2 +- rust/src/llil/function.rs | 10 +- rust/src/llil/lifting.rs | 108 +- rust/src/mlil/function.rs | 194 +- rust/src/mlil/instruction.rs | 178 +- rust/src/mlil/lift.rs | 3 +- rust/src/mlil/operation.rs | 5 +- rust/src/operand_iter.rs | 28 +- rust/src/platform.rs | 9 +- rust/src/references.rs | 20 +- rust/src/relocation.rs | 14 +- rust/src/section.rs | 10 +- rust/src/string.rs | 40 +- rust/src/tags.rs | 2 +- rust/src/typelibrary.rs | 10 +- rust/src/types.rs | 2456 +++-------------- rust/src/variable.rs | 842 ++++++ rust/src/workflow.rs | 4 +- 57 files changed, 3410 insertions(+), 3563 deletions(-) create mode 100644 rust/src/confidence.rs create mode 100644 rust/src/variable.rs diff --git a/arch/msp430/src/architecture.rs b/arch/msp430/src/architecture.rs index c37358eac..a5ba1b91a 100644 --- a/arch/msp430/src/architecture.rs +++ b/arch/msp430/src/architecture.rs @@ -18,6 +18,7 @@ use msp430_asm::{ }; use log::error; +use binaryninja::architecture::BranchKind; const MIN_MNEMONIC: usize = 9; @@ -71,7 +72,7 @@ impl Architecture for Msp430 { self.max_instr_len() } - fn associated_arch_by_addr(&self, _addr: &mut u64) -> CoreArchitecture { + fn associated_arch_by_addr(&self, _addr: u64) -> CoreArchitecture { self.handle } @@ -83,136 +84,118 @@ impl Architecture for Msp430 { match inst { Instruction::Jnz(inst) => { info.add_branch( - BranchInfo::True(offset_to_absolute(addr, inst.offset())), - Some(self.handle), + BranchKind::True(offset_to_absolute(addr, inst.offset())), ); info.add_branch( - BranchInfo::False(addr + inst.size() as u64), - Some(self.handle), + BranchKind::False(addr + inst.size() as u64), ); } Instruction::Jz(inst) => { info.add_branch( - BranchInfo::True(offset_to_absolute(addr, inst.offset())), - Some(self.handle), + BranchKind::True(offset_to_absolute(addr, inst.offset())), ); info.add_branch( - BranchInfo::False(addr + inst.size() as u64), - Some(self.handle), + BranchKind::False(addr + inst.size() as u64), ); } Instruction::Jlo(inst) => { info.add_branch( - BranchInfo::True(offset_to_absolute(addr, inst.offset())), - Some(self.handle), + BranchKind::True(offset_to_absolute(addr, inst.offset())), ); info.add_branch( - BranchInfo::False(addr + inst.size() as u64), - Some(self.handle), + BranchKind::False(addr + inst.size() as u64), ); } Instruction::Jc(inst) => { info.add_branch( - BranchInfo::True(offset_to_absolute(addr, inst.offset())), - Some(self.handle), + BranchKind::True(offset_to_absolute(addr, inst.offset())), ); info.add_branch( - BranchInfo::False(addr + inst.size() as u64), - Some(self.handle), + BranchKind::False(addr + inst.size() as u64), ); } Instruction::Jn(inst) => { info.add_branch( - BranchInfo::True(offset_to_absolute(addr, inst.offset())), - Some(self.handle), + BranchKind::True(offset_to_absolute(addr, inst.offset())), ); info.add_branch( - BranchInfo::False(addr + inst.size() as u64), - Some(self.handle), + BranchKind::False(addr + inst.size() as u64), ); } Instruction::Jge(inst) => { info.add_branch( - BranchInfo::True(offset_to_absolute(addr, inst.offset())), - Some(self.handle), + BranchKind::True(offset_to_absolute(addr, inst.offset())), ); info.add_branch( - BranchInfo::False(addr + inst.size() as u64), - Some(self.handle), + BranchKind::False(addr + inst.size() as u64), ); } Instruction::Jl(inst) => { info.add_branch( - BranchInfo::True(offset_to_absolute(addr, inst.offset())), - Some(self.handle), + BranchKind::True(offset_to_absolute(addr, inst.offset())), ); info.add_branch( - BranchInfo::False(addr + inst.size() as u64), - Some(self.handle), + BranchKind::False(addr + inst.size() as u64), ); } Instruction::Jmp(inst) => { info.add_branch( - BranchInfo::Unconditional(offset_to_absolute(addr, inst.offset())), - Some(self.handle), + BranchKind::Unconditional(offset_to_absolute(addr, inst.offset())), ); } Instruction::Br(inst) => match inst.destination() { Some(Operand::RegisterDirect(_)) => { - info.add_branch(BranchInfo::Indirect, Some(self.handle)) + info.add_branch(BranchKind::Indirect) } Some(Operand::Indexed(_)) => { - info.add_branch(BranchInfo::Indirect, Some(self.handle)) + info.add_branch(BranchKind::Indirect) } Some(Operand::Absolute(value)) => info.add_branch( - BranchInfo::Unconditional(*value as u64), - Some(self.handle), + BranchKind::Unconditional(*value as u64), ), Some(Operand::Symbolic(offset)) => info.add_branch( - BranchInfo::Unconditional((addr as i64 + *offset as i64) as u64), - Some(self.handle), + BranchKind::Unconditional((addr as i64 + *offset as i64) as u64), ), Some(Operand::Immediate(addr)) => info - .add_branch(BranchInfo::Unconditional(*addr as u64), Some(self.handle)), + .add_branch(BranchKind::Unconditional(*addr as u64)), Some(Operand::Constant(_)) => { - info.add_branch(BranchInfo::Unconditional(addr), Some(self.handle)) + info.add_branch(BranchKind::Unconditional(addr)) } Some(Operand::RegisterIndirect(_)) | Some(Operand::RegisterIndirectAutoIncrement(_)) => { - info.add_branch(BranchInfo::Indirect, Some(self.handle)) + info.add_branch(BranchKind::Indirect) } None => {} }, Instruction::Call(inst) => match inst.source() { Operand::RegisterDirect(_) => { - info.add_branch(BranchInfo::Indirect, Some(self.handle)) + info.add_branch(BranchKind::Indirect) } Operand::Indexed(_) => { - info.add_branch(BranchInfo::Indirect, Some(self.handle)) + info.add_branch(BranchKind::Indirect) } Operand::Absolute(value) => { - info.add_branch(BranchInfo::Call(*value as u64), Some(self.handle)) + info.add_branch(BranchKind::Call(*value as u64)) } Operand::Symbolic(offset) => info.add_branch( - BranchInfo::Call((addr as i64 + *offset as i64) as u64), - Some(self.handle), + BranchKind::Call((addr as i64 + *offset as i64) as u64), ), Operand::Immediate(addr) => { - info.add_branch(BranchInfo::Call(*addr as u64), Some(self.handle)) + info.add_branch(BranchKind::Call(*addr as u64)) } Operand::Constant(_) => { - info.add_branch(BranchInfo::Call(addr), Some(self.handle)) + info.add_branch(BranchKind::Call(addr)) } Operand::RegisterIndirect(_) | Operand::RegisterIndirectAutoIncrement(_) => { - info.add_branch(BranchInfo::Indirect, Some(self.handle)) + info.add_branch(BranchKind::Indirect) } }, Instruction::Reti(_) => { - info.add_branch(BranchInfo::FunctionReturn, Some(self.handle)); + info.add_branch(BranchKind::FunctionReturn); } Instruction::Ret(_) => { - info.add_branch(BranchInfo::FunctionReturn, Some(self.handle)); + info.add_branch(BranchKind::FunctionReturn); } _ => {} } diff --git a/arch/riscv/src/lib.rs b/arch/riscv/src/lib.rs index ca2774ca3..e3952b7ec 100644 --- a/arch/riscv/src/lib.rs +++ b/arch/riscv/src/lib.rs @@ -30,7 +30,7 @@ use binaryninja::{ RelocationType, }, symbol::{Symbol, SymbolType}, - types::{max_confidence, min_confidence, Conf, NameAndType, Type}, + types::{NameAndType, Type}, }; use log::LevelFilter; use std::borrow::Cow; @@ -43,6 +43,8 @@ use riscv_dis::{ FloatReg, FloatRegType, Instr, IntRegType, Op, RegFile, Register as RiscVRegister, RiscVDisassembler, RoundMode, }; +use binaryninja::architecture::BranchKind; +use binaryninja::confidence::{Conf, MAX_CONFIDENCE, MIN_CONFIDENCE}; enum RegType { Integer(u32), @@ -509,7 +511,7 @@ impl architecture::Intrinsic for RiscVIntrinsic { } } - fn inputs(&self) -> Vec> { + fn inputs(&self) -> Vec { match self.id { Intrinsic::Uret | Intrinsic::Sret | Intrinsic::Mret | Intrinsic::Wfi => { vec![] @@ -517,17 +519,15 @@ impl architecture::Intrinsic for RiscVIntrinsic { Intrinsic::Csrrd => { vec![NameAndType::new( "csr", - &Type::int(4, false), - max_confidence(), + Conf::new(Type::int(4, false), MAX_CONFIDENCE) )] } Intrinsic::Csrrw | Intrinsic::Csrwr | Intrinsic::Csrrs | Intrinsic::Csrrc => { vec![ - NameAndType::new("csr", &Type::int(4, false), max_confidence()), + NameAndType::new("csr", Conf::new(Type::int(4, false), MAX_CONFIDENCE)), NameAndType::new( "value", - &Type::int(::Int::width(), false), - min_confidence(), + Conf::new(Type::int(::Int::width(), false), MIN_CONFIDENCE) ), ] } @@ -541,8 +541,8 @@ impl architecture::Intrinsic for RiscVIntrinsic { | Intrinsic::Fmin(size) | Intrinsic::Fmax(size) => { vec![ - NameAndType::new("", &Type::float(size as usize), max_confidence()), - NameAndType::new("", &Type::float(size as usize), max_confidence()), + NameAndType::new("", Conf::new(Type::float(size as usize), MAX_CONFIDENCE)), + NameAndType::new("", Conf::new(Type::float(size as usize), MAX_CONFIDENCE)), ] } Intrinsic::Fsqrt(size, _) @@ -552,26 +552,23 @@ impl architecture::Intrinsic for RiscVIntrinsic { | Intrinsic::FcvtFToU(size, _, _) => { vec![NameAndType::new( "", - &Type::float(size as usize), - max_confidence(), + Conf::new(Type::float(size as usize), MAX_CONFIDENCE) )] } Intrinsic::FcvtIToF(size, _, _) => { vec![NameAndType::new( "", - &Type::int(size as usize, true), - max_confidence(), + Conf::new(Type::int(size as usize, true), MAX_CONFIDENCE) )] } Intrinsic::FcvtUToF(size, _, _) => { vec![NameAndType::new( "", - &Type::int(size as usize, false), - max_confidence(), + Conf::new(Type::int(size as usize, false), MAX_CONFIDENCE) )] } Intrinsic::Fence => { - vec![NameAndType::new("", &Type::int(4, false), min_confidence())] + vec![NameAndType::new("", Conf::new(Type::int(4, false), MIN_CONFIDENCE))] } } } @@ -589,7 +586,7 @@ impl architecture::Intrinsic for RiscVIntrinsic { Intrinsic::Csrrw | Intrinsic::Csrrd | Intrinsic::Csrrs | Intrinsic::Csrrc => { vec![Conf::new( Type::int(::Int::width(), false), - min_confidence(), + MIN_CONFIDENCE, )] } Intrinsic::Fadd(size, _) @@ -605,16 +602,16 @@ impl architecture::Intrinsic for RiscVIntrinsic { | Intrinsic::FcvtFToF(_, size, _) | Intrinsic::FcvtIToF(_, size, _) | Intrinsic::FcvtUToF(_, size, _) => { - vec![Conf::new(Type::float(size as usize), max_confidence())] + vec![Conf::new(Type::float(size as usize), MAX_CONFIDENCE)] } Intrinsic::Fclass(_) => { - vec![Conf::new(Type::int(4, false), min_confidence())] + vec![Conf::new(Type::int(4, false), MIN_CONFIDENCE)] } Intrinsic::FcvtFToI(_, size, _) => { - vec![Conf::new(Type::int(size as usize, true), max_confidence())] + vec![Conf::new(Type::int(size as usize, true), MAX_CONFIDENCE)] } Intrinsic::FcvtFToU(_, size, _) => { - vec![Conf::new(Type::int(size as usize, false), max_confidence())] + vec![Conf::new(Type::int(size as usize, false), MAX_CONFIDENCE)] } } } @@ -671,13 +668,11 @@ impl architecture::Architecture fo self.max_instr_len() } - fn associated_arch_by_addr(&self, _addr: &mut u64) -> CoreArchitecture { + fn associated_arch_by_addr(&self, _addr: u64) -> CoreArchitecture { self.handle } fn instruction_info(&self, data: &[u8], addr: u64) -> Option { - use architecture::BranchInfo; - let (inst_len, op) = match D::decode(addr, data) { Ok(Instr::Rv16(op)) => (2, op), Ok(Instr::Rv32(op)) => (4, op), @@ -691,23 +686,23 @@ impl architecture::Architecture fo let target = addr.wrapping_add(j.imm() as i64 as u64); let branch = if j.rd().id() == 0 { - BranchInfo::Unconditional(target) + BranchKind::Unconditional(target) } else { - BranchInfo::Call(target) + BranchKind::Call(target) }; - res.add_branch(branch, None); + res.add_branch(branch); } Op::Jalr(ref i) => { // TODO handle the calls with rs1 == 0? if i.rd().id() == 0 { let branch_type = if i.rs1().id() == 1 { - BranchInfo::FunctionReturn + BranchKind::FunctionReturn } else { - BranchInfo::Unresolved + BranchKind::Unresolved }; - res.add_branch(branch_type, None); + res.add_branch(branch_type); } } Op::Beq(ref b) @@ -716,21 +711,20 @@ impl architecture::Architecture fo | Op::Bge(ref b) | Op::BltU(ref b) | Op::BgeU(ref b) => { - res.add_branch(BranchInfo::False(addr.wrapping_add(inst_len as u64)), None); + res.add_branch(BranchKind::False(addr.wrapping_add(inst_len as u64))); res.add_branch( - BranchInfo::True(addr.wrapping_add(b.imm() as i64 as u64)), - None, + BranchKind::True(addr.wrapping_add(b.imm() as i64 as u64)), ); } Op::Ecall => { - res.add_branch(BranchInfo::SystemCall, None); + res.add_branch(BranchKind::SystemCall); } Op::Ebreak => { // TODO is this valid, or should lifting handle this? - res.add_branch(BranchInfo::Unresolved, None); + res.add_branch(BranchKind::Unresolved); } Op::Uret | Op::Sret | Op::Mret => { - res.add_branch(BranchInfo::FunctionReturn, None); + res.add_branch(BranchKind::FunctionReturn); } _ => {} } @@ -2843,8 +2837,8 @@ impl FunctionRecognizer for RiscVELFPLTRecognizer { // Ensure that load is pointing at an import address let sym = match bv.symbol_by_address(entry) { - Ok(sym) => sym, - Err(_) => return false, + Some(sym) => sym, + None => return false, }; if sym.sym_type() != SymbolType::ImportAddress { return false; @@ -2907,7 +2901,7 @@ impl FunctionRecognizer for RiscVELFPLTRecognizer { for ext_sym in &bv.symbols_by_name(func_sym.raw_name()) { if ext_sym.sym_type() == SymbolType::External { if let Some(var) = bv.data_variable_at_address(ext_sym.address()) { - func.apply_imported_types(func_sym.as_ref(), Some(var.t())); + func.apply_imported_types(func_sym.as_ref(), Some(&var.ty.contents)); return true; } } diff --git a/plugins/warp/benches/convert.rs b/plugins/warp/benches/convert.rs index 15065d8ff..49a2df011 100644 --- a/plugins/warp/benches/convert.rs +++ b/plugins/warp/benches/convert.rs @@ -1,6 +1,5 @@ use binaryninja::binaryview::BinaryViewExt; use binaryninja::headless::Session; -use binaryninja::types::Conf; use criterion::{criterion_group, criterion_main, Criterion}; use std::path::PathBuf; use warp_ninja::convert::from_bn_type; diff --git a/plugins/warp/src/cache.rs b/plugins/warp/src/cache.rs index 8111ca51d..340b00d78 100644 --- a/plugins/warp/src/cache.rs +++ b/plugins/warp/src/cache.rs @@ -230,7 +230,7 @@ impl GUIDCache { for call_site in &function.call_sites() { for cs_ref_addr in view.get_code_refs_from(call_site.address, Some(function)) { match view.function_at(&func_platform, cs_ref_addr) { - Ok(cs_ref_func) => { + Some(cs_ref_func) => { // Call site is a function, constrain on it. let cs_ref_func_id = FunctionID::from(cs_ref_func.as_ref()); if cs_ref_func_id != func_id { @@ -241,11 +241,11 @@ impl GUIDCache { .insert(self.function_constraint(&cs_ref_func, call_site_offset)); } } - Err(_) => { + None => { // We could be dealing with an extern symbol, get the symbol as a constraint. let call_site_offset: i64 = call_site.address.wrapping_sub(func_start) as i64; - if let Ok(call_site_sym) = view.symbol_by_address(cs_ref_addr) { + if let Some(call_site_sym) = view.symbol_by_address(cs_ref_addr) { constraints.insert( self.function_constraint_from_symbol( &call_site_sym, diff --git a/plugins/warp/src/convert.rs b/plugins/warp/src/convert.rs index 8240123db..f3e44bd95 100644 --- a/plugins/warp/src/convert.rs +++ b/plugins/warp/src/convert.rs @@ -6,8 +6,9 @@ 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::types::{ - BaseStructure as BNBaseStructure, Conf as BNConf, EnumerationBuilder as BNEnumerationBuilder, + BaseStructure as BNBaseStructure, EnumerationBuilder as BNEnumerationBuilder, FunctionParameter as BNFunctionParameter, MemberAccess as BNMemberAccess, MemberAccess, MemberScope as BNMemberScope, NamedTypeReference, NamedTypeReference as BNNamedTypeReference, NamedTypeReferenceClass, StructureBuilder as BNStructureBuilder, @@ -184,7 +185,7 @@ pub fn from_bn_type_internal( .collect::>(); // Add base structures as flattened members - if let Ok(base_structs) = raw_struct.base_structures() { + if let Some(base_structs) = raw_struct.base_structures() { let base_to_member_iter = base_structs.iter().map(|base_struct| { let bit_offset = bytes_to_bits(base_struct.offset); let mut modifiers = StructureMemberModifiers::empty(); @@ -275,8 +276,8 @@ pub fn from_bn_type_internal( ty: from_bn_type_internal( view, visited_refs, - &raw_member.t.contents, - raw_member.t.confidence, + &raw_member.ty.contents, + raw_member.ty.confidence, ), // TODO: Just omit location for now? // TODO: Location should be optional... @@ -286,7 +287,7 @@ pub fn from_bn_type_internal( .collect(); let mut out_members = Vec::new(); - if let Ok(return_ty) = raw_ty.return_value() { + if let Some(return_ty) = raw_ty.return_value() { out_members.push(FunctionMember { name: None, ty: from_bn_type_internal( @@ -301,8 +302,7 @@ pub fn from_bn_type_internal( let calling_convention = raw_ty .calling_convention() - .map(|bn_cc| from_bn_calling_convention(bn_cc.contents)) - .ok(); + .map(|bn_cc| from_bn_calling_convention(bn_cc.contents)); let func_class = FunctionClass { calling_convention, @@ -338,7 +338,7 @@ pub fn from_bn_type_internal( } }; - let name = raw_ty.registered_name().map(|n| n.name().to_string()).ok(); + let name = raw_ty.registered_name().map(|n| n.name().to_string()); Type { name, @@ -485,9 +485,10 @@ pub fn to_bn_type(arch: &A, ty: &Type) -> BNRef { builder.insert(member_name, member_value); } // TODO: Warn if enumeration has no size. - let width = bits_to_bytes(c.member_type.size().unwrap()) as _; + let width = bits_to_bytes(c.member_type.size().unwrap()) as usize; let signed = matches!(*c.member_type.class, TypeClass::Integer(c) if c.signed); - BNType::enumeration(&builder.finalize(), width, signed) + // TODO: Passing width like this is weird. + BNType::enumeration(&builder.finalize(), width.try_into().unwrap(), signed) } TypeClass::Union(c) => { let builder = BNStructureBuilder::new(); @@ -533,7 +534,7 @@ pub fn to_bn_type(arch: &A, ty: &Type) -> BNRef { match c.calling_convention.as_ref() { Some(cc) => { let calling_convention = to_bn_calling_convention(arch, cc); - BNType::function_with_options( + BNType::function_with_opts( &return_type, ¶ms, variable_args, diff --git a/plugins/warp/src/lib.rs b/plugins/warp/src/lib.rs index 6e195fc3c..7dae3ec15 100644 --- a/plugins/warp/src/lib.rs +++ b/plugins/warp/src/lib.rs @@ -155,7 +155,7 @@ pub fn basic_block_guid( Some(Type::enumeration( &enumeration_builder.finalize(), - width, + // TODO: This looks bad, look at the comment in [`Type::width`]. + width.try_into().unwrap(), false, )) } diff --git a/rust/examples/dwarf/dwarf_import/src/dwarfdebuginfo.rs b/rust/examples/dwarf/dwarf_import/src/dwarfdebuginfo.rs index 168ddaaac..b94419a9f 100644 --- a/rust/examples/dwarf/dwarf_import/src/dwarfdebuginfo.rs +++ b/rust/examples/dwarf/dwarf_import/src/dwarfdebuginfo.rs @@ -21,7 +21,8 @@ use binaryninja::{ rc::*, symbol::SymbolType, templatesimplifier::simplify_str_to_fqn, - types::{Conf, FunctionParameter, NamedTypedVariable, Type, Variable, VariableSourceType}, + variable::NamedVariableWithType, + types::{FunctionParameter, Type}, }; use gimli::{DebuggingInformationEntry, Dwarf, Unit}; @@ -33,6 +34,8 @@ use std::{ collections::HashMap, hash::Hash, }; +use binaryninja::confidence::Conf; +use binaryninja::variable::{Variable, VariableSourceType}; pub(crate) type TypeUID = usize; @@ -49,7 +52,7 @@ pub(crate) struct FunctionInfoBuilder { pub(crate) parameters: Vec>, pub(crate) platform: Option>, pub(crate) variable_arguments: bool, - pub(crate) stack_variables: Vec, + pub(crate) stack_variables: Vec, pub(crate) use_cfa: bool, //TODO actually store more info about the frame base } @@ -392,7 +395,7 @@ impl DebugInfoBuilder { }; // Either get the known type or use a 0 confidence void type so we at least get the name applied - let t = match type_uid { + let ty = match type_uid { Some(uid) => Conf::new(self.get_type(uid).unwrap().get_type(), 128), None => Conf::new(Type::void(), 0) }; @@ -449,7 +452,7 @@ impl DebugInfoBuilder { } let var = Variable::new(VariableSourceType::StackVariableSourceType, 0, adjusted_offset); - function.stack_variables.push(NamedTypedVariable::new(var, name, t, false)); + function.stack_variables.push(NamedVariableWithType::new(var, ty, name, false)); } @@ -547,7 +550,7 @@ impl DebugInfoBuilder { fn get_function_type(&self, function: &FunctionInfoBuilder) -> Ref { let return_type = match function.return_type { Some(return_type_id) => Conf::new(self.get_type(return_type_id).unwrap().get_type(), 128), - _ => Conf::new(binaryninja::types::Type::void(), 0), + _ => Conf::new(Type::void(), 0), }; let parameters: Vec = function @@ -564,7 +567,7 @@ impl DebugInfoBuilder { }) .collect(); - binaryninja::types::Type::function(&return_type, ¶meters, function.variable_arguments) + Type::function(&return_type, ¶meters, function.variable_arguments) } fn commit_functions(&self, debug_info: &mut DebugInfo) { @@ -593,7 +596,7 @@ impl DebugInfoBuilder { for func in &mut self.functions { // If the function's raw name already exists in the binary... if let Some(raw_name) = &func.raw_name { - if let Ok(symbol) = bv.symbol_by_raw_name(raw_name) { + if let Some(symbol) = bv.symbol_by_raw_name(raw_name) { // Link mangled names without addresses to existing symbols in the binary if func.address.is_none() && func.raw_name.is_some() { // DWARF doesn't contain GOT info, so remove any entries there...they will be wrong (relying on Binja's mechanisms for the GOT is good ) diff --git a/rust/examples/dwarf/dwarf_import/src/helpers.rs b/rust/examples/dwarf/dwarf_import/src/helpers.rs index 6aba99d44..20e8dddc4 100644 --- a/rust/examples/dwarf/dwarf_import/src/helpers.rs +++ b/rust/examples/dwarf/dwarf_import/src/helpers.rs @@ -356,8 +356,8 @@ pub(crate) fn get_expr_value( pub(crate) fn get_build_id(view: &BinaryView) -> Result { let mut build_id: Option = None; - if let Ok(raw_view) = view.raw_view() { - if let Ok(build_id_section) = raw_view.section_by_name(".note.gnu.build-id") { + if let Some(raw_view) = view.raw_view() { + if let Some(build_id_section) = raw_view.section_by_name(".note.gnu.build-id") { // Name size - 4 bytes // Desc size - 4 bytes // Type - 4 bytes diff --git a/rust/examples/dwarf/dwarf_import/src/lib.rs b/rust/examples/dwarf/dwarf_import/src/lib.rs index 1130bd8a0..ffc3f44d5 100644 --- a/rust/examples/dwarf/dwarf_import/src/lib.rs +++ b/rust/examples/dwarf/dwarf_import/src/lib.rs @@ -353,21 +353,21 @@ fn parse_unwind_section>( where >::Offset: std::hash::Hash { let mut bases = gimli::BaseAddresses::default(); - if let Ok(section) = view.section_by_name(".eh_frame_hdr").or(view.section_by_name("__eh_frame_hdr")) { + if let Some(section) = view.section_by_name(".eh_frame_hdr").or(view.section_by_name("__eh_frame_hdr")) { bases = bases.set_eh_frame_hdr(section.start()); } - if let Ok(section) = view.section_by_name(".eh_frame").or(view.section_by_name("__eh_frame")) { + if let Some(section) = view.section_by_name(".eh_frame").or(view.section_by_name("__eh_frame")) { bases = bases.set_eh_frame(section.start()); - } else if let Ok(section) = view.section_by_name(".debug_frame").or(view.section_by_name("__debug_frame")) { + } else if let Some(section) = view.section_by_name(".debug_frame").or(view.section_by_name("__debug_frame")) { bases = bases.set_eh_frame(section.start()); } - if let Ok(section) = view.section_by_name(".text").or(view.section_by_name("__text")) { + if let Some(section) = view.section_by_name(".text").or(view.section_by_name("__text")) { bases = bases.set_text(section.start()); } - if let Ok(section) = view.section_by_name(".got").or(view.section_by_name("__got")) { + if let Some(section) = view.section_by_name(".got").or(view.section_by_name("__got")) { bases = bases.set_got(section.start()); } @@ -433,8 +433,8 @@ where >::Offset: std::hash::Hash { } fn get_supplementary_build_id(bv: &BinaryView) -> Option { - let raw_view = bv.raw_view().ok()?; - if let Ok(section) = raw_view.section_by_name(".gnu_debugaltlink") { + let raw_view = bv.raw_view()?; + if let Some(section) = raw_view.section_by_name(".gnu_debugaltlink") { let start = section.start(); let len = section.len(); @@ -508,7 +508,7 @@ fn parse_dwarf( } let range_data_offsets; - if view.section_by_name(".eh_frame").is_ok() || view.section_by_name("__eh_frame").is_ok() { + if view.section_by_name(".eh_frame").is_some() || view.section_by_name("__eh_frame").is_some() { let eh_frame_endian = get_endian(view); let mut eh_frame_section_reader = |section_id: SectionId| -> _ { create_section_reader(section_id, view, eh_frame_endian, dwo_file) }; @@ -517,7 +517,7 @@ fn parse_dwarf( range_data_offsets = parse_unwind_section(view, eh_frame) .map_err(|e| error!("Error parsing .eh_frame: {}", e))?; } - else if view.section_by_name(".debug_frame").is_ok() || view.section_by_name("__debug_frame").is_ok() { + else if view.section_by_name(".debug_frame").is_some() || view.section_by_name("__debug_frame").is_some() { let debug_frame_endian = get_endian(view); let mut debug_frame_section_reader = |section_id: SectionId| -> _ { create_section_reader(section_id, view, debug_frame_endian, dwo_file) }; diff --git a/rust/examples/dwarf/dwarfdump/src/lib.rs b/rust/examples/dwarf/dwarfdump/src/lib.rs index 2cfbbbce9..b1f60ea6b 100644 --- a/rust/examples/dwarf/dwarfdump/src/lib.rs +++ b/rust/examples/dwarf/dwarfdump/src/lib.rs @@ -244,7 +244,7 @@ fn process_tree( } fn dump_dwarf(bv: &BinaryView) { - let view = if bv.section_by_name(".debug_info").is_ok() { + let view = if bv.section_by_name(".debug_info").is_some() { bv.to_owned() } else { bv.parent_view().unwrap() diff --git a/rust/examples/dwarf/shared/src/lib.rs b/rust/examples/dwarf/shared/src/lib.rs index 23baa42c6..6bd597c76 100644 --- a/rust/examples/dwarf/shared/src/lib.rs +++ b/rust/examples/dwarf/shared/src/lib.rs @@ -38,25 +38,25 @@ pub enum Error { } pub fn is_non_dwo_dwarf(view: &BinaryView) -> bool { - view.section_by_name(".debug_info").is_ok() || view.section_by_name("__debug_info").is_ok() + view.section_by_name(".debug_info").is_some() || view.section_by_name("__debug_info").is_some() } pub fn is_dwo_dwarf(view: &BinaryView) -> bool { - view.section_by_name(".debug_info.dwo").is_ok() + view.section_by_name(".debug_info.dwo").is_some() } pub fn is_raw_non_dwo_dwarf(view: &BinaryView) -> bool { - if let Ok(raw_view) = view.raw_view() { - raw_view.section_by_name(".debug_info").is_ok() - || view.section_by_name("__debug_info").is_ok() + if let Some(raw_view) = view.raw_view() { + raw_view.section_by_name(".debug_info").is_some() + || view.section_by_name("__debug_info").is_some() } else { false } } pub fn is_raw_dwo_dwarf(view: &BinaryView) -> bool { - if let Ok(raw_view) = view.raw_view() { - raw_view.section_by_name(".debug_info.dwo").is_ok() + if let Some(raw_view) = view.raw_view() { + raw_view.section_by_name(".debug_info.dwo").is_some() } else { false } @@ -69,8 +69,8 @@ pub fn can_use_debuginfod(view: &BinaryView) -> bool { } pub fn has_build_id_section(view: &BinaryView) -> bool { - if let Ok(raw_view) = view.raw_view() { - return raw_view.section_by_name(".note.gnu.build-id").is_ok() + if let Some(raw_view) = view.raw_view() { + return raw_view.section_by_name(".note.gnu.build-id").is_some() } false } @@ -101,7 +101,7 @@ pub fn create_section_reader<'a, Endian: 'a + Endianity>( section_id.name() }; - if let Ok(section) = view.section_by_name(section_name) { + if let Some(section) = view.section_by_name(section_name) { // TODO : This is kinda broke....should add rust wrappers for some of this if let Some(symbol) = view .symbols() @@ -111,11 +111,11 @@ pub fn create_section_reader<'a, Endian: 'a + Endianity>( if let Some(data_var) = view .data_variables() .iter() - .find(|var| var.address() == symbol.address()) + .find(|var| var.address == symbol.address()) { // TODO : This should eventually be wrapped by some DataView sorta thingy thing, like how python does it - let data_type = data_var.t(); - let data = view.read_vec(data_var.address(), data_type.width() as usize); + let data_type = data_var.ty.contents; + let data = view.read_vec(data_var.address, data_type.width() as usize); let element_type = data_type.element_type().unwrap().contents; if let Some(current_section_header) = data @@ -179,7 +179,7 @@ pub fn create_section_reader<'a, Endian: 'a + Endianity>( endian, )) } - } else if let Ok(section) = view.section_by_name("__".to_string() + §ion_name[1..]) { + } else if let Some(section) = view.section_by_name("__".to_string() + §ion_name[1..]) { Ok(EndianRcSlice::new( Rc::from(view.read_vec(section.start(), section.len()).as_slice()), endian, diff --git a/rust/examples/hlil_visitor/src/main.rs b/rust/examples/hlil_visitor/src/main.rs index e1bdefb0a..7860ab17e 100644 --- a/rust/examples/hlil_visitor/src/main.rs +++ b/rust/examples/hlil_visitor/src/main.rs @@ -3,7 +3,7 @@ use std::env; use binaryninja::binaryview::BinaryViewExt; use binaryninja::hlil::HighLevelILLiftedOperand; use binaryninja::hlil::{HighLevelILFunction, HighLevelILLiftedInstruction}; -use binaryninja::types::Variable; +use binaryninja::variable::Variable; fn print_indent(indent: usize) { print!("{: Result<(), ()>> TranslateIDBTypes<'_, F> { } Type::enumeration( &eb.finalize(), - usize::try_from(bytesize).unwrap(), + // TODO: This looks bad, look at the comment in [`Type::width`]. + (bytesize as usize).try_into().unwrap(), Conf::new(false, 0), ) } @@ -637,7 +639,7 @@ pub fn translate_til_types( types_by_ord, types_by_name, }; - if (translator.progress)(0, total).is_err() { + if (&translator.progress)(0, total).is_err() { return Err(anyhow!("IDB import aborted")); } @@ -681,7 +683,7 @@ pub fn translate_til_types( // means we acomplilshed nothing during this loop, there is no point in trying again break; } - if (translator.progress)(num_translated, total).is_err() { + if (&translator.progress)(num_translated, total).is_err() { // error means the user aborted the progress break; } diff --git a/rust/examples/mlil_visitor/src/main.rs b/rust/examples/mlil_visitor/src/main.rs index 752548c9f..8fe7e6a34 100644 --- a/rust/examples/mlil_visitor/src/main.rs +++ b/rust/examples/mlil_visitor/src/main.rs @@ -4,7 +4,7 @@ use binaryninja::architecture::Intrinsic; use binaryninja::binaryview::BinaryViewExt; use binaryninja::mlil::MediumLevelILLiftedOperand; use binaryninja::mlil::{MediumLevelILFunction, MediumLevelILLiftedInstruction}; -use binaryninja::types::Variable; +use binaryninja::variable::Variable; fn print_indent(indent: usize) { print!("{: bool { let pdb_magic_bytes = "Microsoft C/C++ MSF 7.00\r\n\x1A\x44\x53\x00\x00\x00"; - if let Ok(raw_view) = view.raw_view() { + if let Some(raw_view) = view.raw_view() { raw_view.read_vec(0, pdb_magic_bytes.len()) == pdb_magic_bytes.as_bytes() } else { false diff --git a/rust/examples/pdb-ng/src/parser.rs b/rust/examples/pdb-ng/src/parser.rs index 2d56a76d1..a47836063 100644 --- a/rust/examples/pdb-ng/src/parser.rs +++ b/rust/examples/pdb-ng/src/parser.rs @@ -24,15 +24,16 @@ use pdb::*; use binaryninja::architecture::{Architecture, CoreArchitecture}; use binaryninja::binaryview::{BinaryView, BinaryViewExt}; use binaryninja::callingconvention::CallingConvention; +use binaryninja::confidence::{Conf, MIN_CONFIDENCE}; use binaryninja::debuginfo::{DebugFunctionInfo, DebugInfo}; use binaryninja::platform::Platform; use binaryninja::rc::Ref; use binaryninja::settings::Settings; use binaryninja::types::{ - min_confidence, Conf, DataVariableAndName, EnumerationBuilder, NamedTypeReference, + EnumerationBuilder, NamedTypeReference, NamedTypeReferenceClass, StructureBuilder, StructureType, Type, TypeClass, }; - +use binaryninja::variable::NamedDataVariableWithType; use crate::symbol_parser::{ParsedDataSymbol, ParsedProcedure, ParsedSymbol}; use crate::type_parser::ParsedType; @@ -201,8 +202,8 @@ impl<'a, S: Source<'a> + 'a> PDBParserInstance<'a, S> { self.settings .get_bool("pdb.features.allowVoidGlobals", Some(self.bv), None); - let min_confidence_type = Conf::new(Type::void(), min_confidence()); - for sym in symbols.iter() { + let min_confidence_type = Conf::new(Type::void(), MIN_CONFIDENCE); + for sym in symbols { match sym { ParsedSymbol::Data(ParsedDataSymbol { address, @@ -229,11 +230,11 @@ impl<'a, S: Source<'a> + 'a> PDBParserInstance<'a, S> { ) }); self.debug_info - .add_data_variable_info(DataVariableAndName::new( - *address, + .add_data_variable_info(NamedDataVariableWithType::new( + address, real_type.clone(), + name.full_name.unwrap_or(name.raw_name), true, - name.full_name.as_ref().unwrap_or(&name.raw_name), )); } s => { @@ -315,13 +316,13 @@ impl<'a, S: Source<'a> + 'a> PDBParserInstance<'a, S> { ) { match ty.type_class() { TypeClass::StructureTypeClass => { - if let Ok(structure) = ty.get_structure() { - if let Ok(members) = structure.members() { + if let Some(structure) = ty.get_structure() { + if let Some(members) = structure.members() { for member in members { self.collect_names(member.ty.contents.as_ref(), unknown_names); } } - if let Ok(bases) = structure.base_structures() { + if let Some(bases) = structure.base_structures() { for base in bases { self.collect_name(base.ty.as_ref(), unknown_names); } @@ -329,27 +330,27 @@ impl<'a, S: Source<'a> + 'a> PDBParserInstance<'a, S> { } } TypeClass::PointerTypeClass => { - if let Ok(target) = ty.target() { + if let Some(target) = ty.target() { self.collect_names(target.contents.as_ref(), unknown_names); } } TypeClass::ArrayTypeClass => { - if let Ok(element_type) = ty.element_type() { + if let Some(element_type) = ty.element_type() { self.collect_names(element_type.contents.as_ref(), unknown_names); } } TypeClass::FunctionTypeClass => { - if let Ok(return_value) = ty.return_value() { + if let Some(return_value) = ty.return_value() { self.collect_names(return_value.contents.as_ref(), unknown_names); } - if let Ok(params) = ty.parameters() { + if let Some(params) = ty.parameters() { for param in params { - self.collect_names(param.t.contents.as_ref(), unknown_names); + self.collect_names(param.ty.contents.as_ref(), unknown_names); } } } TypeClass::NamedTypeReferenceClass => { - if let Ok(ntr) = ty.get_named_type_reference() { + if let Some(ntr) = ty.get_named_type_reference() { self.collect_name(ntr.as_ref(), unknown_names); } } @@ -396,7 +397,7 @@ impl<'a, S: Source<'a> + 'a> PDBParserInstance<'a, S> { } _ => {} } - (progress)(i, count)?; + (&progress)(i, count)?; } for (name, class) in unknown_names.into_iter() { @@ -441,7 +442,7 @@ impl<'a, S: Source<'a> + 'a> PDBParserInstance<'a, S> { name, Type::enumeration( enumeration.finalize().as_ref(), - self.arch.default_integer_size(), + self.arch.default_integer_size().try_into()?, false, ) .as_ref(), diff --git a/rust/examples/pdb-ng/src/struct_grouper.rs b/rust/examples/pdb-ng/src/struct_grouper.rs index 042eb979c..eb4618b20 100644 --- a/rust/examples/pdb-ng/src/struct_grouper.rs +++ b/rust/examples/pdb-ng/src/struct_grouper.rs @@ -15,12 +15,11 @@ use std::cmp::Ordering; use std::env; use std::fmt::{Debug, Display, Formatter}; - use anyhow::{anyhow, Result}; use log::{debug, warn}; - +use binaryninja::confidence::{Conf, MAX_CONFIDENCE}; use binaryninja::types::{ - max_confidence, Conf, MemberAccess, MemberScope, StructureBuilder, StructureType, Type, + MemberAccess, MemberScope, StructureBuilder, StructureType, Type, }; use crate::type_parser::ParsedMember; @@ -414,7 +413,7 @@ fn apply_groups( let mut inner = StructureBuilder::new(); apply_groups(members, &mut inner, children, inner_offset); structure.insert( - &Conf::new(Type::structure(inner.finalize().as_ref()), max_confidence()), + &Conf::new(Type::structure(inner.finalize().as_ref()), MAX_CONFIDENCE), format!("__inner{}", i), inner_offset - offset, false, @@ -427,7 +426,7 @@ fn apply_groups( inner.set_structure_type(StructureType::UnionStructureType); apply_groups(members, &mut inner, children, inner_offset); structure.insert( - &Conf::new(Type::structure(inner.finalize().as_ref()), max_confidence()), + &Conf::new(Type::structure(inner.finalize().as_ref()), MAX_CONFIDENCE), format!("__inner{}", i), inner_offset - offset, false, diff --git a/rust/examples/pdb-ng/src/symbol_parser.rs b/rust/examples/pdb-ng/src/symbol_parser.rs index 7a90c4ab9..f23a2ab8d 100644 --- a/rust/examples/pdb-ng/src/symbol_parser.rs +++ b/rust/examples/pdb-ng/src/symbol_parser.rs @@ -12,6 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. +use binaryninja::confidence::ConfMergable; use std::collections::{BTreeMap, HashMap, HashSet}; use std::mem; use std::sync::OnceLock; @@ -36,13 +37,14 @@ use pdb::{ use binaryninja::architecture::{Architecture, ArchitectureExt, Register}; use binaryninja::binaryview::BinaryViewBase; +use binaryninja::confidence::{Conf, MAX_CONFIDENCE, MIN_CONFIDENCE}; use binaryninja::demangle::demangle_ms; use binaryninja::rc::Ref; use binaryninja::types::{ - max_confidence, min_confidence, Conf, ConfMergable, FunctionParameter, QualifiedName, - StructureBuilder, Type, TypeClass, Variable, VariableSourceType, + FunctionParameter, QualifiedName, + StructureBuilder, Type, TypeClass, }; - +use binaryninja::variable::{Variable, VariableSourceType}; use crate::PDBParserInstance; const DEMANGLE_CONFIDENCE: u8 = 32; @@ -230,7 +232,7 @@ impl<'a, S: Source<'a> + 'a> PDBParserInstance<'a, S> { let this_confidence = match type_ { Some(Conf { confidence, .. }) => *confidence, - _ => min_confidence(), + _ => MIN_CONFIDENCE, }; let (new_better, old_exists) = match best_symbols.get(raw_name) { Some(ParsedSymbol::Data(ParsedDataSymbol { @@ -288,7 +290,7 @@ impl<'a, S: Source<'a> + 'a> PDBParserInstance<'a, S> { let this_confidence = match type_ { Some(Conf { confidence, .. }) => *confidence, - _ => min_confidence(), + _ => MIN_CONFIDENCE, }; let (new_better, old_exists) = match best_functions.get(raw_name) { Some(ParsedSymbol::Procedure(ParsedProcedure { @@ -735,7 +737,7 @@ impl<'a, S: Source<'a> + 'a> PDBParserInstance<'a, S> { let storage = if let Some(reg) = self.convert_register(data.register) { vec![ParsedLocation { location: Variable { - t: VariableSourceType::RegisterVariableSourceType, + ty: VariableSourceType::RegisterVariableSourceType, index: 0, storage: reg, }, @@ -953,11 +955,11 @@ impl<'a, S: Source<'a> + 'a> PDBParserInstance<'a, S> { let raw_params = raw_type .contents .parameters() - .map_err(|_| anyhow!("no params"))?; + .ok_or(anyhow!("no params"))?; let mut fancy_params = fancy_type .contents .parameters() - .map_err(|_| anyhow!("no params"))?; + .ok_or(anyhow!("no params"))?; // Collect all the parameters we are expecting from the symbols let mut parsed_params = vec![]; @@ -965,7 +967,7 @@ impl<'a, S: Source<'a> + 'a> PDBParserInstance<'a, S> { let param = FunctionParameter::new( p.type_.clone().merge(Conf::new( Type::int(self.arch.address_size(), false), - min_confidence(), + MIN_CONFIDENCE, )), p.name.clone(), p.storage.get(0).map(|loc| loc.location.clone()), @@ -980,7 +982,7 @@ impl<'a, S: Source<'a> + 'a> PDBParserInstance<'a, S> { let param = FunctionParameter::new( p.type_.clone().merge(Conf::new( Type::int(self.arch.address_size(), false), - min_confidence(), + MIN_CONFIDENCE, )), p.name.clone(), p.storage.get(0).map(|loc| loc.location.clone()), @@ -1028,7 +1030,7 @@ impl<'a, S: Source<'a> + 'a> PDBParserInstance<'a, S> { let mut i = 0; for p in fancy_params.iter_mut() { if p.name.as_str().is_empty() { - if p.t.contents != expected_parsed_params[i].t.contents { + if p.ty.contents != expected_parsed_params[i].ty.contents { self.log(|| { format!( "Suspicious parameter {}: {:?} vs {:?}", @@ -1049,7 +1051,7 @@ impl<'a, S: Source<'a> + 'a> PDBParserInstance<'a, S> { let cc = fancy_type .contents .calling_convention() - .map_or_else(|_| Conf::new(self.default_cc.clone(), 0), |cc| cc); + .map_or_else(|| Conf::new(self.default_cc.clone(), 0), |cc| cc); self.log(|| { format!( @@ -1069,17 +1071,17 @@ impl<'a, S: Source<'a> + 'a> PDBParserInstance<'a, S> { // Use the new locals we've parsed to make the Real Definitely True function type let fancy_type = Conf::new( - Type::function_with_options( + Type::function_with_opts( &fancy_type .contents .return_value() - .map_err(|_| anyhow!("no ret"))?, + .ok_or(anyhow!("no ret"))?, fancy_params.as_slice(), fancy_type.contents.has_variable_arguments().contents, &cc, fancy_type.contents.stack_adjustment(), ), - max_confidence(), + MAX_CONFIDENCE, ); let fancier_type = fancy_type @@ -1424,7 +1426,7 @@ impl<'a, S: Source<'a> + 'a> PDBParserInstance<'a, S> { type_: self.lookup_type_conf(&data.type_index, false)?, storage: vec![ParsedLocation { location: Variable { - t: VariableSourceType::StackVariableSourceType, + ty: VariableSourceType::StackVariableSourceType, index: 0, storage: data.offset as i64, }, @@ -1442,7 +1444,7 @@ impl<'a, S: Source<'a> + 'a> PDBParserInstance<'a, S> { type_: self.lookup_type_conf(&data.type_index, false)?, storage: vec![ParsedLocation { location: Variable { - t: VariableSourceType::StackVariableSourceType, + ty: VariableSourceType::StackVariableSourceType, index: 0, storage: data.offset as i64, }, @@ -1586,7 +1588,7 @@ impl<'a, S: Source<'a> + 'a> PDBParserInstance<'a, S> { if let Some(reg) = self.convert_register(data.register) { Ok(Some(ParsedSymbol::Location(ParsedLocation { location: Variable { - t: VariableSourceType::RegisterVariableSourceType, + ty: VariableSourceType::RegisterVariableSourceType, index: 0, storage: reg, }, @@ -1653,7 +1655,7 @@ impl<'a, S: Source<'a> + 'a> PDBParserInstance<'a, S> { type_: self.lookup_type_conf(&data.type_index, false)?, storage: vec![ParsedLocation { location: Variable { - t: VariableSourceType::StackVariableSourceType, + ty: VariableSourceType::StackVariableSourceType, index: 0, storage: data.offset as i64, }, @@ -1695,14 +1697,14 @@ impl<'a, S: Source<'a> + 'a> PDBParserInstance<'a, S> { for loc in &new_storage { match loc { Variable { - t: VariableSourceType::RegisterVariableSourceType, + ty: VariableSourceType::RegisterVariableSourceType, .. } => { // Assume register vars are always parameters really_is_param = true; } Variable { - t: VariableSourceType::StackVariableSourceType, + ty: VariableSourceType::StackVariableSourceType, storage, .. } if *storage >= 0 => { @@ -1795,14 +1797,14 @@ impl<'a, S: Source<'a> + 'a> PDBParserInstance<'a, S> { let parameters = ty .contents .parameters() - .map_err(|_| anyhow!("no parameters"))?; + .ok_or(anyhow!("no parameters"))?; if let [p] = parameters.as_slice() { - if p.t.contents.type_class() == TypeClass::VoidTypeClass { + if p.ty.contents.type_class() == TypeClass::VoidTypeClass { t = Some(Conf::new( Type::function::<_>( &ty.contents .return_value() - .map_err(|_| anyhow!("no return value"))?, + .ok_or(anyhow!("no return value"))?, &[], ty.contents.has_variable_arguments().contents, ), @@ -1979,13 +1981,13 @@ impl<'a, S: Source<'a> + 'a> PDBParserInstance<'a, S> { } let structure = base_type .get_structure() - .map_err(|_| anyhow!("Expected structure"))?; + .ok_or(anyhow!("Expected structure"))?; let mut members = structure .members() - .map_err(|_| anyhow!("Expected structure to have members"))?; + .ok_or(anyhow!("Expected structure to have members"))?; let last_member = members .last_mut() - .ok_or_else(|| anyhow!("Not enough members"))?; + .ok_or(anyhow!("Not enough members"))?; if last_member.ty.contents.type_class() != TypeClass::ArrayTypeClass { return Ok(None); @@ -1998,7 +2000,7 @@ impl<'a, S: Source<'a> + 'a> PDBParserInstance<'a, S> { .ty .contents .element_type() - .map_err(|_| anyhow!("Last member has no type"))? + .ok_or(anyhow!("Last member has no type"))? .contents; let member_width = member_element.width(); diff --git a/rust/examples/pdb-ng/src/type_parser.rs b/rust/examples/pdb-ng/src/type_parser.rs index caf732b25..730cdf458 100644 --- a/rust/examples/pdb-ng/src/type_parser.rs +++ b/rust/examples/pdb-ng/src/type_parser.rs @@ -22,7 +22,7 @@ use binaryninja::callingconvention::CallingConvention; use binaryninja::platform::Platform; use binaryninja::rc::Ref; use binaryninja::types::{ - max_confidence, BaseStructure, Conf, EnumerationBuilder, EnumerationMember, FunctionParameter, + BaseStructure, EnumerationBuilder, EnumerationMember, FunctionParameter, MemberAccess, MemberScope, NamedTypeReference, NamedTypeReferenceClass, QualifiedName, StructureBuilder, StructureMember, StructureType, Type, TypeBuilder, TypeClass, }; @@ -37,7 +37,7 @@ use pdb::{ VirtualFunctionTablePointerType, VirtualFunctionTableType, VirtualTableShapeType, }; use regex::Regex; - +use binaryninja::confidence::{Conf, MAX_CONFIDENCE}; use crate::struct_grouper::group_structure; use crate::PDBParserInstance; @@ -351,7 +351,7 @@ impl<'a, S: Source<'a> + 'a> PDBParserInstance<'a, S> { // See if we have this type let name = ty .get_named_type_reference() - .map_err(|_| anyhow!("expected ntr"))? + .ok_or(anyhow!("expected ntr"))? .name() .to_string(); if Self::is_name_anonymous(&name) { @@ -368,7 +368,7 @@ impl<'a, S: Source<'a> + 'a> PDBParserInstance<'a, S> { self.log(|| format!("Got undefined but referenced named type: {}", &name)); let type_class = ty .get_named_type_reference() - .map_err(|_| anyhow!("expected ntr"))? + .ok_or(anyhow!("expected ntr"))? .class(); let bare_type = match type_class { @@ -392,7 +392,7 @@ impl<'a, S: Source<'a> + 'a> PDBParserInstance<'a, S> { ), NamedTypeReferenceClass::EnumNamedTypeClass => Type::enumeration( EnumerationBuilder::new().finalize().as_ref(), - self.arch.default_integer_size(), + self.arch.default_integer_size().try_into()?, false, ), }; @@ -482,14 +482,14 @@ impl<'a, S: Source<'a> + 'a> PDBParserInstance<'a, S> { match self.lookup_type(index, fancy_procs)? { Some(ty) if ty.type_class() == TypeClass::VoidTypeClass => Ok(Some(Conf::new(ty, 0))), Some(ty) => { - let mut confidence = max_confidence(); + let mut confidence = MAX_CONFIDENCE; // Extra check here for void(void) functions, they should get minimum confidence since this // is the signature PDB uses when it doesn't actually know the signature if ty.type_class() == TypeClass::FunctionTypeClass { - if let Ok(ret) = ty.return_value() { + if let Some(ret) = ty.return_value() { if ret.contents.type_class() == TypeClass::VoidTypeClass { - if let Ok(params) = ty.parameters() { + if let Some(params) = ty.parameters() { if params.len() == 0 { confidence = 0; } @@ -503,9 +503,9 @@ impl<'a, S: Source<'a> + 'a> PDBParserInstance<'a, S> { // the types of their contents if ty.type_class() == TypeClass::ArrayTypeClass { - if let Ok(ptr) = ty.element_type() { + if let Some(ptr) = ty.element_type() { if ptr.contents.type_class() == TypeClass::PointerTypeClass { - if let Ok(fun) = ptr.contents.target() { + if let Some(fun) = ptr.contents.target() { if fun.contents.type_class() == TypeClass::FunctionTypeClass && fun .contents @@ -514,7 +514,7 @@ impl<'a, S: Source<'a> + 'a> PDBParserInstance<'a, S> { .unwrap_or(0) == 0 { - if let Ok(ret) = fun.contents.return_value() { + if let Some(ret) = fun.contents.return_value() { if ret.contents.type_class() == TypeClass::VoidTypeClass { confidence = 0; } @@ -879,7 +879,7 @@ impl<'a, S: Source<'a> + 'a> PDBParserInstance<'a, S> { combined_bitfield_members.push(ParsedMember { ty: Conf::new( Type::structure(builder.finalize().as_ref()), - max_confidence(), + MAX_CONFIDENCE, ), name: bitfield_name(last_bitfield_offset, last_bitfield_idx), offset: last_bitfield_offset, @@ -913,7 +913,7 @@ impl<'a, S: Source<'a> + 'a> PDBParserInstance<'a, S> { combined_bitfield_members.push(ParsedMember { ty: Conf::new( Type::structure(builder.finalize().as_ref()), - max_confidence(), + MAX_CONFIDENCE, ), name: bitfield_name(last_bitfield_offset, last_bitfield_idx), offset: last_bitfield_offset, @@ -934,7 +934,7 @@ impl<'a, S: Source<'a> + 'a> PDBParserInstance<'a, S> { combined_bitfield_members.push(ParsedMember { ty: Conf::new( Type::structure(builder.finalize().as_ref()), - max_confidence(), + MAX_CONFIDENCE, ), name: bitfield_name(last_bitfield_offset, last_bitfield_idx), offset: last_bitfield_offset, @@ -964,13 +964,13 @@ impl<'a, S: Source<'a> + 'a> PDBParserInstance<'a, S> { let ntr_class = match self.named_types.get(name) { Some(ty) if ty.type_class() == TypeClass::StructureTypeClass => { match ty.get_structure() { - Ok(str) + Some(str) if str.structure_type() == StructureType::StructStructureType => { NamedTypeReferenceClass::StructNamedTypeClass } - Ok(str) + Some(str) if str.structure_type() == StructureType::ClassStructureType => { @@ -996,13 +996,13 @@ impl<'a, S: Source<'a> + 'a> PDBParserInstance<'a, S> { let ntr_class = match self.named_types.get(base_name) { Some(ty) if ty.type_class() == TypeClass::StructureTypeClass => { match ty.get_structure() { - Ok(str) + Some(str) if str.structure_type() == StructureType::StructStructureType => { NamedTypeReferenceClass::StructNamedTypeClass } - Ok(str) + Some(str) if str.structure_type() == StructureType::ClassStructureType => { @@ -1066,13 +1066,13 @@ impl<'a, S: Source<'a> + 'a> PDBParserInstance<'a, S> { let ntr_class = if vt_base_type.type_class() == TypeClass::StructureTypeClass { match vt_base_type.get_structure() { - Ok(str) + Some(str) if str.structure_type() == StructureType::StructStructureType => { NamedTypeReferenceClass::StructNamedTypeClass } - Ok(str) + Some(str) if str.structure_type() == StructureType::ClassStructureType => { @@ -1113,8 +1113,8 @@ impl<'a, S: Source<'a> + 'a> PDBParserInstance<'a, S> { for (offset, (name, method)) in virt_methods { vt.insert( &Conf::new( - Type::pointer(&self.arch, &Conf::new(method.method_type, max_confidence())), - max_confidence(), + Type::pointer(&self.arch, &Conf::new(method.method_type, MAX_CONFIDENCE)), + MAX_CONFIDENCE, ), &name, offset as u64, @@ -1141,12 +1141,12 @@ impl<'a, S: Source<'a> + 'a> PDBParserInstance<'a, S> { &self.arch, &Conf::new( Type::named_type_from_type(&QualifiedName::from(vt_name), vt_type.as_ref()), - max_confidence(), + MAX_CONFIDENCE, ), ); structure.insert( - &Conf::new(vt_pointer, max_confidence()), + &Conf::new(vt_pointer, MAX_CONFIDENCE), "vtable", 0, true, @@ -1180,7 +1180,7 @@ impl<'a, S: Source<'a> + 'a> PDBParserInstance<'a, S> { match self.try_type_index_to_bare(data.field_type, finder, true)? { Some(ty) => Ok(Some(Box::new(ParsedType::Member(ParsedMember { - ty: Conf::new(ty, max_confidence()), + ty: Conf::new(ty, MAX_CONFIDENCE), name: member_name.into_owned(), offset: member_offset, access, @@ -1191,7 +1191,7 @@ impl<'a, S: Source<'a> + 'a> PDBParserInstance<'a, S> { None => match self.handle_type_index(data.field_type, finder)? { Some(ParsedType::BitfieldType(bitfield)) => { Ok(Some(Box::new(ParsedType::Member(ParsedMember { - ty: Conf::new(bitfield.ty.clone(), max_confidence()), + ty: Conf::new(bitfield.ty.clone(), MAX_CONFIDENCE), name: member_name.into_owned(), offset: member_offset, access, @@ -1216,7 +1216,7 @@ impl<'a, S: Source<'a> + 'a> PDBParserInstance<'a, S> { let class_name = match self.handle_type_index(data.class_type, finder)? { Some(ParsedType::Bare(ty)) if ty.type_class() == TypeClass::NamedTypeReferenceClass => { ty.get_named_type_reference() - .map_err(|_| anyhow!("Expected NTR to have NTR"))? + .ok_or(anyhow!("Expected NTR to have NTR"))? .name() .to_string() } @@ -1240,7 +1240,7 @@ impl<'a, S: Source<'a> + 'a> PDBParserInstance<'a, S> { // It looks like pdb stores varargs by having the final argument be void let mut is_varargs = false; if let Some(last) = arguments.pop() { - if last.t.contents.as_ref().type_class() == TypeClass::VoidTypeClass { + if last.ty.contents.as_ref().type_class() == TypeClass::VoidTypeClass { is_varargs = true; } else { arguments.push(last); @@ -1257,12 +1257,12 @@ impl<'a, S: Source<'a> + 'a> PDBParserInstance<'a, S> { // This probably means the return value got pushed to the stack fancy_return_type = Type::pointer( &self.arch, - &Conf::new(return_type.clone(), max_confidence()), + &Conf::new(return_type.clone(), MAX_CONFIDENCE), ); fancy_arguments.insert( 0, FunctionParameter::new( - Conf::new(fancy_return_type.clone(), max_confidence()), + Conf::new(fancy_return_type.clone(), MAX_CONFIDENCE), "__return".to_string(), None, ), @@ -1275,27 +1275,27 @@ impl<'a, S: Source<'a> + 'a> PDBParserInstance<'a, S> { let convention = self .cv_call_t_to_calling_convention(data.attributes.calling_convention()) - .map(|cc| Conf::new(cc, max_confidence())) + .map(|cc| Conf::new(cc, MAX_CONFIDENCE)) .unwrap_or({ if is_varargs { - Conf::new(self.cdecl_cc.clone(), max_confidence()) + Conf::new(self.cdecl_cc.clone(), MAX_CONFIDENCE) } else if this_pointer_type.is_some() { - Conf::new(self.thiscall_cc.clone(), max_confidence()) + Conf::new(self.thiscall_cc.clone(), MAX_CONFIDENCE) } else { Conf::new(self.default_cc.clone(), 16) } }); - let func = Type::function_with_options( - &Conf::new(return_type, max_confidence()), + let func = Type::function_with_opts( + &Conf::new(return_type, MAX_CONFIDENCE), arguments.as_slice(), is_varargs, &convention, Conf::new(0, 0), ); - let fancy_func = Type::function_with_options( - &Conf::new(fancy_return_type, max_confidence()), + let fancy_func = Type::function_with_opts( + &Conf::new(fancy_return_type, MAX_CONFIDENCE), fancy_arguments.as_slice(), is_varargs, &convention, @@ -1392,7 +1392,7 @@ impl<'a, S: Source<'a> + 'a> PDBParserInstance<'a, S> { Some(ParsedType::Bare(t)) if t.type_class() == TypeClass::NamedTypeReferenceClass => { let name = t .get_named_type_reference() - .map_err(|_| anyhow!("Expected NTR to have NTR"))? + .ok_or(anyhow!("Expected NTR to have NTR"))? .name() .to_string(); (name, t.clone()) @@ -1417,7 +1417,7 @@ impl<'a, S: Source<'a> + 'a> PDBParserInstance<'a, S> { Ok(Some(Box::new(ParsedType::BaseClass( member_name.clone(), StructureMember::new( - Conf::new(resolved_type, max_confidence()), + Conf::new(resolved_type, MAX_CONFIDENCE), member_name, base_offset as u64, access, @@ -1438,7 +1438,7 @@ impl<'a, S: Source<'a> + 'a> PDBParserInstance<'a, S> { Some(ParsedType::Bare(t)) if t.type_class() == TypeClass::NamedTypeReferenceClass => { let name = t .get_named_type_reference() - .map_err(|_| anyhow!("Expected NTR to have NTR"))? + .ok_or(anyhow!("Expected NTR to have NTR"))? .name() .to_string(); (name, t.clone()) @@ -1511,7 +1511,7 @@ impl<'a, S: Source<'a> + 'a> PDBParserInstance<'a, S> { } else { None } - .map(|r| Conf::new(r, max_confidence())) + .map(|r| Conf::new(r, MAX_CONFIDENCE)) .unwrap_or(Conf::new(Type::void(), 0)); let mut arguments = match self.handle_type_index(data.argument_list, finder)? { @@ -1522,7 +1522,7 @@ impl<'a, S: Source<'a> + 'a> PDBParserInstance<'a, S> { // It looks like pdb stores varargs by having the final argument be void let mut is_varargs = false; if let Some(last) = arguments.pop() { - if last.t.contents.as_ref().type_class() == TypeClass::VoidTypeClass { + if last.ty.contents.as_ref().type_class() == TypeClass::VoidTypeClass { is_varargs = true; } else { arguments.push(last); @@ -1539,7 +1539,7 @@ impl<'a, S: Source<'a> + 'a> PDBParserInstance<'a, S> { if return_stacky { // Stack return via a pointer in the first parameter fancy_return_type = - Conf::new(Type::pointer(&self.arch, &return_type), max_confidence()); + Conf::new(Type::pointer(&self.arch, &return_type), MAX_CONFIDENCE); fancy_arguments.insert( 0, FunctionParameter::new(fancy_return_type.clone(), "__return".to_string(), None), @@ -1548,11 +1548,11 @@ impl<'a, S: Source<'a> + 'a> PDBParserInstance<'a, S> { let convention = self .cv_call_t_to_calling_convention(data.attributes.calling_convention()) - .map(|cc| Conf::new(cc, max_confidence())) + .map(|cc| Conf::new(cc, MAX_CONFIDENCE)) .unwrap_or(Conf::new(self.default_cc.clone(), 0)); self.log(|| format!("Convention: {:?}", convention)); - let func = Type::function_with_options( + let func = Type::function_with_opts( &return_type, arguments.as_slice(), is_varargs, @@ -1560,7 +1560,7 @@ impl<'a, S: Source<'a> + 'a> PDBParserInstance<'a, S> { Conf::new(0, 0), ); - let fancy_func = Type::function_with_options( + let fancy_func = Type::function_with_opts( &fancy_return_type, fancy_arguments.as_slice(), is_varargs, @@ -1670,7 +1670,8 @@ impl<'a, S: Source<'a> + 'a> PDBParserInstance<'a, S> { let new_type = Type::enumeration( enumeration.finalize().as_ref(), - underlying.width() as usize, + // TODO: This looks bad, look at the comment in [`Type::width`]. + (underlying.width() as usize).try_into()?, underlying.is_signed().contents, ); @@ -1695,7 +1696,7 @@ impl<'a, S: Source<'a> + 'a> PDBParserInstance<'a, S> { Variant::I32(v) => (v as i64) as u64, Variant::I64(v) => (v as i64) as u64, }, - is_default: false, + default: false, })))) } @@ -1843,7 +1844,7 @@ impl<'a, S: Source<'a> + 'a> PDBParserInstance<'a, S> { structure.insert( &Conf::new( Type::structure(inner_struct.finalize().as_ref()), - max_confidence(), + MAX_CONFIDENCE, ), format!("__inner{:x}", i), 0, @@ -1923,7 +1924,7 @@ impl<'a, S: Source<'a> + 'a> PDBParserInstance<'a, S> { // TODO: Ugly hack if self.arch.address_size() == 4 || Self::size_can_fit_in_register(ty.width()) { args.push(FunctionParameter::new( - Conf::new(ty.clone(), max_confidence()), + Conf::new(ty.clone(), MAX_CONFIDENCE), "".to_string(), None, )); @@ -1931,7 +1932,7 @@ impl<'a, S: Source<'a> + 'a> PDBParserInstance<'a, S> { args.push(FunctionParameter::new( Conf::new( Type::pointer(self.arch.as_ref(), ty.as_ref()), - max_confidence(), + MAX_CONFIDENCE, ), "".to_string(), None, @@ -2014,7 +2015,7 @@ impl<'a, S: Source<'a> + 'a> PDBParserInstance<'a, S> { // Replace empty NTR with fully parsed NTR, if we can let name = type_ .get_named_type_reference() - .map_err(|_| anyhow!("expected ntr"))? + .ok_or(anyhow!("expected ntr"))? .name() .to_string(); if let Some(full_ntr) = self.named_types.get(&name) { @@ -2058,7 +2059,7 @@ impl<'a, S: Source<'a> + 'a> PDBParserInstance<'a, S> { // some_type:: let name = type_ .get_named_type_reference() - .map_err(|_| anyhow!("expected ntr"))? + .ok_or(anyhow!("expected ntr"))? .name() .to_string(); if Self::is_name_anonymous(&name) { @@ -2178,7 +2179,7 @@ impl<'a, S: Source<'a> + 'a> PDBParserInstance<'a, S> { parameters.insert( 0, FunctionParameter::new( - Conf::new(this_type, max_confidence()), + Conf::new(this_type, MAX_CONFIDENCE), "this".to_string(), None, ), diff --git a/rust/src/architecture.rs b/rust/src/architecture.rs index 027af77fd..8fbf896d7 100644 --- a/rust/src/architecture.rs +++ b/rust/src/architecture.rs @@ -21,12 +21,11 @@ use binaryninjacore_sys::*; use std::{ borrow::{Borrow, Cow}, collections::HashMap, - ffi::{c_char, c_int, CStr, CString}, + ffi::{c_char, c_void, c_int, CStr, CString}, hash::Hash, - mem::{zeroed, MaybeUninit}, - ops, ptr, slice, + mem::MaybeUninit, }; - +use std::ops::Deref; use crate::{ callingconvention::CallingConvention, databuffer::DataBuffer, @@ -39,12 +38,22 @@ use crate::{ relocation::CoreRelocationHandler, string::BnStrCompatible, string::*, - types::{Conf, NameAndType, Type}, - {BranchType, Endianness}, + types::{NameAndType, Type}, + Endianness, }; -#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] -pub enum BranchInfo { +use crate::functionrecognizer::FunctionRecognizer; +use crate::relocation::{CustomRelocationHandlerHandle, RelocationHandler}; + +pub use binaryninjacore_sys::BNFlagRole as FlagRole; +pub use binaryninjacore_sys::BNImplicitRegisterExtend as ImplicitRegisterExtend; +pub use binaryninjacore_sys::BNLowLevelILFlagCondition as FlagCondition; +use crate::confidence::Conf; + +#[derive(Default, Copy, Clone, PartialEq, Eq, Hash, Debug)] +pub enum BranchKind { + #[default] + Unresolved, Unconditional(u64), False(u64), True(u64), @@ -53,134 +62,167 @@ pub enum BranchInfo { SystemCall, Indirect, Exception, - Unresolved, UserDefined, } -pub struct BranchIter<'a>(&'a InstructionInfo, ops::Range); -impl<'a> Iterator for BranchIter<'a> { - type Item = (BranchInfo, Option); - - fn next(&mut self) -> Option { - use crate::BranchType::*; +#[derive(Default, Copy, Clone, PartialEq, Eq, Hash, Debug)] +pub struct BranchInfo { + pub arch: Option, + pub kind: BranchKind, +} - match self.1.next() { - Some(i) => { - let target = (self.0).0.branchTarget[i]; - let arch = (self.0).0.branchArch[i]; - let arch = if arch.is_null() { - None - } else { - Some(CoreArchitecture(arch)) - }; - - let res = match (self.0).0.branchType[i] { - UnconditionalBranch => BranchInfo::Unconditional(target), - FalseBranch => BranchInfo::False(target), - TrueBranch => BranchInfo::True(target), - CallDestination => BranchInfo::Call(target), - FunctionReturn => BranchInfo::FunctionReturn, - SystemCall => BranchInfo::SystemCall, - IndirectBranch => BranchInfo::Indirect, - ExceptionBranch => BranchInfo::Exception, - UnresolvedBranch => BranchInfo::Unresolved, - UserDefinedBranch => BranchInfo::UserDefined, - }; - - Some((res, arch)) - } - _ => None, +impl BranchInfo { + /// Branches to an instruction with the current architecture. + pub fn new(kind: BranchKind) -> Self { + Self { + arch: None, + kind, } } -} -#[repr(C)] -pub struct InstructionInfo(BNInstructionInfo); -impl InstructionInfo { - pub fn new(len: usize, delay_slots: u8) -> Self { - InstructionInfo(BNInstructionInfo { - length: len, - archTransitionByTargetAddr: false, - delaySlots: delay_slots, - branchCount: 0usize, - branchType: [BranchType::UnresolvedBranch; 3], - branchTarget: [0u64; 3], - branchArch: [ptr::null_mut(); 3], - }) + /// Branches to an instruction with an explicit architecture. + /// + /// Use this if your architecture can transition to another architecture with a branch. + pub fn new_with_arch(kind: BranchKind, arch: CoreArchitecture) -> Self { + Self { + arch: Some(arch), + kind, + } } - - pub fn len(&self) -> usize { - self.0.length + + pub fn target(&self) -> Option { + match self.kind { + BranchKind::Unconditional(target) => Some(target), + BranchKind::False(target) => Some(target), + BranchKind::True(target) => Some(target), + BranchKind::Call(target) => Some(target), + _ => None, + } } +} - pub fn is_empty(&self) -> bool { - self.0.length == 0 +impl From for BNBranchType { + fn from(value: BranchInfo) -> Self { + match value.kind { + BranchKind::Unresolved => BNBranchType::UnresolvedBranch, + BranchKind::Unconditional(_) => BNBranchType::UnconditionalBranch, + BranchKind::False(_) => BNBranchType::FalseBranch, + BranchKind::True(_) => BNBranchType::TrueBranch, + BranchKind::Call(_) => BNBranchType::CallDestination, + BranchKind::FunctionReturn => BNBranchType::FunctionReturn, + BranchKind::SystemCall => BNBranchType::SystemCall, + BranchKind::Indirect => BNBranchType::IndirectBranch, + BranchKind::Exception => BNBranchType::ExceptionBranch, + BranchKind::UserDefined => BNBranchType::UserDefinedBranch, + } } +} - pub fn branch_count(&self) -> usize { - self.0.branchCount +impl From for BranchInfo { + fn from(value: BranchKind) -> Self { + Self { + arch: None, + kind: value, + } } +} - pub fn delay_slots(&self) -> u8 { - self.0.delaySlots - } +#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] +pub struct InstructionInfo { + pub length: usize, + pub arch_transition_by_target_addr: bool, + pub delay_slots: u8, + pub branches: [Option; 3] +} - pub fn branches(&self) -> BranchIter { - BranchIter(self, 0..self.branch_count()) +impl InstructionInfo { + pub fn new(length: usize, delay_slots: u8) -> Self { + Self { + length, + arch_transition_by_target_addr: false, + delay_slots, + branches: Default::default(), + } + } + + pub fn add_branch(&mut self, branch_info: impl Into) { + // Will go through each slot and attempt to add the branch info. + // TODO: Return a result with BranchInfoSlotsFilled error. + for branch in &mut self.branches { + if branch.is_none() { + *branch = Some(branch_info.into()); + return; + } + } } +} - pub fn allow_arch_transition_by_target_addr(&mut self, transition: bool) { - self.0.archTransitionByTargetAddr = transition; +impl From for InstructionInfo { + fn from(value: BNInstructionInfo) -> Self { + // TODO: This is quite ugly, but we destructure the branch info so this will have to do. + let mut branch_info = [None; 3]; + for i in 0..value.branchCount { + let branch_target = value.branchTarget[i]; + branch_info[i] = Some(BranchInfo { + kind: match value.branchType[i] { + BNBranchType::UnconditionalBranch => BranchKind::Unconditional(branch_target), + BNBranchType::FalseBranch => BranchKind::False(branch_target), + BNBranchType::TrueBranch => BranchKind::True(branch_target), + BNBranchType::CallDestination => BranchKind::Call(branch_target), + BNBranchType::FunctionReturn => BranchKind::FunctionReturn, + BNBranchType::SystemCall => BranchKind::SystemCall, + BNBranchType::IndirectBranch => BranchKind::Indirect, + BNBranchType::ExceptionBranch => BranchKind::Exception, + BNBranchType::UnresolvedBranch => BranchKind::Unresolved, + BNBranchType::UserDefinedBranch => BranchKind::UserDefined, + }, + arch: if value.branchArch[i].is_null() { + None + } else { + Some(unsafe { CoreArchitecture::from_raw(value.branchArch[i]) }) + }, + }); + } + Self { + length: value.length, + arch_transition_by_target_addr: value.archTransitionByTargetAddr, + delay_slots: value.delaySlots, + branches: branch_info, + } } +} - pub fn add_branch(&mut self, branch: BranchInfo, arch: Option) { - if self.0.branchCount < self.0.branchType.len() { - let idx = self.0.branchCount; - - let ty = match branch { - BranchInfo::Unconditional(t) => { - self.0.branchTarget[idx] = t; - BranchType::UnconditionalBranch - } - BranchInfo::False(t) => { - self.0.branchTarget[idx] = t; - BranchType::FalseBranch - } - BranchInfo::True(t) => { - self.0.branchTarget[idx] = t; - BranchType::TrueBranch - } - BranchInfo::Call(t) => { - self.0.branchTarget[idx] = t; - BranchType::CallDestination - } - BranchInfo::FunctionReturn => BranchType::FunctionReturn, - BranchInfo::SystemCall => BranchType::SystemCall, - BranchInfo::Indirect => BranchType::IndirectBranch, - BranchInfo::Exception => BranchType::ExceptionBranch, - BranchInfo::Unresolved => BranchType::UnresolvedBranch, - BranchInfo::UserDefined => BranchType::UserDefinedBranch, - }; - - self.0.branchType[idx] = ty; - self.0.branchArch[idx] = match arch { - Some(a) => a.0, - _ => ptr::null_mut(), - }; - - self.0.branchCount += 1; - } else { - error!("Attempt to branch to instruction with no additional branch space!"); +impl From for BNInstructionInfo { + fn from(value: InstructionInfo) -> Self { + let branch_count = value.branches.into_iter().filter(Option::is_some).count(); + // TODO: This is quite ugly, but we destructure the branch info so this will have to do. + let branch_info_0 = value.branches[0].unwrap_or_default(); + let branch_info_1 = value.branches[1].unwrap_or_default(); + let branch_info_2 = value.branches[2].unwrap_or_default(); + Self { + length: value.length, + branchCount: branch_count, + archTransitionByTargetAddr: value.arch_transition_by_target_addr, + delaySlots: value.delay_slots, + branchType: [ + branch_info_0.into(), + branch_info_1.into(), + branch_info_2.into(), + ], + branchTarget: [ + branch_info_0.target().unwrap_or_default(), + branch_info_1.target().unwrap_or_default(), + branch_info_2.target().unwrap_or_default(), + ], + branchArch: [ + branch_info_0.arch.map(|a| a.handle).unwrap_or(std::ptr::null_mut()), + branch_info_1.arch.map(|a| a.handle).unwrap_or(std::ptr::null_mut()), + branch_info_2.arch.map(|a| a.handle).unwrap_or(std::ptr::null_mut()), + ], } } } -use crate::functionrecognizer::FunctionRecognizer; -use crate::relocation::{CustomRelocationHandlerHandle, RelocationHandler}; -pub use binaryninjacore_sys::BNFlagRole as FlagRole; -pub use binaryninjacore_sys::BNImplicitRegisterExtend as ImplicitRegisterExtend; -pub use binaryninjacore_sys::BNLowLevelILFlagCondition as FlagCondition; - pub trait RegisterInfo: Sized { type RegType: Register; @@ -312,10 +354,11 @@ pub trait Intrinsic: Sized + Clone + Copy { /// Unique identifier for this `Intrinsic`. fn id(&self) -> u32; - /// Reeturns the list of the input names and types for this intrinsic. - fn inputs(&self) -> Vec>; + // TODO: Maybe just return `(String, Conf>)`? + /// List of the input names and types for this intrinsic. + fn inputs(&self) -> Vec; - /// Returns the list of the output types for this intrinsic. + /// List of the output types for this intrinsic. fn outputs(&self) -> Vec>>; } @@ -349,7 +392,7 @@ pub trait Architecture: 'static + Sized + AsRef { fn max_instr_len(&self) -> usize; fn opcode_display_len(&self) -> usize; - fn associated_arch_by_addr(&self, addr: &mut u64) -> CoreArchitecture; + fn associated_arch_by_addr(&self, addr: u64) -> CoreArchitecture; fn instruction_info(&self, data: &[u8], addr: u64) -> Option; fn instruction_text( @@ -640,7 +683,7 @@ impl FlagGroup for UnusedFlag { } /// Type for architrectures that do not use intrinsics. Will panic if accessed as an intrinsic. -#[derive(Clone, Copy, PartialEq, Eq, Hash)] +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] pub struct UnusedIntrinsic; impl Intrinsic for UnusedIntrinsic { @@ -650,7 +693,7 @@ impl Intrinsic for UnusedIntrinsic { fn id(&self) -> u32 { unreachable!() } - fn inputs(&self) -> Vec> { + fn inputs(&self) -> Vec { unreachable!() } fn outputs(&self) -> Vec>> { @@ -658,39 +701,65 @@ impl Intrinsic for UnusedIntrinsic { } } -pub struct CoreRegisterInfo(*mut BNArchitecture, u32, BNRegisterInfo); +#[derive(Debug, Copy, Clone)] +pub struct CoreRegisterInfo { + arch: *mut BNArchitecture, + id: u32, + info: BNRegisterInfo, +} + +impl CoreRegisterInfo { + pub fn new(arch: *mut BNArchitecture, id: u32, info: BNRegisterInfo) -> Self { + Self { + arch, + id, + info, + } + } +} + impl RegisterInfo for CoreRegisterInfo { type RegType = CoreRegister; fn parent(&self) -> Option { - if self.1 != self.2.fullWidthRegister { - Some(CoreRegister(self.0, self.2.fullWidthRegister)) + if self.id != self.info.fullWidthRegister { + Some(CoreRegister::new(self.arch, self.info.fullWidthRegister)) } else { None } } fn size(&self) -> usize { - self.2.size + self.info.size } fn offset(&self) -> usize { - self.2.offset + self.info.offset } fn implicit_extend(&self) -> ImplicitRegisterExtend { - self.2.extend + self.info.extend } } #[derive(Copy, Clone, Eq, PartialEq, Hash)] -pub struct CoreRegister(*mut BNArchitecture, u32); +pub struct CoreRegister { + arch: *mut BNArchitecture, + id: u32, +} + +impl CoreRegister { + pub fn new(arch: *mut BNArchitecture, id: u32) -> Self { + Self { arch, id } + } +} + impl Register for CoreRegister { type InfoType = CoreRegisterInfo; fn name(&self) -> Cow { unsafe { - let name = BNGetArchitectureRegisterName(self.0, self.1); + let name = BNGetArchitectureRegisterName(self.arch, self.id); // We need to guarantee ownership, as if we're still // a Borrowed variant we're about to free the underlying @@ -705,13 +774,13 @@ impl Register for CoreRegister { } fn info(&self) -> CoreRegisterInfo { - CoreRegisterInfo(self.0, self.1, unsafe { - BNGetArchitectureRegisterInfo(self.0, self.1) + CoreRegisterInfo::new(self.arch, self.id, unsafe { + BNGetArchitectureRegisterInfo(self.arch, self.id) }) } fn id(&self) -> u32 { - self.1 + self.id } } @@ -725,12 +794,23 @@ unsafe impl CoreArrayProviderInner for CoreRegister { unsafe fn free(raw: *mut Self::Raw, _count: usize, _context: &Self::Context) { BNFreeRegisterList(raw) } + unsafe fn wrap_raw<'a>(raw: &'a Self::Raw, context: &'a Self::Context) -> Self::Wrapped<'a> { - Self(context.0, *raw) + Self::new(context.handle, *raw) } } -pub struct CoreRegisterStackInfo(*mut BNArchitecture, BNRegisterStackInfo); +#[derive(Debug, Copy, Clone)] +pub struct CoreRegisterStackInfo { + arch: *mut BNArchitecture, + info: BNRegisterStackInfo, +} + +impl CoreRegisterStackInfo { + pub fn new(arch: *mut BNArchitecture, info: BNRegisterStackInfo) -> Self { + Self { arch, info } + } +} impl RegisterStackInfo for CoreRegisterStackInfo { type RegStackType = CoreRegisterStack; @@ -739,29 +819,38 @@ impl RegisterStackInfo for CoreRegisterStackInfo { fn storage_regs(&self) -> (Self::RegType, u32) { ( - CoreRegister(self.0, self.1.firstStorageReg), - self.1.storageCount, + CoreRegister::new(self.arch, self.info.firstStorageReg), + self.info.storageCount, ) } fn top_relative_regs(&self) -> Option<(Self::RegType, u32)> { - if self.1.topRelativeCount == 0 { + if self.info.topRelativeCount == 0 { None } else { Some(( - CoreRegister(self.0, self.1.firstTopRelativeReg), - self.1.topRelativeCount, + CoreRegister::new(self.arch, self.info.firstTopRelativeReg), + self.info.topRelativeCount, )) } } fn stack_top_reg(&self) -> Self::RegType { - CoreRegister(self.0, self.1.stackTopReg) + CoreRegister::new(self.arch, self.info.stackTopReg) } } -#[derive(Copy, Clone, Eq, PartialEq, Hash)] -pub struct CoreRegisterStack(*mut BNArchitecture, u32); +#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)] +pub struct CoreRegisterStack { + arch: *mut BNArchitecture, + id: u32 +} + +impl CoreRegisterStack { + pub fn new(arch: *mut BNArchitecture, id: u32) -> Self { + Self { arch, id } + } +} impl RegisterStack for CoreRegisterStack { type InfoType = CoreRegisterStackInfo; @@ -770,7 +859,7 @@ impl RegisterStack for CoreRegisterStack { fn name(&self) -> Cow { unsafe { - let name = BNGetArchitectureRegisterStackName(self.0, self.1); + let name = BNGetArchitectureRegisterStackName(self.arch, self.id); // We need to guarantee ownership, as if we're still // a Borrowed variant we're about to free the underlying @@ -785,24 +874,34 @@ impl RegisterStack for CoreRegisterStack { } fn info(&self) -> CoreRegisterStackInfo { - CoreRegisterStackInfo(self.0, unsafe { - BNGetArchitectureRegisterStackInfo(self.0, self.1) + CoreRegisterStackInfo::new(self.arch, unsafe { + BNGetArchitectureRegisterStackInfo(self.arch, self.id) }) } fn id(&self) -> u32 { - self.1 + self.id + } +} + +#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)] +pub struct CoreFlag { + arch: *mut BNArchitecture, + id: u32 +} + +impl CoreFlag { + pub fn new(arch: *mut BNArchitecture, id: u32) -> Self { + Self { arch, id } } } -#[derive(Copy, Clone, Eq, PartialEq, Hash)] -pub struct CoreFlag(*mut BNArchitecture, u32); impl Flag for CoreFlag { type FlagClass = CoreFlagClass; fn name(&self) -> Cow { unsafe { - let name = BNGetArchitectureFlagName(self.0, self.1); + let name = BNGetArchitectureFlagName(self.arch, self.id); // We need to guarantee ownership, as if we're still // a Borrowed variant we're about to free the underlying @@ -818,27 +917,37 @@ impl Flag for CoreFlag { fn role(&self, class: Option) -> FlagRole { let class_id = match class { - Some(class) => class.1, + Some(class) => class.id, _ => 0, }; - unsafe { BNGetArchitectureFlagRole(self.0, self.1, class_id) } + unsafe { BNGetArchitectureFlagRole(self.arch, self.id, class_id) } } fn id(&self) -> u32 { - self.1 + self.id } } #[derive(Copy, Clone, Eq, PartialEq, Hash)] -pub struct CoreFlagWrite(*mut BNArchitecture, u32); +pub struct CoreFlagWrite { + arch: *mut BNArchitecture, + id: u32 +} + +impl CoreFlagWrite { + pub fn new(arch: *mut BNArchitecture, id: u32) -> Self { + Self { arch, id } + } +} + impl FlagWrite for CoreFlagWrite { type FlagType = CoreFlag; type FlagClass = CoreFlagClass; fn name(&self) -> Cow { unsafe { - let name = BNGetArchitectureFlagWriteTypeName(self.0, self.1); + let name = BNGetArchitectureFlagWriteTypeName(self.arch, self.id); // We need to guarantee ownership, as if we're still // a Borrowed variant we're about to free the underlying @@ -852,20 +961,29 @@ impl FlagWrite for CoreFlagWrite { } } + fn class(&self) -> Option { + let class = unsafe { BNGetArchitectureSemanticClassForFlagWriteType(self.arch, self.id) }; + + match class { + 0 => None, + class_id => Some(CoreFlagClass::new(self.arch, class_id)), + } + } + fn id(&self) -> u32 { - self.1 + self.id } fn flags_written(&self) -> Vec { let mut count: usize = 0; let regs: *mut u32 = unsafe { - BNGetArchitectureFlagsWrittenByFlagWriteType(self.0, self.1, &mut count as *mut _) + BNGetArchitectureFlagsWrittenByFlagWriteType(self.arch, self.id, &mut count) }; let ret = unsafe { - slice::from_raw_parts_mut(regs, count) + std::slice::from_raw_parts(regs, count) .iter() - .map(|reg| CoreFlag(self.0, *reg)) + .map(|®| CoreFlag::new(self.arch, reg)) .collect() }; @@ -875,23 +993,24 @@ impl FlagWrite for CoreFlagWrite { ret } +} - fn class(&self) -> Option { - let class = unsafe { BNGetArchitectureSemanticClassForFlagWriteType(self.0, self.1) }; +#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)] +pub struct CoreFlagClass { + arch: *mut BNArchitecture, + id: u32 +} - match class { - 0 => None, - id => Some(CoreFlagClass(self.0, id)), - } +impl CoreFlagClass { + pub fn new(arch: *mut BNArchitecture, id: u32) -> Self { + Self { arch, id } } } -#[derive(Copy, Clone, Eq, PartialEq, Hash)] -pub struct CoreFlagClass(*mut BNArchitecture, u32); impl FlagClass for CoreFlagClass { fn name(&self) -> Cow { unsafe { - let name = BNGetArchitectureSemanticFlagClassName(self.0, self.1); + let name = BNGetArchitectureSemanticFlagClassName(self.arch, self.id); // We need to guarantee ownership, as if we're still // a Borrowed variant we're about to free the underlying @@ -906,19 +1025,29 @@ impl FlagClass for CoreFlagClass { } fn id(&self) -> u32 { - self.1 + self.id + } +} + +#[derive(Debug, Copy, Clone, Eq, PartialEq)] +pub struct CoreFlagGroup { + arch: *mut BNArchitecture, + id: u32 +} + +impl CoreFlagGroup { + pub fn new(arch: *mut BNArchitecture, id: u32) -> Self { + Self { arch, id } } } -#[derive(Copy, Clone, Eq, PartialEq)] -pub struct CoreFlagGroup(*mut BNArchitecture, u32); impl FlagGroup for CoreFlagGroup { type FlagType = CoreFlag; type FlagClass = CoreFlagClass; fn name(&self) -> Cow { unsafe { - let name = BNGetArchitectureSemanticFlagGroupName(self.0, self.1); + let name = BNGetArchitectureSemanticFlagGroupName(self.arch, self.id); // We need to guarantee ownership, as if we're still // a Borrowed variant we're about to free the underlying @@ -933,19 +1062,19 @@ impl FlagGroup for CoreFlagGroup { } fn id(&self) -> u32 { - self.1 + self.id } fn flags_required(&self) -> Vec { let mut count: usize = 0; let regs: *mut u32 = unsafe { - BNGetArchitectureFlagsRequiredForSemanticFlagGroup(self.0, self.1, &mut count as *mut _) + BNGetArchitectureFlagsRequiredForSemanticFlagGroup(self.arch, self.id, &mut count) }; let ret = unsafe { - slice::from_raw_parts_mut(regs, count) + std::slice::from_raw_parts(regs, count) .iter() - .map(|reg| CoreFlag(self.0, *reg)) + .map(|®| CoreFlag::new(self.arch, reg)) .collect() }; @@ -961,16 +1090,16 @@ impl FlagGroup for CoreFlagGroup { unsafe { let flag_conds = BNGetArchitectureFlagConditionsForSemanticFlagGroup( - self.0, - self.1, - &mut count as *mut _, + self.arch, + self.id, + &mut count, ); - let ret = slice::from_raw_parts_mut(flag_conds, count) + let ret = std::slice::from_raw_parts_mut(flag_conds, count) .iter() .map(|class_cond| { ( - CoreFlagClass(self.0, class_cond.semanticClass), + CoreFlagClass::new(self.arch, class_cond.semanticClass), class_cond.condition, ) }) @@ -984,16 +1113,26 @@ impl FlagGroup for CoreFlagGroup { } #[derive(Debug, Copy, Clone, Eq, PartialEq)] -pub struct CoreIntrinsic(pub(crate) *mut BNArchitecture, pub(crate) u32); +pub struct CoreIntrinsic { + pub arch: *mut BNArchitecture, + pub id: u32, +} -impl Intrinsic for crate::architecture::CoreIntrinsic { +impl CoreIntrinsic { + pub fn new(arch: *mut BNArchitecture, id: u32) -> Self { + Self { arch, id } + } +} + +impl Intrinsic for CoreIntrinsic { fn name(&self) -> Cow { unsafe { - let name = BNGetArchitectureIntrinsicName(self.0, self.1); + let name = BNGetArchitectureIntrinsicName(self.arch, self.id); // We need to guarantee ownership, as if we're still // a Borrowed variant we're about to free the underlying // memory. + // TODO: ^ the above assertion nullifies any benefit to passing back Cow tho? let res = CStr::from_ptr(name); let res = res.to_string_lossy().into_owned().into(); @@ -1004,18 +1143,18 @@ impl Intrinsic for crate::architecture::CoreIntrinsic { } fn id(&self) -> u32 { - self.1 + self.id } - fn inputs(&self) -> Vec> { + fn inputs(&self) -> Vec { let mut count: usize = 0; - unsafe { - let inputs = BNGetArchitectureIntrinsicInputs(self.0, self.1, &mut count as *mut _); + let inputs = BNGetArchitectureIntrinsicInputs(self.arch, self.id, &mut count); - let ret = slice::from_raw_parts_mut(inputs, count) + let ret = std::slice::from_raw_parts_mut(inputs, count) .iter() - .map(|x| NameAndType::from_raw(x).to_owned()) + .copied() + .map(Into::into) .collect(); BNFreeNameAndTypeList(inputs, count); @@ -1026,11 +1165,10 @@ impl Intrinsic for crate::architecture::CoreIntrinsic { fn outputs(&self) -> Vec>> { let mut count: usize = 0; - unsafe { - let inputs = BNGetArchitectureIntrinsicOutputs(self.0, self.1, &mut count as *mut _); + let inputs = BNGetArchitectureIntrinsicOutputs(self.arch, self.id, &mut count); - let ret = slice::from_raw_parts_mut(inputs, count) + let ret = std::slice::from_raw_parts_mut(inputs, count) .iter() .map(|input| (*input).into()) .collect(); @@ -1042,12 +1180,14 @@ impl Intrinsic for crate::architecture::CoreIntrinsic { } } +// TODO: WTF?!?!?!? pub struct CoreArchitectureList(*mut *mut BNArchitecture, usize); -impl ops::Deref for CoreArchitectureList { + +impl Deref for CoreArchitectureList { type Target = [CoreArchitecture]; fn deref(&self) -> &Self::Target { - unsafe { slice::from_raw_parts_mut(self.0 as *mut CoreArchitecture, self.1) } + unsafe { std::slice::from_raw_parts_mut(self.0 as *mut CoreArchitecture, self.1) } } } @@ -1060,37 +1200,39 @@ impl Drop for CoreArchitectureList { } #[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)] -pub struct CoreArchitecture(pub(crate) *mut BNArchitecture); - -unsafe impl Send for CoreArchitecture {} -unsafe impl Sync for CoreArchitecture {} +pub struct CoreArchitecture { + pub(crate) handle: *mut BNArchitecture +} impl CoreArchitecture { - pub(crate) unsafe fn from_raw(raw: *mut BNArchitecture) -> Self { - CoreArchitecture(raw) + pub(crate) unsafe fn from_raw(handle: *mut BNArchitecture) -> Self { + debug_assert!(!handle.is_null()); + CoreArchitecture { handle } } pub fn list_all() -> CoreArchitectureList { let mut count: usize = 0; - let archs = unsafe { BNGetArchitectureList(&mut count as *mut _) }; + let archs = unsafe { BNGetArchitectureList(&mut count) }; CoreArchitectureList(archs, count) } pub fn by_name(name: &str) -> Option { - let res = unsafe { BNGetArchitectureByName(name.into_bytes_with_nul().as_ptr() as *mut _) }; - - match res.is_null() { - false => Some(CoreArchitecture(res)), + let handle = unsafe { BNGetArchitectureByName(name.into_bytes_with_nul().as_ptr() as *mut _) }; + match handle.is_null() { + false => Some(CoreArchitecture { handle }), true => None, } } pub fn name(&self) -> BnString { - unsafe { BnString::from_raw(BNGetArchitectureName(self.0)) } + unsafe { BnString::from_raw(BNGetArchitectureName(self.handle)) } } } +unsafe impl Send for CoreArchitecture {} +unsafe impl Sync for CoreArchitecture {} + impl AsRef for CoreArchitecture { fn as_ref(&self) -> &Self { self @@ -1111,49 +1253,48 @@ impl Architecture for CoreArchitecture { type Intrinsic = CoreIntrinsic; fn endianness(&self) -> Endianness { - unsafe { BNGetArchitectureEndianness(self.0) } + unsafe { BNGetArchitectureEndianness(self.handle) } } fn address_size(&self) -> usize { - unsafe { BNGetArchitectureAddressSize(self.0) } + unsafe { BNGetArchitectureAddressSize(self.handle) } } fn default_integer_size(&self) -> usize { - unsafe { BNGetArchitectureDefaultIntegerSize(self.0) } + unsafe { BNGetArchitectureDefaultIntegerSize(self.handle) } } fn instruction_alignment(&self) -> usize { - unsafe { BNGetArchitectureInstructionAlignment(self.0) } + unsafe { BNGetArchitectureInstructionAlignment(self.handle) } } fn max_instr_len(&self) -> usize { - unsafe { BNGetArchitectureMaxInstructionLength(self.0) } + unsafe { BNGetArchitectureMaxInstructionLength(self.handle) } } fn opcode_display_len(&self) -> usize { - unsafe { BNGetArchitectureOpcodeDisplayLength(self.0) } + unsafe { BNGetArchitectureOpcodeDisplayLength(self.handle) } } - fn associated_arch_by_addr(&self, addr: &mut u64) -> CoreArchitecture { - let arch = unsafe { BNGetAssociatedArchitectureByAddress(self.0, addr as *mut _) }; - - CoreArchitecture(arch) + fn associated_arch_by_addr(&self, addr: u64) -> CoreArchitecture { + let handle = unsafe { BNGetAssociatedArchitectureByAddress(self.handle, addr as *mut _) }; + CoreArchitecture { handle } } fn instruction_info(&self, data: &[u8], addr: u64) -> Option { - let mut info = unsafe { zeroed::() }; + let mut info = BNInstructionInfo::default(); let success = unsafe { BNGetInstructionInfo( - self.0, + self.handle, data.as_ptr(), addr, data.len(), - &mut (info.0) as *mut _, + &mut info, ) }; if success { - Some(info) + Some(info.into()) } else { None } @@ -1166,18 +1307,18 @@ impl Architecture for CoreArchitecture { ) -> Option<(usize, Vec)> { let mut consumed = data.len(); let mut count: usize = 0; - let mut result: *mut BNInstructionTextToken = ptr::null_mut(); + let mut result: *mut BNInstructionTextToken = std::ptr::null_mut(); unsafe { if BNGetInstructionText( - self.0, + self.handle, data.as_ptr(), addr, - &mut consumed as *mut _, - &mut result as *mut _, - &mut count as *mut _, + &mut consumed, + &mut result, + &mut count, ) { - let vec = slice::from_raw_parts(result, count) + let vec = std::slice::from_raw_parts(result, count) .iter() .map(|x| InstructionTextToken::from_raw(x).to_owned()) .collect(); @@ -1197,7 +1338,7 @@ impl Architecture for CoreArchitecture { ) -> Option<(usize, bool)> { let mut size = data.len(); let success = unsafe { - BNGetInstructionLowLevelIL(self.0, data.as_ptr(), addr, &mut size as *mut _, il.handle) + BNGetInstructionLowLevelIL(self.handle, data.as_ptr(), addr, &mut size as *mut _, il.handle) }; if !success { @@ -1237,11 +1378,11 @@ impl Architecture for CoreArchitecture { fn registers_all(&self) -> Vec { unsafe { let mut count: usize = 0; - let regs = BNGetAllArchitectureRegisters(self.0, &mut count as *mut _); + let regs = BNGetAllArchitectureRegisters(self.handle, &mut count); - let ret = slice::from_raw_parts_mut(regs, count) + let ret = std::slice::from_raw_parts(regs, count) .iter() - .map(|reg| CoreRegister(self.0, *reg)) + .map(|®| CoreRegister::new(self.handle, reg)) .collect(); BNFreeRegisterList(regs); @@ -1253,11 +1394,11 @@ impl Architecture for CoreArchitecture { fn registers_full_width(&self) -> Vec { unsafe { let mut count: usize = 0; - let regs = BNGetFullWidthArchitectureRegisters(self.0, &mut count as *mut _); + let regs = BNGetFullWidthArchitectureRegisters(self.handle, &mut count); - let ret = slice::from_raw_parts_mut(regs, count) + let ret = std::slice::from_raw_parts(regs, count) .iter() - .map(|reg| CoreRegister(self.0, *reg)) + .map(|®| CoreRegister::new(self.handle, reg)) .collect(); BNFreeRegisterList(regs); @@ -1269,11 +1410,11 @@ impl Architecture for CoreArchitecture { fn registers_global(&self) -> Vec { unsafe { let mut count: usize = 0; - let regs = BNGetArchitectureGlobalRegisters(self.0, &mut count as *mut _); + let regs = BNGetArchitectureGlobalRegisters(self.handle, &mut count); - let ret = slice::from_raw_parts_mut(regs, count) + let ret = std::slice::from_raw_parts(regs, count) .iter() - .map(|reg| CoreRegister(self.0, *reg)) + .map(|®| CoreRegister::new(self.handle, reg)) .collect(); BNFreeRegisterList(regs); @@ -1285,11 +1426,11 @@ impl Architecture for CoreArchitecture { fn registers_system(&self) -> Vec { unsafe { let mut count: usize = 0; - let regs = BNGetArchitectureSystemRegisters(self.0, &mut count as *mut _); + let regs = BNGetArchitectureSystemRegisters(self.handle, &mut count); - let ret = slice::from_raw_parts_mut(regs, count) + let ret = std::slice::from_raw_parts(regs, count) .iter() - .map(|reg| CoreRegister(self.0, *reg)) + .map(|®| CoreRegister::new(self.handle, reg)) .collect(); BNFreeRegisterList(regs); @@ -1301,11 +1442,11 @@ impl Architecture for CoreArchitecture { fn register_stacks(&self) -> Vec { unsafe { let mut count: usize = 0; - let regs = BNGetAllArchitectureRegisterStacks(self.0, &mut count as *mut _); + let regs = BNGetAllArchitectureRegisterStacks(self.handle, &mut count); - let ret = slice::from_raw_parts_mut(regs, count) + let ret = std::slice::from_raw_parts(regs, count) .iter() - .map(|reg| CoreRegisterStack(self.0, *reg)) + .map(|®| CoreRegisterStack::new(self.handle, reg)) .collect(); BNFreeRegisterList(regs); @@ -1317,11 +1458,11 @@ impl Architecture for CoreArchitecture { fn flags(&self) -> Vec { unsafe { let mut count: usize = 0; - let regs = BNGetAllArchitectureFlags(self.0, &mut count as *mut _); + let regs = BNGetAllArchitectureFlags(self.handle, &mut count); - let ret = slice::from_raw_parts_mut(regs, count) + let ret = std::slice::from_raw_parts(regs, count) .iter() - .map(|reg| CoreFlag(self.0, *reg)) + .map(|®| CoreFlag::new(self.handle, reg)) .collect(); BNFreeRegisterList(regs); @@ -1333,11 +1474,11 @@ impl Architecture for CoreArchitecture { fn flag_write_types(&self) -> Vec { unsafe { let mut count: usize = 0; - let regs = BNGetAllArchitectureFlagWriteTypes(self.0, &mut count as *mut _); + let regs = BNGetAllArchitectureFlagWriteTypes(self.handle, &mut count); - let ret = slice::from_raw_parts_mut(regs, count) + let ret = std::slice::from_raw_parts(regs, count) .iter() - .map(|reg| CoreFlagWrite(self.0, *reg)) + .map(|®| CoreFlagWrite::new(self.handle, reg)) .collect(); BNFreeRegisterList(regs); @@ -1349,11 +1490,11 @@ impl Architecture for CoreArchitecture { fn flag_classes(&self) -> Vec { unsafe { let mut count: usize = 0; - let regs = BNGetAllArchitectureSemanticFlagClasses(self.0, &mut count as *mut _); + let regs = BNGetAllArchitectureSemanticFlagClasses(self.handle, &mut count); - let ret = slice::from_raw_parts_mut(regs, count) + let ret = std::slice::from_raw_parts(regs, count) .iter() - .map(|reg| CoreFlagClass(self.0, *reg)) + .map(|®| CoreFlagClass::new(self.handle, reg)) .collect(); BNFreeRegisterList(regs); @@ -1365,11 +1506,11 @@ impl Architecture for CoreArchitecture { fn flag_groups(&self) -> Vec { unsafe { let mut count: usize = 0; - let regs = BNGetAllArchitectureSemanticFlagGroups(self.0, &mut count as *mut _); + let regs = BNGetAllArchitectureSemanticFlagGroups(self.handle, &mut count); - let ret = slice::from_raw_parts_mut(regs, count) + let ret = std::slice::from_raw_parts(regs, count) .iter() - .map(|reg| CoreFlagGroup(self.0, *reg)) + .map(|®| CoreFlagGroup::new(self.handle, reg)) .collect(); BNFreeRegisterList(regs); @@ -1388,15 +1529,15 @@ impl Architecture for CoreArchitecture { unsafe { let mut count: usize = 0; let flags = BNGetArchitectureFlagsRequiredForFlagCondition( - self.0, + self.handle, condition, class_id, - &mut count as *mut _, + &mut count, ); - let ret = slice::from_raw_parts_mut(flags, count) + let ret = std::slice::from_raw_parts(flags, count) .iter() - .map(|flag| CoreFlag(self.0, *flag)) + .map(|&flag| CoreFlag::new(self.handle, flag)) .collect(); BNFreeRegisterList(flags); @@ -1406,57 +1547,57 @@ impl Architecture for CoreArchitecture { } fn stack_pointer_reg(&self) -> Option { - match unsafe { BNGetArchitectureStackPointerRegister(self.0) } { + match unsafe { BNGetArchitectureStackPointerRegister(self.handle) } { 0xffff_ffff => None, - reg => Some(CoreRegister(self.0, reg)), + reg => Some(CoreRegister::new(self.handle, reg)), } } fn link_reg(&self) -> Option { - match unsafe { BNGetArchitectureLinkRegister(self.0) } { + match unsafe { BNGetArchitectureLinkRegister(self.handle) } { 0xffff_ffff => None, - reg => Some(CoreRegister(self.0, reg)), + reg => Some(CoreRegister::new(self.handle, reg)), } } fn register_from_id(&self, id: u32) -> Option { // TODO validate in debug builds - Some(CoreRegister(self.0, id)) + Some(CoreRegister::new(self.handle, id)) } fn register_stack_from_id(&self, id: u32) -> Option { // TODO validate in debug builds - Some(CoreRegisterStack(self.0, id)) + Some(CoreRegisterStack::new(self.handle, id)) } fn flag_from_id(&self, id: u32) -> Option { // TODO validate in debug builds - Some(CoreFlag(self.0, id)) + Some(CoreFlag::new(self.handle, id)) } fn flag_write_from_id(&self, id: u32) -> Option { // TODO validate in debug builds - Some(CoreFlagWrite(self.0, id)) + Some(CoreFlagWrite::new(self.handle, id)) } fn flag_class_from_id(&self, id: u32) -> Option { // TODO validate in debug builds - Some(CoreFlagClass(self.0, id)) + Some(CoreFlagClass::new(self.handle, id)) } fn flag_group_from_id(&self, id: u32) -> Option { // TODO validate in debug builds - Some(CoreFlagGroup(self.0, id)) + Some(CoreFlagGroup::new(self.handle, id)) } fn intrinsics(&self) -> Vec { unsafe { let mut count: usize = 0; - let intrinsics = BNGetAllArchitectureIntrinsics(self.0, &mut count as *mut _); + let intrinsics = BNGetAllArchitectureIntrinsics(self.handle, &mut count); - let ret = slice::from_raw_parts_mut(intrinsics, count) + let ret = std::slice::from_raw_parts_mut(intrinsics, count) .iter() - .map(|reg| CoreIntrinsic(self.0, *reg)) + .map(|®| CoreIntrinsic::new(self.handle, reg)) .collect(); BNFreeRegisterList(intrinsics); @@ -1465,17 +1606,17 @@ impl Architecture for CoreArchitecture { } } - fn intrinsic_class(&self, id: u32) -> binaryninjacore_sys::BNIntrinsicClass { - unsafe { BNGetArchitectureIntrinsicClass(self.0, id) } + fn intrinsic_class(&self, id: u32) -> BNIntrinsicClass { + unsafe { BNGetArchitectureIntrinsicClass(self.handle, id) } } fn intrinsic_from_id(&self, id: u32) -> Option { // TODO validate in debug builds - Some(CoreIntrinsic(self.0, id)) + Some(CoreIntrinsic::new(self.handle, id)) } fn can_assemble(&self) -> bool { - unsafe { BNCanArchitectureAssemble(self.0) } + unsafe { BNCanArchitectureAssemble(self.handle) } } fn assemble(&self, code: &str, addr: u64) -> Result, String> { @@ -1485,10 +1626,10 @@ impl Architecture for CoreArchitecture { Ok(result) => result, Err(_) => return Err("Result buffer allocation failed".to_string()), }; - let mut error_raw: *mut c_char = ptr::null_mut(); + let mut error_raw: *mut c_char = std::ptr::null_mut(); let res = unsafe { BNAssemble( - self.0, + self.handle, code.as_ptr(), addr, result.as_raw(), @@ -1510,32 +1651,32 @@ impl Architecture for CoreArchitecture { fn is_never_branch_patch_available(&self, data: &[u8], addr: u64) -> bool { unsafe { - BNIsArchitectureNeverBranchPatchAvailable(self.0, data.as_ptr(), addr, data.len()) + BNIsArchitectureNeverBranchPatchAvailable(self.handle, data.as_ptr(), addr, data.len()) } } fn is_always_branch_patch_available(&self, data: &[u8], addr: u64) -> bool { unsafe { - BNIsArchitectureAlwaysBranchPatchAvailable(self.0, data.as_ptr(), addr, data.len()) + BNIsArchitectureAlwaysBranchPatchAvailable(self.handle, data.as_ptr(), addr, data.len()) } } fn is_invert_branch_patch_available(&self, data: &[u8], addr: u64) -> bool { unsafe { - BNIsArchitectureInvertBranchPatchAvailable(self.0, data.as_ptr(), addr, data.len()) + BNIsArchitectureInvertBranchPatchAvailable(self.handle, data.as_ptr(), addr, data.len()) } } fn is_skip_and_return_zero_patch_available(&self, data: &[u8], addr: u64) -> bool { unsafe { - BNIsArchitectureSkipAndReturnZeroPatchAvailable(self.0, data.as_ptr(), addr, data.len()) + BNIsArchitectureSkipAndReturnZeroPatchAvailable(self.handle, data.as_ptr(), addr, data.len()) } } fn is_skip_and_return_value_patch_available(&self, data: &[u8], addr: u64) -> bool { unsafe { BNIsArchitectureSkipAndReturnValuePatchAvailable( - self.0, + self.handle, data.as_ptr(), addr, data.len(), @@ -1544,20 +1685,20 @@ impl Architecture for CoreArchitecture { } fn convert_to_nop(&self, data: &mut [u8], addr: u64) -> bool { - unsafe { BNArchitectureConvertToNop(self.0, data.as_mut_ptr(), addr, data.len()) } + unsafe { BNArchitectureConvertToNop(self.handle, data.as_mut_ptr(), addr, data.len()) } } fn always_branch(&self, data: &mut [u8], addr: u64) -> bool { - unsafe { BNArchitectureAlwaysBranch(self.0, data.as_mut_ptr(), addr, data.len()) } + unsafe { BNArchitectureAlwaysBranch(self.handle, data.as_mut_ptr(), addr, data.len()) } } fn invert_branch(&self, data: &mut [u8], addr: u64) -> bool { - unsafe { BNArchitectureInvertBranch(self.0, data.as_mut_ptr(), addr, data.len()) } + unsafe { BNArchitectureInvertBranch(self.handle, data.as_mut_ptr(), addr, data.len()) } } fn skip_and_return_value(&self, data: &mut [u8], addr: u64, value: u64) -> bool { unsafe { - BNArchitectureSkipAndReturnValue(self.0, data.as_mut_ptr(), addr, data.len(), value) + BNArchitectureSkipAndReturnValue(self.handle, data.as_mut_ptr(), addr, data.len(), value) } } @@ -1569,10 +1710,10 @@ impl Architecture for CoreArchitecture { macro_rules! cc_func { ($get_name:ident, $get_api:ident, $set_name:ident, $set_api:ident) => { fn $get_name(&self) -> Option>> { - let handle = self.as_ref(); + let arch = self.as_ref(); unsafe { - let cc = $get_api(handle.0); + let cc = $get_api(arch.handle); if cc.is_null() { None @@ -1583,15 +1724,15 @@ macro_rules! cc_func { } fn $set_name(&self, cc: &CallingConvention) { - let handle = self.as_ref(); + let arch = self.as_ref(); assert!( - cc.arch_handle.borrow().as_ref().0 == handle.0, + cc.arch_handle.borrow().as_ref().handle == arch.handle, "use of calling convention with non-matching architecture!" ); unsafe { - $set_api(handle.0, cc.handle); + $set_api(arch.handle, cc.handle); } } }; @@ -1603,7 +1744,7 @@ pub trait ArchitectureExt: Architecture { let name = name.into_bytes_with_nul(); match unsafe { - BNGetArchitectureRegisterByName(self.as_ref().0, name.as_ref().as_ptr() as *mut _) + BNGetArchitectureRegisterByName(self.as_ref().handle, name.as_ref().as_ptr() as *mut _) } { 0xffff_ffff => None, reg => self.register_from_id(reg), @@ -1613,7 +1754,7 @@ pub trait ArchitectureExt: Architecture { fn calling_conventions(&self) -> Array> { unsafe { let mut count = 0; - let calling_convs = BNGetArchitectureCallingConventions(self.as_ref().0, &mut count); + let calling_convs = BNGetArchitectureCallingConventions(self.as_ref().handle, &mut count); Array::new(calling_convs, count, self.handle()) } } @@ -1648,7 +1789,7 @@ pub trait ArchitectureExt: Architecture { fn standalone_platform(&self) -> Option> { unsafe { - let handle = BNGetArchitectureStandalonePlatform(self.as_ref().0); + let handle = BNGetArchitectureStandalonePlatform(self.as_ref().handle); if handle.is_null() { return None; @@ -1665,7 +1806,7 @@ pub trait ArchitectureExt: Architecture { }; unsafe { - let handle = BNArchitectureGetRelocationHandler(self.as_ref().0, view_name.as_ptr()); + let handle = BNArchitectureGetRelocationHandler(self.as_ref().handle, view_name.as_ptr()); if handle.is_null() { return None; @@ -1704,9 +1845,6 @@ where A: 'static + Architecture> + Send + Sync + Sized, F: FnOnce(CustomArchitectureHandle, CoreArchitecture) -> A, { - use std::mem; - use std::os::raw::{c_char, c_void}; - #[repr(C)] struct ArchitectureBuilder where @@ -1731,7 +1869,7 @@ where let create = custom_arch.func.take().unwrap(); custom_arch .arch - .write(create(custom_arch_handle, CoreArchitecture(obj))); + .write(create(custom_arch_handle, CoreArchitecture::from_raw(obj))); } } @@ -1791,9 +1929,9 @@ where A: 'static + Architecture> + Send + Sync, { let custom_arch = unsafe { &*(ctxt as *mut A) }; - let addr = unsafe { &mut *(addr) }; + let addr = unsafe { *(addr) }; - custom_arch.associated_arch_by_addr(addr).0 + custom_arch.associated_arch_by_addr(addr).handle } extern "C" fn cb_instruction_info( @@ -1807,12 +1945,12 @@ where A: 'static + Architecture> + Send + Sync, { let custom_arch = unsafe { &*(ctxt as *mut A) }; - let data = unsafe { slice::from_raw_parts(data, len) }; - let result = unsafe { &mut *(result as *mut InstructionInfo) }; + let data = unsafe { std::slice::from_raw_parts(data, len) }; match custom_arch.instruction_info(data, addr) { Some(info) => { - result.0 = info.0; + // SAFETY: Passed in to be written to + unsafe { *result = info.into() }; true } None => false, @@ -1831,7 +1969,7 @@ where A: 'static + Architecture> + Send + Sync, { let custom_arch = unsafe { &*(ctxt as *mut A) }; - let data = unsafe { slice::from_raw_parts(data, *len) }; + let data = unsafe { std::slice::from_raw_parts(data, *len) }; let result = unsafe { &mut *result }; let Some((res_size, res_tokens)) = custom_arch.instruction_text(data, addr) else { @@ -1852,7 +1990,7 @@ where } extern "C" fn cb_free_instruction_text(tokens: *mut BNInstructionTextToken, count: usize) { - let _tokens = unsafe { Box::from_raw(ptr::slice_from_raw_parts_mut(tokens, count)) }; + let _tokens = unsafe { Box::from_raw(std::ptr::slice_from_raw_parts_mut(tokens, count)) }; } extern "C" fn cb_instruction_llil( @@ -1870,7 +2008,7 @@ where handle: ctxt as *mut A, }; - let data = unsafe { slice::from_raw_parts(data, *len) }; + let data = unsafe { std::slice::from_raw_parts(data, *len) }; let mut lifter = unsafe { Lifter::from_raw(custom_arch_handle, il) }; match custom_arch.instruction_llil(data, addr, &mut lifter) { @@ -1947,7 +2085,7 @@ where A: 'static + Architecture> + Send + Sync, { let custom_arch = unsafe { &*(ctxt as *mut A) }; - let mut regs: Vec<_> = custom_arch + let mut regs: Box<[_]> = custom_arch .registers_full_width() .iter() .map(|r| r.id()) @@ -1956,7 +2094,7 @@ where // SAFETY: `count` is an out parameter unsafe { *count = regs.len() }; let regs_ptr = regs.as_mut_ptr(); - mem::forget(regs); + std::mem::forget(regs); regs_ptr } @@ -1965,12 +2103,12 @@ where A: 'static + Architecture> + Send + Sync, { let custom_arch = unsafe { &*(ctxt as *mut A) }; - let mut regs: Vec<_> = custom_arch.registers_all().iter().map(|r| r.id()).collect(); + let mut regs: Box<[_]> = custom_arch.registers_all().iter().map(|r| r.id()).collect(); // SAFETY: `count` is an out parameter unsafe { *count = regs.len() }; let regs_ptr = regs.as_mut_ptr(); - mem::forget(regs); + std::mem::forget(regs); regs_ptr } @@ -1979,7 +2117,7 @@ where A: 'static + Architecture> + Send + Sync, { let custom_arch = unsafe { &*(ctxt as *mut A) }; - let mut regs: Vec<_> = custom_arch + let mut regs: Box<[_]> = custom_arch .registers_global() .iter() .map(|r| r.id()) @@ -1988,7 +2126,7 @@ where // SAFETY: `count` is an out parameter unsafe { *count = regs.len() }; let regs_ptr = regs.as_mut_ptr(); - mem::forget(regs); + std::mem::forget(regs); regs_ptr } @@ -1997,7 +2135,7 @@ where A: 'static + Architecture> + Send + Sync, { let custom_arch = unsafe { &*(ctxt as *mut A) }; - let mut regs: Vec<_> = custom_arch + let mut regs: Box<[_]> = custom_arch .registers_system() .iter() .map(|r| r.id()) @@ -2006,7 +2144,7 @@ where // SAFETY: `count` is an out parameter unsafe { *count = regs.len() }; let regs_ptr = regs.as_mut_ptr(); - mem::forget(regs); + std::mem::forget(regs); regs_ptr } @@ -2015,12 +2153,12 @@ where A: 'static + Architecture> + Send + Sync, { let custom_arch = unsafe { &*(ctxt as *mut A) }; - let mut flags: Vec<_> = custom_arch.flags().iter().map(|f| f.id()).collect(); + let mut flags: Box<[_]> = custom_arch.flags().iter().map(|f| f.id()).collect(); // SAFETY: `count` is an out parameter unsafe { *count = flags.len() }; let flags_ptr = flags.as_mut_ptr(); - mem::forget(flags); + std::mem::forget(flags); flags_ptr } @@ -2029,7 +2167,7 @@ where A: 'static + Architecture> + Send + Sync, { let custom_arch = unsafe { &*(ctxt as *mut A) }; - let mut flag_writes: Vec<_> = custom_arch + let mut flag_writes: Box<[_]> = custom_arch .flag_write_types() .iter() .map(|f| f.id()) @@ -2038,7 +2176,7 @@ where // SAFETY: `count` is an out parameter unsafe { *count = flag_writes.len() }; let flags_ptr = flag_writes.as_mut_ptr(); - mem::forget(flag_writes); + std::mem::forget(flag_writes); flags_ptr } @@ -2047,12 +2185,12 @@ where A: 'static + Architecture> + Send + Sync, { let custom_arch = unsafe { &*(ctxt as *mut A) }; - let mut flag_classes: Vec<_> = custom_arch.flag_classes().iter().map(|f| f.id()).collect(); + let mut flag_classes: Box<[_]> = custom_arch.flag_classes().iter().map(|f| f.id()).collect(); // SAFETY: `count` is an out parameter unsafe { *count = flag_classes.len() }; let flags_ptr = flag_classes.as_mut_ptr(); - mem::forget(flag_classes); + std::mem::forget(flag_classes); flags_ptr } @@ -2061,12 +2199,12 @@ where A: 'static + Architecture> + Send + Sync, { let custom_arch = unsafe { &*(ctxt as *mut A) }; - let mut flag_groups: Vec<_> = custom_arch.flag_groups().iter().map(|f| f.id()).collect(); + let mut flag_groups: Box<[_]> = custom_arch.flag_groups().iter().map(|f| f.id()).collect(); // SAFETY: `count` is an out parameter unsafe { *count = flag_groups.len() }; let flags_ptr = flag_groups.as_mut_ptr(); - mem::forget(flag_groups); + std::mem::forget(flag_groups); flags_ptr } @@ -2097,7 +2235,7 @@ where { let custom_arch = unsafe { &*(ctxt as *mut A) }; let class = custom_arch.flag_class_from_id(class); - let mut flags: Vec<_> = custom_arch + let mut flags: Box<[_]> = custom_arch .flags_required_for_flag_condition(cond, class) .iter() .map(|f| f.id()) @@ -2106,7 +2244,7 @@ where // SAFETY: `count` is an out parameter unsafe { *count = flags.len() }; let flags_ptr = flags.as_mut_ptr(); - mem::forget(flags); + std::mem::forget(flags); flags_ptr } @@ -2121,18 +2259,18 @@ where let custom_arch = unsafe { &*(ctxt as *mut A) }; if let Some(group) = custom_arch.flag_group_from_id(group) { - let mut flags: Vec<_> = group.flags_required().iter().map(|f| f.id()).collect(); + let mut flags: Box<[_]> = group.flags_required().iter().map(|f| f.id()).collect(); // SAFETY: `count` is an out parameter unsafe { *count = flags.len() }; let flags_ptr = flags.as_mut_ptr(); - mem::forget(flags); + std::mem::forget(flags); flags_ptr } else { unsafe { *count = 0; } - ptr::null_mut() + std::ptr::null_mut() } } @@ -2148,24 +2286,24 @@ where if let Some(group) = custom_arch.flag_group_from_id(group) { let flag_conditions = group.flag_conditions(); - let mut flags = flag_conditions + let mut flags: Box<[_]> = flag_conditions .iter() .map(|(&class, &condition)| BNFlagConditionForSemanticClass { semanticClass: class.id(), condition, }) - .collect::>(); + .collect(); // SAFETY: `count` is an out parameter unsafe { *count = flags.len() }; let flags_ptr = flags.as_mut_ptr(); - mem::forget(flags); + std::mem::forget(flags); flags_ptr } else { unsafe { *count = 0; } - ptr::null_mut() + std::ptr::null_mut() } } @@ -2181,7 +2319,7 @@ where } unsafe { - let flags_ptr = ptr::slice_from_raw_parts_mut(conds, count); + let flags_ptr = std::ptr::slice_from_raw_parts_mut(conds, count); let _flags = Box::from_raw(flags_ptr); } } @@ -2197,19 +2335,19 @@ where let custom_arch = unsafe { &*(ctxt as *mut A) }; if let Some(write_type) = custom_arch.flag_write_from_id(write_type) { - let mut flags_written: Vec<_> = + let mut flags_written: Box<[_]> = write_type.flags_written().iter().map(|f| f.id()).collect(); // SAFETY: `count` is an out parameter unsafe { *count = flags_written.len() }; let flags_ptr = flags_written.as_mut_ptr(); - mem::forget(flags_written); + std::mem::forget(flags_written); flags_ptr } else { unsafe { *count = 0; } - ptr::null_mut() + std::ptr::null_mut() } } @@ -2248,7 +2386,7 @@ where let flag_write = custom_arch.flag_write_from_id(flag_write); let flag = custom_arch.flag_from_id(flag); - let operands = unsafe { slice::from_raw_parts(operands_raw, operand_count) }; + let operands = unsafe { std::slice::from_raw_parts(operands_raw, operand_count) }; let mut lifter = unsafe { Lifter::from_raw(custom_arch_handle, il) }; if let (Some(flag_write), Some(flag)) = (flag_write, flag) { @@ -2269,7 +2407,7 @@ where unsafe { BNGetDefaultArchitectureFlagWriteLowLevelIL( - custom_arch.as_ref().0, + custom_arch.as_ref().handle, op, size, role, @@ -2341,7 +2479,7 @@ where } unsafe { - let regs_ptr = ptr::slice_from_raw_parts_mut(regs, count); + let regs_ptr = std::ptr::slice_from_raw_parts_mut(regs, count); let _regs = Box::from_raw(regs_ptr); } } @@ -2410,7 +2548,7 @@ where A: 'static + Architecture> + Send + Sync, { let custom_arch = unsafe { &*(ctxt as *mut A) }; - let mut regs: Vec<_> = custom_arch + let mut regs: Box<[_]> = custom_arch .register_stacks() .iter() .map(|r| r.id()) @@ -2419,7 +2557,7 @@ where // SAFETY: Passed in to be written unsafe { *count = regs.len() }; let regs_ptr = regs.as_mut_ptr(); - mem::forget(regs); + std::mem::forget(regs); regs_ptr } @@ -2476,12 +2614,12 @@ where A: 'static + Architecture> + Send + Sync, { let custom_arch = unsafe { &*(ctxt as *mut A) }; - let mut intrinsics: Vec<_> = custom_arch.intrinsics().iter().map(|i| i.id()).collect(); + let mut intrinsics: Box<[_]> = custom_arch.intrinsics().iter().map(|i| i.id()).collect(); // SAFETY: Passed in to be written unsafe { *count = intrinsics.len() }; let intrinsics_ptr = intrinsics.as_mut_ptr(); - mem::forget(intrinsics); + std::mem::forget(intrinsics); intrinsics_ptr } @@ -2496,44 +2634,42 @@ where let custom_arch = unsafe { &*(ctxt as *mut A) }; let Some(intrinsic) = custom_arch.intrinsic_from_id(intrinsic) else { - unsafe { - *count = 0; - } - return ptr::null_mut(); + // SAFETY: Passed in to be written + unsafe { *count = 0; } + return std::ptr::null_mut(); }; let inputs = intrinsic.inputs(); - let mut res: Box<[_]> = inputs + let mut raw_inputs: Box<[_]> = inputs .into_iter() - .map(|input| unsafe { Ref::into_raw(input) }.0) + .map(Into::into) .collect(); - - unsafe { - *count = res.len(); - if res.is_empty() { - ptr::null_mut() - } else { - let raw = res.as_mut_ptr(); - mem::forget(res); - raw - } + + // SAFETY: Passed in to be written + unsafe { *count = raw_inputs.len(); } + + if raw_inputs.is_empty() { + std::ptr::null_mut() + } else { + let raw_ptr = raw_inputs.as_mut_ptr(); + // Core is responsible for calling back to `cb_free_name_and_types`. + std::mem::forget(raw_inputs); + raw_ptr } } - extern "C" fn cb_free_name_and_types(ctxt: *mut c_void, nt: *mut BNNameAndType, count: usize) + extern "C" fn cb_free_name_and_types(_ctxt: *mut c_void, nt: *mut BNNameAndType, count: usize) where A: 'static + Architecture> + Send + Sync, { - let _custom_arch = unsafe { &*(ctxt as *mut A) }; - - if !nt.is_null() { - unsafe { - let name_and_types = Box::from_raw(ptr::slice_from_raw_parts_mut(nt, count)); - for nt in name_and_types.iter() { - Ref::new(NameAndType::from_raw(nt)); - } - } + if nt.is_null() { + return; } + + // Reconstruct the box and drop. + let nt_ptr = std::ptr::slice_from_raw_parts_mut(nt, count); + // SAFETY: nt_ptr is a pointer to a Box. + let _ = unsafe { Box::from_raw(nt_ptr) }; } extern "C" fn cb_intrinsic_outputs( @@ -2546,25 +2682,28 @@ where { let custom_arch = unsafe { &*(ctxt as *mut A) }; - if let Some(intrinsic) = custom_arch.intrinsic_from_id(intrinsic) { - let inputs = intrinsic.outputs(); - let mut res: Box<[_]> = inputs.iter().map(|input| input.as_ref().into()).collect(); + let Some(intrinsic) = custom_arch.intrinsic_from_id(intrinsic) else { + // SAFETY: Passed in to be written + unsafe { *count = 0; } + return std::ptr::null_mut(); + }; - unsafe { - *count = res.len(); - if res.is_empty() { - ptr::null_mut() - } else { - let raw = res.as_mut_ptr(); - mem::forget(res); - raw - } - } + let outputs = intrinsic.outputs(); + let mut raw_outputs: Box<[BNTypeWithConfidence]> = outputs + .into_iter() + .map(|o| o.as_ref().into()) + .collect(); + + // SAFETY: Passed in to be written + unsafe { *count = raw_outputs.len(); } + + if raw_outputs.is_empty() { + std::ptr::null_mut() } else { - unsafe { - *count = 0; - } - ptr::null_mut() + let raw_ptr = raw_outputs.as_mut_ptr(); + // Core is responsible for calling back to `cb_free_name_and_types`. + std::mem::forget(raw_outputs); + raw_ptr } } @@ -2577,7 +2716,7 @@ where { let _custom_arch = unsafe { &*(ctxt as *mut A) }; if !tl.is_null() { - let _type_list = unsafe { Box::from_raw(ptr::slice_from_raw_parts_mut(tl, count)) }; + let _type_list = unsafe { Box::from_raw(std::ptr::slice_from_raw_parts_mut(tl, count)) }; } } @@ -2620,7 +2759,7 @@ where }; // Caller owns the data buffer, don't free it - mem::forget(buffer); + std::mem::forget(buffer); result } @@ -2635,7 +2774,7 @@ where A: 'static + Architecture> + Send + Sync, { let custom_arch = unsafe { &*(ctxt as *mut A) }; - let data = unsafe { slice::from_raw_parts(data, len) }; + let data = unsafe { std::slice::from_raw_parts(data, len) }; custom_arch.is_never_branch_patch_available(data, addr) } @@ -2649,7 +2788,7 @@ where A: 'static + Architecture> + Send + Sync, { let custom_arch = unsafe { &*(ctxt as *mut A) }; - let data = unsafe { slice::from_raw_parts(data, len) }; + let data = unsafe { std::slice::from_raw_parts(data, len) }; custom_arch.is_always_branch_patch_available(data, addr) } @@ -2663,7 +2802,7 @@ where A: 'static + Architecture> + Send + Sync, { let custom_arch = unsafe { &*(ctxt as *mut A) }; - let data = unsafe { slice::from_raw_parts(data, len) }; + let data = unsafe { std::slice::from_raw_parts(data, len) }; custom_arch.is_invert_branch_patch_available(data, addr) } @@ -2677,7 +2816,7 @@ where A: 'static + Architecture> + Send + Sync, { let custom_arch = unsafe { &*(ctxt as *mut A) }; - let data = unsafe { slice::from_raw_parts(data, len) }; + let data = unsafe { std::slice::from_raw_parts(data, len) }; custom_arch.is_skip_and_return_zero_patch_available(data, addr) } @@ -2691,7 +2830,7 @@ where A: 'static + Architecture> + Send + Sync, { let custom_arch = unsafe { &*(ctxt as *mut A) }; - let data = unsafe { slice::from_raw_parts(data, len) }; + let data = unsafe { std::slice::from_raw_parts(data, len) }; custom_arch.is_skip_and_return_value_patch_available(data, addr) } @@ -2705,7 +2844,7 @@ where A: 'static + Architecture> + Send + Sync, { let custom_arch = unsafe { &*(ctxt as *mut A) }; - let data = unsafe { slice::from_raw_parts_mut(data, len) }; + let data = unsafe { std::slice::from_raw_parts_mut(data, len) }; custom_arch.convert_to_nop(data, addr) } @@ -2719,7 +2858,7 @@ where A: 'static + Architecture> + Send + Sync, { let custom_arch = unsafe { &*(ctxt as *mut A) }; - let data = unsafe { slice::from_raw_parts_mut(data, len) }; + let data = unsafe { std::slice::from_raw_parts_mut(data, len) }; custom_arch.always_branch(data, addr) } @@ -2733,7 +2872,7 @@ where A: 'static + Architecture> + Send + Sync, { let custom_arch = unsafe { &*(ctxt as *mut A) }; - let data = unsafe { slice::from_raw_parts_mut(data, len) }; + let data = unsafe { std::slice::from_raw_parts_mut(data, len) }; custom_arch.invert_branch(data, addr) } @@ -2748,7 +2887,7 @@ where A: 'static + Architecture> + Send + Sync, { let custom_arch = unsafe { &*(ctxt as *mut A) }; - let data = unsafe { slice::from_raw_parts_mut(data, len) }; + let data = unsafe { std::slice::from_raw_parts_mut(data, len) }; custom_arch.skip_and_return_value(data, addr, val) } @@ -2923,9 +3062,9 @@ pub fn llvm_assemble( let code = CString::new(code).map_err(|_| "Invalid encoding in code string".to_string())?; let arch_triple = CString::new(arch_triple) .map_err(|_| "Invalid encoding in architecture triple string".to_string())?; - let mut out_bytes: *mut c_char = ptr::null_mut(); + let mut out_bytes: *mut c_char = std::ptr::null_mut(); let mut out_bytes_len: c_int = 0; - let mut err_bytes: *mut c_char = ptr::null_mut(); + let mut err_bytes: *mut c_char = std::ptr::null_mut(); let mut err_len: c_int = 0; unsafe { @@ -2950,7 +3089,7 @@ pub fn llvm_assemble( Vec::new() } else { unsafe { - slice::from_raw_parts( + std::slice::from_raw_parts( out_bytes as *const c_char as *const u8, out_bytes_len as usize, ) @@ -2962,7 +3101,7 @@ pub fn llvm_assemble( "".into() } else { String::from_utf8_lossy(unsafe { - slice::from_raw_parts(err_bytes as *const c_char as *const u8, err_len as usize) + std::slice::from_raw_parts(err_bytes as *const c_char as *const u8, err_len as usize) }) .into_owned() }; diff --git a/rust/src/basicblock.rs b/rust/src/basicblock.rs index a6ac8f745..e14af3387 100644 --- a/rust/src/basicblock.rs +++ b/rust/src/basicblock.rs @@ -17,7 +17,7 @@ use std::fmt; use crate::architecture::CoreArchitecture; use crate::function::Function; use binaryninjacore_sys::*; - +use crate::BranchType; use crate::rc::*; enum EdgeDirection { @@ -26,7 +26,7 @@ enum EdgeDirection { } pub struct Edge<'a, C: 'a + BlockContext> { - branch: super::BranchType, + branch: BranchType, back_edge: bool, source: Guard<'a, BasicBlock>, target: Guard<'a, BasicBlock>, @@ -128,7 +128,7 @@ impl BasicBlock { pub fn function(&self) -> Ref { unsafe { let func = BNGetBasicBlockFunction(self.handle); - Function::from_raw(func) + Function::ref_from_raw(func) } } diff --git a/rust/src/binaryview.rs b/rust/src/binaryview.rs index 47f0c18d2..1decbc21e 100644 --- a/rust/src/binaryview.rs +++ b/rust/src/binaryview.rs @@ -30,6 +30,7 @@ use std::{ops, ptr, result, slice}; use crate::architecture::{Architecture, CoreArchitecture}; use crate::basicblock::BasicBlock; use crate::component::{Component, ComponentBuilder, IntoComponentGuid}; +use crate::confidence::Conf; use crate::databuffer::DataBuffer; use crate::debuginfo::DebugInfo; use crate::fileaccessor::FileAccessor; @@ -47,14 +48,14 @@ use crate::symbol::{Symbol, SymbolType}; use crate::tags::{Tag, TagType}; use crate::typelibrary::TypeLibrary; use crate::types::{ - Conf, DataVariable, NamedTypeReference, QualifiedName, QualifiedNameAndType, Type, + NamedTypeReference, QualifiedName, QualifiedNameAndType, Type, }; use crate::Endianness; use crate::rc::*; use crate::references::{CodeReference, DataReference}; use crate::string::*; - +use crate::variable::DataVariable; // TODO : general reorg of modules related to bv pub type Result = result::Result; @@ -194,27 +195,16 @@ pub trait BinaryViewExt: BinaryViewBase { unsafe { BnString::from_raw(ptr) } } - fn parent_view(&self) -> Result> { - let handle = unsafe { BNGetParentView(self.as_ref().handle) }; - - if handle.is_null() { - return Err(()); + fn parent_view(&self) -> Option> { + let raw_view_ptr = unsafe { BNGetParentView(self.as_ref().handle) }; + match raw_view_ptr.is_null() { + false => None, + true => Some(unsafe { BinaryView::ref_from_raw(raw_view_ptr) }), } - - unsafe { Ok(BinaryView::from_raw(handle)) } } - fn raw_view(&self) -> Result> { - let raw = "Raw".into_bytes_with_nul(); - - let handle = - unsafe { BNGetFileViewOfType(self.file().as_ref().handle, raw.as_ptr() as *mut _) }; - - if handle.is_null() { - return Err(()); - } - - unsafe { Ok(BinaryView::from_raw(handle)) } + fn raw_view(&self) -> Option> { + self.file().get_view_of_type("Raw") } fn view_type(&self) -> BnString { @@ -326,7 +316,7 @@ pub trait BinaryViewExt: BinaryViewBase { let mut active_info_list = vec![]; for active_info in active_infos { - let func = unsafe { Function::from_raw(BNNewFunctionReference(active_info.func)) }; + let func = unsafe { Function::ref_from_raw(active_info.func) }; active_info_list.push(ActiveAnalysisInfo { func, analysis_time: active_info.analysisTime, @@ -368,7 +358,7 @@ pub trait BinaryViewExt: BinaryViewBase { fn set_default_arch(&self, arch: &A) { unsafe { - BNSetDefaultArchitecture(self.as_ref().handle, arch.as_ref().0); + BNSetDefaultArchitecture(self.as_ref().handle, arch.as_ref().handle); } } @@ -392,7 +382,7 @@ pub trait BinaryViewExt: BinaryViewBase { fn instruction_len(&self, arch: &A, addr: u64) -> Option { unsafe { - let size = BNGetInstructionLength(self.as_ref().handle, arch.as_ref().0, addr); + let size = BNGetInstructionLength(self.as_ref().handle, arch.as_ref().handle, addr); if size > 0 { Some(size) @@ -402,33 +392,29 @@ pub trait BinaryViewExt: BinaryViewBase { } } - fn symbol_by_address(&self, addr: u64) -> Result> { + fn symbol_by_address(&self, addr: u64) -> Option> { unsafe { - let raw_sym = BNGetSymbolByAddress(self.as_ref().handle, addr, ptr::null_mut()); - - if raw_sym.is_null() { - return Err(()); + let raw_sym_ptr = BNGetSymbolByAddress(self.as_ref().handle, addr, ptr::null_mut()); + match raw_sym_ptr.is_null() { + false => None, + true => Some(Symbol::ref_from_raw(raw_sym_ptr)), } - - Ok(Symbol::ref_from_raw(raw_sym)) } } - fn symbol_by_raw_name(&self, raw_name: S) -> Result> { + fn symbol_by_raw_name(&self, raw_name: S) -> Option> { let raw_name = raw_name.into_bytes_with_nul(); unsafe { - let raw_sym = BNGetSymbolByRawName( + let raw_sym_ptr = BNGetSymbolByRawName( self.as_ref().handle, raw_name.as_ref().as_ptr() as *mut _, ptr::null_mut(), ); - - if raw_sym.is_null() { - return Err(()); + match raw_sym_ptr.is_null() { + false => None, + true => Some(Symbol::ref_from_raw(raw_sym_ptr)), } - - Ok(Symbol::ref_from_raw(raw_sym)) } } @@ -561,11 +547,11 @@ pub trait BinaryViewExt: BinaryViewBase { } } - fn data_variable_at_address(&self, addr: u64) -> Option> { - let dv = BNDataVariable::default(); + fn data_variable_at_address(&self, addr: u64) -> Option { + let mut dv = BNDataVariable::default(); unsafe { - if BNGetDataVariableAtAddress(self.as_ref().handle, addr, std::mem::transmute(&dv)) { - Some(DataVariable(dv).to_owned()) + if BNGetDataVariableAtAddress(self.as_ref().handle, addr, &mut dv) { + Some(DataVariable::from(dv)) } else { None } @@ -910,17 +896,15 @@ pub trait BinaryViewExt: BinaryViewBase { } } - fn section_by_name(&self, name: S) -> Result
{ + fn section_by_name(&self, name: S) -> Option> { unsafe { let raw_name = name.into_bytes_with_nul(); let name_ptr = raw_name.as_ref().as_ptr() as *mut _; - let raw_section = BNGetSectionByName(self.as_ref().handle, name_ptr); - - if raw_section.is_null() { - return Err(()); + let raw_section_ptr = BNGetSectionByName(self.as_ref().handle, name_ptr); + match raw_section_ptr.is_null() { + false => None, + true => Some(Section::ref_from_raw(raw_section_ptr)), } - - Ok(Section::from_raw(raw_section)) } } @@ -956,7 +940,7 @@ pub trait BinaryViewExt: BinaryViewBase { return None; } - Some(Function::from_raw(handle)) + Some(Function::ref_from_raw(handle)) } } @@ -985,7 +969,7 @@ pub trait BinaryViewExt: BinaryViewBase { return None; } - Some(Function::from_raw(handle)) + Some(Function::ref_from_raw(handle)) } } @@ -1003,7 +987,7 @@ pub trait BinaryViewExt: BinaryViewBase { return Err(()); } - Ok(Function::from_raw(func)) + Ok(Function::ref_from_raw(func)) } } @@ -1011,15 +995,13 @@ pub trait BinaryViewExt: BinaryViewBase { unsafe { BNHasFunctions(self.as_ref().handle) } } - fn entry_point_function(&self) -> Result> { + fn entry_point_function(&self) -> Option> { unsafe { - let func = BNGetAnalysisEntryPoint(self.as_ref().handle); - - if func.is_null() { - return Err(()); + let raw_func_ptr = BNGetAnalysisEntryPoint(self.as_ref().handle); + match raw_func_ptr.is_null() { + false => None, + true => Some(Function::ref_from_raw(raw_func_ptr)), } - - Ok(Function::from_raw(func)) } } @@ -1063,15 +1045,13 @@ pub trait BinaryViewExt: BinaryViewBase { } } - fn function_at(&self, platform: &Platform, addr: u64) -> Result> { + fn function_at(&self, platform: &Platform, addr: u64) -> Option> { unsafe { - let handle = BNGetAnalysisFunction(self.as_ref().handle, platform.handle, addr); - - if handle.is_null() { - return Err(()); + let raw_func_ptr = BNGetAnalysisFunction(self.as_ref().handle, platform.handle, addr); + match raw_func_ptr.is_null() { + false => None, + true => Some(Function::ref_from_raw(raw_func_ptr)), } - - Ok(Function::from_raw(handle)) } } @@ -1121,7 +1101,7 @@ pub trait BinaryViewExt: BinaryViewBase { } fn debug_info(&self) -> Ref { - unsafe { DebugInfo::from_raw(BNGetDebugInfo(self.as_ref().handle)) } + unsafe { DebugInfo::ref_from_raw(BNGetDebugInfo(self.as_ref().handle)) } } fn set_debug_info(&self, debug_info: &DebugInfo) { @@ -1358,7 +1338,7 @@ pub trait BinaryViewExt: BinaryViewBase { // TODO: For now just construct it manually. let mut src = BNReferenceSource { func: func.map(|f| f.handle).unwrap_or(std::ptr::null_mut()), - arch: func.map(|f| f.arch().0).unwrap_or(std::ptr::null_mut()), + arch: func.map(|f| f.arch().handle).unwrap_or(std::ptr::null_mut()), addr, }; let addresses = BNGetCodeReferencesFrom(self.as_ref().handle, &mut src, &mut count); @@ -1526,7 +1506,7 @@ pub trait BinaryViewExt: BinaryViewBase { let result = unsafe { BNGetDataVariableParentComponents( self.as_ref().handle, - data_variable.address(), + data_variable.address, &mut count, ) }; @@ -1742,9 +1722,8 @@ pub struct BinaryView { } impl BinaryView { - pub(crate) unsafe fn from_raw(handle: *mut BNBinaryView) -> Ref { + pub(crate) unsafe fn ref_from_raw(handle: *mut BNBinaryView) -> Ref { debug_assert!(!handle.is_null()); - Ref::new(Self { handle }) } @@ -1947,7 +1926,7 @@ where ) { ffi_wrap!("EventHandler::on_event", { let context = unsafe { &*(ctx as *const Handler) }; - context.on_event(&BinaryView::from_raw(BNNewViewReference(view))); + context.on_event(&BinaryView::ref_from_raw(BNNewViewReference(view))); }) } diff --git a/rust/src/callingconvention.rs b/rust/src/callingconvention.rs index 37c5012fb..fd773982b 100644 --- a/rust/src/callingconvention.rs +++ b/rust/src/callingconvention.rs @@ -16,11 +16,9 @@ use std::borrow::Borrow; use std::fmt::{Debug, Formatter}; +use std::hash::{Hash, Hasher}; use std::marker::PhantomData; -use std::mem; -use std::os::raw::c_void; -use std::ptr; -use std::slice; +use std::ffi::c_void; use binaryninjacore_sys::*; @@ -29,7 +27,8 @@ use crate::rc::{ CoreArrayProvider, CoreArrayProviderInner, Guard, Ref, RefCountable, }; use crate::string::*; - +use crate::types::FunctionParameter; +use crate::variable::Variable; // TODO // force valid registers once Arch has _from_id methods // CallingConvention impl @@ -87,7 +86,7 @@ where return; } - let _regs = Box::from_raw(ptr::slice_from_raw_parts_mut(regs, count)); + let _regs = Box::from_raw(std::ptr::slice_from_raw_parts_mut(regs, count)); }) } @@ -107,7 +106,7 @@ where // SAFETY: `count` is an out parameter *count = regs.len(); let regs_ptr = regs.as_mut_ptr(); - mem::forget(regs); + std::mem::forget(regs); regs_ptr }) } @@ -128,7 +127,7 @@ where // SAFETY: `count` is an out parameter *count = regs.len(); let regs_ptr = regs.as_mut_ptr(); - mem::forget(regs); + std::mem::forget(regs); regs_ptr }) } @@ -144,7 +143,7 @@ where // SAFETY: `count` is an out parameter *count = regs.len(); let regs_ptr = regs.as_mut_ptr(); - mem::forget(regs); + std::mem::forget(regs); regs_ptr }) } @@ -165,7 +164,7 @@ where // SAFETY: `count` is an out parameter *count = regs.len(); let regs_ptr = regs.as_mut_ptr(); - mem::forget(regs); + std::mem::forget(regs); regs_ptr }) } @@ -292,7 +291,7 @@ where // SAFETY: `count` is an out parameter *count = regs.len(); let regs_ptr = regs.as_mut_ptr(); - mem::forget(regs); + std::mem::forget(regs); regs_ptr }) } @@ -343,7 +342,7 @@ where { ffi_wrap!("CallingConvention::incoming_var_for_param", unsafe { let ctxt = &*(ctxt as *mut CustomCallingConventionContext); - ptr::write( + std::ptr::write( param, BNGetDefaultIncomingVariableForParameterVariable(ctxt.raw_handle, var), ); @@ -360,7 +359,7 @@ where { ffi_wrap!("CallingConvention::incoming_param_for_var", unsafe { let ctxt = &*(ctxt as *mut CustomCallingConventionContext); - ptr::write( + std::ptr::write( param, BNGetDefaultParameterVariableForIncomingVariable(ctxt.raw_handle, var), ); @@ -383,7 +382,7 @@ where let name = name.into_bytes_with_nul(); let raw = Box::into_raw(Box::new(CustomCallingConventionContext { - raw_handle: ptr::null_mut(), + raw_handle: std::ptr::null_mut(), cc, })); let mut cc = BNCustomCallingConvention { @@ -418,13 +417,13 @@ where unsafe { let cc_name = name.as_ref().as_ptr() as *mut _; - let result = BNCreateCallingConvention(arch.as_ref().0, cc_name, &mut cc); + let result = BNCreateCallingConvention(arch.as_ref().handle, cc_name, &mut cc); assert!(!result.is_null()); (*raw).raw_handle = result; - BNRegisterCallingConvention(arch.as_ref().0, result); + BNRegisterCallingConvention(arch.as_ref().handle, result); Ref::new(CallingConvention { handle: result, @@ -440,9 +439,6 @@ pub struct CallingConvention { _arch: PhantomData<*mut A>, } -unsafe impl Send for CallingConvention {} -unsafe impl Sync for CallingConvention {} - impl CallingConvention { pub(crate) unsafe fn ref_from_raw( handle: *mut BNCallingConvention, @@ -464,35 +460,16 @@ impl CallingConvention { params: &[FunctionParameter], permitted_registers: Option<&[A::Register]>, ) -> Vec { - let mut bn_params: Vec = vec![]; - let name_strings = params.iter().map(|parameter| ¶meter.name); - - for (parameter, raw_name) in params.iter().zip(name_strings) { - let location = match ¶meter.location { - Some(location) => location.raw(), - None => unsafe { mem::zeroed() }, - }; - bn_params.push(BNFunctionParameter { - name: BnString::new(raw_name).into_raw(), - type_: parameter.t.contents.handle, - typeConfidence: parameter.t.confidence, - defaultLocation: parameter.location.is_none(), - location, - }); - } - let mut count: usize = 0; - let vars: *mut BNVariable = if let Some(permitted_args) = permitted_registers { - let mut permitted_regs = vec![]; - for r in permitted_args { - permitted_regs.push(r.id()); - } + let raw_params: Vec = params.iter().cloned().map(Into::into).collect(); + let raw_vars_ptr: *mut BNVariable = if let Some(permitted_args) = permitted_registers { + let permitted_regs = permitted_args.iter().map(|r| r.id()).collect::>(); unsafe { BNGetVariablesForParameters( self.handle, - bn_params.as_ptr(), - bn_params.len(), + raw_params.as_ptr(), + raw_params.len(), permitted_regs.as_ptr(), permitted_regs.len(), &mut count, @@ -502,24 +479,23 @@ impl CallingConvention { unsafe { BNGetVariablesForParametersDefaultPermittedArgs( self.handle, - bn_params.as_ptr(), - bn_params.len(), + raw_params.as_ptr(), + raw_params.len(), &mut count, ) } }; - let vars_slice = unsafe { slice::from_raw_parts(vars, count) }; - let mut result = vec![]; - for var in vars_slice { - result.push(unsafe { Variable::from_raw(*var) }); - } - - unsafe { BNFreeVariableList(vars) }; - result + let raw_vars = unsafe { std::slice::from_raw_parts(raw_vars_ptr, count) }; + let vars: Vec<_> = raw_vars.iter().copied().map(Into::into).collect(); + unsafe { BNFreeVariableList(raw_vars_ptr) }; + vars } } +unsafe impl Send for CallingConvention {} +unsafe impl Sync for CallingConvention {} + impl Eq for CallingConvention {} impl PartialEq for CallingConvention { fn eq(&self, rhs: &Self) -> bool { @@ -527,9 +503,6 @@ impl PartialEq for CallingConvention { } } -use crate::types::{FunctionParameter, Variable}; -use std::hash::{Hash, Hasher}; - impl Hash for CallingConvention { fn hash(&self, state: &mut H) { self.handle.hash(state); @@ -545,7 +518,7 @@ impl CallingConventionBase for CallingConvention { let regs = BNGetCallerSavedRegisters(self.handle, &mut count); let arch = self.arch_handle.borrow(); - let res = slice::from_raw_parts(regs, count) + let res = std::slice::from_raw_parts(regs, count) .iter() .map(|&r| { arch.register_from_id(r) @@ -565,7 +538,7 @@ impl CallingConventionBase for CallingConvention { let regs = BNGetCalleeSavedRegisters(self.handle, &mut count); let arch = self.arch_handle.borrow(); - let res = slice::from_raw_parts(regs, count) + let res = std::slice::from_raw_parts(regs, count) .iter() .map(|&r| { arch.register_from_id(r) @@ -585,7 +558,7 @@ impl CallingConventionBase for CallingConvention { let regs = BNGetIntegerArgumentRegisters(self.handle, &mut count); let arch = self.arch_handle.borrow(); - let res = slice::from_raw_parts(regs, count) + let res = std::slice::from_raw_parts(regs, count) .iter() .map(|&r| { arch.register_from_id(r) @@ -605,7 +578,7 @@ impl CallingConventionBase for CallingConvention { let regs = BNGetFloatArgumentRegisters(self.handle, &mut count); let arch = self.arch_handle.borrow(); - let res = slice::from_raw_parts(regs, count) + let res = std::slice::from_raw_parts(regs, count) .iter() .map(|&r| { arch.register_from_id(r) diff --git a/rust/src/component.rs b/rust/src/component.rs index ec2350436..4184d1ec4 100644 --- a/rust/src/component.rs +++ b/rust/src/component.rs @@ -4,9 +4,10 @@ use crate::binaryview::{BinaryView, BinaryViewBase, BinaryViewExt}; use crate::function::Function; use crate::rc::{Array, CoreArrayProvider, CoreArrayProviderInner, Ref}; use crate::string::{BnStrCompatible, BnString}; -use crate::types::{ComponentReferencedTypes, DataVariable}; +use crate::types::{ComponentReferencedType}; use binaryninjacore_sys::*; +use crate::variable::DataVariable; pub struct ComponentBuilder { bv: *mut BNBinaryView, @@ -124,17 +125,17 @@ impl Component { /// Add data variable to this component. pub fn add_data_variable(&self, data_variable: &DataVariable) -> bool { - unsafe { BNComponentAddDataVariable(self.as_raw(), data_variable.address()) } + unsafe { BNComponentAddDataVariable(self.as_raw(), data_variable.address) } } /// Check whether this component contains a data variable. pub fn contains_data_variable(&self, data_variable: &DataVariable) -> bool { - unsafe { BNComponentContainsDataVariable(self.as_raw(), data_variable.address()) } + unsafe { BNComponentContainsDataVariable(self.as_raw(), data_variable.address) } } /// Remove data variable from this component. pub fn remove_data_variable(&self, data_variable: &DataVariable) -> bool { - unsafe { BNComponentRemoveDataVariable(self.as_raw(), data_variable.address()) } + unsafe { BNComponentRemoveDataVariable(self.as_raw(), data_variable.address) } } /// Original name of the component @@ -173,7 +174,7 @@ impl Component { pub fn view(&self) -> Option> { let result = unsafe { BNComponentGetView(self.as_raw()) }; - (!result.is_null()).then(|| unsafe { BinaryView::from_raw(result) }) + (!result.is_null()).then(|| unsafe { BinaryView::ref_from_raw(result) }) } /// Is an iterator for all Components contained within this Component @@ -216,7 +217,7 @@ impl Component { /// Get Types referenced by this component /// /// * `recursive` - Get all Types referenced by this component and subcomponents. - pub fn get_referenced_types(&self, recursive: bool) -> Array { + pub fn get_referenced_types(&self, recursive: bool) -> Array { let mut count = 0; let result = if recursive { unsafe { BNComponentGetReferencedTypesRecursive(self.as_raw(), &mut count) } @@ -278,6 +279,7 @@ unsafe impl CoreArrayProviderInner for Component { } } +// TODO: Should we keep this? pub trait IntoComponentGuid { fn component_guid(self) -> BnString; } diff --git a/rust/src/confidence.rs b/rust/src/confidence.rs new file mode 100644 index 000000000..296af79b8 --- /dev/null +++ b/rust/src/confidence.rs @@ -0,0 +1,270 @@ +use std::fmt; +use std::fmt::{Debug, Display, Formatter}; +use std::hash::{Hash, Hasher}; +use binaryninjacore_sys::{BNBoolWithConfidence, BNCallingConventionWithConfidence, BNGetCallingConventionArchitecture, BNOffsetWithConfidence, BNTypeWithConfidence}; +use crate::architecture::{Architecture, CoreArchitecture}; +use crate::callingconvention::CallingConvention; +use crate::rc::{Ref, RefCountable}; +use crate::types::Type; + +/// The minimum allowed confidence of any given [`Type`]. +pub const MIN_CONFIDENCE: u8 = u8::MIN; + +/// The maximum allowed confidence of any given [`Type`]. +pub const MAX_CONFIDENCE: u8 = u8::MAX; + +/// Compatible with the `BNType*WithConfidence` types +pub struct Conf { + pub contents: T, + pub confidence: u8, +} + +pub trait ConfMergable { + type Result; + /// Merge two confidence types' values depending on whichever has higher confidence + /// In the event of a tie, the LHS (caller's) value is used. + fn merge(self, other: O) -> Self::Result; +} + +impl Conf { + pub fn new(contents: T, confidence: u8) -> Self { + Self { + contents, + confidence, + } + } + + pub fn map(self, f: F) -> Conf + where + F: FnOnce(T) -> U, + { + Conf::new(f(self.contents), self.confidence) + } + + pub fn as_ref(&self) -> Conf<&U> + where + T: AsRef, + { + Conf::new(self.contents.as_ref(), self.confidence) + } +} + +/// Returns best value or LHS on tie +/// +/// `Conf` + `Conf` → `Conf` +impl ConfMergable> for Conf { + type Result = Conf; + fn merge(self, other: Conf) -> Conf { + if other.confidence > self.confidence { + other + } else { + self + } + } +} + +/// Returns LHS if RHS is None +/// +/// `Conf` + `Option>` → `Conf` +impl ConfMergable>> for Conf { + type Result = Conf; + fn merge(self, other: Option>) -> Conf { + match other { + Some(c @ Conf { confidence, .. }) if confidence > self.confidence => c, + _ => self, + } + } +} + +/// Returns RHS if LHS is None +/// +/// `Option>` + `Conf` → `Conf` +impl ConfMergable> for Option> { + type Result = Conf; + fn merge(self, other: Conf) -> Conf { + match self { + Some(c @ Conf { confidence, .. }) if confidence >= other.confidence => c, + _ => other, + } + } +} + +/// Returns best non-None value or None +/// +/// `Option>` + `Option>` → `Option>` +impl ConfMergable>> for Option> { + type Result = Option>; + fn merge(self, other: Option>) -> Option> { + match (self, other) { + ( + Some( + this @ Conf { + confidence: this_confidence, + .. + }, + ), + Some( + other @ Conf { + confidence: other_confidence, + .. + }, + ), + ) => { + if this_confidence >= other_confidence { + Some(this) + } else { + Some(other) + } + } + (None, Some(c)) => Some(c), + (Some(c), None) => Some(c), + (None, None) => None, + } + } +} + +impl Debug for Conf { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + write!(f, "{:?} ({} confidence)", self.contents, self.confidence) + } +} + +impl Display for Conf { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + write!(f, "{} ({} confidence)", self.contents, self.confidence) + } +} + +impl PartialEq for Conf { + fn eq(&self, other: &Self) -> bool { + self.contents.eq(&other.contents) + } +} + +impl Eq for Conf {} + +impl Hash for Conf { + fn hash(&self, state: &mut H) { + self.contents.hash(state); + } +} + +impl<'a, T> From<&'a Conf> for Conf<&'a T> { + fn from(c: &'a Conf) -> Self { + Conf::new(&c.contents, c.confidence) + } +} + +impl<'a, T: RefCountable> From<&'a Conf>> for Conf<&'a T> { + fn from(c: &'a Conf>) -> Self { + Conf::new(c.contents.as_ref(), c.confidence) + } +} + +impl<'a, T: RefCountable> From<&'a Ref> for Conf<&'a T> { + fn from(r: &'a Ref) -> Self { + r.as_ref().into() + } +} + +impl Clone for Conf { + fn clone(&self) -> Self { + Self { + contents: self.contents.clone(), + confidence: self.confidence, + } + } +} + +impl Copy for Conf {} + +impl From for Conf { + fn from(contents: T) -> Self { + Self::new(contents, MAX_CONFIDENCE) + } +} + +impl From for Conf> { + fn from(type_with_confidence: BNTypeWithConfidence) -> Self { + Self::new( + unsafe { Type::ref_from_raw(type_with_confidence.type_) }, + type_with_confidence.confidence, + ) + } +} + +impl From for Conf { + fn from(bool_with_confidence: BNBoolWithConfidence) -> Self { + Self::new(bool_with_confidence.value, bool_with_confidence.confidence) + } +} + +impl From for Conf>> { + fn from(cc_with_confidence: BNCallingConventionWithConfidence) -> Self { + Self::new( + unsafe { + CallingConvention::ref_from_raw( + cc_with_confidence.convention, + CoreArchitecture::from_raw(BNGetCallingConventionArchitecture( + cc_with_confidence.convention, + )), + ) + }, + cc_with_confidence.confidence, + ) + } +} + +impl From for Conf { + fn from(offset_with_confidence: BNOffsetWithConfidence) -> Self { + Self::new( + offset_with_confidence.value, + offset_with_confidence.confidence, + ) + } +} + +impl From>> for BNTypeWithConfidence { + fn from(conf: Conf>) -> Self { + Self { + type_: conf.contents.handle, + confidence: conf.confidence, + } + } +} + +impl From> for BNTypeWithConfidence { + fn from(conf: Conf<&Type>) -> Self { + Self { + type_: conf.contents.handle, + confidence: conf.confidence, + } + } +} + +impl From> for BNBoolWithConfidence { + fn from(conf: Conf) -> Self { + Self { + value: conf.contents, + confidence: conf.confidence, + } + } +} + +impl From>> for BNCallingConventionWithConfidence { + fn from(conf: Conf<&CallingConvention>) -> Self { + Self { + convention: conf.contents.handle, + confidence: conf.confidence, + } + } +} + +impl From> for BNOffsetWithConfidence { + fn from(conf: Conf) -> Self { + Self { + value: conf.contents, + confidence: conf.confidence, + } + } +} \ No newline at end of file diff --git a/rust/src/custombinaryview.rs b/rust/src/custombinaryview.rs index 2383ce1e5..c02cbfd5c 100644 --- a/rust/src/custombinaryview.rs +++ b/rust/src/custombinaryview.rs @@ -52,7 +52,7 @@ where { ffi_wrap!("BinaryViewTypeBase::is_valid_for", unsafe { let view_type = &*(ctxt as *mut T); - let data = BinaryView::from_raw(BNNewViewReference(data)); + let data = BinaryView::ref_from_raw(BNNewViewReference(data)); view_type.is_valid_for(&data) }) @@ -84,7 +84,7 @@ where { ffi_wrap!("BinaryViewTypeBase::create", unsafe { let view_type = &*(ctxt as *mut T); - let data = BinaryView::from_raw(BNNewViewReference(data)); + let data = BinaryView::ref_from_raw(BNNewViewReference(data)); let builder = CustomViewBuilder { view_type, @@ -112,7 +112,7 @@ where { ffi_wrap!("BinaryViewTypeBase::parse", unsafe { let view_type = &*(ctxt as *mut T); - let data = BinaryView::from_raw(BNNewViewReference(data)); + let data = BinaryView::ref_from_raw(BNNewViewReference(data)); let builder = CustomViewBuilder { view_type, @@ -140,7 +140,7 @@ where { ffi_wrap!("BinaryViewTypeBase::load_settings", unsafe { let view_type = &*(ctxt as *mut T); - let data = BinaryView::from_raw(BNNewViewReference(data)); + let data = BinaryView::ref_from_raw(BNNewViewReference(data)); match view_type.load_settings_for_data(&data) { Some(settings) => Ref::into_raw(settings).handle, @@ -168,9 +168,8 @@ where }; unsafe { - let res = BNRegisterBinaryViewType(name_ptr, long_name_ptr, &mut bn_obj as *mut _); - - if res.is_null() { + let handle = BNRegisterBinaryViewType(name_ptr, long_name_ptr, &mut bn_obj as *mut _); + if handle.is_null() { // avoid leaking the space allocated for the type, but also // avoid running its Drop impl (if any -- not that there should // be one since view types live for the life of the process) as @@ -180,8 +179,7 @@ where panic!("bvt registration failed"); } - ctxt.write(constructor(BinaryViewType(res))); - + ctxt.write(constructor(BinaryViewType { handle })); ctxt.assume_init_mut() } } @@ -199,7 +197,7 @@ pub trait BinaryViewTypeBase: AsRef { fn default_load_settings_for_data(&self, data: &BinaryView) -> Option> { let settings_handle = - unsafe { BNGetBinaryViewDefaultLoadSettingsForData(self.as_ref().0, data.handle) }; + unsafe { BNGetBinaryViewDefaultLoadSettingsForData(self.as_ref().handle, data.handle) }; if settings_handle.is_null() { None @@ -215,16 +213,16 @@ pub trait BinaryViewTypeBase: AsRef { pub trait BinaryViewTypeExt: BinaryViewTypeBase { fn name(&self) -> BnString { - unsafe { BnString::from_raw(BNGetBinaryViewTypeName(self.as_ref().0)) } + unsafe { BnString::from_raw(BNGetBinaryViewTypeName(self.as_ref().handle)) } } fn long_name(&self) -> BnString { - unsafe { BnString::from_raw(BNGetBinaryViewTypeLongName(self.as_ref().0)) } + unsafe { BnString::from_raw(BNGetBinaryViewTypeLongName(self.as_ref().handle)) } } fn register_arch(&self, id: u32, endianness: Endianness, arch: &A) { unsafe { - BNRegisterArchitectureForViewType(self.as_ref().0, id, endianness, arch.as_ref().0); + BNRegisterArchitectureForViewType(self.as_ref().handle, id, endianness, arch.as_ref().handle); } } @@ -232,12 +230,12 @@ pub trait BinaryViewTypeExt: BinaryViewTypeBase { let arch = plat.arch(); unsafe { - BNRegisterPlatformForViewType(self.as_ref().0, id, arch.0, plat.handle); + BNRegisterPlatformForViewType(self.as_ref().handle, id, arch.handle, plat.handle); } } fn open(&self, data: &BinaryView) -> Result> { - let handle = unsafe { BNCreateBinaryViewOfType(self.as_ref().0, data.handle) }; + let handle = unsafe { BNCreateBinaryViewOfType(self.as_ref().handle, data.handle) }; if handle.is_null() { error!( @@ -247,11 +245,11 @@ pub trait BinaryViewTypeExt: BinaryViewTypeBase { return Err(()); } - unsafe { Ok(BinaryView::from_raw(handle)) } + unsafe { Ok(BinaryView::ref_from_raw(handle)) } } fn parse(&self, data: &BinaryView) -> Result> { - let handle = unsafe { BNParseBinaryViewOfType(self.as_ref().0, data.handle) }; + let handle = unsafe { BNParseBinaryViewOfType(self.as_ref().handle, data.handle) }; if handle.is_null() { error!( @@ -261,21 +259,22 @@ pub trait BinaryViewTypeExt: BinaryViewTypeBase { return Err(()); } - unsafe { Ok(BinaryView::from_raw(handle)) } + unsafe { Ok(BinaryView::ref_from_raw(handle)) } } } impl BinaryViewTypeExt for T {} #[derive(Copy, Clone, PartialEq, Eq, Hash)] -pub struct BinaryViewType(pub *mut BNBinaryViewType); +pub struct BinaryViewType { + pub handle: *mut BNBinaryViewType +} impl BinaryViewType { pub fn list_all() -> Array { unsafe { let mut count: usize = 0; let types = BNGetBinaryViewTypes(&mut count as *mut _); - Array::new(types, count, ()) } } @@ -284,7 +283,6 @@ impl BinaryViewType { unsafe { let mut count: usize = 0; let types = BNGetBinaryViewTypesForData(data.handle, &mut count as *mut _); - Array::new(types, count, ()) } } @@ -292,11 +290,9 @@ impl BinaryViewType { /// Looks up a BinaryViewType by its short name pub fn by_name(name: N) -> Result { let bytes = name.into_bytes_with_nul(); - - let res = unsafe { BNGetBinaryViewTypeByName(bytes.as_ref().as_ptr() as *const _) }; - - match res.is_null() { - false => Ok(BinaryViewType(res)), + let handle = unsafe { BNGetBinaryViewTypeByName(bytes.as_ref().as_ptr() as *const _) }; + match handle.is_null() { + false => Ok(BinaryViewType { handle }), true => Err(()), } } @@ -304,19 +300,19 @@ impl BinaryViewType { impl BinaryViewTypeBase for BinaryViewType { fn is_valid_for(&self, data: &BinaryView) -> bool { - unsafe { BNIsBinaryViewTypeValidForData(self.0, data.handle) } + unsafe { BNIsBinaryViewTypeValidForData(self.handle, data.handle) } } fn is_deprecated(&self) -> bool { - unsafe { BNIsBinaryViewTypeDeprecated(self.0) } + unsafe { BNIsBinaryViewTypeDeprecated(self.handle) } } fn is_force_loadable(&self) -> bool { - unsafe { BNIsBinaryViewTypeForceLoadable(self.0) } + unsafe { BNIsBinaryViewTypeForceLoadable(self.handle) } } fn load_settings_for_data(&self, data: &BinaryView) -> Option> { - let settings_handle = unsafe { BNGetBinaryViewLoadSettingsForData(self.0, data.handle) }; + let settings_handle = unsafe { BNGetBinaryViewLoadSettingsForData(self.handle, data.handle) }; if settings_handle.is_null() { None @@ -337,7 +333,7 @@ unsafe impl CoreArrayProviderInner for BinaryViewType { BNFreeBinaryViewTypeList(raw); } unsafe fn wrap_raw<'a>(raw: &'a Self::Raw, _context: &'a Self::Context) -> Self::Wrapped<'a> { - Guard::new(BinaryViewType(*raw), &()) + Guard::new(BinaryViewType { handle: *raw }, &()) } } @@ -433,7 +429,7 @@ impl<'a, T: CustomBinaryViewType> CustomViewBuilder<'a, T> { let view_name = view_type.name(); - if let Ok(bv) = file.get_view_of_type(view_name.as_str()) { + if let Some(bv) = file.get_view_of_type(view_name.as_str()) { // while it seems to work most of the time, you can get really unlucky // if the a free of the existing view of the same type kicks off while // BNCreateBinaryViewOfType is still running. the freeObject callback @@ -471,7 +467,7 @@ impl<'a, T: CustomBinaryViewType> CustomViewBuilder<'a, T> { { ffi_wrap!("BinaryViewBase::init", unsafe { let context = &mut *(ctxt as *mut CustomViewContext); - let handle = BinaryView::from_raw(context.raw_handle); + let handle = BinaryView::ref_from_raw(context.raw_handle); match V::new(handle.as_ref(), &context.args) { Ok(v) => { @@ -837,7 +833,7 @@ impl<'a, T: CustomBinaryViewType> CustomViewBuilder<'a, T> { (*ctxt).raw_handle = res; Ok(CustomView { - handle: BinaryView::from_raw(res), + handle: BinaryView::ref_from_raw(res), _builder: PhantomData, }) } diff --git a/rust/src/databuffer.rs b/rust/src/databuffer.rs index 29d7636b4..5049cf525 100644 --- a/rust/src/databuffer.rs +++ b/rust/src/databuffer.rs @@ -27,6 +27,7 @@ impl DataBuffer { pub(crate) fn from_raw(raw: *mut BNDataBuffer) -> Self { DataBuffer(raw) } + pub(crate) fn as_raw(&self) -> *mut BNDataBuffer { self.0 } @@ -114,7 +115,7 @@ impl DataBuffer { } pub fn from_escaped_string(value: &BnString) -> Self { - Self(unsafe { BNDecodeEscapedString(value.as_raw()) }) + Self(unsafe { BNDecodeEscapedString(value.as_ptr()) }) } pub fn to_base64(&self) -> BnString { @@ -122,7 +123,7 @@ impl DataBuffer { } pub fn from_base64(value: &BnString) -> Self { - Self(unsafe { BNDecodeBase64(value.as_raw()) }) + Self(unsafe { BNDecodeBase64(value.as_ptr()) }) } pub fn zlib_compress(&self) -> Self { diff --git a/rust/src/debuginfo.rs b/rust/src/debuginfo.rs index 2b43c9cc7..61344fc9d 100644 --- a/rust/src/debuginfo.rs +++ b/rust/src/debuginfo.rs @@ -68,6 +68,7 @@ //! `DebugInfo` object just returned. This is automatic when opening a binary view with multiple valid debug info parsers. If you //! wish to set the debug info for a binary view without applying it as well, you can call `binaryninja::binaryview::BinaryView::set_debug_info`. +use std::ffi::c_void; use binaryninjacore_sys::*; use crate::{ @@ -75,10 +76,10 @@ use crate::{ platform::Platform, rc::*, string::{raw_to_string, BnStrCompatible, BnString}, - types::{DataVariableAndName, NameAndType, NamedTypedVariable, Type}, + types::{NameAndType, Type}, }; -use std::{hash::Hash, os::raw::c_void, ptr, slice}; +use crate::variable::{NamedDataVariableWithType, NamedVariableWithType}; struct ProgressContext(Option Result<(), ()>>>); @@ -170,7 +171,7 @@ impl DebugInfoParser { self.handle, view.handle, debug_file.handle, - ptr::null_mut(), + std::ptr::null_mut(), Some(Self::cb_progress), &mut progress_raw as *mut _ as *mut c_void, ) @@ -179,7 +180,7 @@ impl DebugInfoParser { if info.is_null() { return None; } - Some(unsafe { DebugInfo::from_raw(info) }) + Some(unsafe { DebugInfo::ref_from_raw(info) }) } // Registers a DebugInfoParser. See `binaryninja::debuginfo::DebugInfoParser` for more details. @@ -194,7 +195,7 @@ impl DebugInfoParser { { ffi_wrap!("CustomDebugInfoParser::is_valid", unsafe { let cmd = &*(ctxt as *const C); - let view = BinaryView::from_raw(view); + let view = BinaryView::ref_from_raw(view); cmd.is_valid(&view) }) @@ -213,9 +214,9 @@ impl DebugInfoParser { { ffi_wrap!("CustomDebugInfoParser::parse_info", unsafe { let cmd = &*(ctxt as *const C); - let view = BinaryView::from_raw(view); - let debug_file = BinaryView::from_raw(debug_file); - let mut debug_info = DebugInfo::from_raw(debug_info); + let view = BinaryView::ref_from_raw(view); + let debug_file = BinaryView::ref_from_raw(debug_file); + let mut debug_info = DebugInfo::ref_from_raw(debug_info); cmd.parse_info( &mut debug_info, @@ -301,24 +302,13 @@ pub struct DebugFunctionInfo { address: u64, platform: Option>, components: Vec, - local_variables: Vec, + local_variables: Vec, } impl From<&BNDebugFunctionInfo> for DebugFunctionInfo { fn from(raw: &BNDebugFunctionInfo) -> Self { - let components = unsafe { slice::from_raw_parts(raw.components, raw.componentN) } - .iter() - .map(|component| raw_to_string(*component as *const _).unwrap()) - .collect(); - - let local_variables: Vec = unsafe { slice::from_raw_parts(raw.localVariables, raw.localVariableN) } - .iter() - .map(|local_variable| { - unsafe { - NamedTypedVariable::from_raw(local_variable) - } - }) - .collect(); + let raw_components = unsafe { std::slice::from_raw_parts(raw.components, raw.componentN) }; + let raw_local_variables = unsafe { std::slice::from_raw_parts(raw.localVariables, raw.localVariableN) }; Self { short_name: raw_to_string(raw.shortName), @@ -335,8 +325,8 @@ impl From<&BNDebugFunctionInfo> for DebugFunctionInfo { } else { Some(unsafe { Platform::ref_from_raw(raw.platform) }) }, - components, - local_variables, + components: raw_components.iter().copied().filter_map(|c| raw_to_string(c)).collect(), + local_variables: raw_local_variables.into_iter().copied().map(Into::into).collect(), } } } @@ -351,7 +341,7 @@ impl DebugFunctionInfo { address: Option, platform: Option>, components: Vec, - local_variables: Vec, + local_variables: Vec, ) -> Self { Self { short_name, @@ -388,14 +378,13 @@ pub struct DebugInfo { } impl DebugInfo { - pub(crate) unsafe fn from_raw(handle: *mut BNDebugInfo) -> Ref { + pub(crate) unsafe fn ref_from_raw(handle: *mut BNDebugInfo) -> Ref { debug_assert!(!handle.is_null()); - Ref::new(Self { handle }) } - /// Returns a generator of all types provided by a named DebugInfoParser - pub fn types_by_name(&self, parser_name: S) -> Vec> { + /// Returns all types within the parser + pub fn types_by_name(&self, parser_name: S) -> Vec { let parser_name = parser_name.into_bytes_with_nul(); let mut count: usize = 0; @@ -406,25 +395,26 @@ impl DebugInfo { &mut count, ) }; - let result: Vec> = unsafe { - slice::from_raw_parts_mut(debug_types_ptr, count) + let result: Vec<_> = unsafe { + std::slice::from_raw_parts_mut(debug_types_ptr, count) .iter() - .map(|x| NameAndType::from_raw(x).to_owned()) + .copied() + .map(Into::into) .collect() }; unsafe { BNFreeDebugTypes(debug_types_ptr, count) }; result } - - /// A generator of all types provided by DebugInfoParsers - pub fn types(&self) -> Vec> { + + pub fn types(&self) -> Vec { let mut count: usize = 0; - let debug_types_ptr = unsafe { BNGetDebugTypes(self.handle, ptr::null_mut(), &mut count) }; - let result: Vec> = unsafe { - slice::from_raw_parts_mut(debug_types_ptr, count) + let debug_types_ptr = unsafe { BNGetDebugTypes(self.handle, std::ptr::null_mut(), &mut count) }; + let result: Vec<_> = unsafe { + std::slice::from_raw_parts_mut(debug_types_ptr, count) .iter() - .map(|x| NameAndType::from_raw(x).to_owned()) + .copied() + .map(Into::into) .collect() }; @@ -432,7 +422,7 @@ impl DebugInfo { result } - /// Returns a generator of all functions provided by a named DebugInfoParser + /// Returns all functions within the parser pub fn functions_by_name( &self, parser_name: S @@ -449,7 +439,7 @@ impl DebugInfo { }; let result: Vec = unsafe { - slice::from_raw_parts_mut(functions_ptr, count) + std::slice::from_raw_parts_mut(functions_ptr, count) .iter() .map(DebugFunctionInfo::from) .collect() @@ -458,15 +448,14 @@ impl DebugInfo { unsafe { BNFreeDebugFunctions(functions_ptr, count) }; result } - - /// A generator of all functions provided by DebugInfoParsers + pub fn functions(&self) -> Vec { let mut count: usize = 0; let functions_ptr = - unsafe { BNGetDebugFunctions(self.handle, ptr::null_mut(), &mut count) }; + unsafe { BNGetDebugFunctions(self.handle, std::ptr::null_mut(), &mut count) }; let result: Vec = unsafe { - slice::from_raw_parts_mut(functions_ptr, count) + std::slice::from_raw_parts_mut(functions_ptr, count) .iter() .map(DebugFunctionInfo::from) .collect() @@ -476,11 +465,11 @@ impl DebugInfo { result } - /// Returns a generator of all data variables provided by a named DebugInfoParser + /// Returns all data variables within the parser pub fn data_variables_by_name( &self, parser_name: S, - ) -> Vec> { + ) -> Vec { let parser_name = parser_name.into_bytes_with_nul(); let mut count: usize = 0; @@ -492,35 +481,35 @@ impl DebugInfo { ) }; - let result: Vec> = unsafe { - slice::from_raw_parts_mut(data_variables_ptr, count) + let result: Vec = unsafe { + std::slice::from_raw_parts_mut(data_variables_ptr, count) .iter() - .map(DataVariableAndName::::from_raw) + .copied() + .map(Into::into) .collect() }; unsafe { BNFreeDataVariablesAndName(data_variables_ptr, count) }; result } - - /// A generator of all data variables provided by DebugInfoParsers - pub fn data_variables(&self) -> Vec> { + + pub fn data_variables(&self) -> Vec { let mut count: usize = 0; let data_variables_ptr = - unsafe { BNGetDebugDataVariables(self.handle, ptr::null_mut(), &mut count) }; + unsafe { BNGetDebugDataVariables(self.handle, std::ptr::null_mut(), &mut count) }; - let result: Vec> = unsafe { - slice::from_raw_parts_mut(data_variables_ptr, count) + let result: Vec = unsafe { + std::slice::from_raw_parts_mut(data_variables_ptr, count) .iter() - .map(DataVariableAndName::::from_raw) + .copied() + .map(Into::into) .collect() }; unsafe { BNFreeDataVariablesAndName(data_variables_ptr, count) }; result } - - /// May return nullptr + pub fn type_by_name(&self, parser_name: S, name: S) -> Option> { let parser_name = parser_name.into_bytes_with_nul(); let name = name.into_bytes_with_nul(); @@ -543,11 +532,10 @@ impl DebugInfo { &self, parser_name: S, name: S, - ) -> Option<(u64, Ref)> { + ) -> Option { let parser_name = parser_name.into_bytes_with_nul(); let name = name.into_bytes_with_nul(); - - let result = unsafe { + let raw_named_var = unsafe { BNGetDebugDataVariableByName( self.handle, parser_name.as_ref().as_ptr() as *mut _, @@ -555,9 +543,10 @@ impl DebugInfo { ) }; - if !result.is_null() { - unsafe { BNFreeString((*result).name) }; - Some(unsafe { ((*result).address, Type::ref_from_raw((*result).type_)) }) + if !raw_named_var.is_null() { + let result = unsafe { raw_named_var.read() }; + unsafe { BNFreeDataVariableAndName(raw_named_var) }; + Some(NamedDataVariableWithType::from(result)) } else { None } @@ -567,9 +556,9 @@ impl DebugInfo { &self, parser_name: S, address: u64, - ) -> Option<(String, Ref)> { + ) -> Option { let parser_name = parser_name.into_bytes_with_nul(); - let name_and_var = unsafe { + let raw_named_var = unsafe { BNGetDebugDataVariableByAddress( self.handle, parser_name.as_ref().as_ptr() as *mut _, @@ -577,45 +566,33 @@ impl DebugInfo { ) }; - if !name_and_var.is_null() { - let result = unsafe { - ( - raw_to_string((*name_and_var).name).unwrap(), - Type::ref_from_raw((*name_and_var).type_), - ) - }; - unsafe { BNFreeString((*name_and_var).name) }; - Some(result) + if !raw_named_var.is_null() { + let result = unsafe { raw_named_var.read() }; + unsafe { BNFreeDataVariableAndName(raw_named_var) }; + Some(NamedDataVariableWithType::from(result)) } else { None } } - // The tuple is (DebugInfoParserName, type) - pub fn get_types_by_name(&self, name: S) -> Vec<(String, Ref)> { - let name = name.into_bytes_with_nul(); - + /// Returns a list of [`NameAndType`] where the `name` is the parser the type originates from. + pub fn get_types_by_name(&self, name: S) -> Vec { let mut count: usize = 0; - let raw_names_and_types = unsafe { + let name = name.into_bytes_with_nul(); + let raw_names_and_types_ptr = unsafe { BNGetDebugTypesByName(self.handle, name.as_ref().as_ptr() as *mut _, &mut count) }; - let names_and_types: &[*mut BNNameAndType] = - unsafe { slice::from_raw_parts(raw_names_and_types as *mut _, count) }; + let raw_names_and_types: &[BNNameAndType] = unsafe { std::slice::from_raw_parts(raw_names_and_types_ptr, count) }; - let result = names_and_types + let names_and_types = raw_names_and_types .iter() - .take(count) - .map(|&name_and_type| unsafe { - ( - raw_to_string((*name_and_type).name).unwrap(), - Type::ref_from_raw(BNNewTypeReference((*name_and_type).type_)), - ) - }) + .copied() + .map(Into::into) .collect(); - unsafe { BNFreeNameAndTypeList(raw_names_and_types, count) }; - result + unsafe { BNFreeNameAndTypeList(raw_names_and_types_ptr, count) }; + names_and_types } // The tuple is (DebugInfoParserName, address, type) @@ -631,7 +608,7 @@ impl DebugInfo { }; let variables_and_names: &[*mut BNDataVariableAndName] = - unsafe { slice::from_raw_parts(raw_variables_and_names as *mut _, count) }; + unsafe { std::slice::from_raw_parts(raw_variables_and_names as *mut _, count) }; let result = variables_and_names .iter() @@ -656,7 +633,7 @@ impl DebugInfo { unsafe { BNGetDebugDataVariablesByAddress(self.handle, address, &mut count) }; let variables_and_names: &[*mut BNDataVariableAndNameAndDebugParser] = - unsafe { slice::from_raw_parts(raw_variables_and_names as *mut _, count) }; + unsafe { std::slice::from_raw_parts(raw_variables_and_names as *mut _, count) }; let result = variables_and_names .iter() @@ -777,15 +754,15 @@ impl DebugInfo { let short_name_bytes = new_func.short_name.map(|name| name.into_bytes_with_nul()); let short_name = short_name_bytes .as_ref() - .map_or(ptr::null_mut() as *mut _, |name| name.as_ptr() as _); + .map_or(std::ptr::null_mut() as *mut _, |name| name.as_ptr() as _); let full_name_bytes = new_func.full_name.map(|name| name.into_bytes_with_nul()); let full_name = full_name_bytes .as_ref() - .map_or(ptr::null_mut() as *mut _, |name| name.as_ptr() as _); + .map_or(std::ptr::null_mut() as *mut _, |name| name.as_ptr() as _); let raw_name_bytes = new_func.raw_name.map(|name| name.into_bytes_with_nul()); let raw_name = raw_name_bytes .as_ref() - .map_or(ptr::null_mut() as *mut _, |name| name.as_ptr() as _); + .map_or(std::ptr::null_mut() as *mut _, |name| name.as_ptr() as _); let mut components_array: Vec<*mut ::std::os::raw::c_char> = Vec::with_capacity(new_func.components.len()); @@ -802,7 +779,7 @@ impl DebugInfo { for local_variable in &new_func.local_variables { local_variables_array.push( BNVariableNameAndType { - var: local_variable.var.raw(), + var: local_variable.variable.into(), autoDefined: local_variable.auto_defined, typeConfidence: local_variable.ty.confidence, name: BNAllocString(local_variable.name.clone().into_bytes_with_nul().as_ptr() as _), @@ -820,11 +797,11 @@ impl DebugInfo { address: new_func.address, type_: match new_func.type_ { Some(type_) => type_.handle, - _ => ptr::null_mut(), + _ => std::ptr::null_mut(), }, platform: match new_func.platform { Some(platform) => platform.handle, - _ => ptr::null_mut(), + _ => std::ptr::null_mut(), }, components: components_array.as_ptr() as _, componentN: new_func.components.len(), @@ -877,7 +854,7 @@ impl DebugInfo { self.handle, address, t.handle, - ptr::null_mut(), + std::ptr::null_mut(), components.as_ptr() as _, components.len(), ) @@ -885,20 +862,8 @@ impl DebugInfo { } } - pub fn add_data_variable_info(&self, var: DataVariableAndName) -> bool { - let name = var.name.into_bytes_with_nul(); - unsafe { - BNAddDebugDataVariableInfo( - self.handle, - &BNDataVariableAndName { - address: var.address, - type_: var.t.contents.handle, - name: name.as_ref().as_ptr() as *mut _, - autoDiscovered: var.auto_discovered, - typeConfidence: var.t.confidence, - }, - ) - } + pub fn add_data_variable_info(&self, var: NamedDataVariableWithType) -> bool { + unsafe { BNAddDebugDataVariableInfo(self.handle, &var.into()) } } } diff --git a/rust/src/demangle.rs b/rust/src/demangle.rs index 8fbc43161..3d2188d34 100644 --- a/rust/src/demangle.rs +++ b/rust/src/demangle.rs @@ -15,9 +15,7 @@ //! Interfaces for demangling and simplifying mangled names in binaries. use binaryninjacore_sys::*; -use std::os::raw::c_char; -use std::{ffi::CStr, result}; -use std::ffi::c_void; +use std::ffi::{c_char, c_void, CStr}; use crate::architecture::CoreArchitecture; use crate::binaryview::BinaryView; @@ -26,7 +24,7 @@ use crate::types::{QualifiedName, Type}; use crate::rc::*; -pub type Result = result::Result; +pub type Result = std::result::Result; pub fn demangle_generic( arch: &CoreArchitecture, @@ -48,7 +46,7 @@ pub fn demangle_generic( }; let res = unsafe { BNDemangleGeneric( - arch.0, + arch.handle, mangled_name_ptr.as_ptr() as *const c_char, &mut out_type, &mut out_name, @@ -141,7 +139,7 @@ pub fn demangle_gnu3( let mut out_size: usize = 0; let res = unsafe { BNDemangleGNU3( - arch.0, + arch.handle, mangled_name_ptr.as_ptr() as *const c_char, &mut out_type, &mut out_name, @@ -197,7 +195,7 @@ pub fn demangle_ms( let mut out_size: usize = 0; let res = unsafe { BNDemangleMS( - arch.0, + arch.handle, mangled_name_ptr.as_ptr() as *const c_char, &mut out_type, &mut out_name, @@ -277,7 +275,7 @@ impl Demangler { None => std::ptr::null_mut() }; - if !unsafe { BNDemanglerDemangle(self.handle, arch.0, name_bytes.as_ref().as_ptr() as *const _, &mut out_type, &mut out_var_name, view_ptr) } { + if !unsafe { BNDemanglerDemangle(self.handle, arch.handle, name_bytes.as_ref().as_ptr() as *const _, &mut out_type, &mut out_var_name, view_ptr) } { return Err(()); } @@ -348,7 +346,7 @@ impl Demangler { let view = if view.is_null() { None } else { - Some(BinaryView::from_raw(BNNewViewReference(view))) + Some(BinaryView::ref_from_raw(BNNewViewReference(view))) }; match cmd.demangle(&arch, &name, view) { diff --git a/rust/src/filemetadata.rs b/rust/src/filemetadata.rs index 15a7059d4..aa72d7114 100644 --- a/rust/src/filemetadata.rs +++ b/rust/src/filemetadata.rs @@ -173,16 +173,14 @@ impl FileMetadata { } } - pub fn get_view_of_type(&self, view: S) -> Result, ()> { + pub fn get_view_of_type(&self, view: S) -> Option> { let view = view.into_bytes_with_nul(); unsafe { - let res = BNGetFileViewOfType(self.handle, view.as_ref().as_ptr() as *const _); - - if res.is_null() { - Err(()) - } else { - Ok(BinaryView::from_raw(res)) + let raw_view_ptr = BNGetFileViewOfType(self.handle, view.as_ref().as_ptr() as *const _); + match raw_view_ptr.is_null() { + false => None, + true => Some(BinaryView::ref_from_raw(raw_view_ptr)), } } } @@ -241,7 +239,7 @@ impl FileMetadata { if bv.is_null() { Err(()) } else { - Ok(BinaryView::from_raw(bv)) + Ok(BinaryView::ref_from_raw(bv)) } } } @@ -269,14 +267,14 @@ impl FileMetadata { if view.is_null() { Err(()) } else { - Ok(unsafe { BinaryView::from_raw(view) }) + Ok(unsafe { BinaryView::ref_from_raw(view) }) } } /// Get the current database pub fn database(&self) -> Option { let result = unsafe { BNGetFileMetadataDatabase(self.handle) }; - ptr::NonNull::new(result).map(|handle| unsafe { Database::from_raw(handle) }) + NonNull::new(result).map(|handle| unsafe { Database::from_raw(handle) }) } } diff --git a/rust/src/function.rs b/rust/src/function.rs index 0df9fcf58..f0d12f4fb 100644 --- a/rust/src/function.rs +++ b/rust/src/function.rs @@ -30,9 +30,9 @@ use crate::{ symbol::Symbol, tags::{Tag, TagReference, TagType}, types::{ - Conf, ConstantReference, HighlightColor, IndirectBranchInfo, IntegerDisplayType, - MergedVariable, NamedTypedVariable, QualifiedName, RegisterStackAdjustment, RegisterValue, - RegisterValueType, StackVariableReference, Type, UnresolvedIndirectBranches, Variable, + IntegerDisplayType, + QualifiedName, + Type, }, }; use crate::{databuffer::DataBuffer, disassembly::InstructionTextToken, rc::*}; @@ -40,17 +40,32 @@ pub use binaryninjacore_sys::BNAnalysisSkipReason as AnalysisSkipReason; pub use binaryninjacore_sys::BNFunctionAnalysisSkipOverride as FunctionAnalysisSkipOverride; pub use binaryninjacore_sys::BNFunctionUpdateType as FunctionUpdateType; pub use binaryninjacore_sys::BNBuiltinType as BuiltinType; +pub use binaryninjacore_sys::BNHighlightStandardColor as HighlightStandardColor; use std::{fmt, mem}; use std::{ffi::c_char, hash::Hash, ops::Range}; use std::ptr::NonNull; +use std::time::Duration; +use crate::confidence::Conf; +use crate::variable::{IndirectBranchInfo, MergedVariable, NamedVariableWithType, RegisterValue, RegisterValueType, StackVariableReference, Variable}; use crate::workflow::Workflow; +/// Used to describe a location within a [`Function`]. +#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] pub struct Location { pub arch: Option, pub addr: u64, } +impl Location { + pub fn from_raw(addr: u64, arch: *mut BNArchitecture) -> Self { + Self { + addr, + arch: Some(unsafe { CoreArchitecture::from_raw(arch) }), + } + } +} + impl From for Location { fn from(addr: u64) -> Self { Location { arch: None, addr } @@ -66,6 +81,21 @@ impl From<(CoreArchitecture, u64)> for Location { } } +impl From for Location { + fn from(value: BNArchitectureAndAddress) -> Self { + Self::from_raw(value.address, value.arch) + } +} + +impl From for BNArchitectureAndAddress { + fn from(value: Location) -> Self { + Self { + arch: value.arch.map(|a| a.handle).unwrap_or(std::ptr::null_mut()), + address: value.addr, + } + } +} + pub struct NativeBlockIter { arch: CoreArchitecture, bv: Ref, @@ -108,8 +138,8 @@ impl NativeBlock { } impl BlockContext for NativeBlock { - type Iter = NativeBlockIter; type Instruction = u64; + type Iter = NativeBlockIter; fn start(&self, block: &BasicBlock) -> u64 { block.raw_start() @@ -181,8 +211,8 @@ impl Into for FunctionGraphType { BNFunctionGraphType::HighLevelILFunctionGraph => FunctionViewType::HighLevelIL, BNFunctionGraphType::HighLevelILSSAFormFunctionGraph => FunctionViewType::HighLevelILSSAForm, BNFunctionGraphType::HighLevelLanguageRepresentationFunctionGraph => { - FunctionViewType::HighLevelLanguageRepresentation("Pseudo C".into() - ) + // Historically this was the only language representation. + FunctionViewType::HighLevelLanguageRepresentation("Pseudo C".into()) } _ => FunctionViewType::Normal, } @@ -206,7 +236,7 @@ unsafe impl Send for Function {} unsafe impl Sync for Function {} impl Function { - pub(crate) unsafe fn from_raw(handle: *mut BNFunction) -> Ref { + pub(crate) unsafe fn ref_from_raw(handle: *mut BNFunction) -> Ref { Ref::new(Self { handle }) } @@ -227,7 +257,7 @@ impl Function { pub fn view(&self) -> Ref { unsafe { let view = BNGetFunctionData(self.handle); - BinaryView::from_raw(view) + BinaryView::ref_from_raw(view) } } @@ -301,7 +331,7 @@ impl Function { } /// All comments in the function - pub fn comments(&self) -> Array { + pub fn comments(&self) -> Array { let mut count = 0; let lines = unsafe { BNGetCommentedAddresses(self.handle, &mut count) }; unsafe { Array::new(lines, count, self.to_owned()) } @@ -335,7 +365,7 @@ impl Function { ) -> Option>> { let arch = arch.unwrap_or_else(|| self.arch()); unsafe { - let block = BNGetFunctionBasicBlockAtAddress(self.handle, arch.0, addr); + let block = BNGetFunctionBasicBlockAtAddress(self.handle, arch.handle, addr); let context = NativeBlock { _priv: () }; if block.is_null() { @@ -353,14 +383,14 @@ impl Function { ) -> Array> { let arch = arch.unwrap_or_else(|| self.arch()); let mut count = 0; - let lines = unsafe { BNGetFunctionBlockAnnotations(self.handle, arch.0, addr, &mut count) }; + let lines = unsafe { BNGetFunctionBlockAnnotations(self.handle, arch.handle, addr, &mut count) }; assert!(!lines.is_null()); unsafe { Array::new(lines, count, ()) } } pub fn get_variable_name(&self, var: &Variable) -> BnString { unsafe { - let raw_var = var.raw(); + let raw_var = BNVariable::from(var); let raw_name = BNGetVariableName(self.handle, &raw_var); BnString::from_raw(raw_name) } @@ -512,7 +542,7 @@ impl Function { unsafe { BNSetFunctionAutoType(self.handle, t.handle) } } - pub fn stack_layout(&self) -> Array { + pub fn stack_layout(&self) -> Array { let mut count = 0; unsafe { let variables = BNGetStackLayout(self.handle, &mut count); @@ -547,7 +577,7 @@ impl Function { pub fn call_stack_adjustment(&self, addr: u64, arch: Option) -> Conf { let arch = arch.unwrap_or_else(|| self.arch()); - let result = unsafe { BNGetCallStackAdjustment(self.handle, arch.0, addr) }; + let result = unsafe { BNGetCallStackAdjustment(self.handle, arch.handle, addr) }; result.into() } @@ -564,7 +594,7 @@ impl Function { unsafe { BNSetUserCallStackAdjustment( self.handle, - arch.0, + arch.handle, addr, adjust.contents, adjust.confidence, @@ -585,7 +615,7 @@ impl Function { unsafe { BNSetAutoCallStackAdjustment( self.handle, - arch.0, + arch.handle, addr, adjust.contents, adjust.confidence, @@ -599,7 +629,7 @@ impl Function { arch: Option, ) -> Option>> { let arch = arch.unwrap_or_else(|| self.arch()); - let result = unsafe { BNGetCallTypeAdjustment(self.handle, arch.0, addr) }; + let result = unsafe { BNGetCallTypeAdjustment(self.handle, arch.handle, addr) }; (!result.type_.is_null()) .then(|| unsafe { Conf::new(Type::ref_from_raw(result.type_), result.confidence) }) } @@ -629,7 +659,7 @@ impl Function { .as_mut() .map(|x| x as *mut _) .unwrap_or(core::ptr::null_mut()); - unsafe { BNSetUserCallTypeAdjustment(self.handle, arch.0, addr, adjust_ptr) } + unsafe { BNSetUserCallTypeAdjustment(self.handle, arch.handle, addr, adjust_ptr) } } pub fn set_auto_call_type_adjustment<'a, I>( @@ -645,7 +675,7 @@ impl Function { unsafe { BNSetAutoCallTypeAdjustment( self.handle, - arch.0, + arch.handle, addr, &mut BNTypeWithConfidence { type_: adjust_type.contents.handle, @@ -659,13 +689,13 @@ impl Function { &self, addr: u64, arch: Option, - ) -> Array> { + ) -> Array { let arch = arch.unwrap_or_else(|| self.arch()); let mut count = 0; let adjust = - unsafe { BNGetCallRegisterStackAdjustment(self.handle, arch.0, addr, &mut count) }; + unsafe { BNGetCallRegisterStackAdjustment(self.handle, arch.handle, addr, &mut count) }; assert!(!adjust.is_null()); - unsafe { Array::new(adjust, count, arch.handle()) } + unsafe { Array::new(adjust, count, ()) } } pub fn set_user_call_reg_stack_adjustment( @@ -674,18 +704,17 @@ impl Function { adjust: I, arch: Option, ) where - I: IntoIterator>, + I: IntoIterator, { let arch = arch.unwrap_or_else(|| self.arch()); - let mut adjust_buf: Box<[BNRegisterStackAdjustment]> = - adjust.into_iter().map(|adjust| adjust.into_raw()).collect(); + let adjustments: Vec = adjust.into_iter().map(Into::into).collect(); unsafe { BNSetUserCallRegisterStackAdjustment( self.handle, - arch.0, + arch.handle, addr, - adjust_buf.as_mut_ptr(), - adjust_buf.len(), + adjustments.as_ptr() as *mut _, + adjustments.len(), ) } } @@ -696,19 +725,17 @@ impl Function { adjust: I, arch: Option, ) where - I: IntoIterator>, + I: IntoIterator, { let arch = arch.unwrap_or_else(|| self.arch()); - let mut adjust_buf: Box<[BNRegisterStackAdjustment]> = - adjust.into_iter().map(|reg| reg.into_raw()).collect(); - + let adjustments: Vec = adjust.into_iter().map(Into::into).collect(); unsafe { BNSetAutoCallRegisterStackAdjustment( self.handle, - arch.0, + arch.handle, addr, - adjust_buf.as_mut_ptr(), - adjust_buf.len(), + adjustments.as_ptr() as *mut _, + adjustments.len(), ) } } @@ -718,17 +745,17 @@ impl Function { addr: u64, reg_stack_id: u32, arch: Option, - ) -> RegisterStackAdjustment { + ) -> RegisterStackAdjustment { let arch = arch.unwrap_or_else(|| self.arch()); let adjust = unsafe { BNGetCallRegisterStackAdjustmentForRegisterStack( self.handle, - arch.0, + arch.handle, addr, reg_stack_id, ) }; - unsafe { RegisterStackAdjustment::from_raw(adjust, arch) } + RegisterStackAdjustment::from(adjust) } pub fn set_user_call_reg_stack_adjustment_for_reg_stack( @@ -745,7 +772,7 @@ impl Function { unsafe { BNSetUserCallRegisterStackAdjustmentForRegisterStack( self.handle, - arch.0, + arch.handle, addr, reg_stack_id, adjust.contents, @@ -768,7 +795,7 @@ impl Function { unsafe { BNSetAutoCallRegisterStackAdjustmentForRegisterStack( self.handle, - arch.0, + arch.handle, addr, reg_stack_id, adjust.contents, @@ -777,40 +804,36 @@ impl Function { } } - pub fn reg_stack_adjustments(&self) -> Array> { + pub fn reg_stack_adjustments(&self) -> Array { let mut count = 0; let adjust = unsafe { BNGetFunctionRegisterStackAdjustments(self.handle, &mut count) }; assert!(!adjust.is_null()); - unsafe { Array::new(adjust, count, self.arch().handle()) } + unsafe { Array::new(adjust, count, ()) } } - pub fn set_user_reg_stack_adjustments(&self, values: I) + pub fn set_user_reg_stack_adjustments(&self, values: I) where - I: IntoIterator>, - A: Architecture, + I: IntoIterator, { - let mut values: Box<[BNRegisterStackAdjustment]> = - values.into_iter().map(|r| r.into_raw()).collect(); + let values: Vec = values.into_iter().map(Into::into).collect(); unsafe { BNSetUserFunctionRegisterStackAdjustments( self.handle, - values.as_mut_ptr(), + values.as_ptr() as *mut _, values.len(), ) } } - pub fn set_auto_reg_stack_adjustments(&self, values: I) + pub fn set_auto_reg_stack_adjustments(&self, values: I) where - I: IntoIterator>, - A: Architecture, + I: IntoIterator, { - let mut values: Box<[BNRegisterStackAdjustment]> = - values.into_iter().map(|r| r.into_raw()).collect(); + let values: Vec = values.into_iter().map(Into::into).collect(); unsafe { BNSetAutoFunctionRegisterStackAdjustments( self.handle, - values.as_mut_ptr(), + values.as_ptr() as *mut _, values.len(), ) } @@ -839,7 +862,7 @@ impl Function { let vars = std::slice::from_raw_parts(variables.vars, variables.count); for var in vars.iter().take(variables.count) { - result.push(Variable::from_raw(*var)); + result.push(Variable::from(*var)); } BNFreeParameterVariables(&mut variables); @@ -851,12 +874,12 @@ impl Function { where I: IntoIterator, { - let mut vars: Box<[BNVariable]> = values.into_iter().map(|var| var.raw()).collect(); + let vars: Vec = values.into_iter().map(Into::into).collect(); unsafe { BNSetUserFunctionParameterVariables( self.handle, &mut BNParameterVariablesWithConfidence { - vars: vars.as_mut_ptr(), + vars: vars.as_ptr() as *mut _, count: vars.len(), confidence, }, @@ -868,12 +891,12 @@ impl Function { where I: IntoIterator, { - let mut vars: Box<[BNVariable]> = values.into_iter().map(|var| var.raw()).collect(); + let vars: Vec = values.into_iter().map(Into::into).collect(); unsafe { BNSetAutoFunctionParameterVariables( self.handle, &mut BNParameterVariablesWithConfidence { - vars: vars.as_mut_ptr(), + vars: vars.as_ptr() as *mut _, count: vars.len(), confidence, }, @@ -891,7 +914,7 @@ impl Function { let arch = arch.unwrap_or_else(|| self.arch()); let func_type = func_type.map(|f| f.handle).unwrap_or(core::ptr::null_mut()); let value = - unsafe { BNGetParameterValueAtInstruction(self.handle, arch.0, addr, func_type, i) }; + unsafe { BNGetParameterValueAtInstruction(self.handle, arch.handle, addr, func_type, i) }; value.into() } @@ -1045,15 +1068,15 @@ impl Function { // Create tag let tag = Tag::new(tag_type, data); - let binaryview = unsafe { BinaryView::from_raw(BNGetFunctionData(self.handle)) }; + let binaryview = unsafe { BinaryView::ref_from_raw(BNGetFunctionData(self.handle)) }; unsafe { BNAddTag(binaryview.handle, tag.handle, user) }; unsafe { match (user, addr) { (false, None) => BNAddAutoFunctionTag(self.handle, tag.handle), - (false, Some(addr)) => BNAddAutoAddressTag(self.handle, arch.0, addr, tag.handle), + (false, Some(addr)) => BNAddAutoAddressTag(self.handle, arch.handle, addr, tag.handle), (true, None) => BNAddUserFunctionTag(self.handle, tag.handle), - (true, Some(addr)) => BNAddUserAddressTag(self.handle, arch.0, addr, tag.handle), + (true, Some(addr)) => BNAddUserAddressTag(self.handle, arch.handle, addr, tag.handle), } } } @@ -1075,10 +1098,10 @@ impl Function { match (user, addr) { (false, None) => BNRemoveAutoFunctionTag(self.handle, tag.handle), (false, Some(addr)) => { - BNRemoveAutoAddressTag(self.handle, arch.0, addr, tag.handle) + BNRemoveAutoAddressTag(self.handle, arch.handle, addr, tag.handle) } (true, None) => BNRemoveUserFunctionTag(self.handle, tag.handle), - (true, Some(addr)) => BNRemoveUserAddressTag(self.handle, arch.0, addr, tag.handle), + (true, Some(addr)) => BNRemoveUserAddressTag(self.handle, arch.handle, addr, tag.handle), } } } @@ -1101,11 +1124,11 @@ impl Function { match (user, addr) { (false, None) => BNRemoveAutoFunctionTagsOfType(self.handle, tag_type.handle), (false, Some(addr)) => { - BNRemoveAutoAddressTagsOfType(self.handle, arch.0, addr, tag_type.handle) + BNRemoveAutoAddressTagsOfType(self.handle, arch.handle, addr, tag_type.handle) } (true, None) => BNRemoveUserFunctionTagsOfType(self.handle, tag_type.handle), (true, Some(addr)) => { - BNRemoveUserAddressTagsOfType(self.handle, arch.0, addr, tag_type.handle) + BNRemoveUserAddressTagsOfType(self.handle, arch.handle, addr, tag_type.handle) } } } @@ -1129,7 +1152,7 @@ impl Function { /// ``` pub fn add_user_code_ref(&self, from_addr: u64, to_addr: u64, arch: Option) { let arch = arch.unwrap_or_else(|| self.arch()); - unsafe { BNAddUserCodeReference(self.handle, arch.0, from_addr, to_addr) } + unsafe { BNAddUserCodeReference(self.handle, arch.handle, from_addr, to_addr) } } /// Removes a user-defined cross-reference. @@ -1154,7 +1177,7 @@ impl Function { arch: Option, ) { let arch = arch.unwrap_or_else(|| self.arch()); - unsafe { BNRemoveUserCodeReference(self.handle, arch.0, from_addr, to_addr) } + unsafe { BNRemoveUserCodeReference(self.handle, arch.handle, from_addr, to_addr) } } /// Places a user-defined type cross-reference from the instruction at @@ -1180,7 +1203,7 @@ impl Function { ) { let arch = arch.unwrap_or_else(|| self.arch()); let name_ptr = &name.0 as *const BNQualifiedName as *mut _; - unsafe { BNAddUserTypeReference(self.handle, arch.0, from_addr, name_ptr) } + unsafe { BNAddUserTypeReference(self.handle, arch.handle, from_addr, name_ptr) } } /// Removes a user-defined type cross-reference. @@ -1205,7 +1228,7 @@ impl Function { ) { let arch = arch.unwrap_or_else(|| self.arch()); let name_ptr = &name.0 as *const BNQualifiedName as *mut _; - unsafe { BNRemoveUserTypeReference(self.handle, arch.0, from_addr, name_ptr) } + unsafe { BNRemoveUserTypeReference(self.handle, arch.handle, from_addr, name_ptr) } } /// Places a user-defined type field cross-reference from the @@ -1237,7 +1260,7 @@ impl Function { let arch = arch.unwrap_or_else(|| self.arch()); let name_ptr = &name.0 as *const _ as *mut _; unsafe { - BNAddUserTypeFieldReference(self.handle, arch.0, from_addr, name_ptr, offset, size) + BNAddUserTypeFieldReference(self.handle, arch.handle, from_addr, name_ptr, offset, size) } } @@ -1269,7 +1292,7 @@ impl Function { let arch = arch.unwrap_or_else(|| self.arch()); let name_ptr = &name.0 as *const _ as *mut _; unsafe { - BNRemoveUserTypeFieldReference(self.handle, arch.0, from_addr, name_ptr, offset, size) + BNRemoveUserTypeFieldReference(self.handle, arch.handle, from_addr, name_ptr, offset, size) } } @@ -1280,9 +1303,9 @@ impl Function { size: Option, ) -> (DataBuffer, BuiltinType) { let size = size.unwrap_or(0); - let state_raw = state.into_raw_value(); + // TODO: Adjust `BuiltinType`? let mut builtin_type = BuiltinType::BuiltinNone; - let buffer = DataBuffer::from_raw(unsafe { BNGetConstantData(self.handle, state_raw, value, size, &mut builtin_type) }); + let buffer = DataBuffer::from_raw(unsafe { BNGetConstantData(self.handle, state.into(), value, size, &mut builtin_type) }); (buffer, builtin_type) } @@ -1294,7 +1317,7 @@ impl Function { let arch = arch.unwrap_or_else(|| self.arch()); let mut count = 0; let refs = - unsafe { BNGetConstantsReferencedByInstruction(self.handle, arch.0, addr, &mut count) }; + unsafe { BNGetConstantsReferencedByInstruction(self.handle, arch.handle, addr, &mut count) }; assert!(!refs.is_null()); unsafe { Array::new(refs, count, ()) } } @@ -1307,7 +1330,7 @@ impl Function { let arch = arch.unwrap_or_else(|| self.arch()); let mut count = 0; let refs = unsafe { - BNGetConstantsReferencedByInstructionIfAvailable(self.handle, arch.0, addr, &mut count) + BNGetConstantsReferencedByInstructionIfAvailable(self.handle, arch.handle, addr, &mut count) }; assert!(!refs.is_null()); unsafe { Array::new(refs, count, ()) } @@ -1368,9 +1391,9 @@ impl Function { let mut count = 0; let tags = match auto { - None => unsafe { BNGetAddressTags(self.handle, arch.0, addr, &mut count) }, - Some(true) => unsafe { BNGetAutoAddressTags(self.handle, arch.0, addr, &mut count) }, - Some(false) => unsafe { BNGetUserAddressTags(self.handle, arch.0, addr, &mut count) }, + None => unsafe { BNGetAddressTags(self.handle, arch.handle, addr, &mut count) }, + Some(true) => unsafe { BNGetAutoAddressTags(self.handle, arch.handle, addr, &mut count) }, + Some(false) => unsafe { BNGetUserAddressTags(self.handle, arch.handle, addr, &mut count) }, }; assert!(!tags.is_null()); unsafe { Array::new(tags, count, ()) } @@ -1391,13 +1414,13 @@ impl Function { let tags = match auto { None => unsafe { - BNGetAddressTagsInRange(self.handle, arch.0, range.start, range.end, &mut count) + BNGetAddressTagsInRange(self.handle, arch.handle, range.start, range.end, &mut count) }, Some(true) => unsafe { - BNGetAutoAddressTagsInRange(self.handle, arch.0, range.start, range.end, &mut count) + BNGetAutoAddressTagsInRange(self.handle, arch.handle, range.start, range.end, &mut count) }, Some(false) => unsafe { - BNGetUserAddressTagsInRange(self.handle, arch.0, range.start, range.end, &mut count) + BNGetUserAddressTagsInRange(self.handle, arch.handle, range.start, range.end, &mut count) }, }; assert!(!tags.is_null()); @@ -1425,13 +1448,13 @@ impl Function { .into_iter() .map(|address| BNArchitectureAndAddress { address, - arch: arch.0, + arch: arch.handle, }) .collect(); unsafe { BNSetUserIndirectBranches( self.handle, - arch.0, + arch.handle, source, branches.as_mut_ptr(), branches.len(), @@ -1452,13 +1475,13 @@ impl Function { .into_iter() .map(|address| BNArchitectureAndAddress { address, - arch: arch.0, + arch: arch.handle, }) .collect(); unsafe { BNSetAutoIndirectBranches( self.handle, - arch.0, + arch.handle, source, branches.as_mut_ptr(), branches.len(), @@ -1474,7 +1497,7 @@ impl Function { ) -> Array { let arch = arch.unwrap_or_else(|| self.arch()); let mut count = 0; - let branches = unsafe { BNGetIndirectBranchesAt(self.handle, arch.0, addr, &mut count) }; + let branches = unsafe { BNGetIndirectBranchesAt(self.handle, arch.handle, addr, &mut count) }; assert!(!branches.is_null()); unsafe { Array::new(branches, count, ()) } } @@ -1486,8 +1509,8 @@ impl Function { /// ``` pub fn instr_highlight(&self, addr: u64, arch: Option) -> HighlightColor { let arch = arch.unwrap_or_else(|| self.arch()); - let color = unsafe { BNGetInstructionHighlight(self.handle, arch.0, addr) }; - HighlightColor::from_raw(color) + let color = unsafe { BNGetInstructionHighlight(self.handle, arch.handle, addr) }; + HighlightColor::from(color) } /// Sets the highlights the instruction at the specified address with the supplied color @@ -1504,22 +1527,24 @@ impl Function { arch: Option, ) { let arch = arch.unwrap_or_else(|| self.arch()); - let color_raw = color.into_raw(); - unsafe { BNSetAutoInstructionHighlight(self.handle, arch.0, addr, color_raw) } + unsafe { BNSetAutoInstructionHighlight(self.handle, arch.handle, addr, color.into()) } } /// Sets the highlights the instruction at the specified address with the supplied color /// /// * `addr` - virtual address of the instruction to be highlighted /// * `color` - Color value to use for highlighting - /// * `arch` - (optional) Architecture of the instruction if different from self.arch + /// * `arch` - (optional) Architecture of the instruction, pass this if not views default arch /// /// # Example /// ```no_run - /// # use binaryninja::types::HighlightColor; - /// # let fun: binaryninja::function::Function = todo!(); - /// let color = HighlightColor::NoHighlightColor { alpha: u8::MAX }; - /// fun.set_user_instr_highlight(0x1337, color, None); + /// # use binaryninja::function::{HighlightColor, HighlightStandardColor}; + /// # let function: binaryninja::function::Function = todo!(); + /// let color = HighlightColor::StandardHighlightColor { + /// color: HighlightStandardColor::RedHighlightColor, + /// alpha: u8::MAX + /// }; + /// function.set_user_instr_highlight(0x1337, color, None); /// ``` pub fn set_user_instr_highlight( &self, @@ -1528,8 +1553,7 @@ impl Function { arch: Option, ) { let arch = arch.unwrap_or_else(|| self.arch()); - let color_raw = color.into_raw(); - unsafe { BNSetUserInstructionHighlight(self.handle, arch.0, addr, color_raw) } + unsafe { BNSetUserInstructionHighlight(self.handle, arch.handle, addr, color.into()) } } /// return the address, if any, of the instruction that contains the @@ -1541,7 +1565,7 @@ impl Function { ) -> Option { let arch = arch.unwrap_or_else(|| self.arch()); let mut start = 0; - unsafe { BNGetInstructionContainingAddress(self.handle, arch.0, addr, &mut start) } + unsafe { BNGetInstructionContainingAddress(self.handle, arch.handle, addr, &mut start) } .then_some(start) } @@ -1561,7 +1585,7 @@ impl Function { arch: Option, ) -> IntegerDisplayType { let arch = arch.unwrap_or_else(|| self.arch()); - unsafe { BNGetIntegerConstantDisplayType(self.handle, arch.0, instr_addr, value, operand) } + unsafe { BNGetIntegerConstantDisplayType(self.handle, arch.handle, instr_addr, value, operand) } } /// Change the text display type for an integer token in the disassembly or IL views @@ -1589,7 +1613,7 @@ impl Function { unsafe { BNSetIntegerConstantDisplayType( self.handle, - arch.0, + arch.handle, instr_addr, value, operand, @@ -1618,7 +1642,7 @@ impl Function { unsafe { BnString::from_raw(BNGetIntegerConstantDisplayTypeEnumerationType( self.handle, - arch.0, + arch.handle, instr_addr, value, operand, @@ -1665,7 +1689,7 @@ impl Function { arch: Option, ) -> RegisterValue { let arch = arch.unwrap_or_else(|| self.arch()); - let register = unsafe { BNGetRegisterValueAtInstruction(self.handle, arch.0, addr, reg) }; + let register = unsafe { BNGetRegisterValueAtInstruction(self.handle, arch.handle, addr, reg) }; register.into() } @@ -1690,7 +1714,7 @@ impl Function { ) -> RegisterValue { let arch = arch.unwrap_or_else(|| self.arch()); let register = - unsafe { BNGetRegisterValueAfterInstruction(self.handle, arch.0, addr, reg) }; + unsafe { BNGetRegisterValueAfterInstruction(self.handle, arch.handle, addr, reg) }; register.into() } @@ -1707,7 +1731,7 @@ impl Function { let arch = arch.unwrap_or_else(|| self.arch()); let mut count = 0; let regs = - unsafe { BNGetRegistersReadByInstruction(self.handle, arch.0, addr, &mut count) }; + unsafe { BNGetRegistersReadByInstruction(self.handle, arch.handle, addr, &mut count) }; assert!(!regs.is_null()); unsafe { Array::new(regs, count, arch) } } @@ -1720,7 +1744,7 @@ impl Function { let arch = arch.unwrap_or_else(|| self.arch()); let mut count = 0; let regs = - unsafe { BNGetRegistersWrittenByInstruction(self.handle, arch.0, addr, &mut count) }; + unsafe { BNGetRegistersWrittenByInstruction(self.handle, arch.handle, addr, &mut count) }; assert!(!regs.is_null()); unsafe { Array::new(regs, count, arch) } } @@ -1768,7 +1792,7 @@ impl Function { ) -> RegisterValue { let arch = arch.unwrap_or_else(|| self.arch()); let value = - unsafe { BNGetStackContentsAtInstruction(self.handle, arch.0, addr, offset, size) }; + unsafe { BNGetStackContentsAtInstruction(self.handle, arch.handle, addr, offset, size) }; value.into() } @@ -1781,7 +1805,7 @@ impl Function { ) -> RegisterValue { let arch = arch.unwrap_or_else(|| self.arch()); let value = - unsafe { BNGetStackContentsAfterInstruction(self.handle, arch.0, addr, offset, size) }; + unsafe { BNGetStackContentsAfterInstruction(self.handle, arch.handle, addr, offset, size) }; value.into() } @@ -1794,12 +1818,12 @@ impl Function { let arch = arch.unwrap_or_else(|| self.arch()); let mut found_value: BNVariableNameAndType = unsafe { mem::zeroed() }; let found = unsafe { - BNGetStackVariableAtFrameOffset(self.handle, arch.0, addr, offset, &mut found_value) + BNGetStackVariableAtFrameOffset(self.handle, arch.handle, addr, offset, &mut found_value) }; if !found { return None; } - let var = unsafe { Variable::from_raw(found_value.var) }; + let var = Variable::from(found_value.var); let name = unsafe { BnString::from_raw(found_value.name) }; let var_type = Conf::new( unsafe { Type::ref_from_raw(found_value.type_) }, @@ -1816,7 +1840,7 @@ impl Function { let arch = arch.unwrap_or_else(|| self.arch()); let mut count = 0; let refs = unsafe { - BNGetStackVariablesReferencedByInstruction(self.handle, arch.0, addr, &mut count) + BNGetStackVariablesReferencedByInstruction(self.handle, arch.handle, addr, &mut count) }; assert!(!refs.is_null()); unsafe { Array::new(refs, count, ()) } @@ -1832,7 +1856,7 @@ impl Function { let refs = unsafe { BNGetStackVariablesReferencedByInstructionIfAvailable( self.handle, - arch.0, + arch.handle, addr, &mut count, ) @@ -1860,11 +1884,12 @@ impl Function { pub fn is_call_instruction(&self, addr: u64, arch: Option) -> bool { let arch = arch.unwrap_or_else(|| self.arch()); - unsafe { BNIsCallInstruction(self.handle, arch.0, addr) } + unsafe { BNIsCallInstruction(self.handle, arch.handle, addr) } } pub fn is_variable_user_defined(&self, var: &Variable) -> bool { - unsafe { BNIsVariableUserDefined(self.handle, &var.raw()) } + let raw_var = BNVariable::from(var); + unsafe { BNIsVariableUserDefined(self.handle, &raw_var) } } pub fn is_pure(&self) -> Conf { @@ -1906,7 +1931,7 @@ impl Function { /// Indicates that callers of this function need to be reanalyzed during the next update cycle /// - /// * `uppdate_type` - Desired update type + /// * `update_type` - Desired update type pub fn mark_caller_updates_required(&self, update_type: FunctionUpdateType) { unsafe { BNMarkCallerUpdatesRequired(self.handle, update_type) } } @@ -1933,11 +1958,12 @@ impl Function { target: &Variable, sources: impl IntoIterator, ) { - let sources_raw: Box<[BNVariable]> = sources.into_iter().map(|s| s.raw()).collect(); + let raw_target_var = BNVariable::from(target); + let sources_raw: Vec = sources.into_iter().copied().map(Into::into).collect(); unsafe { BNMergeVariables( self.handle, - &target.raw(), + &raw_target_var, sources_raw.as_ptr(), sources_raw.len(), ) @@ -1954,11 +1980,12 @@ impl Function { target: &Variable, sources: impl IntoIterator, ) { - let sources_raw: Box<[BNVariable]> = sources.into_iter().map(|s| s.raw()).collect(); + let raw_target_var = BNVariable::from(target); + let sources_raw: Vec = sources.into_iter().copied().map(Into::into).collect(); unsafe { BNUnmergeVariables( self.handle, - &target.raw(), + &raw_target_var, sources_raw.as_ptr(), sources_raw.len(), ) @@ -1984,7 +2011,8 @@ impl Function { /// /// * `var` - variable to split pub fn split_variable(&self, var: &Variable) { - unsafe { BNSplitVariable(self.handle, &var.raw()) } + let raw_var = BNVariable::from(var); + unsafe { BNSplitVariable(self.handle, &raw_var) } } /// Undoes varible splitting performed with [Function::split_variable]. The given `var` @@ -1993,7 +2021,8 @@ impl Function { /// /// * `var` - variable to unsplit pub fn unsplit_variable(&self, var: &Variable) { - unsafe { BNUnsplitVariable(self.handle, &var.raw()) } + let raw_var = BNVariable::from(var); + unsafe { BNUnsplitVariable(self.handle, &raw_var) } } /// Causes this function to be reanalyzed. This function does not wait for the analysis to finish. @@ -2299,97 +2328,302 @@ impl PartialEq for Function { } } -///////////////// -// AddressRange - -#[repr(transparent)] -pub struct AddressRange(pub(crate) BNAddressRange); +#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] +pub struct AddressRange { + pub start: u64, + pub end: u64 +} -impl AddressRange { - pub fn start(&self) -> u64 { - self.0.start +impl From for AddressRange { + fn from(raw: BNAddressRange) -> Self { + Self { + start: raw.start, + end: raw.end + } } +} - pub fn end(&self) -> u64 { - self.0.end +impl From for BNAddressRange { + fn from(raw: AddressRange) -> Self { + Self { + start: raw.start, + end: raw.end + } } } impl CoreArrayProvider for AddressRange { type Raw = BNAddressRange; type Context = (); - type Wrapped<'a> = &'a AddressRange; + type Wrapped<'a> = Self; } + unsafe impl CoreArrayProviderInner for AddressRange { unsafe fn free(raw: *mut Self::Raw, _count: usize, _context: &Self::Context) { BNFreeAddressRanges(raw); } + unsafe fn wrap_raw<'a>(raw: &'a Self::Raw, _context: &'a Self::Context) -> Self::Wrapped<'a> { - mem::transmute(raw) + Self::from(*raw) } } -///////////////// -// PerformanceInfo - -// NOTE only exists as Array, cant be owned -#[repr(transparent)] -pub struct PerformanceInfo(BNPerformanceInfo); +#[derive(Clone, Debug, Eq, Hash, PartialEq)] +pub struct PerformanceInfo { + pub name: String, + pub seconds: Duration, +} -impl PerformanceInfo { - pub fn name(&self) -> &str { - unsafe { std::ffi::CStr::from_ptr(self.0.name) } - .to_str() - .unwrap() - } - pub fn seconds(&self) -> f64 { - self.0.seconds +impl From for PerformanceInfo { + fn from(value: BNPerformanceInfo) -> Self { + Self { + name: unsafe { BnString::from_raw(value.name) }.to_string(), + seconds: Duration::from_secs_f64(value.seconds), + } } } impl CoreArrayProvider for PerformanceInfo { type Raw = BNPerformanceInfo; type Context = (); - type Wrapped<'a> = Guard<'a, PerformanceInfo>; + type Wrapped<'a> = Self; } + unsafe impl CoreArrayProviderInner for PerformanceInfo { unsafe fn free(raw: *mut Self::Raw, count: usize, _context: &Self::Context) { BNFreeAnalysisPerformanceInfo(raw, count); } - unsafe fn wrap_raw<'a>(raw: &'a Self::Raw, context: &'a Self::Context) -> Self::Wrapped<'a> { - Guard::new(Self(*raw), context) + + unsafe fn wrap_raw<'a>(raw: &'a Self::Raw, _context: &'a Self::Context) -> Self::Wrapped<'a> { + Self::from(*raw) } } -///////////////// -// Comments +// NOTE: only exists as part of an Array, never owned +pub struct UnresolvedIndirectBranches(u64); -// NOTE only exists as Array, cant be owned -pub struct Comments { - addr: u64, - comment: BnString, +impl UnresolvedIndirectBranches { + pub fn address(&self) -> u64 { + self.0 + } } -impl Comments { - pub fn address(&self) -> u64 { - self.addr +impl CoreArrayProvider for UnresolvedIndirectBranches { + type Raw = u64; + type Context = (); + type Wrapped<'a> = Self; +} + +unsafe impl CoreArrayProviderInner for UnresolvedIndirectBranches { + unsafe fn free(raw: *mut Self::Raw, _count: usize, _context: &Self::Context) { + BNFreeAddressList(raw) + } + + unsafe fn wrap_raw<'a>(raw: &'a Self::Raw, _context: &'a Self::Context) -> Self::Wrapped<'a> { + Self(*raw) + } +} + +#[derive(Clone, Copy, Debug, Hash, Eq, PartialEq)] +pub struct ConstantReference { + pub value: i64, + pub size: usize, + pub pointer: bool, + pub intermediate: bool, +} + +impl From for ConstantReference { + fn from(value: BNConstantReference) -> Self { + Self { + value: value.value, + size: value.size, + pointer: value.pointer, + intermediate: value.intermediate, + } + } +} + +impl From for BNConstantReference { + fn from(value: ConstantReference) -> Self { + Self { + value: value.value, + size: value.size, + pointer: value.pointer, + intermediate: value.intermediate, + } + } +} + +impl CoreArrayProvider for ConstantReference { + type Raw = BNConstantReference; + type Context = (); + type Wrapped<'a> = Self; +} + +unsafe impl CoreArrayProviderInner for ConstantReference { + unsafe fn free(raw: *mut Self::Raw, _count: usize, _context: &Self::Context) { + BNFreeConstantReferenceList(raw) } - pub fn comment(&self) -> &str { - self.comment.as_str() + + unsafe fn wrap_raw<'a>(raw: &'a Self::Raw, _context: &'a Self::Context) -> Self::Wrapped<'a> { + Self::from(*raw) } } -impl CoreArrayProvider for Comments { +#[derive(Debug, Copy, Clone)] +pub struct RegisterStackAdjustment { + pub register_id: u32, + pub adjustment: Conf, +} + +impl RegisterStackAdjustment { + pub fn new(register_id: u32, adjustment: impl Into>) -> Self { + Self { + register_id, + adjustment: adjustment.into(), + } + } +} + +impl From for RegisterStackAdjustment { + fn from(value: BNRegisterStackAdjustment) -> Self { + Self { + register_id: value.regStack, + adjustment: Conf::new(value.adjustment, value.confidence), + } + } +} + +impl From for BNRegisterStackAdjustment { + fn from(value: RegisterStackAdjustment) -> Self { + Self { + regStack: value.register_id, + adjustment: value.adjustment.contents, + confidence: value.adjustment.confidence, + } + } +} + +impl CoreArrayProvider for RegisterStackAdjustment { + type Raw = BNRegisterStackAdjustment; + type Context = (); + type Wrapped<'a> = Self; +} + +unsafe impl CoreArrayProviderInner for RegisterStackAdjustment { + unsafe fn free(raw: *mut Self::Raw, _count: usize, _context: &Self::Context) { + BNFreeRegisterStackAdjustments(raw) + } + + unsafe fn wrap_raw<'a>(raw: &'a Self::Raw, _context: &'a Self::Context) -> Self::Wrapped<'a> { + Self::from(*raw) + } +} + +#[derive(Clone, Copy, Debug, Hash, Eq, PartialEq)] +pub enum HighlightColor { + StandardHighlightColor { + color: HighlightStandardColor, + alpha: u8, + }, + MixedHighlightColor { + color: HighlightStandardColor, + mix_color: HighlightStandardColor, + mix: u8, + alpha: u8, + }, + CustomHighlightColor { + r: u8, + g: u8, + b: u8, + alpha: u8, + }, +} + +impl From for HighlightColor { + fn from(value: BNHighlightColor) -> Self { + match value.style { + BNHighlightColorStyle::StandardHighlightColor => { + Self::StandardHighlightColor { + color: value.color, + alpha: value.alpha, + } + } + BNHighlightColorStyle::MixedHighlightColor => { + Self::MixedHighlightColor { + color: value.color, + mix_color: value.mixColor, + mix: value.mix, + alpha: value.alpha, + } + } + BNHighlightColorStyle::CustomHighlightColor => { + Self::CustomHighlightColor { + r: value.r, + g: value.g, + b: value.b, + alpha: value.alpha, + } + } + } + } +} + +impl From for BNHighlightColor { + fn from(value: HighlightColor) -> Self { + match value { + HighlightColor::StandardHighlightColor { color, alpha } => { + BNHighlightColor { + style: BNHighlightColorStyle::StandardHighlightColor, + color, + alpha, + ..Default::default() + } + } + HighlightColor::MixedHighlightColor { color, mix_color, mix, alpha } => { + BNHighlightColor { + style: BNHighlightColorStyle::MixedHighlightColor, + color, + mixColor: mix_color, + mix, + alpha, + ..Default::default() + } + } + HighlightColor::CustomHighlightColor { r, g, b, alpha } => { + BNHighlightColor { + style: BNHighlightColorStyle::CustomHighlightColor, + r, + g, + b, + alpha, + ..Default::default() + } + } + } + } +} + +// NOTE only exists as Array, cant be owned +#[derive(Clone, Debug, Hash, Eq, PartialEq)] +pub struct Comment { + pub addr: u64, + pub comment: BnString, +} + +impl CoreArrayProvider for Comment { type Raw = u64; type Context = Ref; - type Wrapped<'a> = Comments; + type Wrapped<'a> = Comment; } -unsafe impl CoreArrayProviderInner for Comments { + +unsafe impl CoreArrayProviderInner for Comment { unsafe fn free(raw: *mut Self::Raw, _count: usize, _context: &Self::Context) { BNFreeAddressList(raw); } + unsafe fn wrap_raw<'a>(raw: &'a Self::Raw, function: &'a Self::Context) -> Self::Wrapped<'a> { - Comments { + Comment { addr: *raw, comment: function.comment_at(*raw), } diff --git a/rust/src/functionrecognizer.rs b/rust/src/functionrecognizer.rs index c63edcab0..0f7c41730 100644 --- a/rust/src/functionrecognizer.rs +++ b/rust/src/functionrecognizer.rs @@ -1,4 +1,3 @@ -use crate::architecture::Architecture; use crate::{ architecture::CoreArchitecture, binaryview::BinaryView, function::Function, llil, mlil, }; @@ -47,9 +46,9 @@ where R: 'static + FunctionRecognizer + Send + Sync, { let custom_handler = unsafe { &*(ctxt as *mut R) }; - let bv = unsafe { BinaryView::from_raw(BNNewViewReference(bv)) }; + let bv = unsafe { BinaryView::ref_from_raw(BNNewViewReference(bv)) }; let arch = unsafe { BNGetFunctionArchitecture(func) }; - let func = unsafe { Function::from_raw(BNNewFunctionReference(func)) }; + let func = unsafe { Function::ref_from_raw(func) }; if arch.is_null() { return false; } @@ -68,8 +67,8 @@ where R: 'static + FunctionRecognizer + Send + Sync, { let custom_handler = unsafe { &*(ctxt as *mut R) }; - let bv = unsafe { BinaryView::from_raw(BNNewViewReference(bv)) }; - let func = unsafe { Function::from_raw(BNNewFunctionReference(func)) }; + let bv = unsafe { BinaryView::ref_from_raw(BNNewViewReference(bv)) }; + let func = unsafe { Function::ref_from_raw(func) }; let mlil = unsafe { mlil::MediumLevelILFunction::ref_from_raw(mlil) }; custom_handler.recognize_medium_level_il(bv.as_ref(), func.as_ref(), &mlil) } @@ -100,7 +99,7 @@ where let mut recognizer = create_function_recognizer_registration::(recognizer); unsafe { BNRegisterArchitectureFunctionRecognizer( - arch.handle().as_ref().0, + arch.as_ref().handle, &mut recognizer as *mut _, ); } diff --git a/rust/src/hlil/function.rs b/rust/src/hlil/function.rs index 25608d714..1f8088021 100644 --- a/rust/src/hlil/function.rs +++ b/rust/src/hlil/function.rs @@ -93,7 +93,7 @@ impl HighLevelILFunction { pub fn get_function(&self) -> Ref { unsafe { let func = BNGetHighLevelILOwnerFunction(self.handle); - Function::from_raw(func) + Function::ref_from_raw(func) } } diff --git a/rust/src/hlil/instruction.rs b/rust/src/hlil/instruction.rs index 6b27284d4..77e83012c 100644 --- a/rust/src/hlil/instruction.rs +++ b/rust/src/hlil/instruction.rs @@ -4,8 +4,7 @@ use binaryninjacore_sys::BNHighLevelILOperation; use crate::architecture::CoreIntrinsic; use crate::operand_iter::OperandIter; use crate::rc::Ref; -use crate::types::{ConstantData, RegisterValue, RegisterValueType, SSAVariable, Variable}; - +use crate::variable::{ConstantData, RegisterValue, SSAVariable, Variable}; use super::operation::*; use super::{HighLevelILFunction, HighLevelILLiftedInstruction, HighLevelILLiftedInstructionKind}; @@ -750,7 +749,10 @@ impl HighLevelILInstruction { constant_data: ConstantData::new( self.function.get_function(), RegisterValue { - state: RegisterValueType::from_raw_value(op.constant_data_kind).unwrap(), + // TODO: Replace with a From for RegisterValueType. + // TODO: We might also want to change the type of `op.constant_data_kind` + // TODO: To RegisterValueType and do the conversion when creating instruction. + state: unsafe { std::mem::transmute(op.constant_data_kind) }, value: op.constant_data_value, offset: 0, size: op.size, @@ -811,11 +813,11 @@ impl HighLevelILInstruction { cond_false: self.lift_operand(op.cond_false), }), Intrinsic(op) => Lifted::Intrinsic(LiftedIntrinsic { - intrinsic: CoreIntrinsic(self.function.get_function().arch().0, op.intrinsic), + intrinsic: CoreIntrinsic::new(self.function.get_function().arch().handle, op.intrinsic), params: self.lift_instruction_list(op.first_param, op.num_params), }), IntrinsicSsa(op) => Lifted::IntrinsicSsa(LiftedIntrinsicSsa { - intrinsic: CoreIntrinsic(self.function.get_function().arch().0, op.intrinsic), + intrinsic: CoreIntrinsic::new(self.function.get_function().arch().handle, op.intrinsic), params: self.lift_instruction_list(op.first_param, op.num_params), dest_memory: op.dest_memory, src_memory: op.src_memory, @@ -983,7 +985,7 @@ fn get_float(value: u64, size: usize) -> f64 { } fn get_var(id: u64) -> Variable { - unsafe { Variable::from_identifier(id) } + Variable::from_identifier(id) } fn get_member_index(idx: u64) -> Option { diff --git a/rust/src/hlil/lift.rs b/rust/src/hlil/lift.rs index 300a7f1fb..fdfebc0be 100644 --- a/rust/src/hlil/lift.rs +++ b/rust/src/hlil/lift.rs @@ -3,7 +3,7 @@ use super::HighLevelILFunction; use crate::architecture::CoreIntrinsic; use crate::rc::Ref; -use crate::types::{ConstantData, SSAVariable, Variable}; +use crate::variable::{ConstantData, SSAVariable, Variable}; #[derive(Clone)] pub enum HighLevelILLiftedOperand { diff --git a/rust/src/hlil/operation.rs b/rust/src/hlil/operation.rs index e762a74bc..439285113 100644 --- a/rust/src/hlil/operation.rs +++ b/rust/src/hlil/operation.rs @@ -4,8 +4,7 @@ use crate::architecture::CoreIntrinsic; use crate::function::Function; use crate::rc::Ref; use crate::string::BnString; -use crate::types::{ConstantData, SSAVariable, Variable}; - +use crate::variable::{ConstantData, SSAVariable, Variable}; use super::HighLevelILLiftedInstruction; #[derive(Clone, Debug, PartialEq, Eq)] @@ -197,6 +196,7 @@ pub struct ConstData { pub constant_data_value: i64, pub size: usize, } + #[derive(Clone, Debug, PartialEq)] pub struct LiftedConstData { pub constant_data: ConstantData, @@ -220,6 +220,7 @@ pub struct DerefFieldSsa { pub offset: u64, pub member_index: Option, } + #[derive(Clone, Debug, PartialEq)] pub struct LiftedDerefFieldSsa { pub src: Box, @@ -234,6 +235,7 @@ pub struct DerefSsa { pub src: usize, pub src_memory: u64, } + #[derive(Clone, Debug, PartialEq)] pub struct LiftedDerefSsa { pub src: Box, @@ -261,6 +263,7 @@ pub struct ForLoop { pub update: usize, pub body: usize, } + #[derive(Clone, Debug, PartialEq)] pub struct LiftedForLoop { pub init: Box, diff --git a/rust/src/lib.rs b/rust/src/lib.rs index eff9518d2..f7b409b06 100644 --- a/rust/src/lib.rs +++ b/rust/src/lib.rs @@ -171,14 +171,13 @@ pub mod typearchive; pub mod types; pub mod update; pub mod workflow; +pub mod variable; +pub mod confidence; use std::collections::HashMap; -use std::ffi::{c_void, CStr}; +use std::ffi::{c_char, c_void, CStr}; use std::path::PathBuf; -use std::ptr; -use binaryninjacore_sys::{BNBinaryView, BNFileMetadata, BNFunction, BNObjectDestructionCallbacks, BNRegisterObjectDestructionCallbacks, BNUnregisterObjectDestructionCallbacks}; -pub use binaryninjacore_sys::BNBranchType as BranchType; -pub use binaryninjacore_sys::BNEndianness as Endianness; +use binaryninjacore_sys::*; use binaryview::BinaryView; use metadata::Metadata; use metadata::MetadataType; @@ -203,11 +202,16 @@ use crate::function::Function; // const BN_MINIMUM_CONFIDENCE: u8 = 1; // const BN_HEURISTIC_CONFIDENCE: u8 = 192; -const BN_FULL_CONFIDENCE: u8 = 255; -const BN_INVALID_EXPR: usize = usize::MAX; +pub use binaryninjacore_sys::BNBranchType as BranchType; +pub use binaryninjacore_sys::BNEndianness as Endianness; +pub use binaryninjacore_sys::BNILBranchDependence as ILBranchDependence; +pub use binaryninjacore_sys::BNDataFlowQueryOption as DataFlowQueryOption; + +pub const BN_FULL_CONFIDENCE: u8 = u8::MAX; +pub const BN_INVALID_EXPR: usize = usize::MAX; unsafe extern "C" fn cb_progress_func bool>( - ctxt: *mut std::ffi::c_void, + ctxt: *mut c_void, progress: usize, total: usize, ) -> bool { @@ -220,7 +224,7 @@ unsafe extern "C" fn cb_progress_func bool>( unsafe extern "C" fn cb_progress_nop( - _ctxt: *mut std::ffi::c_void, + _ctxt: *mut c_void, _arg1: usize, _arg2: usize ) -> bool { @@ -231,7 +235,7 @@ unsafe extern "C" fn cb_progress_nop( /// The main way to open and load files into Binary Ninja. Make sure you've properly initialized the core before calling this function. See [`crate::headless::init()`] pub fn load( filename: S, -) -> Option> +) -> Option> where S: BnStrCompatible, { @@ -240,26 +244,26 @@ where let handle = unsafe { - binaryninjacore_sys::BNLoadFilename( + BNLoadFilename( filename.as_ref().as_ptr() as *mut _, true, - options.as_ptr() as *mut core::ffi::c_char, + options.as_ptr() as *mut c_char, Some(cb_progress_nop), - ptr::null_mut(), + std::ptr::null_mut(), ) }; if handle.is_null() { None } else { - Some(unsafe { BinaryView::from_raw(handle) }) + Some(unsafe { BinaryView::ref_from_raw(handle) }) } } pub fn load_with_progress( filename: S, mut progress: F, -) -> Option> +) -> Option> where S: BnStrCompatible, F: FnMut(usize, usize) -> bool, @@ -267,13 +271,13 @@ where let filename = filename.into_bytes_with_nul(); let options = "\x00"; - let progress_ctx = &mut progress as *mut F as *mut std::ffi::c_void; + let progress_ctx = &mut progress as *mut F as *mut c_void; let handle = unsafe { - binaryninjacore_sys::BNLoadFilename( + BNLoadFilename( filename.as_ref().as_ptr() as *mut _, true, - options.as_ptr() as *mut core::ffi::c_char, + options.as_ptr() as *mut c_char, Some(cb_progress_func::), progress_ctx, ) @@ -282,7 +286,7 @@ where if handle.is_null() { None } else { - Some(unsafe { BinaryView::from_raw(handle) }) + Some(unsafe { BinaryView::ref_from_raw(handle) }) } } @@ -307,7 +311,7 @@ pub fn load_with_options( filename: S, update_analysis_and_wait: bool, options: Option, -) -> Option> +) -> Option> where S: BnStrCompatible, O: IntoJson, @@ -329,19 +333,19 @@ where }; let handle = unsafe { - binaryninjacore_sys::BNLoadFilename( + BNLoadFilename( filename.as_ref().as_ptr() as *mut _, update_analysis_and_wait, - options_or_default.as_ptr() as *mut core::ffi::c_char, + options_or_default.as_ptr() as *mut c_char, Some(cb_progress_nop), - core::ptr::null_mut(), + std::ptr::null_mut(), ) }; if handle.is_null() { None } else { - Some(unsafe { BinaryView::from_raw(handle) }) + Some(unsafe { BinaryView::ref_from_raw(handle) }) } } @@ -350,7 +354,7 @@ pub fn load_with_options_and_progress( update_analysis_and_wait: bool, options: Option, progress: Option, -) -> Option> +) -> Option> where S: BnStrCompatible, O: IntoJson, @@ -373,15 +377,15 @@ where }; let progress_ctx = match progress { - Some(mut x) => &mut x as *mut F as *mut std::ffi::c_void, - None => core::ptr::null_mut() + Some(mut x) => &mut x as *mut F as *mut c_void, + None => std::ptr::null_mut() }; let handle = unsafe { - binaryninjacore_sys::BNLoadFilename( + BNLoadFilename( filename.as_ref().as_ptr() as *mut _, update_analysis_and_wait, - options_or_default.as_ptr() as *mut core::ffi::c_char, + options_or_default.as_ptr() as *mut c_char, Some(cb_progress_func::), progress_ctx, ) @@ -390,7 +394,7 @@ where if handle.is_null() { None } else { - Some(unsafe { BinaryView::from_raw(handle) }) + Some(unsafe { BinaryView::ref_from_raw(handle) }) } } @@ -417,19 +421,19 @@ where }; let handle = unsafe { - binaryninjacore_sys::BNLoadBinaryView( + BNLoadBinaryView( bv.handle as *mut _, update_analysis_and_wait, - options_or_default.as_ptr() as *mut core::ffi::c_char, + options_or_default.as_ptr() as *mut c_char, Some(cb_progress_nop), - core::ptr::null_mut(), + std::ptr::null_mut(), ) }; if handle.is_null() { None } else { - Some(unsafe { BinaryView::from_raw(handle) }) + Some(unsafe { BinaryView::ref_from_raw(handle) }) } } @@ -458,15 +462,15 @@ where }; let progress_ctx = match progress { - Some(mut x) => &mut x as *mut F as *mut std::ffi::c_void, - None => core::ptr::null_mut() + Some(mut x) => &mut x as *mut F as *mut c_void, + None => std::ptr::null_mut() }; let handle = unsafe { - binaryninjacore_sys::BNLoadBinaryView( + BNLoadBinaryView( bv.handle as *mut _, update_analysis_and_wait, - options_or_default.as_ptr() as *mut core::ffi::c_char, + options_or_default.as_ptr() as *mut c_char, Some(cb_progress_func::), progress_ctx, ) @@ -475,12 +479,12 @@ where if handle.is_null() { None } else { - Some(unsafe { BinaryView::from_raw(handle) }) + Some(unsafe { BinaryView::ref_from_raw(handle) }) } } pub fn install_directory() -> Result { - let s: *mut std::os::raw::c_char = unsafe { binaryninjacore_sys::BNGetInstallDirectory() }; + let s: *mut c_char = unsafe { BNGetInstallDirectory() }; if s.is_null() { return Err(()); } @@ -490,8 +494,7 @@ pub fn install_directory() -> Result { } pub fn bundled_plugin_directory() -> Result { - let s: *mut std::os::raw::c_char = - unsafe { binaryninjacore_sys::BNGetBundledPluginDirectory() }; + let s: *mut c_char = unsafe { BNGetBundledPluginDirectory() }; if s.is_null() { return Err(()); } @@ -502,14 +505,14 @@ pub fn bundled_plugin_directory() -> Result { pub fn set_bundled_plugin_directory(new_dir: S) { unsafe { - binaryninjacore_sys::BNSetBundledPluginDirectory( - new_dir.into_bytes_with_nul().as_ref().as_ptr() as *const std::os::raw::c_char, + BNSetBundledPluginDirectory( + new_dir.into_bytes_with_nul().as_ref().as_ptr() as *const c_char, ) }; } pub fn user_directory() -> Result { - let s: *mut std::os::raw::c_char = unsafe { binaryninjacore_sys::BNGetUserDirectory() }; + let s: *mut c_char = unsafe { BNGetUserDirectory() }; if s.is_null() { return Err(()); } @@ -519,7 +522,7 @@ pub fn user_directory() -> Result { } pub fn user_plugin_directory() -> Result { - let s: *mut std::os::raw::c_char = unsafe { binaryninjacore_sys::BNGetUserPluginDirectory() }; + let s: *mut c_char = unsafe { BNGetUserPluginDirectory() }; if s.is_null() { return Err(()); } @@ -529,7 +532,7 @@ pub fn user_plugin_directory() -> Result { } pub fn repositories_directory() -> Result { - let s: *mut std::os::raw::c_char = unsafe { binaryninjacore_sys::BNGetRepositoriesDirectory() }; + let s: *mut c_char = unsafe { BNGetRepositoriesDirectory() }; if s.is_null() { return Err(()); } @@ -539,7 +542,7 @@ pub fn repositories_directory() -> Result { } pub fn settings_file_name() -> Result { - let s: *mut std::os::raw::c_char = unsafe { binaryninjacore_sys::BNGetSettingsFileName() }; + let s: *mut c_char = unsafe { BNGetSettingsFileName() }; if s.is_null() { return Err(()); } @@ -549,15 +552,15 @@ pub fn settings_file_name() -> Result { } pub fn save_last_run() { - unsafe { binaryninjacore_sys::BNSaveLastRun() }; + unsafe { BNSaveLastRun() }; } pub fn path_relative_to_bundled_plugin_directory( path: S, ) -> Result { - let s: *mut std::os::raw::c_char = unsafe { - binaryninjacore_sys::BNGetPathRelativeToBundledPluginDirectory( - path.into_bytes_with_nul().as_ref().as_ptr() as *const std::os::raw::c_char, + let s: *mut c_char = unsafe { + BNGetPathRelativeToBundledPluginDirectory( + path.into_bytes_with_nul().as_ref().as_ptr() as *const c_char, ) }; if s.is_null() { @@ -571,9 +574,9 @@ pub fn path_relative_to_bundled_plugin_directory( pub fn path_relative_to_user_plugin_directory( path: S, ) -> Result { - let s: *mut std::os::raw::c_char = unsafe { - binaryninjacore_sys::BNGetPathRelativeToUserPluginDirectory( - path.into_bytes_with_nul().as_ref().as_ptr() as *const std::os::raw::c_char, + let s: *mut c_char = unsafe { + BNGetPathRelativeToUserPluginDirectory( + path.into_bytes_with_nul().as_ref().as_ptr() as *const c_char, ) }; if s.is_null() { @@ -585,9 +588,9 @@ pub fn path_relative_to_user_plugin_directory( } pub fn path_relative_to_user_directory(path: S) -> Result { - let s: *mut std::os::raw::c_char = unsafe { - binaryninjacore_sys::BNGetPathRelativeToUserDirectory( - path.into_bytes_with_nul().as_ref().as_ptr() as *const std::os::raw::c_char, + let s: *mut c_char = unsafe { + BNGetPathRelativeToUserDirectory( + path.into_bytes_with_nul().as_ref().as_ptr() as *const c_char, ) }; if s.is_null() { @@ -602,13 +605,13 @@ pub fn memory_info() -> HashMap { let mut count = 0; let mut usage = HashMap::new(); unsafe { - let info_ptr = binaryninjacore_sys::BNGetMemoryUsageInfo(&mut count); + let info_ptr = BNGetMemoryUsageInfo(&mut count); let info_list = std::slice::from_raw_parts(info_ptr, count); for info in info_list { let info_name = CStr::from_ptr(info.name).to_str().unwrap().to_string(); usage.insert(info_name, info.value); } - binaryninjacore_sys::BNFreeMemoryUsageInfo(info_ptr, count); + BNFreeMemoryUsageInfo(info_ptr, count); } usage } @@ -665,11 +668,11 @@ pub trait ObjectDestructor: 'static + Sync + Sized { } pub fn version() -> string::BnString { - unsafe { string::BnString::from_raw(binaryninjacore_sys::BNGetVersionString()) } + unsafe { string::BnString::from_raw(BNGetVersionString()) } } pub fn build_id() -> u32 { - unsafe { binaryninjacore_sys::BNGetBuildId() } + unsafe { BNGetBuildId() } } #[derive(Clone, PartialEq, Eq, Hash)] @@ -681,7 +684,7 @@ pub struct VersionInfo { } pub fn version_info() -> VersionInfo { - let info_raw = unsafe { binaryninjacore_sys::BNGetVersionInfo() }; + let info_raw = unsafe { BNGetVersionInfo() }; VersionInfo { major: info_raw.major, minor: info_raw.minor, @@ -691,88 +694,88 @@ pub fn version_info() -> VersionInfo { } pub fn serial_number() -> string::BnString { - unsafe { string::BnString::from_raw(binaryninjacore_sys::BNGetSerialNumber()) } + unsafe { string::BnString::from_raw(BNGetSerialNumber()) } } pub fn is_license_validated() -> bool { - unsafe { binaryninjacore_sys::BNIsLicenseValidated() } + unsafe { BNIsLicenseValidated() } } pub fn licensed_user_email() -> string::BnString { - unsafe { string::BnString::from_raw(binaryninjacore_sys::BNGetLicensedUserEmail()) } + unsafe { string::BnString::from_raw(BNGetLicensedUserEmail()) } } pub fn license_count() -> i32 { - unsafe { binaryninjacore_sys::BNGetLicenseCount() } + unsafe { BNGetLicenseCount() } } pub fn set_license(license: S) { let license = license.into_bytes_with_nul(); let license_slice = license.as_ref(); - unsafe { binaryninjacore_sys::BNSetLicense(license_slice.as_ptr() as *const std::os::raw::c_char) } + unsafe { BNSetLicense(license_slice.as_ptr() as *const c_char) } } pub fn product() -> string::BnString { - unsafe { string::BnString::from_raw(binaryninjacore_sys::BNGetProduct()) } + unsafe { string::BnString::from_raw(BNGetProduct()) } } pub fn product_type() -> string::BnString { - unsafe { string::BnString::from_raw(binaryninjacore_sys::BNGetProductType()) } + unsafe { string::BnString::from_raw(BNGetProductType()) } } pub fn license_expiration_time() -> std::time::SystemTime { let m = std::time::Duration::from_secs(unsafe { - binaryninjacore_sys::BNGetLicenseExpirationTime() + BNGetLicenseExpirationTime() }); std::time::UNIX_EPOCH + m } pub fn is_ui_enabled() -> bool { - unsafe { binaryninjacore_sys::BNIsUIEnabled() } + unsafe { BNIsUIEnabled() } } pub fn is_database(filename: S) -> bool { let filename = filename.into_bytes_with_nul(); let filename_slice = filename.as_ref(); - unsafe { binaryninjacore_sys::BNIsDatabase(filename_slice.as_ptr() as *const std::os::raw::c_char) } + unsafe { BNIsDatabase(filename_slice.as_ptr() as *const c_char) } } pub fn plugin_abi_version() -> u32 { - binaryninjacore_sys::BN_CURRENT_CORE_ABI_VERSION + BN_CURRENT_CORE_ABI_VERSION } pub fn plugin_abi_minimum_version() -> u32 { - binaryninjacore_sys::BN_MINIMUM_CORE_ABI_VERSION + BN_MINIMUM_CORE_ABI_VERSION } pub fn core_abi_version() -> u32 { - unsafe { binaryninjacore_sys::BNGetCurrentCoreABIVersion() } + unsafe { BNGetCurrentCoreABIVersion() } } pub fn core_abi_minimum_version() -> u32 { - unsafe { binaryninjacore_sys::BNGetMinimumCoreABIVersion() } + unsafe { BNGetMinimumCoreABIVersion() } } pub fn plugin_ui_abi_version() -> u32 { - binaryninjacore_sys::BN_CURRENT_UI_ABI_VERSION + BN_CURRENT_UI_ABI_VERSION } pub fn plugin_ui_abi_minimum_version() -> u32 { - binaryninjacore_sys::BN_MINIMUM_UI_ABI_VERSION + BN_MINIMUM_UI_ABI_VERSION } pub fn add_required_plugin_dependency(name: S) { unsafe { - binaryninjacore_sys::BNAddRequiredPluginDependency( - name.into_bytes_with_nul().as_ref().as_ptr() as *const std::os::raw::c_char, + BNAddRequiredPluginDependency( + name.into_bytes_with_nul().as_ref().as_ptr() as *const c_char, ) }; } pub fn add_optional_plugin_dependency(name: S) { unsafe { - binaryninjacore_sys::BNAddOptionalPluginDependency( - name.into_bytes_with_nul().as_ref().as_ptr() as *const std::os::raw::c_char, + BNAddOptionalPluginDependency( + name.into_bytes_with_nul().as_ref().as_ptr() as *const c_char, ) }; } diff --git a/rust/src/linearview.rs b/rust/src/linearview.rs index b90d27847..f81685d3c 100644 --- a/rust/src/linearview.rs +++ b/rust/src/linearview.rs @@ -390,7 +390,7 @@ pub struct LinearDisassemblyLine { impl LinearDisassemblyLine { pub(crate) unsafe fn from_raw(raw: &BNLinearDisassemblyLine) -> Self { let linetype = raw.type_; - let function = mem::ManuallyDrop::new(Function::from_raw(raw.function)); + let function = mem::ManuallyDrop::new(Function::ref_from_raw(raw.function)); let contents = mem::ManuallyDrop::new(DisassemblyTextLine(raw.contents)); Self { t: linetype, diff --git a/rust/src/llil/function.rs b/rust/src/llil/function.rs index abc501e59..076787590 100644 --- a/rust/src/llil/function.rs +++ b/rust/src/llil/function.rs @@ -112,10 +112,10 @@ where use binaryninjacore_sys::BNLowLevelILGetInstructionStart; let loc: Location = loc.into(); - let arch_handle = loc.arch.unwrap_or_else(|| *self.arch().as_ref()); + let arch = loc.arch.unwrap_or_else(|| *self.arch().as_ref()); unsafe { - let instr_idx = BNLowLevelILGetInstructionStart(self.handle, arch_handle.0, loc.addr); + let instr_idx = BNLowLevelILGetInstructionStart(self.handle, arch.handle, loc.addr); if instr_idx >= BNGetLowLevelILInstructionCount(self.handle) { None @@ -152,7 +152,7 @@ where pub fn get_function(&self) -> Ref { unsafe { let func = BNGetLowLevelILOwnerFunction(self.handle); - crate::function::Function::from_raw(func) + crate::function::Function::ref_from_raw(func) } } } @@ -189,8 +189,8 @@ impl Function> { let handle = unsafe { match source_func { - Some(func) => BNCreateLowLevelILFunction(arch.0, func.handle), - None => BNCreateLowLevelILFunction(arch.0, null_mut()), + Some(func) => BNCreateLowLevelILFunction(arch.handle, func.handle), + None => BNCreateLowLevelILFunction(arch.handle, null_mut()), } }; if handle.is_null() { diff --git a/rust/src/llil/lifting.rs b/rust/src/llil/lifting.rs index 840e029b2..fa9294114 100644 --- a/rust/src/llil/lifting.rs +++ b/rust/src/llil/lifting.rs @@ -13,7 +13,6 @@ // limitations under the License. use std::marker::PhantomData; -use std::mem; use crate::architecture::Architecture; use crate::architecture::Register as ArchReg; @@ -21,8 +20,6 @@ use crate::architecture::{ Flag, FlagClass, FlagCondition, FlagGroup, FlagRole, FlagWrite, Intrinsic, }; -use super::*; - pub trait Liftable<'func, A: 'func + Architecture> { type Result: ExpressionResultType; @@ -42,7 +39,7 @@ pub trait LiftableWithSize<'func, A: 'func + Architecture>: ) -> Expression<'func, A, Mutable, NonSSA, ValueExpr>; } -use binaryninjacore_sys::BNRegisterOrConstant; +use binaryninjacore_sys::{BNLowLevelILLabel, BNRegisterOrConstant}; #[derive(Copy, Clone)] pub enum RegisterOrConstant { @@ -50,15 +47,15 @@ pub enum RegisterOrConstant { Constant(usize, u64), } -impl RegisterOrConstant { - pub(crate) fn into_api(self) -> BNRegisterOrConstant { - match self { - RegisterOrConstant::Register(_, r) => BNRegisterOrConstant { +impl From> for BNRegisterOrConstant { + fn from(value: RegisterOrConstant) -> Self { + match value { + RegisterOrConstant::Register(_, r) => Self { constant: false, reg: r.id(), value: 0, }, - RegisterOrConstant::Constant(_, value) => BNRegisterOrConstant { + RegisterOrConstant::Constant(_, value) => Self { constant: true, reg: 0, value, @@ -296,7 +293,7 @@ impl FlagWriteOp { pub(crate) fn api_operands(&self) -> (usize, [BNRegisterOrConstant; 5]) { use self::FlagWriteOp::*; - let mut operands: [BNRegisterOrConstant; 5] = unsafe { mem::zeroed() }; + let mut operands: [BNRegisterOrConstant; 5] = [BNRegisterOrConstant::default(); 5]; let count = match *self { Pop(_) => 0, @@ -311,7 +308,7 @@ impl FlagWriteOp { | LowPart(_, op0) | BoolToInt(_, op0) | FloatToInt(_, op0) => { - operands[0] = op0.into_api(); + operands[0] = op0.into(); 1 } @@ -340,8 +337,8 @@ impl FlagWriteOp { | ModsDp(_, op0, op1) | TestBit(_, op0, op1) | AddOverflow(_, op0, op1) => { - operands[0] = op0.into_api(); - operands[1] = op1.into_api(); + operands[0] = op0.into(); + operands[1] = op1.into(); 2 } @@ -349,9 +346,9 @@ impl FlagWriteOp { | Sbb(_, op0, op1, op2) | Rlc(_, op0, op1, op2) | Rrc(_, op0, op1, op2) => { - operands[0] = op0.into_api(); - operands[1] = op1.into_api(); - operands[2] = op2.into_api(); + operands[0] = op0.into(); + operands[1] = op1.into(); + operands[2] = op2.into(); 3 } }; @@ -375,7 +372,7 @@ where let expr_idx = unsafe { use binaryninjacore_sys::BNGetDefaultArchitectureFlagWriteLowLevelIL; BNGetDefaultArchitectureFlagWriteLowLevelIL( - arch.as_ref().0, + arch.as_ref().handle, operation, size, role, @@ -398,13 +395,11 @@ where A: 'func + Architecture, { use binaryninjacore_sys::BNGetDefaultArchitectureFlagConditionLowLevelIL; - - let handle = arch.as_ref(); let class_id = class.map(|c| c.id()).unwrap_or(0); unsafe { let expr_idx = - BNGetDefaultArchitectureFlagConditionLowLevelIL(handle.0, cond, class_id, il.handle); + BNGetDefaultArchitectureFlagConditionLowLevelIL(arch.as_ref().handle, cond, class_id, il.handle); Expression::new(il, expr_idx) } @@ -418,7 +413,7 @@ macro_rules! prim_int_lifter { fn lift(il: &'a Function>, val: Self) -> Expression<'a, A, Mutable, NonSSA, Self::Result> { - il.const_int(mem::size_of::(), val as i64 as u64) + il.const_int(std::mem::size_of::(), val as i64 as u64) } } @@ -586,6 +581,9 @@ where } use binaryninjacore_sys::BNLowLevelILOperation; +use crate::function::Location; +use crate::llil::{Expression, ExpressionResultType, Function, LiftedExpr, LiftedNonSSA, Lifter, Mutable, NonSSA, Register, ValueExpr, VoidExpr}; + pub struct ExpressionBuilder<'func, A, R> where A: 'func + Architecture, @@ -983,8 +981,8 @@ where pub fn if_expr<'a: 'b, 'b, C>( &'a self, cond: C, - t: &'b Label, - f: &'b Label, + true_label: &'b Label, + false_label: &'b Label, ) -> Expression<'a, A, Mutable, NonSSA, VoidExpr> where C: Liftable<'b, A, Result = ValueExpr>, @@ -993,25 +991,29 @@ where let cond = C::lift(self, cond); + let mut raw_true_label = BNLowLevelILLabel::from(true_label); + let mut raw_false_label = BNLowLevelILLabel::from(false_label); let expr_idx = unsafe { BNLowLevelILIf( self.handle, cond.expr_idx as u64, - &t.0 as *const _ as *mut _, - &f.0 as *const _ as *mut _, + &mut raw_true_label, + &mut raw_false_label, ) }; Expression::new(self, expr_idx) } + // TODO: Wtf are these lifetimes?? pub fn goto<'a: 'b, 'b>( &'a self, - l: &'b Label, + label: &'b Label, ) -> Expression<'a, A, Mutable, NonSSA, VoidExpr> { use binaryninjacore_sys::BNLowLevelILGoto; - let expr_idx = unsafe { BNLowLevelILGoto(self.handle, &l.0 as *const _ as *mut _) }; + let mut raw_label = BNLowLevelILLabel::from(label); + let expr_idx = unsafe { BNLowLevelILGoto(self.handle, &mut raw_label) }; Expression::new(self, expr_idx) } @@ -1440,7 +1442,7 @@ where let arch = loc.arch.unwrap_or_else(|| *self.arch().as_ref()); unsafe { - BNLowLevelILSetCurrentAddress(self.handle, arch.0, loc.addr); + BNLowLevelILSetCurrentAddress(self.handle, arch.handle, loc.addr); } } @@ -1450,7 +1452,7 @@ where let loc: Location = loc.into(); let arch = loc.arch.unwrap_or_else(|| *self.arch().as_ref()); - let res = unsafe { BNGetLowLevelILLabelForAddress(self.handle, arch.0, loc.addr) }; + let res = unsafe { BNGetLowLevelILLabelForAddress(self.handle, arch.handle, loc.addr) }; if res.is_null() { None @@ -1462,29 +1464,59 @@ where pub fn mark_label(&self, label: &mut Label) { use binaryninjacore_sys::BNLowLevelILMarkLabel; - unsafe { - BNLowLevelILMarkLabel(self.handle, &mut label.0 as *mut _); - } + let mut raw_label = BNLowLevelILLabel::from(*label); + unsafe { BNLowLevelILMarkLabel(self.handle, &mut raw_label) }; + *label = Label::from(raw_label); } } -use binaryninjacore_sys::BNLowLevelILLabel; +#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] +pub struct Label { + pub resolved: bool, + // TODO: Rename this to something more sensible? + pub expr_ref: usize, + pub operand: usize, +} -#[repr(C)] -pub struct Label(BNLowLevelILLabel); impl Label { pub fn new() -> Self { use binaryninjacore_sys::BNLowLevelILInitLabel; unsafe { // This is one instance where it'd be easy to use mem::MaybeUninit, but *shrug* this is easier - let mut res = Label(mem::zeroed()); - BNLowLevelILInitLabel(&mut res.0 as *mut _); - res + let mut raw_label = BNLowLevelILLabel::default(); + BNLowLevelILInitLabel(&mut raw_label); + raw_label.into() } } } +impl From for Label { + fn from(value: BNLowLevelILLabel) -> Self { + Self { + resolved: value.resolved, + expr_ref: value.ref_, + operand: value.operand, + } + } +} + +impl From>) -> Self { - Self { - convention: conf.contents.handle, - confidence: conf.confidence, - } - } -} - -impl From> for BNOffsetWithConfidence { - fn from(conf: Conf) -> Self { - Self { - value: conf.contents, - confidence: conf.confidence, - } - } -} - -////////////////// -// Type Builder +pub type IntegerDisplayType = BNIntegerDisplayType; #[derive(PartialEq, Eq, Hash)] pub struct TypeBuilder { @@ -395,60 +123,50 @@ impl TypeBuilder { pub fn is_floating_point(&self) -> bool { unsafe { BNIsTypeBuilderFloatingPoint(self.handle) } } - - pub fn target(&self) -> Result>> { + + pub fn child_type(&self) -> Option>> { let raw_target = unsafe { BNGetTypeBuilderChildType(self.handle) }; - if raw_target.type_.is_null() { - Err(()) - } else { - Ok(raw_target.into()) + match raw_target.type_.is_null() { + false => Some(raw_target.into()), + true => None, } } - pub fn element_type(&self) -> Result>> { - let raw_target = unsafe { BNGetTypeBuilderChildType(self.handle) }; - if raw_target.type_.is_null() { - Err(()) - } else { - Ok(raw_target.into()) - } + /// This is an alias for [`Self::child_type`]. + pub fn target(&self) -> Option>> { + self.child_type() } - pub fn return_value(&self) -> Result>> { - let raw_target = unsafe { BNGetTypeBuilderChildType(self.handle) }; - if raw_target.type_.is_null() { - Err(()) - } else { - Ok(raw_target.into()) - } + /// This is an alias for [`Self::child_type`]. + pub fn element_type(&self) -> Option>> { + self.child_type() } - pub fn calling_convention(&self) -> Result>>> { - let convention_confidence = unsafe { BNGetTypeBuilderCallingConvention(self.handle) }; - if convention_confidence.convention.is_null() { - Err(()) - } else { - Ok(convention_confidence.into()) + /// This is an alias for [`Self::child_type`]. + pub fn return_value(&self) -> Option>> { + self.child_type() + } + + pub fn calling_convention(&self) -> Option>>> { + let raw_convention_confidence = unsafe { BNGetTypeBuilderCallingConvention(self.handle) }; + match raw_convention_confidence.convention.is_null() { + false => Some(raw_convention_confidence.into()), + true => None, } } - pub fn parameters(&self) -> Result> { + pub fn parameters(&self) -> Option> { unsafe { let mut count = 0; - let parameters_raw = BNGetTypeBuilderParameters(self.handle, &mut count); - if parameters_raw.is_null() { - Err(()) - } else { - let parameters: &[BNFunctionParameter] = - slice::from_raw_parts(parameters_raw, count); - - let result = (0..count) - .map(|i| FunctionParameter::from_raw(parameters[i])) - .collect(); - - BNFreeTypeParameterList(parameters_raw, count); - - Ok(result) + let raw_parameters_ptr = BNGetTypeBuilderParameters(self.handle, &mut count); + match raw_parameters_ptr.is_null() { + false => { + let raw_parameters = std::slice::from_raw_parts(raw_parameters_ptr, count); + let parameters = raw_parameters.iter().map(Into::into).collect(); + BNFreeTypeParameterList(raw_parameters_ptr, count); + Some(parameters) + } + true => None } } } @@ -465,30 +183,33 @@ impl TypeBuilder { unsafe { BNIsTypeBuilderPure(self.handle).into() } } - pub fn get_structure(&self) -> Result> { - let result = unsafe { BNGetTypeBuilderStructure(self.handle) }; - if result.is_null() { - Err(()) - } else { - Ok(unsafe { Structure::ref_from_raw(result) }) + // TODO: This naming is problematic... rename to `as_structure`? + // TODO: We wouldn't need these sort of functions if we destructured `Type`... + pub fn get_structure(&self) -> Option> { + let raw_struct_ptr = unsafe { BNGetTypeBuilderStructure(self.handle) }; + match raw_struct_ptr.is_null() { + false => Some(unsafe { Structure::ref_from_raw(raw_struct_ptr) }), + true => None, } } - pub fn get_enumeration(&self) -> Result> { - let result = unsafe { BNGetTypeBuilderEnumeration(self.handle) }; - if result.is_null() { - Err(()) - } else { - Ok(unsafe { Enumeration::ref_from_raw(result) }) + // TODO: This naming is problematic... rename to `as_enumeration`? + // TODO: We wouldn't need these sort of functions if we destructured `Type`... + pub fn get_enumeration(&self) -> Option> { + let raw_enum_ptr = unsafe { BNGetTypeBuilderEnumeration(self.handle) }; + match raw_enum_ptr.is_null() { + false => Some(unsafe { Enumeration::ref_from_raw(raw_enum_ptr) }), + true => None, } } - pub fn get_named_type_reference(&self) -> Result> { - let result = unsafe { BNGetTypeBuilderNamedTypeReference(self.handle) }; - if result.is_null() { - Err(()) - } else { - Ok(unsafe { NamedTypeReference::ref_from_raw(result) }) + // TODO: This naming is problematic... rename to `as_named_type_reference`? + // TODO: We wouldn't need these sort of functions if we destructured `Type`... + pub fn get_named_type_reference(&self) -> Option> { + let raw_type_ref_ptr = unsafe { BNGetTypeBuilderNamedTypeReference(self.handle) }; + match raw_type_ref_ptr.is_null() { + false => Some(unsafe { NamedTypeReference::ref_from_raw(raw_type_ref_ptr) }), + true => None, } } @@ -520,7 +241,7 @@ impl TypeBuilder { } pub fn int(width: usize, is_signed: bool) -> Self { - let mut is_signed = Conf::new(is_signed, max_confidence()).into(); + let mut is_signed = Conf::new(is_signed, MAX_CONFIDENCE).into(); unsafe { Self::from_raw(BNCreateIntegerTypeBuilder( @@ -532,7 +253,7 @@ impl TypeBuilder { } pub fn named_int(width: usize, is_signed: bool, alt_name: S) -> Self { - let mut is_signed = Conf::new(is_signed, max_confidence()).into(); + let mut is_signed = Conf::new(is_signed, MAX_CONFIDENCE).into(); // let alt_name = BnString::new(alt_name); let alt_name = alt_name.into_bytes_with_nul(); // This segfaulted once, so the above version is there if we need to change to it, but in theory this is copied into a `const string&` on the C++ side; I'm just not 100% confident that a constant reference copies data @@ -570,20 +291,22 @@ impl TypeBuilder { unsafe { Self::from_raw(BNCreateArrayTypeBuilder(&t.into().into(), count)) } } - /// The C/C++ APIs require an associated architecture, but in the core we only query the default_int_size if the given width is 0 - /// For simplicity's sake, that convention isn't followed and you can query the default_int_size from an arch, if you have it, if you need to + /// ## NOTE + /// + /// The C/C++ APIs require an associated architecture, but in the core we only query the default_int_size if the given width is 0. + /// + /// For simplicity's sake, that convention isn't followed, and you can query [`Architecture::default_integer_size`] if you need to. pub fn enumeration>>( enumeration: &Enumeration, - width: usize, + width: NonZeroUsize, is_signed: T, ) -> Self { unsafe { - // TODO : This is _extremely fragile_, we should change the internals of BNCreateEnumerationTypeBuilder instead of doing this - let mut fake_arch: BNArchitecture = mem::zeroed(); Self::from_raw(BNCreateEnumerationTypeBuilder( - &mut fake_arch, + // TODO: We pass nullptr arch, really we should not even be passing arch. + std::ptr::null_mut(), enumeration.handle, - width, + width.get(), &mut is_signed.into().into(), )) } @@ -594,8 +317,8 @@ impl TypeBuilder { } pub fn named_type(type_reference: NamedTypeReference) -> Self { - let mut is_const = Conf::new(false, min_confidence()).into(); - let mut is_volatile = Conf::new(false, min_confidence()).into(); + let mut is_const = Conf::new(false, MIN_CONFIDENCE).into(); + let mut is_volatile = Conf::new(false, MIN_CONFIDENCE).into(); unsafe { Self::from_raw(BNCreateNamedTypeReferenceBuilder( type_reference.handle, @@ -622,12 +345,12 @@ impl TypeBuilder { // TODO : BNCreateFunctionTypeBuilder pub fn pointer<'a, A: Architecture, T: Into>>(arch: &A, t: T) -> Self { - let mut is_const = Conf::new(false, min_confidence()).into(); - let mut is_volatile = Conf::new(false, min_confidence()).into(); + let mut is_const = Conf::new(false, MIN_CONFIDENCE).into(); + let mut is_volatile = Conf::new(false, MIN_CONFIDENCE).into(); unsafe { Self::from_raw(BNCreatePointerTypeBuilder( - arch.as_ref().0, + arch.as_ref().handle, &t.into().into(), &mut is_const, &mut is_volatile, @@ -637,12 +360,12 @@ impl TypeBuilder { } pub fn const_pointer<'a, A: Architecture, T: Into>>(arch: &A, t: T) -> Self { - let mut is_const = Conf::new(true, max_confidence()).into(); - let mut is_volatile = Conf::new(false, min_confidence()).into(); + let mut is_const = Conf::new(true, MAX_CONFIDENCE).into(); + let mut is_volatile = Conf::new(false, MIN_CONFIDENCE).into(); unsafe { Self::from_raw(BNCreatePointerTypeBuilder( - arch.as_ref().0, + arch.as_ref().handle, &t.into().into(), &mut is_const, &mut is_volatile, @@ -658,8 +381,8 @@ impl TypeBuilder { is_volatile: bool, ref_type: Option, ) -> Self { - let mut is_const = Conf::new(is_const, max_confidence()).into(); - let mut is_volatile = Conf::new(is_volatile, max_confidence()).into(); + let mut is_const = Conf::new(is_const, MAX_CONFIDENCE).into(); + let mut is_volatile = Conf::new(is_volatile, MAX_CONFIDENCE).into(); unsafe { Self::from_raw(BNCreatePointerTypeBuilderOfWidth( @@ -679,11 +402,11 @@ impl TypeBuilder { is_volatile: bool, ref_type: Option, ) -> Self { - let mut is_const = Conf::new(is_const, max_confidence()).into(); - let mut is_volatile = Conf::new(is_volatile, max_confidence()).into(); + let mut is_const = Conf::new(is_const, MAX_CONFIDENCE).into(); + let mut is_volatile = Conf::new(is_volatile, MAX_CONFIDENCE).into(); unsafe { Self::from_raw(BNCreatePointerTypeBuilder( - arch.as_ref().0, + arch.as_ref().handle, &t.into().into(), &mut is_const, &mut is_volatile, @@ -693,10 +416,10 @@ impl TypeBuilder { } } -impl fmt::Display for TypeBuilder { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { +impl Display for TypeBuilder { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { write!(f, "{}", unsafe { - BnString::from_raw(BNGetTypeBuilderString(self.handle, ptr::null_mut())) + BnString::from_raw(BNGetTypeBuilderString(self.handle, std::ptr::null_mut())) }) } } @@ -707,9 +430,6 @@ impl Drop for TypeBuilder { } } -////////// -// Type - #[repr(transparent)] pub struct Type { pub(crate) handle: *mut BNType, @@ -738,13 +458,13 @@ impl Type { pub fn to_builder(&self) -> TypeBuilder { TypeBuilder::new(self) } - - // Readable properties - + pub fn type_class(&self) -> TypeClass { unsafe { BNGetTypeClass(self.handle) } } + // TODO: We need to decide on a public type to represent type width. + // TODO: The api uses both `u64` and `usize`, pick one or a new type! pub fn width(&self) -> u64 { unsafe { BNGetTypeWidth(self.handle) } } @@ -769,60 +489,49 @@ impl Type { unsafe { BNIsTypeFloatingPoint(self.handle) } } - pub fn target(&self) -> Result>> { + pub fn child_type(&self) -> Option>> { let raw_target = unsafe { BNGetChildType(self.handle) }; - if raw_target.type_.is_null() { - Err(()) - } else { - Ok(raw_target.into()) + match raw_target.type_.is_null() { + false => Some(raw_target.into()), + true => None, } } - pub fn element_type(&self) -> Result>> { - let raw_target = unsafe { BNGetChildType(self.handle) }; - if raw_target.type_.is_null() { - Err(()) - } else { - Ok(raw_target.into()) - } + /// This is an alias for [`Self::child_type`]. + pub fn target(&self) -> Option>> { + self.child_type() } - pub fn return_value(&self) -> Result>> { - let raw_target = unsafe { BNGetChildType(self.handle) }; - if raw_target.type_.is_null() { - Err(()) - } else { - Ok(raw_target.into()) - } + /// This is an alias for [`Self::child_type`]. + pub fn element_type(&self) -> Option>> { + self.child_type() } - pub fn calling_convention(&self) -> Result>>> { + /// This is an alias for [`Self::child_type`]. + pub fn return_value(&self) -> Option>> { + self.child_type() + } + + pub fn calling_convention(&self) -> Option>>> { let convention_confidence = unsafe { BNGetTypeCallingConvention(self.handle) }; - if convention_confidence.convention.is_null() { - Err(()) - } else { - Ok(convention_confidence.into()) + match convention_confidence.convention.is_null() { + false => Some(convention_confidence.into()), + true => None, } } - pub fn parameters(&self) -> Result> { + pub fn parameters(&self) -> Option> { unsafe { let mut count = 0; - let parameters_raw: *mut BNFunctionParameter = - BNGetTypeParameters(self.handle, &mut count); - if parameters_raw.is_null() { - Err(()) - } else { - let parameters: &[BNFunctionParameter] = - slice::from_raw_parts(parameters_raw, count); - - let result = (0..count) - .map(|i| FunctionParameter::from_raw(parameters[i])) - .collect(); - - BNFreeTypeParameterList(parameters_raw, count); - - Ok(result) + let parameters_raw_ptr = BNGetTypeParameters(self.handle, &mut count); + match parameters_raw_ptr.is_null() { + false => { + let raw_parameters = std::slice::from_raw_parts(parameters_raw_ptr, count); + let parameters = raw_parameters.iter().map(Into::into).collect(); + BNFreeTypeParameterList(parameters_raw_ptr, count); + Some(parameters) + } + true => None } } } @@ -839,30 +548,33 @@ impl Type { unsafe { BNIsTypePure(self.handle).into() } } - pub fn get_structure(&self) -> Result> { - let result = unsafe { BNGetTypeStructure(self.handle) }; - if result.is_null() { - Err(()) - } else { - Ok(unsafe { Structure::ref_from_raw(result) }) + // TODO: This naming is problematic... rename to `as_structure`? + // TODO: We wouldn't need these sort of functions if we destructured `Type`... + pub fn get_structure(&self) -> Option> { + let raw_struct_ptr = unsafe { BNGetTypeStructure(self.handle) }; + match raw_struct_ptr.is_null() { + false => Some(unsafe { Structure::ref_from_raw(raw_struct_ptr) }), + true => None, } } - pub fn get_enumeration(&self) -> Result> { - let result = unsafe { BNGetTypeEnumeration(self.handle) }; - if result.is_null() { - Err(()) - } else { - Ok(unsafe { Enumeration::ref_from_raw(result) }) + // TODO: This naming is problematic... rename to `as_enumeration`? + // TODO: We wouldn't need these sort of functions if we destructured `Type`... + pub fn get_enumeration(&self) -> Option> { + let raw_enum_ptr = unsafe { BNGetTypeEnumeration(self.handle) }; + match raw_enum_ptr.is_null() { + false => Some(unsafe { Enumeration::ref_from_raw(raw_enum_ptr) }), + true => None, } } - pub fn get_named_type_reference(&self) -> Result> { - let result = unsafe { BNGetTypeNamedTypeReference(self.handle) }; - if result.is_null() { - Err(()) - } else { - Ok(unsafe { NamedTypeReference::ref_from_raw(result) }) + // TODO: This naming is problematic... rename to `as_named_type_reference`? + // TODO: We wouldn't need these sort of functions if we destructured `Type`... + pub fn get_named_type_reference(&self) -> Option> { + let raw_type_ref_ptr = unsafe { BNGetTypeNamedTypeReference(self.handle) }; + match raw_type_ref_ptr.is_null() { + false => Some(unsafe { NamedTypeReference::ref_from_raw(raw_type_ref_ptr) }), + true => None, } } @@ -878,12 +590,11 @@ impl Type { unsafe { BNGetTypeStackAdjustment(self.handle).into() } } - pub fn registered_name(&self) -> Result> { - let result = unsafe { BNGetRegisteredTypeName(self.handle) }; - if result.is_null() { - Err(()) - } else { - Ok(unsafe { NamedTypeReference::ref_from_raw(result) }) + pub fn registered_name(&self) -> Option> { + let raw_type_ref_ptr = unsafe { BNGetRegisteredTypeName(self.handle) }; + match raw_type_ref_ptr.is_null() { + false => Some(unsafe { NamedTypeReference::ref_from_raw(raw_type_ref_ptr) }), + true => None, } } @@ -912,7 +623,7 @@ impl Type { } pub fn int(width: usize, is_signed: bool) -> Ref { - let mut is_signed = Conf::new(is_signed, max_confidence()).into(); + let mut is_signed = Conf::new(is_signed, MAX_CONFIDENCE).into(); unsafe { Self::ref_from_raw(BNCreateIntegerType( width, @@ -923,7 +634,7 @@ impl Type { } pub fn named_int(width: usize, is_signed: bool, alt_name: S) -> Ref { - let mut is_signed = Conf::new(is_signed, max_confidence()).into(); + let mut is_signed = Conf::new(is_signed, MAX_CONFIDENCE).into(); // let alt_name = BnString::new(alt_name); let alt_name = alt_name.into_bytes_with_nul(); // This segfaulted once, so the above version is there if we need to change to it, but in theory this is copied into a `const string&` on the C++ side; I'm just not 100% confident that a constant reference copies data @@ -956,21 +667,22 @@ impl Type { unsafe { Self::ref_from_raw(BNCreateArrayType(&t.into().into(), count)) } } - /// The C/C++ APIs require an associated architecture, but in the core we only query the default_int_size if the given width is 0 + /// ## NOTE + /// + /// The C/C++ APIs require an associated architecture, but in the core we only query the default_int_size if the given width is 0. /// - /// For simplicity's sake, that convention isn't followed and you can query the default_int_size from an arch, if you have it, if you need to + /// For simplicity's sake, that convention isn't followed, and you can query [`Architecture::default_integer_size`] if you need to. pub fn enumeration>>( enumeration: &Enumeration, - width: usize, + width: NonZeroUsize, is_signed: T, ) -> Ref { unsafe { - // TODO : This is _extremely fragile_, we should change the internals of BNCreateEnumerationType instead of doing this - let mut fake_arch: BNArchitecture = mem::zeroed(); Self::ref_from_raw(BNCreateEnumerationType( - &mut fake_arch, + // TODO: We pass nullptr arch, really we should not even be passing arch. + std::ptr::null_mut(), enumeration.handle, - width, + width.get(), &mut is_signed.into().into(), )) } @@ -981,8 +693,8 @@ impl Type { } pub fn named_type(type_reference: &NamedTypeReference) -> Ref { - let mut is_const = Conf::new(false, min_confidence()).into(); - let mut is_volatile = Conf::new(false, min_confidence()).into(); + let mut is_const = Conf::new(false, MIN_CONFIDENCE).into(); + let mut is_volatile = Conf::new(false, MIN_CONFIDENCE).into(); unsafe { Self::ref_from_raw(BNCreateNamedTypeReference( type_reference.handle, @@ -1006,46 +718,30 @@ impl Type { } } + // TODO: FunctionBuilder pub fn function<'a, T: Into>>( return_type: T, parameters: &[FunctionParameter], variable_arguments: bool, ) -> Ref { let mut return_type = return_type.into().into(); - let mut variable_arguments = Conf::new(variable_arguments, max_confidence()).into(); - let mut can_return = Conf::new(true, min_confidence()).into(); - let mut pure = Conf::new(false, min_confidence()).into(); + let mut variable_arguments = Conf::new(variable_arguments, MAX_CONFIDENCE).into(); + let mut can_return = Conf::new(true, MIN_CONFIDENCE).into(); + let mut pure = Conf::new(false, MIN_CONFIDENCE).into(); let mut raw_calling_convention: BNCallingConventionWithConfidence = BNCallingConventionWithConfidence { - convention: ptr::null_mut(), - confidence: min_confidence(), - }; - - let mut stack_adjust = Conf::::new(0, min_confidence()).into(); - let mut raw_parameters = Vec::::with_capacity(parameters.len()); - let mut parameter_name_references = Vec::with_capacity(parameters.len()); - for parameter in parameters { - let raw_name = parameter.name.as_str().into_bytes_with_nul(); - let location = match ¶meter.location { - Some(location) => location.raw(), - None => unsafe { mem::zeroed() }, + convention: std::ptr::null_mut(), + confidence: MIN_CONFIDENCE, }; - raw_parameters.push(BNFunctionParameter { - name: raw_name.as_slice().as_ptr() as *mut _, - type_: parameter.t.contents.handle, - typeConfidence: parameter.t.confidence, - defaultLocation: parameter.location.is_none(), - location, - }); - parameter_name_references.push(raw_name); - } - let reg_stack_adjust_regs = ptr::null_mut(); - let reg_stack_adjust_values = ptr::null_mut(); + let mut stack_adjust = Conf::new(0, MIN_CONFIDENCE).into(); + let mut raw_parameters = parameters.iter().cloned().map(Into::into).collect::>(); + let reg_stack_adjust_regs = std::ptr::null_mut(); + let reg_stack_adjust_values = std::ptr::null_mut(); let mut return_regs: BNRegisterSetWithConfidence = BNRegisterSetWithConfidence { - regs: ptr::null_mut(), + regs: std::ptr::null_mut(), count: 0, confidence: 0, }; @@ -1069,7 +765,8 @@ impl Type { } } - pub fn function_with_options< + // TODO: FunctionBuilder + pub fn function_with_opts< 'a, A: Architecture, T: Into>, @@ -1082,43 +779,21 @@ impl Type { stack_adjust: Conf, ) -> Ref { let mut return_type = return_type.into().into(); - let mut variable_arguments = Conf::new(variable_arguments, max_confidence()).into(); - let mut can_return = Conf::new(true, min_confidence()).into(); - let mut pure = Conf::new(false, min_confidence()).into(); - let mut raw_calling_convention: BNCallingConventionWithConfidence = - calling_convention.into().into(); + let mut variable_arguments = Conf::new(variable_arguments, MAX_CONFIDENCE).into(); + let mut can_return = Conf::new(true, MIN_CONFIDENCE).into(); + let mut pure = Conf::new(false, MIN_CONFIDENCE).into(); + + let mut raw_calling_convention: BNCallingConventionWithConfidence = calling_convention.into().into(); + let mut stack_adjust = stack_adjust.into(); - - let mut raw_parameters = Vec::::with_capacity(parameters.len()); - let mut parameter_name_references = Vec::with_capacity(parameters.len()); - let mut name_ptrs = vec![]; - for parameter in parameters { - name_ptrs.push(parameter.name.clone()); - } - - for (name, parameter) in zip(name_ptrs, parameters) { - let raw_name = name.as_str().into_bytes_with_nul(); - let location = match ¶meter.location { - Some(location) => location.raw(), - None => unsafe { mem::zeroed() }, - }; - - raw_parameters.push(BNFunctionParameter { - name: raw_name.as_slice().as_ptr() as *mut _, - type_: parameter.t.contents.handle, - typeConfidence: parameter.t.confidence, - defaultLocation: parameter.location.is_none(), - location, - }); - parameter_name_references.push(raw_name); - } + let mut raw_parameters = parameters.iter().cloned().map(Into::into).collect::>(); // TODO: Update type signature and include these (will be a breaking change) - let reg_stack_adjust_regs = ptr::null_mut(); - let reg_stack_adjust_values = ptr::null_mut(); + let reg_stack_adjust_regs = std::ptr::null_mut(); + let reg_stack_adjust_values = std::ptr::null_mut(); let mut return_regs: BNRegisterSetWithConfidence = BNRegisterSetWithConfidence { - regs: ptr::null_mut(), + regs: std::ptr::null_mut(), count: 0, confidence: 0, }; @@ -1143,11 +818,11 @@ impl Type { } pub fn pointer<'a, A: Architecture, T: Into>>(arch: &A, t: T) -> Ref { - let mut is_const = Conf::new(false, min_confidence()).into(); - let mut is_volatile = Conf::new(false, min_confidence()).into(); + let mut is_const = Conf::new(false, MIN_CONFIDENCE).into(); + let mut is_volatile = Conf::new(false, MIN_CONFIDENCE).into(); unsafe { Self::ref_from_raw(BNCreatePointerType( - arch.as_ref().0, + arch.as_ref().handle, &t.into().into(), &mut is_const, &mut is_volatile, @@ -1160,11 +835,11 @@ impl Type { arch: &A, t: T, ) -> Ref { - let mut is_const = Conf::new(true, max_confidence()).into(); - let mut is_volatile = Conf::new(false, min_confidence()).into(); + let mut is_const = Conf::new(true, MAX_CONFIDENCE).into(); + let mut is_volatile = Conf::new(false, MIN_CONFIDENCE).into(); unsafe { Self::ref_from_raw(BNCreatePointerType( - arch.as_ref().0, + arch.as_ref().handle, &t.into().into(), &mut is_const, &mut is_volatile, @@ -1180,8 +855,8 @@ impl Type { is_volatile: bool, ref_type: Option, ) -> Ref { - let mut is_const = Conf::new(is_const, max_confidence()).into(); - let mut is_volatile = Conf::new(is_volatile, max_confidence()).into(); + let mut is_const = Conf::new(is_const, MAX_CONFIDENCE).into(); + let mut is_volatile = Conf::new(is_volatile, MAX_CONFIDENCE).into(); unsafe { Self::ref_from_raw(BNCreatePointerTypeOfWidth( size, @@ -1200,11 +875,11 @@ impl Type { is_volatile: bool, ref_type: Option, ) -> Ref { - let mut is_const = Conf::new(is_const, max_confidence()).into(); - let mut is_volatile = Conf::new(is_volatile, max_confidence()).into(); + let mut is_const = Conf::new(is_const, MAX_CONFIDENCE).into(); + let mut is_volatile = Conf::new(is_volatile, MAX_CONFIDENCE).into(); unsafe { Self::ref_from_raw(BNCreatePointerType( - arch.as_ref().0, + arch.as_ref().handle, &t.into().into(), &mut is_const, &mut is_volatile, @@ -1219,12 +894,12 @@ impl Type { } } -impl fmt::Display for Type { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { +impl Display for Type { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { write!(f, "{}", unsafe { BnString::from_raw(BNGetTypeString( self.handle, - ptr::null_mut(), + std::ptr::null_mut(), BNTokenEscapingType::NoTokenEscapingType, )) }) @@ -1236,8 +911,8 @@ lazy_static! { Mutex::new(BinaryView::from_data(&FileMetadata::new(), &[]).ok()); } -impl fmt::Debug for Type { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { +impl Debug for Type { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { if let Ok(lock) = TYPE_DEBUG_BV.lock() { if let Some(bv) = &*lock { let container = unsafe { BNGetAnalysisTypeContainer(bv.handle) }; @@ -1248,12 +923,12 @@ impl fmt::Debug for Type { unsafe { BNGetTypePrinterByName(c"CoreTypePrinter".as_ptr()) } }; if printer.is_null() { - return Err(fmt::Error); + return Err(std::fmt::Error); } let mut name = QualifiedName::from(""); - let mut lines: *mut BNTypeDefinitionLine = null_mut(); + let mut lines: *mut BNTypeDefinitionLine = std::ptr::null_mut(); let mut count: usize = 0; unsafe { @@ -1274,11 +949,11 @@ impl fmt::Debug for Type { } if lines.is_null() { - return Err(fmt::Error); + return Err(std::fmt::Error); } let line_slice: &[BNTypeDefinitionLine] = - unsafe { slice::from_raw_parts(lines, count) }; + unsafe { std::slice::from_raw_parts(lines, count) }; for (i, line) in line_slice.iter().enumerate() { if i > 0 { @@ -1286,7 +961,7 @@ impl fmt::Debug for Type { } let tokens: &[BNInstructionTextToken] = - unsafe { slice::from_raw_parts(line.tokens, line.count) }; + unsafe { std::slice::from_raw_parts(line.tokens, line.count) }; for token in tokens { let text: *const c_char = token.text; @@ -1301,7 +976,7 @@ impl fmt::Debug for Type { return Ok(()); } } - Err(fmt::Error) + Err(std::fmt::Error) } } @@ -1340,124 +1015,93 @@ impl ToOwned for Type { } } -pub struct ComponentReferencedTypes; -impl CoreArrayProvider for ComponentReferencedTypes { +// TODO: Remove this struct, or make it not a ZST with a terrible array provider. +/// ZST used only for `Array`. +pub struct ComponentReferencedType; + +impl CoreArrayProvider for ComponentReferencedType { type Raw = *mut BNType; type Context = (); type Wrapped<'a> = &'a Type; } -unsafe impl CoreArrayProviderInner for ComponentReferencedTypes { +unsafe impl CoreArrayProviderInner for ComponentReferencedType { unsafe fn free(raw: *mut Self::Raw, count: usize, _context: &Self::Context) { BNComponentFreeReferencedTypes(raw, count) } unsafe fn wrap_raw<'a>(raw: &'a Self::Raw, _context: &'a Self::Context) -> Self::Wrapped<'a> { - // SAFETY: BNType and Type are trasparent - core::mem::transmute(raw) + // SAFETY: &*mut BNType == &Type (*mut BNType == Type) + std::mem::transmute(raw) } } -/////////////////////// -// FunctionParameter - -#[derive(Clone, Debug)] +#[derive(Debug, Clone, Hash, PartialEq, Eq)] pub struct FunctionParameter { - pub t: Conf>, + pub ty: Conf>, pub name: String, pub location: Option, } impl FunctionParameter { - pub fn new>>>(t: T, name: String, location: Option) -> Self { + pub fn new>>>(ty: T, name: String, location: Option) -> Self { Self { - t: t.into(), + ty: ty.into(), name, location, } } +} - pub(crate) fn from_raw(member: BNFunctionParameter) -> Self { - let name = if member.name.is_null() { - if member.location.type_ == VariableSourceType::RegisterVariableSourceType { - format!("reg_{}", member.location.storage) - } else if member.location.type_ == VariableSourceType::StackVariableSourceType { - format!("arg_{}", member.location.storage) +impl From for FunctionParameter { + fn from(value: BNFunctionParameter) -> Self { + // TODO: I copied this from the original `from_raw` function. + // TODO: So this actually needs to be audited later. + let name = if value.name.is_null() { + if value.location.type_ == VariableSourceType::RegisterVariableSourceType { + format!("reg_{}", value.location.storage) + } else if value.location.type_ == VariableSourceType::StackVariableSourceType { + format!("arg_{}", value.location.storage) } else { String::new() } } else { - unsafe { CStr::from_ptr(member.name) } - .to_str() - .unwrap() - .to_owned() + unsafe { BnString::from_raw(value.name) }.to_string() }; - + Self { - t: Conf::new( - unsafe { Type::ref_from_raw(BNNewTypeReference(member.type_)) }, - member.typeConfidence, + ty: Conf::new( + unsafe { Type::ref_from_raw(value.type_) }, + value.typeConfidence, ), name, - location: if member.defaultLocation { - None - } else { - Some(unsafe { Variable::from_raw(member.location) }) + location: match value.defaultLocation { + false => Some(Variable::from(value.location)), + true => None, }, } } } -////////////// -// Variable - -#[derive(Clone, Copy, Debug, Hash, Eq, PartialEq)] -pub struct Variable { - pub t: VariableSourceType, - pub index: u32, - pub storage: i64, -} - -impl Variable { - pub fn new(t: VariableSourceType, index: u32, storage: i64) -> Self { - Self { t, index, storage } +impl From<&BNFunctionParameter> for FunctionParameter { + fn from(value: &BNFunctionParameter) -> Self { + Self::from(*value) } +} - pub(crate) unsafe fn from_raw(var: BNVariable) -> Self { +impl From for BNFunctionParameter { + fn from(value: FunctionParameter) -> Self { + let bn_name = BnString::new(value.name); Self { - t: var.type_, - index: var.index, - storage: var.storage, - } - } - pub(crate) unsafe fn from_identifier(var: u64) -> Self { - Self::from_raw(unsafe { BNFromVariableIdentifier(var) }) - } - - pub(crate) fn raw(&self) -> BNVariable { - BNVariable { - type_: self.t, - index: self.index, - storage: self.storage, + name: bn_name.into_raw(), + type_: value.ty.contents.handle, + typeConfidence: value.ty.confidence, + defaultLocation: value.location.is_none(), + location: value.location.map(Into::into).unwrap_or_default(), } } } -impl CoreArrayProvider for Variable { - type Raw = BNVariable; - type Context = (); - type Wrapped<'a> = Self; -} - -unsafe impl CoreArrayProviderInner for Variable { - unsafe fn free(raw: *mut Self::Raw, _count: usize, _context: &Self::Context) { - BNFreeVariableList(raw) - } - unsafe fn wrap_raw<'a>(raw: &'a Self::Raw, _context: &'a Self::Context) -> Self::Wrapped<'a> { - Variable::from_raw(*raw) - } -} - // Name, Variable and Type impl CoreArrayProvider for (&str, Variable, &Type) { type Raw = BNVariableNameAndType; @@ -1469,203 +1113,100 @@ unsafe impl CoreArrayProviderInner for (&str, Variable, &Type) { unsafe fn free(raw: *mut Self::Raw, count: usize, _context: &Self::Context) { BNFreeVariableNameAndTypeList(raw, count) } + unsafe fn wrap_raw<'a>( raw: &'a Self::Raw, _context: &'a Self::Context, ) -> (&'a str, Variable, &'a Type) { let name = CStr::from_ptr(raw.name).to_str().unwrap(); - let var = Variable::from_raw(raw.var); - let var_type = core::mem::transmute(&raw.type_); + let var = Variable::from(raw.var); + let var_type = std::mem::transmute(&raw.type_); (name, var, var_type) } } -////////////// -// SSAVariable - -#[derive(Clone, Copy, Debug, Hash, Eq, PartialEq)] -pub struct SSAVariable { - pub variable: Variable, - pub version: usize, +#[derive(Debug, Clone, Hash, PartialEq, Eq)] +pub struct EnumerationMember { + pub name: String, + /// The associated constant value for the member. + pub value: u64, + /// Whether this is the default member for the associated [`Enumeration`]. + pub default: bool, } -impl SSAVariable { - pub fn new(variable: Variable, version: usize) -> Self { - Self { variable, version } +impl EnumerationMember { + pub fn new(name: String, value: u64, default: bool) -> Self { + Self { + name, + value, + default, + } } } -impl CoreArrayProvider for SSAVariable { - type Raw = usize; - type Context = Variable; - type Wrapped<'a> = Self; -} - -unsafe impl CoreArrayProviderInner for SSAVariable { - unsafe fn free(raw: *mut Self::Raw, _count: usize, _context: &Self::Context) { - BNFreeILInstructionList(raw) - } - - unsafe fn wrap_raw<'a>(raw: &'a Self::Raw, context: &'a Self::Context) -> Self::Wrapped<'a> { - SSAVariable::new(*context, *raw) +impl From for EnumerationMember { + fn from(value: BNEnumerationMember) -> Self { + Self { + name: unsafe { BnString::from_raw(value.name).to_string() }, + value: value.value, + default: value.isDefault, + } } } -impl CoreArrayProvider for Array { - type Raw = BNVariable; - type Context = Ref; - type Wrapped<'a> = Self; -} - -unsafe impl CoreArrayProviderInner for Array { - unsafe fn free(raw: *mut Self::Raw, _count: usize, _context: &Self::Context) { - BNFreeVariableList(raw) - } - - unsafe fn wrap_raw<'a>(raw: &'a Self::Raw, context: &'a Self::Context) -> Self::Wrapped<'a> { - let mut count = 0; - let versions = - unsafe { BNGetMediumLevelILVariableSSAVersions(context.handle, raw, &mut count) }; - Array::new(versions, count, Variable::from_raw(*raw)) +impl From for BNEnumerationMember { + fn from(value: EnumerationMember) -> Self { + let bn_name = BnString::new(value.name); + Self { + name: bn_name.into_raw(), + value: value.value, + isDefault: value.default, + } } } -/////////////// -// NamedVariable - -#[derive(Clone, Debug, Hash, Eq, PartialEq)] -pub struct NamedTypedVariable { - pub name: String, - pub ty: Conf>, - pub var: Variable, - pub auto_defined: bool, +#[derive(PartialEq, Eq, Hash)] +pub struct EnumerationBuilder { + pub(crate) handle: *mut BNEnumerationBuilder, } -impl NamedTypedVariable { - pub fn new(var: Variable, name: String, ty: Conf>, auto_defined: bool) -> Self { +impl EnumerationBuilder { + pub fn new() -> Self { Self { - name, - ty, - var, - auto_defined, + handle: unsafe { BNCreateEnumerationBuilder() }, } } - pub(crate) unsafe fn from_raw(var: &BNVariableNameAndType) -> Self { - Self { - var: Variable::from_raw(var.var), - auto_defined: var.autoDefined, - name: CStr::from_ptr(var.name).to_str().unwrap().to_string(), - ty: Conf::new(Type::ref_from_raw(var.type_), var.typeConfidence), - } + pub(crate) unsafe fn from_raw(handle: *mut BNEnumerationBuilder) -> Self { + Self { handle } } - pub fn name(&self) -> &str { - &self.name + pub fn finalize(&self) -> Ref { + unsafe { Enumeration::ref_from_raw(BNFinalizeEnumerationBuilder(self.handle)) } } - pub fn var(&self) -> Variable { - self.var + pub fn append(&self, name: S) -> &Self { + let name = name.into_bytes_with_nul(); + unsafe { + BNAddEnumerationBuilderMember(self.handle, name.as_ref().as_ptr() as _); + } + self } - pub fn auto_defined(&self) -> bool { - self.auto_defined + pub fn insert(&self, name: S, value: u64) -> &Self { + let name = name.into_bytes_with_nul(); + unsafe { + BNAddEnumerationBuilderMemberWithValue(self.handle, name.as_ref().as_ptr() as _, value); + } + self } - pub fn type_confidence(&self) -> u8 { - self.ty.confidence - } - - pub fn var_type(&self) -> Ref { - self.ty.contents.clone() - } -} - -impl CoreArrayProvider for NamedTypedVariable { - type Raw = BNVariableNameAndType; - type Context = (); - type Wrapped<'a> = Guard<'a, NamedTypedVariable>; -} - -unsafe impl CoreArrayProviderInner for NamedTypedVariable { - unsafe fn free(raw: *mut Self::Raw, count: usize, _context: &Self::Context) { - BNFreeVariableNameAndTypeList(raw, count) - } - unsafe fn wrap_raw<'a>(raw: &'a Self::Raw, _context: &'a Self::Context) -> Self::Wrapped<'a> { - unsafe { Guard::new(NamedTypedVariable::from_raw(raw), raw) } - } -} - -//////////////////////// -// EnumerationBuilder - -#[derive(Debug, Clone)] -pub struct EnumerationMember { - pub name: String, - pub value: u64, - pub is_default: bool, -} - -impl EnumerationMember { - pub fn new(name: String, value: u64, is_default: bool) -> Self { - Self { - name, - value, - is_default, - } - } - - pub(crate) unsafe fn from_raw(member: BNEnumerationMember) -> Self { - Self { - name: raw_to_string(member.name).unwrap(), - value: member.value, - is_default: member.isDefault, - } - } -} - -#[derive(PartialEq, Eq, Hash)] -pub struct EnumerationBuilder { - pub(crate) handle: *mut BNEnumerationBuilder, -} - -impl EnumerationBuilder { - pub fn new() -> Self { - Self { - handle: unsafe { BNCreateEnumerationBuilder() }, - } - } - - pub(crate) unsafe fn from_raw(handle: *mut BNEnumerationBuilder) -> Self { - Self { handle } - } - - pub fn finalize(&self) -> Ref { - unsafe { Enumeration::ref_from_raw(BNFinalizeEnumerationBuilder(self.handle)) } - } - - pub fn append(&self, name: S) -> &Self { - let name = name.into_bytes_with_nul(); - unsafe { - BNAddEnumerationBuilderMember(self.handle, name.as_ref().as_ptr() as _); - } - self - } - - pub fn insert(&self, name: S, value: u64) -> &Self { - let name = name.into_bytes_with_nul(); - unsafe { - BNAddEnumerationBuilderMemberWithValue(self.handle, name.as_ref().as_ptr() as _, value); - } - self - } - - pub fn replace(&self, id: usize, name: S, value: u64) -> &Self { - let name = name.into_bytes_with_nul(); - unsafe { - BNReplaceEnumerationBuilderMember(self.handle, id, name.as_ref().as_ptr() as _, value); - } - self + pub fn replace(&self, id: usize, name: S, value: u64) -> &Self { + let name = name.into_bytes_with_nul(); + unsafe { + BNReplaceEnumerationBuilderMember(self.handle, id, name.as_ref().as_ptr() as _, value); + } + self } pub fn remove(&self, id: usize) -> &Self { @@ -1679,16 +1220,11 @@ impl EnumerationBuilder { pub fn members(&self) -> Vec { unsafe { let mut count = 0; - let members_raw = BNGetEnumerationBuilderMembers(self.handle, &mut count); - let members: &[BNEnumerationMember] = slice::from_raw_parts(members_raw, count); - - let result = (0..count) - .map(|i| EnumerationMember::from_raw(members[i])) - .collect(); - - BNFreeEnumerationMemberList(members_raw, count); - - result + let members_raw_ptr = BNGetEnumerationBuilderMembers(self.handle, &mut count); + let members_raw: &[BNEnumerationMember] = std::slice::from_raw_parts(members_raw_ptr, count); + let members = members_raw.iter().copied().map(Into::into).collect(); + BNFreeEnumerationMemberList(members_raw_ptr, count); + members } } } @@ -1715,9 +1251,6 @@ impl Drop for EnumerationBuilder { } } -///////////////// -// Enumeration - #[derive(PartialEq, Eq, Hash)] pub struct Enumeration { pub(crate) handle: *mut BNEnumeration, @@ -1736,16 +1269,11 @@ impl Enumeration { pub fn members(&self) -> Vec { unsafe { let mut count = 0; - let members_raw = BNGetEnumerationMembers(self.handle, &mut count); - let members: &[BNEnumerationMember] = slice::from_raw_parts(members_raw, count); - - let result = (0..count) - .map(|i| EnumerationMember::from_raw(members[i])) - .collect(); - - BNFreeEnumerationMemberList(members_raw, count); - - result + let members_raw_ptr = BNGetEnumerationMembers(self.handle, &mut count); + let members_raw: &[BNEnumerationMember] = std::slice::from_raw_parts(members_raw_ptr, count); + let members = members_raw.iter().copied().map(Into::into).collect(); + BNFreeEnumerationMemberList(members_raw_ptr, count); + members } } } @@ -1768,9 +1296,6 @@ impl ToOwned for Enumeration { } } -////////////////////// -// StructureBuilder - pub type StructureType = BNStructureVariant; #[derive(PartialEq, Eq, Hash)] @@ -1976,24 +1501,17 @@ impl StructureBuilder { unsafe { BNStructureBuilderPropagatesDataVariableReferences(self.handle) } } - pub fn base_structures(&self) -> Result> { + pub fn base_structures(&self) -> Option> { let mut count = 0usize; - let bases = unsafe { BNGetBaseStructuresForStructureBuilder(self.handle, &mut count) }; - if bases.is_null() { - Err(()) - } else { - let bases_slice = unsafe { slice::from_raw_parts_mut(bases, count) }; - - let result = bases_slice - .iter() - .map(|base| unsafe { BaseStructure::from_raw(*base) }) - .collect::>(); - - unsafe { - BNFreeBaseStructureList(bases, count); - } - - Ok(result) + let bases_raw_ptr = unsafe { BNGetBaseStructuresForStructureBuilder(self.handle, &mut count) }; + match bases_raw_ptr.is_null() { + false => { + let bases_raw = unsafe { std::slice::from_raw_parts(bases_raw_ptr, count) }; + let bases = bases_raw.iter().copied().map(Into::into).collect(); + unsafe { BNFreeBaseStructureList(bases_raw_ptr, count) }; + Some(bases) + }, + true => None, } } @@ -2068,7 +1586,7 @@ impl From> for StructureBuilder { } impl Debug for StructureBuilder { - fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { write!(f, "StructureBuilder {{ ... }}") } } @@ -2085,9 +1603,6 @@ impl Default for StructureBuilder { } } -/////////////// -// Structure - #[derive(PartialEq, Eq, Hash)] pub struct Structure { pub(crate) handle: *mut BNStructure, @@ -2116,44 +1631,36 @@ impl Structure { unsafe { BNGetStructureType(self.handle) } } - pub fn members(&self) -> Result> { + // TODO: Omit `Option` and pass empty vec? + pub fn members(&self) -> Option> { unsafe { let mut count = 0; - let members_raw: *mut BNStructureMember = + let members_raw_ptr: *mut BNStructureMember = BNGetStructureMembers(self.handle, &mut count); - if members_raw.is_null() { - return Err(()); + match members_raw_ptr.is_null() { + false => { + let members_raw = std::slice::from_raw_parts(members_raw_ptr, count); + let members = members_raw.iter().copied().map(Into::into).collect(); + BNFreeStructureMemberList(members_raw_ptr, count); + Some(members) + }, + true => None, } - let members = slice::from_raw_parts(members_raw, count); - - let result = (0..count) - .map(|i| StructureMember::from_raw(members[i])) - .collect(); - - BNFreeStructureMemberList(members_raw, count); - - Ok(result) } } - pub fn base_structures(&self) -> Result> { - let mut count = 0usize; - let bases = unsafe { BNGetBaseStructuresForStructure(self.handle, &mut count) }; - if bases.is_null() { - Err(()) - } else { - let bases_slice = unsafe { slice::from_raw_parts_mut(bases, count) }; - - let result = bases_slice - .iter() - .map(|base| unsafe { BaseStructure::from_raw(*base) }) - .collect::>(); - - unsafe { - BNFreeBaseStructureList(bases, count); - } - - Ok(result) + // TODO: Omit `Option` and pass empty vec? + pub fn base_structures(&self) -> Option> { + let mut count = 0; + let bases_raw_ptr = unsafe { BNGetBaseStructuresForStructure(self.handle, &mut count) }; + match bases_raw_ptr.is_null() { + false => { + let bases_raw = unsafe { std::slice::from_raw_parts(bases_raw_ptr, count) }; + let bases = bases_raw.iter().copied().map(Into::into).collect(); + unsafe { BNFreeBaseStructureList(bases_raw_ptr, count) }; + Some(bases) + }, + true => None, } } @@ -2161,9 +1668,9 @@ impl Structure { } impl Debug for Structure { - fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { write!(f, "Structure {{")?; - if let Ok(members) = self.members() { + if let Some(members) = self.members() { for member in members { write!(f, " {:?}", member)?; } @@ -2190,7 +1697,7 @@ impl ToOwned for Structure { } } -#[derive(Debug, Clone)] +#[derive(Debug, Clone, Hash, PartialEq, Eq)] pub struct StructureMember { pub ty: Conf>, pub name: String, @@ -2215,17 +1722,33 @@ impl StructureMember { scope, } } +} - pub(crate) unsafe fn from_raw(handle: BNStructureMember) -> Self { +impl From for StructureMember { + fn from(value: BNStructureMember) -> Self { Self { ty: Conf::new( - RefCountable::inc_ref(&Type::from_raw(handle.type_)), - handle.typeConfidence, + unsafe { Type::ref_from_raw(value.type_) }, + value.typeConfidence, ), - name: CStr::from_ptr(handle.name).to_string_lossy().to_string(), - offset: handle.offset, - access: handle.access, - scope: handle.scope, + name: unsafe { BnString::from_raw(value.name) }.to_string(), + offset: value.offset, + access: value.access, + scope: value.scope, + } + } +} + +impl From for BNStructureMember { + fn from(value: StructureMember) -> Self { + let bn_name = BnString::new(value.name); + Self { + type_: value.ty.contents.handle, + name: bn_name.into_raw(), + offset: value.offset, + typeConfidence: value.ty.confidence, + access: value.access, + scope: value.scope, } } } @@ -2240,12 +1763,13 @@ unsafe impl CoreArrayProviderInner for StructureMember { unsafe fn free(raw: *mut Self::Raw, count: usize, _context: &Self::Context) { BNFreeStructureMemberList(raw, count) } + unsafe fn wrap_raw<'a>(raw: &'a Self::Raw, _context: &'a Self::Context) -> Self::Wrapped<'a> { - Guard::new(StructureMember::from_raw(*raw), &()) + Guard::new(StructureMember::from(*raw), &()) } } -#[derive(Debug, Clone)] +#[derive(Debug, Clone, Hash, PartialEq, Eq)] pub struct InheritedStructureMember { pub base: Ref, pub base_offset: u64, @@ -2267,18 +1791,9 @@ impl InheritedStructureMember { member_index, } } - - // pub(crate) unsafe fn from_raw(handle: BNInheritedStructureMember) -> Self { - // Self { - // base: RefCountable::inc_ref(&NamedTypeReference::from_raw(handle.base)), - // base_offset: handle.baseOffset, - // member: StructureMember::from_raw(handle.member), - // member_index: handle.memberIndex, - // } - // } } -#[derive(Debug, Clone)] +#[derive(Debug, Clone, Hash, PartialEq, Eq)] pub struct BaseStructure { pub ty: Ref, pub offset: u64, @@ -2289,19 +1804,18 @@ impl BaseStructure { pub fn new(ty: Ref, offset: u64, width: u64) -> Self { Self { ty, offset, width } } +} - pub(crate) unsafe fn from_raw(handle: BNBaseStructure) -> Self { +impl From for BaseStructure { + fn from(value: BNBaseStructure) -> Self { Self { - ty: RefCountable::inc_ref(&NamedTypeReference::from_raw(handle.type_)), - offset: handle.offset, - width: handle.width, + ty: unsafe { NamedTypeReference::ref_from_raw(value.type_) }, + offset: value.offset, + width: value.width, } } } -//////////////////////// -// NamedTypeReference - #[derive(PartialEq, Eq, Hash)] pub struct NamedTypeReference { pub(crate) handle: *mut BNNamedTypeReference, @@ -2310,7 +1824,6 @@ pub struct NamedTypeReference { impl NamedTypeReference { pub(crate) unsafe fn from_raw(handle: *mut BNNamedTypeReference) -> Self { debug_assert!(!handle.is_null()); - Self { handle } } @@ -2326,7 +1839,7 @@ impl NamedTypeReference { /// the core will do the id stuff for you. pub fn new(type_class: NamedTypeReferenceClass, mut name: QualifiedName) -> Ref { unsafe { - Self::ref_from_raw(BNCreateNamedType(type_class, ptr::null() as *const _, &mut name.0)) + Self::ref_from_raw(BNCreateNamedType(type_class, std::ptr::null() as *const _, &mut name.0)) } } @@ -2406,14 +1919,11 @@ unsafe impl RefCountable for NamedTypeReference { } impl Debug for NamedTypeReference { - fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { write!(f, "{} (id: {})", self.name(), self.id()) } } -/////////////////// -// QualifiedName - #[repr(transparent)] pub struct QualifiedName(pub(crate) BNQualifiedName); @@ -2421,7 +1931,7 @@ impl QualifiedName { // TODO : I think this is bad pub fn string(&self) -> String { unsafe { - slice::from_raw_parts(self.0.name, self.0.nameCount) + std::slice::from_raw_parts(self.0.name, self.0.nameCount) .iter() .map(|c| CStr::from_ptr(*c).to_string_lossy()) .collect::>() @@ -2437,7 +1947,7 @@ impl QualifiedName { pub fn strings(&self) -> Vec> { let names: *mut *mut c_char = self.0.name; unsafe { - slice::from_raw_parts(names, self.0.nameCount) + std::slice::from_raw_parts(names, self.0.nameCount) .iter() .map(|name| CStr::from_ptr(*name).to_string_lossy()) .collect::>() @@ -2503,13 +2013,13 @@ impl Hash for QualifiedName { } impl Debug for QualifiedName { - fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { write!(f, "{}", self.string()) } } impl Display for QualifiedName { - fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { write!(f, "{}", self.string()) } } @@ -2535,24 +2045,23 @@ impl CoreArrayProvider for QualifiedName { type Context = (); type Wrapped<'a> = &'a QualifiedName; } + unsafe impl CoreArrayProviderInner for QualifiedName { unsafe fn free(raw: *mut Self::Raw, count: usize, _context: &Self::Context) { BNFreeTypeNameList(raw, count); } + unsafe fn wrap_raw<'a>(raw: &'a Self::Raw, _context: &'a Self::Context) -> Self::Wrapped<'a> { - mem::transmute(raw) + std::mem::transmute(raw) } } -////////////////////////// -// QualifiedNameAndType - #[repr(transparent)] pub struct QualifiedNameAndType(pub(crate) BNQualifiedNameAndType); impl QualifiedNameAndType { pub fn name(&self) -> &QualifiedName { - unsafe { mem::transmute(&self.0.name) } + unsafe { std::mem::transmute(&self.0.name) } } pub fn type_object(&self) -> Guard { @@ -2573,31 +2082,29 @@ impl CoreArrayProvider for QualifiedNameAndType { type Context = (); type Wrapped<'a> = &'a QualifiedNameAndType; } + unsafe impl CoreArrayProviderInner for QualifiedNameAndType { unsafe fn free(raw: *mut Self::Raw, count: usize, _context: &Self::Context) { BNFreeTypeAndNameList(raw, count); } unsafe fn wrap_raw<'a>(raw: &'a Self::Raw, _context: &'a Self::Context) -> Self::Wrapped<'a> { - mem::transmute(raw) + std::mem::transmute(raw) } } -////////////////////////// -// QualifiedNameTypeAndId - #[repr(transparent)] pub struct QualifiedNameTypeAndId(pub(crate) BNQualifiedNameTypeAndId); impl QualifiedNameTypeAndId { pub fn name(&self) -> &QualifiedName { - unsafe { mem::transmute(&self.0.name) } + unsafe { std::mem::transmute(&self.0.name) } } pub fn id(&self) -> &str { unsafe { CStr::from_ptr(self.0.id).to_str().unwrap() } } - pub fn type_object(&self) -> Guard { + pub fn ty(&self) -> Guard { unsafe { Guard::new(Type::from_raw(self.0.type_), self) } } } @@ -2615,72 +2122,53 @@ impl CoreArrayProvider for QualifiedNameTypeAndId { type Context = (); type Wrapped<'a> = &'a QualifiedNameTypeAndId; } + unsafe impl CoreArrayProviderInner for QualifiedNameTypeAndId { unsafe fn free(raw: *mut Self::Raw, count: usize, _context: &Self::Context) { BNFreeTypeIdList(raw, count); } + unsafe fn wrap_raw<'a>(raw: &'a Self::Raw, _context: &'a Self::Context) -> Self::Wrapped<'a> { - mem::transmute(raw) + // TODO: Oh my god what is wrong with all of you people + std::mem::transmute(raw) } } -////////////////////////// -// NameAndType - -pub struct NameAndType(pub(crate) BNNameAndType); - -impl NameAndType { - pub(crate) unsafe fn from_raw(raw: &BNNameAndType) -> Self { - Self(*raw) - } +// TODO: Document how this type is used for many different purposes. (this is literally (string, type)) +// TODO: Ex. the name might be the parser it came from +// TODO: Ex. the name might be the param name for an intrinsic input +// TODO: Should we make new types for each varying use case? +#[derive(Debug, Clone, Eq, PartialEq, Hash)] +pub struct NameAndType { + pub name: String, + pub ty: Conf>, } impl NameAndType { - pub fn new(name: S, t: &Type, confidence: u8) -> Ref { - unsafe { - Ref::new(Self(BNNameAndType { - name: BNAllocString(name.into_bytes_with_nul().as_ref().as_ptr() as *mut _), - type_: Ref::into_raw(t.to_owned()).handle, - typeConfidence: confidence, - })) - } - } - - pub fn name(&self) -> &str { - let c_str = unsafe { CStr::from_ptr(self.0.name) }; - c_str.to_str().unwrap() - } - - pub fn t(&self) -> &Type { - unsafe { mem::transmute::<_, &Type>(&self.0.type_) } - } - - pub fn type_with_confidence(&self) -> Conf<&Type> { - Conf::new(self.t(), self.0.typeConfidence) + pub fn new(name: impl Into, ty: Conf>) -> Self { + Self { name: name.into(), ty} } } -impl ToOwned for NameAndType { - type Owned = Ref; - - fn to_owned(&self) -> Self::Owned { - unsafe { RefCountable::inc_ref(self) } +impl From for NameAndType { + fn from(value: BNNameAndType) -> Self { + Self { + name: unsafe { BnString::from_raw(value.name) }.to_string(), + ty: Conf::new( + unsafe { Type::ref_from_raw(value.type_) }, + value.typeConfidence, + ), + } } } -unsafe impl RefCountable for NameAndType { - unsafe fn inc_ref(handle: &Self) -> Ref { - Self::new( - CStr::from_ptr(handle.0.name), - handle.t(), - handle.0.typeConfidence, - ) - } - - unsafe fn dec_ref(handle: &Self) { - unsafe { - BNFreeString(handle.0.name); - RefCountable::dec_ref(handle.t()); +impl From for BNNameAndType { + fn from(value: NameAndType) -> Self { + let bn_name = BnString::new(value.name); + Self { + name: bn_name.into_raw(), + type_: value.ty.contents.handle, + typeConfidence: value.ty.confidence, } } } @@ -2688,1093 +2176,15 @@ unsafe impl RefCountable for NameAndType { impl CoreArrayProvider for NameAndType { type Raw = BNNameAndType; type Context = (); - type Wrapped<'a> = Guard<'a, NameAndType>; + type Wrapped<'a> = Self; } unsafe impl CoreArrayProviderInner for NameAndType { unsafe fn free(raw: *mut Self::Raw, count: usize, _context: &Self::Context) { BNFreeNameAndTypeList(raw, count); } + unsafe fn wrap_raw<'a>(raw: &'a Self::Raw, _context: &'a Self::Context) -> Self::Wrapped<'a> { - unsafe { Guard::new(NameAndType::from_raw(raw), raw) } - } -} - -////////////////// -// DataVariable - -#[repr(transparent)] -pub struct DataVariable(pub(crate) BNDataVariable); - -// impl DataVariable { -// pub(crate) fn from_raw(var: &BNDataVariable) -> Self { -// let var = DataVariable(*var); -// Self(BNDataVariable { -// type_: unsafe { Ref::into_raw(var.t().to_owned()).handle }, -// ..var.0 -// }) -// } -// } - -impl DataVariable { - pub fn address(&self) -> u64 { - self.0.address - } - - pub fn auto_discovered(&self) -> bool { - self.0.autoDiscovered - } - - pub fn t(&self) -> &Type { - unsafe { mem::transmute(&self.0.type_) } - } - - pub fn type_with_confidence(&self) -> Conf<&Type> { - Conf::new(self.t(), self.0.typeConfidence) - } - - pub fn symbol(&self, bv: &BinaryView) -> Option> { - bv.symbol_by_address(self.0.address).ok() - } -} - -impl ToOwned for DataVariable { - type Owned = Ref; - - fn to_owned(&self) -> Self::Owned { - unsafe { RefCountable::inc_ref(self) } - } -} - -unsafe impl RefCountable for DataVariable { - unsafe fn inc_ref(handle: &Self) -> Ref { - unsafe { - Ref::new(Self(BNDataVariable { - type_: Ref::into_raw(handle.t().to_owned()).handle, - ..handle.0 - })) - } - } - - unsafe fn dec_ref(handle: &Self) { - unsafe { BNFreeType(handle.0.type_) } - } -} - -impl CoreArrayProvider for DataVariable { - type Raw = BNDataVariable; - type Context = (); - type Wrapped<'a> = &'a DataVariable; -} -unsafe impl CoreArrayProviderInner for DataVariable { - unsafe fn free(raw: *mut Self::Raw, count: usize, _context: &Self::Context) { - BNFreeDataVariables(raw, count); - } - unsafe fn wrap_raw<'a>(raw: &'a Self::Raw, _context: &'a Self::Context) -> Self::Wrapped<'a> { - mem::transmute(raw) - } -} - -///////////////////////// -// DataVariableAndName - -pub struct DataVariableAndName { - pub address: u64, - pub t: Conf>, - pub auto_discovered: bool, - pub name: S, -} - -impl DataVariableAndName { - pub(crate) fn from_raw(var: &BNDataVariableAndName) -> Self { - Self { - address: var.address, - t: Conf::new(unsafe { Type::ref_from_raw(var.type_) }, var.typeConfidence), - auto_discovered: var.autoDiscovered, - name: raw_to_string(var.name).unwrap(), - } - } -} - -impl DataVariableAndName { - pub fn new(address: u64, t: Conf>, auto_discovered: bool, name: S) -> Self { - Self { - address, - t, - auto_discovered, - name, - } - } - - pub fn type_with_confidence(&self) -> Conf> { - Conf::new(self.t.contents.clone(), self.t.confidence) - } -} - -///////////////////////// -// RegisterValueType - -#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)] -pub enum RegisterValueType { - UndeterminedValue, - EntryValue, - ConstantValue, - ConstantPointerValue, - ExternalPointerValue, - StackFrameOffset, - ReturnAddressValue, - ImportedAddressValue, - SignedRangeValue, - UnsignedRangeValue, - LookupTableValue, - InSetOfValues, - NotInSetOfValues, - ConstantDataValue, - ConstantDataZeroExtendValue, - ConstantDataSignExtendValue, - ConstantDataAggregateValue, -} - -impl RegisterValueType { - pub(crate) fn from_raw_value(value: u32) -> Option { - use BNRegisterValueType::*; - Some(match value { - x if x == UndeterminedValue as u32 => Self::UndeterminedValue, - x if x == EntryValue as u32 => Self::EntryValue, - x if x == ConstantValue as u32 => Self::ConstantValue, - x if x == ConstantPointerValue as u32 => Self::ConstantPointerValue, - x if x == ExternalPointerValue as u32 => Self::ExternalPointerValue, - x if x == StackFrameOffset as u32 => Self::StackFrameOffset, - x if x == ReturnAddressValue as u32 => Self::ReturnAddressValue, - x if x == ImportedAddressValue as u32 => Self::ImportedAddressValue, - x if x == SignedRangeValue as u32 => Self::SignedRangeValue, - x if x == UnsignedRangeValue as u32 => Self::UnsignedRangeValue, - x if x == LookupTableValue as u32 => Self::LookupTableValue, - x if x == InSetOfValues as u32 => Self::InSetOfValues, - x if x == NotInSetOfValues as u32 => Self::NotInSetOfValues, - x if x == ConstantDataValue as u32 => Self::ConstantDataValue, - x if x == ConstantDataZeroExtendValue as u32 => Self::ConstantDataZeroExtendValue, - x if x == ConstantDataSignExtendValue as u32 => Self::ConstantDataSignExtendValue, - x if x == ConstantDataAggregateValue as u32 => Self::ConstantDataAggregateValue, - _ => return None, - }) - } - - pub(crate) fn into_raw_value(self) -> BNRegisterValueType { - use BNRegisterValueType::*; - match self { - Self::UndeterminedValue => UndeterminedValue, - Self::EntryValue => EntryValue, - Self::ConstantValue => ConstantValue, - Self::ConstantPointerValue => ConstantPointerValue, - Self::ExternalPointerValue => ExternalPointerValue, - Self::StackFrameOffset => StackFrameOffset, - Self::ReturnAddressValue => ReturnAddressValue, - Self::ImportedAddressValue => ImportedAddressValue, - Self::SignedRangeValue => SignedRangeValue, - Self::UnsignedRangeValue => UnsignedRangeValue, - Self::LookupTableValue => LookupTableValue, - Self::InSetOfValues => InSetOfValues, - Self::NotInSetOfValues => NotInSetOfValues, - Self::ConstantDataValue => ConstantDataValue, - Self::ConstantDataZeroExtendValue => ConstantDataZeroExtendValue, - Self::ConstantDataSignExtendValue => ConstantDataSignExtendValue, - Self::ConstantDataAggregateValue => ConstantDataAggregateValue, - } - } -} - -///////////////////////// -// RegisterValue - -#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] -pub struct RegisterValue { - pub(crate) state: RegisterValueType, - pub(crate) value: i64, - pub(crate) offset: i64, - pub(crate) size: usize, -} - -impl RegisterValue { - pub fn new(state: RegisterValueType, value: i64, offset: i64, size: usize) -> Self { - Self { - state, - value, - offset, - size, - } - } -} - -impl From for RegisterValue { - fn from(value: BNRegisterValue) -> Self { - Self { - state: RegisterValueType::from_raw_value(value.state as u32).unwrap(), - value: value.value, - offset: value.offset, - size: value.size, - } - } -} - -impl From for BNRegisterValue { - fn from(value: RegisterValue) -> Self { - Self { - state: value.state.into_raw_value(), - value: value.value, - offset: value.offset, - size: value.size, - } - } -} - -///////////////////////// -// ConstantData - -#[derive(Clone, Debug, PartialEq, Hash)] -pub struct ConstantData { - function: Ref, - value: RegisterValue, -} - -impl ConstantData { - pub(crate) fn new(function: Ref, value: RegisterValue) -> Self { - Self { function, value } - } -} - -// unsafe impl CoreArrayProvider for DataVariableAndName { -// type Raw = BNDataVariableAndName; -// type Context = (); -// } - -// unsafe impl CoreOwnedArrayProvider for DataVariableAndName { -// unsafe fn free(raw: *mut Self::Raw, count: usize, _context: &Self::Context) { -// BNFreeDataVariablesAndName(raw, count); -// } -// } - -// unsafe impl<'a, S: 'a + BnStrCompatible> CoreArrayWrapper<'a> for DataVariableAndName { -// type Wrapped = &'a DataVariableAndName; -// } - -// unsafe impl<'a, S: 'a + BnStrCompatible> CoreArrayWrapper<'a> for DataVariableAndName { -// unsafe fn wrap_raw(raw: &'a Self::Raw, _context: &'a Self::Context) -> Self::Wrapped { -// mem::transmute(raw) -// } -// } - -///////////////////////// -// ValueRange - -#[repr(transparent)] -#[derive(Copy, Clone, Debug)] -pub struct ValueRange { - raw: BNValueRange, - _t: core::marker::PhantomData, -} - -impl ValueRange { - fn from_raw(value: BNValueRange) -> Self { - Self { - raw: value, - _t: core::marker::PhantomData, - } - } - fn into_raw(self) -> BNValueRange { - self.raw - } -} - -impl IntoIterator for ValueRange { - type Item = u64; - type IntoIter = core::iter::StepBy>; - - fn into_iter(self) -> Self::IntoIter { - (self.raw.start..self.raw.end).step_by(self.raw.step.try_into().unwrap()) - } -} -impl IntoIterator for ValueRange { - type Item = i64; - type IntoIter = core::iter::StepBy>; - - fn into_iter(self) -> Self::IntoIter { - (self.raw.start as i64..self.raw.end as i64).step_by(self.raw.step.try_into().unwrap()) - } -} - -///////////////////////// -// PossibleValueSet - -#[derive(Clone, Debug)] -pub enum PossibleValueSet { - UndeterminedValue, - EntryValue { - reg: i64, - }, - ConstantValue { - value: i64, - }, - ConstantPointerValue { - value: i64, - }, - ExternalPointerValue, - StackFrameOffset { - offset: i64, - }, - ReturnAddressValue, - ImportedAddressValue, - SignedRangeValue { - offset: i64, - ranges: Vec>, - }, - UnsignedRangeValue { - offset: i64, - ranges: Vec>, - }, - LookupTableValue { - tables: Vec, - }, - InSetOfValues { - values: HashSet, - }, - NotInSetOfValues { - values: HashSet, - }, - ConstantDataValue { - value_type: ConstantDataType, - value: i64, - }, -} - -#[derive(Copy, Clone, Debug)] -pub enum ConstantDataType { - Value, - ZeroExtend, - SignExtend, - Aggregate, -} - -impl PossibleValueSet { - pub(crate) unsafe fn from_raw(value: BNPossibleValueSet) -> Self { - unsafe fn from_range(value: BNPossibleValueSet) -> Vec> { - core::slice::from_raw_parts(value.ranges, value.count) - .iter() - .copied() - .map(|range| ValueRange::from_raw(range)) - .collect() - } - let from_sets = |value: BNPossibleValueSet| { - unsafe { core::slice::from_raw_parts(value.valueSet, value.count) } - .iter() - .copied() - .collect() - }; - use BNRegisterValueType::*; - match value.state { - UndeterminedValue => Self::UndeterminedValue, - EntryValue => Self::EntryValue { reg: value.value }, - ConstantValue => Self::ConstantValue { value: value.value }, - ConstantPointerValue => Self::ConstantPointerValue { value: value.value }, - StackFrameOffset => Self::StackFrameOffset { - offset: value.value, - }, - ConstantDataValue => Self::ConstantDataValue { - value_type: ConstantDataType::Value, - value: value.value, - }, - ConstantDataZeroExtendValue => Self::ConstantDataValue { - value_type: ConstantDataType::ZeroExtend, - value: value.value, - }, - ConstantDataSignExtendValue => Self::ConstantDataValue { - value_type: ConstantDataType::SignExtend, - value: value.value, - }, - ConstantDataAggregateValue => Self::ConstantDataValue { - value_type: ConstantDataType::Aggregate, - value: value.value, - }, - SignedRangeValue => Self::SignedRangeValue { - offset: value.value, - ranges: from_range(value), - }, - UnsignedRangeValue => Self::UnsignedRangeValue { - offset: value.value, - ranges: from_range(value), - }, - LookupTableValue => { - let raw_tables = unsafe { core::slice::from_raw_parts(value.table, value.count) }; - let raw_from_tables = |i: &BNLookupTableEntry| unsafe { - core::slice::from_raw_parts(i.fromValues, i.fromCount) - }; - let tables = raw_tables - .iter() - .map(|table| LookupTableEntry { - from_values: raw_from_tables(table).to_vec(), - to_value: table.toValue, - }) - .collect(); - Self::LookupTableValue { tables } - } - NotInSetOfValues => Self::NotInSetOfValues { - values: from_sets(value), - }, - InSetOfValues => Self::InSetOfValues { - values: from_sets(value), - }, - ImportedAddressValue => Self::ImportedAddressValue, - ReturnAddressValue => Self::ReturnAddressValue, - ExternalPointerValue => Self::ExternalPointerValue, - } - } - pub(crate) fn into_raw(self) -> PossibleValueSetRaw { - let mut raw: BNPossibleValueSet = unsafe { core::mem::zeroed() }; - // set the state field - raw.state = self.value_type().into_raw_value(); - // set all other fields - match self { - PossibleValueSet::UndeterminedValue - | PossibleValueSet::ExternalPointerValue - | PossibleValueSet::ReturnAddressValue - | PossibleValueSet::ImportedAddressValue => {} - PossibleValueSet::EntryValue { reg: value } - | PossibleValueSet::ConstantValue { value } - | PossibleValueSet::ConstantPointerValue { value } - | PossibleValueSet::ConstantDataValue { value, .. } - | PossibleValueSet::StackFrameOffset { offset: value } => raw.value = value, - PossibleValueSet::NotInSetOfValues { values } - | PossibleValueSet::InSetOfValues { values } => { - let values = Box::leak(values.into_iter().collect()); - raw.valueSet = values.as_mut_ptr(); - raw.count = values.len(); - } - PossibleValueSet::SignedRangeValue { offset, ranges } => { - let ranges = Box::leak(ranges.into_iter().map(|x| x.into_raw()).collect()); - raw.value = offset; - raw.ranges = ranges.as_mut_ptr(); - raw.count = ranges.len(); - } - PossibleValueSet::UnsignedRangeValue { offset, ranges } => { - let ranges = Box::leak(ranges.into_iter().map(|x| x.into_raw()).collect()); - raw.value = offset; - raw.ranges = ranges.as_mut_ptr(); - raw.count = ranges.len(); - } - PossibleValueSet::LookupTableValue { tables } => { - let tables = Box::leak(tables.into_iter().map(|table| table.into_raw()).collect()); - // SAFETY: BNLookupTableEntry and LookupTableEntryRaw are transparent - raw.table = tables.as_mut_ptr() as *mut BNLookupTableEntry; - raw.count = tables.len(); - } - } - PossibleValueSetRaw(raw) - } - - pub fn value_type(&self) -> RegisterValueType { - use RegisterValueType::*; - match self { - PossibleValueSet::UndeterminedValue => UndeterminedValue, - PossibleValueSet::EntryValue { .. } => EntryValue, - PossibleValueSet::ConstantValue { .. } => ConstantValue, - PossibleValueSet::ConstantPointerValue { .. } => ConstantPointerValue, - PossibleValueSet::ExternalPointerValue => ExternalPointerValue, - PossibleValueSet::StackFrameOffset { .. } => StackFrameOffset, - PossibleValueSet::ReturnAddressValue => ReturnAddressValue, - PossibleValueSet::ImportedAddressValue => ImportedAddressValue, - PossibleValueSet::SignedRangeValue { .. } => SignedRangeValue, - PossibleValueSet::UnsignedRangeValue { .. } => UnsignedRangeValue, - PossibleValueSet::LookupTableValue { .. } => LookupTableValue, - PossibleValueSet::InSetOfValues { .. } => InSetOfValues, - PossibleValueSet::NotInSetOfValues { .. } => NotInSetOfValues, - PossibleValueSet::ConstantDataValue { - value_type: ConstantDataType::Value, - .. - } => ConstantDataValue, - PossibleValueSet::ConstantDataValue { - value_type: ConstantDataType::ZeroExtend, - .. - } => ConstantDataZeroExtendValue, - PossibleValueSet::ConstantDataValue { - value_type: ConstantDataType::SignExtend, - .. - } => ConstantDataSignExtendValue, - PossibleValueSet::ConstantDataValue { - value_type: ConstantDataType::Aggregate, - .. - } => ConstantDataAggregateValue, - } - } -} - -/// The owned version of the BNPossibleValueSet -#[repr(transparent)] -pub(crate) struct PossibleValueSetRaw(BNPossibleValueSet); - -impl PossibleValueSetRaw { - pub fn as_ffi(&self) -> &BNPossibleValueSet { - &self.0 - } -} - -impl Drop for PossibleValueSetRaw { - fn drop(&mut self) { - use BNRegisterValueType::*; - match self.0.state { - UndeterminedValue - | ExternalPointerValue - | ReturnAddressValue - | ImportedAddressValue - | EntryValue - | ConstantValue - | ConstantPointerValue - | StackFrameOffset - | ConstantDataValue - | ConstantDataZeroExtendValue - | ConstantDataSignExtendValue - | ConstantDataAggregateValue => {} - InSetOfValues | NotInSetOfValues => { - let _values: Box<[i64]> = unsafe { - Box::from_raw(ptr::slice_from_raw_parts_mut(self.0.valueSet, self.0.count)) - }; - } - SignedRangeValue | UnsignedRangeValue => { - let _ranges: Box<[BNValueRange]> = unsafe { - Box::from_raw(ptr::slice_from_raw_parts_mut(self.0.ranges, self.0.count)) - }; - } - LookupTableValue => { - // SAFETY: LookupTableEntryRaw and BNLookupTableEntry can be safely transmuted - let table_ptr = self.0.table as *mut LookupTableEntryRaw; - let _table: Box<[LookupTableEntryRaw]> = unsafe { - Box::from_raw(ptr::slice_from_raw_parts_mut(table_ptr, self.0.count)) - }; - } - } - } -} - -///////////////////////// -// LookupTableEntry - -#[derive(Clone, Debug)] -pub struct LookupTableEntry { - pub from_values: Vec, - pub to_value: i64, -} - -impl LookupTableEntry { - fn into_raw(self) -> LookupTableEntryRaw { - let from_value = Box::leak(self.from_values.into_boxed_slice()); - LookupTableEntryRaw(BNLookupTableEntry { - toValue: self.to_value, - fromValues: from_value.as_mut_ptr(), - fromCount: from_value.len(), - }) - } -} - -/// The owned version of the BNLookupTableEntry -#[repr(transparent)] -struct LookupTableEntryRaw(BNLookupTableEntry); -impl Drop for LookupTableEntryRaw { - fn drop(&mut self) { - let _from_value: Box<[i64]> = unsafe { - Box::from_raw(ptr::slice_from_raw_parts_mut( - self.0.fromValues, - self.0.fromCount, - )) - }; - } -} - -///////////////////////// -// ArchAndAddr - -#[derive(Copy, Clone, Eq, Hash, PartialEq)] -pub struct ArchAndAddr { - pub arch: CoreArchitecture, - pub address: u64, -} - -///////////////////////// -// UserVariableValues - -pub struct UserVariableValues { - pub(crate) vars: *const [BNUserVariableValue], -} - -impl UserVariableValues { - pub fn into_hashmap(self) -> HashMap> { - let mut result: HashMap> = HashMap::new(); - for (var, def_site, possible_val) in self.all() { - result - .entry(var) - .or_default() - .entry(def_site) - .or_insert(possible_val); - } - result - } - pub fn all(&self) -> impl Iterator { - unsafe { &*self.vars }.iter().map(|var_val| { - let var = unsafe { Variable::from_raw(var_val.var) }; - let def_site = ArchAndAddr { - arch: unsafe { CoreArchitecture::from_raw(var_val.defSite.arch) }, - address: var_val.defSite.address, - }; - let possible_val = unsafe { PossibleValueSet::from_raw(var_val.value) }; - (var, def_site, possible_val) - }) - } - pub fn values_from_variable( - &self, - var: Variable, - ) -> impl Iterator { - self.all() - .filter(move |(t_var, _, _)| t_var == &var) - .map(|(_var, def_site, possible_val)| (def_site, possible_val)) - } -} - -impl Drop for UserVariableValues { - fn drop(&mut self) { - unsafe { BNFreeUserVariableValues(self.vars as *mut BNUserVariableValue) }; - } -} - -///////////////////////// -// ConstantReference - -#[derive(Copy, Clone, Eq, Hash, PartialEq)] -pub struct ConstantReference { - pub value: i64, - pub size: usize, - pub pointer: bool, - pub intermediate: bool, -} - -impl ConstantReference { - pub fn from_raw(value: BNConstantReference) -> Self { - Self { - value: value.value, - size: value.size, - pointer: value.pointer, - intermediate: value.intermediate, - } - } - pub fn into_raw(self) -> BNConstantReference { - BNConstantReference { - value: self.value, - size: self.size, - pointer: self.pointer, - intermediate: self.intermediate, - } - } -} - -impl CoreArrayProvider for ConstantReference { - type Raw = BNConstantReference; - type Context = (); - type Wrapped<'a> = Self; -} - -unsafe impl CoreArrayProviderInner for ConstantReference { - unsafe fn free(raw: *mut Self::Raw, _count: usize, _context: &Self::Context) { - BNFreeConstantReferenceList(raw) - } - unsafe fn wrap_raw<'a>(raw: &'a Self::Raw, _context: &'a Self::Context) -> Self::Wrapped<'a> { - Self::from_raw(*raw) - } -} - -///////////////////////// -// IndirectBranchInfo - -pub struct IndirectBranchInfo { - pub source_arch: CoreArchitecture, - pub source_addr: u64, - pub dest_arch: CoreArchitecture, - pub dest_addr: u64, - pub auto_defined: bool, -} - -impl IndirectBranchInfo { - pub fn from_raw(value: BNIndirectBranchInfo) -> Self { - Self { - source_arch: unsafe { CoreArchitecture::from_raw(value.sourceArch) }, - source_addr: value.sourceAddr, - dest_arch: unsafe { CoreArchitecture::from_raw(value.destArch) }, - dest_addr: value.destAddr, - auto_defined: value.autoDefined, - } - } - pub fn into_raw(self) -> BNIndirectBranchInfo { - BNIndirectBranchInfo { - sourceArch: self.source_arch.0, - sourceAddr: self.source_addr, - destArch: self.dest_arch.0, - destAddr: self.dest_addr, - autoDefined: self.auto_defined, - } - } -} - -impl CoreArrayProvider for IndirectBranchInfo { - type Raw = BNIndirectBranchInfo; - type Context = (); - type Wrapped<'a> = Self; -} - -unsafe impl CoreArrayProviderInner for IndirectBranchInfo { - unsafe fn free(raw: *mut Self::Raw, _count: usize, _context: &Self::Context) { - BNFreeIndirectBranchList(raw) - } - unsafe fn wrap_raw<'a>(raw: &'a Self::Raw, _context: &'a Self::Context) -> Self::Wrapped<'a> { - Self::from_raw(*raw) - } -} - -///////////////////////// -// HighlightStandardColor - -#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)] -pub enum HighlightStandardColor { - //NoHighlightColor, - BlueHighlightColor, - GreenHighlightColor, - CyanHighlightColor, - RedHighlightColor, - MagentaHighlightColor, - YellowHighlightColor, - OrangeHighlightColor, - WhiteHighlightColor, - BlackHighlightColor, -} - -impl HighlightStandardColor { - pub fn from_raw(value: BNHighlightStandardColor) -> Option { - Some(match value { - BNHighlightStandardColor::NoHighlightColor => return None, - BNHighlightStandardColor::BlueHighlightColor => Self::BlueHighlightColor, - BNHighlightStandardColor::GreenHighlightColor => Self::GreenHighlightColor, - BNHighlightStandardColor::CyanHighlightColor => Self::CyanHighlightColor, - BNHighlightStandardColor::RedHighlightColor => Self::RedHighlightColor, - BNHighlightStandardColor::MagentaHighlightColor => Self::MagentaHighlightColor, - BNHighlightStandardColor::YellowHighlightColor => Self::YellowHighlightColor, - BNHighlightStandardColor::OrangeHighlightColor => Self::OrangeHighlightColor, - BNHighlightStandardColor::WhiteHighlightColor => Self::WhiteHighlightColor, - BNHighlightStandardColor::BlackHighlightColor => Self::BlackHighlightColor, - }) - } - pub fn into_raw(self) -> BNHighlightStandardColor { - match self { - //Self::NoHighlightColor => BNHighlightStandardColor::NoHighlightColor, - Self::BlueHighlightColor => BNHighlightStandardColor::BlueHighlightColor, - Self::GreenHighlightColor => BNHighlightStandardColor::GreenHighlightColor, - Self::CyanHighlightColor => BNHighlightStandardColor::CyanHighlightColor, - Self::RedHighlightColor => BNHighlightStandardColor::RedHighlightColor, - Self::MagentaHighlightColor => BNHighlightStandardColor::MagentaHighlightColor, - Self::YellowHighlightColor => BNHighlightStandardColor::YellowHighlightColor, - Self::OrangeHighlightColor => BNHighlightStandardColor::OrangeHighlightColor, - Self::WhiteHighlightColor => BNHighlightStandardColor::WhiteHighlightColor, - Self::BlackHighlightColor => BNHighlightStandardColor::BlackHighlightColor, - } - } -} - -///////////////////////// -// HighlightColor - -#[derive(Debug, Copy, Clone)] -pub enum HighlightColor { - NoHighlightColor { - alpha: u8, - }, - StandardHighlightColor { - color: HighlightStandardColor, - alpha: u8, - }, - MixedHighlightColor { - color: HighlightStandardColor, - mix_color: HighlightStandardColor, - mix: u8, - alpha: u8, - }, - CustomHighlightColor { - r: u8, - g: u8, - b: u8, - alpha: u8, - }, -} - -impl HighlightColor { - pub fn from_raw(raw: BNHighlightColor) -> Self { - const HIGHLIGHT_COLOR: u32 = BNHighlightColorStyle::StandardHighlightColor as u32; - const MIXED_HIGHLIGHT_COLOR: u32 = BNHighlightColorStyle::MixedHighlightColor as u32; - const CUSTOM_HIGHLIHGT_COLOR: u32 = BNHighlightColorStyle::CustomHighlightColor as u32; - match raw.style as u32 { - HIGHLIGHT_COLOR => { - let Some(color) = HighlightStandardColor::from_raw(raw.color) else { - // StandardHighlightColor with NoHighlightColor, is no color - return Self::NoHighlightColor { alpha: raw.alpha }; - }; - Self::StandardHighlightColor { - color, - alpha: raw.alpha, - } - } - MIXED_HIGHLIGHT_COLOR => { - let Some(color) = HighlightStandardColor::from_raw(raw.color) else { - panic!("Highlight mixed color with no color"); - }; - let Some(mix_color) = HighlightStandardColor::from_raw(raw.mixColor) else { - panic!("Highlight mixed color with no mix_color"); - }; - Self::MixedHighlightColor { - color, - mix_color, - mix: raw.mix, - alpha: raw.alpha, - } - } - CUSTOM_HIGHLIHGT_COLOR => Self::CustomHighlightColor { - r: raw.r, - g: raw.g, - b: raw.b, - alpha: raw.alpha, - }, - // other color style is just no color - _ => Self::NoHighlightColor { alpha: u8::MAX }, - } - } - - pub fn into_raw(self) -> BNHighlightColor { - let zeroed: BNHighlightColor = unsafe { core::mem::zeroed() }; - match self { - Self::NoHighlightColor { alpha } => BNHighlightColor { - style: BNHighlightColorStyle::StandardHighlightColor, - color: BNHighlightStandardColor::NoHighlightColor, - alpha, - ..zeroed - }, - Self::StandardHighlightColor { color, alpha } => BNHighlightColor { - style: BNHighlightColorStyle::StandardHighlightColor, - color: color.into_raw(), - alpha, - ..zeroed - }, - Self::MixedHighlightColor { - color, - mix_color, - mix, - alpha, - } => BNHighlightColor { - color: color.into_raw(), - mixColor: mix_color.into_raw(), - mix, - alpha, - ..zeroed - }, - Self::CustomHighlightColor { r, g, b, alpha } => BNHighlightColor { - r, - g, - b, - alpha, - ..zeroed - }, - } - } -} - -///////////////////////// -// IntegerDisplayType - -pub type IntegerDisplayType = binaryninjacore_sys::BNIntegerDisplayType; - -///////////////////////// -// StackVariableReference - -#[derive(Debug, Clone)] -pub struct StackVariableReference { - _source_operand: u32, - var_type: Conf>, - name: BnString, - var: Variable, - offset: i64, - size: usize, -} - -impl StackVariableReference { - pub fn from_raw(value: BNStackVariableReference) -> Self { - let var_type = Conf::new( - unsafe { Type::ref_from_raw(value.type_) }, - value.typeConfidence, - ); - let name = unsafe { BnString::from_raw(value.name) }; - let var = unsafe { Variable::from_identifier(value.varIdentifier) }; - let offset = value.referencedOffset; - let size = value.size; - Self { - _source_operand: value.sourceOperand, - var_type, - name, - var, - offset, - size, - } - } - pub fn variable(&self) -> &Variable { - &self.var - } - pub fn variable_type(&self) -> Conf<&Type> { - self.var_type.as_ref() - } - pub fn name(&self) -> &str { - self.name.as_str() - } - pub fn offset(&self) -> i64 { - self.offset - } - pub fn size(&self) -> usize { - self.size - } -} - -impl CoreArrayProvider for StackVariableReference { - type Raw = BNStackVariableReference; - type Context = (); - type Wrapped<'a> = Guard<'a, Self>; -} - -unsafe impl CoreArrayProviderInner for StackVariableReference { - unsafe fn free(raw: *mut Self::Raw, count: usize, _context: &Self::Context) { - BNFreeStackVariableReferenceList(raw, count) - } - unsafe fn wrap_raw<'a>(raw: &'a Self::Raw, context: &'a Self::Context) -> Self::Wrapped<'a> { - Guard::new(Self::from_raw(*raw), context) - } -} - -///////////////////////// -// RegisterStackAdjustment - -#[derive(Debug, Copy, Clone)] -pub struct RegisterStackAdjustment { - reg_id: u32, - adjustment: Conf, - arch: A::Handle, -} - -impl RegisterStackAdjustment { - pub(crate) unsafe fn from_raw(value: BNRegisterStackAdjustment, arch: A::Handle) -> Self { - RegisterStackAdjustment { - reg_id: value.regStack, - adjustment: Conf::new(value.adjustment, value.confidence), - arch, - } - } - pub(crate) fn into_raw(self) -> BNRegisterStackAdjustment { - BNRegisterStackAdjustment { - regStack: self.reg_id, - adjustment: self.adjustment.contents, - confidence: self.adjustment.confidence, - } - } - pub fn new(reg_id: u32, adjustment: I, arch_handle: A::Handle) -> Self - where - I: Into>, - { - Self { - reg_id, - adjustment: adjustment.into(), - arch: arch_handle, - } - } - pub const fn register_id(&self) -> u32 { - self.reg_id - } - pub fn register(&self) -> A::Register { - self.arch.borrow().register_from_id(self.reg_id).unwrap() - } -} - -impl CoreArrayProvider for RegisterStackAdjustment { - type Raw = BNRegisterStackAdjustment; - type Context = A::Handle; - type Wrapped<'a> = Self; -} - -unsafe impl CoreArrayProviderInner for RegisterStackAdjustment { - unsafe fn free(raw: *mut Self::Raw, _count: usize, _context: &Self::Context) { - BNFreeRegisterStackAdjustments(raw) - } - unsafe fn wrap_raw<'a>(raw: &'a Self::Raw, context: &'a Self::Context) -> Self::Wrapped<'a> { - Self::from_raw(*raw, context.clone()) - } -} - -///////////////////////// -// RegisterStackAdjustment - -// NOTE only exists as part of an Array, never owned -pub struct MergedVariable { - target: Variable, - // droped by the CoreArrayProviderInner::free - sources: ManuallyDrop>, -} - -impl MergedVariable { - pub fn target(&self) -> Variable { - self.target - } - pub fn sources(&self) -> &Array { - &self.sources - } -} - -impl CoreArrayProvider for MergedVariable { - type Raw = BNMergedVariable; - type Context = (); - type Wrapped<'a> = Self; -} - -unsafe impl CoreArrayProviderInner for MergedVariable { - unsafe fn free(raw: *mut Self::Raw, count: usize, _context: &Self::Context) { - BNFreeMergedVariableList(raw, count) - } - unsafe fn wrap_raw<'a>(raw: &'a Self::Raw, _context: &'a Self::Context) -> Self::Wrapped<'a> { - Self { - target: Variable::from_raw(raw.target), - sources: ManuallyDrop::new(Array::new(raw.sources, raw.sourceCount, ())), - } - } -} - -///////////////////////// -// UnresolvedIndirectBranches - -// NOTE only exists as part of an Array, never owned -pub struct UnresolvedIndirectBranches(u64); - -impl UnresolvedIndirectBranches { - pub fn address(&self) -> u64 { - self.0 - } -} - -impl CoreArrayProvider for UnresolvedIndirectBranches { - type Raw = u64; - type Context = (); - type Wrapped<'a> = Self; -} - -unsafe impl CoreArrayProviderInner for UnresolvedIndirectBranches { - unsafe fn free(raw: *mut Self::Raw, _count: usize, _context: &Self::Context) { - BNFreeAddressList(raw) - } - unsafe fn wrap_raw<'a>(raw: &'a Self::Raw, _context: &'a Self::Context) -> Self::Wrapped<'a> { - Self(*raw) + NameAndType::from(*raw) } } diff --git a/rust/src/variable.rs b/rust/src/variable.rs new file mode 100644 index 000000000..56b687cc8 --- /dev/null +++ b/rust/src/variable.rs @@ -0,0 +1,842 @@ +use crate::function::{Function, Location}; +use crate::mlil::MediumLevelILFunction; +use crate::rc::{Array, CoreArrayProvider, CoreArrayProviderInner, Guard, Ref}; +use crate::string::BnString; +use crate::types::Type; +use binaryninjacore_sys::{BNDataVariable, BNDataVariableAndName, BNFreeDataVariables, BNFreeILInstructionList, BNFreeIndirectBranchList, BNFreeMergedVariableList, BNFreeStackVariableReferenceList, BNFreeUserVariableValues, BNFreeVariableList, BNFreeVariableNameAndTypeList, BNFromVariableIdentifier, BNGetMediumLevelILVariableSSAVersions, BNIndirectBranchInfo, BNLookupTableEntry, BNMergedVariable, BNPossibleValueSet, BNRegisterValue, BNRegisterValueType, BNStackVariableReference, BNToVariableIdentifier, BNUserVariableValue, BNValueRange, BNVariable, BNVariableNameAndType, BNVariableSourceType}; +use std::collections::HashSet; +use crate::confidence::Conf; + +pub type VariableSourceType = BNVariableSourceType; +pub type RegisterValueType = BNRegisterValueType; + +#[derive(Debug, Clone, Hash, PartialEq, Eq)] +pub struct DataVariable { + pub address: u64, + pub ty: Conf>, + pub auto_discovered: bool, +} + +impl DataVariable { + pub fn new(address: u64, ty: Conf>, auto_discovered: bool) -> Self { + Self { + address, + ty, + auto_discovered, + } + } +} + +impl From for DataVariable { + fn from(value: BNDataVariable) -> Self { + Self { + address: value.address, + ty: Conf::new( + unsafe { Type::ref_from_raw(value.type_) }, + value.typeConfidence, + ), + auto_discovered: value.autoDiscovered, + } + } +} + +impl CoreArrayProvider for DataVariable { + type Raw = BNDataVariable; + type Context = (); + type Wrapped<'a> = Self; +} + +unsafe impl CoreArrayProviderInner for DataVariable { + unsafe fn free(raw: *mut Self::Raw, count: usize, _context: &Self::Context) { + BNFreeDataVariables(raw, count); + } + + unsafe fn wrap_raw<'a>(raw: &'a Self::Raw, _context: &'a Self::Context) -> Self::Wrapped<'a> { + Self::from(*raw) + } +} + +#[derive(Debug, Clone, Hash, PartialEq, Eq)] +pub struct NamedDataVariableWithType { + pub address: u64, + pub ty: Conf>, + pub name: String, + pub auto_discovered: bool, +} + +impl NamedDataVariableWithType { + pub fn new(address: u64, ty: Conf>, name: String, auto_discovered: bool) -> Self { + Self { + address, + ty, + name, + auto_discovered, + } + } +} + +impl From for NamedDataVariableWithType { + fn from(value: BNDataVariableAndName) -> Self { + Self { + address: value.address, + ty: Conf::new( + unsafe { Type::ref_from_raw(value.type_) }, + value.typeConfidence, + ), + name: unsafe { BnString::from_raw(value.name) }.to_string(), + auto_discovered: value.autoDiscovered, + } + } +} + +impl From for BNDataVariableAndName { + fn from(value: NamedDataVariableWithType) -> Self { + let bn_name = BnString::new(value.name); + Self { + address: value.address, + type_: value.ty.contents.handle, + name: bn_name.into_raw(), + autoDiscovered: value.auto_discovered, + typeConfidence: value.ty.confidence, + } + } +} + +#[derive(Debug, Clone, Hash, PartialEq, Eq)] +pub struct NamedVariableWithType { + pub variable: Variable, + pub ty: Conf>, + pub name: String, + pub auto_defined: bool, +} + +impl NamedVariableWithType { + pub fn new(variable: Variable, ty: Conf>, name: String, auto_defined: bool) -> Self { + Self { + variable, + ty, + name, + auto_defined, + } + } +} + +impl From for NamedVariableWithType { + fn from(value: BNVariableNameAndType) -> Self { + Self { + variable: value.var.into(), + ty: Conf::new( + unsafe { Type::ref_from_raw(value.type_) }, + value.typeConfidence, + ), + name: unsafe { BnString::from_raw(value.name) }.to_string(), + auto_defined: value.autoDefined, + } + } +} + +impl From for BNVariableNameAndType { + fn from(value: NamedVariableWithType) -> Self { + let bn_name = BnString::new(value.name); + Self { + var: value.variable.into(), + type_: value.ty.contents.handle, + name: bn_name.into_raw(), + autoDefined: value.auto_defined, + typeConfidence: value.ty.confidence, + } + } +} + +impl CoreArrayProvider for NamedVariableWithType { + type Raw = BNVariableNameAndType; + type Context = (); + type Wrapped<'a> = Guard<'a, NamedVariableWithType>; +} + +unsafe impl CoreArrayProviderInner for NamedVariableWithType { + unsafe fn free(raw: *mut Self::Raw, count: usize, _context: &Self::Context) { + BNFreeVariableNameAndTypeList(raw, count) + } + + unsafe fn wrap_raw<'a>(raw: &'a Self::Raw, _context: &'a Self::Context) -> Self::Wrapped<'a> { + unsafe { Guard::new(NamedVariableWithType::from(*raw), raw) } + } +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct UserVariableValue { + pub variable: Variable, + pub def_site: Location, + pub value: PossibleValueSet, +} + +impl From for UserVariableValue { + fn from(value: BNUserVariableValue) -> Self { + Self { + variable: value.var.into(), + def_site: value.defSite.into(), + value: value.value.into(), + } + } +} + +impl From for BNUserVariableValue { + fn from(value: UserVariableValue) -> Self { + Self { + var: value.variable.into(), + defSite: value.def_site.into(), + value: value.value.into(), + } + } +} + +impl CoreArrayProvider for UserVariableValue { + type Raw = BNUserVariableValue; + type Context = (); + type Wrapped<'a> = Self; +} + +unsafe impl CoreArrayProviderInner for UserVariableValue { + unsafe fn free(raw: *mut Self::Raw, _count: usize, _context: &Self::Context) { + BNFreeUserVariableValues(raw) + } + + unsafe fn wrap_raw<'a>(raw: &'a Self::Raw, _context: &'a Self::Context) -> Self::Wrapped<'a> { + UserVariableValue::from(*raw) + } +} + +#[derive(Debug, Clone, Hash, PartialEq, Eq)] +pub struct StackVariableReference { + source_operand: u32, + pub variable_type: Conf>, + pub name: String, + pub variable: Variable, + pub offset: i64, + pub size: usize, +} + +impl From for StackVariableReference { + fn from(value: BNStackVariableReference) -> Self { + Self { + source_operand: value.sourceOperand, + variable_type: Conf::new( + unsafe { Type::ref_from_raw(value.type_) }, + value.typeConfidence, + ), + name: unsafe { BnString::from_raw(value.name) }.to_string(), + // TODO: It might be beneficial to newtype the identifier as VariableIdentifier. + variable: Variable::from_identifier(value.varIdentifier), + offset: value.referencedOffset, + size: value.size, + } + } +} + +impl From for BNStackVariableReference { + fn from(value: StackVariableReference) -> Self { + let bn_name = BnString::new(value.name); + Self { + sourceOperand: value.source_operand, + typeConfidence: value.variable_type.confidence, + type_: value.variable_type.contents.handle, + name: bn_name.into_raw(), + varIdentifier: value.variable.to_identifier(), + referencedOffset: value.offset, + size: value.size, + } + } +} + +impl CoreArrayProvider for StackVariableReference { + type Raw = BNStackVariableReference; + type Context = (); + type Wrapped<'a> = Guard<'a, Self>; +} + +unsafe impl CoreArrayProviderInner for StackVariableReference { + unsafe fn free(raw: *mut Self::Raw, count: usize, _context: &Self::Context) { + BNFreeStackVariableReferenceList(raw, count) + } + + unsafe fn wrap_raw<'a>(raw: &'a Self::Raw, context: &'a Self::Context) -> Self::Wrapped<'a> { + Guard::new(Self::from(*raw), context) + } +} + +#[derive(Clone, Copy, Debug, Hash, Eq, PartialEq)] +pub struct SSAVariable { + pub variable: Variable, + pub version: usize, +} + +impl SSAVariable { + pub fn new(variable: Variable, version: usize) -> Self { + Self { variable, version } + } +} + +impl CoreArrayProvider for SSAVariable { + type Raw = usize; + type Context = Variable; + type Wrapped<'a> = Self; +} + +unsafe impl CoreArrayProviderInner for SSAVariable { + unsafe fn free(raw: *mut Self::Raw, _count: usize, _context: &Self::Context) { + BNFreeILInstructionList(raw) + } + + unsafe fn wrap_raw<'a>(raw: &'a Self::Raw, context: &'a Self::Context) -> Self::Wrapped<'a> { + SSAVariable::new(*context, *raw) + } +} + +impl CoreArrayProvider for Array { + type Raw = BNVariable; + type Context = Ref; + type Wrapped<'a> = Self; +} + +unsafe impl CoreArrayProviderInner for Array { + unsafe fn free(raw: *mut Self::Raw, _count: usize, _context: &Self::Context) { + BNFreeVariableList(raw) + } + + unsafe fn wrap_raw<'a>(raw: &'a Self::Raw, context: &'a Self::Context) -> Self::Wrapped<'a> { + let mut count = 0; + let versions = + unsafe { BNGetMediumLevelILVariableSSAVersions(context.handle, raw, &mut count) }; + Array::new(versions, count, Variable::from(*raw)) + } +} + +/// Variables exist within functions at Medium Level IL or higher. +/// +/// As such, they are to be used within the context of a [`crate::Function`]. +/// See [`crate::Function::get_variable_name`] as an example of how to interact with variables. +#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)] +pub struct Variable { + pub ty: VariableSourceType, + pub index: u32, + // TODO: Type this to `VariableStorage` + pub storage: i64, +} + +impl Variable { + pub fn new(ty: VariableSourceType, index: u32, storage: i64) -> Self { + Self { ty, index, storage } + } + + // TODO: Retype this... + // TODO: Add VariableIdentifier + // TODO: StackVariableReference has a varIdentifier, i think thats really it. + pub fn from_identifier(ident: u64) -> Self { + unsafe { BNFromVariableIdentifier(ident) }.into() + } + + pub fn to_identifier(&self) -> u64 { + let raw = BNVariable::from(*self); + unsafe { BNToVariableIdentifier(&raw) } + } +} + +impl From for Variable { + fn from(value: BNVariable) -> Self { + Self { + ty: value.type_, + index: value.index, + storage: value.storage, + } + } +} + +impl From for BNVariable { + fn from(value: Variable) -> Self { + Self { + type_: value.ty, + index: value.index, + storage: value.storage, + } + } +} + +impl From<&Variable> for BNVariable { + fn from(value: &Variable) -> Self { + BNVariable::from(*value) + } +} + +impl CoreArrayProvider for Variable { + type Raw = BNVariable; + type Context = (); + type Wrapped<'a> = Self; +} + +unsafe impl CoreArrayProviderInner for Variable { + unsafe fn free(raw: *mut Self::Raw, _count: usize, _context: &Self::Context) { + BNFreeVariableList(raw) + } + + unsafe fn wrap_raw<'a>(raw: &'a Self::Raw, _context: &'a Self::Context) -> Self::Wrapped<'a> { + Variable::from(*raw) + } +} + +#[derive(Debug, Clone, Hash, PartialEq, Eq)] +pub struct MergedVariable { + pub target: Variable, + pub sources: Vec, +} + +impl From for MergedVariable { + fn from(value: BNMergedVariable) -> Self { + let sources = unsafe { + std::slice::from_raw_parts(value.sources, value.sourceCount) + .iter() + .copied() + .map(Into::into) + .collect() + }; + Self { + target: value.target.into(), + sources, + } + } +} + +impl CoreArrayProvider for MergedVariable { + type Raw = BNMergedVariable; + type Context = (); + type Wrapped<'a> = Self; +} + +unsafe impl CoreArrayProviderInner for MergedVariable { + unsafe fn free(raw: *mut Self::Raw, count: usize, _context: &Self::Context) { + BNFreeMergedVariableList(raw, count) + } + + unsafe fn wrap_raw<'a>(raw: &'a Self::Raw, _context: &'a Self::Context) -> Self::Wrapped<'a> { + Self::from(*raw) + } +} + +// TODO: This is used in MLIL and HLIL, this really should exist in each of those. +#[derive(Clone, Debug, Hash, Eq, PartialEq)] +pub struct ConstantData { + // TODO: We really do not want to store a ref to function here. + pub function: Ref, + pub value: RegisterValue, +} + +impl ConstantData { + pub fn new(function: Ref, value: RegisterValue) -> Self { + Self { function, value } + } +} + +#[derive(Clone, Copy, Debug, Hash, Eq, PartialEq)] +pub struct RegisterValue { + pub state: RegisterValueType, + // TODO: This value can be anything. Make `T` + pub value: i64, + pub offset: i64, + pub size: usize, +} + +impl RegisterValue { + pub fn new(state: RegisterValueType, value: i64, offset: i64, size: usize) -> Self { + Self { + state, + value, + offset, + size, + } + } +} + +impl From for RegisterValue { + fn from(value: BNRegisterValue) -> Self { + Self { + state: value.state.into(), + value: value.value, + offset: value.offset, + size: value.size, + } + } +} + +impl From for BNRegisterValue { + fn from(value: RegisterValue) -> Self { + Self { + state: value.state.into(), + value: value.value, + offset: value.offset, + size: value.size, + } + } +} + +#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)] +pub struct ValueRange { + pub start: T, + pub end: T, + pub step: u64, +} + +impl From for ValueRange { + fn from(value: BNValueRange) -> Self { + Self { + start: value.start, + end: value.end, + step: value.step, + } + } +} + +impl From> for BNValueRange { + fn from(value: ValueRange) -> Self { + Self { + start: value.start, + end: value.end, + step: value.step, + } + } +} + +impl From for ValueRange { + fn from(value: BNValueRange) -> Self { + Self { + start: value.start as i64, + end: value.end as i64, + step: value.step, + } + } +} + +impl From> for BNValueRange { + fn from(value: ValueRange) -> Self { + Self { + start: value.start as u64, + end: value.end as u64, + step: value.step, + } + } +} + +// TODO: Document where its used and why it exists. +// TODO: What if we are looking up u64? +#[derive(Clone, Debug, Eq, PartialEq)] +pub struct LookupTableEntry { + /// The set of integers that correspond with [`Self::to`]. + from: HashSet, + /// The associated "mapped" value. + to: i64, +} + +impl From for LookupTableEntry { + fn from(value: BNLookupTableEntry) -> Self { + let from_values = unsafe { std::slice::from_raw_parts(value.fromValues, value.fromCount) }; + Self { + // TODO: Better way to construct HashSet? + from: from_values.iter().copied().collect(), + to: value.toValue, + } + } +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub enum PossibleValueSet { + UndeterminedValue, + EntryValue { + // TODO: This is actually the BNVariable storage. + // TODO: Type this to `VariableStorage` or something. + reg: i64, + }, + ConstantValue { + // TODO: Make this T + // TODO: This can be really anything (signed, unsigned or even a float). + value: i64, + }, + ConstantPointerValue { + // TODO: Shouldn't this be u64? + value: i64, + }, + ExternalPointerValue { + // TODO: Shouldn't this be u64? + value: i64, + offset: i64, + }, + StackFrameOffset { + value: i64, + }, + ReturnAddressValue, + ImportedAddressValue, + SignedRangeValue { + value: i64, + ranges: Vec>, + }, + UnsignedRangeValue { + value: i64, + ranges: Vec>, + }, + LookupTableValue { + table: Vec, + }, + InSetOfValues { + values: HashSet, + }, + NotInSetOfValues { + values: HashSet, + }, + // TODO: Can you even get _just_ a constant data value? + ConstantDataValue { + value: i64, + size: usize, + }, + ConstantDataZeroExtendValue { + // TODO: Zero extend should be u64? + value: i64, + size: usize, + }, + ConstantDataSignExtendValue { + value: i64, + size: usize, + }, + ConstantDataAggregateValue { + // WTF is aggregate?? + value: i64, + size: usize, + }, +} + +impl PossibleValueSet { + pub fn value_type(&self) -> RegisterValueType { + match self { + PossibleValueSet::UndeterminedValue => RegisterValueType::UndeterminedValue, + PossibleValueSet::EntryValue { .. } => RegisterValueType::EntryValue, + PossibleValueSet::ConstantValue { .. } => RegisterValueType::ConstantValue, + PossibleValueSet::ConstantPointerValue { .. } => { + RegisterValueType::ConstantPointerValue + } + PossibleValueSet::ExternalPointerValue { .. } => { + RegisterValueType::ExternalPointerValue + } + PossibleValueSet::StackFrameOffset { .. } => RegisterValueType::StackFrameOffset, + PossibleValueSet::ReturnAddressValue => RegisterValueType::ReturnAddressValue, + PossibleValueSet::ImportedAddressValue => RegisterValueType::ImportedAddressValue, + PossibleValueSet::SignedRangeValue { .. } => RegisterValueType::SignedRangeValue, + PossibleValueSet::UnsignedRangeValue { .. } => RegisterValueType::UnsignedRangeValue, + PossibleValueSet::LookupTableValue { .. } => RegisterValueType::LookupTableValue, + PossibleValueSet::InSetOfValues { .. } => RegisterValueType::InSetOfValues, + PossibleValueSet::NotInSetOfValues { .. } => RegisterValueType::NotInSetOfValues, + PossibleValueSet::ConstantDataValue { .. } => RegisterValueType::ConstantDataValue, + PossibleValueSet::ConstantDataZeroExtendValue { .. } => { + RegisterValueType::ConstantDataZeroExtendValue + } + PossibleValueSet::ConstantDataSignExtendValue { .. } => { + RegisterValueType::ConstantDataSignExtendValue + } + PossibleValueSet::ConstantDataAggregateValue { .. } => { + RegisterValueType::ConstantDataAggregateValue + } + } + } +} + +impl From for PossibleValueSet { + fn from(value: BNPossibleValueSet) -> Self { + match value.state { + RegisterValueType::UndeterminedValue => Self::UndeterminedValue, + RegisterValueType::EntryValue => Self::EntryValue { reg: value.value }, + RegisterValueType::ConstantValue => Self::ConstantValue { value: value.value }, + RegisterValueType::ConstantPointerValue => { + Self::ConstantPointerValue { value: value.value } + } + RegisterValueType::ExternalPointerValue => Self::ExternalPointerValue { + value: value.value, + offset: value.offset, + }, + RegisterValueType::StackFrameOffset => Self::StackFrameOffset { value: value.value }, + RegisterValueType::ReturnAddressValue => Self::ReturnAddressValue, + RegisterValueType::ImportedAddressValue => Self::ImportedAddressValue, + RegisterValueType::SignedRangeValue => { + let raw_ranges = unsafe { std::slice::from_raw_parts(value.ranges, value.count) }; + Self::SignedRangeValue { + value: value.value, + ranges: raw_ranges.iter().map(|&r| r.into()).collect(), + } + } + RegisterValueType::UnsignedRangeValue => { + let raw_ranges = unsafe { std::slice::from_raw_parts(value.ranges, value.count) }; + Self::UnsignedRangeValue { + value: value.value, + ranges: raw_ranges.iter().map(|&r| r.into()).collect(), + } + } + RegisterValueType::LookupTableValue => { + let raw_entries = unsafe { std::slice::from_raw_parts(value.table, value.count) }; + Self::LookupTableValue { + table: raw_entries.iter().map(|&r| r.into()).collect(), + } + } + RegisterValueType::InSetOfValues => { + let raw_values = unsafe { std::slice::from_raw_parts(value.valueSet, value.count) }; + Self::InSetOfValues { + values: raw_values.iter().copied().collect(), + } + } + RegisterValueType::NotInSetOfValues => { + let raw_values = unsafe { std::slice::from_raw_parts(value.valueSet, value.count) }; + Self::NotInSetOfValues { + values: raw_values.iter().copied().collect(), + } + } + RegisterValueType::ConstantDataValue => Self::ConstantDataValue { + value: value.value, + size: value.size, + }, + RegisterValueType::ConstantDataZeroExtendValue => Self::ConstantDataZeroExtendValue { + value: value.value, + size: value.size, + }, + RegisterValueType::ConstantDataSignExtendValue => Self::ConstantDataSignExtendValue { + value: value.value, + size: value.size, + }, + RegisterValueType::ConstantDataAggregateValue => Self::ConstantDataAggregateValue { + value: value.value, + size: value.size, + }, + } + } +} + +// TODO: Anything requiring core allocation is missing! +impl From for BNPossibleValueSet { + fn from(value: PossibleValueSet) -> Self { + let mut raw = BNPossibleValueSet::default(); + raw.state = value.value_type().into(); + match value { + PossibleValueSet::UndeterminedValue => {} + PossibleValueSet::EntryValue { reg } => { + raw.value = reg; + } + PossibleValueSet::ConstantValue { value } => { + raw.value = value; + } + PossibleValueSet::ConstantPointerValue { value } => { + raw.value = value; + } + PossibleValueSet::ExternalPointerValue { value, offset } => { + raw.value = value; + raw.offset = offset; + } + PossibleValueSet::StackFrameOffset { value } => { + raw.value = value; + } + PossibleValueSet::ReturnAddressValue => {} + PossibleValueSet::ImportedAddressValue => {} + PossibleValueSet::SignedRangeValue { value, .. } => { + raw.value = value; + // TODO: raw.ranges + // TODO: requires core allocation and freeing. + // TODO: See `BNFreePossibleValueSet` for why this sucks. + } + PossibleValueSet::UnsignedRangeValue { value, .. } => { + raw.value = value; + // TODO: raw.ranges + // TODO: requires core allocation and freeing. + // TODO: See `BNFreePossibleValueSet` for why this sucks. + } + PossibleValueSet::LookupTableValue { .. } => { + // TODO: raw.table + // TODO: requires core allocation and freeing. + // TODO: See `BNFreePossibleValueSet` for why this sucks. + } + PossibleValueSet::InSetOfValues { .. } => { + // TODO: raw.valueSet + // TODO: requires core allocation and freeing. + // TODO: See `BNFreePossibleValueSet` for why this sucks. + } + PossibleValueSet::NotInSetOfValues { .. } => { + // TODO: raw.valueSet + // TODO: requires core allocation and freeing. + // TODO: See `BNFreePossibleValueSet` for why this sucks. + } + PossibleValueSet::ConstantDataValue { value, size } => { + raw.value = value; + raw.size = size; + } + PossibleValueSet::ConstantDataZeroExtendValue { value, size } => { + raw.value = value; + raw.size = size; + } + PossibleValueSet::ConstantDataSignExtendValue { value, size } => { + raw.value = value; + raw.size = size; + } + PossibleValueSet::ConstantDataAggregateValue { value, size } => { + raw.value = value; + raw.size = size; + } + }; + raw + } +} + +#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)] +pub struct IndirectBranchInfo { + pub source: Location, + pub dest: Location, + pub auto_defined: bool, +} + +impl From for IndirectBranchInfo { + fn from(value: BNIndirectBranchInfo) -> Self { + Self { + source: Location::from_raw(value.sourceAddr, value.sourceArch), + dest: Location::from_raw(value.destAddr, value.destArch), + auto_defined: value.autoDefined, + } + } +} + +impl From for BNIndirectBranchInfo { + fn from(value: IndirectBranchInfo) -> Self { + let source_arch = value + .source + .arch + .map(|a| a.handle) + .unwrap_or(std::ptr::null_mut()); + let dest_arch = value + .source + .arch + .map(|a| a.handle) + .unwrap_or(std::ptr::null_mut()); + Self { + sourceArch: source_arch, + sourceAddr: value.source.addr, + destArch: dest_arch, + destAddr: value.dest.addr, + autoDefined: value.auto_defined, + } + } +} + +impl CoreArrayProvider for IndirectBranchInfo { + type Raw = BNIndirectBranchInfo; + type Context = (); + type Wrapped<'a> = Self; +} + +unsafe impl CoreArrayProviderInner for IndirectBranchInfo { + unsafe fn free(raw: *mut Self::Raw, _count: usize, _context: &Self::Context) { + BNFreeIndirectBranchList(raw) + } + + unsafe fn wrap_raw<'a>(raw: &'a Self::Raw, _context: &'a Self::Context) -> Self::Wrapped<'a> { + Self::from(*raw) + } +} diff --git a/rust/src/workflow.rs b/rust/src/workflow.rs index 122536447..c28c530b3 100644 --- a/rust/src/workflow.rs +++ b/rust/src/workflow.rs @@ -34,14 +34,14 @@ impl AnalysisContext { pub fn view(&self) -> Ref { let result = unsafe { BNAnalysisContextGetBinaryView(self.handle.as_ptr()) }; assert!(!result.is_null()); - unsafe { BinaryView::from_raw(result) } + unsafe { BinaryView::ref_from_raw(result) } } /// Function for the current AnalysisContext pub fn function(&self) -> Ref { let result = unsafe { BNAnalysisContextGetFunction(self.handle.as_ptr()) }; assert!(!result.is_null()); - unsafe { Function::from_raw(result) } + unsafe { Function::ref_from_raw(result) } } /// LowLevelILFunction used to represent Low Level IL From e419b932eba25705329fba411da703542472f1e6 Mon Sep 17 00:00:00 2001 From: Mason Reed Date: Sun, 22 Dec 2024 01:53:45 -0500 Subject: [PATCH 02/93] More rust cleanup - Fixed invalid views being able to invoke UB when dealing with databases - Cleaned up some helper code in dwarf_import - Fixed inverted is_null checks causing crashes! Oops! --- .../dwarf/dwarf_import/src/helpers.rs | 46 +++++++++---------- rust/src/binaryview.rs | 24 +++++----- rust/src/filemetadata.rs | 26 +++++++---- 3 files changed, 50 insertions(+), 46 deletions(-) diff --git a/rust/examples/dwarf/dwarf_import/src/helpers.rs b/rust/examples/dwarf/dwarf_import/src/helpers.rs index 20e8dddc4..6109a13d5 100644 --- a/rust/examples/dwarf/dwarf_import/src/helpers.rs +++ b/rust/examples/dwarf/dwarf_import/src/helpers.rs @@ -494,31 +494,29 @@ pub(crate) fn find_local_debug_file_for_build_id(build_id: &String, view: &Binar } for debug_info_path in debug_info_paths.into_iter() { - if let Ok(path) = PathBuf::from_str(&debug_info_path.to_string()) - { - let elf_path = path - .join(&build_id[..2]) - .join(&build_id[2..]) - .join("elf"); - - let debug_ext_path = path - .join(&build_id[..2]) - .join(format!("{}.debug", &build_id[2..])); - - let final_path = if debug_ext_path.exists() { - debug_ext_path - } - else if elf_path.exists() { - elf_path - } - else { - // No paths exist in this dir, try the next one - continue; - }; - return final_path - .to_str() - .and_then(|x| Some(x.to_string())); + let path = PathBuf::from(debug_info_path); + let elf_path = path + .join(&build_id[..2]) + .join(&build_id[2..]) + .join("elf"); + + let debug_ext_path = path + .join(&build_id[..2]) + .join(format!("{}.debug", &build_id[2..])); + + let final_path = if debug_ext_path.exists() { + debug_ext_path + } + else if elf_path.exists() { + elf_path } + else { + // No paths exist in this dir, try the next one + continue; + }; + return final_path + .to_str() + .and_then(|x| Some(x.to_string())); } None } diff --git a/rust/src/binaryview.rs b/rust/src/binaryview.rs index 1decbc21e..8a3554ab8 100644 --- a/rust/src/binaryview.rs +++ b/rust/src/binaryview.rs @@ -198,8 +198,8 @@ pub trait BinaryViewExt: BinaryViewBase { fn parent_view(&self) -> Option> { let raw_view_ptr = unsafe { BNGetParentView(self.as_ref().handle) }; match raw_view_ptr.is_null() { - false => None, - true => Some(unsafe { BinaryView::ref_from_raw(raw_view_ptr) }), + false => Some(unsafe { BinaryView::ref_from_raw(raw_view_ptr) }), + true => None, } } @@ -396,8 +396,8 @@ pub trait BinaryViewExt: BinaryViewBase { unsafe { let raw_sym_ptr = BNGetSymbolByAddress(self.as_ref().handle, addr, ptr::null_mut()); match raw_sym_ptr.is_null() { - false => None, - true => Some(Symbol::ref_from_raw(raw_sym_ptr)), + false => Some(Symbol::ref_from_raw(raw_sym_ptr)), + true => None, } } } @@ -412,8 +412,8 @@ pub trait BinaryViewExt: BinaryViewBase { ptr::null_mut(), ); match raw_sym_ptr.is_null() { - false => None, - true => Some(Symbol::ref_from_raw(raw_sym_ptr)), + false => Some(Symbol::ref_from_raw(raw_sym_ptr)), + true => None, } } } @@ -902,8 +902,8 @@ pub trait BinaryViewExt: BinaryViewBase { let name_ptr = raw_name.as_ref().as_ptr() as *mut _; let raw_section_ptr = BNGetSectionByName(self.as_ref().handle, name_ptr); match raw_section_ptr.is_null() { - false => None, - true => Some(Section::ref_from_raw(raw_section_ptr)), + false => Some(Section::ref_from_raw(raw_section_ptr)), + true => None, } } } @@ -999,8 +999,8 @@ pub trait BinaryViewExt: BinaryViewBase { unsafe { let raw_func_ptr = BNGetAnalysisEntryPoint(self.as_ref().handle); match raw_func_ptr.is_null() { - false => None, - true => Some(Function::ref_from_raw(raw_func_ptr)), + false => Some(Function::ref_from_raw(raw_func_ptr)), + true => None, } } } @@ -1049,8 +1049,8 @@ pub trait BinaryViewExt: BinaryViewBase { unsafe { let raw_func_ptr = BNGetAnalysisFunction(self.as_ref().handle, platform.handle, addr); match raw_func_ptr.is_null() { - false => None, - true => Some(Function::ref_from_raw(raw_func_ptr)), + false => Some(Function::ref_from_raw(raw_func_ptr)), + true => None, } } } diff --git a/rust/src/filemetadata.rs b/rust/src/filemetadata.rs index aa72d7114..15c7d5dfc 100644 --- a/rust/src/filemetadata.rs +++ b/rust/src/filemetadata.rs @@ -179,8 +179,8 @@ impl FileMetadata { unsafe { let raw_view_ptr = BNGetFileViewOfType(self.handle, view.as_ref().as_ptr() as *const _); match raw_view_ptr.is_null() { - false => None, - true => Some(BinaryView::ref_from_raw(raw_view_ptr)), + false => Some(BinaryView::ref_from_raw(raw_view_ptr)), + true => None, } } } @@ -199,15 +199,17 @@ impl FileMetadata { ) -> bool { let filename = filename.into_bytes_with_nul(); let filename_ptr = filename.as_ref().as_ptr() as *mut _; - let raw = "Raw".into_bytes_with_nul(); - let raw_ptr = raw.as_ptr() as *mut _; - - let handle = unsafe { BNGetFileViewOfType(self.handle, raw_ptr) }; + + // Databases are created with the root view (Raw). + let Some(raw_view) = self.get_view_of_type("Raw") else { + return false; + }; + match progress_func { - None => unsafe { BNCreateDatabase(handle, filename_ptr, ptr::null_mut()) }, + None => unsafe { BNCreateDatabase(raw_view.handle, filename_ptr, ptr::null_mut()) }, Some(func) => unsafe { BNCreateDatabaseWithProgress( - handle, + raw_view.handle, filename_ptr, func as *mut c_void, Some(cb_progress_func), @@ -218,10 +220,14 @@ impl FileMetadata { } pub fn save_auto_snapshot(&self) -> bool { - let raw = "Raw".into_bytes_with_nul(); + // Snapshots are saved with the root view (Raw). + let Some(raw_view) = self.get_view_of_type("Raw") else { + return false; + }; + unsafe { BNSaveAutoSnapshot( - BNGetFileViewOfType(self.handle, raw.as_ptr() as *mut _), + raw_view.handle, ptr::null_mut() as *mut _, ) } From 5ab10d5182316378e079f6f1b74e0553b5a2eb25 Mon Sep 17 00:00:00 2001 From: Mason Reed Date: Tue, 24 Dec 2024 00:23:37 -0500 Subject: [PATCH 03/93] More rust cleanup Still a WIP, I think branch info is still invalid, need to figure out the issue there. - Fixed some invalid Ref lifetimes when constructing indirectly, see Array for example - Added some more comments - Removed some "magic" functions like MLIL Function::ssa_variables There are still a bunch of invalid lifetimes that aren't crashing us due to the usage of those API's not living long enough. But they ARE an issue. --- rust/examples/dwarf/shared/src/lib.rs | 2 +- rust/src/architecture.rs | 13 +- rust/src/basicblock.rs | 7 + rust/src/binaryview.rs | 8 + rust/src/callingconvention.rs | 1 + rust/src/debuginfo.rs | 1 + rust/src/disassembly.rs | 54 ++++--- rust/src/downloadprovider.rs | 1 + rust/src/function.rs | 206 +++++++++++++------------- rust/src/functionrecognizer.rs | 26 ++-- rust/src/linearview.rs | 6 +- rust/src/llil/function.rs | 1 + rust/src/metadata.rs | 4 +- rust/src/mlil/function.rs | 127 ++++++++-------- rust/src/platform.rs | 5 +- rust/src/references.rs | 1 + rust/src/relocation.rs | 1 + rust/src/section.rs | 1 + rust/src/segment.rs | 1 + rust/src/string.rs | 1 + rust/src/symbol.rs | 1 + rust/src/tags.rs | 45 ++---- rust/src/types.rs | 80 ++++++++-- rust/src/variable.rs | 109 ++++++++++---- 24 files changed, 421 insertions(+), 281 deletions(-) diff --git a/rust/examples/dwarf/shared/src/lib.rs b/rust/examples/dwarf/shared/src/lib.rs index 6bd597c76..c5fa28f44 100644 --- a/rust/examples/dwarf/shared/src/lib.rs +++ b/rust/examples/dwarf/shared/src/lib.rs @@ -114,7 +114,7 @@ pub fn create_section_reader<'a, Endian: 'a + Endianity>( .find(|var| var.address == symbol.address()) { // TODO : This should eventually be wrapped by some DataView sorta thingy thing, like how python does it - let data_type = data_var.ty.contents; + let data_type = &data_var.ty.contents; let data = view.read_vec(data_var.address, data_type.width() as usize); let element_type = data_type.element_type().unwrap().contents; diff --git a/rust/src/architecture.rs b/rust/src/architecture.rs index 8fbf896d7..573d4a240 100644 --- a/rust/src/architecture.rs +++ b/rust/src/architecture.rs @@ -67,6 +67,7 @@ pub enum BranchKind { #[derive(Default, Copy, Clone, PartialEq, Eq, Hash, Debug)] pub struct BranchInfo { + /// If `None` the target architecture is the same as the branch instruction. pub arch: Option, pub kind: BranchKind, } @@ -127,15 +128,20 @@ impl From for BranchInfo { } } +/// This is the number of branches that can be specified in an [`InstructionInfo`]. +pub const NUM_BRANCH_INFO: usize = 3; + #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] pub struct InstructionInfo { pub length: usize, + // TODO: This field name is really long... pub arch_transition_by_target_addr: bool, pub delay_slots: u8, - pub branches: [Option; 3] + pub branches: [Option; NUM_BRANCH_INFO] } impl InstructionInfo { + // TODO: `new_with_delay_slot`? pub fn new(length: usize, delay_slots: u8) -> Self { Self { length, @@ -160,8 +166,8 @@ impl InstructionInfo { impl From for InstructionInfo { fn from(value: BNInstructionInfo) -> Self { // TODO: This is quite ugly, but we destructure the branch info so this will have to do. - let mut branch_info = [None; 3]; - for i in 0..value.branchCount { + let mut branch_info = [None; NUM_BRANCH_INFO]; + for i in 0..value.branchCount.min(NUM_BRANCH_INFO) { let branch_target = value.branchTarget[i]; branch_info[i] = Some(BranchInfo { kind: match value.branchType[i] { @@ -1205,6 +1211,7 @@ pub struct CoreArchitecture { } impl CoreArchitecture { + // TODO: Leave a note on architecture lifetimes. Specifically that they are never freed. pub(crate) unsafe fn from_raw(handle: *mut BNArchitecture) -> Self { debug_assert!(!handle.is_null()); CoreArchitecture { handle } diff --git a/rust/src/basicblock.rs b/rust/src/basicblock.rs index e14af3387..87cfab0c9 100644 --- a/rust/src/basicblock.rs +++ b/rust/src/basicblock.rs @@ -75,7 +75,9 @@ unsafe impl<'a, C: 'a + BlockContext> CoreArrayProviderInner for Edge<'a, C> { unsafe fn free(raw: *mut Self::Raw, count: usize, _context: &Self::Context) { BNFreeBasicBlockEdgeList(raw, count); } + unsafe fn wrap_raw<'b>(raw: &'b Self::Raw, context: &'b Self::Context) -> Self::Wrapped<'b> { + // TODO: Why not just take a ref to this? Why do we store a guard in Edge? let edge_target = Guard::new( BasicBlock::from_raw(raw.target, context.orig_block.context.clone()), raw, @@ -124,6 +126,10 @@ impl BasicBlock { Self { handle, context } } + pub(crate) unsafe fn ref_from_raw(handle: *mut BNBasicBlock, context: C) -> Ref { + Ref::new(Self::from_raw(handle, context)) + } + // TODO native bb vs il bbs pub fn function(&self) -> Ref { unsafe { @@ -304,6 +310,7 @@ unsafe impl CoreArrayProviderInner for BasicBlock { unsafe fn free(raw: *mut Self::Raw, count: usize, _context: &Self::Context) { BNFreeBasicBlockList(raw, count); } + unsafe fn wrap_raw<'a>(raw: &'a Self::Raw, context: &'a Self::Context) -> Self::Wrapped<'a> { Guard::new(BasicBlock::from_raw(*raw, context.clone()), context) } diff --git a/rust/src/binaryview.rs b/rust/src/binaryview.rs index 8a3554ab8..96ea0b682 100644 --- a/rust/src/binaryview.rs +++ b/rust/src/binaryview.rs @@ -836,6 +836,7 @@ pub trait BinaryViewExt: BinaryViewBase { segment.create(self.as_ref()); } + // TODO: Replace with BulkModify guard. /// Start adding segments in bulk. Useful for adding large numbers of segments. /// /// After calling this any call to [BinaryViewExt::add_segment] will be uncommited until a call to @@ -851,6 +852,7 @@ pub trait BinaryViewExt: BinaryViewBase { } } + // TODO: Replace with BulkModify guard. /// Commit all auto and user segments that have been added since the call to [Self::begin_bulk_add_segments]. /// /// NOTE: This **must** be paired with a prior call to [Self::begin_bulk_add_segments], otherwise this @@ -861,6 +863,7 @@ pub trait BinaryViewExt: BinaryViewBase { } } + // TODO: Replace with BulkModify guard. /// Flushes the auto and user segments that have yet to be committed. /// /// This is to be used in conjunction with [Self::begin_bulk_add_segments] @@ -1722,6 +1725,11 @@ pub struct BinaryView { } impl BinaryView { + pub(crate) unsafe fn from_raw(handle: *mut BNBinaryView) -> Self { + debug_assert!(!handle.is_null()); + Self { handle } + } + pub(crate) unsafe fn ref_from_raw(handle: *mut BNBinaryView) -> Ref { debug_assert!(!handle.is_null()); Ref::new(Self { handle }) diff --git a/rust/src/callingconvention.rs b/rust/src/callingconvention.rs index fd773982b..0fa8e2c5e 100644 --- a/rust/src/callingconvention.rs +++ b/rust/src/callingconvention.rs @@ -677,6 +677,7 @@ unsafe impl CoreArrayProviderInner for CallingConvention { unsafe fn free(raw: *mut *mut BNCallingConvention, count: usize, _content: &Self::Context) { BNFreeCallingConventionList(raw, count); } + unsafe fn wrap_raw<'a>(raw: &'a Self::Raw, context: &'a Self::Context) -> Self::Wrapped<'a> { Guard::new( CallingConvention { diff --git a/rust/src/debuginfo.rs b/rust/src/debuginfo.rs index 61344fc9d..fbd7f99b2 100644 --- a/rust/src/debuginfo.rs +++ b/rust/src/debuginfo.rs @@ -281,6 +281,7 @@ unsafe impl CoreArrayProviderInner for DebugInfoParser { unsafe fn free(raw: *mut Self::Raw, count: usize, _: &Self::Context) { BNFreeDebugInfoParserList(raw, count); } + unsafe fn wrap_raw<'a>(raw: &'a Self::Raw, context: &'a Self::Context) -> Self::Wrapped<'a> { Guard::new(Self { handle: *raw }, context) } diff --git a/rust/src/disassembly.rs b/rust/src/disassembly.rs index 5d3da31e0..7a18bb29f 100644 --- a/rust/src/disassembly.rs +++ b/rust/src/disassembly.rs @@ -23,8 +23,6 @@ use crate::rc::*; use std::convert::From; use std::ffi::CStr; -use std::mem; -use std::ptr; pub type InstructionTextTokenType = BNInstructionTextTokenType; pub type InstructionTextTokenContext = BNInstructionTextTokenContext; @@ -73,6 +71,7 @@ pub type InstructionTextTokenContext = BNInstructionTextTokenContext; // IndirectImportToken = 69, // ExternalSymbolToken = 70, +// TODO: How about we new type this pls. #[repr(transparent)] pub struct InstructionTextToken(pub(crate) BNInstructionTextToken); @@ -100,11 +99,13 @@ pub enum InstructionTextTokenContents { impl InstructionTextToken { pub(crate) unsafe fn from_raw(raw: &BNInstructionTextToken) -> &Self { - mem::transmute(raw) + // TODO: Insane!!! + std::mem::transmute(raw) } pub(crate) fn into_raw(self) -> BNInstructionTextToken { - mem::ManuallyDrop::new(self).0 + // TODO: This is insane. + std::mem::ManuallyDrop::new(self).0 } pub fn new(text: &str, contents: InstructionTextTokenContents) -> Self { @@ -162,7 +163,7 @@ impl InstructionTextToken { context: InstructionTextTokenContext::NoTokenContext, confidence: BN_FULL_CONFIDENCE, address, - typeNames: ptr::null_mut(), + typeNames: std::ptr::null_mut(), namesCount: 0, exprIndex: BN_INVALID_EXPR, }) @@ -224,7 +225,7 @@ impl Default for InstructionTextToken { fn default() -> Self { InstructionTextToken(BNInstructionTextToken { type_: InstructionTextTokenType::TextToken, - text: ptr::null_mut(), + text: std::ptr::null_mut(), value: 0, width: 0, size: 0, @@ -232,7 +233,7 @@ impl Default for InstructionTextToken { context: InstructionTextTokenContext::NoTokenContext, confidence: BN_FULL_CONFIDENCE, address: 0, - typeNames: ptr::null_mut(), + typeNames: std::ptr::null_mut(), namesCount: 0, exprIndex: BN_INVALID_EXPR, }) @@ -251,7 +252,7 @@ impl Clone for InstructionTextToken { width: 0, text: BnString::new(self.text()).into_raw(), confidence: 0xff, - typeNames: ptr::null_mut(), + typeNames: std::ptr::null_mut(), namesCount: 0, exprIndex: self.0.exprIndex, }) @@ -274,29 +275,35 @@ impl CoreArrayProvider for InstructionTextToken { type Context = (); type Wrapped<'a> = &'a Self; } + unsafe impl CoreArrayProviderInner for InstructionTextToken { unsafe fn free(raw: *mut Self::Raw, count: usize, _context: &Self::Context) { BNFreeInstructionText(raw, count) } + unsafe fn wrap_raw<'a>(raw: &'a Self::Raw, _context: &'a Self::Context) -> Self::Wrapped<'a> { - core::mem::transmute(raw) + // TODO: This MUST be removed. + std::mem::transmute(raw) } } impl CoreArrayProvider for Array { type Raw = BNInstructionTextLine; type Context = (); - type Wrapped<'a> = mem::ManuallyDrop; + type Wrapped<'a> = std::mem::ManuallyDrop; } unsafe impl CoreArrayProviderInner for Array { unsafe fn free(raw: *mut Self::Raw, count: usize, _context: &Self::Context) { BNFreeInstructionTextLines(raw, count) } + unsafe fn wrap_raw<'a>(raw: &'a Self::Raw, _context: &'a Self::Context) -> Self::Wrapped<'a> { - mem::ManuallyDrop::new(Self::new(raw.tokens, raw.count, ())) + // TODO: This is insane. + std::mem::ManuallyDrop::new(Self::new(raw.tokens, raw.count, ())) } } +// TODO: How about we new type this please! #[repr(transparent)] pub struct DisassemblyTextLine(pub(crate) BNDisassemblyTextLine); @@ -349,7 +356,7 @@ impl From> for DisassemblyTextLine { // 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(); - mem::forget(tokens); + std::mem::forget(tokens); DisassemblyTextLine(BNDisassemblyTextLine { addr: 0, @@ -366,11 +373,11 @@ impl From> for DisassemblyTextLine { b: 0, alpha: 0, }, - tags: ptr::null_mut(), + tags: std::ptr::null_mut(), tagCount: 0, typeInfo: BNDisassemblyTextLineTypeInfo { hasTypeInfo: false, - parentType: ptr::null_mut(), + parentType: std::ptr::null_mut(), fieldIndex: usize::MAX, offset: 0, }, @@ -388,7 +395,8 @@ impl From<&Vec<&str>> for DisassemblyTextLine { // 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(); - mem::forget(tokens); + // TODO: ???? + std::mem::forget(tokens); DisassemblyTextLine(BNDisassemblyTextLine { addr: 0, @@ -405,11 +413,11 @@ impl From<&Vec<&str>> for DisassemblyTextLine { b: 0, alpha: 0, }, - tags: ptr::null_mut(), + tags: std::ptr::null_mut(), tagCount: 0, typeInfo: BNDisassemblyTextLineTypeInfo { hasTypeInfo: false, - parentType: ptr::null_mut(), + parentType: std::ptr::null_mut(), fieldIndex: usize::MAX, offset: 0, }, @@ -422,7 +430,7 @@ impl Default for DisassemblyTextLine { DisassemblyTextLine(BNDisassemblyTextLine { addr: 0, instrIndex: BN_INVALID_EXPR, - tokens: ptr::null_mut(), + tokens: std::ptr::null_mut(), count: 0, highlight: BNHighlightColor { style: BNHighlightColorStyle::StandardHighlightColor, @@ -434,11 +442,11 @@ impl Default for DisassemblyTextLine { b: 0, alpha: 0, }, - tags: ptr::null_mut(), + tags: std::ptr::null_mut(), tagCount: 0, typeInfo: BNDisassemblyTextLineTypeInfo { hasTypeInfo: false, - parentType: ptr::null_mut(), + parentType: std::ptr::null_mut(), fieldIndex: usize::MAX, offset: 0, }, @@ -449,7 +457,7 @@ impl Default for DisassemblyTextLine { impl Drop for DisassemblyTextLine { fn drop(&mut self) { if !self.0.tokens.is_null() { - let ptr = core::ptr::slice_from_raw_parts_mut(self.0.tokens, self.0.count); + let ptr = std::ptr::slice_from_raw_parts_mut(self.0.tokens, self.0.count); let _ = unsafe { Box::from_raw(ptr) }; } } @@ -465,8 +473,10 @@ unsafe impl CoreArrayProviderInner for DisassemblyTextLine { unsafe fn free(raw: *mut Self::Raw, count: usize, _context: &Self::Context) { BNFreeDisassemblyTextLines(raw, count) } + unsafe fn wrap_raw<'a>(raw: &'a Self::Raw, _context: &'a Self::Context) -> Self::Wrapped<'a> { - core::mem::transmute(raw) + // TODO: This MUST be removed. + std::mem::transmute(raw) } } diff --git a/rust/src/downloadprovider.rs b/rust/src/downloadprovider.rs index 0d388486e..568a4e023 100644 --- a/rust/src/downloadprovider.rs +++ b/rust/src/downloadprovider.rs @@ -68,6 +68,7 @@ unsafe impl CoreArrayProviderInner for DownloadProvider { unsafe fn free(raw: *mut Self::Raw, _count: usize, _context: &Self::Context) { BNFreeDownloadProviderList(raw); } + unsafe fn wrap_raw<'a>(raw: &'a Self::Raw, _context: &'a Self::Context) -> Self::Wrapped<'a> { Guard::new(DownloadProvider::from_raw(*raw), &()) } diff --git a/rust/src/function.rs b/rust/src/function.rs index f0d12f4fb..88b3a9c80 100644 --- a/rust/src/function.rs +++ b/rust/src/function.rs @@ -21,9 +21,8 @@ use crate::{ callingconvention::CallingConvention, component::Component, disassembly::{DisassemblySettings, DisassemblyTextLine}, - flowgraph::FlowGraph, - hlil, llil, - mlil::{self, FunctionGraphType}, + flowgraph::FlowGraph, llil, + mlil::FunctionGraphType, platform::Platform, references::CodeReference, string::*, @@ -42,11 +41,13 @@ pub use binaryninjacore_sys::BNFunctionUpdateType as FunctionUpdateType; pub use binaryninjacore_sys::BNBuiltinType as BuiltinType; pub use binaryninjacore_sys::BNHighlightStandardColor as HighlightStandardColor; -use std::{fmt, mem}; use std::{ffi::c_char, hash::Hash, ops::Range}; +use std::fmt::{Debug, Formatter}; use std::ptr::NonNull; use std::time::Duration; use crate::confidence::Conf; +use crate::hlil::HighLevelILFunction; +use crate::mlil::MediumLevelILFunction; use crate::variable::{IndirectBranchInfo, MergedVariable, NamedVariableWithType, RegisterValue, RegisterValueType, StackVariableReference, Variable}; use crate::workflow::Workflow; @@ -236,7 +237,13 @@ unsafe impl Send for Function {} unsafe impl Sync for Function {} impl Function { + pub(crate) unsafe fn from_raw(handle: *mut BNFunction) -> Self { + debug_assert!(!handle.is_null()); + Self { handle } + } + pub(crate) unsafe fn ref_from_raw(handle: *mut BNFunction) -> Ref { + debug_assert!(!handle.is_null()); Ref::new(Self { handle }) } @@ -365,14 +372,12 @@ impl Function { ) -> Option>> { let arch = arch.unwrap_or_else(|| self.arch()); unsafe { - let block = BNGetFunctionBasicBlockAtAddress(self.handle, arch.handle, addr); + let basic_block_ptr = BNGetFunctionBasicBlockAtAddress(self.handle, arch.handle, addr); let context = NativeBlock { _priv: () }; - - if block.is_null() { - return None; + match basic_block_ptr.is_null() { + false => Some(BasicBlock::ref_from_raw(basic_block_ptr, context)), + true => None, } - - Some(Ref::new(BasicBlock::from_raw(block, context))) } } @@ -396,102 +401,101 @@ impl Function { } } - pub fn high_level_il(&self, full_ast: bool) -> Result, ()> { + pub fn high_level_il(&self, full_ast: bool) -> Result, ()> { unsafe { - let hlil = BNGetFunctionHighLevelIL(self.handle); - - if hlil.is_null() { - return Err(()); + let hlil_ptr = BNGetFunctionHighLevelIL(self.handle); + match hlil_ptr.is_null() { + false => Ok(HighLevelILFunction::ref_from_raw(hlil_ptr, full_ast)), + true => Err(()), } - - Ok(hlil::HighLevelILFunction::ref_from_raw(hlil, full_ast)) } } - pub fn high_level_il_if_available(&self) -> Option> { - let hlil = unsafe { BNGetFunctionHighLevelILIfAvailable(self.handle) }; - (!hlil.is_null()).then(|| unsafe { hlil::HighLevelILFunction::ref_from_raw(hlil, true) }) + pub fn high_level_il_if_available(&self) -> Option> { + let hlil_ptr = unsafe { BNGetFunctionHighLevelILIfAvailable(self.handle) }; + match hlil_ptr.is_null() { + false => Some(unsafe { HighLevelILFunction::ref_from_raw(hlil_ptr, true) }), + true => None, + } } /// MediumLevelILFunction used to represent Function mapped medium level IL - pub fn mapped_medium_level_il(&self) -> Result, ()> { - let mlil = unsafe { BNGetFunctionMappedMediumLevelIL(self.handle) }; - if mlil.is_null() { - return Err(()); + pub fn mapped_medium_level_il(&self) -> Result, ()> { + let mlil_ptr = unsafe { BNGetFunctionMappedMediumLevelIL(self.handle) }; + match mlil_ptr.is_null() { + false => Ok(unsafe { MediumLevelILFunction::ref_from_raw(mlil_ptr) }), + true => Err(()), } - Ok(unsafe { mlil::MediumLevelILFunction::ref_from_raw(mlil) }) } pub fn mapped_medium_level_il_if_available( &self, - ) -> Result, ()> { - let mlil = unsafe { BNGetFunctionMappedMediumLevelILIfAvailable(self.handle) }; - if mlil.is_null() { - return Err(()); + ) -> Option> { + let mlil_ptr = unsafe { BNGetFunctionMappedMediumLevelILIfAvailable(self.handle) }; + match mlil_ptr.is_null() { + false => Some(unsafe { MediumLevelILFunction::ref_from_raw(mlil_ptr) }), + true => None, } - Ok(unsafe { mlil::MediumLevelILFunction::ref_from_raw(mlil) }) } - pub fn medium_level_il(&self) -> Result, ()> { + pub fn medium_level_il(&self) -> Result, ()> { unsafe { - let mlil = BNGetFunctionMediumLevelIL(self.handle); - - if mlil.is_null() { - return Err(()); + let mlil_ptr = BNGetFunctionMediumLevelIL(self.handle); + match mlil_ptr.is_null() { + false => Ok(MediumLevelILFunction::ref_from_raw(mlil_ptr)), + true => Err(()), } - - Ok(mlil::MediumLevelILFunction::ref_from_raw(mlil)) } } - pub fn medium_level_il_if_available(&self) -> Option> { - let mlil = unsafe { BNGetFunctionMediumLevelILIfAvailable(self.handle) }; - (!mlil.is_null()).then(|| unsafe { mlil::MediumLevelILFunction::ref_from_raw(mlil) }) + pub fn medium_level_il_if_available(&self) -> Option> { + let mlil_ptr = unsafe { BNGetFunctionMediumLevelILIfAvailable(self.handle) }; + match mlil_ptr.is_null() { + false => Some(unsafe { MediumLevelILFunction::ref_from_raw(mlil_ptr) }), + true => None, + } } pub fn low_level_il(&self) -> Result>, ()> { unsafe { - let llil = BNGetFunctionLowLevelIL(self.handle); - - if llil.is_null() { - return Err(()); + let llil_ptr = BNGetFunctionLowLevelIL(self.handle); + match llil_ptr.is_null() { + false => Ok(llil::RegularFunction::ref_from_raw(self.arch(), llil_ptr)), + true => Err(()), } - - Ok(llil::RegularFunction::ref_from_raw(self.arch(), llil)) } } pub fn low_level_il_if_available( &self, ) -> Option>> { - let llil = unsafe { BNGetFunctionLowLevelILIfAvailable(self.handle) }; - (!llil.is_null()).then(|| unsafe { llil::RegularFunction::ref_from_raw(self.arch(), llil) }) + let llil_ptr = unsafe { BNGetFunctionLowLevelILIfAvailable(self.handle) }; + match llil_ptr.is_null() { + false => Some(unsafe { llil::RegularFunction::ref_from_raw(self.arch(), llil_ptr) }), + true => None, + } } pub fn lifted_il(&self) -> Result>, ()> { unsafe { - let llil = BNGetFunctionLiftedIL(self.handle); - - if llil.is_null() { - return Err(()); + let llil_ptr = BNGetFunctionLiftedIL(self.handle); + match llil_ptr.is_null() { + false => Ok(llil::LiftedFunction::ref_from_raw(self.arch(), llil_ptr)), + true => Err(()), } - - Ok(llil::LiftedFunction::ref_from_raw(self.arch(), llil)) } } pub fn lifted_il_if_available(&self) -> Option>> { - let llil = unsafe { BNGetFunctionLiftedILIfAvailable(self.handle) }; - (!llil.is_null()).then(|| unsafe { llil::LiftedFunction::ref_from_raw(self.arch(), llil) }) + let llil_ptr = unsafe { BNGetFunctionLiftedILIfAvailable(self.handle) }; + match llil_ptr.is_null() { + false => Some(unsafe { llil::LiftedFunction::ref_from_raw(self.arch(), llil_ptr) }), + true => None, + } } pub fn return_type(&self) -> Conf> { - let result = unsafe { BNGetFunctionReturnType(self.handle) }; - - Conf::new( - unsafe { Type::ref_from_raw(result.type_) }, - result.confidence, - ) + unsafe { BNGetFunctionReturnType(self.handle) }.into() } pub fn set_auto_return_type<'a, C>(&self, return_type: C) @@ -499,13 +503,11 @@ impl Function { C: Into>, { let return_type: Conf<&Type> = return_type.into(); + let mut raw_return_type = BNTypeWithConfidence::from(return_type); unsafe { BNSetAutoFunctionReturnType( self.handle, - &mut BNTypeWithConfidence { - type_: return_type.contents.handle, - confidence: return_type.confidence, - }, + &mut raw_return_type, ) } } @@ -515,13 +517,11 @@ impl Function { C: Into>, { let return_type: Conf<&Type> = return_type.into(); + let mut raw_return_type = BNTypeWithConfidence::from(return_type); unsafe { BNSetUserFunctionReturnType( self.handle, - &mut BNTypeWithConfidence { - type_: return_type.contents.handle, - confidence: return_type.confidence, - }, + &mut raw_return_type, ) } } @@ -630,8 +630,10 @@ impl Function { ) -> Option>> { let arch = arch.unwrap_or_else(|| self.arch()); let result = unsafe { BNGetCallTypeAdjustment(self.handle, arch.handle, addr) }; - (!result.type_.is_null()) - .then(|| unsafe { Conf::new(Type::ref_from_raw(result.type_), result.confidence) }) + match result.type_.is_null() { + false => Some(result.into()), + true => None, + } } /// Sets or removes the call type override at a call site to the given type. @@ -839,6 +841,7 @@ impl Function { } } + // TODO: Turn this into an actual type? /// List of function variables: including name, variable and type pub fn variables(&self) -> Array<(&str, Variable, &Type)> { let mut count = 0; @@ -856,17 +859,12 @@ impl Function { pub fn parameter_variables(&self) -> Conf> { unsafe { - let mut variables = BNGetFunctionParameterVariables(self.handle); - let mut result = Vec::with_capacity(variables.count); - let confidence = variables.confidence; - let vars = std::slice::from_raw_parts(variables.vars, variables.count); - - for var in vars.iter().take(variables.count) { - result.push(Variable::from(*var)); - } - - BNFreeParameterVariables(&mut variables); - Conf::new(result, confidence) + let mut raw_variables = BNGetFunctionParameterVariables(self.handle); + let raw_var_list = std::slice::from_raw_parts(raw_variables.vars, raw_variables.count); + let variables: Vec = raw_var_list.iter().map(Into::into).collect(); + let confidence = raw_variables.confidence; + BNFreeParameterVariables(&mut raw_variables); + Conf::new(variables, confidence) } } @@ -935,11 +933,7 @@ impl Function { BNApplyImportedTypes( self.handle, sym.handle, - if let Some(t) = t { - t.handle - } else { - core::ptr::null_mut() - }, + t.map(|t| t.handle).unwrap_or(std::ptr::null_mut()), ); } } @@ -998,10 +992,7 @@ impl Function { unsafe { BNSetAutoFunctionInlinedDuringAnalysis( self.handle, - BNBoolWithConfidence { - value: value.contents, - confidence: value.confidence, - }, + value.into(), ) } } @@ -1014,10 +1005,7 @@ impl Function { unsafe { BNSetUserFunctionInlinedDuringAnalysis( self.handle, - BNBoolWithConfidence { - value: value.contents, - confidence: value.confidence, - }, + value.into(), ) } } @@ -1816,7 +1804,7 @@ impl Function { arch: Option, ) -> Option<(Variable, BnString, Conf>)> { let arch = arch.unwrap_or_else(|| self.arch()); - let mut found_value: BNVariableNameAndType = unsafe { mem::zeroed() }; + let mut found_value = BNVariableNameAndType::default(); let found = unsafe { BNGetStackVariableAtFrameOffset(self.handle, arch.handle, addr, offset, &mut found_value) }; @@ -2109,7 +2097,7 @@ impl Function { where I: Into>>, { - let mut conv_conf: BNCallingConventionWithConfidence = unsafe { mem::zeroed() }; + let mut conv_conf = BNCallingConventionWithConfidence::default(); if let Some(value) = value { let value = value.into(); conv_conf.convention = value.contents.handle; @@ -2123,7 +2111,7 @@ impl Function { where I: Into>>, { - let mut conv_conf: BNCallingConventionWithConfidence = unsafe { mem::zeroed() }; + let mut conv_conf = BNCallingConventionWithConfidence::default(); if let Some(value) = value { let value = value.into(); conv_conf.convention = value.contents.handle; @@ -2261,8 +2249,8 @@ impl Function { } } -impl fmt::Debug for Function { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { +impl Debug for Function { + fn fmt(&self, f: &mut Formatter) -> std::fmt::Result { write!( f, "", @@ -2300,10 +2288,11 @@ impl CoreArrayProvider for Function { } unsafe impl CoreArrayProviderInner for Function { - unsafe fn free(raw: *mut *mut BNFunction, count: usize, _context: &()) { + unsafe fn free(raw: *mut Self::Raw, count: usize, _context: &Self::Context) { BNFreeFunctionList(raw, count); } - unsafe fn wrap_raw<'a>(raw: &'a *mut BNFunction, context: &'a ()) -> Self::Wrapped<'a> { + + unsafe fn wrap_raw<'a>(raw: &'a Self::Raw, context: &'a Self::Context) -> Self::Wrapped<'a> { Guard::new(Function { handle: *raw }, context) } } @@ -2383,6 +2372,16 @@ impl From for PerformanceInfo { } } +impl From<&BNPerformanceInfo> for PerformanceInfo { + fn from(value: &BNPerformanceInfo) -> Self { + Self { + // TODO: Name will be freed by this. FIX! + name: unsafe { BnString::from_raw(value.name) }.to_string(), + seconds: Duration::from_secs_f64(value.seconds), + } + } +} + impl CoreArrayProvider for PerformanceInfo { type Raw = BNPerformanceInfo; type Context = (); @@ -2395,6 +2394,7 @@ unsafe impl CoreArrayProviderInner for PerformanceInfo { } unsafe fn wrap_raw<'a>(raw: &'a Self::Raw, _context: &'a Self::Context) -> Self::Wrapped<'a> { + // TODO: Swap this to the ref version. Self::from(*raw) } } diff --git a/rust/src/functionrecognizer.rs b/rust/src/functionrecognizer.rs index 0f7c41730..e931a1397 100644 --- a/rust/src/functionrecognizer.rs +++ b/rust/src/functionrecognizer.rs @@ -3,6 +3,7 @@ use crate::{ }; use binaryninjacore_sys::*; use std::os::raw::c_void; +use crate::rc::RefCountable; pub trait FunctionRecognizer { fn recognize_low_level_il( @@ -46,15 +47,12 @@ where R: 'static + FunctionRecognizer + Send + Sync, { let custom_handler = unsafe { &*(ctxt as *mut R) }; - let bv = unsafe { BinaryView::ref_from_raw(BNNewViewReference(bv)) }; - let arch = unsafe { BNGetFunctionArchitecture(func) }; - let func = unsafe { Function::ref_from_raw(func) }; - if arch.is_null() { - return false; - } - let arch = unsafe { CoreArchitecture::from_raw(arch) }; - let llil = unsafe { llil::RegularFunction::from_raw(arch, llil) }; - custom_handler.recognize_low_level_il(bv.as_ref(), func.as_ref(), &llil) + let bv = unsafe { BinaryView::inc_ref(&BinaryView::from_raw(bv)) }; + let func = unsafe { Function::inc_ref(&Function::from_raw(func)) }; + let llil = unsafe { + llil::RegularFunction::inc_ref(&llil::RegularFunction::from_raw(func.arch(), llil)) + }; + custom_handler.recognize_low_level_il(&bv, &func, &llil) } extern "C" fn cb_recognize_medium_level_il( @@ -67,10 +65,12 @@ where R: 'static + FunctionRecognizer + Send + Sync, { let custom_handler = unsafe { &*(ctxt as *mut R) }; - let bv = unsafe { BinaryView::ref_from_raw(BNNewViewReference(bv)) }; - let func = unsafe { Function::ref_from_raw(func) }; - let mlil = unsafe { mlil::MediumLevelILFunction::ref_from_raw(mlil) }; - custom_handler.recognize_medium_level_il(bv.as_ref(), func.as_ref(), &mlil) + let bv = unsafe { BinaryView::inc_ref(&BinaryView::from_raw(bv)) }; + let func = unsafe { Function::inc_ref(&Function::from_raw(func)) }; + let mlil = unsafe { + mlil::MediumLevelILFunction::inc_ref(&mlil::MediumLevelILFunction::from_raw(mlil)) + }; + custom_handler.recognize_medium_level_il(&bv, &func, &mlil) } let recognizer = FunctionRecognizerHandlerContext { recognizer }; diff --git a/rust/src/linearview.rs b/rust/src/linearview.rs index f81685d3c..8e0a0fcd4 100644 --- a/rust/src/linearview.rs +++ b/rust/src/linearview.rs @@ -383,6 +383,7 @@ pub struct LinearDisassemblyLine { // These will be cleaned up by BNFreeLinearDisassemblyLines, so we // don't drop them in the relevant deconstructors. + // TODO: This is insane! function: mem::ManuallyDrop>, contents: mem::ManuallyDrop, } @@ -390,6 +391,7 @@ pub struct LinearDisassemblyLine { 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(raw.contents)); Self { @@ -428,10 +430,12 @@ impl CoreArrayProvider for LinearDisassemblyLine { } unsafe impl CoreArrayProviderInner for LinearDisassemblyLine { - unsafe fn free(raw: *mut BNLinearDisassemblyLine, count: usize, _context: &()) { + unsafe fn free(raw: *mut Self::Raw, count: usize, _context: &Self::Context) { 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(LinearDisassemblyLine::from_raw(raw), _context) } } diff --git a/rust/src/llil/function.rs b/rust/src/llil/function.rs index 076787590..0ebec5d18 100644 --- a/rust/src/llil/function.rs +++ b/rust/src/llil/function.rs @@ -100,6 +100,7 @@ where borrower: A::Handle, handle: *mut BNLowLevelILFunction, ) -> Ref { + debug_assert!(!handle.is_null()); Ref::new(Self::from_raw(borrower, handle)) } diff --git a/rust/src/metadata.rs b/rust/src/metadata.rs index 9eeff8fad..9ef956303 100644 --- a/rust/src/metadata.rs +++ b/rust/src/metadata.rs @@ -350,10 +350,10 @@ impl CoreArrayProvider for Metadata { } unsafe impl CoreArrayProviderInner for Metadata { - unsafe fn free(raw: *mut *mut BNMetadata, _count: usize, _context: &()) { + unsafe fn free(raw: *mut Self::Raw, count: usize, _context: &Self::Context) { BNFreeMetadataArray(raw); } - unsafe fn wrap_raw<'a>(raw: &'a *mut BNMetadata, context: &'a ()) -> Self::Wrapped<'a> { + unsafe fn wrap_raw<'a>(raw: &'a Self::Raw, context: &'a Self::Context) -> Self::Wrapped<'a> { Guard::new(Metadata::from_raw(*raw), context) } } diff --git a/rust/src/mlil/function.rs b/rust/src/mlil/function.rs index 050cefb3a..50816b93e 100644 --- a/rust/src/mlil/function.rs +++ b/rust/src/mlil/function.rs @@ -15,6 +15,9 @@ use crate::types::Type; use crate::variable::{PossibleValueSet, RegisterValue, SSAVariable, UserVariableValue, Variable}; use super::{MediumLevelILBlock, MediumLevelILInstruction, MediumLevelILLiftedInstruction}; +// TODO: Does this belong here? +pub use binaryninjacore_sys::BNFunctionGraphType as FunctionGraphType; + pub struct MediumLevelILFunction { pub(crate) handle: *mut BNMediumLevelILFunction, } @@ -36,10 +39,14 @@ impl Hash for MediumLevelILFunction { } impl MediumLevelILFunction { + pub(crate) unsafe fn from_raw(handle: *mut BNMediumLevelILFunction) -> Self { + debug_assert!(!handle.is_null()); + Self { handle } + } + pub(crate) unsafe fn ref_from_raw(handle: *mut BNMediumLevelILFunction) -> Ref { debug_assert!(!handle.is_null()); - - Self { handle }.to_owned() + Ref::new(Self::from_raw(handle)) } pub fn instruction_at>(&self, loc: L) -> Option { @@ -65,7 +72,7 @@ impl MediumLevelILFunction { } pub fn instruction_from_instruction_idx(&self, instr_idx: usize) -> MediumLevelILInstruction { - MediumLevelILInstruction::new(self.to_owned(), unsafe { + self.instruction_from_idx(unsafe { BNGetMediumLevelILIndexForInstruction(self.handle, instr_idx) }) } @@ -325,11 +332,10 @@ impl MediumLevelILFunction { /// # use binaryninja::types::Variable; /// # let mlil_fun: MediumLevelILFunction = todo!(); /// # let mlil_var: Variable = todo!(); - /// let instr = mlil_fun.var_refs(&mlil_var).get(0).expr(); + /// let instr_idx = mlil_fun.var_refs(&mlil_var).get(0).expr_idx; /// ``` pub fn var_refs(&self, var: &Variable) -> Array { let mut count = 0; - // TODO: I don't think this needs to be mutable let mut raw_var = BNVariable::from(var); let refs = unsafe { BNGetMediumLevelILVariableReferences( @@ -339,7 +345,7 @@ impl MediumLevelILFunction { ) }; assert!(!refs.is_null()); - unsafe { Array::new(refs, count, self.to_owned()) } + unsafe { Array::new(refs, count, ()) } } /// Retrieves variable references from a specified location or range within a medium-level IL function. @@ -372,7 +378,7 @@ impl MediumLevelILFunction { } }; assert!(!refs.is_null()); - unsafe { Array::new(refs, count, self.to_owned()) } + unsafe { Array::new(refs, count, ()) } } // TODO: Rename to `current_location`? @@ -569,14 +575,12 @@ impl MediumLevelILFunction { unsafe { Array::new(uses, count, ()) } } - /// This gets just the MLIL SSA variables - you may be interested in the - /// union of [MediumLevelILFunction::aliased_variables] and - /// [crate::function::Function::parameter_variables] for all the - /// variables used in the function. - pub fn ssa_variables(&self) -> Array> { + /// This gets the MLIL SSA variables for a given [`Variable`]. + pub fn ssa_variables(&self, variable: &Variable) -> Array { let mut count = 0; - let vars = unsafe { BNGetMediumLevelILVariables(self.handle, &mut count) }; - unsafe { Array::new(vars, count, self.to_owned()) } + let raw_variable = BNVariable::from(variable); + let versions = unsafe { BNGetMediumLevelILVariableSSAVersions(self.handle, &raw_variable, &mut count) }; + unsafe { Array::new(versions, count, *variable) } } } @@ -640,79 +644,82 @@ impl DoubleEndedIterator for MediumLevelILInstructionList<'_> { impl ExactSizeIterator for MediumLevelILInstructionList<'_> {} impl core::iter::FusedIterator for MediumLevelILInstructionList<'_> {} -///////////////////////// -// FunctionGraphType - -pub type FunctionGraphType = binaryninjacore_sys::BNFunctionGraphType; - -///////////////////////// -// ILReferenceSource - pub struct ILReferenceSource { - mlil: Ref, - _func: Ref, - _arch: CoreArchitecture, - addr: u64, - type_: FunctionGraphType, - expr_id: usize, + pub function: Ref, + pub arch: CoreArchitecture, + pub addr: u64, + pub graph_type: FunctionGraphType, + pub expr_idx: usize, } -impl ILReferenceSource { - unsafe fn from_raw(value: BNILReferenceSource, mlil: Ref) -> Self { +impl From for ILReferenceSource { + fn from(value: BNILReferenceSource) -> Self { Self { - mlil, - _func: Function::ref_from_raw(value.func), - _arch: CoreArchitecture::from_raw(value.arch), + function: unsafe { Function::ref_from_raw(value.func) }, + arch: unsafe { CoreArchitecture::from_raw(value.arch) }, addr: value.addr, - type_: value.type_, - expr_id: value.exprId, + graph_type: value.type_, + expr_idx: value.exprId, } } - pub fn addr(&self) -> u64 { - self.addr - } - pub fn graph_type(&self) -> FunctionGraphType { - self.type_ - } - pub fn expr(&self) -> MediumLevelILInstruction { - self.mlil.instruction_from_idx(self.expr_id) +} + +impl From<&BNILReferenceSource> for ILReferenceSource { + fn from(value: &BNILReferenceSource) -> Self { + Self { + function: unsafe { Function::from_raw(value.func).to_owned() }, + arch: unsafe { CoreArchitecture::from_raw(value.arch) }, + addr: value.addr, + graph_type: value.type_, + expr_idx: value.exprId, + } } } impl CoreArrayProvider for ILReferenceSource { type Raw = BNILReferenceSource; - type Context = Ref; + type Context = (); type Wrapped<'a> = Self; } + unsafe impl CoreArrayProviderInner for ILReferenceSource { unsafe fn free(raw: *mut Self::Raw, count: usize, _context: &Self::Context) { BNFreeILReferences(raw, count) } + unsafe fn wrap_raw<'a>(raw: &'a Self::Raw, context: &'a Self::Context) -> Self::Wrapped<'a> { - Self::from_raw(*raw, context.to_owned()) + raw.into() } } -///////////////////////// -// VariableReferenceSource - pub struct VariableReferenceSource { - var: Variable, - source: ILReferenceSource, + pub variable: Variable, + pub source: ILReferenceSource, } -impl VariableReferenceSource { - pub fn variable(&self) -> &Variable { - &self.var +impl From for VariableReferenceSource { + fn from(value: BNVariableReferenceSource) -> Self { + Self { + variable: Variable::from(value.var), + source: value.source.into(), + } } - pub fn source(&self) -> &ILReferenceSource { - &self.source +} + +impl From<&BNVariableReferenceSource> for VariableReferenceSource { + fn from(value: &BNVariableReferenceSource) -> Self { + Self { + variable: Variable::from(value.var), + // TODO: We really need to document this better, or have some other facility for this. + // NOTE: We take this as a ref to increment the function ref. + source: ILReferenceSource::from(&value.source), + } } } impl CoreArrayProvider for VariableReferenceSource { type Raw = BNVariableReferenceSource; - type Context = Ref; + type Context = (); type Wrapped<'a> = Self; } @@ -720,10 +727,8 @@ unsafe impl CoreArrayProviderInner for VariableReferenceSource { unsafe fn free(raw: *mut Self::Raw, count: usize, _context: &Self::Context) { BNFreeVariableReferenceSourceList(raw, count) } - unsafe fn wrap_raw<'a>(raw: &'a Self::Raw, context: &'a Self::Context) -> Self::Wrapped<'a> { - Self { - var: Variable::from(raw.var), - source: ILReferenceSource::from_raw(raw.source, context.to_owned()), - } + + unsafe fn wrap_raw<'a>(raw: &'a Self::Raw, _context: &'a Self::Context) -> Self::Wrapped<'a> { + raw.into() } } diff --git a/rust/src/platform.rs b/rust/src/platform.rs index 9b775f01c..37c9d3f98 100644 --- a/rust/src/platform.rs +++ b/rust/src/platform.rs @@ -378,10 +378,11 @@ impl CoreArrayProvider for Platform { } unsafe impl CoreArrayProviderInner for Platform { - unsafe fn free(raw: *mut *mut BNPlatform, count: usize, _context: &()) { + unsafe fn free(raw: *mut Self::Raw, count: usize, _context: &Self::Context) { BNFreePlatformList(raw, count); } - unsafe fn wrap_raw<'a>(raw: &'a *mut BNPlatform, context: &'a ()) -> Self::Wrapped<'a> { + + unsafe fn wrap_raw<'a>(raw: &'a Self::Raw, context: &'a Self::Context) -> Self::Wrapped<'a> { debug_assert!(!raw.is_null()); Guard::new(Platform { handle: *raw }, context) } diff --git a/rust/src/references.rs b/rust/src/references.rs index 34b60d119..c4b61d8a2 100644 --- a/rust/src/references.rs +++ b/rust/src/references.rs @@ -57,6 +57,7 @@ unsafe impl CoreArrayProviderInner for CodeReference { unsafe fn free(raw: *mut Self::Raw, count: usize, _context: &Self::Context) { BNFreeCodeReferences(raw, count) } + unsafe fn wrap_raw<'a>(raw: &'a Self::Raw, _context: &'a Self::Context) -> Self::Wrapped<'a> { Guard::new(CodeReference::new(raw), &()) } diff --git a/rust/src/relocation.rs b/rust/src/relocation.rs index f4aefcf75..54d498401 100644 --- a/rust/src/relocation.rs +++ b/rust/src/relocation.rs @@ -228,6 +228,7 @@ unsafe impl CoreArrayProviderInner for Relocation { unsafe fn free(raw: *mut Self::Raw, count: usize, _context: &Self::Context) { BNFreeRelocationList(raw, count); } + unsafe fn wrap_raw<'a>(raw: &'a Self::Raw, _context: &'a Self::Context) -> Self::Wrapped<'a> { Guard::new(Relocation(*raw), &()) } diff --git a/rust/src/section.rs b/rust/src/section.rs index b6358faee..b8de57841 100644 --- a/rust/src/section.rs +++ b/rust/src/section.rs @@ -187,6 +187,7 @@ unsafe impl CoreArrayProviderInner for Section { unsafe fn free(raw: *mut Self::Raw, count: usize, _context: &Self::Context) { BNFreeSectionList(raw, count); } + unsafe fn wrap_raw<'a>(raw: &'a Self::Raw, context: &'a Self::Context) -> Self::Wrapped<'a> { Guard::new(Section::from_raw(*raw), context) } diff --git a/rust/src/segment.rs b/rust/src/segment.rs index 4c06a7ba6..30b044f55 100644 --- a/rust/src/segment.rs +++ b/rust/src/segment.rs @@ -211,6 +211,7 @@ unsafe impl CoreArrayProviderInner for Segment { unsafe fn free(raw: *mut Self::Raw, count: usize, _context: &Self::Context) { BNFreeSegmentList(raw, count); } + unsafe fn wrap_raw<'a>(raw: &'a Self::Raw, context: &'a Self::Context) -> Self::Wrapped<'a> { Guard::new(Segment::from_raw(*raw), context) } diff --git a/rust/src/string.rs b/rust/src/string.rs index 17c6d6f26..f0a9681db 100644 --- a/rust/src/string.rs +++ b/rust/src/string.rs @@ -24,6 +24,7 @@ use std::ops::Deref; use crate::rc::*; use crate::types::QualifiedName; +// TODO: Remove or refactor this. pub(crate) fn raw_to_string(ptr: *const c_char) -> Option { if ptr.is_null() { None diff --git a/rust/src/symbol.rs b/rust/src/symbol.rs index d7a73a0ae..d5d991ba2 100644 --- a/rust/src/symbol.rs +++ b/rust/src/symbol.rs @@ -326,6 +326,7 @@ unsafe impl CoreArrayProviderInner for Symbol { unsafe fn free(raw: *mut Self::Raw, count: usize, _context: &Self::Context) { BNFreeSymbolList(raw, count); } + unsafe fn wrap_raw<'a>(raw: &'a Self::Raw, context: &'a Self::Context) -> Self::Wrapped<'a> { Guard::new(Symbol::from_raw(*raw), context) } diff --git a/rust/src/tags.rs b/rust/src/tags.rs index 44f8dd33d..e35d79ded 100644 --- a/rust/src/tags.rs +++ b/rust/src/tags.rs @@ -197,43 +197,25 @@ unsafe impl Sync for TagType {} pub type TagReferenceType = BNTagReferenceType; pub struct TagReference { - ref_type: TagReferenceType, - auto_defined: bool, - tag: Ref, - arch: CoreArchitecture, - func: Ref, - addr: u64, + pub reference_type: TagReferenceType, + pub auto_defined: bool, + pub tag: Ref, + pub arch: CoreArchitecture, + pub func: Ref, + pub addr: u64, } -impl TagReference { - unsafe fn from_borrowed_raw(value: &BNTagReference) -> Self { +impl From<&BNTagReference> for TagReference { + fn from(value: &BNTagReference) -> Self { Self { - ref_type: value.refType, + reference_type: value.refType, auto_defined: value.autoDefined, - tag: Tag { handle: value.tag }.to_owned(), - arch: CoreArchitecture::from_raw(value.arch), - func: Function { handle: value.func }.to_owned(), + tag: unsafe { Tag::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, } } - pub fn ref_type(&self) -> TagReferenceType { - self.ref_type - } - pub fn auto(&self) -> bool { - self.auto_defined - } - pub fn tag(&self) -> &Tag { - &self.tag - } - pub fn arch(&self) -> CoreArchitecture { - self.arch - } - pub fn functions(&self) -> &Function { - &self.func - } - pub fn address(&self) -> u64 { - self.addr - } } impl CoreArrayProvider for TagReference { @@ -246,7 +228,8 @@ unsafe impl CoreArrayProviderInner for TagReference { unsafe fn free(raw: *mut Self::Raw, count: usize, _context: &Self::Context) { BNFreeTagReferences(raw, count) } + unsafe fn wrap_raw<'a>(raw: &'a Self::Raw, _context: &'a Self::Context) -> Self::Wrapped<'a> { - Self::from_borrowed_raw(raw) + raw.into() } } diff --git a/rust/src/types.rs b/rust/src/types.rs index 4da20a94e..e65d97ab4 100644 --- a/rust/src/types.rs +++ b/rust/src/types.rs @@ -39,6 +39,7 @@ use std::{ }; use std::num::NonZeroUsize; use crate::confidence::{Conf, MAX_CONFIDENCE, MIN_CONFIDENCE}; +use crate::string::raw_to_string; use crate::variable::{Variable, VariableSourceType}; pub type ReferenceType = BNReferenceType; @@ -445,7 +446,7 @@ pub struct Type { /// bv.define_user_type("int_2", &my_custom_type_2); /// ``` impl Type { - unsafe fn from_raw(handle: *mut BNType) -> Self { + pub(crate) unsafe fn from_raw(handle: *mut BNType) -> Self { debug_assert!(!handle.is_null()); Self { handle } } @@ -523,12 +524,12 @@ impl Type { pub fn parameters(&self) -> Option> { unsafe { let mut count = 0; - let parameters_raw_ptr = BNGetTypeParameters(self.handle, &mut count); - match parameters_raw_ptr.is_null() { + let raw_parameters_ptr = BNGetTypeParameters(self.handle, &mut count); + match raw_parameters_ptr.is_null() { false => { - let raw_parameters = std::slice::from_raw_parts(parameters_raw_ptr, count); + let raw_parameters = std::slice::from_raw_parts(raw_parameters_ptr, count); let parameters = raw_parameters.iter().map(Into::into).collect(); - BNFreeTypeParameterList(parameters_raw_ptr, count); + //BNFreeTypeParameterList(raw_parameters_ptr, count); Some(parameters) } true => None @@ -1085,7 +1086,31 @@ impl From for FunctionParameter { impl From<&BNFunctionParameter> for FunctionParameter { fn from(value: &BNFunctionParameter) -> Self { - Self::from(*value) + // TODO: I copied this from the original `from_raw` function. + // TODO: So this actually needs to be audited later. + let name = if value.name.is_null() { + if value.location.type_ == VariableSourceType::RegisterVariableSourceType { + format!("reg_{}", value.location.storage) + } else if value.location.type_ == VariableSourceType::StackVariableSourceType { + format!("arg_{}", value.location.storage) + } else { + String::new() + } + } else { + raw_to_string(value.name as *const _).unwrap() + }; + + Self { + ty: Conf::new( + unsafe { Type::from_raw(value.type_).to_owned() }, + value.typeConfidence, + ), + name, + location: match value.defaultLocation { + false => Some(Variable::from(value.location)), + true => None, + }, + } } } @@ -1631,16 +1656,17 @@ impl Structure { unsafe { BNGetStructureType(self.handle) } } - // TODO: Omit `Option` and pass empty vec? + // TODO: Omit `Option` and pass empty vec? Actually the core will only nullptr on failed allocation, use debug_assert. pub fn members(&self) -> Option> { unsafe { let mut count = 0; let members_raw_ptr: *mut BNStructureMember = BNGetStructureMembers(self.handle, &mut count); + // TODO: Debug assert members_raw_ptr. match members_raw_ptr.is_null() { false => { let members_raw = std::slice::from_raw_parts(members_raw_ptr, count); - let members = members_raw.iter().copied().map(Into::into).collect(); + let members = members_raw.iter().map(Into::into).collect(); BNFreeStructureMemberList(members_raw_ptr, count); Some(members) }, @@ -1681,7 +1707,7 @@ impl Debug for Structure { unsafe impl RefCountable for Structure { unsafe fn inc_ref(handle: &Self) -> Ref { - Ref::new(Self::from_raw(BNNewStructureReference(handle.handle))) + Self::ref_from_raw(BNNewStructureReference(handle.handle)) } unsafe fn dec_ref(handle: &Self) { @@ -1739,9 +1765,26 @@ impl From for StructureMember { } } +impl From<&BNStructureMember> for StructureMember { + fn from(value: &BNStructureMember) -> Self { + Self { + ty: Conf::new( + unsafe { Type::from_raw(value.type_).to_owned() }, + value.typeConfidence, + ), + // TODO: I dislike using this function here. + name: raw_to_string(value.name as *mut _).unwrap(), + offset: value.offset, + access: value.access, + scope: value.scope, + } + } +} + impl From for BNStructureMember { fn from(value: StructureMember) -> Self { let bn_name = BnString::new(value.name); + // TODO: Dec ref here? Self { type_: value.ty.contents.handle, name: bn_name.into_raw(), @@ -1756,7 +1799,7 @@ impl From for BNStructureMember { impl CoreArrayProvider for StructureMember { type Raw = BNStructureMember; type Context = (); - type Wrapped<'a> = Guard<'a, StructureMember>; + type Wrapped<'a> = Guard<'a, Self>; } unsafe impl CoreArrayProviderInner for StructureMember { @@ -1765,7 +1808,7 @@ unsafe impl CoreArrayProviderInner for StructureMember { } unsafe fn wrap_raw<'a>(raw: &'a Self::Raw, _context: &'a Self::Context) -> Self::Wrapped<'a> { - Guard::new(StructureMember::from(*raw), &()) + Guard::new(Self::from(raw), raw) } } @@ -2162,6 +2205,19 @@ impl From for NameAndType { } } +impl From<&BNNameAndType> for NameAndType { + fn from(value: &BNNameAndType) -> Self { + Self { + // TODO: I dislike using this function here. + name: raw_to_string(value.name as *mut _).unwrap(), + ty: Conf::new( + unsafe { Type::from_raw(value.type_).to_owned() }, + value.typeConfidence, + ), + } + } +} + impl From for BNNameAndType { fn from(value: NameAndType) -> Self { let bn_name = BnString::new(value.name); @@ -2185,6 +2241,6 @@ unsafe impl CoreArrayProviderInner for NameAndType { } unsafe fn wrap_raw<'a>(raw: &'a Self::Raw, _context: &'a Self::Context) -> Self::Wrapped<'a> { - NameAndType::from(*raw) + raw.into() } } diff --git a/rust/src/variable.rs b/rust/src/variable.rs index 56b687cc8..5fc37ada7 100644 --- a/rust/src/variable.rs +++ b/rust/src/variable.rs @@ -1,7 +1,7 @@ use crate::function::{Function, Location}; use crate::mlil::MediumLevelILFunction; -use crate::rc::{Array, CoreArrayProvider, CoreArrayProviderInner, Guard, Ref}; -use crate::string::BnString; +use crate::rc::{Array, CoreArrayProvider, CoreArrayProviderInner, Guard, Ref, RefCountable}; +use crate::string::{raw_to_string, BnString}; use crate::types::Type; use binaryninjacore_sys::{BNDataVariable, BNDataVariableAndName, BNFreeDataVariables, BNFreeILInstructionList, BNFreeIndirectBranchList, BNFreeMergedVariableList, BNFreeStackVariableReferenceList, BNFreeUserVariableValues, BNFreeVariableList, BNFreeVariableNameAndTypeList, BNFromVariableIdentifier, BNGetMediumLevelILVariableSSAVersions, BNIndirectBranchInfo, BNLookupTableEntry, BNMergedVariable, BNPossibleValueSet, BNRegisterValue, BNRegisterValueType, BNStackVariableReference, BNToVariableIdentifier, BNUserVariableValue, BNValueRange, BNVariable, BNVariableNameAndType, BNVariableSourceType}; use std::collections::HashSet; @@ -40,10 +40,23 @@ impl From for DataVariable { } } +impl From<&BNDataVariable> for DataVariable { + fn from(value: &BNDataVariable) -> Self { + Self { + address: value.address, + ty: Conf::new( + unsafe { Type::ref_from_raw(value.type_).to_owned() }, + value.typeConfidence, + ), + auto_discovered: value.autoDiscovered, + } + } +} + impl CoreArrayProvider for DataVariable { type Raw = BNDataVariable; type Context = (); - type Wrapped<'a> = Self; + type Wrapped<'a> = Guard<'a, Self>; } unsafe impl CoreArrayProviderInner for DataVariable { @@ -52,7 +65,7 @@ unsafe impl CoreArrayProviderInner for DataVariable { } unsafe fn wrap_raw<'a>(raw: &'a Self::Raw, _context: &'a Self::Context) -> Self::Wrapped<'a> { - Self::from(*raw) + Guard::new(Self::from(raw), raw) } } @@ -89,6 +102,21 @@ impl From for NamedDataVariableWithType { } } +impl From<&BNDataVariableAndName> for NamedDataVariableWithType { + fn from(value: &BNDataVariableAndName) -> Self { + Self { + address: value.address, + ty: Conf::new( + unsafe { Type::from_raw(value.type_).to_owned() }, + value.typeConfidence, + ), + // TODO: I dislike using this function here. + name: raw_to_string(value.name as *mut _).unwrap(), + auto_discovered: value.autoDiscovered, + } + } +} + impl From for BNDataVariableAndName { fn from(value: NamedDataVariableWithType) -> Self { let bn_name = BnString::new(value.name); @@ -135,6 +163,21 @@ impl From for NamedVariableWithType { } } +impl From<&BNVariableNameAndType> for NamedVariableWithType { + fn from(value: &BNVariableNameAndType) -> Self { + Self { + variable: value.var.into(), + ty: Conf::new( + unsafe { Type::from_raw(value.type_).to_owned() }, + value.typeConfidence, + ), + // TODO: I dislike using this function here. + name: raw_to_string(value.name as *mut _).unwrap(), + auto_defined: value.autoDefined, + } + } +} + impl From for BNVariableNameAndType { fn from(value: NamedVariableWithType) -> Self { let bn_name = BnString::new(value.name); @@ -151,7 +194,7 @@ impl From for BNVariableNameAndType { impl CoreArrayProvider for NamedVariableWithType { type Raw = BNVariableNameAndType; type Context = (); - type Wrapped<'a> = Guard<'a, NamedVariableWithType>; + type Wrapped<'a> = Guard<'a, Self>; } unsafe impl CoreArrayProviderInner for NamedVariableWithType { @@ -160,7 +203,7 @@ unsafe impl CoreArrayProviderInner for NamedVariableWithType { } unsafe fn wrap_raw<'a>(raw: &'a Self::Raw, _context: &'a Self::Context) -> Self::Wrapped<'a> { - unsafe { Guard::new(NamedVariableWithType::from(*raw), raw) } + unsafe { Guard::new(Self::from(raw), raw) } } } @@ -234,6 +277,25 @@ impl From for StackVariableReference { } } +impl From<&BNStackVariableReference> for StackVariableReference { + fn from(value: &BNStackVariableReference) -> Self { + Self { + source_operand: value.sourceOperand, + variable_type: Conf::new( + unsafe { Type::from_raw(value.type_).to_owned() }, + value.typeConfidence, + ), + // TODO: I dislike using this function here. + name: raw_to_string(value.name as *mut _).unwrap(), + // TODO: It might be beneficial to newtype the identifier as VariableIdentifier. + variable: Variable::from_identifier(value.varIdentifier), + offset: value.referencedOffset, + size: value.size, + } + } +} + + impl From for BNStackVariableReference { fn from(value: StackVariableReference) -> Self { let bn_name = BnString::new(value.name); @@ -252,7 +314,7 @@ impl From for BNStackVariableReference { impl CoreArrayProvider for StackVariableReference { type Raw = BNStackVariableReference; type Context = (); - type Wrapped<'a> = Guard<'a, Self>; + type Wrapped<'a> = Self; } unsafe impl CoreArrayProviderInner for StackVariableReference { @@ -260,8 +322,8 @@ unsafe impl CoreArrayProviderInner for StackVariableReference { BNFreeStackVariableReferenceList(raw, count) } - unsafe fn wrap_raw<'a>(raw: &'a Self::Raw, context: &'a Self::Context) -> Self::Wrapped<'a> { - Guard::new(Self::from(*raw), context) + unsafe fn wrap_raw<'a>(raw: &'a Self::Raw, _context: &'a Self::Context) -> Self::Wrapped<'a> { + raw.into() } } @@ -293,29 +355,10 @@ unsafe impl CoreArrayProviderInner for SSAVariable { } } -impl CoreArrayProvider for Array { - type Raw = BNVariable; - type Context = Ref; - type Wrapped<'a> = Self; -} - -unsafe impl CoreArrayProviderInner for Array { - unsafe fn free(raw: *mut Self::Raw, _count: usize, _context: &Self::Context) { - BNFreeVariableList(raw) - } - - unsafe fn wrap_raw<'a>(raw: &'a Self::Raw, context: &'a Self::Context) -> Self::Wrapped<'a> { - let mut count = 0; - let versions = - unsafe { BNGetMediumLevelILVariableSSAVersions(context.handle, raw, &mut count) }; - Array::new(versions, count, Variable::from(*raw)) - } -} - /// Variables exist within functions at Medium Level IL or higher. /// -/// As such, they are to be used within the context of a [`crate::Function`]. -/// See [`crate::Function::get_variable_name`] as an example of how to interact with variables. +/// As such, they are to be used within the context of a [`Function`]. +/// See [`Function::get_variable_name`] as an example of how to interact with variables. #[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)] pub struct Variable { pub ty: VariableSourceType, @@ -352,6 +395,12 @@ impl From for Variable { } } +impl From<&BNVariable> for Variable { + fn from(value: &BNVariable) -> Self { + Self::from(*value) + } +} + impl From for BNVariable { fn from(value: Variable) -> Self { Self { From 5b4199d1b5774459267e90cb671afd12b8aaf550 Mon Sep 17 00:00:00 2001 From: Mason Reed Date: Fri, 27 Dec 2024 22:33:22 -0500 Subject: [PATCH 04/93] More rust cleanup Trying to comment more TODO's as I go along. - Renamed llil::Function to llil::LowLevelILFunction for clarity and consistency - Take base structures by ref in StructureBuilder::set_base_structures to prevent premature drops - Added more raw to wrapper conversions - Removed UB prone apis - Getting rid of more core module references, use std! - Removed improper Guard usage in array impls for wrapper types with no context - Untangling the UB of the Label api, still only half done :/ --- arch/msp430/src/architecture.rs | 2 +- arch/msp430/src/lift.rs | 24 +++-- arch/riscv/src/lib.rs | 27 ++++-- plugins/warp/src/cache.rs | 8 +- plugins/warp/src/convert.rs | 2 +- plugins/warp/src/lib.rs | 6 +- rust/examples/pdb-ng/src/type_parser.rs | 4 +- rust/src/architecture.rs | 12 +-- rust/src/basicblock.rs | 31 ++----- rust/src/binaryview.rs | 60 +++++++------ rust/src/custombinaryview.rs | 9 +- rust/src/function.rs | 14 +-- rust/src/linearview.rs | 4 +- rust/src/llil/block.rs | 46 +++++----- rust/src/llil/expression.rs | 6 +- rust/src/llil/function.rs | 82 +++++++++-------- rust/src/llil/instruction.rs | 4 +- rust/src/llil/lifting.rs | 86 ++++++++++-------- rust/src/llil/mod.rs | 12 +-- rust/src/llil/operation.rs | 6 +- rust/src/metadata.rs | 3 +- rust/src/mlil/block.rs | 53 +++++------ rust/src/mlil/function.rs | 111 ++++++++---------------- rust/src/mlil/instruction.rs | 1 + rust/src/platform.rs | 7 +- rust/src/references.rs | 1 + rust/src/tags.rs | 2 +- rust/src/types.rs | 58 +++++++------ rust/src/variable.rs | 10 +-- rust/src/workflow.rs | 12 +-- 30 files changed, 349 insertions(+), 354 deletions(-) diff --git a/arch/msp430/src/architecture.rs b/arch/msp430/src/architecture.rs index a5ba1b91a..3008ed4ea 100644 --- a/arch/msp430/src/architecture.rs +++ b/arch/msp430/src/architecture.rs @@ -4,7 +4,7 @@ use crate::register::Register; use binaryninja::{ architecture::{ - Architecture, BranchInfo, CoreArchitecture, CustomArchitectureHandle, FlagCondition, + Architecture, CoreArchitecture, CustomArchitectureHandle, FlagCondition, InstructionInfo, UnusedIntrinsic, UnusedRegisterStack, UnusedRegisterStackInfo, }, disassembly::{InstructionTextToken, InstructionTextTokenContents}, diff --git a/arch/msp430/src/lift.rs b/arch/msp430/src/lift.rs index feb8ce357..c7da2fba3 100644 --- a/arch/msp430/src/lift.rs +++ b/arch/msp430/src/lift.rs @@ -146,17 +146,21 @@ macro_rules! conditional_jump { $il.if_expr( $cond, - true_label.unwrap_or_else(|| &new_true), - false_label.unwrap_or_else(|| &new_false), + &mut true_label.unwrap_or(new_true), + &mut false_label.unwrap_or(new_false), ) .append(); - if true_label.is_none() { + if let Some(true_label) = true_label { + $il.update_label_for_address(true_addr, true_label); + } else { $il.mark_label(&mut new_true); $il.jump($il.const_ptr(true_addr)).append(); } - - if false_label.is_none() { + + if let Some(false_label) = false_label { + $il.update_label_for_address(false_addr, false_label); + } else { $il.mark_label(&mut new_false); } }; @@ -277,8 +281,9 @@ pub(crate) fn lift_instruction(inst: &Instruction, addr: u64, il: &Lifter { - il.goto(label).append(); + Some(mut label) => { + il.goto(&mut label).append(); + il.update_label_for_address(fixed_addr, label); } None => { il.jump(il.const_ptr(fixed_addr)).append(); @@ -411,8 +416,9 @@ pub(crate) fn lift_instruction(inst: &Instruction, addr: u64, il: &Lifter { let dest = if let Some(Operand::Immediate(dest)) = inst.destination() { - if let Some(label) = il.label_for_address(*dest as u64) { - il.goto(label).append(); + if let Some(mut label) = il.label_for_address(*dest as u64) { + il.goto(&mut label).append(); + il.update_label_for_address(*dest as u64, label); return; } else { il.const_ptr(*dest as u64) diff --git a/arch/riscv/src/lib.rs b/arch/riscv/src/lib.rs index e3952b7ec..c52a01cd5 100644 --- a/arch/riscv/src/lib.rs +++ b/arch/riscv/src/lib.rs @@ -1196,7 +1196,11 @@ impl architecture::Architecture fo let target = addr.wrapping_add(j.imm() as i64 as u64); match (j.rd().id(), il.label_for_address(target)) { - (0, Some(l)) => il.goto(l), + (0, Some(mut l)) => { + let jump_expr = il.goto(&mut l); + il.update_label_for_address(target, l); + jump_expr + }, (0, None) => il.jump(il.const_ptr(target)), (_, _) => il.call(il.const_ptr(target)), } @@ -1260,17 +1264,20 @@ impl architecture::Architecture fo let tt = addr.wrapping_add(b.imm() as i64 as u64); { - let f = il.label_for_address(ft).unwrap_or_else(|| { + let mut f = il.label_for_address(ft).unwrap_or_else(|| { new_false = Some(Label::new()); - new_false.as_ref().unwrap() + new_false.unwrap() }); - let t = il.label_for_address(tt).unwrap_or_else(|| { + let mut t = il.label_for_address(tt).unwrap_or_else(|| { new_true = Some(Label::new()); - new_true.as_ref().unwrap() + new_true.unwrap() }); - il.if_expr(cond_expr, t, f).append(); + il.if_expr(cond_expr, &mut t, &mut f).append(); + + il.update_label_for_address(ft, f); + il.update_label_for_address(tt, t); } if let Some(t) = new_true.as_mut() { @@ -1430,12 +1437,14 @@ impl architecture::Architecture fo let cond_expr = il.cmp_e(max_width, dest_reg, 0u64); let ft = addr.wrapping_add(inst_len); - let f = il.label_for_address(ft).unwrap_or_else(|| { + let mut f = il.label_for_address(ft).unwrap_or_else(|| { new_false = Some(Label::new()); - new_false.as_ref().unwrap() + new_false.unwrap() }); - il.if_expr(cond_expr, &t, f).append(); + il.if_expr(cond_expr, &mut t, &mut f).append(); + + il.update_label_for_address(ft, f); } il.mark_label(&mut t); diff --git a/plugins/warp/src/cache.rs b/plugins/warp/src/cache.rs index 340b00d78..2991644bb 100644 --- a/plugins/warp/src/cache.rs +++ b/plugins/warp/src/cache.rs @@ -66,7 +66,7 @@ pub fn try_cached_function_match(function: &BNFunction) -> Option { pub fn cached_function( function: &BNFunction, - llil: &llil::Function>, + llil: &llil::LowLevelILFunction>, ) -> Function { let view = function.view(); let view_id = ViewID::from(view.as_ref()); @@ -120,7 +120,7 @@ where pub fn cached_function_guid( function: &BNFunction, - llil: &llil::Function>, + llil: &llil::LowLevelILFunction>, ) -> FunctionGUID { let view = function.view(); let view_id = ViewID::from(view); @@ -201,7 +201,7 @@ impl FunctionCache { pub fn function( &self, function: &BNFunction, - llil: &llil::Function>, + llil: &llil::LowLevelILFunction>, ) -> Function { let function_id = FunctionID::from(function); match self.cache.get(&function_id) { @@ -329,7 +329,7 @@ impl GUIDCache { pub fn function_guid( &self, function: &BNFunction, - llil: &llil::Function>, + llil: &llil::LowLevelILFunction>, ) -> FunctionGUID { let function_id = FunctionID::from(function); match self.cache.get(&function_id) { diff --git a/plugins/warp/src/convert.rs b/plugins/warp/src/convert.rs index f3e44bd95..370fadcae 100644 --- a/plugins/warp/src/convert.rs +++ b/plugins/warp/src/convert.rs @@ -473,7 +473,7 @@ pub fn to_bn_type(arch: &A, ty: &Type) -> BNRef { ); } } - builder.set_base_structures(base_structs); + builder.set_base_structures(&base_structs); BNType::structure(&builder.finalize()) } TypeClass::Enumeration(c) => { diff --git a/plugins/warp/src/lib.rs b/plugins/warp/src/lib.rs index 7dae3ec15..ebe39b921 100644 --- a/plugins/warp/src/lib.rs +++ b/plugins/warp/src/lib.rs @@ -43,7 +43,7 @@ pub fn user_signature_dir() -> PathBuf { pub fn build_function( func: &BNFunction, - llil: &llil::Function>, + llil: &llil::LowLevelILFunction>, ) -> Function { let bn_fn_ty = func.function_type(); Function { @@ -75,7 +75,7 @@ pub fn sorted_basic_blocks(func: &BNFunction) -> Vec( func: &BNFunction, - llil: &llil::Function>, + llil: &llil::LowLevelILFunction>, ) -> FunctionGUID { let basic_blocks = sorted_basic_blocks(func); let basic_block_guids = basic_blocks @@ -87,7 +87,7 @@ pub fn function_guid( pub fn basic_block_guid( basic_block: &BNBasicBlock, - llil: &llil::Function>, + llil: &llil::LowLevelILFunction>, ) -> BasicBlockGUID { let func = basic_block.function(); let view = func.view(); diff --git a/rust/examples/pdb-ng/src/type_parser.rs b/rust/examples/pdb-ng/src/type_parser.rs index 730cdf458..195736949 100644 --- a/rust/examples/pdb-ng/src/type_parser.rs +++ b/rust/examples/pdb-ng/src/type_parser.rs @@ -1037,7 +1037,7 @@ impl<'a, S: Source<'a> + 'a> PDBParserInstance<'a, S> { .ok_or_else(|| anyhow!("Expected class in ns stack"))? ); } - structure.set_base_structures(bases); + structure.set_base_structures(&bases); if self .settings @@ -1107,7 +1107,7 @@ impl<'a, S: Source<'a> + 'a> PDBParserInstance<'a, S> { min_width = min_width.max(base.width); } - vt.set_base_structures(vt_bases); + vt.set_base_structures(&vt_bases); vt.set_propagates_data_var_refs(true); for (offset, (name, method)) in virt_methods { diff --git a/rust/src/architecture.rs b/rust/src/architecture.rs index 573d4a240..c80c3aafe 100644 --- a/rust/src/architecture.rs +++ b/rust/src/architecture.rs @@ -1290,7 +1290,7 @@ impl Architecture for CoreArchitecture { fn instruction_info(&self, data: &[u8], addr: u64) -> Option { let mut info = BNInstructionInfo::default(); - let success = unsafe { + if unsafe { BNGetInstructionInfo( self.handle, data.as_ptr(), @@ -1298,9 +1298,7 @@ impl Architecture for CoreArchitecture { data.len(), &mut info, ) - }; - - if success { + } { Some(info.into()) } else { None @@ -1325,12 +1323,12 @@ impl Architecture for CoreArchitecture { &mut result, &mut count, ) { - let vec = std::slice::from_raw_parts(result, count) + let instr_text_tokens = std::slice::from_raw_parts(result, count) .iter() .map(|x| InstructionTextToken::from_raw(x).to_owned()) .collect(); BNFreeInstructionText(result, count); - Some((consumed, vec)) + Some((consumed, instr_text_tokens)) } else { None } @@ -2913,7 +2911,9 @@ where getAddressSize: Some(cb_address_size::), getDefaultIntegerSize: Some(cb_default_integer_size::), getInstructionAlignment: Some(cb_instruction_alignment::), + // TODO: Make getOpcodeDisplayLength optional. getMaxInstructionLength: Some(cb_max_instr_len::), + // TODO: Make getOpcodeDisplayLength optional. getOpcodeDisplayLength: Some(cb_opcode_display_len::), getAssociatedArchitectureByAddress: Some(cb_associated_arch_by_addr::), getInstructionInfo: Some(cb_instruction_info::), diff --git a/rust/src/basicblock.rs b/rust/src/basicblock.rs index 87cfab0c9..75566a064 100644 --- a/rust/src/basicblock.rs +++ b/rust/src/basicblock.rs @@ -26,30 +26,12 @@ enum EdgeDirection { } pub struct Edge<'a, C: 'a + BlockContext> { - branch: BranchType, - back_edge: bool, - source: Guard<'a, BasicBlock>, + pub branch: BranchType, + pub back_edge: bool, + pub source: Guard<'a, BasicBlock>, target: Guard<'a, BasicBlock>, } -impl<'a, C: 'a + BlockContext> Edge<'a, C> { - pub fn branch_type(&self) -> super::BranchType { - self.branch - } - - pub fn back_edge(&self) -> bool { - self.back_edge - } - - pub fn source(&self) -> &BasicBlock { - &self.source - } - - pub fn target(&self) -> &BasicBlock { - &self.target - } -} - impl<'a, C: 'a + fmt::Debug + BlockContext> fmt::Debug for Edge<'a, C> { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!( @@ -77,7 +59,6 @@ unsafe impl<'a, C: 'a + BlockContext> CoreArrayProviderInner for Edge<'a, C> { } unsafe fn wrap_raw<'b>(raw: &'b Self::Raw, context: &'b Self::Context) -> Self::Wrapped<'b> { - // TODO: Why not just take a ref to this? Why do we store a guard in Edge? let edge_target = Guard::new( BasicBlock::from_raw(raw.target, context.orig_block.context.clone()), raw, @@ -118,9 +99,6 @@ pub struct BasicBlock { context: C, } -unsafe impl Send for BasicBlock {} -unsafe impl Sync for BasicBlock {} - impl BasicBlock { pub(crate) unsafe fn from_raw(handle: *mut BNBasicBlock, context: C) -> Self { Self { handle, context } @@ -315,3 +293,6 @@ unsafe impl CoreArrayProviderInner for BasicBlock { Guard::new(BasicBlock::from_raw(*raw, context.clone()), context) } } + +unsafe impl Send for BasicBlock {} +unsafe impl Sync for BasicBlock {} diff --git a/rust/src/binaryview.rs b/rust/src/binaryview.rs index 96ea0b682..c806b6979 100644 --- a/rust/src/binaryview.rs +++ b/rust/src/binaryview.rs @@ -14,7 +14,11 @@ //! A view on binary data and queryable interface of a binary file. //! -//! One key job of BinaryView is file format parsing which allows Binary Ninja to read, write, insert, remove portions of the file given a virtual address. For the purposes of this documentation we define a virtual address as the memory address that the various pieces of the physical file will be loaded at. +//! One key job of BinaryView is file format parsing which allows Binary Ninja to read, write, +//! insert, remove portions of the file given a virtual address. +//! +//! For the purposes of this documentation we define a virtual address as the memory address that +//! the various pieces of the physical file will be loaded at. //! TODO : Mirror the Python docs for this use binaryninjacore_sys::*; @@ -25,8 +29,8 @@ pub use binaryninjacore_sys::BNModificationStatus as ModificationStatus; use std::collections::HashMap; use std::ffi::{c_char, c_void}; use std::ops::Range; -use std::{ops, ptr, result, slice}; - +use std::{ops, result, slice}; +use std::ptr::NonNull; use crate::architecture::{Architecture, CoreArchitecture}; use crate::basicblock::BasicBlock; use crate::component::{Component, ComponentBuilder, IntoComponentGuid}; @@ -394,7 +398,7 @@ pub trait BinaryViewExt: BinaryViewBase { fn symbol_by_address(&self, addr: u64) -> Option> { unsafe { - let raw_sym_ptr = BNGetSymbolByAddress(self.as_ref().handle, addr, ptr::null_mut()); + let raw_sym_ptr = BNGetSymbolByAddress(self.as_ref().handle, addr, std::ptr::null_mut()); match raw_sym_ptr.is_null() { false => Some(Symbol::ref_from_raw(raw_sym_ptr)), true => None, @@ -409,7 +413,7 @@ pub trait BinaryViewExt: BinaryViewBase { let raw_sym_ptr = BNGetSymbolByRawName( self.as_ref().handle, raw_name.as_ref().as_ptr() as *mut _, - ptr::null_mut(), + std::ptr::null_mut(), ); match raw_sym_ptr.is_null() { false => Some(Symbol::ref_from_raw(raw_sym_ptr)), @@ -421,7 +425,7 @@ pub trait BinaryViewExt: BinaryViewBase { fn symbols(&self) -> Array { unsafe { let mut count = 0; - let handles = BNGetSymbols(self.as_ref().handle, &mut count, ptr::null_mut()); + let handles = BNGetSymbols(self.as_ref().handle, &mut count, std::ptr::null_mut()); Array::new(handles, count, ()) } @@ -436,7 +440,7 @@ pub trait BinaryViewExt: BinaryViewBase { self.as_ref().handle, raw_name.as_ref().as_ptr() as *mut _, &mut count, - ptr::null_mut(), + std::ptr::null_mut(), ); Array::new(handles, count, ()) @@ -452,7 +456,7 @@ pub trait BinaryViewExt: BinaryViewBase { range.start, len, &mut count, - ptr::null_mut(), + std::ptr::null_mut(), ); Array::new(handles, count, ()) @@ -463,7 +467,7 @@ pub trait BinaryViewExt: BinaryViewBase { unsafe { let mut count = 0; let handles = - BNGetSymbolsOfType(self.as_ref().handle, ty.into(), &mut count, ptr::null_mut()); + BNGetSymbolsOfType(self.as_ref().handle, ty.into(), &mut count, std::ptr::null_mut()); Array::new(handles, count, ()) } @@ -479,7 +483,7 @@ pub trait BinaryViewExt: BinaryViewBase { range.start, len, &mut count, - ptr::null_mut(), + std::ptr::null_mut(), ); Array::new(handles, count, ()) @@ -501,7 +505,7 @@ pub trait BinaryViewExt: BinaryViewBase { let raw_type = if let Some(t) = ty.into() { t.handle } else { - ptr::null_mut() + std::ptr::null_mut() }; unsafe { @@ -657,8 +661,8 @@ pub trait BinaryViewExt: BinaryViewBase { } let mut progress_raw = ProgressContext(progress); - let mut result_ids: *mut *mut c_char = ptr::null_mut(); - let mut result_names: *mut BNQualifiedName = ptr::null_mut(); + let mut result_ids: *mut *mut c_char = std::ptr::null_mut(); + let mut result_names: *mut BNQualifiedName = std::ptr::null_mut(); let result_count = unsafe { BNDefineAnalysisTypes( self.as_ref().handle, @@ -936,7 +940,7 @@ pub trait BinaryViewExt: BinaryViewBase { plat.handle, addr, false, - ptr::null_mut(), + std::ptr::null_mut(), ); if handle.is_null() { @@ -957,7 +961,7 @@ pub trait BinaryViewExt: BinaryViewBase { unsafe { let func_type = match func_type { Some(func_type) => func_type.handle, - None => ptr::null_mut(), + None => std::ptr::null_mut(), }; let handle = BNAddFunctionForAnalysis( @@ -1466,15 +1470,15 @@ pub trait BinaryViewExt: BinaryViewBase { let result = unsafe { BNGetComponentByGuid( self.as_ref().handle, - name.as_ref().as_ptr() as *const core::ffi::c_char, + name.as_ref().as_ptr() as *const c_char, ) }; - core::ptr::NonNull::new(result).map(|h| unsafe { Component::from_raw(h) }) + NonNull::new(result).map(|h| unsafe { Component::from_raw(h) }) } fn root_component(&self) -> Option { let result = unsafe { BNGetRootComponent(self.as_ref().handle) }; - core::ptr::NonNull::new(result).map(|h| unsafe { Component::from_raw(h) }) + NonNull::new(result).map(|h| unsafe { Component::from_raw(h) }) } fn component_builder(&self) -> ComponentBuilder { @@ -1486,10 +1490,10 @@ pub trait BinaryViewExt: BinaryViewBase { let result = unsafe { BNGetComponentByPath( self.as_ref().handle, - path.as_ref().as_ptr() as *const core::ffi::c_char, + path.as_ref().as_ptr() as *const c_char, ) }; - core::ptr::NonNull::new(result).map(|h| unsafe { Component::from_raw(h) }) + NonNull::new(result).map(|h| unsafe { Component::from_raw(h) }) } fn remove_component(&self, component: &Component) -> bool { @@ -1526,10 +1530,10 @@ pub trait BinaryViewExt: BinaryViewBase { let result = unsafe { BNGetBinaryViewTypeLibrary( self.as_ref().handle, - name.as_ref().as_ptr() as *const core::ffi::c_char, + name.as_ref().as_ptr() as *const c_char, ) }; - core::ptr::NonNull::new(result).map(|h| unsafe { TypeLibrary::from_raw(h) }) + NonNull::new(result).map(|h| unsafe { TypeLibrary::from_raw(h) }) } /// Should be called by custom py:py:class:`BinaryView` implementations @@ -1576,7 +1580,7 @@ pub trait BinaryViewExt: BinaryViewBase { let mut lib_ref = lib .as_mut() .map(|l| unsafe { l.as_raw() } as *mut _) - .unwrap_or(ptr::null_mut()); + .unwrap_or(std::ptr::null_mut()); let result = unsafe { BNBinaryViewImportTypeLibraryType( self.as_ref().handle, @@ -1604,7 +1608,7 @@ pub trait BinaryViewExt: BinaryViewBase { let mut lib_ref = lib .as_mut() .map(|l| unsafe { l.as_raw() } as *mut _) - .unwrap_or(ptr::null_mut()); + .unwrap_or(std::ptr::null_mut()); let result = unsafe { BNBinaryViewImportTypeLibraryObject( self.as_ref().handle, @@ -1672,7 +1676,7 @@ pub trait BinaryViewExt: BinaryViewBase { addr: u64, platform: &Platform, ) -> Option<(TypeLibrary, QualifiedName)> { - let mut result_lib = ptr::null_mut(); + let mut result_lib = std::ptr::null_mut(); let mut result_name = Default::default(); let success = unsafe { BNBinaryViewLookupImportedObjectLibrary( @@ -1686,7 +1690,7 @@ pub trait BinaryViewExt: BinaryViewBase { if !success { return None; } - let lib = unsafe { TypeLibrary::from_raw(ptr::NonNull::new(result_lib)?) }; + let lib = unsafe { TypeLibrary::from_raw(NonNull::new(result_lib)?) }; let name = QualifiedName(result_name); Some((lib, name)) } @@ -1698,7 +1702,7 @@ pub trait BinaryViewExt: BinaryViewBase { &self, name: &QualifiedNameAndType, ) -> Option<(TypeLibrary, QualifiedName)> { - let mut result_lib = ptr::null_mut(); + let mut result_lib = std::ptr::null_mut(); let mut result_name = Default::default(); let success = unsafe { BNBinaryViewLookupImportedTypeLibrary( @@ -1711,7 +1715,7 @@ pub trait BinaryViewExt: BinaryViewBase { if !success { return None; } - let lib = unsafe { TypeLibrary::from_raw(ptr::NonNull::new(result_lib)?) }; + let lib = unsafe { TypeLibrary::from_raw(NonNull::new(result_lib)?) }; let name = QualifiedName(result_name); Some((lib, name)) } diff --git a/rust/src/custombinaryview.rs b/rust/src/custombinaryview.rs index c02cbfd5c..3b947c3a7 100644 --- a/rust/src/custombinaryview.rs +++ b/rust/src/custombinaryview.rs @@ -271,6 +271,11 @@ pub struct BinaryViewType { } impl BinaryViewType { + pub(crate) unsafe fn from_raw(handle: *mut BNBinaryViewType) -> Self { + debug_assert!(!handle.is_null()); + Self { handle } + } + pub fn list_all() -> Array { unsafe { let mut count: usize = 0; @@ -292,7 +297,7 @@ impl BinaryViewType { let bytes = name.into_bytes_with_nul(); let handle = unsafe { BNGetBinaryViewTypeByName(bytes.as_ref().as_ptr() as *const _) }; match handle.is_null() { - false => Ok(BinaryViewType { handle }), + false => Ok(unsafe { BinaryViewType::from_raw(handle) }), true => Err(()), } } @@ -333,7 +338,7 @@ unsafe impl CoreArrayProviderInner for BinaryViewType { BNFreeBinaryViewTypeList(raw); } unsafe fn wrap_raw<'a>(raw: &'a Self::Raw, _context: &'a Self::Context) -> Self::Wrapped<'a> { - Guard::new(BinaryViewType { handle: *raw }, &()) + Guard::new(BinaryViewType::from_raw(*raw), &()) } } diff --git a/rust/src/function.rs b/rust/src/function.rs index 88b3a9c80..4513ada8f 100644 --- a/rust/src/function.rs +++ b/rust/src/function.rs @@ -660,7 +660,7 @@ impl Function { let adjust_ptr = adjust_type .as_mut() .map(|x| x as *mut _) - .unwrap_or(core::ptr::null_mut()); + .unwrap_or(std::ptr::null_mut()); unsafe { BNSetUserCallTypeAdjustment(self.handle, arch.handle, addr, adjust_ptr) } } @@ -910,7 +910,7 @@ impl Function { arch: Option, ) -> RegisterValue { let arch = arch.unwrap_or_else(|| self.arch()); - let func_type = func_type.map(|f| f.handle).unwrap_or(core::ptr::null_mut()); + let func_type = func_type.map(|f| f.handle).unwrap_or(std::ptr::null_mut()); let value = unsafe { BNGetParameterValueAtInstruction(self.handle, arch.handle, addr, func_type, i) }; value.into() @@ -1336,7 +1336,7 @@ impl Function { let tags = unsafe { match (tag_type, auto) { // received a tag_type, BinaryView found none - (Some(None), _) => return Array::new(core::ptr::null_mut(), 0, ()), + (Some(None), _) => return Array::new(std::ptr::null_mut(), 0, ()), // with tag_type (Some(Some(tag_type)), None) => { @@ -1597,7 +1597,7 @@ impl Function { let enum_display_typeid = enum_display_typeid.map(BnStrCompatible::into_bytes_with_nul); let enum_display_typeid_ptr = enum_display_typeid .map(|x| x.as_ref().as_ptr() as *const c_char) - .unwrap_or(core::ptr::null()); + .unwrap_or(std::ptr::null()); unsafe { BNSetIntegerConstantDisplayType( self.handle, @@ -1863,7 +1863,7 @@ impl Function { &self, settings: Option<&DisassemblySettings>, ) -> Array { - let settings = settings.map(|s| s.handle).unwrap_or(core::ptr::null_mut()); + let settings = settings.map(|s| s.handle).unwrap_or(std::ptr::null_mut()); let mut count = 0; let lines = unsafe { BNGetFunctionTypeTokens(self.handle, settings, &mut count) }; assert!(!lines.is_null()); @@ -2236,7 +2236,7 @@ impl Function { view_type: FunctionViewType, settings: Option, ) -> Ref { - let settings_raw = settings.map(|s| s.handle).unwrap_or(core::ptr::null_mut()); + let settings_raw = settings.map(|s| s.handle).unwrap_or(std::ptr::null_mut()); let result = unsafe { BNCreateFunctionGraph(self.handle, view_type.as_raw().0, settings_raw) }; unsafe { Ref::new(FlowGraph::from_raw(result)) } } @@ -2293,7 +2293,7 @@ unsafe impl CoreArrayProviderInner for Function { } unsafe fn wrap_raw<'a>(raw: &'a Self::Raw, context: &'a Self::Context) -> Self::Wrapped<'a> { - Guard::new(Function { handle: *raw }, context) + Guard::new(Self::from_raw(*raw), context) } } diff --git a/rust/src/linearview.rs b/rust/src/linearview.rs index 8e0a0fcd4..14ab911c4 100644 --- a/rust/src/linearview.rs +++ b/rust/src/linearview.rs @@ -434,8 +434,8 @@ unsafe impl CoreArrayProviderInner for LinearDisassemblyLine { BNFreeLinearDisassemblyLines(raw, count); } - unsafe fn wrap_raw<'a>(raw: &'a Self::Raw, _context: &'a Self::Context) -> Self::Wrapped<'a> { + 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(LinearDisassemblyLine::from_raw(raw), _context) + Guard::new(Self::from_raw(raw), context) } } diff --git a/rust/src/llil/block.rs b/rust/src/llil/block.rs index d904d2d8d..b1f33c80a 100644 --- a/rust/src/llil/block.rs +++ b/rust/src/llil/block.rs @@ -19,17 +19,17 @@ use crate::basicblock::{BasicBlock, BlockContext}; use super::*; -pub struct BlockIter<'func, A, M, F> +pub struct LowLevelILBlockIter<'func, A, M, F> where A: 'func + Architecture, M: FunctionMutability, F: FunctionForm, { - function: &'func Function, + function: &'func LowLevelILFunction, range: Range, } -impl<'func, A, M, F> Iterator for BlockIter<'func, A, M, F> +impl<'func, A, M, F> Iterator for LowLevelILBlockIter<'func, A, M, F> where A: 'func + Architecture, M: FunctionMutability, @@ -45,34 +45,24 @@ where } } -pub struct Block<'func, A, M, F> +#[derive(Copy)] +pub struct LowLevelILBlock<'func, A, M, F> where A: 'func + Architecture, M: FunctionMutability, F: FunctionForm, { - pub(crate) function: &'func Function, + pub(crate) function: &'func LowLevelILFunction, } -impl<'func, A, M, F> fmt::Debug for Block<'func, A, M, F> +impl<'func, A, M, F> BlockContext for LowLevelILBlock<'func, A, M, F> where A: 'func + Architecture, M: FunctionMutability, F: FunctionForm, { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "llil_bb {:?}", self.function) - } -} - -impl<'func, A, M, F> BlockContext for Block<'func, A, M, F> -where - A: 'func + Architecture, - M: FunctionMutability, - F: FunctionForm, -{ - type Iter = BlockIter<'func, A, M, F>; type Instruction = Instruction<'func, A, M, F>; + type Iter = LowLevelILBlockIter<'func, A, M, F>; fn start(&self, block: &BasicBlock) -> Instruction<'func, A, M, F> { Instruction { @@ -81,22 +71,34 @@ where } } - fn iter(&self, block: &BasicBlock) -> BlockIter<'func, A, M, F> { - BlockIter { + fn iter(&self, block: &BasicBlock) -> LowLevelILBlockIter<'func, A, M, F> { + LowLevelILBlockIter { function: self.function, range: block.raw_start()..block.raw_end(), } } } -impl<'func, A, M, F> Clone for Block<'func, A, M, F> +impl<'func, A, M, F> fmt::Debug for LowLevelILBlock<'func, A, M, F> +where + A: 'func + Architecture, + M: FunctionMutability, + F: FunctionForm, +{ + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + // TODO: Make this better + write!(f, "llil_bb {:?}", self.function) + } +} + +impl<'func, A, M, F> Clone for LowLevelILBlock<'func, A, M, F> where A: 'func + Architecture, M: FunctionMutability, F: FunctionForm, { fn clone(&self) -> Self { - Block { + LowLevelILBlock { function: self.function, } } diff --git a/rust/src/llil/expression.rs b/rust/src/llil/expression.rs index f04368a70..e3dc72ef0 100644 --- a/rust/src/llil/expression.rs +++ b/rust/src/llil/expression.rs @@ -44,7 +44,7 @@ where F: FunctionForm, R: ExpressionResultType, { - pub(crate) function: &'func Function, + pub(crate) function: &'func LowLevelILFunction, pub(crate) expr_idx: usize, // tag the 'return' type of this expression @@ -58,7 +58,7 @@ where F: FunctionForm, R: ExpressionResultType, { - pub(crate) fn new(function: &'func Function, expr_idx: usize) -> Self { + pub(crate) fn new(function: &'func LowLevelILFunction, expr_idx: usize) -> Self { Self { function, expr_idx, @@ -84,7 +84,7 @@ where } fn common_info<'func, A, M, F>( - function: &'func Function, + function: &'func LowLevelILFunction, op: BNLowLevelILInstruction, ) -> ExprInfo<'func, A, M, F> where diff --git a/rust/src/llil/function.rs b/rust/src/llil/function.rs index 0ebec5d18..ff75e0c7a 100644 --- a/rust/src/llil/function.rs +++ b/rust/src/llil/function.rs @@ -19,9 +19,11 @@ use binaryninjacore_sys::BNNewLowLevelILFunctionReference; use std::borrow::Borrow; use std::marker::PhantomData; +use std::hash::{Hash, Hasher}; use crate::architecture::CoreArchitecture; use crate::basicblock::BasicBlock; +use crate::function::Function; use crate::rc::*; use super::*; @@ -53,7 +55,7 @@ pub trait FunctionForm: 'static {} impl FunctionForm for SSA {} impl FunctionForm for NonSSA {} -pub struct Function { +pub struct LowLevelILFunction { pub(crate) borrower: A::Handle, pub(crate) handle: *mut BNLowLevelILFunction, _arch: PhantomData<*mut A>, @@ -61,24 +63,7 @@ pub struct Function { _form: PhantomData, } -unsafe impl Send for Function {} -unsafe impl Sync for Function {} - -impl Eq for Function {} -impl PartialEq for Function { - fn eq(&self, rhs: &Self) -> bool { - self.get_function().eq(&rhs.get_function()) - } -} - -use std::hash::{Hash, Hasher}; -impl Hash for Function { - fn hash(&self, state: &mut H) { - self.get_function().hash(state) - } -} - -impl<'func, A, M, F> Function +impl<'func, A, M, F> LowLevelILFunction where A: 'func + Architecture, M: FunctionMutability, @@ -150,10 +135,10 @@ where } } - pub fn get_function(&self) -> Ref { + pub fn get_function(&self) -> Ref { unsafe { let func = BNGetLowLevelILOwnerFunction(self.handle); - crate::function::Function::ref_from_raw(func) + Function::ref_from_raw(func) } } } @@ -161,7 +146,7 @@ where // LLIL basic blocks are not available until the function object // is finalized, so ensure we can't try requesting basic blocks // during lifting -impl<'func, A, F> Function +impl<'func, A, F> LowLevelILFunction where A: 'func + Architecture, F: FunctionForm, @@ -173,44 +158,37 @@ where let mut count = 0; let blocks = BNGetLowLevelILBasicBlockList(self.handle, &mut count); let context = LowLevelBlock { function: self }; - Array::new(blocks, count, context) } } } // Allow instantiating Lifted IL functions for querying Lifted IL from Architectures -impl Function> { +impl LowLevelILFunction> { + // TODO: Document what happens when you pass None for `source_func`. + // TODO: Doing so would construct a LowLevelILFunction with no basic blocks + // TODO: Document why you would want to do that. pub fn new( arch: CoreArchitecture, - source_func: Option, - ) -> Result, ()> { + source_func: Option, + ) -> Ref { use binaryninjacore_sys::BNCreateLowLevelILFunction; - use std::ptr::null_mut; let handle = unsafe { match source_func { Some(func) => BNCreateLowLevelILFunction(arch.handle, func.handle), - None => BNCreateLowLevelILFunction(arch.handle, null_mut()), + None => BNCreateLowLevelILFunction(arch.handle, std::ptr::null_mut()), } }; - if handle.is_null() { - return Err(()); - } + + // BNCreateLowLevelILFunction should always return a valid object. + assert!(!handle.is_null()); - Ok(unsafe { - Ref::new(Self { - borrower: arch, - handle, - _arch: PhantomData, - _mutability: PhantomData, - _form: PhantomData, - }) - }) + unsafe { Self::ref_from_raw(arch, handle) } } } -impl<'func, A, M, F> ToOwned for Function +impl<'func, A, M, F> ToOwned for LowLevelILFunction where A: 'func + Architecture, M: FunctionMutability, @@ -223,7 +201,7 @@ where } } -unsafe impl<'func, A, M, F> RefCountable for Function +unsafe impl<'func, A, M, F> RefCountable for LowLevelILFunction where A: 'func + Architecture, M: FunctionMutability, @@ -244,13 +222,31 @@ where } } -impl<'func, A, M, F> fmt::Debug for Function +impl<'func, A, M, F> fmt::Debug for LowLevelILFunction where A: 'func + Architecture, M: FunctionMutability, F: FunctionForm, { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + // TODO: Make this better write!(f, "", self.handle) } } + +unsafe impl Send for LowLevelILFunction {} +unsafe impl Sync for LowLevelILFunction {} + +impl Eq for LowLevelILFunction {} + +impl PartialEq for LowLevelILFunction { + fn eq(&self, rhs: &Self) -> bool { + self.get_function().eq(&rhs.get_function()) + } +} + +impl Hash for LowLevelILFunction { + fn hash(&self, state: &mut H) { + self.get_function().hash(state) + } +} \ No newline at end of file diff --git a/rust/src/llil/instruction.rs b/rust/src/llil/instruction.rs index 62e50453f..ecc8eb76f 100644 --- a/rust/src/llil/instruction.rs +++ b/rust/src/llil/instruction.rs @@ -28,12 +28,12 @@ where M: FunctionMutability, F: FunctionForm, { - pub(crate) function: &'func Function, + pub(crate) function: &'func LowLevelILFunction, pub(crate) instr_idx: usize, } fn common_info<'func, A, M, F>( - function: &'func Function, + function: &'func LowLevelILFunction, op: BNLowLevelILInstruction, ) -> Option> where diff --git a/rust/src/llil/lifting.rs b/rust/src/llil/lifting.rs index fa9294114..0c59c351a 100644 --- a/rust/src/llil/lifting.rs +++ b/rust/src/llil/lifting.rs @@ -24,7 +24,7 @@ pub trait Liftable<'func, A: 'func + Architecture> { type Result: ExpressionResultType; fn lift( - il: &'func Function>, + il: &'func LowLevelILFunction>, expr: Self, ) -> Expression<'func, A, Mutable, NonSSA, Self::Result>; } @@ -33,7 +33,7 @@ pub trait LiftableWithSize<'func, A: 'func + Architecture>: Liftable<'func, A, Result = ValueExpr> { fn lift_with_size( - il: &'func Function>, + il: &'func LowLevelILFunction>, expr: Self, size: usize, ) -> Expression<'func, A, Mutable, NonSSA, ValueExpr>; @@ -410,7 +410,7 @@ macro_rules! prim_int_lifter { impl<'a, A: 'a + Architecture> Liftable<'a, A> for $x { type Result = ValueExpr; - fn lift(il: &'a Function>, val: Self) + fn lift(il: &'a LowLevelILFunction>, val: Self) -> Expression<'a, A, Mutable, NonSSA, Self::Result> { il.const_int(std::mem::size_of::(), val as i64 as u64) @@ -418,7 +418,7 @@ macro_rules! prim_int_lifter { } impl<'a, A: 'a + Architecture> LiftableWithSize<'a, A> for $x { - fn lift_with_size(il: &'a Function>, val: Self, size: usize) + fn lift_with_size(il: &'a LowLevelILFunction>, val: Self, size: usize) -> Expression<'a, A, Mutable, NonSSA, ValueExpr> { let raw = val as i64; @@ -459,7 +459,7 @@ where type Result = ValueExpr; fn lift( - il: &'a Function>, + il: &'a LowLevelILFunction>, reg: Self, ) -> Expression<'a, A, Mutable, NonSSA, Self::Result> { match reg { @@ -474,7 +474,7 @@ where R: LiftableWithSize<'a, A> + Into>, { fn lift_with_size( - il: &'a Function>, + il: &'a LowLevelILFunction>, reg: Self, size: usize, ) -> Expression<'a, A, Mutable, NonSSA, ValueExpr> { @@ -492,7 +492,7 @@ where type Result = ValueExpr; fn lift( - il: &'a Function>, + il: &'a LowLevelILFunction>, reg: Self, ) -> Expression<'a, A, Mutable, NonSSA, Self::Result> { match reg { @@ -507,7 +507,7 @@ where R: LiftableWithSize<'a, A> + Into>, { fn lift_with_size( - il: &'a Function>, + il: &'a LowLevelILFunction>, reg: Self, size: usize, ) -> Expression<'a, A, Mutable, NonSSA, ValueExpr> { @@ -527,7 +527,7 @@ where type Result = R; fn lift( - il: &'a Function>, + il: &'a LowLevelILFunction>, expr: Self, ) -> Expression<'a, A, Mutable, NonSSA, Self::Result> { debug_assert!(expr.function.handle == il.handle); @@ -539,7 +539,7 @@ impl<'a, A: 'a + Architecture> LiftableWithSize<'a, A> for Expression<'a, A, Mutable, NonSSA, ValueExpr> { fn lift_with_size( - il: &'a Function>, + il: &'a LowLevelILFunction>, expr: Self, _size: usize, ) -> Expression<'a, A, Mutable, NonSSA, Self::Result> { @@ -582,14 +582,15 @@ where use binaryninjacore_sys::BNLowLevelILOperation; use crate::function::Location; -use crate::llil::{Expression, ExpressionResultType, Function, LiftedExpr, LiftedNonSSA, Lifter, Mutable, NonSSA, Register, ValueExpr, VoidExpr}; +use crate::llil; +use crate::llil::{Expression, ExpressionResultType, LowLevelILFunction, LiftedExpr, LiftedNonSSA, Lifter, Mutable, NonSSA, Register, ValueExpr, VoidExpr}; pub struct ExpressionBuilder<'func, A, R> where A: 'func + Architecture, R: ExpressionResultType, { - function: &'func Function>, + function: &'func LowLevelILFunction>, op: BNLowLevelILOperation, size: usize, flags: u32, @@ -673,7 +674,7 @@ where type Result = R; fn lift( - il: &'a Function>, + il: &'a LowLevelILFunction>, expr: Self, ) -> Expression<'a, A, Mutable, NonSSA, Self::Result> { debug_assert!(expr.function.handle == il.handle); @@ -687,7 +688,7 @@ where A: 'a + Architecture, { fn lift_with_size( - il: &'a Function>, + il: &'a LowLevelILFunction>, expr: Self, _size: usize, ) -> Expression<'a, A, Mutable, NonSSA, ValueExpr> { @@ -882,7 +883,7 @@ macro_rules! binary_op_carry_lifter { }; } -impl Function> +impl LowLevelILFunction> where A: Architecture, { @@ -981,8 +982,8 @@ where pub fn if_expr<'a: 'b, 'b, C>( &'a self, cond: C, - true_label: &'b Label, - false_label: &'b Label, + true_label: &'b mut Label, + false_label: &'b mut Label, ) -> Expression<'a, A, Mutable, NonSSA, VoidExpr> where C: Liftable<'b, A, Result = ValueExpr>, @@ -991,8 +992,8 @@ where let cond = C::lift(self, cond); - let mut raw_true_label = BNLowLevelILLabel::from(true_label); - let mut raw_false_label = BNLowLevelILLabel::from(false_label); + let mut raw_true_label = BNLowLevelILLabel::from(*true_label); + let mut raw_false_label = BNLowLevelILLabel::from(*false_label); let expr_idx = unsafe { BNLowLevelILIf( self.handle, @@ -1001,6 +1002,10 @@ where &mut raw_false_label, ) }; + + // Update the labels after they have been resolved. + *true_label = Label::from(raw_true_label); + *false_label = Label::from(raw_false_label); Expression::new(self, expr_idx) } @@ -1008,13 +1013,16 @@ where // TODO: Wtf are these lifetimes?? pub fn goto<'a: 'b, 'b>( &'a self, - label: &'b Label, + label: &'b mut Label, ) -> Expression<'a, A, Mutable, NonSSA, VoidExpr> { use binaryninjacore_sys::BNLowLevelILGoto; - let mut raw_label = BNLowLevelILLabel::from(label); + let mut raw_label = BNLowLevelILLabel::from(*label); let expr_idx = unsafe { BNLowLevelILGoto(self.handle, &mut raw_label) }; + // Update the labels after they have been resolved. + *label = Label::from(raw_label); + Expression::new(self, expr_idx) } @@ -1446,18 +1454,29 @@ where } } - pub fn label_for_address>(&self, loc: L) -> Option<&Label> { + pub fn label_for_address>(&self, loc: L) -> Option(_ctxt: *mut c_void, nt: *mut BNNameAndType, count: usize) - where + extern "C" fn cb_free_name_and_types( + _ctxt: *mut c_void, + nt: *mut BNNameAndType, + count: usize, + ) where A: 'static + Architecture> + Send + Sync, { if nt.is_null() { @@ -2689,18 +2705,20 @@ where let Some(intrinsic) = custom_arch.intrinsic_from_id(intrinsic) else { // SAFETY: Passed in to be written - unsafe { *count = 0; } + unsafe { + *count = 0; + } return std::ptr::null_mut(); }; let outputs = intrinsic.outputs(); - let mut raw_outputs: Box<[BNTypeWithConfidence]> = outputs - .into_iter() - .map(|o| o.as_ref().into()) - .collect(); + let mut raw_outputs: Box<[BNTypeWithConfidence]> = + outputs.into_iter().map(|o| o.as_ref().into()).collect(); // SAFETY: Passed in to be written - unsafe { *count = raw_outputs.len(); } + unsafe { + *count = raw_outputs.len(); + } if raw_outputs.is_empty() { std::ptr::null_mut() @@ -2721,7 +2739,8 @@ where { let _custom_arch = unsafe { &*(ctxt as *mut A) }; if !tl.is_null() { - let _type_list = unsafe { Box::from_raw(std::ptr::slice_from_raw_parts_mut(tl, count)) }; + let _type_list = + unsafe { Box::from_raw(std::ptr::slice_from_raw_parts_mut(tl, count)) }; } } diff --git a/rust/src/basicblock.rs b/rust/src/basicblock.rs index 75566a064..f8713b713 100644 --- a/rust/src/basicblock.rs +++ b/rust/src/basicblock.rs @@ -16,9 +16,9 @@ use std::fmt; use crate::architecture::CoreArchitecture; use crate::function::Function; -use binaryninjacore_sys::*; -use crate::BranchType; use crate::rc::*; +use crate::BranchType; +use binaryninjacore_sys::*; enum EdgeDirection { Incoming, @@ -50,14 +50,17 @@ pub struct EdgeContext<'a, C: 'a + BlockContext> { impl<'a, C: 'a + BlockContext> CoreArrayProvider for Edge<'a, C> { type Raw = BNBasicBlockEdge; type Context = EdgeContext<'a, C>; - type Wrapped<'b> = Edge<'b, C> where 'a: 'b; + type Wrapped<'b> + = Edge<'b, C> + where + 'a: 'b; } unsafe impl<'a, C: 'a + BlockContext> CoreArrayProviderInner for Edge<'a, C> { unsafe fn free(raw: *mut Self::Raw, count: usize, _context: &Self::Context) { BNFreeBasicBlockEdgeList(raw, count); } - + unsafe fn wrap_raw<'b>(raw: &'b Self::Raw, context: &'b Self::Context) -> Self::Wrapped<'b> { let edge_target = Guard::new( BasicBlock::from_raw(raw.target, context.orig_block.context.clone()), @@ -281,14 +284,17 @@ unsafe impl RefCountable for BasicBlock { impl CoreArrayProvider for BasicBlock { type Raw = *mut BNBasicBlock; type Context = C; - type Wrapped<'a> = Guard<'a, BasicBlock> where C: 'a; + type Wrapped<'a> + = Guard<'a, BasicBlock> + where + C: 'a; } unsafe impl CoreArrayProviderInner for BasicBlock { unsafe fn free(raw: *mut Self::Raw, count: usize, _context: &Self::Context) { BNFreeBasicBlockList(raw, count); } - + unsafe fn wrap_raw<'a>(raw: &'a Self::Raw, context: &'a Self::Context) -> Self::Wrapped<'a> { Guard::new(BasicBlock::from_raw(*raw, context.clone()), context) } diff --git a/rust/src/binaryview.rs b/rust/src/binaryview.rs index 2237fe186..1855c77f2 100644 --- a/rust/src/binaryview.rs +++ b/rust/src/binaryview.rs @@ -14,10 +14,10 @@ //! A view on binary data and queryable interface of a binary file. //! -//! One key job of BinaryView is file format parsing which allows Binary Ninja to read, write, -//! insert, remove portions of the file given a virtual address. -//! -//! For the purposes of this documentation we define a virtual address as the memory address that +//! One key job of BinaryView is file format parsing which allows Binary Ninja to read, write, +//! insert, remove portions of the file given a virtual address. +//! +//! For the purposes of this documentation we define a virtual address as the memory address that //! the various pieces of the physical file will be loaded at. //! TODO : Mirror the Python docs for this @@ -26,11 +26,6 @@ use binaryninjacore_sys::*; pub use binaryninjacore_sys::BNAnalysisState as AnalysisState; pub use binaryninjacore_sys::BNModificationStatus as ModificationStatus; -use std::collections::HashMap; -use std::ffi::{c_char, c_void}; -use std::ops::Range; -use std::{ops, result, slice}; -use std::ptr::NonNull; use crate::architecture::{Architecture, CoreArchitecture}; use crate::basicblock::BasicBlock; use crate::component::{Component, ComponentBuilder, IntoComponentGuid}; @@ -51,10 +46,13 @@ use crate::settings::Settings; use crate::symbol::{Symbol, SymbolType}; use crate::tags::{Tag, TagType}; use crate::typelibrary::TypeLibrary; -use crate::types::{ - NamedTypeReference, QualifiedName, QualifiedNameAndType, Type, -}; +use crate::types::{NamedTypeReference, QualifiedName, QualifiedNameAndType, Type}; use crate::Endianness; +use std::collections::HashMap; +use std::ffi::{c_char, c_void}; +use std::ops::Range; +use std::ptr::NonNull; +use std::{ops, result, slice}; use crate::rc::*; use crate::references::{CodeReference, DataReference}; @@ -262,15 +260,11 @@ pub trait BinaryViewExt: BinaryViewBase { } fn original_image_base(&self) -> u64 { - unsafe { - BNGetOriginalImageBase(self.as_ref().handle) - } + unsafe { BNGetOriginalImageBase(self.as_ref().handle) } } fn set_original_image_base(&self, image_base: u64) { - unsafe { - BNSetOriginalImageBase(self.as_ref().handle, image_base) - } + unsafe { BNSetOriginalImageBase(self.as_ref().handle, image_base) } } fn end(&self) -> u64 { @@ -398,7 +392,8 @@ pub trait BinaryViewExt: BinaryViewBase { fn symbol_by_address(&self, addr: u64) -> Option> { unsafe { - let raw_sym_ptr = BNGetSymbolByAddress(self.as_ref().handle, addr, std::ptr::null_mut()); + let raw_sym_ptr = + BNGetSymbolByAddress(self.as_ref().handle, addr, std::ptr::null_mut()); match raw_sym_ptr.is_null() { false => Some(Symbol::ref_from_raw(raw_sym_ptr)), true => None, @@ -466,8 +461,12 @@ pub trait BinaryViewExt: BinaryViewBase { fn symbols_of_type(&self, ty: SymbolType) -> Array { unsafe { let mut count = 0; - let handles = - BNGetSymbolsOfType(self.as_ref().handle, ty.into(), &mut count, std::ptr::null_mut()); + let handles = BNGetSymbolsOfType( + self.as_ref().handle, + ty.into(), + &mut count, + std::ptr::null_mut(), + ); Array::new(handles, count, ()) } @@ -833,54 +832,47 @@ pub trait BinaryViewExt: BinaryViewBase { } /// Adds a segment to the view. - /// - /// NOTE: Consider using [BinaryViewExt::begin_bulk_add_segments] and [BinaryViewExt::end_bulk_add_segments] + /// + /// NOTE: Consider using [BinaryViewExt::begin_bulk_add_segments] and [BinaryViewExt::end_bulk_add_segments] /// if you plan on adding a number of segments all at once, to avoid unnecessary MemoryMap updates. fn add_segment(&self, segment: SegmentBuilder) { segment.create(self.as_ref()); } - + // TODO: Replace with BulkModify guard. /// Start adding segments in bulk. Useful for adding large numbers of segments. - /// - /// After calling this any call to [BinaryViewExt::add_segment] will be uncommited until a call to - /// [BinaryViewExt::end_bulk_add_segments] - /// + /// + /// After calling this any call to [BinaryViewExt::add_segment] will be uncommited until a call to + /// [BinaryViewExt::end_bulk_add_segments] + /// /// If you wish to discard the uncommited segments you can call [BinaryViewExt::cancel_bulk_add_segments]. - /// - /// NOTE: This **must** be paired with a later call to [BinaryViewExt::end_bulk_add_segments] or + /// + /// NOTE: This **must** be paired with a later call to [BinaryViewExt::end_bulk_add_segments] or /// [BinaryViewExt::cancel_bulk_add_segments], otherwise segments added after this call will stay uncommited. fn begin_bulk_add_segments(&self) { - unsafe { - BNBeginBulkAddSegments(self.as_ref().handle) - } + unsafe { BNBeginBulkAddSegments(self.as_ref().handle) } } // TODO: Replace with BulkModify guard. /// Commit all auto and user segments that have been added since the call to [Self::begin_bulk_add_segments]. - /// + /// /// NOTE: This **must** be paired with a prior call to [Self::begin_bulk_add_segments], otherwise this /// does nothing and segments are added individually. fn end_bulk_add_segments(&self) { - unsafe { - BNEndBulkAddSegments(self.as_ref().handle) - } + unsafe { BNEndBulkAddSegments(self.as_ref().handle) } } // TODO: Replace with BulkModify guard. /// Flushes the auto and user segments that have yet to be committed. - /// + /// /// This is to be used in conjunction with [Self::begin_bulk_add_segments] /// and [Self::end_bulk_add_segments], where the latter will commit the segments /// which have been added since [Self::begin_bulk_add_segments], this function /// will discard them so that they do not get added to the view. fn cancel_bulk_add_segments(&self) { - unsafe { - BNCancelBulkAddSegments(self.as_ref().handle) - } + unsafe { BNCancelBulkAddSegments(self.as_ref().handle) } } - fn add_section(&self, section: SectionBuilder) { section.create(self.as_ref()); } @@ -1061,7 +1053,7 @@ pub trait BinaryViewExt: BinaryViewBase { } } } - + fn function_start_before(&self, addr: u64) -> u64 { unsafe { BNGetPreviousFunctionStartBeforeAddress(self.as_ref().handle, addr) } } @@ -1345,7 +1337,9 @@ pub trait BinaryViewExt: BinaryViewBase { // TODO: For now just construct it manually. let mut src = BNReferenceSource { func: func.map(|f| f.handle).unwrap_or(std::ptr::null_mut()), - arch: func.map(|f| f.arch().handle).unwrap_or(std::ptr::null_mut()), + arch: func + .map(|f| f.arch().handle) + .unwrap_or(std::ptr::null_mut()), addr, }; let addresses = BNGetCodeReferencesFrom(self.as_ref().handle, &mut src, &mut count); @@ -1505,10 +1499,7 @@ pub trait BinaryViewExt: BinaryViewBase { unsafe { BNRemoveComponentByGuid(self.as_ref().handle, path.as_ptr()) } } - fn data_variable_parent_components( - &self, - data_variable: &DataVariable, - ) -> Array { + fn data_variable_parent_components(&self, data_variable: &DataVariable) -> Array { let mut count = 0; let result = unsafe { BNGetDataVariableParentComponents( @@ -1733,7 +1724,7 @@ impl BinaryView { debug_assert!(!handle.is_null()); Self { handle } } - + pub(crate) unsafe fn ref_from_raw(handle: *mut BNBinaryView) -> Ref { debug_assert!(!handle.is_null()); Ref::new(Self { handle }) diff --git a/rust/src/callingconvention.rs b/rust/src/callingconvention.rs index 0fa8e2c5e..430b3aeff 100644 --- a/rust/src/callingconvention.rs +++ b/rust/src/callingconvention.rs @@ -15,17 +15,15 @@ //! Contains and provides information about different systems' calling conventions to analysis. use std::borrow::Borrow; +use std::ffi::c_void; use std::fmt::{Debug, Formatter}; use std::hash::{Hash, Hasher}; use std::marker::PhantomData; -use std::ffi::c_void; use binaryninjacore_sys::*; use crate::architecture::{Architecture, ArchitectureExt, CoreArchitecture, Register}; -use crate::rc::{ - CoreArrayProvider, CoreArrayProviderInner, Guard, Ref, RefCountable, -}; +use crate::rc::{CoreArrayProvider, CoreArrayProviderInner, Guard, Ref, RefCountable}; use crate::string::*; use crate::types::FunctionParameter; use crate::variable::Variable; @@ -85,7 +83,7 @@ where if regs.is_null() { return; } - + let _regs = Box::from_raw(std::ptr::slice_from_raw_parts_mut(regs, count)); }) } @@ -677,7 +675,7 @@ unsafe impl CoreArrayProviderInner for CallingConvention { unsafe fn free(raw: *mut *mut BNCallingConvention, count: usize, _content: &Self::Context) { BNFreeCallingConventionList(raw, count); } - + unsafe fn wrap_raw<'a>(raw: &'a Self::Raw, context: &'a Self::Context) -> Self::Wrapped<'a> { Guard::new( CallingConvention { diff --git a/rust/src/component.rs b/rust/src/component.rs index 4184d1ec4..7ed514509 100644 --- a/rust/src/component.rs +++ b/rust/src/component.rs @@ -4,10 +4,10 @@ use crate::binaryview::{BinaryView, BinaryViewBase, BinaryViewExt}; use crate::function::Function; use crate::rc::{Array, CoreArrayProvider, CoreArrayProviderInner, Ref}; use crate::string::{BnStrCompatible, BnString}; -use crate::types::{ComponentReferencedType}; +use crate::types::ComponentReferencedType; -use binaryninjacore_sys::*; use crate::variable::DataVariable; +use binaryninjacore_sys::*; pub struct ComponentBuilder { bv: *mut BNBinaryView, diff --git a/rust/src/confidence.rs b/rust/src/confidence.rs index 296af79b8..83c6511c1 100644 --- a/rust/src/confidence.rs +++ b/rust/src/confidence.rs @@ -1,11 +1,14 @@ -use std::fmt; -use std::fmt::{Debug, Display, Formatter}; -use std::hash::{Hash, Hasher}; -use binaryninjacore_sys::{BNBoolWithConfidence, BNCallingConventionWithConfidence, BNGetCallingConventionArchitecture, BNOffsetWithConfidence, BNTypeWithConfidence}; use crate::architecture::{Architecture, CoreArchitecture}; use crate::callingconvention::CallingConvention; use crate::rc::{Ref, RefCountable}; use crate::types::Type; +use binaryninjacore_sys::{ + BNBoolWithConfidence, BNCallingConventionWithConfidence, BNGetCallingConventionArchitecture, + BNOffsetWithConfidence, BNTypeWithConfidence, +}; +use std::fmt; +use std::fmt::{Debug, Display, Formatter}; +use std::hash::{Hash, Hasher}; /// The minimum allowed confidence of any given [`Type`]. pub const MIN_CONFIDENCE: u8 = u8::MIN; @@ -267,4 +270,4 @@ impl From> for BNOffsetWithConfidence { confidence: conf.confidence, } } -} \ No newline at end of file +} diff --git a/rust/src/custombinaryview.rs b/rust/src/custombinaryview.rs index ab54f55b1..7160b78ed 100644 --- a/rust/src/custombinaryview.rs +++ b/rust/src/custombinaryview.rs @@ -96,7 +96,7 @@ where // in the refcount going to 0 in the process of returning it // to the core -- we're transferring ownership of the Ref here Ref::into_raw(bv.handle).handle - }, + } Err(_) => { error!("CustomBinaryViewType::create_custom_view returned Err"); ptr::null_mut() @@ -104,7 +104,7 @@ where } }) } - + extern "C" fn cb_parse(ctxt: *mut c_void, data: *mut BNBinaryView) -> *mut BNBinaryView where T: CustomBinaryViewType, @@ -117,14 +117,14 @@ where view_type, actual_parent: &data, }; - + match view_type.parse_custom_view(&data, builder) { Ok(bv) => { // force a leak of the Ref; failure to do this would result // in the refcount going to 0 in the process of returning it // to the core -- we're transferring ownership of the Ref here Ref::into_raw(bv.handle).handle - }, + } Err(_) => { error!("CustomBinaryViewType::parse returned Err"); ptr::null_mut() @@ -204,7 +204,7 @@ pub trait BinaryViewTypeBase: AsRef { unsafe { Some(Settings::from_raw(settings_handle)) } } } - + fn load_settings_for_data(&self, _data: &BinaryView) -> Option> { None } @@ -221,7 +221,12 @@ pub trait BinaryViewTypeExt: BinaryViewTypeBase { fn register_arch(&self, id: u32, endianness: Endianness, arch: &A) { unsafe { - BNRegisterArchitectureForViewType(self.as_ref().handle, id, endianness, arch.as_ref().handle); + BNRegisterArchitectureForViewType( + self.as_ref().handle, + id, + endianness, + arch.as_ref().handle, + ); } } @@ -266,7 +271,7 @@ impl BinaryViewTypeExt for T {} #[derive(Copy, Clone, PartialEq, Eq, Hash)] pub struct BinaryViewType { - pub handle: *mut BNBinaryViewType + pub handle: *mut BNBinaryViewType, } impl BinaryViewType { @@ -274,7 +279,7 @@ impl BinaryViewType { debug_assert!(!handle.is_null()); Self { handle } } - + pub fn list_all() -> Array { unsafe { let mut count: usize = 0; @@ -316,7 +321,8 @@ impl BinaryViewTypeBase for BinaryViewType { } fn load_settings_for_data(&self, data: &BinaryView) -> Option> { - let settings_handle = unsafe { BNGetBinaryViewLoadSettingsForData(self.handle, data.handle) }; + let settings_handle = + unsafe { BNGetBinaryViewLoadSettingsForData(self.handle, data.handle) }; if settings_handle.is_null() { None @@ -336,7 +342,7 @@ unsafe impl CoreArrayProviderInner for BinaryViewType { unsafe fn free(raw: *mut Self::Raw, _count: usize, _context: &Self::Context) { BNFreeBinaryViewTypeList(raw); } - + unsafe fn wrap_raw<'a>(raw: &'a Self::Raw, _context: &'a Self::Context) -> Self::Wrapped<'a> { Guard::new(BinaryViewType::from_raw(*raw), &()) } diff --git a/rust/src/databuffer.rs b/rust/src/databuffer.rs index 6338d7663..3bc833b23 100644 --- a/rust/src/databuffer.rs +++ b/rust/src/databuffer.rs @@ -27,7 +27,7 @@ impl DataBuffer { pub(crate) fn from_raw(raw: *mut BNDataBuffer) -> Self { DataBuffer(raw) } - + pub(crate) fn as_raw(&self) -> *mut BNDataBuffer { self.0 } @@ -111,7 +111,13 @@ impl DataBuffer { } pub fn to_escaped_string(&self, null_terminates: bool, escape_printable: bool) -> BnString { - unsafe { BnString::from_raw(BNDataBufferToEscapedString(self.0, null_terminates, escape_printable)) } + unsafe { + BnString::from_raw(BNDataBufferToEscapedString( + self.0, + null_terminates, + escape_printable, + )) + } } pub fn from_escaped_string(value: &BnString) -> Self { diff --git a/rust/src/debuginfo.rs b/rust/src/debuginfo.rs index af8660e61..bb361f60e 100644 --- a/rust/src/debuginfo.rs +++ b/rust/src/debuginfo.rs @@ -281,7 +281,7 @@ unsafe impl CoreArrayProviderInner for DebugInfoParser { unsafe fn free(raw: *mut Self::Raw, count: usize, _: &Self::Context) { BNFreeDebugInfoParserList(raw, count); } - + unsafe fn wrap_raw<'a>(raw: &'a Self::Raw, context: &'a Self::Context) -> Self::Wrapped<'a> { Guard::new(Self { handle: *raw }, context) } @@ -309,7 +309,8 @@ pub struct DebugFunctionInfo { impl From<&BNDebugFunctionInfo> for DebugFunctionInfo { fn from(raw: &BNDebugFunctionInfo) -> Self { let raw_components = unsafe { std::slice::from_raw_parts(raw.components, raw.componentN) }; - let raw_local_variables = unsafe { std::slice::from_raw_parts(raw.localVariables, raw.localVariableN) }; + let raw_local_variables = + unsafe { std::slice::from_raw_parts(raw.localVariables, raw.localVariableN) }; Self { short_name: raw_to_string(raw.shortName), @@ -326,8 +327,16 @@ impl From<&BNDebugFunctionInfo> for DebugFunctionInfo { } else { Some(unsafe { Platform::ref_from_raw(raw.platform) }) }, - components: raw_components.iter().copied().filter_map(|c| raw_to_string(c)).collect(), - local_variables: raw_local_variables.into_iter().copied().map(Into::into).collect(), + components: raw_components + .iter() + .copied() + .filter_map(|c| raw_to_string(c)) + .collect(), + local_variables: raw_local_variables + .into_iter() + .copied() + .map(Into::into) + .collect(), } } } @@ -407,10 +416,11 @@ impl DebugInfo { unsafe { BNFreeDebugTypes(debug_types_ptr, count) }; result } - + pub fn types(&self) -> Vec { let mut count: usize = 0; - let debug_types_ptr = unsafe { BNGetDebugTypes(self.handle, std::ptr::null_mut(), &mut count) }; + let debug_types_ptr = + unsafe { BNGetDebugTypes(self.handle, std::ptr::null_mut(), &mut count) }; let result: Vec<_> = unsafe { std::slice::from_raw_parts_mut(debug_types_ptr, count) .iter() @@ -424,10 +434,7 @@ impl DebugInfo { } /// Returns all functions within the parser - pub fn functions_by_name( - &self, - parser_name: S - ) -> Vec { + pub fn functions_by_name(&self, parser_name: S) -> Vec { let parser_name = parser_name.into_bytes_with_nul(); let mut count: usize = 0; @@ -449,7 +456,7 @@ impl DebugInfo { unsafe { BNFreeDebugFunctions(functions_ptr, count) }; result } - + pub fn functions(&self) -> Vec { let mut count: usize = 0; let functions_ptr = @@ -493,7 +500,7 @@ impl DebugInfo { unsafe { BNFreeDataVariablesAndName(data_variables_ptr, count) }; result } - + pub fn data_variables(&self) -> Vec { let mut count: usize = 0; let data_variables_ptr = @@ -510,7 +517,7 @@ impl DebugInfo { unsafe { BNFreeDataVariablesAndName(data_variables_ptr, count) }; result } - + pub fn type_by_name(&self, parser_name: S, name: S) -> Option> { let parser_name = parser_name.into_bytes_with_nul(); let name = name.into_bytes_with_nul(); @@ -584,7 +591,8 @@ impl DebugInfo { BNGetDebugTypesByName(self.handle, name.as_ref().as_ptr() as *mut _, &mut count) }; - let raw_names_and_types: &[BNNameAndType] = unsafe { std::slice::from_raw_parts(raw_names_and_types_ptr, count) }; + let raw_names_and_types: &[BNNameAndType] = + unsafe { std::slice::from_raw_parts(raw_names_and_types_ptr, count) }; let names_and_types = raw_names_and_types .iter() @@ -734,7 +742,7 @@ impl DebugInfo { ) -> bool { // SAFETY: Lifetime of `components` will live long enough, so passing as_ptr is safe. let raw_components: Vec<_> = components.iter().map(|&c| c.as_ptr()).collect(); - + let name = name.into_bytes_with_nul(); unsafe { BNAddDebugType( @@ -765,25 +773,26 @@ impl DebugInfo { let mut components_array: Vec<*mut ::std::os::raw::c_char> = Vec::with_capacity(new_func.components.len()); - let mut local_variables_array: Vec = Vec::with_capacity(new_func.local_variables.len()); unsafe { for component in &new_func.components { - components_array.push(BNAllocString(component.clone().into_bytes_with_nul().as_ptr() as _)); + components_array.push(BNAllocString( + component.clone().into_bytes_with_nul().as_ptr() as _, + )); } for local_variable in &new_func.local_variables { - local_variables_array.push( - BNVariableNameAndType { - var: local_variable.variable.into(), - autoDefined: local_variable.auto_defined, - typeConfidence: local_variable.ty.confidence, - name: BNAllocString(local_variable.name.clone().into_bytes_with_nul().as_ptr() as _), - type_: local_variable.ty.contents.handle, - } - ); + local_variables_array.push(BNVariableNameAndType { + var: local_variable.variable.into(), + autoDefined: local_variable.auto_defined, + typeConfidence: local_variable.ty.confidence, + name: BNAllocString( + local_variable.name.clone().into_bytes_with_nul().as_ptr() as _ + ), + type_: local_variable.ty.contents.handle, + }); } let result = BNAddDebugFunction( diff --git a/rust/src/demangle.rs b/rust/src/demangle.rs index 905c54a96..cd3279ffd 100644 --- a/rust/src/demangle.rs +++ b/rust/src/demangle.rs @@ -19,7 +19,7 @@ use std::ffi::{c_char, c_void, CStr}; use crate::architecture::CoreArchitecture; use crate::binaryview::BinaryView; -use crate::string::{BnStrCompatible, BnString, raw_to_string}; +use crate::string::{raw_to_string, BnStrCompatible, BnString}; use crate::types::{QualifiedName, Type}; use crate::rc::*; @@ -76,14 +76,11 @@ pub fn demangle_generic( .strings() .iter() .map(|str| str.to_string()) - .collect::>() + .collect::>(), )) } -pub fn demangle_llvm( - mangled_name: S, - simplify: bool, -) -> Result> { +pub fn demangle_llvm(mangled_name: S, simplify: bool) -> Result> { let mangled_name_bwn = mangled_name.into_bytes_with_nul(); let mangled_name_ptr = mangled_name_bwn.as_ref(); let mut out_name: *mut *mut std::os::raw::c_char = std::ptr::null_mut(); @@ -269,19 +266,27 @@ impl Demangler { let view_ptr = match view { Some(v) => v.handle, - None => std::ptr::null_mut() + None => std::ptr::null_mut(), }; - if !unsafe { BNDemanglerDemangle(self.handle, arch.handle, name_bytes.as_ref().as_ptr() as *const _, &mut out_type, &mut out_var_name, view_ptr) } { + if !unsafe { + BNDemanglerDemangle( + self.handle, + arch.handle, + name_bytes.as_ref().as_ptr() as *const _, + &mut out_type, + &mut out_var_name, + view_ptr, + ) + } { return Err(()); } - let var_type = - if out_type.is_null() { - None - } else { - Some(unsafe { Type::ref_from_raw(out_type) }) - }; + let var_type = if out_type.is_null() { + None + } else { + Some(unsafe { Type::ref_from_raw(out_type) }) + }; let var_name = QualifiedName(out_var_name); Ok((var_type, var_name)) @@ -318,28 +323,33 @@ impl Demangler { { ffi_wrap!("CustomDemangler::cb_is_mangled_string", unsafe { let cmd = &*(ctxt as *const C); - let name = - if let Some(n) = raw_to_string(name) { - n - } else { - return false; - }; + let name = if let Some(n) = raw_to_string(name) { + n + } else { + return false; + }; cmd.is_mangled_string(&name) }) } - extern "C" fn cb_demangle(ctxt: *mut c_void, arch: *mut BNArchitecture, name: *const c_char, out_type: *mut *mut BNType, out_var_name: *mut BNQualifiedName, view: *mut BNBinaryView) -> bool + extern "C" fn cb_demangle( + ctxt: *mut c_void, + arch: *mut BNArchitecture, + name: *const c_char, + out_type: *mut *mut BNType, + out_var_name: *mut BNQualifiedName, + view: *mut BNBinaryView, + ) -> bool where C: CustomDemangler, { ffi_wrap!("CustomDemangler::cb_demangle", unsafe { let cmd = &*(ctxt as *const C); let arch = CoreArchitecture::from_raw(arch); - let name = - if let Some(n) = raw_to_string(name) { - n - } else { - return false; - }; + let name = if let Some(n) = raw_to_string(name) { + n + } else { + return false; + }; let view = if view.is_null() { None } else { @@ -350,17 +360,16 @@ impl Demangler { Ok((type_, name)) => { *out_type = match type_ { Some(t) => RefCountable::inc_ref(t.as_ref()).handle, - None => std::ptr::null_mut() + None => std::ptr::null_mut(), }; // TODO: Need to have a better way for api-owned QNames (*out_var_name).nameCount = name.0.nameCount; (*out_var_name).join = BNAllocString(name.0.join); - (*out_var_name).name = BNAllocStringList(name.0.name as *mut *const _, name.0.nameCount); + (*out_var_name).name = + BNAllocStringList(name.0.name as *mut *const _, name.0.nameCount); true - }, - Err(_) => { - false } + Err(_) => false, } }) } @@ -394,7 +403,9 @@ impl Demangler { } pub fn promote(demangler: &Demangler) { - unsafe { BNPromoteDemangler(demangler.handle); } + unsafe { + BNPromoteDemangler(demangler.handle); + } } } diff --git a/rust/src/disassembly.rs b/rust/src/disassembly.rs index 90eabd4ec..a08bed464 100644 --- a/rust/src/disassembly.rs +++ b/rust/src/disassembly.rs @@ -287,7 +287,7 @@ unsafe impl CoreArrayProviderInner for InstructionTextToken { unsafe fn free(raw: *mut Self::Raw, count: usize, _context: &Self::Context) { 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) @@ -303,7 +303,7 @@ unsafe impl CoreArrayProviderInner for Array { unsafe fn free(raw: *mut Self::Raw, count: usize, _context: &Self::Context) { BNFreeInstructionTextLines(raw, count) } - + unsafe fn wrap_raw<'a>(raw: &'a Self::Raw, _context: &'a Self::Context) -> Self::Wrapped<'a> { // TODO: This is insane. std::mem::ManuallyDrop::new(Self::new(raw.tokens, raw.count, ())) @@ -396,7 +396,9 @@ 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()) + .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 @@ -480,7 +482,7 @@ unsafe impl CoreArrayProviderInner for DisassemblyTextLine { unsafe fn free(raw: *mut Self::Raw, count: usize, _context: &Self::Context) { BNFreeDisassemblyTextLines(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) diff --git a/rust/src/downloadprovider.rs b/rust/src/downloadprovider.rs index 568a4e023..19a6dd190 100644 --- a/rust/src/downloadprovider.rs +++ b/rust/src/downloadprovider.rs @@ -1,4 +1,4 @@ -use crate::rc::{Array, CoreArrayProvider, Guard, CoreArrayProviderInner, Ref, RefCountable}; +use crate::rc::{Array, CoreArrayProvider, CoreArrayProviderInner, Guard, Ref, RefCountable}; use crate::settings::Settings; use crate::string::{BnStrCompatible, BnString}; use binaryninjacore_sys::*; @@ -68,7 +68,7 @@ unsafe impl CoreArrayProviderInner for DownloadProvider { unsafe fn free(raw: *mut Self::Raw, _count: usize, _context: &Self::Context) { BNFreeDownloadProviderList(raw); } - + unsafe fn wrap_raw<'a>(raw: &'a Self::Raw, _context: &'a Self::Context) -> Self::Wrapped<'a> { Guard::new(DownloadProvider::from_raw(*raw), &()) } diff --git a/rust/src/enterprise.rs b/rust/src/enterprise.rs index 5b8877f3c..adb1a32a6 100644 --- a/rust/src/enterprise.rs +++ b/rust/src/enterprise.rs @@ -4,7 +4,6 @@ use std::time::{Duration, SystemTime, UNIX_EPOCH}; use crate::rc::Array; use crate::string::{BnStrCompatible, BnString}; - #[derive(Debug)] pub struct EnterpriseCheckoutError(pub String); @@ -50,10 +49,14 @@ pub fn checkout_license(duration: Duration) -> Result<(), EnterpriseCheckoutErro } } } - - if !is_server_license_still_activated() || (!is_server_floating_license() && crate::license_expiration_time() < SystemTime::now()) { + + if !is_server_license_still_activated() + || (!is_server_floating_license() && crate::license_expiration_time() < SystemTime::now()) + { if !update_server_license(duration) { - return Err(EnterpriseCheckoutError("Failed to refresh expired license".to_string())); + return Err(EnterpriseCheckoutError( + "Failed to refresh expired license".to_string(), + )); } } @@ -77,7 +80,9 @@ pub fn server_url() -> BnString { pub fn set_server_url(url: S) -> Result<(), ()> { let url = url.into_bytes_with_nul(); let result = unsafe { - binaryninjacore_sys::BNSetEnterpriseServerUrl(url.as_ref().as_ptr() as *const std::os::raw::c_char) + binaryninjacore_sys::BNSetEnterpriseServerUrl( + url.as_ref().as_ptr() as *const std::os::raw::c_char + ) }; if result { Ok(()) diff --git a/rust/src/externallibrary.rs b/rust/src/externallibrary.rs index a5dcefc8c..9d2158410 100644 --- a/rust/src/externallibrary.rs +++ b/rust/src/externallibrary.rs @@ -60,7 +60,7 @@ impl ExternalLibrary { /// Set the file backing this external library pub fn set_backing_file(&self, file: Option<&ProjectFile>) { let file_handle = file - .map(|x| unsafe {x.as_raw() as *mut _}) + .map(|x| unsafe { x.as_raw() as *mut _ }) .unwrap_or(ptr::null_mut()); unsafe { BNExternalLibrarySetBackingFile(self.as_raw(), file_handle) } } @@ -137,7 +137,7 @@ impl ExternalLocation { /// Set the ExternalLibrary that this ExternalLocation targets pub fn set_external_library(&self, lib: Option<&ExternalLibrary>) { let lib_handle = lib - .map(|x| unsafe {x.as_raw() as *mut _}) + .map(|x| unsafe { x.as_raw() as *mut _ }) .unwrap_or(ptr::null_mut()); unsafe { BNExternalLocationSetExternalLibrary(self.as_raw(), lib_handle) } } @@ -183,12 +183,7 @@ impl ExternalLocation { let symbol = symbol .map(|x| x.into_bytes_with_nul().as_ref().as_ptr() as *const ffi::c_char) .unwrap_or(ptr::null_mut()); - unsafe { - BNExternalLocationSetTargetSymbol( - self.as_raw(), - symbol, - ) - } + unsafe { BNExternalLocationSetTargetSymbol(self.as_raw(), symbol) } } } diff --git a/rust/src/filemetadata.rs b/rust/src/filemetadata.rs index 15c7d5dfc..fc454e4bd 100644 --- a/rust/src/filemetadata.rs +++ b/rust/src/filemetadata.rs @@ -12,9 +12,17 @@ // See the License for the specific language governing permissions and // limitations under the License. -use std::ffi::c_void; -use binaryninjacore_sys::{BNBeginUndoActions, BNCloseFile, BNCommitUndoActions, BNCreateDatabase, BNCreateFileMetadata, BNFileMetadata, BNFileMetadataGetSessionId, BNFreeFileMetadata, BNGetCurrentOffset, BNGetCurrentView, BNGetFileMetadataDatabase, BNGetFileViewOfType, BNGetFilename, BNGetProjectFile, BNIsAnalysisChanged, BNIsBackedByDatabase, BNIsFileModified, BNMarkFileModified, BNMarkFileSaved, BNNavigate, BNNewFileReference, BNOpenDatabaseForConfiguration, BNOpenExistingDatabase, BNRedo, BNRevertUndoActions, BNSaveAutoSnapshot, BNSetFilename, BNUndo}; +use binaryninjacore_sys::{ + BNBeginUndoActions, BNCloseFile, BNCommitUndoActions, BNCreateDatabase, BNCreateFileMetadata, + BNFileMetadata, BNFileMetadataGetSessionId, BNFreeFileMetadata, BNGetCurrentOffset, + BNGetCurrentView, BNGetFileMetadataDatabase, BNGetFileViewOfType, BNGetFilename, + BNGetProjectFile, BNIsAnalysisChanged, BNIsBackedByDatabase, BNIsFileModified, + BNMarkFileModified, BNMarkFileSaved, BNNavigate, BNNewFileReference, + BNOpenDatabaseForConfiguration, BNOpenExistingDatabase, BNRedo, BNRevertUndoActions, + BNSaveAutoSnapshot, BNSetFilename, BNUndo, +}; use binaryninjacore_sys::{BNCreateDatabaseWithProgress, BNOpenExistingDatabaseWithProgress}; +use std::ffi::c_void; use crate::binaryview::BinaryView; use crate::database::Database; @@ -57,11 +65,9 @@ impl FileMetadata { BNCloseFile(self.handle); } } - + pub fn session_id(&self) -> usize { - unsafe { - BNFileMetadataGetSessionId(self.handle) - } + unsafe { BNFileMetadataGetSessionId(self.handle) } } pub fn filename(&self) -> BnString { @@ -199,12 +205,12 @@ impl FileMetadata { ) -> bool { let filename = filename.into_bytes_with_nul(); let filename_ptr = filename.as_ref().as_ptr() as *mut _; - + // Databases are created with the root view (Raw). let Some(raw_view) = self.get_view_of_type("Raw") else { return false; }; - + match progress_func { None => unsafe { BNCreateDatabase(raw_view.handle, filename_ptr, ptr::null_mut()) }, Some(func) => unsafe { @@ -224,13 +230,8 @@ impl FileMetadata { let Some(raw_view) = self.get_view_of_type("Raw") else { return false; }; - - unsafe { - BNSaveAutoSnapshot( - raw_view.handle, - ptr::null_mut() as *mut _, - ) - } + + unsafe { BNSaveAutoSnapshot(raw_view.handle, ptr::null_mut() as *mut _) } } pub fn open_database_for_configuration( diff --git a/rust/src/function.rs b/rust/src/function.rs index 59d63ff5f..0eaad1a7a 100644 --- a/rust/src/function.rs +++ b/rust/src/function.rs @@ -21,35 +21,35 @@ use crate::{ callingconvention::CallingConvention, component::Component, disassembly::{DisassemblySettings, DisassemblyTextLine}, - flowgraph::FlowGraph, llil, + flowgraph::FlowGraph, + llil, mlil::FunctionGraphType, platform::Platform, references::CodeReference, string::*, symbol::Symbol, tags::{Tag, TagReference, TagType}, - types::{ - IntegerDisplayType, - QualifiedName, - Type, - }, + types::{IntegerDisplayType, QualifiedName, Type}, }; use crate::{databuffer::DataBuffer, disassembly::InstructionTextToken, rc::*}; pub use binaryninjacore_sys::BNAnalysisSkipReason as AnalysisSkipReason; +pub use binaryninjacore_sys::BNBuiltinType as BuiltinType; pub use binaryninjacore_sys::BNFunctionAnalysisSkipOverride as FunctionAnalysisSkipOverride; pub use binaryninjacore_sys::BNFunctionUpdateType as FunctionUpdateType; -pub use binaryninjacore_sys::BNBuiltinType as BuiltinType; pub use binaryninjacore_sys::BNHighlightStandardColor as HighlightStandardColor; -use std::{ffi::c_char, hash::Hash, ops::Range}; -use std::fmt::{Debug, Formatter}; -use std::ptr::NonNull; -use std::time::Duration; use crate::confidence::Conf; use crate::hlil::HighLevelILFunction; use crate::mlil::MediumLevelILFunction; -use crate::variable::{IndirectBranchInfo, MergedVariable, NamedVariableWithType, RegisterValue, RegisterValueType, StackVariableReference, Variable}; +use crate::variable::{ + IndirectBranchInfo, MergedVariable, NamedVariableWithType, RegisterValue, RegisterValueType, + StackVariableReference, Variable, +}; use crate::workflow::Workflow; +use std::fmt::{Debug, Formatter}; +use std::ptr::NonNull; +use std::time::Duration; +use std::{ffi::c_char, hash::Hash, ops::Range}; /// Used to describe a location within a [`Function`]. #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] @@ -179,14 +179,26 @@ impl FunctionViewType { FunctionViewType::Normal => BNFunctionGraphType::NormalFunctionGraph, FunctionViewType::LowLevelIL => BNFunctionGraphType::LowLevelILFunctionGraph, FunctionViewType::LiftedIL => BNFunctionGraphType::LiftedILFunctionGraph, - FunctionViewType::LowLevelILSSAForm => BNFunctionGraphType::LowLevelILSSAFormFunctionGraph, + FunctionViewType::LowLevelILSSAForm => { + BNFunctionGraphType::LowLevelILSSAFormFunctionGraph + } FunctionViewType::MediumLevelIL => BNFunctionGraphType::MediumLevelILFunctionGraph, - FunctionViewType::MediumLevelILSSAForm => BNFunctionGraphType::MediumLevelILSSAFormFunctionGraph, - FunctionViewType::MappedMediumLevelIL => BNFunctionGraphType::MappedMediumLevelILFunctionGraph, - FunctionViewType::MappedMediumLevelILSSAForm => BNFunctionGraphType::MappedMediumLevelILSSAFormFunctionGraph, + FunctionViewType::MediumLevelILSSAForm => { + BNFunctionGraphType::MediumLevelILSSAFormFunctionGraph + } + FunctionViewType::MappedMediumLevelIL => { + BNFunctionGraphType::MappedMediumLevelILFunctionGraph + } + FunctionViewType::MappedMediumLevelILSSAForm => { + BNFunctionGraphType::MappedMediumLevelILSSAFormFunctionGraph + } FunctionViewType::HighLevelIL => BNFunctionGraphType::HighLevelILFunctionGraph, - FunctionViewType::HighLevelILSSAForm => BNFunctionGraphType::HighLevelILSSAFormFunctionGraph, - FunctionViewType::HighLevelLanguageRepresentation(_) => BNFunctionGraphType::HighLevelLanguageRepresentationFunctionGraph, + FunctionViewType::HighLevelILSSAForm => { + BNFunctionGraphType::HighLevelILSSAFormFunctionGraph + } + FunctionViewType::HighLevelLanguageRepresentation(_) => { + BNFunctionGraphType::HighLevelLanguageRepresentationFunctionGraph + } }; RawFunctionViewType(BNFunctionViewType { type_: view_type, @@ -204,13 +216,23 @@ impl Into for FunctionGraphType { match self { BNFunctionGraphType::LowLevelILFunctionGraph => FunctionViewType::LowLevelIL, BNFunctionGraphType::LiftedILFunctionGraph => FunctionViewType::LiftedIL, - BNFunctionGraphType::LowLevelILSSAFormFunctionGraph => FunctionViewType::LowLevelILSSAForm, + BNFunctionGraphType::LowLevelILSSAFormFunctionGraph => { + FunctionViewType::LowLevelILSSAForm + } BNFunctionGraphType::MediumLevelILFunctionGraph => FunctionViewType::MediumLevelIL, - BNFunctionGraphType::MediumLevelILSSAFormFunctionGraph => FunctionViewType::MediumLevelILSSAForm, - BNFunctionGraphType::MappedMediumLevelILFunctionGraph => FunctionViewType::MappedMediumLevelIL, - BNFunctionGraphType::MappedMediumLevelILSSAFormFunctionGraph => FunctionViewType::MappedMediumLevelILSSAForm, + BNFunctionGraphType::MediumLevelILSSAFormFunctionGraph => { + FunctionViewType::MediumLevelILSSAForm + } + BNFunctionGraphType::MappedMediumLevelILFunctionGraph => { + FunctionViewType::MappedMediumLevelIL + } + BNFunctionGraphType::MappedMediumLevelILSSAFormFunctionGraph => { + FunctionViewType::MappedMediumLevelILSSAForm + } BNFunctionGraphType::HighLevelILFunctionGraph => FunctionViewType::HighLevelIL, - BNFunctionGraphType::HighLevelILSSAFormFunctionGraph => FunctionViewType::HighLevelILSSAForm, + BNFunctionGraphType::HighLevelILSSAFormFunctionGraph => { + FunctionViewType::HighLevelILSSAForm + } BNFunctionGraphType::HighLevelLanguageRepresentationFunctionGraph => { // Historically this was the only language representation. FunctionViewType::HighLevelLanguageRepresentation("Pseudo C".into()) @@ -223,7 +245,9 @@ impl Into for FunctionGraphType { impl Drop for RawFunctionViewType { fn drop(&mut self) { if !self.0.name.is_null() { - unsafe { let _ = std::ffi::CString::from_raw(self.0.name as *mut _); } + unsafe { + let _ = std::ffi::CString::from_raw(self.0.name as *mut _); + } } } } @@ -385,7 +409,8 @@ impl Function { ) -> Array> { let arch = arch.unwrap_or_else(|| self.arch()); let mut count = 0; - let lines = unsafe { BNGetFunctionBlockAnnotations(self.handle, arch.handle, addr, &mut count) }; + let lines = + unsafe { BNGetFunctionBlockAnnotations(self.handle, arch.handle, addr, &mut count) }; assert!(!lines.is_null()); unsafe { Array::new(lines, count, ()) } } @@ -425,9 +450,7 @@ impl Function { } } - pub fn mapped_medium_level_il_if_available( - &self, - ) -> Option> { + pub fn mapped_medium_level_il_if_available(&self) -> Option> { let mlil_ptr = unsafe { BNGetFunctionMappedMediumLevelILIfAvailable(self.handle) }; match mlil_ptr.is_null() { false => Some(unsafe { MediumLevelILFunction::ref_from_raw(mlil_ptr) }), @@ -501,12 +524,7 @@ impl Function { { let return_type: Conf<&Type> = return_type.into(); let mut raw_return_type = BNTypeWithConfidence::from(return_type); - unsafe { - BNSetAutoFunctionReturnType( - self.handle, - &mut raw_return_type, - ) - } + unsafe { BNSetAutoFunctionReturnType(self.handle, &mut raw_return_type) } } pub fn set_user_return_type<'a, C>(&self, return_type: C) @@ -515,12 +533,7 @@ impl Function { { let return_type: Conf<&Type> = return_type.into(); let mut raw_return_type = BNTypeWithConfidence::from(return_type); - unsafe { - BNSetUserFunctionReturnType( - self.handle, - &mut raw_return_type, - ) - } + unsafe { BNSetUserFunctionReturnType(self.handle, &mut raw_return_type) } } pub fn function_type(&self) -> Ref { @@ -706,7 +719,8 @@ impl Function { I: IntoIterator, { let arch = arch.unwrap_or_else(|| self.arch()); - let adjustments: Vec = adjust.into_iter().map(Into::into).collect(); + let adjustments: Vec = + adjust.into_iter().map(Into::into).collect(); unsafe { BNSetUserCallRegisterStackAdjustment( self.handle, @@ -727,7 +741,8 @@ impl Function { I: IntoIterator, { let arch = arch.unwrap_or_else(|| self.arch()); - let adjustments: Vec = adjust.into_iter().map(Into::into).collect(); + let adjustments: Vec = + adjust.into_iter().map(Into::into).collect(); unsafe { BNSetAutoCallRegisterStackAdjustment( self.handle, @@ -908,8 +923,9 @@ impl Function { ) -> RegisterValue { let arch = arch.unwrap_or_else(|| self.arch()); let func_type = func_type.map(|f| f.handle).unwrap_or(std::ptr::null_mut()); - let value = - unsafe { BNGetParameterValueAtInstruction(self.handle, arch.handle, addr, func_type, i) }; + let value = unsafe { + BNGetParameterValueAtInstruction(self.handle, arch.handle, addr, func_type, i) + }; value.into() } @@ -986,12 +1002,7 @@ impl Function { C: Into>, { let value: Conf = value.into(); - unsafe { - BNSetAutoFunctionInlinedDuringAnalysis( - self.handle, - value.into(), - ) - } + unsafe { BNSetAutoFunctionInlinedDuringAnalysis(self.handle, value.into()) } } pub fn set_user_inline_during_analysis(&self, value: C) @@ -999,12 +1010,7 @@ impl Function { C: Into>, { let value: Conf = value.into(); - unsafe { - BNSetUserFunctionInlinedDuringAnalysis( - self.handle, - value.into(), - ) - } + unsafe { BNSetUserFunctionInlinedDuringAnalysis(self.handle, value.into()) } } pub fn analysis_performance_info(&self) -> Array { @@ -1059,9 +1065,13 @@ impl Function { unsafe { match (user, addr) { (false, None) => BNAddAutoFunctionTag(self.handle, tag.handle), - (false, Some(addr)) => BNAddAutoAddressTag(self.handle, arch.handle, addr, tag.handle), + (false, Some(addr)) => { + BNAddAutoAddressTag(self.handle, arch.handle, addr, tag.handle) + } (true, None) => BNAddUserFunctionTag(self.handle, tag.handle), - (true, Some(addr)) => BNAddUserAddressTag(self.handle, arch.handle, addr, tag.handle), + (true, Some(addr)) => { + BNAddUserAddressTag(self.handle, arch.handle, addr, tag.handle) + } } } } @@ -1086,7 +1096,9 @@ impl Function { BNRemoveAutoAddressTag(self.handle, arch.handle, addr, tag.handle) } (true, None) => BNRemoveUserFunctionTag(self.handle, tag.handle), - (true, Some(addr)) => BNRemoveUserAddressTag(self.handle, arch.handle, addr, tag.handle), + (true, Some(addr)) => { + BNRemoveUserAddressTag(self.handle, arch.handle, addr, tag.handle) + } } } } @@ -1277,7 +1289,14 @@ impl Function { let arch = arch.unwrap_or_else(|| self.arch()); let name_ptr = &name.0 as *const _ as *mut _; unsafe { - BNRemoveUserTypeFieldReference(self.handle, arch.handle, from_addr, name_ptr, offset, size) + BNRemoveUserTypeFieldReference( + self.handle, + arch.handle, + from_addr, + name_ptr, + offset, + size, + ) } } @@ -1290,7 +1309,9 @@ impl Function { let size = size.unwrap_or(0); // TODO: Adjust `BuiltinType`? let mut builtin_type = BuiltinType::BuiltinNone; - let buffer = DataBuffer::from_raw(unsafe { BNGetConstantData(self.handle, state.into(), value, size, &mut builtin_type) }); + let buffer = DataBuffer::from_raw(unsafe { + BNGetConstantData(self.handle, state.into(), value, size, &mut builtin_type) + }); (buffer, builtin_type) } @@ -1301,8 +1322,9 @@ impl Function { ) -> Array { let arch = arch.unwrap_or_else(|| self.arch()); let mut count = 0; - let refs = - unsafe { BNGetConstantsReferencedByInstruction(self.handle, arch.handle, addr, &mut count) }; + let refs = unsafe { + BNGetConstantsReferencedByInstruction(self.handle, arch.handle, addr, &mut count) + }; assert!(!refs.is_null()); unsafe { Array::new(refs, count, ()) } } @@ -1315,7 +1337,12 @@ impl Function { let arch = arch.unwrap_or_else(|| self.arch()); let mut count = 0; let refs = unsafe { - BNGetConstantsReferencedByInstructionIfAvailable(self.handle, arch.handle, addr, &mut count) + BNGetConstantsReferencedByInstructionIfAvailable( + self.handle, + arch.handle, + addr, + &mut count, + ) }; assert!(!refs.is_null()); unsafe { Array::new(refs, count, ()) } @@ -1377,8 +1404,12 @@ impl Function { let tags = match auto { None => unsafe { BNGetAddressTags(self.handle, arch.handle, addr, &mut count) }, - Some(true) => unsafe { BNGetAutoAddressTags(self.handle, arch.handle, addr, &mut count) }, - Some(false) => unsafe { BNGetUserAddressTags(self.handle, arch.handle, addr, &mut count) }, + Some(true) => unsafe { + BNGetAutoAddressTags(self.handle, arch.handle, addr, &mut count) + }, + Some(false) => unsafe { + BNGetUserAddressTags(self.handle, arch.handle, addr, &mut count) + }, }; assert!(!tags.is_null()); unsafe { Array::new(tags, count, ()) } @@ -1399,13 +1430,31 @@ impl Function { let tags = match auto { None => unsafe { - BNGetAddressTagsInRange(self.handle, arch.handle, range.start, range.end, &mut count) + BNGetAddressTagsInRange( + self.handle, + arch.handle, + range.start, + range.end, + &mut count, + ) }, Some(true) => unsafe { - BNGetAutoAddressTagsInRange(self.handle, arch.handle, range.start, range.end, &mut count) + BNGetAutoAddressTagsInRange( + self.handle, + arch.handle, + range.start, + range.end, + &mut count, + ) }, Some(false) => unsafe { - BNGetUserAddressTagsInRange(self.handle, arch.handle, range.start, range.end, &mut count) + BNGetUserAddressTagsInRange( + self.handle, + arch.handle, + range.start, + range.end, + &mut count, + ) }, }; assert!(!tags.is_null()); @@ -1482,7 +1531,8 @@ impl Function { ) -> Array { let arch = arch.unwrap_or_else(|| self.arch()); let mut count = 0; - let branches = unsafe { BNGetIndirectBranchesAt(self.handle, arch.handle, addr, &mut count) }; + let branches = + unsafe { BNGetIndirectBranchesAt(self.handle, arch.handle, addr, &mut count) }; assert!(!branches.is_null()); unsafe { Array::new(branches, count, ()) } } @@ -1525,7 +1575,7 @@ impl Function { /// ```no_run /// # use binaryninja::function::{HighlightColor, HighlightStandardColor}; /// # let function: binaryninja::function::Function = todo!(); - /// let color = HighlightColor::StandardHighlightColor { + /// let color = HighlightColor::StandardHighlightColor { /// color: HighlightStandardColor::RedHighlightColor, /// alpha: u8::MAX /// }; @@ -1570,7 +1620,9 @@ impl Function { arch: Option, ) -> IntegerDisplayType { let arch = arch.unwrap_or_else(|| self.arch()); - unsafe { BNGetIntegerConstantDisplayType(self.handle, arch.handle, instr_addr, value, operand) } + unsafe { + BNGetIntegerConstantDisplayType(self.handle, arch.handle, instr_addr, value, operand) + } } /// Change the text display type for an integer token in the disassembly or IL views @@ -1674,7 +1726,8 @@ impl Function { arch: Option, ) -> RegisterValue { let arch = arch.unwrap_or_else(|| self.arch()); - let register = unsafe { BNGetRegisterValueAtInstruction(self.handle, arch.handle, addr, reg) }; + let register = + unsafe { BNGetRegisterValueAtInstruction(self.handle, arch.handle, addr, reg) }; register.into() } @@ -1728,8 +1781,9 @@ impl Function { ) -> Array { let arch = arch.unwrap_or_else(|| self.arch()); let mut count = 0; - let regs = - unsafe { BNGetRegistersWrittenByInstruction(self.handle, arch.handle, addr, &mut count) }; + let regs = unsafe { + BNGetRegistersWrittenByInstruction(self.handle, arch.handle, addr, &mut count) + }; assert!(!regs.is_null()); unsafe { Array::new(regs, count, arch) } } @@ -1776,8 +1830,9 @@ impl Function { arch: Option, ) -> RegisterValue { let arch = arch.unwrap_or_else(|| self.arch()); - let value = - unsafe { BNGetStackContentsAtInstruction(self.handle, arch.handle, addr, offset, size) }; + let value = unsafe { + BNGetStackContentsAtInstruction(self.handle, arch.handle, addr, offset, size) + }; value.into() } @@ -1789,8 +1844,9 @@ impl Function { arch: Option, ) -> RegisterValue { let arch = arch.unwrap_or_else(|| self.arch()); - let value = - unsafe { BNGetStackContentsAfterInstruction(self.handle, arch.handle, addr, offset, size) }; + let value = unsafe { + BNGetStackContentsAfterInstruction(self.handle, arch.handle, addr, offset, size) + }; value.into() } @@ -1803,7 +1859,13 @@ impl Function { let arch = arch.unwrap_or_else(|| self.arch()); let mut found_value = BNVariableNameAndType::default(); let found = unsafe { - BNGetStackVariableAtFrameOffset(self.handle, arch.handle, addr, offset, &mut found_value) + BNGetStackVariableAtFrameOffset( + self.handle, + arch.handle, + addr, + offset, + &mut found_value, + ) }; if !found { return None; @@ -2234,15 +2296,17 @@ impl Function { settings: Option, ) -> Ref { let settings_raw = settings.map(|s| s.handle).unwrap_or(std::ptr::null_mut()); - let result = unsafe { BNCreateFunctionGraph(self.handle, view_type.as_raw().0, settings_raw) }; + let result = + unsafe { BNCreateFunctionGraph(self.handle, view_type.as_raw().0, settings_raw) }; unsafe { Ref::new(FlowGraph::from_raw(result)) } } pub fn parent_components(&self) -> Array { let mut count = 0; - let result = unsafe{ BNGetFunctionParentComponents(self.view().handle, self.handle, &mut count) }; + let result = + unsafe { BNGetFunctionParentComponents(self.view().handle, self.handle, &mut count) }; assert!(!result.is_null()); - unsafe{ Array::new(result, count, ()) } + unsafe { Array::new(result, count, ()) } } } @@ -2291,7 +2355,7 @@ unsafe impl CoreArrayProviderInner for Function { unsafe fn free(raw: *mut Self::Raw, count: usize, _context: &Self::Context) { BNFreeFunctionList(raw, count); } - + unsafe fn wrap_raw<'a>(raw: &'a Self::Raw, context: &'a Self::Context) -> Self::Wrapped<'a> { Guard::new(Self::from_raw(*raw), context) } @@ -2320,14 +2384,14 @@ impl PartialEq for Function { #[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] pub struct AddressRange { pub start: u64, - pub end: u64 + pub end: u64, } impl From for AddressRange { fn from(raw: BNAddressRange) -> Self { Self { start: raw.start, - end: raw.end + end: raw.end, } } } @@ -2336,7 +2400,7 @@ impl From for BNAddressRange { fn from(raw: AddressRange) -> Self { Self { start: raw.start, - end: raw.end + end: raw.end, } } } @@ -2351,7 +2415,7 @@ unsafe impl CoreArrayProviderInner for AddressRange { unsafe fn free(raw: *mut Self::Raw, _count: usize, _context: &Self::Context) { BNFreeAddressRanges(raw); } - + unsafe fn wrap_raw<'a>(raw: &'a Self::Raw, _context: &'a Self::Context) -> Self::Wrapped<'a> { Self::from(*raw) } @@ -2392,7 +2456,7 @@ unsafe impl CoreArrayProviderInner for PerformanceInfo { unsafe fn free(raw: *mut Self::Raw, count: usize, _context: &Self::Context) { BNFreeAnalysisPerformanceInfo(raw, count); } - + unsafe fn wrap_raw<'a>(raw: &'a Self::Raw, _context: &'a Self::Context) -> Self::Wrapped<'a> { // TODO: Swap this to the ref version. Self::from(*raw) @@ -2464,7 +2528,7 @@ unsafe impl CoreArrayProviderInner for ConstantReference { unsafe fn free(raw: *mut Self::Raw, _count: usize, _context: &Self::Context) { BNFreeConstantReferenceList(raw) } - + unsafe fn wrap_raw<'a>(raw: &'a Self::Raw, _context: &'a Self::Context) -> Self::Wrapped<'a> { Self::from(*raw) } @@ -2543,28 +2607,22 @@ pub enum HighlightColor { impl From for HighlightColor { fn from(value: BNHighlightColor) -> Self { match value.style { - BNHighlightColorStyle::StandardHighlightColor => { - Self::StandardHighlightColor { - color: value.color, - alpha: value.alpha, - } - } - BNHighlightColorStyle::MixedHighlightColor => { - Self::MixedHighlightColor { - color: value.color, - mix_color: value.mixColor, - mix: value.mix, - alpha: value.alpha, - } - } - BNHighlightColorStyle::CustomHighlightColor => { - Self::CustomHighlightColor { - r: value.r, - g: value.g, - b: value.b, - alpha: value.alpha, - } - } + BNHighlightColorStyle::StandardHighlightColor => Self::StandardHighlightColor { + color: value.color, + alpha: value.alpha, + }, + BNHighlightColorStyle::MixedHighlightColor => Self::MixedHighlightColor { + color: value.color, + mix_color: value.mixColor, + mix: value.mix, + alpha: value.alpha, + }, + BNHighlightColorStyle::CustomHighlightColor => Self::CustomHighlightColor { + r: value.r, + g: value.g, + b: value.b, + alpha: value.alpha, + }, } } } @@ -2572,34 +2630,33 @@ impl From for HighlightColor { impl From for BNHighlightColor { fn from(value: HighlightColor) -> Self { match value { - HighlightColor::StandardHighlightColor { color, alpha } => { - BNHighlightColor { - style: BNHighlightColorStyle::StandardHighlightColor, - color, - alpha, - ..Default::default() - } - } - HighlightColor::MixedHighlightColor { color, mix_color, mix, alpha } => { - BNHighlightColor { - style: BNHighlightColorStyle::MixedHighlightColor, - color, - mixColor: mix_color, - mix, - alpha, - ..Default::default() - } - } - HighlightColor::CustomHighlightColor { r, g, b, alpha } => { - BNHighlightColor { - style: BNHighlightColorStyle::CustomHighlightColor, - r, - g, - b, - alpha, - ..Default::default() - } - } + HighlightColor::StandardHighlightColor { color, alpha } => BNHighlightColor { + style: BNHighlightColorStyle::StandardHighlightColor, + color, + alpha, + ..Default::default() + }, + HighlightColor::MixedHighlightColor { + color, + mix_color, + mix, + alpha, + } => BNHighlightColor { + style: BNHighlightColorStyle::MixedHighlightColor, + color, + mixColor: mix_color, + mix, + alpha, + ..Default::default() + }, + HighlightColor::CustomHighlightColor { r, g, b, alpha } => BNHighlightColor { + style: BNHighlightColorStyle::CustomHighlightColor, + r, + g, + b, + alpha, + ..Default::default() + }, } } } @@ -2621,7 +2678,7 @@ unsafe impl CoreArrayProviderInner for Comment { unsafe fn free(raw: *mut Self::Raw, _count: usize, _context: &Self::Context) { BNFreeAddressList(raw); } - + unsafe fn wrap_raw<'a>(raw: &'a Self::Raw, function: &'a Self::Context) -> Self::Wrapped<'a> { Comment { addr: *raw, diff --git a/rust/src/functionrecognizer.rs b/rust/src/functionrecognizer.rs index 2debb8734..f16b87cb5 100644 --- a/rust/src/functionrecognizer.rs +++ b/rust/src/functionrecognizer.rs @@ -1,10 +1,8 @@ -use crate::{ - architecture::CoreArchitecture, binaryview::BinaryView, function::Function, llil, -}; -use binaryninjacore_sys::*; -use std::os::raw::c_void; use crate::llil::LowLevelILFunction; use crate::mlil::MediumLevelILFunction; +use crate::{architecture::CoreArchitecture, binaryview::BinaryView, function::Function, llil}; +use binaryninjacore_sys::*; +use std::os::raw::c_void; pub trait FunctionRecognizer { fn recognize_low_level_il( @@ -67,7 +65,9 @@ where let bv = unsafe { BinaryView::from_raw(bv).to_owned() }; let func = unsafe { Function::from_raw(func).to_owned() }; let mlil = unsafe { MediumLevelILFunction::from_raw(mlil).to_owned() }; - context.recognizer.recognize_medium_level_il(&bv, &func, &mlil) + context + .recognizer + .recognize_medium_level_il(&bv, &func, &mlil) } let recognizer = FunctionRecognizerHandlerContext { recognizer }; diff --git a/rust/src/headless.rs b/rust/src/headless.rs index 0cc92faaf..77b95fc0f 100644 --- a/rust/src/headless.rs +++ b/rust/src/headless.rs @@ -13,8 +13,7 @@ // limitations under the License. use crate::{ - binaryview, - rc, + binaryview, rc, string::{BnStrCompatible, IntoJson}, }; @@ -85,11 +84,12 @@ use binaryninjacore_sys::{BNInitPlugins, BNInitRepoPlugins, BNSetBundledPluginDi pub fn init() { match crate::product().as_str() { "Binary Ninja Enterprise Client" | "Binary Ninja Ultimate" => { - crate::enterprise::checkout_license(Duration::from_secs(900)).expect("Failed to checkout license"); - }, + crate::enterprise::checkout_license(Duration::from_secs(900)) + .expect("Failed to checkout license"); + } _ => {} } - + unsafe { let path = binja_path().join("plugins").into_os_string(); let path = path.into_string().unwrap(); @@ -107,10 +107,10 @@ pub fn shutdown() { match crate::product().as_str() { "Binary Ninja Enterprise Client" | "Binary Ninja Ultimate" => { crate::enterprise::release_license() - }, + } _ => {} } - + unsafe { binaryninjacore_sys::BNShutdown() }; } diff --git a/rust/src/hlil/function.rs b/rust/src/hlil/function.rs index 2ea1a79ec..66e75be38 100644 --- a/rust/src/hlil/function.rs +++ b/rust/src/hlil/function.rs @@ -2,12 +2,12 @@ use std::hash::{Hash, Hasher}; use binaryninjacore_sys::*; +use super::{HighLevelILBlock, HighLevelILInstruction, HighLevelILLiftedInstruction}; use crate::architecture::CoreArchitecture; use crate::basicblock::BasicBlock; use crate::function::Function; use crate::rc::{Array, Ref, RefCountable}; use crate::variable::{SSAVariable, Variable}; -use super::{HighLevelILBlock, HighLevelILInstruction, HighLevelILLiftedInstruction}; pub struct HighLevelILFunction { pub(crate) full_ast: bool, @@ -242,7 +242,8 @@ impl HighLevelILFunction { pub fn ssa_variables(&self, variable: &Variable) -> Array { let mut count = 0; let raw_variable = BNVariable::from(variable); - let variables = unsafe { BNGetHighLevelILVariableSSAVersions(self.handle, &raw_variable, &mut count) }; + let variables = + unsafe { BNGetHighLevelILVariableSSAVersions(self.handle, &raw_variable, &mut count) }; unsafe { Array::new(variables, count, *variable) } } } diff --git a/rust/src/hlil/instruction.rs b/rust/src/hlil/instruction.rs index f03c673ce..4bf6a477e 100644 --- a/rust/src/hlil/instruction.rs +++ b/rust/src/hlil/instruction.rs @@ -1,5 +1,7 @@ use binaryninjacore_sys::*; +use super::operation::*; +use super::{HighLevelILFunction, HighLevelILLiftedInstruction, HighLevelILLiftedInstructionKind}; use crate::architecture::CoreIntrinsic; use crate::confidence::Conf; use crate::disassembly::DisassemblyTextLine; @@ -7,8 +9,6 @@ use crate::operand_iter::OperandIter; use crate::rc::{Array, CoreArrayProvider, CoreArrayProviderInner, Ref}; use crate::types::Type; use crate::variable::{ConstantData, RegisterValue, SSAVariable, Variable}; -use super::operation::*; -use super::{HighLevelILFunction, HighLevelILLiftedInstruction, HighLevelILLiftedInstructionKind}; #[derive(Clone)] pub struct HighLevelILInstruction { @@ -815,11 +815,17 @@ impl HighLevelILInstruction { cond_false: self.lift_operand(op.cond_false), }), Intrinsic(op) => Lifted::Intrinsic(LiftedIntrinsic { - intrinsic: CoreIntrinsic::new(self.function.get_function().arch().handle, op.intrinsic), + intrinsic: CoreIntrinsic::new( + self.function.get_function().arch().handle, + op.intrinsic, + ), params: self.lift_instruction_list(op.first_param, op.num_params), }), IntrinsicSsa(op) => Lifted::IntrinsicSsa(LiftedIntrinsicSsa { - intrinsic: CoreIntrinsic::new(self.function.get_function().arch().handle, op.intrinsic), + intrinsic: CoreIntrinsic::new( + self.function.get_function().arch().handle, + op.intrinsic, + ), params: self.lift_instruction_list(op.first_param, op.num_params), dest_memory: op.dest_memory, src_memory: op.src_memory, diff --git a/rust/src/hlil/operation.rs b/rust/src/hlil/operation.rs index ced8bdc99..f7a4b5be3 100644 --- a/rust/src/hlil/operation.rs +++ b/rust/src/hlil/operation.rs @@ -2,12 +2,12 @@ use core::ffi; use binaryninjacore_sys::*; +use super::HighLevelILLiftedInstruction; use crate::architecture::CoreIntrinsic; use crate::function::Function; use crate::rc::Ref; use crate::string::{BnStrCompatible, BnString}; use crate::variable::{ConstantData, SSAVariable, Variable}; -use super::HighLevelILLiftedInstruction; #[derive(Clone, Debug, PartialEq, Eq)] pub struct GotoLabel { diff --git a/rust/src/interaction.rs b/rust/src/interaction.rs index e00908320..9067b5ff4 100644 --- a/rust/src/interaction.rs +++ b/rust/src/interaction.rs @@ -96,7 +96,11 @@ pub fn get_open_filename_input(prompt: &str, extension: &str) -> Option Some(PathBuf::from(string.as_str())) } -pub fn get_save_filename_input(prompt: &str, extension: &str, default_name: &str) -> Option { +pub fn get_save_filename_input( + prompt: &str, + extension: &str, + default_name: &str, +) -> Option { let mut value: *mut c_char = std::ptr::null_mut(); let result = unsafe { diff --git a/rust/src/lib.rs b/rust/src/lib.rs index 6ba40cecd..beeb7e572 100644 --- a/rust/src/lib.rs +++ b/rust/src/lib.rs @@ -131,15 +131,16 @@ pub mod binaryview; pub mod binarywriter; pub mod callingconvention; pub mod command; +pub mod component; +pub mod confidence; pub mod custombinaryview; pub mod database; pub mod databuffer; pub mod debuginfo; pub mod demangle; pub mod disassembly; -pub mod enterprise; -pub mod component; pub mod downloadprovider; +pub mod enterprise; pub mod externallibrary; pub mod fileaccessor; pub mod filemetadata; @@ -166,25 +167,24 @@ pub mod string; pub mod symbol; pub mod tags; pub mod templatesimplifier; -pub mod typelibrary; pub mod typearchive; +pub mod typelibrary; pub mod types; pub mod update; -pub mod workflow; pub mod variable; -pub mod confidence; +pub mod workflow; -use std::collections::HashMap; -use std::ffi::{c_char, c_void, CStr}; -use std::path::PathBuf; +use crate::filemetadata::FileMetadata; +use crate::function::Function; use binaryninjacore_sys::*; use binaryview::BinaryView; use metadata::Metadata; use metadata::MetadataType; +use std::collections::HashMap; +use std::ffi::{c_char, c_void, CStr}; +use std::path::PathBuf; use string::BnStrCompatible; use string::IntoJson; -use crate::filemetadata::FileMetadata; -use crate::function::Function; // Commented out to suppress unused warnings // const BN_MAX_INSTRUCTION_LENGTH: u64 = 256; // const BN_DEFAULT_INSTRUCTION_LENGTH: u64 = 16; @@ -203,9 +203,9 @@ use crate::function::Function; // const BN_HEURISTIC_CONFIDENCE: u8 = 192; pub use binaryninjacore_sys::BNBranchType as BranchType; +pub use binaryninjacore_sys::BNDataFlowQueryOption as DataFlowQueryOption; pub use binaryninjacore_sys::BNEndianness as Endianness; pub use binaryninjacore_sys::BNILBranchDependence as ILBranchDependence; -pub use binaryninjacore_sys::BNDataFlowQueryOption as DataFlowQueryOption; pub const BN_FULL_CONFIDENCE: u8 = u8::MAX; pub const BN_INVALID_EXPR: usize = usize::MAX; @@ -222,27 +222,18 @@ unsafe extern "C" fn cb_progress_func bool>( closure(progress, total) } - -unsafe extern "C" fn cb_progress_nop( - _ctxt: *mut c_void, - _arg1: usize, - _arg2: usize -) -> bool { +unsafe extern "C" fn cb_progress_nop(_ctxt: *mut c_void, _arg1: usize, _arg2: usize) -> bool { true } - /// The main way to open and load files into Binary Ninja. Make sure you've properly initialized the core before calling this function. See [`crate::headless::init()`] -pub fn load( - filename: S, -) -> Option> +pub fn load(filename: S) -> Option> where S: BnStrCompatible, { let filename = filename.into_bytes_with_nul(); let options = "\x00"; - let handle = unsafe { BNLoadFilename( filename.as_ref().as_ptr() as *mut _, @@ -260,10 +251,7 @@ where } } -pub fn load_with_progress( - filename: S, - mut progress: F, -) -> Option> +pub fn load_with_progress(filename: S, mut progress: F) -> Option> where S: BnStrCompatible, F: FnMut(usize, usize) -> bool, @@ -378,7 +366,7 @@ where let progress_ctx = match progress { Some(mut x) => &mut x as *mut F as *mut c_void, - None => std::ptr::null_mut() + None => std::ptr::null_mut(), }; let handle = unsafe { @@ -463,7 +451,7 @@ where let progress_ctx = match progress { Some(mut x) => &mut x as *mut F as *mut c_void, - None => std::ptr::null_mut() + None => std::ptr::null_mut(), }; let handle = unsafe { @@ -505,9 +493,7 @@ pub fn bundled_plugin_directory() -> Result { pub fn set_bundled_plugin_directory(new_dir: S) { unsafe { - BNSetBundledPluginDirectory( - new_dir.into_bytes_with_nul().as_ref().as_ptr() as *const c_char, - ) + BNSetBundledPluginDirectory(new_dir.into_bytes_with_nul().as_ref().as_ptr() as *const c_char) }; } @@ -560,7 +546,7 @@ pub fn path_relative_to_bundled_plugin_directory( ) -> Result { let s: *mut c_char = unsafe { BNGetPathRelativeToBundledPluginDirectory( - path.into_bytes_with_nul().as_ref().as_ptr() as *const c_char, + path.into_bytes_with_nul().as_ref().as_ptr() as *const c_char ) }; if s.is_null() { @@ -576,7 +562,7 @@ pub fn path_relative_to_user_plugin_directory( ) -> Result { let s: *mut c_char = unsafe { BNGetPathRelativeToUserPluginDirectory( - path.into_bytes_with_nul().as_ref().as_ptr() as *const c_char, + path.into_bytes_with_nul().as_ref().as_ptr() as *const c_char ) }; if s.is_null() { @@ -590,7 +576,7 @@ pub fn path_relative_to_user_plugin_directory( pub fn path_relative_to_user_directory(path: S) -> Result { let s: *mut c_char = unsafe { BNGetPathRelativeToUserDirectory( - path.into_bytes_with_nul().as_ref().as_ptr() as *const c_char, + path.into_bytes_with_nul().as_ref().as_ptr() as *const c_char ) }; if s.is_null() { @@ -622,8 +608,7 @@ pub trait ObjectDestructor: 'static + Sync + Sized { fn destruct_file_metadata(&self, _metadata: &FileMetadata) {} fn destruct_function(&self, _func: &Function) {} - unsafe extern "C" fn cb_destruct_binary_view(ctxt: *mut c_void, view: *mut BNBinaryView) - { + unsafe extern "C" fn cb_destruct_binary_view(ctxt: *mut c_void, view: *mut BNBinaryView) { ffi_wrap!("ObjectDestructor::destruct_view", { let view_type = &*(ctxt as *mut Self); let view = BinaryView { handle: view }; @@ -631,8 +616,7 @@ pub trait ObjectDestructor: 'static + Sync + Sized { }) } - unsafe extern "C" fn cb_destruct_file_metadata(ctxt: *mut c_void, file: *mut BNFileMetadata) - { + unsafe extern "C" fn cb_destruct_file_metadata(ctxt: *mut c_void, file: *mut BNFileMetadata) { ffi_wrap!("ObjectDestructor::destruct_file_metadata", { let view_type = &*(ctxt as *mut Self); let file = FileMetadata::from_raw(file); @@ -640,15 +624,14 @@ pub trait ObjectDestructor: 'static + Sync + Sized { }) } - unsafe extern "C" fn cb_destruct_function(ctxt: *mut c_void, func: *mut BNFunction) - { + unsafe extern "C" fn cb_destruct_function(ctxt: *mut c_void, func: *mut BNFunction) { ffi_wrap!("ObjectDestructor::destruct_function", { let view_type = &*(ctxt as *mut Self); let func = Function { handle: func }; view_type.destruct_function(&func); }) } - + unsafe fn as_callbacks(&'static mut self) -> BNObjectDestructionCallbacks { BNObjectDestructionCallbacks { context: std::mem::transmute(&self), @@ -657,11 +640,11 @@ pub trait ObjectDestructor: 'static + Sync + Sized { destructFunction: Some(Self::cb_destruct_function), } } - + fn register(&'static mut self) { unsafe { BNRegisterObjectDestructionCallbacks(&mut self.as_callbacks()) }; } - + fn unregister(&'static mut self) { unsafe { BNUnregisterObjectDestructionCallbacks(&mut self.as_callbacks()) }; } @@ -724,9 +707,7 @@ pub fn product_type() -> string::BnString { } pub fn license_expiration_time() -> std::time::SystemTime { - let m = std::time::Duration::from_secs(unsafe { - BNGetLicenseExpirationTime() - }); + let m = std::time::Duration::from_secs(unsafe { BNGetLicenseExpirationTime() }); std::time::UNIX_EPOCH + m } @@ -766,17 +747,13 @@ pub fn plugin_ui_abi_minimum_version() -> u32 { pub fn add_required_plugin_dependency(name: S) { unsafe { - BNAddRequiredPluginDependency( - name.into_bytes_with_nul().as_ref().as_ptr() as *const c_char, - ) + BNAddRequiredPluginDependency(name.into_bytes_with_nul().as_ref().as_ptr() as *const c_char) }; } pub fn add_optional_plugin_dependency(name: S) { unsafe { - BNAddOptionalPluginDependency( - name.into_bytes_with_nul().as_ref().as_ptr() as *const c_char, - ) + BNAddOptionalPluginDependency(name.into_bytes_with_nul().as_ref().as_ptr() as *const c_char) }; } diff --git a/rust/src/linearview.rs b/rust/src/linearview.rs index 14ab911c4..0aae4ba9e 100644 --- a/rust/src/linearview.rs +++ b/rust/src/linearview.rs @@ -433,7 +433,7 @@ unsafe impl CoreArrayProviderInner for LinearDisassemblyLine { unsafe fn free(raw: *mut Self::Raw, count: usize, _context: &Self::Context) { 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) diff --git a/rust/src/llil/function.rs b/rust/src/llil/function.rs index ff75e0c7a..95b34b64a 100644 --- a/rust/src/llil/function.rs +++ b/rust/src/llil/function.rs @@ -18,8 +18,8 @@ use binaryninjacore_sys::BNLowLevelILFunction; use binaryninjacore_sys::BNNewLowLevelILFunctionReference; use std::borrow::Borrow; -use std::marker::PhantomData; use std::hash::{Hash, Hasher}; +use std::marker::PhantomData; use crate::architecture::CoreArchitecture; use crate::basicblock::BasicBlock; @@ -80,7 +80,7 @@ where _form: PhantomData, } } - + pub(crate) unsafe fn ref_from_raw( borrower: A::Handle, handle: *mut BNLowLevelILFunction, @@ -168,10 +168,7 @@ impl LowLevelILFunction> { // TODO: Document what happens when you pass None for `source_func`. // TODO: Doing so would construct a LowLevelILFunction with no basic blocks // TODO: Document why you would want to do that. - pub fn new( - arch: CoreArchitecture, - source_func: Option, - ) -> Ref { + pub fn new(arch: CoreArchitecture, source_func: Option) -> Ref { use binaryninjacore_sys::BNCreateLowLevelILFunction; let handle = unsafe { @@ -180,7 +177,7 @@ impl LowLevelILFunction> { None => BNCreateLowLevelILFunction(arch.handle, std::ptr::null_mut()), } }; - + // BNCreateLowLevelILFunction should always return a valid object. assert!(!handle.is_null()); @@ -234,12 +231,20 @@ where } } -unsafe impl Send for LowLevelILFunction {} -unsafe impl Sync for LowLevelILFunction {} +unsafe impl Send + for LowLevelILFunction +{ +} +unsafe impl Sync + for LowLevelILFunction +{ +} impl Eq for LowLevelILFunction {} -impl PartialEq for LowLevelILFunction { +impl PartialEq + for LowLevelILFunction +{ fn eq(&self, rhs: &Self) -> bool { self.get_function().eq(&rhs.get_function()) } @@ -249,4 +254,4 @@ impl Hash for LowLevelI fn hash(&self, state: &mut H) { self.get_function().hash(state) } -} \ No newline at end of file +} diff --git a/rust/src/llil/lifting.rs b/rust/src/llil/lifting.rs index 37514f610..56f10c773 100644 --- a/rust/src/llil/lifting.rs +++ b/rust/src/llil/lifting.rs @@ -19,9 +19,12 @@ use crate::architecture::Register as ArchReg; use crate::architecture::{ Flag, FlagClass, FlagCondition, FlagGroup, FlagRole, FlagWrite, Intrinsic, }; -use binaryninjacore_sys::{BNAddLowLevelILLabelForAddress, BNLowLevelILOperation}; use crate::function::Location; -use crate::llil::{Expression, ExpressionResultType, LowLevelILFunction, LiftedExpr, LiftedNonSSA, Lifter, Mutable, NonSSA, Register, ValueExpr, VoidExpr}; +use crate::llil::{ + Expression, ExpressionResultType, LiftedExpr, LiftedNonSSA, Lifter, LowLevelILFunction, + Mutable, NonSSA, Register, ValueExpr, VoidExpr, +}; +use binaryninjacore_sys::{BNAddLowLevelILLabelForAddress, BNLowLevelILOperation}; use binaryninjacore_sys::{BNLowLevelILLabel, BNRegisterOrConstant}; pub trait Liftable<'func, A: 'func + Architecture> { @@ -400,8 +403,12 @@ where let class_id = class.map(|c| c.id()).unwrap_or(0); unsafe { - let expr_idx = - BNGetDefaultArchitectureFlagConditionLowLevelIL(arch.as_ref().handle, cond, class_id, il.handle); + let expr_idx = BNGetDefaultArchitectureFlagConditionLowLevelIL( + arch.as_ref().handle, + cond, + class_id, + il.handle, + ); Expression::new(il, expr_idx) } @@ -606,9 +613,7 @@ where pub fn from_expr(expr: Expression<'a, A, Mutable, NonSSA, R>) -> Self { use binaryninjacore_sys::BNGetLowLevelILByIndex; - let instr = unsafe { - BNGetLowLevelILByIndex(expr.function.handle, expr.expr_idx) - }; + let instr = unsafe { BNGetLowLevelILByIndex(expr.function.handle, expr.expr_idx) }; ExpressionBuilder { function: expr.function, @@ -619,7 +624,7 @@ where op2: instr.operands[1], op3: instr.operands[2], op4: instr.operands[3], - _ty: PhantomData + _ty: PhantomData, } } @@ -999,7 +1004,7 @@ where &mut raw_false_label, ) }; - + // Update the labels after they have been resolved. *true_label = Label::from(raw_true_label); *false_label = Label::from(raw_false_label); @@ -1453,28 +1458,30 @@ where pub fn label_for_address>(&self, loc: L) -> Option( @@ -2480,14 +2655,14 @@ where let mut lifter = unsafe { Lifter::from_raw(custom_arch_handle, il) }; - if let Some(group) = custom_arch.flag_group_from_id(group) { + if let Some(group) = custom_arch.flag_group_from_id(FlagGroupId(group)) { if let Some(expr) = custom_arch.flag_group_llil(group, &mut lifter) { // TODO verify that returned expr is a bool value - return expr.expr_idx; + return expr.index.0; } } - lifter.unimplemented().expr_idx + lifter.unimplemented().index.0 } extern "C" fn cb_free_register_list(_ctxt: *mut c_void, regs: *mut u32, count: usize) { @@ -2508,12 +2683,12 @@ where let custom_arch = unsafe { &*(ctxt as *mut A) }; let result = unsafe { &mut *result }; - if let Some(reg) = custom_arch.register_from_id(reg) { + if let Some(reg) = custom_arch.register_from_id(RegisterId(reg)) { let info = reg.info(); result.fullWidthRegister = match info.parent() { - Some(p) => p.id(), - None => reg.id(), + Some(p) => p.id().0, + None => reg.id().0, }; result.offset = info.offset(); @@ -2529,7 +2704,7 @@ where let custom_arch = unsafe { &*(ctxt as *mut A) }; if let Some(reg) = custom_arch.stack_pointer_reg() { - reg.id() + reg.id().0 } else { 0xffff_ffff } @@ -2542,7 +2717,7 @@ where let custom_arch = unsafe { &*(ctxt as *mut A) }; if let Some(reg) = custom_arch.link_reg() { - reg.id() + reg.id().0 } else { 0xffff_ffff } @@ -2554,7 +2729,7 @@ where { let custom_arch = unsafe { &*(ctxt as *mut A) }; - match custom_arch.register_stack_from_id(stack) { + match custom_arch.register_stack_from_id(RegisterStackId(stack)) { Some(stack) => BnString::new(stack.name().as_ref()).into_raw(), None => BnString::new("invalid_reg_stack").into_raw(), } @@ -2568,7 +2743,7 @@ where let mut regs: Box<[_]> = custom_arch .register_stacks() .iter() - .map(|r| r.id()) + .map(|r| r.id().0) .collect(); // SAFETY: Passed in to be written @@ -2588,22 +2763,22 @@ where let custom_arch = unsafe { &*(ctxt as *mut A) }; let result = unsafe { &mut *result }; - if let Some(stack) = custom_arch.register_stack_from_id(stack) { + if let Some(stack) = custom_arch.register_stack_from_id(RegisterStackId(stack)) { let info = stack.info(); let (reg, count) = info.storage_regs(); - result.firstStorageReg = reg.id(); - result.storageCount = count; + result.firstStorageReg = reg.id().0; + result.storageCount = count as u32; if let Some((reg, count)) = info.top_relative_regs() { - result.firstTopRelativeReg = reg.id(); - result.topRelativeCount = count; + result.firstTopRelativeReg = reg.id().0; + result.topRelativeCount = count as u32; } else { result.firstTopRelativeReg = 0xffff_ffff; result.topRelativeCount = 0; } - result.stackTopReg = info.stack_top_reg().id(); + result.stackTopReg = info.stack_top_reg().id().0; } } @@ -2612,7 +2787,11 @@ where A: 'static + Architecture> + Send + Sync, { let custom_arch = unsafe { &*(ctxt as *mut A) }; - custom_arch.intrinsic_class(intrinsic) + match custom_arch.intrinsic_from_id(IntrinsicId(intrinsic)) { + Some(intrinsic) => intrinsic.class(), + // TODO: Make this unreachable? + None => BNIntrinsicClass::GeneralIntrinsicClass, + } } extern "C" fn cb_intrinsic_name(ctxt: *mut c_void, intrinsic: u32) -> *mut c_char @@ -2620,8 +2799,8 @@ where A: 'static + Architecture> + Send + Sync, { let custom_arch = unsafe { &*(ctxt as *mut A) }; - match custom_arch.intrinsic_from_id(intrinsic) { - Some(intrinsic) => BnString::new(intrinsic.name().as_ref()).into_raw(), + match custom_arch.intrinsic_from_id(IntrinsicId(intrinsic)) { + Some(intrinsic) => BnString::new(intrinsic.name()).into_raw(), None => BnString::new("invalid_intrinsic").into_raw(), } } @@ -2631,7 +2810,7 @@ where A: 'static + Architecture> + Send + Sync, { let custom_arch = unsafe { &*(ctxt as *mut A) }; - let mut intrinsics: Box<[_]> = custom_arch.intrinsics().iter().map(|i| i.id()).collect(); + let mut intrinsics: Box<[_]> = custom_arch.intrinsics().iter().map(|i| i.id().0).collect(); // SAFETY: Passed in to be written unsafe { *count = intrinsics.len() }; @@ -2650,7 +2829,7 @@ where { let custom_arch = unsafe { &*(ctxt as *mut A) }; - let Some(intrinsic) = custom_arch.intrinsic_from_id(intrinsic) else { + let Some(intrinsic) = custom_arch.intrinsic_from_id(IntrinsicId(intrinsic)) else { // SAFETY: Passed in to be written unsafe { *count = 0; @@ -2703,7 +2882,7 @@ where { let custom_arch = unsafe { &*(ctxt as *mut A) }; - let Some(intrinsic) = custom_arch.intrinsic_from_id(intrinsic) else { + let Some(intrinsic) = custom_arch.intrinsic_from_id(IntrinsicId(intrinsic)) else { // SAFETY: Passed in to be written unsafe { *count = 0; diff --git a/rust/src/basicblock.rs b/rust/src/basicblock.rs index f8713b713..56ff0abeb 100644 --- a/rust/src/basicblock.rs +++ b/rust/src/basicblock.rs @@ -130,10 +130,12 @@ impl BasicBlock { self.context.iter(self) } + // TODO: This needs to be generic over the IL index / mapped address. pub fn raw_start(&self) -> u64 { unsafe { BNGetBasicBlockStart(self.handle) } } + // TODO: This needs to be generic over the IL index / mapped address. pub fn raw_end(&self) -> u64 { unsafe { BNGetBasicBlockEnd(self.handle) } } diff --git a/rust/src/callingconvention.rs b/rust/src/callingconvention.rs index 430b3aeff..2b338c694 100644 --- a/rust/src/callingconvention.rs +++ b/rust/src/callingconvention.rs @@ -22,7 +22,7 @@ use std::marker::PhantomData; use binaryninjacore_sys::*; -use crate::architecture::{Architecture, ArchitectureExt, CoreArchitecture, Register}; +use crate::architecture::{Architecture, ArchitectureExt, CoreArchitecture, Register, RegisterId}; use crate::rc::{CoreArrayProvider, CoreArrayProviderInner, Guard, Ref, RefCountable}; use crate::string::*; use crate::types::FunctionParameter; @@ -98,7 +98,7 @@ where .cc .caller_saved_registers() .iter() - .map(|r| r.id()) + .map(|r| r.id().0) .collect(); // SAFETY: `count` is an out parameter @@ -119,7 +119,7 @@ where .cc .callee_saved_registers() .iter() - .map(|r| r.id()) + .map(|r| r.id().0) .collect(); // SAFETY: `count` is an out parameter @@ -136,7 +136,12 @@ where { ffi_wrap!("CallingConvention::int_arg_registers", unsafe { let ctxt = &*(ctxt as *mut CustomCallingConventionContext); - let mut regs: Vec<_> = ctxt.cc.int_arg_registers().iter().map(|r| r.id()).collect(); + let mut regs: Vec<_> = ctxt + .cc + .int_arg_registers() + .iter() + .map(|r| r.id().0) + .collect(); // SAFETY: `count` is an out parameter *count = regs.len(); @@ -156,7 +161,7 @@ where .cc .float_arg_registers() .iter() - .map(|r| r.id()) + .map(|r| r.id().0) .collect(); // SAFETY: `count` is an out parameter @@ -222,7 +227,7 @@ where let ctxt = &*(ctxt as *mut CustomCallingConventionContext); match ctxt.cc.return_int_reg() { - Some(r) => r.id(), + Some(r) => r.id().0, _ => 0xffff_ffff, } }) @@ -236,7 +241,7 @@ where let ctxt = &*(ctxt as *mut CustomCallingConventionContext); match ctxt.cc.return_hi_int_reg() { - Some(r) => r.id(), + Some(r) => r.id().0, _ => 0xffff_ffff, } }) @@ -250,7 +255,7 @@ where let ctxt = &*(ctxt as *mut CustomCallingConventionContext); match ctxt.cc.return_float_reg() { - Some(r) => r.id(), + Some(r) => r.id().0, _ => 0xffff_ffff, } }) @@ -264,7 +269,7 @@ where let ctxt = &*(ctxt as *mut CustomCallingConventionContext); match ctxt.cc.global_pointer_reg() { - Some(r) => r.id(), + Some(r) => r.id().0, _ => 0xffff_ffff, } }) @@ -283,7 +288,7 @@ where .cc .implicitly_defined_registers() .iter() - .map(|r| r.id()) + .map(|r| r.id().0) .collect(); // SAFETY: `count` is an out parameter @@ -461,7 +466,7 @@ impl CallingConvention { let mut count: usize = 0; let raw_params: Vec = params.iter().cloned().map(Into::into).collect(); let raw_vars_ptr: *mut BNVariable = if let Some(permitted_args) = permitted_registers { - let permitted_regs = permitted_args.iter().map(|r| r.id()).collect::>(); + let permitted_regs = permitted_args.iter().map(|r| r.id().0).collect::>(); unsafe { BNGetVariablesForParameters( @@ -518,10 +523,8 @@ impl CallingConventionBase for CallingConvention { let res = std::slice::from_raw_parts(regs, count) .iter() - .map(|&r| { - arch.register_from_id(r) - .expect("bad reg id from CallingConvention") - }) + .map(|&id| RegisterId(id)) + .filter_map(|r| arch.register_from_id(r)) .collect(); BNFreeRegisterList(regs); @@ -538,10 +541,8 @@ impl CallingConventionBase for CallingConvention { let res = std::slice::from_raw_parts(regs, count) .iter() - .map(|&r| { - arch.register_from_id(r) - .expect("bad reg id from CallingConvention") - }) + .map(|&id| RegisterId(id)) + .filter_map(|r| arch.register_from_id(r)) .collect(); BNFreeRegisterList(regs); @@ -558,10 +559,8 @@ impl CallingConventionBase for CallingConvention { let res = std::slice::from_raw_parts(regs, count) .iter() - .map(|&r| { - arch.register_from_id(r) - .expect("bad reg id from CallingConvention") - }) + .map(|&id| RegisterId(id)) + .filter_map(|r| arch.register_from_id(r)) .collect(); BNFreeRegisterList(regs); @@ -578,10 +577,8 @@ impl CallingConventionBase for CallingConvention { let res = std::slice::from_raw_parts(regs, count) .iter() - .map(|&r| { - arch.register_from_id(r) - .expect("bad reg id from CallingConvention") - }) + .map(|&id| RegisterId(id)) + .filter_map(|r| arch.register_from_id(r)) .collect(); BNFreeRegisterList(regs); @@ -608,28 +605,28 @@ impl CallingConventionBase for CallingConvention { fn return_int_reg(&self) -> Option { match unsafe { BNGetIntegerReturnValueRegister(self.handle) } { - id if id < 0x8000_0000 => self.arch_handle.borrow().register_from_id(id), + id if id < 0x8000_0000 => self.arch_handle.borrow().register_from_id(RegisterId(id)), _ => None, } } fn return_hi_int_reg(&self) -> Option { match unsafe { BNGetHighIntegerReturnValueRegister(self.handle) } { - id if id < 0x8000_0000 => self.arch_handle.borrow().register_from_id(id), + id if id < 0x8000_0000 => self.arch_handle.borrow().register_from_id(RegisterId(id)), _ => None, } } fn return_float_reg(&self) -> Option { match unsafe { BNGetFloatReturnValueRegister(self.handle) } { - id if id < 0x8000_0000 => self.arch_handle.borrow().register_from_id(id), + id if id < 0x8000_0000 => self.arch_handle.borrow().register_from_id(RegisterId(id)), _ => None, } } fn global_pointer_reg(&self) -> Option { match unsafe { BNGetGlobalPointerRegister(self.handle) } { - id if id < 0x8000_0000 => self.arch_handle.borrow().register_from_id(id), + id if id < 0x8000_0000 => self.arch_handle.borrow().register_from_id(RegisterId(id)), _ => None, } } diff --git a/rust/src/debuginfo.rs b/rust/src/debuginfo.rs index bb361f60e..03739e0d4 100644 --- a/rust/src/debuginfo.rs +++ b/rust/src/debuginfo.rs @@ -333,7 +333,7 @@ impl From<&BNDebugFunctionInfo> for DebugFunctionInfo { .filter_map(|c| raw_to_string(c)) .collect(), local_variables: raw_local_variables - .into_iter() + .iter() .copied() .map(Into::into) .collect(), diff --git a/rust/src/disassembly.rs b/rust/src/disassembly.rs index a08bed464..218ea750f 100644 --- a/rust/src/disassembly.rs +++ b/rust/src/disassembly.rs @@ -262,7 +262,7 @@ impl Clone for InstructionTextToken { impl Display for InstructionTextToken { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "{}", self.text().to_string()) + write!(f, "{}", self.text()) } } diff --git a/rust/src/function.rs b/rust/src/function.rs index 0eaad1a7a..8a0b6ed86 100644 --- a/rust/src/function.rs +++ b/rust/src/function.rs @@ -38,6 +38,7 @@ pub use binaryninjacore_sys::BNFunctionAnalysisSkipOverride as FunctionAnalysisS pub use binaryninjacore_sys::BNFunctionUpdateType as FunctionUpdateType; pub use binaryninjacore_sys::BNHighlightStandardColor as HighlightStandardColor; +use crate::architecture::RegisterId; use crate::confidence::Conf; use crate::hlil::HighLevelILFunction; use crate::mlil::MediumLevelILFunction; @@ -59,7 +60,7 @@ pub struct Location { } impl Location { - pub fn from_raw(addr: u64, arch: *mut BNArchitecture) -> Self { + pub(crate) fn from_raw(addr: u64, arch: *mut BNArchitecture) -> Self { Self { addr, arch: Some(unsafe { CoreArchitecture::from_raw(arch) }), @@ -211,9 +212,9 @@ impl FunctionViewType { } } -impl Into for FunctionGraphType { - fn into(self) -> FunctionViewType { - match self { +impl From for FunctionViewType { + fn from(view_type: FunctionGraphType) -> Self { + match view_type { BNFunctionGraphType::LowLevelILFunctionGraph => FunctionViewType::LowLevelIL, BNFunctionGraphType::LiftedILFunctionGraph => FunctionViewType::LiftedIL, BNFunctionGraphType::LowLevelILSSAFormFunctionGraph => { @@ -237,7 +238,9 @@ impl Into for FunctionGraphType { // Historically this was the only language representation. FunctionViewType::HighLevelLanguageRepresentation("Pseudo C".into()) } - _ => FunctionViewType::Normal, + BNFunctionGraphType::InvalidILViewType | BNFunctionGraphType::NormalFunctionGraph => { + FunctionViewType::Normal + } } } } @@ -1310,7 +1313,7 @@ impl Function { // TODO: Adjust `BuiltinType`? let mut builtin_type = BuiltinType::BuiltinNone; let buffer = DataBuffer::from_raw(unsafe { - BNGetConstantData(self.handle, state.into(), value, size, &mut builtin_type) + BNGetConstantData(self.handle, state, value, size, &mut builtin_type) }); (buffer, builtin_type) } @@ -1722,12 +1725,12 @@ impl Function { pub fn register_value_at( &self, addr: u64, - reg: u32, + reg: RegisterId, arch: Option, ) -> RegisterValue { let arch = arch.unwrap_or_else(|| self.arch()); let register = - unsafe { BNGetRegisterValueAtInstruction(self.handle, arch.handle, addr, reg) }; + unsafe { BNGetRegisterValueAtInstruction(self.handle, arch.handle, addr, reg.0) }; register.into() } @@ -1747,12 +1750,12 @@ impl Function { pub fn register_value_after( &self, addr: u64, - reg: u32, + reg: RegisterId, arch: Option, ) -> RegisterValue { let arch = arch.unwrap_or_else(|| self.arch()); let register = - unsafe { BNGetRegisterValueAfterInstruction(self.handle, arch.handle, addr, reg) }; + unsafe { BNGetRegisterValueAfterInstruction(self.handle, arch.handle, addr, reg.0) }; register.into() } @@ -1800,7 +1803,7 @@ impl Function { where I: IntoIterator, { - let mut regs: Box<[u32]> = registers.into_iter().map(|reg| reg.id()).collect(); + let mut regs: Box<[u32]> = registers.into_iter().map(|reg| reg.id().0).collect(); let mut regs = BNRegisterSetWithConfidence { regs: regs.as_mut_ptr(), count: regs.len(), @@ -1813,7 +1816,7 @@ impl Function { where I: IntoIterator, { - let mut regs: Box<[u32]> = registers.into_iter().map(|reg| reg.id()).collect(); + let mut regs: Box<[u32]> = registers.into_iter().map(|reg| reg.id().0).collect(); let mut regs = BNRegisterSetWithConfidence { regs: regs.as_mut_ptr(), count: regs.len(), @@ -2262,7 +2265,7 @@ impl Function { where I: IntoIterator, { - let mut regs: Box<[u32]> = values.into_iter().map(|reg| reg.id()).collect(); + let mut regs: Box<[u32]> = values.into_iter().map(|reg| reg.id().0).collect(); let mut regs = BNRegisterSetWithConfidence { regs: regs.as_mut_ptr(), count: regs.len(), @@ -2275,7 +2278,7 @@ impl Function { where I: IntoIterator, { - let mut regs: Box<[u32]> = values.into_iter().map(|reg| reg.id()).collect(); + let mut regs: Box<[u32]> = values.into_iter().map(|reg| reg.id().0).collect(); let mut regs = BNRegisterSetWithConfidence { regs: regs.as_mut_ptr(), count: regs.len(), diff --git a/rust/src/hlil/instruction.rs b/rust/src/hlil/instruction.rs index 4bf6a477e..664faf3c3 100644 --- a/rust/src/hlil/instruction.rs +++ b/rust/src/hlil/instruction.rs @@ -2,7 +2,7 @@ use binaryninjacore_sys::*; use super::operation::*; use super::{HighLevelILFunction, HighLevelILLiftedInstruction, HighLevelILLiftedInstructionKind}; -use crate::architecture::CoreIntrinsic; +use crate::architecture::{CoreIntrinsic, IntrinsicId}; use crate::confidence::Conf; use crate::disassembly::DisassemblyTextLine; use crate::operand_iter::OperandIter; @@ -816,16 +816,18 @@ impl HighLevelILInstruction { }), Intrinsic(op) => Lifted::Intrinsic(LiftedIntrinsic { intrinsic: CoreIntrinsic::new( - self.function.get_function().arch().handle, - op.intrinsic, - ), + self.function.get_function().arch(), + IntrinsicId(op.intrinsic), + ) + .expect("Invalid intrinsic"), params: self.lift_instruction_list(op.first_param, op.num_params), }), IntrinsicSsa(op) => Lifted::IntrinsicSsa(LiftedIntrinsicSsa { intrinsic: CoreIntrinsic::new( - self.function.get_function().arch().handle, - op.intrinsic, - ), + self.function.get_function().arch(), + IntrinsicId(op.intrinsic), + ) + .expect("Invalid intrinsic"), params: self.lift_instruction_list(op.first_param, op.num_params), dest_memory: op.dest_memory, src_memory: op.src_memory, diff --git a/rust/src/llil/block.rs b/rust/src/llil/block.rs index b1f33c80a..9068bb7a3 100644 --- a/rust/src/llil/block.rs +++ b/rust/src/llil/block.rs @@ -26,7 +26,8 @@ where F: FunctionForm, { function: &'func LowLevelILFunction, - range: Range, + // TODO: Once step_trait is stable we can do Range + range: Range, } impl<'func, A, M, F> Iterator for LowLevelILBlockIter<'func, A, M, F> @@ -38,10 +39,9 @@ where type Item = Instruction<'func, A, M, F>; fn next(&mut self) -> Option { - self.range.next().map(|i| Instruction { - function: self.function, - instr_idx: i as usize, - }) + self.range + .next() + .map(|i| Instruction::new(self.function, InstructionIndex(i))) } } @@ -65,16 +65,15 @@ where type Iter = LowLevelILBlockIter<'func, A, M, F>; fn start(&self, block: &BasicBlock) -> Instruction<'func, A, M, F> { - Instruction { - function: self.function, - instr_idx: block.raw_start() as usize, - } + // TODO: block.raw_start should really return InstructionIndex... + Instruction::new(self.function, InstructionIndex(block.raw_start() as usize)) } fn iter(&self, block: &BasicBlock) -> LowLevelILBlockIter<'func, A, M, F> { + // TODO: block.raw_start should really return InstructionIndex... LowLevelILBlockIter { function: self.function, - range: block.raw_start()..block.raw_end(), + range: (block.raw_start() as usize)..(block.raw_end() as usize), } } } diff --git a/rust/src/llil/expression.rs b/rust/src/llil/expression.rs index e3dc72ef0..b6968dbf4 100644 --- a/rust/src/llil/expression.rs +++ b/rust/src/llil/expression.rs @@ -16,6 +16,7 @@ use binaryninjacore_sys::BNGetLowLevelILByIndex; use binaryninjacore_sys::BNLowLevelILInstruction; use std::fmt; +use std::fmt::{Display, Formatter}; use std::marker::PhantomData; use super::operation; @@ -37,6 +38,15 @@ pub trait ExpressionResultType: 'static {} impl ExpressionResultType for ValueExpr {} impl ExpressionResultType for VoidExpr {} +#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] +pub struct ExpressionIndex(pub usize); + +impl Display for ExpressionIndex { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + f.write_fmt(format_args!("{}", self.0)) + } +} + pub struct Expression<'func, A, M, F, R> where A: 'func + Architecture, @@ -45,7 +55,7 @@ where R: ExpressionResultType, { pub(crate) function: &'func LowLevelILFunction, - pub(crate) expr_idx: usize, + pub index: ExpressionIndex, // tag the 'return' type of this expression pub(crate) _ty: PhantomData, @@ -58,17 +68,16 @@ where F: FunctionForm, R: ExpressionResultType, { - pub(crate) fn new(function: &'func LowLevelILFunction, expr_idx: usize) -> Self { + pub(crate) fn new( + function: &'func LowLevelILFunction, + index: ExpressionIndex, + ) -> Self { Self { function, - expr_idx, + index, _ty: PhantomData, } } - - pub fn index(&self) -> usize { - self.expr_idx - } } impl<'func, A, M, V> fmt::Debug for Expression<'func, A, M, NonSSA, ValueExpr> @@ -79,7 +88,7 @@ where { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { let op_info = self.info(); - write!(f, "", self.expr_idx, op_info) + write!(f, "", self.index, op_info) } } @@ -288,7 +297,7 @@ where pub fn info(&self) -> ExprInfo<'func, A, M, NonSSA> { unsafe { - let op = BNGetLowLevelILByIndex(self.function.handle, self.expr_idx); + let op = BNGetLowLevelILByIndex(self.function.handle, self.index.0); self.info_from_op(op) } } @@ -341,7 +350,7 @@ where pub fn info(&self) -> ExprInfo<'func, A, M, SSA> { unsafe { - let op = BNGetLowLevelILByIndex(self.function.handle, self.expr_idx); + let op = BNGetLowLevelILByIndex(self.function.handle, self.index.0); self.info_from_op(op) } } diff --git a/rust/src/llil/function.rs b/rust/src/llil/function.rs index 95b34b64a..cd1f8e39c 100644 --- a/rust/src/llil/function.rs +++ b/rust/src/llil/function.rs @@ -106,26 +106,17 @@ where if instr_idx >= BNGetLowLevelILInstructionCount(self.handle) { None } else { - Some(Instruction { - function: self, - instr_idx, - }) + Some(Instruction::new(self, InstructionIndex(instr_idx))) } } } - pub fn instruction_from_idx(&self, instr_idx: usize) -> Instruction { - unsafe { - use binaryninjacore_sys::BNGetLowLevelILInstructionCount; - if instr_idx >= BNGetLowLevelILInstructionCount(self.handle) { - panic!("instruction index {} out of bounds", instr_idx); - } - - Instruction { - function: self, - instr_idx, - } + // TODO: This should be a Option instead + pub fn instruction_from_idx(&self, index: InstructionIndex) -> Instruction { + if index.0 >= self.instruction_count() { + panic!("instruction index {} out of bounds", index); } + Instruction::new(self, index) } pub fn instruction_count(&self) -> usize { diff --git a/rust/src/llil/instruction.rs b/rust/src/llil/instruction.rs index ecc8eb76f..f080c5cbd 100644 --- a/rust/src/llil/instruction.rs +++ b/rust/src/llil/instruction.rs @@ -15,6 +15,7 @@ use binaryninjacore_sys::BNGetLowLevelILByIndex; use binaryninjacore_sys::BNGetLowLevelILIndexForInstruction; use binaryninjacore_sys::BNLowLevelILInstruction; +use std::fmt::{Display, Formatter}; use super::operation; use super::operation::Operation; @@ -22,6 +23,15 @@ use super::*; use crate::architecture::Architecture; +#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] +pub struct InstructionIndex(pub usize); + +impl Display for InstructionIndex { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + f.write_fmt(format_args!("{}", self.0)) + } +} + pub struct Instruction<'func, A, M, F> where A: 'func + Architecture, @@ -29,7 +39,20 @@ where F: FunctionForm, { pub(crate) function: &'func LowLevelILFunction, - pub(crate) instr_idx: usize, + pub index: InstructionIndex, +} + +impl<'func, A, M, F> Instruction<'func, A, M, F> +where + A: 'func + Architecture, + M: FunctionMutability, + F: FunctionForm, +{ + // TODO: Should we check the instruction count here with BNGetLowLevelILInstructionCount? + // TODO: If we _can_ then this should become an Option methinks + pub fn new(function: &'func LowLevelILFunction, index: InstructionIndex) -> Self { + Self { function, index } + } } fn common_info<'func, A, M, F>( @@ -97,7 +120,7 @@ where { pub fn address(&self) -> u64 { let expr_idx = - unsafe { BNGetLowLevelILIndexForInstruction(self.function.handle, self.instr_idx) }; + unsafe { BNGetLowLevelILIndexForInstruction(self.function.handle, self.index.0) }; let op = unsafe { BNGetLowLevelILByIndex(self.function.handle, expr_idx) }; op.address } @@ -106,7 +129,7 @@ where use binaryninjacore_sys::BNLowLevelILOperation::*; let expr_idx = - unsafe { BNGetLowLevelILIndexForInstruction(self.function.handle, self.instr_idx) }; + unsafe { BNGetLowLevelILIndexForInstruction(self.function.handle, self.index.0) }; let op = unsafe { BNGetLowLevelILByIndex(self.function.handle, expr_idx) }; match op.operation { @@ -126,7 +149,7 @@ where // Hopefully this is a bare value. If it isn't (expression // from wrong function form or similar) it won't really cause // any problems as it'll come back as undefined when queried. - let expr = Expression::new(self.function, expr_idx); + let expr = Expression::new(self.function, ExpressionIndex(expr_idx)); let info = unsafe { expr.info_from_op(op) }; diff --git a/rust/src/llil/lifting.rs b/rust/src/llil/lifting.rs index 56f10c773..2861a5a81 100644 --- a/rust/src/llil/lifting.rs +++ b/rust/src/llil/lifting.rs @@ -14,15 +14,15 @@ use std::marker::PhantomData; -use crate::architecture::Architecture; use crate::architecture::Register as ArchReg; +use crate::architecture::{Architecture, FlagWriteId, RegisterId}; use crate::architecture::{ Flag, FlagClass, FlagCondition, FlagGroup, FlagRole, FlagWrite, Intrinsic, }; use crate::function::Location; use crate::llil::{ - Expression, ExpressionResultType, LiftedExpr, LiftedNonSSA, Lifter, LowLevelILFunction, - Mutable, NonSSA, Register, ValueExpr, VoidExpr, + Expression, ExpressionIndex, ExpressionResultType, LiftedExpr, LiftedNonSSA, Lifter, + LowLevelILFunction, Mutable, NonSSA, Register, ValueExpr, VoidExpr, }; use binaryninjacore_sys::{BNAddLowLevelILLabelForAddress, BNLowLevelILOperation}; use binaryninjacore_sys::{BNLowLevelILLabel, BNRegisterOrConstant}; @@ -57,7 +57,7 @@ impl From> for BNRegisterOrConstant { match value { RegisterOrConstant::Register(_, r) => Self { constant: false, - reg: r.id(), + reg: r.id().0, value: 0, }, RegisterOrConstant::Constant(_, value) => Self { @@ -170,7 +170,7 @@ impl FlagWriteOp { RegisterOrConstant::Constant(size, operand.value) } else { let il_reg = if 0x8000_0000 & operand.reg == 0 { - Register::ArchReg(arch.register_from_id(operand.reg).unwrap()) + Register::ArchReg(arch.register_from_id(RegisterId(operand.reg)).unwrap()) } else { Register::Temp(operand.reg) }; @@ -387,7 +387,7 @@ where ) }; - Expression::new(il, expr_idx) + Expression::new(il, ExpressionIndex(expr_idx)) } pub fn get_default_flag_cond_llil<'func, A>( @@ -400,7 +400,7 @@ where A: 'func + Architecture, { use binaryninjacore_sys::BNGetDefaultArchitectureFlagConditionLowLevelIL; - let class_id = class.map(|c| c.id()).unwrap_or(0); + let class_id = class.map(|c| c.id().0).unwrap_or(0); unsafe { let expr_idx = BNGetDefaultArchitectureFlagConditionLowLevelIL( @@ -410,7 +410,7 @@ where il.handle, ); - Expression::new(il, expr_idx) + Expression::new(il, ExpressionIndex(expr_idx)) } } @@ -578,7 +578,7 @@ where pub fn with_source_operand(self, op: u32) -> Self { use binaryninjacore_sys::BNLowLevelILSetExprSourceOperand; - unsafe { BNLowLevelILSetExprSourceOperand(self.function.handle, self.expr_idx, op) } + unsafe { BNLowLevelILSetExprSourceOperand(self.function.handle, self.index.0, op) } self } @@ -597,7 +597,7 @@ where function: &'func LowLevelILFunction>, op: BNLowLevelILOperation, size: usize, - flags: u32, + flag_write: FlagWriteId, op1: u64, op2: u64, op3: u64, @@ -613,13 +613,13 @@ where pub fn from_expr(expr: Expression<'a, A, Mutable, NonSSA, R>) -> Self { use binaryninjacore_sys::BNGetLowLevelILByIndex; - let instr = unsafe { BNGetLowLevelILByIndex(expr.function.handle, expr.expr_idx) }; + let instr = unsafe { BNGetLowLevelILByIndex(expr.function.handle, expr.index.0) }; ExpressionBuilder { function: expr.function, op: instr.operation, size: instr.size, - flags: instr.flags, + flag_write: FlagWriteId(instr.flags), op1: instr.operands[0], op2: instr.operands[1], op3: instr.operands[2], @@ -630,7 +630,7 @@ where pub fn with_flag_write(mut self, flag_write: A::FlagWrite) -> Self { // TODO verify valid id - self.flags = flag_write.id(); + self.flag_write = flag_write.id(); self } @@ -642,7 +642,7 @@ where self.function.handle, self.op, self.size, - self.flags, + self.flag_write.0, self.op1, self.op2, self.op3, @@ -650,7 +650,7 @@ where ) }; - Expression::new(self.function, expr_idx) + Expression::new(self.function, ExpressionIndex(expr_idx)) } pub fn with_source_operand( @@ -720,7 +720,7 @@ macro_rules! no_arg_lifter { let expr_idx = unsafe { BNLowLevelILAddExpr(self.handle, $op, 0, 0, 0, 0, 0, 0) }; - Expression::new(self, expr_idx) + Expression::new(self, ExpressionIndex(expr_idx)) } }; } @@ -734,7 +734,7 @@ macro_rules! sized_no_arg_lifter { function: self, op: $op, size, - flags: 0, + flag_write: FlagWriteId(0), op1: 0, op2: 0, op3: 0, @@ -760,10 +760,10 @@ macro_rules! unsized_unary_op_lifter { let expr = E::lift(self, expr); let expr_idx = unsafe { - BNLowLevelILAddExpr(self.handle, $op, 0, 0, expr.expr_idx as u64, 0, 0, 0) + BNLowLevelILAddExpr(self.handle, $op, 0, 0, expr.index.0 as u64, 0, 0, 0) }; - Expression::new(self, expr_idx) + Expression::new(self, ExpressionIndex(expr_idx)) } }; } @@ -782,8 +782,8 @@ macro_rules! sized_unary_op_lifter { function: self, op: $op, size, - flags: 0, - op1: expr.expr_idx as u64, + flag_write: FlagWriteId(0), + op1: expr.index.0 as u64, op2: 0, op3: 0, op4: 0, @@ -807,8 +807,8 @@ macro_rules! size_changing_unary_op_lifter { function: self, op: $op, size, - flags: 0, - op1: expr.expr_idx as u64, + flag_write: FlagWriteId(0), + op1: expr.index.0 as u64, op2: 0, op3: 0, op4: 0, @@ -839,9 +839,9 @@ macro_rules! binary_op_lifter { function: self, op: $op, size, - flags: 0, - op1: left.expr_idx as u64, - op2: right.expr_idx as u64, + flag_write: FlagWriteId(0), + op1: left.index.0 as u64, + op2: right.index.0 as u64, op3: 0, op4: 0, _ty: PhantomData, @@ -874,10 +874,10 @@ macro_rules! binary_op_carry_lifter { function: self, op: $op, size, - flags: 0, - op1: left.expr_idx as u64, - op2: right.expr_idx as u64, - op3: carry.expr_idx as u64, + flag_write: FlagWriteId(0), + op1: left.index.0 as u64, + op2: right.index.0 as u64, + op3: carry.index.0 as u64, op4: 0, _ty: PhantomData, } @@ -904,19 +904,20 @@ where unsafe { use binaryninjacore_sys::BNLowLevelILAddInstruction; - BNLowLevelILAddInstruction(self.handle, expr.expr_idx); + BNLowLevelILAddInstruction(self.handle, expr.index.0); } } pub unsafe fn replace_expression<'a, E: Liftable<'a, A>>( &'a self, - replaced_expr_index: usize, + replaced_expr_index: ExpressionIndex, replacement: E, ) { use binaryninjacore_sys::BNGetLowLevelILExprCount; use binaryninjacore_sys::BNReplaceLowLevelILExpr; - if replaced_expr_index >= BNGetLowLevelILExprCount(self.handle) { + // Return false instead? + if replaced_expr_index.0 >= BNGetLowLevelILExprCount(self.handle) { panic!( "bad expr idx used: {} exceeds function bounds", replaced_expr_index @@ -924,7 +925,7 @@ where } let expr = self.expression(replacement); - BNReplaceLowLevelILExpr(self.handle, replaced_expr_index, expr.expr_idx); + BNReplaceLowLevelILExpr(self.handle, replaced_expr_index.0, expr.index.0); } pub fn const_int( @@ -938,7 +939,7 @@ where let expr_idx = unsafe { BNLowLevelILAddExpr(self.handle, LLIL_CONST, size, 0, val, 0, 0, 0) }; - Expression::new(self, expr_idx) + Expression::new(self, ExpressionIndex(expr_idx)) } pub fn const_ptr_sized( @@ -952,7 +953,7 @@ where let expr_idx = unsafe { BNLowLevelILAddExpr(self.handle, LLIL_CONST_PTR, size, 0, val, 0, 0, 0) }; - Expression::new(self, expr_idx) + Expression::new(self, ExpressionIndex(expr_idx)) } pub fn const_ptr(&self, val: u64) -> Expression, ValueExpr> { @@ -965,7 +966,7 @@ where let expr_idx = unsafe { BNLowLevelILAddExpr(self.handle, LLIL_TRAP, 0, 0, val, 0, 0, 0) }; - Expression::new(self, expr_idx) + Expression::new(self, ExpressionIndex(expr_idx)) } no_arg_lifter!(unimplemented, LLIL_UNIMPL, ValueExpr); @@ -999,7 +1000,7 @@ where let expr_idx = unsafe { BNLowLevelILIf( self.handle, - cond.expr_idx as u64, + cond.index.0 as u64, &mut raw_true_label, &mut raw_false_label, ) @@ -1009,7 +1010,7 @@ where *true_label = Label::from(raw_true_label); *false_label = Label::from(raw_false_label); - Expression::new(self, expr_idx) + Expression::new(self, ExpressionIndex(expr_idx)) } // TODO: Wtf are these lifetimes?? @@ -1025,7 +1026,7 @@ where // Update the labels after they have been resolved. *label = Label::from(raw_label); - Expression::new(self, expr_idx) + Expression::new(self, ExpressionIndex(expr_idx)) } pub fn reg>>( @@ -1037,15 +1038,12 @@ where use binaryninjacore_sys::BNLowLevelILOperation::LLIL_REG; // TODO verify valid id - let reg = match reg.into() { - Register::ArchReg(r) => r.id(), - Register::Temp(r) => 0x8000_0000 | r, - }; + let reg = reg.into().id(); let expr_idx = - unsafe { BNLowLevelILAddExpr(self.handle, LLIL_REG, size, 0, reg as u64, 0, 0, 0) }; + unsafe { BNLowLevelILAddExpr(self.handle, LLIL_REG, size, 0, reg.0 as u64, 0, 0, 0) }; - Expression::new(self, expr_idx) + Expression::new(self, ExpressionIndex(expr_idx)) } pub fn reg_split>, L: Into>>( @@ -1058,16 +1056,8 @@ where use binaryninjacore_sys::BNLowLevelILOperation::LLIL_REG_SPLIT; // TODO verify valid id - let hi_reg = match hi_reg.into() { - Register::ArchReg(r) => r.id(), - Register::Temp(r) => 0x8000_0000 | r, - }; - - // TODO verify valid id - let lo_reg = match lo_reg.into() { - Register::ArchReg(r) => r.id(), - Register::Temp(r) => 0x8000_0000 | r, - }; + let hi_reg = hi_reg.into().id(); + let lo_reg = lo_reg.into().id(); let expr_idx = unsafe { BNLowLevelILAddExpr( @@ -1075,14 +1065,14 @@ where LLIL_REG_SPLIT, size, 0, - hi_reg as u64, - lo_reg as u64, + hi_reg.0 as u64, + lo_reg.0 as u64, 0, 0, ) }; - Expression::new(self, expr_idx) + Expression::new(self, ExpressionIndex(expr_idx)) } pub fn set_reg<'a, R, E>( @@ -1098,10 +1088,7 @@ where use binaryninjacore_sys::BNLowLevelILOperation::LLIL_SET_REG; // TODO verify valid id - let dest_reg = match dest_reg.into() { - Register::ArchReg(r) => r.id(), - Register::Temp(r) => 0x8000_0000 | r, - }; + let dest_reg = dest_reg.into().id(); let expr = E::lift_with_size(self, expr, size); @@ -1109,9 +1096,10 @@ where function: self, op: LLIL_SET_REG, size, - flags: 0, - op1: dest_reg as u64, - op2: expr.expr_idx as u64, + // TODO: Make these optional? + flag_write: FlagWriteId(0), + op1: dest_reg.0 as u64, + op2: expr.index.0 as u64, op3: 0, op4: 0, _ty: PhantomData, @@ -1133,16 +1121,8 @@ where use binaryninjacore_sys::BNLowLevelILOperation::LLIL_SET_REG_SPLIT; // TODO verify valid id - let hi_reg = match hi_reg.into() { - Register::ArchReg(r) => r.id(), - Register::Temp(r) => 0x8000_0000 | r, - }; - - // TODO verify valid id - let lo_reg = match lo_reg.into() { - Register::ArchReg(r) => r.id(), - Register::Temp(r) => 0x8000_0000 | r, - }; + let hi_reg = hi_reg.into().id(); + let lo_reg = lo_reg.into().id(); let expr = E::lift_with_size(self, expr, size); @@ -1150,10 +1130,11 @@ where function: self, op: LLIL_SET_REG_SPLIT, size, - flags: 0, - op1: hi_reg as u64, - op2: lo_reg as u64, - op3: expr.expr_idx as u64, + // TODO: Make these optional? + flag_write: FlagWriteId(0), + op1: hi_reg.0 as u64, + op2: lo_reg.0 as u64, + op3: expr.index.0 as u64, op4: 0, _ty: PhantomData, } @@ -1164,10 +1145,11 @@ where use binaryninjacore_sys::BNLowLevelILOperation::LLIL_FLAG; // TODO verify valid id - let expr_idx = - unsafe { BNLowLevelILAddExpr(self.handle, LLIL_FLAG, 0, 0, flag.id() as u64, 0, 0, 0) }; + let expr_idx = unsafe { + BNLowLevelILAddExpr(self.handle, LLIL_FLAG, 0, 0, flag.id().0 as u64, 0, 0, 0) + }; - Expression::new(self, expr_idx) + Expression::new(self, ExpressionIndex(expr_idx)) } pub fn flag_cond( @@ -1181,7 +1163,7 @@ where let expr_idx = unsafe { BNLowLevelILAddExpr(self.handle, LLIL_FLAG_COND, 0, 0, cond as u64, 0, 0, 0) }; - Expression::new(self, expr_idx) + Expression::new(self, ExpressionIndex(expr_idx)) } pub fn flag_group( @@ -1198,14 +1180,14 @@ where LLIL_FLAG_GROUP, 0, 0, - group.id() as u64, + group.id().0 as u64, 0, 0, 0, ) }; - Expression::new(self, expr_idx) + Expression::new(self, ExpressionIndex(expr_idx)) } pub fn set_flag<'a, E>( @@ -1226,9 +1208,9 @@ where function: self, op: LLIL_SET_FLAG, size: 0, - flags: 0, - op1: dest_flag.id() as u64, - op2: expr.expr_idx as u64, + flag_write: FlagWriteId(0), + op1: dest_flag.id().0 as u64, + op2: expr.index.0 as u64, op3: 0, op4: 0, _ty: PhantomData, @@ -1252,8 +1234,8 @@ where function: self, op: LLIL_LOAD, size, - flags: 0, - op1: expr.expr_idx as u64, + flag_write: FlagWriteId(0), + op1: expr.index.0 as u64, op2: 0, op3: 0, op4: 0, @@ -1280,9 +1262,9 @@ where function: self, op: LLIL_STORE, size, - flags: 0, - op1: dest_mem.expr_idx as u64, - op2: value.expr_idx as u64, + flag_write: FlagWriteId(0), + op1: dest_mem.index.0 as u64, + op2: value.index.0 as u64, op3: 0, op4: 0, _ty: PhantomData, @@ -1307,14 +1289,7 @@ where let mut outputs: Vec = outputs .into_iter() - .map(|output| { - // TODO verify valid id - let output = match output.into() { - Register::ArchReg(r) => r.id(), - Register::Temp(r) => 0x8000_0000 | r, - }; - output as u64 - }) + .map(|output| output.into().id().0 as u64) .collect(); let output_expr_idx = unsafe { BNLowLevelILAddOperandList(self.handle, outputs.as_mut_ptr(), outputs.len()) }; @@ -1325,7 +1300,7 @@ where .into_iter() .map(|input| { let input = P::lift(self, input); - input.expr_idx as u64 + input.index.0 as u64 }) .collect(); let input_list_expr_idx = @@ -1347,10 +1322,10 @@ where function: self, op: LLIL_INTRINSIC, size: 0, - flags: 0, + flag_write: FlagWriteId(0), op1: outputs.len() as u64, op2: output_expr_idx as u64, - op3: intrinsic.id() as u64, + op3: intrinsic.id().0 as u64, op4: input_expr_idx as u64, _ty: PhantomData, } @@ -1500,8 +1475,10 @@ where #[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] pub struct Label { pub resolved: bool, - // TODO: Rename this to something more sensible? - pub expr_ref: usize, + // TODO: This expr_ref is not actually a valid one sometimes... + // TODO: We should make these non public and only accessible if resolved is true. + pub expr_ref: ExpressionIndex, + // TODO: If this is 7 this label is not valid. pub operand: usize, } @@ -1519,7 +1496,7 @@ impl From for Label { fn from(value: BNLowLevelILLabel) -> Self { Self { resolved: value.resolved, - expr_ref: value.ref_, + expr_ref: ExpressionIndex(value.ref_), operand: value.operand, } } @@ -1529,7 +1506,7 @@ impl From

{ type Item = P::Wrapped<'a>; type IntoIter = ArrayIter<'a, P>; diff --git a/rust/src/string.rs b/rust/src/string.rs index 52458c03d..c660aa301 100644 --- a/rust/src/string.rs +++ b/rust/src/string.rs @@ -277,7 +277,7 @@ unsafe impl BnStrCompatible for &Path { type Result = Vec; fn into_bytes_with_nul(self) -> Self::Result { - self.to_string_lossy().into_bytes_with_nul() + self.as_os_str().as_encoded_bytes().to_vec() } } diff --git a/rust/src/typecontainer.rs b/rust/src/typecontainer.rs new file mode 100644 index 000000000..bd71b177f --- /dev/null +++ b/rust/src/typecontainer.rs @@ -0,0 +1,448 @@ +// TODO: Add these! +// The `TypeContainer` class should not generally be instantiated directly. Instances +// can be retrieved from the following properties and methods in the API: +// * [BinaryView::type_container] +// * [BinaryView::auto_type_container] +// * [BinaryView::user_type_container] +// * [Platform::type_container] +// * [TypeLibrary::type_container] +// * [DebugInfo::get_type_container] + +use crate::platform::Platform; +use crate::rc::{Array, Ref}; +use crate::string::{raw_to_string, BnStrCompatible, BnString}; +use crate::typeparser::{TypeParserError, TypeParserResult}; +use crate::types::{QualifiedName, QualifiedNameAndType, Type}; +use binaryninjacore_sys::*; +use std::collections::HashMap; +use std::ffi::{c_char, c_void}; +use std::ptr::NonNull; + +pub type TypeContainerType = BNTypeContainerType; + +/// A `TypeContainer` is a generic interface to access various Binary Ninja models +/// that contain types. Types are stored with both a unique id and a unique name. +#[repr(transparent)] +pub struct TypeContainer { + pub handle: NonNull, +} + +impl TypeContainer { + pub(crate) unsafe fn from_raw(handle: NonNull) -> Self { + // NOTE: There does not seem to be any shared ref counting for type containers, it seems if the + // NOTE: binary view is freed the type container will be freed and cause this to become invalid + // NOTE: but this is how the C++ and Python bindings operate so i guess its fine? + // TODO: I really dont get how some of the usage of the TypeContainer doesnt free the underlying container. + // TODO: So for now we always duplicate the type container + // let cloned_ptr = NonNull::new(BNDuplicateTypeContainer(handle.as_ptr())); + // Self { + // handle: cloned_ptr.unwrap(), + // } + Self { handle } + } + + /// Get an id string for the Type Container. This will be unique within a given + /// analysis session, but may not be globally unique. + pub fn id(&self) -> BnString { + let result = unsafe { BNTypeContainerGetId(self.handle.as_ptr()) }; + assert!(!result.is_null()); + unsafe { BnString::from_raw(result) } + } + + /// Get a user-friendly name for the Type Container. + pub fn name(&self) -> BnString { + let result = unsafe { BNTypeContainerGetName(self.handle.as_ptr()) }; + assert!(!result.is_null()); + unsafe { BnString::from_raw(result) } + } + + /// Get the type of underlying model the Type Container is accessing. + pub fn container_type(&self) -> TypeContainerType { + unsafe { BNTypeContainerGetType(self.handle.as_ptr()) } + } + + /// If the Type Container supports mutable operations (add, rename, delete) + pub fn is_mutable(&self) -> bool { + unsafe { BNTypeContainerIsMutable(self.handle.as_ptr()) } + } + + /// Get the Platform object associated with this Type Container. All Type Containers + /// have exactly one associated Platform (as opposed to, e.g. Type Libraries). + pub fn platform(&self) -> Ref { + let result = unsafe { BNTypeContainerGetPlatform(self.handle.as_ptr()) }; + assert!(!result.is_null()); + unsafe { Platform::ref_from_raw(result) } + } + + /// Add or update types to a Type Container. If the Type Container already contains + /// a type with the same name as a type being added, the existing type will be + /// replaced with the definition given to this function, and references will be + /// updated in the source model. + pub fn add_types(&self, types: I) -> bool + where + I: IntoIterator, + T: Into, + { + // TODO: I dislike how this iter unzip looks like... but its how to avoid allocating again... + let (raw_names, mut raw_types): (Vec, Vec<_>) = types + .into_iter() + .map(|t| { + let t = t.into(); + (BNQualifiedName::from(t.name), t.ty.handle) + }) + .unzip(); + + let mut result_names = std::ptr::null_mut(); + let mut result_ids = std::ptr::null_mut(); + let mut result_count = 0; + unsafe { + BNTypeContainerAddTypes( + self.handle.as_ptr(), + raw_names.as_ptr(), + raw_types.as_mut_ptr(), + raw_types.len(), + Some(cb_progress_nop), + std::ptr::null_mut(), + &mut result_names, + &mut result_ids, + &mut result_count, + ) + } + } + + pub fn add_types_with_progress(&self, types: I, mut progress: F) -> bool + where + I: IntoIterator, + T: Into, + F: FnMut(usize, usize) -> bool, + { + // TODO: I dislike how this iter unzip looks like... but its how to avoid allocating again... + let (raw_names, mut raw_types): (Vec, Vec<_>) = types + .into_iter() + .map(|t| { + let t = t.into(); + (BNQualifiedName::from(t.name), t.ty.handle) + }) + .unzip(); + + let mut result_names = std::ptr::null_mut(); + let mut result_ids = std::ptr::null_mut(); + let mut result_count = 0; + unsafe { + BNTypeContainerAddTypes( + self.handle.as_ptr(), + raw_names.as_ptr(), + raw_types.as_mut_ptr(), + raw_types.len(), + Some(cb_progress::), + &mut progress as *mut F as *mut c_void, + &mut result_names, + &mut result_ids, + &mut result_count, + ) + } + } + + /// Rename a type in the Type Container. All references to this type will be updated + /// (by id) to use the new name. + /// + /// Returns true if the type was renamed. + pub fn rename_type, S: BnStrCompatible>( + &self, + name: T, + type_id: S, + ) -> bool { + let type_id = type_id.into_bytes_with_nul(); + let raw_name = BNQualifiedName::from(name.into()); + unsafe { + BNTypeContainerRenameType( + self.handle.as_ptr(), + type_id.as_ref().as_ptr() as *const c_char, + &raw_name, + ) + } + } + + /// Delete a type in the Type Container. Behavior of references to this type is + /// not specified and you may end up with broken references if any still exist. + /// + /// Returns true if the type was deleted. + pub fn delete_type(&self, type_id: S) -> bool { + let type_id = type_id.into_bytes_with_nul(); + unsafe { + BNTypeContainerDeleteType( + self.handle.as_ptr(), + type_id.as_ref().as_ptr() as *const c_char, + ) + } + } + + /// Get the unique id of the type in the Type Container with the given name. + /// + /// If no type with that name exists, returns None. + pub fn type_id>(&self, name: T) -> Option { + let mut result = std::ptr::null_mut(); + let raw_name = BNQualifiedName::from(name.into()); + let success = + unsafe { BNTypeContainerGetTypeId(self.handle.as_ptr(), &raw_name, &mut result) }; + success.then(|| unsafe { BnString::from_raw(result) }) + } + + /// Get the unique name of the type in the Type Container with the given id. + /// + /// If no type with that id exists, returns None. + pub fn type_name(&self, type_id: S) -> Option { + let type_id = type_id.into_bytes_with_nul(); + let mut result = BNQualifiedName::default(); + let success = unsafe { + BNTypeContainerGetTypeName( + self.handle.as_ptr(), + type_id.as_ref().as_ptr() as *const c_char, + &mut result, + ) + }; + success.then(|| QualifiedName::from(result)) + } + + /// Get the definition of the type in the Type Container with the given id. + /// + /// If no type with that id exists, returns None. + pub fn type_by_id(&self, type_id: S) -> Option> { + let type_id = type_id.into_bytes_with_nul(); + let mut result = std::ptr::null_mut(); + let success = unsafe { + BNTypeContainerGetTypeById( + self.handle.as_ptr(), + type_id.as_ref().as_ptr() as *const c_char, + &mut result, + ) + }; + success.then(|| unsafe { Type::ref_from_raw(result) }) + } + + /// Get the definition of the type in the Type Container with the given name. + /// + /// If no type with that name exists, returns None. + pub fn type_by_name>(&self, name: T) -> Option> { + let mut result = std::ptr::null_mut(); + let raw_name = BNQualifiedName::from(name.into()); + let success = + unsafe { BNTypeContainerGetTypeByName(self.handle.as_ptr(), &raw_name, &mut result) }; + success.then(|| unsafe { Type::ref_from_raw(result) }) + } + + /// Get a mapping of all types in a Type Container. + // TODO: This needs to be redone... somehow all of these need to be merged into one array... + pub fn types(&self) -> Option)>> { + let mut type_ids = std::ptr::null_mut(); + let mut type_names = std::ptr::null_mut(); + let mut type_types = std::ptr::null_mut(); + let mut type_count = 0; + let success = unsafe { + BNTypeContainerGetTypes( + self.handle.as_ptr(), + &mut type_ids, + &mut type_names, + &mut type_types, + &mut type_count, + ) + }; + success.then(|| unsafe { + let raw_ids = std::slice::from_raw_parts(type_ids, type_count); + let raw_names = std::slice::from_raw_parts(type_names, type_count); + let raw_types = std::slice::from_raw_parts(type_types, type_count); + let mut map = HashMap::new(); + for (idx, raw_id) in raw_ids.iter().enumerate() { + let id = raw_to_string(*raw_id).expect("Valid string"); + // Take the qualified name as a ref as the name should not be freed. + let name = QualifiedName::from(&raw_names[idx]); + // Take the type as an owned ref, as the returned type was not already incremented. + let ty = Type::from_raw(raw_types[idx]).to_owned(); + map.insert(id, (name, ty)); + } + BNFreeStringList(type_ids, type_count); + BNFreeTypeNameList(type_names, type_count); + BNFreeTypeList(type_types, type_count); + map + }) + } + + /// Get all type ids in a Type Container. + pub fn type_ids(&self) -> Option> { + let mut type_ids = std::ptr::null_mut(); + let mut type_count = 0; + let success = unsafe { + BNTypeContainerGetTypeIds(self.handle.as_ptr(), &mut type_ids, &mut type_count) + }; + success.then(|| unsafe { Array::new(type_ids, type_count, ()) }) + } + + /// Get all type names in a Type Container. + pub fn type_names(&self) -> Option> { + let mut type_ids = std::ptr::null_mut(); + let mut type_count = 0; + let success = unsafe { + BNTypeContainerGetTypeNames(self.handle.as_ptr(), &mut type_ids, &mut type_count) + }; + success.then(|| unsafe { Array::new(type_ids, type_count, ()) }) + } + + /// Get a mapping of all type ids and type names in a Type Container. + pub fn type_names_and_ids(&self) -> Option<(Array, Array)> { + let mut type_ids = std::ptr::null_mut(); + let mut type_names = std::ptr::null_mut(); + let mut type_count = 0; + let success = unsafe { + BNTypeContainerGetTypeNamesAndIds( + self.handle.as_ptr(), + &mut type_ids, + &mut type_names, + &mut type_count, + ) + }; + success.then(|| unsafe { + let ids = Array::new(type_ids, type_count, ()); + let names = Array::new(type_names, type_count, ()); + (ids, names) + }) + } + + /// Parse a single type and name from a string containing their definition, with + /// knowledge of the types in the Type Container. + /// + /// * `source` - Source code to parse + /// * `import_dependencies` - If Type Library / Type Archive types should be imported during parsing + pub fn parse_type_string( + &self, + source: S, + import_dependencies: bool, + ) -> Result> { + let source = source.into_bytes_with_nul(); + let mut result = BNQualifiedNameAndType::default(); + let mut errors = std::ptr::null_mut(); + let mut error_count = 0; + let success = unsafe { + BNTypeContainerParseTypeString( + self.handle.as_ptr(), + source.as_ref().as_ptr() as *const c_char, + import_dependencies, + &mut result, + &mut errors, + &mut error_count, + ) + }; + if success { + Ok(QualifiedNameAndType::from(result)) + } else { + assert!(!errors.is_null()); + Err(unsafe { Array::new(errors, error_count, ()) }) + } + } + + /// Parse an entire block of source into types, variables, and functions, with + /// knowledge of the types in the Type Container. + + /// * `source` - Source code to parse + /// * `file_name` - Name of the file containing the source (optional: exists on disk) + /// * `options` - String arguments to pass as options, e.g. command line arguments + /// * `include_dirs` - List of directories to include in the header search path + /// * `auto_type_source` - Source of types if used for automatically generated types + /// * `import_dependencies` - If Type Library / Type Archive types should be imported during parsing + pub fn parse_types_from_source( + &self, + source: S, + filename: F, + options: O, + include_directories: D, + auto_type_source: A, + import_dependencies: bool, + ) -> Result> + where + S: BnStrCompatible, + F: BnStrCompatible, + O: IntoIterator, + O::Item: BnStrCompatible, + D: IntoIterator, + D::Item: BnStrCompatible, + A: BnStrCompatible, + { + let source = source.into_bytes_with_nul(); + let filename = filename.into_bytes_with_nul(); + let options: Vec<_> = options + .into_iter() + .map(|o| o.into_bytes_with_nul()) + .collect(); + let options_raw: Vec<*const c_char> = options + .iter() + .map(|o| o.as_ref().as_ptr() as *const c_char) + .collect(); + let include_directories: Vec<_> = include_directories + .into_iter() + .map(|d| d.into_bytes_with_nul()) + .collect(); + let include_directories_raw: Vec<*const c_char> = include_directories + .iter() + .map(|d| d.as_ref().as_ptr() as *const c_char) + .collect(); + let auto_type_source = auto_type_source.into_bytes_with_nul(); + let mut raw_result = BNTypeParserResult::default(); + let mut errors = std::ptr::null_mut(); + let mut error_count = 0; + let success = unsafe { + BNTypeContainerParseTypesFromSource( + self.handle.as_ptr(), + source.as_ref().as_ptr() as *const c_char, + filename.as_ref().as_ptr() as *const c_char, + options_raw.as_ptr(), + options_raw.len(), + include_directories_raw.as_ptr(), + include_directories_raw.len(), + auto_type_source.as_ref().as_ptr() as *const c_char, + import_dependencies, + &mut raw_result, + &mut errors, + &mut error_count, + ) + }; + if success { + Ok(raw_result.into()) + } else { + assert!(!errors.is_null()); + Err(unsafe { Array::new(errors, error_count, ()) }) + } + } +} + +impl Drop for TypeContainer { + fn drop(&mut self) { + unsafe { BNFreeTypeContainer(self.handle.as_ptr()) } + } +} + +impl Clone for TypeContainer { + fn clone(&self) -> Self { + unsafe { + let cloned_ptr = NonNull::new(BNDuplicateTypeContainer(self.handle.as_ptr())); + Self { + handle: cloned_ptr.unwrap(), + } + } + } +} + +unsafe extern "C" fn cb_progress_nop( + _ctxt: *mut ::std::os::raw::c_void, + _progress: usize, + _total: usize, +) -> bool { + true +} + +unsafe extern "C" fn cb_progress bool>( + ctxt: *mut ::std::os::raw::c_void, + progress: usize, + total: usize, +) -> bool { + let ctxt = &mut *(ctxt as *mut F); + ctxt(progress, total) +} diff --git a/rust/src/typeparser.rs b/rust/src/typeparser.rs index d95c94d58..111ad9df8 100644 --- a/rust/src/typeparser.rs +++ b/rust/src/typeparser.rs @@ -1,39 +1,53 @@ -use core::{ffi, mem, ptr}; - use binaryninjacore_sys::*; +use std::ffi::{c_char, c_void}; +use std::fmt::Debug; +use std::ptr::NonNull; -use crate::binaryview::BinaryView; -use crate::disassembly::InstructionTextToken; use crate::platform::Platform; -use crate::rc::{Array, ArrayGuard, CoreArrayProvider, CoreArrayProviderInner, Ref}; -use crate::string::{BnStrCompatible, BnString}; -use crate::types::{NamedTypeReference, QualifiedName, QualifiedNameAndType, Type}; +use crate::rc::{Array, CoreArrayProvider, CoreArrayProviderInner, Ref}; +use crate::string::{raw_to_string, BnStrCompatible, BnString}; +use crate::typecontainer::TypeContainer; +use crate::types::{QualifiedName, QualifiedNameAndType, Type}; pub type TypeParserErrorSeverity = BNTypeParserErrorSeverity; pub type TypeParserOption = BNTypeParserOption; -pub type TokenEscapingType = BNTokenEscapingType; -pub type TypeDefinitionLineType = BNTypeDefinitionLineType; -pub type TypeContainerType = BNTypeContainerType; + +/// Register a custom parser with the API +pub fn register_type_parser( + name: S, + parser: T, +) -> (&'static mut T, CoreTypeParser) { + let parser = Box::leak(Box::new(parser)); + let mut callback = BNTypeParserCallbacks { + context: parser as *mut _ as *mut c_void, + getOptionText: Some(cb_get_option_text::), + preprocessSource: Some(cb_preprocess_source::), + parseTypesFromSource: Some(cb_parse_types_from_source::), + parseTypeString: Some(cb_parse_type_string::), + freeString: Some(cb_free_string), + freeResult: Some(cb_free_result), + freeErrorList: Some(cb_free_error_list), + }; + let result = unsafe { + BNRegisterTypeParser( + name.into_bytes_with_nul().as_ref().as_ptr() as *const _, + &mut callback, + ) + }; + let core = unsafe { CoreTypeParser::from_raw(NonNull::new(result).unwrap()) }; + (parser, core) +} #[repr(transparent)] pub struct CoreTypeParser { - handle: ptr::NonNull, + handle: NonNull, } impl CoreTypeParser { - #[allow(clippy::mut_from_ref)] - fn as_raw(&self) -> &mut BNTypeParser { - unsafe { &mut *self.handle.as_ptr() } - } - - pub(crate) unsafe fn from_raw(handle: ptr::NonNull) -> Self { + pub(crate) unsafe fn from_raw(handle: NonNull) -> Self { Self { handle } } - pub(crate) unsafe fn ref_from_raw(handle: &*mut BNTypeParser) -> &Self { - mem::transmute(handle) - } - pub fn parsers() -> Array { let mut count = 0; let result = unsafe { BNGetTypeParserList(&mut count) }; @@ -42,53 +56,59 @@ impl CoreTypeParser { pub fn parser_by_name(name: S) -> Option { let name_raw = name.into_bytes_with_nul(); - let result = - unsafe { BNGetTypeParserByName(name_raw.as_ref().as_ptr() as *const ffi::c_char) }; - ptr::NonNull::new(result).map(|x| unsafe { Self::from_raw(x) }) + let result = unsafe { BNGetTypeParserByName(name_raw.as_ref().as_ptr() as *const c_char) }; + NonNull::new(result).map(|x| unsafe { Self::from_raw(x) }) } pub fn name(&self) -> BnString { - let result = unsafe { BNGetTypeParserName(self.as_raw()) }; + let result = unsafe { BNGetTypeParserName(self.handle.as_ptr()) }; assert!(!result.is_null()); unsafe { BnString::from_raw(result) } } +} - pub fn get_option_text(&self, option: TypeParserOption, value: &str) -> Option { - let mut output = ptr::null_mut(); +impl TypeParser for CoreTypeParser { + fn get_option_text(&self, option: TypeParserOption, value: &str) -> Option { + let mut output = std::ptr::null_mut(); let value_cstr = BnString::new(value); let result = unsafe { - BNGetTypeParserOptionText(self.as_raw(), option, value_cstr.as_ptr(), &mut output) + BNGetTypeParserOptionText( + self.handle.as_ptr(), + option, + value_cstr.as_ptr(), + &mut output, + ) }; result.then(|| { assert!(!output.is_null()); - unsafe { BnString::from_raw(output) } + value_cstr.to_string() }) } - pub fn preprocess_source( + fn preprocess_source( &self, source: &str, file_name: &str, platform: &Platform, existing_types: &TypeContainer, - options: &[BnString], - include_dirs: &[BnString], - ) -> Result> { + options: &[String], + include_dirs: &[String], + ) -> Result> { let source_cstr = BnString::new(source); let file_name_cstr = BnString::new(file_name); - let mut result = ptr::null_mut(); - let mut errors = ptr::null_mut(); + let mut result = std::ptr::null_mut(); + let mut errors = std::ptr::null_mut(); let mut error_count = 0; let success = unsafe { BNTypeParserPreprocessSource( - self.as_raw(), + self.handle.as_ptr(), source_cstr.as_ptr(), file_name_cstr.as_ptr(), platform.handle, - existing_types.as_raw(), - options.as_ptr() as *const *const ffi::c_char, + existing_types.handle.as_ptr(), + options.as_ptr() as *const *const c_char, options.len(), - include_dirs.as_ptr() as *const *const ffi::c_char, + include_dirs.as_ptr() as *const *const c_char, include_dirs.len(), &mut result, &mut errors, @@ -97,87 +117,93 @@ impl CoreTypeParser { }; if success { assert!(!result.is_null()); - Ok(unsafe { BnString::from_raw(result) }) + let bn_result = unsafe { BnString::from_raw(result) }; + Ok(bn_result.to_string()) } else { - Err(unsafe { Array::new(errors, error_count, ()) }) + let errors: Array = unsafe { Array::new(errors, error_count, ()) }; + Err(errors.to_vec()) } } - pub fn parse_types_from_source( + fn parse_types_from_source( &self, source: &str, file_name: &str, platform: &Platform, existing_types: &TypeContainer, - options: &[BnString], - include_dirs: &[BnString], + options: &[String], + include_dirs: &[String], auto_type_source: &str, - ) -> Result> { + ) -> Result> { let source_cstr = BnString::new(source); let file_name_cstr = BnString::new(file_name); let auto_type_source = BnString::new(auto_type_source); - let mut result = BNTypeParserResult::default(); - let mut errors = ptr::null_mut(); + let mut raw_result = BNTypeParserResult::default(); + let mut errors = std::ptr::null_mut(); let mut error_count = 0; let success = unsafe { BNTypeParserParseTypesFromSource( - self.as_raw(), + self.handle.as_ptr(), source_cstr.as_ptr(), file_name_cstr.as_ptr(), platform.handle, - existing_types.as_raw(), - options.as_ptr() as *const *const ffi::c_char, + existing_types.handle.as_ptr(), + options.as_ptr() as *const *const c_char, options.len(), - include_dirs.as_ptr() as *const *const ffi::c_char, + include_dirs.as_ptr() as *const *const c_char, include_dirs.len(), auto_type_source.as_ptr(), - &mut result, + &mut raw_result, &mut errors, &mut error_count, ) }; if success { - Ok(unsafe { TypeParserResult::from_raw(result) }) + Ok(raw_result.into()) } else { - Err(unsafe { Array::new(errors, error_count, ()) }) + let errors: Array = unsafe { Array::new(errors, error_count, ()) }; + Err(errors.to_vec()) } } - pub fn parse_type_string( + fn parse_type_string( &self, source: &str, platform: &Platform, existing_types: &TypeContainer, - ) -> Result> { + ) -> Result> { let source_cstr = BnString::new(source); let mut output = BNQualifiedNameAndType::default(); - let mut errors = ptr::null_mut(); + let mut errors = std::ptr::null_mut(); let mut error_count = 0; let result = unsafe { BNTypeParserParseTypeString( - self.as_raw(), + self.handle.as_ptr(), source_cstr.as_ptr(), platform.handle, - existing_types.as_raw(), + existing_types.handle.as_ptr(), &mut output, &mut errors, &mut error_count, ) }; if result { - Ok(QualifiedNameAndType(output)) + Ok(QualifiedNameAndType::from(output)) } else { - Err(unsafe { Array::new(errors, error_count, ()) }) + let errors: Array = unsafe { Array::new(errors, error_count, ()) }; + Err(errors.to_vec()) } } } impl Default for CoreTypeParser { fn default() -> Self { - unsafe { Self::from_raw(ptr::NonNull::new(BNGetDefaultTypeParser()).unwrap()) } + // TODO: This should return a ref + unsafe { Self::from_raw(NonNull::new(BNGetDefaultTypeParser()).unwrap()) } } } +// TODO: Impl this on platform. pub trait TypeParser { /// Get the string representation of an option for passing to parse_type_*. /// Returns a string representing the option if the parser supports it, @@ -185,7 +211,7 @@ pub trait TypeParser { /// /// * `option` - Option type /// * `value` - Option value - fn get_option_text(&self, option: TypeParserOption, value: &str) -> Option; + fn get_option_text(&self, option: TypeParserOption, value: &str) -> Option; /// Preprocess a block of source, returning the source that would be parsed /// @@ -201,9 +227,9 @@ pub trait TypeParser { file_name: &str, platform: &Platform, existing_types: &TypeContainer, - options: &[BnString], - include_dirs: &[BnString], - ) -> Result>; + options: &[String], + include_dirs: &[String], + ) -> Result>; /// Parse an entire block of source into types, variables, and functions /// @@ -220,10 +246,10 @@ pub trait TypeParser { file_name: &str, platform: &Platform, existing_types: &TypeContainer, - options: &[BnString], - include_dirs: &[BnString], + options: &[String], + include_dirs: &[String], auto_type_source: &str, - ) -> Result>; + ) -> Result>; /// Parse a single type and name from a string containing their definition. /// @@ -238,36 +264,10 @@ pub trait TypeParser { ) -> Result>; } -/// Register a custom parser with the API -pub fn register_type_parser( - name: S, - parser: T, -) -> (&'static mut T, CoreTypeParser) { - let parser = Box::leak(Box::new(parser)); - let mut callback = BNTypeParserCallbacks { - context: parser as *mut _ as *mut ffi::c_void, - getOptionText: Some(cb_get_option_text::), - preprocessSource: Some(cb_preprocess_source::), - parseTypesFromSource: Some(cb_parse_types_from_source::), - parseTypeString: Some(cb_parse_type_string::), - freeString: Some(cb_free_string), - freeResult: Some(cb_free_result), - freeErrorList: Some(cb_free_error_list), - }; - let result = unsafe { - BNRegisterTypeParser( - name.into_bytes_with_nul().as_ref().as_ptr() as *const ffi::c_char, - &mut callback, - ) - }; - let core = unsafe { CoreTypeParser::from_raw(ptr::NonNull::new(result).unwrap()) }; - (parser, core) -} - impl CoreArrayProvider for CoreTypeParser { type Raw = *mut BNTypeParser; type Context = (); - type Wrapped<'a> = &'a Self; + type Wrapped<'a> = Self; } unsafe impl CoreArrayProviderInner for CoreTypeParser { @@ -276,1366 +276,336 @@ unsafe impl CoreArrayProviderInner for CoreTypeParser { } unsafe fn wrap_raw<'a>(raw: &'a Self::Raw, _context: &'a Self::Context) -> Self::Wrapped<'a> { - CoreTypeParser::ref_from_raw(raw) + // TODO: Because handle is a NonNull we should prob make Self::Raw that as well... + let handle = NonNull::new(*raw).unwrap(); + CoreTypeParser::from_raw(handle) } } -/// A `TypeContainer` is a generic interface to access various Binary Ninja models -/// that contain types. Types are stored with both a unique id and a unique name. -// /// -// /// The `TypeContainer` class should not generally be instantiated directly. Instances -// /// can be retrieved from the following properties and methods in the API: -// /// * [BinaryView::type_container] -// /// * [BinaryView::auto_type_container] -// /// * [BinaryView::user_type_container] -// /// * [Platform::type_container] -// /// * [TypeLibrary::type_container] -// /// * [DebugInfo::get_type_container] -#[repr(transparent)] -pub struct TypeContainer { - handle: ptr::NonNull, +#[derive(Clone, Debug, Eq, PartialEq)] +pub struct TypeParserError { + pub severity: TypeParserErrorSeverity, + pub message: String, + pub file_name: String, + pub line: u64, + pub column: u64, } -impl TypeContainer { - pub(crate) unsafe fn from_raw(handle: ptr::NonNull) -> Self { - Self { handle } - } - - pub(crate) unsafe fn ref_from_raw(handle: &*mut BNTypeContainer) -> &Self { - assert!(!handle.is_null()); - mem::transmute(handle) - } - - #[allow(clippy::ref_as_ptr)] - pub(crate) unsafe fn as_raw(&self) -> &mut BNTypeContainer { - &mut *self.handle.as_ptr() - } - - /// Get an id string for the Type Container. This will be unique within a given - /// analysis session, but may not be globally unique. - pub fn id(&self) -> BnString { - let result = unsafe { BNTypeContainerGetId(self.as_raw()) }; - assert!(!result.is_null()); - unsafe { BnString::from_raw(result) } - } - - /// Get a user-friendly name for the Type Container. - pub fn name(&self) -> BnString { - let result = unsafe { BNTypeContainerGetName(self.as_raw()) }; - assert!(!result.is_null()); - unsafe { BnString::from_raw(result) } - } - - /// Get the type of underlying model the Type Container is accessing. - pub fn container_type(&self) -> TypeContainerType { - unsafe { BNTypeContainerGetType(self.as_raw()) } - } - - /// Test if the Type Container supports mutable operations (add, rename, delete) - pub fn is_mutable(&self) -> bool { - unsafe { BNTypeContainerIsMutable(self.as_raw()) } - } - - /// Get the Platform object associated with this Type Container. All Type Containers - /// have exactly one associated Platform (as opposed to, e.g. Type Libraries). - pub fn platform(&self) -> Ref { - let result = unsafe { BNTypeContainerGetPlatform(self.as_raw()) }; - assert!(!result.is_null()); - unsafe { Platform::ref_from_raw(result) } - } - - /// Add or update types to a Type Container. If the Type Container already contains - /// a type with the same name as a type being added, the existing type will be - /// replaced with the definition given to this function, and references will be - /// updated in the source model. - pub fn add_types(&self, types: I) -> bool - where - I: IntoIterator)>, - { - let (names, types): (Vec, Vec>) = types.into_iter().unzip(); - - // SAFETY QualifiedName is transparent with BNQualifiedName - let names_raw: &[BNQualifiedName] = unsafe { mem::transmute(names.as_slice()) }; - - let mut types_raw: Vec<*mut BNType> = types.iter().map(|t| t.handle).collect(); - - let mut result_names = ptr::null_mut(); - let mut result_ids = ptr::null_mut(); - let mut result_count = 0; - unsafe { - BNTypeContainerAddTypes( - self.as_raw(), - names_raw.as_ptr(), - types_raw.as_mut_ptr(), - types.len(), - Some(cb_progress_nop), - ptr::null_mut(), - &mut result_names, - &mut result_ids, - &mut result_count, - ) +impl TypeParserError { + pub fn new( + severity: TypeParserErrorSeverity, + message: String, + file_name: String, + line: u64, + column: u64, + ) -> Self { + Self { + severity, + message, + file_name, + line, + column, } } +} - pub fn add_types_with_progress(&self, types: I, mut progress: F) -> bool - where - I: IntoIterator)>, - F: FnMut(usize, usize) -> bool, - { - let (names, types): (Vec, Vec>) = types.into_iter().unzip(); - - // SAFETY QualifiedName is transparent with BNQualifiedName - let names_raw: &[BNQualifiedName] = unsafe { mem::transmute(names.as_slice()) }; - - let mut types_raw: Vec<*mut BNType> = types.iter().map(|t| t.handle).collect(); - - let mut result_names = ptr::null_mut(); - let mut result_ids = ptr::null_mut(); - let mut result_count = 0; - unsafe { - BNTypeContainerAddTypes( - self.as_raw(), - names_raw.as_ptr(), - types_raw.as_mut_ptr(), - types.len(), - Some(cb_progress::), - &mut progress as *mut F as *mut ffi::c_void, - &mut result_names, - &mut result_ids, - &mut result_count, - ) +impl From for TypeParserError { + fn from(value: BNTypeParserError) -> Self { + Self { + severity: value.severity, + message: unsafe { BnString::from_raw(value.message).to_string() }, + file_name: unsafe { BnString::from_raw(value.fileName).to_string() }, + line: value.line, + column: value.column, } } +} - /// Rename a type in the Type Container. All references to this type will be updated - /// (by id) to use the new name. - pub fn rename_type(&self, name: &QualifiedName, type_id: S) -> bool { - let type_id = type_id.into_bytes_with_nul(); - unsafe { - BNTypeContainerRenameType( - self.as_raw(), - type_id.as_ref().as_ptr() as *const ffi::c_char, - &name.0, - ) +impl From<&BNTypeParserError> for TypeParserError { + fn from(value: &BNTypeParserError) -> Self { + Self { + severity: value.severity, + message: raw_to_string(value.message).unwrap(), + file_name: raw_to_string(value.fileName).unwrap(), + line: value.line, + column: value.column, } } +} - /// Delete a type in the Type Container. Behavior of references to this type is - /// not specified and you may end up with broken references if any still exist. - pub fn delete_type(&self, type_id: S) -> bool { - let type_id = type_id.into_bytes_with_nul(); - unsafe { - BNTypeContainerDeleteType( - self.as_raw(), - type_id.as_ref().as_ptr() as *const ffi::c_char, - ) +impl From for BNTypeParserError { + fn from(value: TypeParserError) -> Self { + Self { + severity: value.severity, + message: BnString::new(value.message).into_raw(), + fileName: BnString::new(value.file_name).into_raw(), + line: value.line, + column: value.column, } } +} - /// Get the unique id of the type in the Type Container with the given name. - /// If no type with that name exists, returns None. - pub fn type_id(&self, type_name: &QualifiedName) -> Option { - let mut result = ptr::null_mut(); - let success = unsafe { BNTypeContainerGetTypeId(self.as_raw(), &type_name.0, &mut result) }; - success.then(|| unsafe { BnString::from_raw(result) }) - } +impl CoreArrayProvider for TypeParserError { + type Raw = BNTypeParserError; + type Context = (); + type Wrapped<'a> = Self; +} - /// Get the unique name of the type in the Type Container with the given id. - /// If no type with that id exists, returns None. - pub fn type_name(&self, type_id: S) -> Option { - let type_id = type_id.into_bytes_with_nul(); - let mut result = BNQualifiedName::default(); - let success = unsafe { - BNTypeContainerGetTypeName( - self.as_raw(), - type_id.as_ref().as_ptr() as *const ffi::c_char, - &mut result, - ) - }; - success.then(|| QualifiedName(result)) +unsafe impl CoreArrayProviderInner for TypeParserError { + unsafe fn free(raw: *mut Self::Raw, count: usize, _context: &Self::Context) { + unsafe { BNFreeTypeParserErrors(raw, count) } } - /// Get the definition of the type in the Type Container with the given id. - /// If no type with that id exists, returns None. - pub fn type_by_id(&self, type_id: S) -> Option> { - let type_id = type_id.into_bytes_with_nul(); - let mut result = ptr::null_mut(); - let success = unsafe { - BNTypeContainerGetTypeById( - self.as_raw(), - type_id.as_ref().as_ptr() as *const ffi::c_char, - &mut result, - ) - }; - success.then(|| unsafe { Type::ref_from_raw(result) }) + unsafe fn wrap_raw<'a>(raw: &'a Self::Raw, _context: &'a Self::Context) -> Self::Wrapped<'a> { + Self::from(raw) } +} - /// Get the definition of the type in the Type Container with the given name. - /// If no type with that name exists, returns None. - pub fn type_by_name(&self, type_name: &QualifiedName) -> Option> { - let mut result = ptr::null_mut(); - let success = - unsafe { BNTypeContainerGetTypeByName(self.as_raw(), &type_name.0, &mut result) }; - success.then(|| unsafe { Type::ref_from_raw(result) }) - } +#[derive(Debug, Eq, PartialEq, Default)] +pub struct TypeParserResult { + pub types: Vec, + pub variables: Vec, + pub functions: Vec, +} - /// Get a mapping of all types in a Type Container. - pub fn types(&self) -> Option<(Array, Array, Array)> { - let mut type_ids = ptr::null_mut(); - let mut type_names = ptr::null_mut(); - let mut type_types = ptr::null_mut(); - let mut type_count = 0; - let success = unsafe { - BNTypeContainerGetTypes( - self.as_raw(), - &mut type_ids, - &mut type_names, - &mut type_types, - &mut type_count, - ) +impl From for TypeParserResult { + fn from(mut value: BNTypeParserResult) -> Self { + let raw_types = unsafe { std::slice::from_raw_parts(value.types, value.typeCount) }; + let raw_variables = + unsafe { std::slice::from_raw_parts(value.variables, value.variableCount) }; + let raw_functions = + unsafe { std::slice::from_raw_parts(value.functions, value.functionCount) }; + let result = TypeParserResult { + types: raw_types.iter().map(|t| ParsedType::from(t)).collect(), + variables: raw_variables.iter().map(|t| ParsedType::from(t)).collect(), + functions: raw_functions.iter().map(|t| ParsedType::from(t)).collect(), }; - success.then(|| unsafe { - let ids = Array::new(type_ids, type_count, ()); - let names = Array::new(type_names, type_count, ()); - let types = Array::new(type_types, type_count, ()); - (types, names, ids) - }) - } - - /// Get all type ids in a Type Container. - pub fn type_ids(&self) -> Option> { - let mut type_ids = ptr::null_mut(); - let mut type_count = 0; - let success = - unsafe { BNTypeContainerGetTypeIds(self.as_raw(), &mut type_ids, &mut type_count) }; - success.then(|| unsafe { Array::new(type_ids, type_count, ()) }) - } - - /// Get all type names in a Type Container. - pub fn type_names(&self) -> Option> { - let mut type_ids = ptr::null_mut(); - let mut type_count = 0; - let success = - unsafe { BNTypeContainerGetTypeNames(self.as_raw(), &mut type_ids, &mut type_count) }; - success.then(|| unsafe { Array::new(type_ids, type_count, ()) }) + // SAFETY: `value` must be a properly initialized BNTypeParserResult. + unsafe { BNFreeTypeParserResult(&mut value) }; + result } +} - /// Get a mapping of all type ids and type names in a Type Container. - pub fn type_names_and_ids(&self) -> Option<(Array, Array)> { - let mut type_ids = ptr::null_mut(); - let mut type_names = ptr::null_mut(); - let mut type_count = 0; - let success = unsafe { - BNTypeContainerGetTypeNamesAndIds( - self.as_raw(), - &mut type_ids, - &mut type_names, - &mut type_count, - ) - }; - success.then(|| unsafe { - let ids = Array::new(type_ids, type_count, ()); - let names = Array::new(type_names, type_count, ()); - (ids, names) - }) - } +#[derive(Debug, Clone, Eq, PartialEq)] +pub struct ParsedType { + name: QualifiedName, + ty: Ref, + user: bool, +} - /// Parse a single type and name from a string containing their definition, with - /// knowledge of the types in the Type Container. - /// - /// * `source` - Source code to parse - /// * `import_dependencies` - If Type Library / Type Archive types should be imported during parsing - pub fn parse_type_string( - &self, - source: S, - import_dependencies: bool, - ) -> Result> { - let source = source.into_bytes_with_nul(); - let mut result = BNQualifiedNameAndType::default(); - let mut errors = ptr::null_mut(); - let mut error_count = 0; - let success = unsafe { - BNTypeContainerParseTypeString( - self.as_raw(), - source.as_ref().as_ptr() as *const ffi::c_char, - import_dependencies, - &mut result, - &mut errors, - &mut error_count, - ) - }; - if success { - Ok(QualifiedNameAndType(result)) - } else { - assert!(!errors.is_null()); - Err(unsafe { Array::new(errors, error_count, ()) }) - } +impl ParsedType { + pub fn new(name: QualifiedName, ty: Ref, user: bool) -> Self { + Self { name, ty, user } } +} - /// Parse an entire block of source into types, variables, and functions, with - /// knowledge of the types in the Type Container. - - /// * `source` - Source code to parse - /// * `file_name` - Name of the file containing the source (optional: exists on disk) - /// * `options` - String arguments to pass as options, e.g. command line arguments - /// * `include_dirs` - List of directories to include in the header search path - /// * `auto_type_source` - Source of types if used for automatically generated types - /// * `import_dependencies` - If Type Library / Type Archive types should be imported during parsing - pub fn parse_types_from_source( - &self, - source: S, - filename: F, - options: O, - include_directories: D, - auto_type_source: A, - import_dependencies: bool, - ) -> Result> - where - S: BnStrCompatible, - F: BnStrCompatible, - O: IntoIterator, - O::Item: BnStrCompatible, - D: IntoIterator, - D::Item: BnStrCompatible, - A: BnStrCompatible, - { - let source = source.into_bytes_with_nul(); - let filename = filename.into_bytes_with_nul(); - let options: Vec<_> = options - .into_iter() - .map(|o| o.into_bytes_with_nul()) - .collect(); - let options_raw: Vec<*const ffi::c_char> = options - .iter() - .map(|o| o.as_ref().as_ptr() as *const ffi::c_char) - .collect(); - let include_directories: Vec<_> = include_directories - .into_iter() - .map(|d| d.into_bytes_with_nul()) - .collect(); - let include_directories_raw: Vec<*const ffi::c_char> = include_directories - .iter() - .map(|d| d.as_ref().as_ptr() as *const ffi::c_char) - .collect(); - let auto_type_source = auto_type_source.into_bytes_with_nul(); - let mut result = BNTypeParserResult::default(); - let mut errors = ptr::null_mut(); - let mut error_count = 0; - let success = unsafe { - BNTypeContainerParseTypesFromSource( - self.as_raw(), - source.as_ref().as_ptr() as *const ffi::c_char, - filename.as_ref().as_ptr() as *const ffi::c_char, - options_raw.as_ptr(), - options_raw.len(), - include_directories_raw.as_ptr(), - include_directories_raw.len(), - auto_type_source.as_ref().as_ptr() as *const ffi::c_char, - import_dependencies, - &mut result, - &mut errors, - &mut error_count, - ) - }; - if success { - Ok(unsafe { TypeParserResult::from_raw(result) }) - } else { - assert!(!errors.is_null()); - Err(unsafe { Array::new(errors, error_count, ()) }) +impl From for ParsedType { + fn from(value: BNParsedType) -> Self { + Self { + name: value.name.into(), + ty: unsafe { Type::ref_from_raw(value.type_) }, + user: value.isUser, } } } -impl Drop for TypeContainer { - fn drop(&mut self) { - unsafe { BNFreeTypeContainer(self.as_raw()) } +impl From<&BNParsedType> for ParsedType { + fn from(value: &BNParsedType) -> Self { + Self { + name: QualifiedName::from(&value.name), + ty: unsafe { Type::from_raw(value.type_).to_owned() }, + user: value.isUser, + } } } -impl Clone for TypeContainer { - fn clone(&self) -> Self { - unsafe { - Self::from_raw(ptr::NonNull::new(BNDuplicateTypeContainer(self.as_raw())).unwrap()) +impl From for BNParsedType { + fn from(value: ParsedType) -> Self { + Self { + name: value.name.into(), + type_: value.ty.handle, + isUser: value.user, } } } -#[repr(transparent)] -pub struct CoreTypePrinter { - handle: ptr::NonNull, +impl CoreArrayProvider for ParsedType { + type Raw = BNParsedType; + type Context = (); + type Wrapped<'b> = Self; } -impl CoreTypePrinter { - pub(crate) unsafe fn from_raw(handle: ptr::NonNull) -> CoreTypePrinter { - Self { handle } +unsafe impl CoreArrayProviderInner for ParsedType { + unsafe fn free(_raw: *mut Self::Raw, _count: usize, _context: &Self::Context) { + // Expected to be freed with BNFreeTypeParserResult + // TODO ^ because of the above, we should not provide an array provider for this } - pub(crate) unsafe fn ref_from_raw(handle: &*mut BNTypePrinter) -> &Self { - mem::transmute(handle) + unsafe fn wrap_raw<'b>(raw: &'b Self::Raw, _context: &'b Self::Context) -> Self::Wrapped<'b> { + ParsedType::from(raw) } +} - #[allow(clippy::mut_from_ref)] - pub(crate) unsafe fn as_raw(&self) -> &mut BNTypePrinter { - &mut *self.handle.as_ptr() +unsafe extern "C" fn cb_get_option_text( + ctxt: *mut ::std::os::raw::c_void, + option: BNTypeParserOption, + value: *const c_char, + result: *mut *mut c_char, +) -> bool { + let ctxt: &mut T = &mut *(ctxt as *mut T); + if let Some(inner_result) = ctxt.get_option_text(option, &raw_to_string(value).unwrap()) { + let bn_inner_result = BnString::new(inner_result); + // NOTE: Dropped by `cb_free_string` + *result = bn_inner_result.into_raw(); + true + } else { + *result = std::ptr::null_mut(); + false } +} - pub fn printers() -> Array { - let mut count = 0; - let result = unsafe { BNGetTypePrinterList(&mut count) }; - assert!(!result.is_null()); - unsafe { Array::new(result, count, ()) } +unsafe extern "C" fn cb_preprocess_source( + ctxt: *mut c_void, + source: *const c_char, + file_name: *const c_char, + platform: *mut BNPlatform, + existing_types: *mut BNTypeContainer, + options: *const *const c_char, + option_count: usize, + include_dirs: *const *const c_char, + include_dir_count: usize, + result: *mut *mut c_char, + errors: *mut *mut BNTypeParserError, + error_count: *mut usize, +) -> bool { + let ctxt: &mut T = &mut *(ctxt as *mut T); + let platform = Platform { handle: platform }; + let existing_types_ptr = NonNull::new(existing_types).unwrap(); + let existing_types = TypeContainer::from_raw(existing_types_ptr); + let options_raw = unsafe { std::slice::from_raw_parts(options, option_count) }; + let options: Vec<_> = options_raw + .iter() + .filter_map(|&r| raw_to_string(r)) + .collect(); + let includes_raw = unsafe { std::slice::from_raw_parts(include_dirs, include_dir_count) }; + let includes: Vec<_> = includes_raw + .iter() + .filter_map(|&r| raw_to_string(r)) + .collect(); + match ctxt.preprocess_source( + &raw_to_string(source).unwrap(), + &raw_to_string(file_name).unwrap(), + &platform, + &existing_types, + &options, + &includes, + ) { + Ok(inner_result) => { + let bn_inner_result = BnString::new(inner_result); + // NOTE: Dropped by `cb_free_string` + *result = bn_inner_result.into_raw(); + *errors = std::ptr::null_mut(); + *error_count = 0; + true + } + Err(inner_errors) => { + *error_count = inner_errors.len(); + let inner_errors: Box<[_]> = inner_errors.into_iter().map(Into::into).collect(); + *result = std::ptr::null_mut(); + // NOTE: Dropped by `cb_free_error_list` + *errors = Box::leak(inner_errors).as_mut_ptr(); + false + } } +} - pub fn printer_by_name(name: S) -> Option { - let name_raw = name.into_bytes_with_nul(); - let result = - unsafe { BNGetTypePrinterByName(name_raw.as_ref().as_ptr() as *const ffi::c_char) }; - ptr::NonNull::new(result).map(|x| unsafe { Self::from_raw(x) }) +unsafe extern "C" fn cb_parse_types_from_source( + ctxt: *mut c_void, + source: *const c_char, + file_name: *const c_char, + platform: *mut BNPlatform, + existing_types: *mut BNTypeContainer, + options: *const *const c_char, + option_count: usize, + include_dirs: *const *const c_char, + include_dir_count: usize, + auto_type_source: *const c_char, + result: *mut BNTypeParserResult, + errors: *mut *mut BNTypeParserError, + error_count: *mut usize, +) -> bool { + let ctxt: &mut T = &mut *(ctxt as *mut T); + let platform = Platform { handle: platform }; + let existing_types_ptr = NonNull::new(existing_types).unwrap(); + let existing_types = TypeContainer::from_raw(existing_types_ptr); + let options_raw = unsafe { std::slice::from_raw_parts(options, option_count) }; + let options: Vec<_> = options_raw + .iter() + .filter_map(|&r| raw_to_string(r)) + .collect(); + let includes_raw = unsafe { std::slice::from_raw_parts(include_dirs, include_dir_count) }; + let includes: Vec<_> = includes_raw + .iter() + .filter_map(|&r| raw_to_string(r)) + .collect(); + match ctxt.parse_types_from_source( + &raw_to_string(source).unwrap(), + &raw_to_string(file_name).unwrap(), + &platform, + &existing_types, + &options, + &includes, + &raw_to_string(auto_type_source).unwrap(), + ) { + Ok(type_parser_result) => { + let boxed_raw_types: Box<[BNParsedType]> = type_parser_result + .types + .into_iter() + .map(Into::into) + .collect(); + let boxed_raw_variables: Box<[BNParsedType]> = type_parser_result + .variables + .into_iter() + .map(Into::into) + .collect(); + let boxed_raw_functions: Box<[BNParsedType]> = type_parser_result + .functions + .into_iter() + .map(Into::into) + .collect(); + let type_count = boxed_raw_types.len(); + let variable_count = boxed_raw_variables.len(); + let function_count = boxed_raw_functions.len(); + let raw_result = BNTypeParserResult { + // NOTE: Freed with `cb_free_result`. + types: Box::leak(boxed_raw_types).as_mut_ptr(), + // NOTE: Freed with `cb_free_result`. + variables: Box::leak(boxed_raw_variables).as_mut_ptr(), + // NOTE: Freed with `cb_free_result`. + functions: Box::leak(boxed_raw_functions).as_mut_ptr(), + typeCount: type_count, + variableCount: variable_count, + functionCount: function_count, + }; + *result = raw_result; + *errors = std::ptr::null_mut(); + *error_count = 0; + true + } + Err(inner_errors) => { + *error_count = inner_errors.len(); + let inner_errors: Box<[_]> = inner_errors.into_iter().map(Into::into).collect(); + *result = Default::default(); + // NOTE: Dropped by cb_free_error_list + *errors = Box::leak(inner_errors).as_mut_ptr(); + false + } } - - pub fn name(&self) -> BnString { - let result = unsafe { BNGetTypePrinterName(self.as_raw()) }; - assert!(!result.is_null()); - unsafe { BnString::from_raw(result) } - } - - pub fn get_type_tokens( - &self, - type_: &Type, - platform: &Platform, - name: &QualifiedName, - base_confidence: u8, - escaping: TokenEscapingType, - ) -> Option> { - let mut result_count = 0; - let mut result = ptr::null_mut(); - let success = unsafe { - BNGetTypePrinterTypeTokens( - self.as_raw(), - type_.handle, - platform.handle, - &name.0 as *const _ as *mut _, - base_confidence, - escaping, - &mut result, - &mut result_count, - ) - }; - success.then(|| { - assert!(!result.is_null()); - unsafe { Array::new(result, result_count, ()) } - }) - } - - pub fn get_type_tokens_before_name( - &self, - type_: &Type, - platform: &Platform, - base_confidence: u8, - parent_type: &Type, - escaping: TokenEscapingType, - ) -> Option> { - let mut result_count = 0; - let mut result = ptr::null_mut(); - let success = unsafe { - BNGetTypePrinterTypeTokensBeforeName( - self.as_raw(), - type_.handle, - platform.handle, - base_confidence, - parent_type.handle, - escaping, - &mut result, - &mut result_count, - ) - }; - success.then(|| { - assert!(!result.is_null()); - unsafe { Array::new(result, result_count, ()) } - }) - } - - pub fn get_type_tokens_after_name( - &self, - type_: &Type, - platform: &Platform, - base_confidence: u8, - parent_type: &Type, - escaping: TokenEscapingType, - ) -> Option> { - let mut result_count = 0; - let mut result = ptr::null_mut(); - let success = unsafe { - BNGetTypePrinterTypeTokensAfterName( - self.as_raw(), - type_.handle, - platform.handle, - base_confidence, - parent_type.handle, - escaping, - &mut result, - &mut result_count, - ) - }; - success.then(|| { - assert!(!result.is_null()); - unsafe { Array::new(result, result_count, ()) } - }) - } - - pub fn get_type_string( - &self, - type_: &Type, - platform: &Platform, - name: &QualifiedName, - escaping: TokenEscapingType, - ) -> Option { - let mut result = ptr::null_mut(); - let success = unsafe { - BNGetTypePrinterTypeString( - self.as_raw(), - type_.handle, - platform.handle, - &name.0 as *const _ as *mut _, - escaping, - &mut result, - ) - }; - success.then(|| unsafe { - assert!(!result.is_null()); - BnString::from_raw(result) - }) - } - - pub fn get_type_string_before_name( - &self, - type_: &Type, - platform: &Platform, - escaping: BNTokenEscapingType, - ) -> Option { - let mut result = ptr::null_mut(); - let success = unsafe { - BNGetTypePrinterTypeStringAfterName( - self.as_raw(), - type_.handle, - platform.handle, - escaping, - &mut result, - ) - }; - success.then(|| unsafe { - assert!(!result.is_null()); - BnString::from_raw(result) - }) - } - - pub fn get_type_string_after_name( - &self, - type_: &Type, - platform: &Platform, - escaping: TokenEscapingType, - ) -> Option { - let mut result = ptr::null_mut(); - let success = unsafe { - BNGetTypePrinterTypeStringBeforeName( - self.as_raw(), - type_.handle, - platform.handle, - escaping, - &mut result, - ) - }; - success.then(|| unsafe { - assert!(!result.is_null()); - BnString::from_raw(result) - }) - } - - pub fn get_type_lines( - &self, - type_: &Type, - types: &TypeContainer, - name: &QualifiedName, - padding_cols: ffi::c_int, - collapsed: bool, - escaping: TokenEscapingType, - ) -> Option> { - let mut result_count = 0; - let mut result = ptr::null_mut(); - let success = unsafe { - BNGetTypePrinterTypeLines( - self.as_raw(), - type_.handle, - types.as_raw(), - &name.0 as *const BNQualifiedName as *mut BNQualifiedName, - padding_cols, - collapsed, - escaping, - &mut result, - &mut result_count, - ) - }; - success.then(|| { - assert!(!result.is_null()); - unsafe { Array::::new(result, result_count, ()) } - }) - } - - /// Print all types to a single big string, including headers, sections, etc - /// - /// * `types` - All types to print - /// * `data` - Binary View in which all the types are defined - /// * `padding_cols` - Maximum number of bytes represented by each padding line - /// * `escaping` - Style of escaping literals which may not be parsable - pub fn default_print_all_types( - &self, - types: &[QualifiedNameAndType], - data: &BinaryView, - padding_cols: ffi::c_int, - escaping: TokenEscapingType, - ) -> Option { - let mut result = ptr::null_mut(); - let mut types_raw: Vec<*mut BNType> = types - .iter() - .map(|t| t.type_object().as_ref().handle) - .collect(); - let mut names_raw: Vec = types.iter().map(|t| t.name().0).collect(); - let success = unsafe { - BNTypePrinterDefaultPrintAllTypes( - self.as_raw(), - names_raw.as_mut_ptr(), - types_raw.as_mut_ptr(), - types.len(), - data.handle, - padding_cols, - escaping, - &mut result, - ) - }; - success.then(|| unsafe { - assert!(!result.is_null()); - BnString::from_raw(result) - }) - } - - pub fn print_all_types( - &self, - types: &[QualifiedNameAndType], - data: &BinaryView, - padding_cols: ffi::c_int, - escaping: TokenEscapingType, - ) -> Option { - let mut result = ptr::null_mut(); - let mut types_raw: Vec<*mut BNType> = types - .iter() - .map(|t| t.type_object().as_ref().handle) - .collect(); - let mut names_raw: Vec = types.iter().map(|t| t.name().0).collect(); - let success = unsafe { - BNTypePrinterPrintAllTypes( - self.as_raw(), - names_raw.as_mut_ptr(), - types_raw.as_mut_ptr(), - types.len(), - data.handle, - padding_cols, - escaping, - &mut result, - ) - }; - success.then(|| unsafe { - assert!(!result.is_null()); - BnString::from_raw(result) - }) - } -} - -impl Default for CoreTypePrinter { - fn default() -> Self { - let default_settings = crate::settings::Settings::default(); - let name = default_settings.get_string( - ffi::CStr::from_bytes_with_nul(b"analysis.types.printerName\x00").unwrap(), - None, - None, - ); - Self::printer_by_name(name).unwrap() - } -} - -impl CoreArrayProvider for CoreTypePrinter { - type Raw = *mut BNTypePrinter; - type Context = (); - type Wrapped<'a> = &'a Self; -} - -unsafe impl CoreArrayProviderInner for CoreTypePrinter { - unsafe fn free(raw: *mut Self::Raw, _count: usize, _context: &Self::Context) { - BNFreeTypePrinterList(raw) - } - - unsafe fn wrap_raw<'a>(raw: &'a Self::Raw, _context: &'a Self::Context) -> Self::Wrapped<'a> { - CoreTypePrinter::ref_from_raw(raw) - } -} - -pub trait TypePrinter { - /// Generate a single-line text representation of a type, Returns a List - /// of text tokens representing the type. - /// - /// * `type_` - Type to print - /// * `platform` - Platform responsible for this type - /// * `name` - Name of the type - /// * `base_confidence` - Confidence to use for tokens created for this type - /// * `escaping` - Style of escaping literals which may not be parsable - fn get_type_tokens( - &self, - type_: &Type, - platform: &Platform, - name: &QualifiedName, - base_confidence: u8, - escaping: TokenEscapingType, - ) -> Option>; - - /// In a single-line text representation of a type, generate the tokens that - /// should be printed before the type's name. Returns a list of text tokens - /// representing the type - /// - /// * `type_` - Type to print - /// * `platform` - Platform responsible for this type - /// * `base_confidence` - Confidence to use for tokens created for this type - /// * `parent_type` - Type of the parent of this type, or None - /// * `escaping` - Style of escaping literals which may not be parsable - fn get_type_tokens_before_name( - &self, - type_: &Type, - platform: &Platform, - base_confidence: u8, - parent_type: &Type, - escaping: TokenEscapingType, - ) -> Option>; - - /// In a single-line text representation of a type, generate the tokens - /// that should be printed after the type's name. Returns a ist of text - /// tokens representing the type - /// - /// * `type_` - Type to print - /// * `platform` - Platform responsible for this type - /// * `base_confidence` - Confidence to use for tokens created for this type - /// * `parent_type` - Type of the parent of this type, or None - /// * `escaping` - Style of escaping literals which may not be parsable - fn get_type_tokens_after_name( - &self, - type_: &Type, - platform: &Platform, - base_confidence: u8, - parent_type: &Type, - escaping: TokenEscapingType, - ) -> Option>; - - /// Generate a single-line text representation of a type. Returns a string - /// representing the type - /// - /// * `type_` - Type to print - /// * `platform` - Platform responsible for this type - /// * `name` - Name of the type - /// * `escaping` - Style of escaping literals which may not be parsable - fn get_type_string( - &self, - type_: &Type, - platform: &Platform, - name: &QualifiedName, - escaping: TokenEscapingType, - ) -> Option; - - /// In a single-line text representation of a type, generate the string that - /// should be printed before the type's name. Returns a string representing - /// the type - /// - /// * `type_` - Type to print - /// * `platform` - Platform responsible for this type - /// * `escaping` - Style of escaping literals which may not be parsable - fn get_type_string_before_name( - &self, - type_: &Type, - platform: &Platform, - escaping: TokenEscapingType, - ) -> Option; - - /// In a single-line text representation of a type, generate the string that - /// should be printed after the type's name. Returns a string representing - /// the type - /// - /// * `type_` - Type to print - /// * `platform` - Platform responsible for this type - /// * `escaping` - Style of escaping literals which may not be parsable - fn get_type_string_after_name( - &self, - type_: &Type, - platform: &Platform, - escaping: TokenEscapingType, - ) -> Option; - - /// Generate a multi-line representation of a type. Returns a list of type - /// definition lines - /// - /// * `type_` - Type to print - /// * `types` - Type Container containing the type and dependencies - /// * `name` - Name of the type - /// * `padding_cols` - Maximum number of bytes represented by each padding line - /// * `collapsed` - Whether to collapse structure/enum blocks - /// * `escaping` - Style of escaping literals which may not be parsable - fn get_type_lines( - &self, - type_: &Type, - types: &TypeContainer, - name: &QualifiedName, - padding_cols: ffi::c_int, - collapsed: bool, - escaping: TokenEscapingType, - ) -> Option>; - - /// Print all types to a single big string, including headers, sections, - /// etc. - /// - /// * `types` - All types to print - /// * `data` - Binary View in which all the types are defined - /// * `padding_cols` - Maximum number of bytes represented by each padding line - /// * `escaping` - Style of escaping literals which may not be parsable - fn print_all_types( - &self, - names: &QualifiedName, - types: &[Type], - data: &BinaryView, - padding_cols: ffi::c_int, - escaping: TokenEscapingType, - ) -> Option; -} - -/// Register a custom parser with the API -pub fn register_type_printer( - name: S, - parser: T, -) -> (&'static mut T, CoreTypePrinter) { - let parser = Box::leak(Box::new(parser)); - let mut callback = BNTypePrinterCallbacks { - context: parser as *mut _ as *mut ffi::c_void, - getTypeTokens: Some(cb_get_type_tokens::), - getTypeTokensBeforeName: Some(cb_get_type_tokens_before_name::), - getTypeTokensAfterName: Some(cb_get_type_tokens_after_name::), - getTypeString: Some(cb_get_type_string::), - getTypeStringBeforeName: Some(cb_get_type_string_before_name::), - getTypeStringAfterName: Some(cb_get_type_string_after_name::), - getTypeLines: Some(cb_get_type_lines::), - printAllTypes: Some(cb_print_all_types::), - freeTokens: Some(cb_free_tokens), - freeString: Some(cb_free_string), - freeLines: Some(cb_free_lines), - }; - let result = unsafe { - BNRegisterTypePrinter( - name.into_bytes_with_nul().as_ref().as_ptr() as *const ffi::c_char, - &mut callback, - ) - }; - let core = unsafe { CoreTypePrinter::from_raw(ptr::NonNull::new(result).unwrap()) }; - (parser, core) -} - -#[repr(C)] -#[derive(Clone)] -pub struct TypeParserError { - pub severity: TypeParserErrorSeverity, - pub message: BnString, - pub file_name: BnString, - pub line: u64, - pub column: u64, -} - -impl TypeParserError { - pub fn new( - severity: TypeParserErrorSeverity, - message: M, - file_name: F, - line: u64, - column: u64, - ) -> Self { - Self { - severity, - message: BnString::new(message), - file_name: BnString::new(file_name), - line, - column, - } - } - - pub(crate) unsafe fn ref_from_raw(handle: &BNTypeParserError) -> &Self { - assert!(!handle.message.is_null()); - assert!(!handle.fileName.is_null()); - mem::transmute(handle) - } -} - -impl core::fmt::Debug for TypeParserError { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - let severity = match self.severity { - BNTypeParserErrorSeverity::IgnoredSeverity => "ignored", - BNTypeParserErrorSeverity::NoteSeverity => "note", - BNTypeParserErrorSeverity::RemarkSeverity => "remark", - BNTypeParserErrorSeverity::WarningSeverity => "warning", - BNTypeParserErrorSeverity::FatalSeverity | BNTypeParserErrorSeverity::ErrorSeverity => { - "error" - } - }; - let message = if self.file_name.as_str() == "" { - self.message.as_str().to_owned() - } else { - format!( - "{}: {}:{} {}", - self.file_name.as_str(), - self.line, - self.column, - self.message - ) - }; - f.debug_struct("TypeParserError") - .field("severity", &severity) - .field("message", &message) - .finish() - } -} - -impl CoreArrayProvider for TypeParserError { - type Raw = BNTypeParserError; - type Context = (); - type Wrapped<'a> = &'a Self; -} - -unsafe impl CoreArrayProviderInner for TypeParserError { - unsafe fn free(raw: *mut Self::Raw, count: usize, _context: &Self::Context) { - unsafe { BNFreeTypeParserErrors(raw, count) } - } - - unsafe fn wrap_raw<'a>(raw: &'a Self::Raw, _context: &'a Self::Context) -> Self::Wrapped<'a> { - Self::ref_from_raw(raw) - } -} - -/// The user created version of TypeParserResult -#[derive(Debug, Clone, Default)] -pub struct TypeParserResultUser { - pub types: Vec, - pub variables: Vec, - pub functions: Vec, -} - -impl TypeParserResultUser { - fn into_user_raw(self) -> BNTypeParserResult { - let Self { - types, - variables, - functions, - } = self; - let types = Box::leak(types.into_iter().map(|t| unsafe { t.into_raw() }).collect()); - let variables = Box::leak( - variables - .into_iter() - .map(|t| unsafe { t.into_raw() }) - .collect(), - ); - let functions = Box::leak( - functions - .into_iter() - .map(|t| unsafe { t.into_raw() }) - .collect(), - ); - BNTypeParserResult { - types: types.as_mut_ptr(), - typeCount: types.len(), - variables: variables.as_mut_ptr(), - variableCount: variables.len(), - functions: functions.as_mut_ptr(), - functionCount: functions.len(), - } - } - - /// SAFETY only can be used with products from [TypeParserResultUser::into_user_raw] - unsafe fn from_user_raw(value: BNTypeParserResult) -> Self { - let from_raw = |raw, count| { - Box::from_raw(ptr::slice_from_raw_parts_mut(raw, count)) - .into_iter() - .map(|t| ParsedType::from_raw(t)) - .collect() - }; - Self { - types: from_raw(value.types, value.typeCount), - variables: from_raw(value.variables, value.variableCount), - functions: from_raw(value.functions, value.functionCount), - } - } -} - -pub struct TypeParserResult(BNTypeParserResult); - -impl TypeParserResult { - pub(crate) unsafe fn from_raw(value: BNTypeParserResult) -> Self { - Self(value) - } - - fn as_raw(&mut self) -> &mut BNTypeParserResult { - &mut self.0 - } - - pub fn types(&self) -> ArrayGuard<&ParsedType> { - unsafe { ArrayGuard::new(self.0.types, self.0.typeCount, &()) } - } - - pub fn variables(&self) -> ArrayGuard<&ParsedType> { - unsafe { ArrayGuard::new(self.0.variables, self.0.variableCount, &()) } - } - - pub fn functions(&self) -> ArrayGuard<&ParsedType> { - unsafe { ArrayGuard::new(self.0.functions, self.0.functionCount, &()) } - } -} - -impl Drop for TypeParserResult { - fn drop(&mut self) { - unsafe { BNFreeTypeParserResult(self.as_raw()) } - } -} - -#[derive(Debug, Clone)] -pub struct ParsedType { - name: QualifiedName, - type_: Ref, - is_user: bool, -} - -impl ParsedType { - pub fn new(name: QualifiedName, type_: Ref, is_user: bool) -> Self { - Self { - name, - type_, - is_user, - } - } - - pub(crate) unsafe fn from_raw(parsed: &BNParsedType) -> Self { - Self { - name: QualifiedName(parsed.name), - type_: Type::ref_from_raw(parsed.type_), - is_user: parsed.isUser, - } - } - - pub(crate) unsafe fn clone_from_raw(parsed: &BNParsedType) -> Self { - let name = mem::ManuallyDrop::new(QualifiedName(parsed.name)); - let type_ = Type { - handle: parsed.type_, - } - .to_owned(); - let user = parsed.isUser; - ParsedType::new((&*name).clone(), type_, user) - } - - pub(crate) unsafe fn into_raw(self) -> BNParsedType { - let Self { - name, - type_, - is_user, - } = self; - BNParsedType { - name: mem::ManuallyDrop::new(name).0, - type_: Ref::into_raw(type_).handle, - isUser: is_user, - } - } - - pub fn name(&self) -> &QualifiedName { - &self.name - } - - pub fn ty(&self) -> &Type { - &self.type_ - } - - pub fn is_user(&self) -> bool { - self.is_user - } -} - -impl<'a> CoreArrayProvider for &'a ParsedType { - type Raw = BNParsedType; - type Context = &'a (); - type Wrapped<'b> = ParsedType where 'a: 'b; -} - -unsafe impl<'a> CoreArrayProviderInner for &'a ParsedType { - // ParsedType can only by used in a ArrayGuard - unsafe fn free(_raw: *mut Self::Raw, _count: usize, _context: &Self::Context) { - unreachable!() - } - - unsafe fn wrap_raw<'b>(raw: &'b Self::Raw, _context: &'b Self::Context) -> Self::Wrapped<'b> { - ParsedType::clone_from_raw(raw) - } -} - -#[derive(Clone)] -pub struct TypeDefinitionLine { - pub line_type: TypeDefinitionLineType, - pub tokens: Vec, - pub type_: Ref, - pub parent_type: Ref, - pub root_type: Ref, - pub root_type_name: BnString, - pub base_type: Ref, - pub base_offset: u64, - pub offset: u64, - pub field_index: usize, -} - -impl TypeDefinitionLine { - pub(crate) unsafe fn clone_from_raw(value: &BNTypeDefinitionLine) -> Self { - Self { - line_type: value.lineType, - tokens: unsafe { core::slice::from_raw_parts(value.tokens, value.count) } - .iter() - .map(|i| InstructionTextToken::from_raw(i).to_owned()) - .collect(), - type_: Type { - handle: value.type_, - } - .to_owned(), - parent_type: Type { - handle: value.parentType, - } - .to_owned(), - root_type: Type { - handle: value.rootType, - } - .to_owned(), - root_type_name: unsafe { BnString::new(ffi::CStr::from_ptr(value.rootTypeName)) }, - base_type: NamedTypeReference::from_raw(value.baseType).to_owned(), - base_offset: value.baseOffset, - offset: value.offset, - field_index: value.fieldIndex, - } - } - - fn into_raw(self) -> TypeDefinitionLineRaw { - let TypeDefinitionLine { - line_type, - tokens, - type_, - parent_type, - root_type, - root_type_name, - base_type, - base_offset, - offset, - field_index, - } = self; - let tokens = Box::leak(tokens.into_boxed_slice()); - // SAFETY BNInstructionTextToken and InstructionTextToken are transparent - let tokens_ptr = tokens.as_mut_ptr() as *mut BNInstructionTextToken; - TypeDefinitionLineRaw(BNTypeDefinitionLine { - lineType: line_type, - tokens: tokens_ptr, - count: tokens.len(), - type_: unsafe { Ref::into_raw(type_) }.handle, - parentType: unsafe { Ref::into_raw(parent_type) }.handle, - rootType: unsafe { Ref::into_raw(root_type) }.handle, - rootTypeName: root_type_name.into_raw(), - baseType: unsafe { Ref::into_raw(base_type) }.handle, - baseOffset: base_offset, - offset: offset, - fieldIndex: field_index, - }) - } -} - -#[repr(transparent)] -struct TypeDefinitionLineRaw(BNTypeDefinitionLine); - -impl Drop for TypeDefinitionLineRaw { - fn drop(&mut self) { - let _tokens = unsafe { - Box::from_raw(ptr::slice_from_raw_parts_mut( - self.0.tokens as *mut InstructionTextToken, - self.0.count, - )) - }; - let _type = unsafe { Type::ref_from_raw(self.0.type_) }; - let _parent_type = unsafe { Type::ref_from_raw(self.0.parentType) }; - let _root_type = unsafe { Type::ref_from_raw(self.0.rootType) }; - let _root_type_name = unsafe { BnString::from_raw(self.0.rootTypeName) }; - let _base_type = unsafe { NamedTypeReference::ref_from_raw(self.0.baseType) }; - } -} - -impl CoreArrayProvider for TypeDefinitionLine { - type Raw = BNTypeDefinitionLine; - type Context = (); - type Wrapped<'a> = Self; -} - -unsafe impl CoreArrayProviderInner for TypeDefinitionLine { - unsafe fn free(raw: *mut Self::Raw, count: usize, _context: &Self::Context) { - unsafe { BNFreeTypeDefinitionLineList(raw, count) }; - } - - unsafe fn wrap_raw<'a>(raw: &'a Self::Raw, _context: &'a Self::Context) -> Self::Wrapped<'a> { - Self::clone_from_raw(raw) - } -} - -pub fn options_text(options: O) -> Array { - let options = options.into_bytes_with_nul(); - let mut count = 0; - let result = unsafe { - BNParseTypeParserOptionsText(options.as_ref().as_ptr() as *const ffi::c_char, &mut count) - }; - assert!(!result.is_null()); - unsafe { Array::new(result, count, ()) } -} - -pub fn parser_errors(errors: &[TypeParserError]) -> BnString { - // SAFETY TypeParserError and BNTypeParserError are transparent - let errors: &[BNTypeParserError] = unsafe { mem::transmute(errors) }; - let result = unsafe { BNFormatTypeParserParseErrors(errors.as_ptr() as *mut _, errors.len()) }; - assert!(!result.is_null()); - unsafe { BnString::from_raw(result) } -} - -unsafe extern "C" fn cb_get_option_text( - ctxt: *mut ::std::os::raw::c_void, - option: BNTypeParserOption, - value: *const ffi::c_char, - result: *mut *mut ffi::c_char, -) -> bool { - let ctxt: &mut T = &mut *(ctxt as *mut T); - let value = ffi::CStr::from_ptr(value); - let value = value.to_string_lossy(); - if let Some(inner_result) = ctxt.get_option_text(option, &value) { - // SAFETY dropped by cb_free_string - *result = inner_result.into_raw(); - true - } else { - *result = ptr::null_mut(); - false - } -} - -unsafe extern "C" fn cb_preprocess_source( - ctxt: *mut ffi::c_void, - source: *const ffi::c_char, - file_name: *const ffi::c_char, - platform: *mut BNPlatform, - existing_types: *mut BNTypeContainer, - options: *const *const ffi::c_char, - option_count: usize, - include_dirs: *const *const ffi::c_char, - include_dir_count: usize, - result: *mut *mut ffi::c_char, - errors: *mut *mut BNTypeParserError, - error_count: *mut usize, -) -> bool { - let ctxt: &mut T = &mut *(ctxt as *mut T); - let source = ffi::CStr::from_ptr(source); - let file_name = ffi::CStr::from_ptr(file_name); - let platform = Platform { handle: platform }; - let existing_types = TypeContainer::ref_from_raw(&existing_types); - // SAFETY BnString and *ffi::c_char are transparent - let options: &[BnString] = - core::slice::from_raw_parts(options as *const BnString, option_count); - let include_dirs: &[BnString] = - core::slice::from_raw_parts(include_dirs as *const BnString, include_dir_count); - match ctxt.preprocess_source( - &source.to_string_lossy(), - &file_name.to_string_lossy(), - &platform, - existing_types, - options, - include_dirs, - ) { - Ok(inner_result) => { - // SAFETY drop by the function cb_free_string - *result = inner_result.into_raw(); - *errors = ptr::null_mut(); - *error_count = 0; - true - } - Err(inner_erros) => { - // SAFETY drop by the function cb_free_error_list - let inner_errors = Box::leak(inner_erros.into_boxed_slice()); - // SAFETY: TypeParserError and BNTypeParserError are transparent - *result = ptr::null_mut(); - *errors = inner_errors.as_ptr() as *mut BNTypeParserError; - *error_count = inner_errors.len(); - false - } - } -} - -unsafe extern "C" fn cb_parse_types_from_source( - ctxt: *mut ffi::c_void, - source: *const ffi::c_char, - file_name: *const ffi::c_char, - platform: *mut BNPlatform, - existing_types: *mut BNTypeContainer, - options: *const *const ffi::c_char, - option_count: usize, - include_dirs: *const *const ffi::c_char, - include_dir_count: usize, - auto_type_source: *const ffi::c_char, - result: *mut BNTypeParserResult, - errors: *mut *mut BNTypeParserError, - error_count: *mut usize, -) -> bool { - let ctxt: &mut T = &mut *(ctxt as *mut T); - let source = ffi::CStr::from_ptr(source); - let file_name = ffi::CStr::from_ptr(file_name); - let auto_type_source = ffi::CStr::from_ptr(auto_type_source); - let platform = Platform { handle: platform }; - let existing_types = TypeContainer::ref_from_raw(&existing_types); - // SAFETY BnString and *ffi::c_char are transparent - let options: &[BnString] = - core::slice::from_raw_parts(options as *const BnString, option_count); - let include_dirs: &[BnString] = - core::slice::from_raw_parts(include_dirs as *const BnString, include_dir_count); - match ctxt.parse_types_from_source( - &source.to_string_lossy(), - &file_name.to_string_lossy(), - &platform, - existing_types, - options, - include_dirs, - &auto_type_source.to_string_lossy(), - ) { - Ok(inner_result) => { - let inner_result_ffi = inner_result.into_user_raw(); - // SAFETY drop by the function cb_free_result - *result = inner_result_ffi; - *errors = ptr::null_mut(); - *error_count = 0; - true - } - Err(inner_erros) => { - // SAFETY drop by the function cb_free_error_list - let inner_errors = Box::leak(inner_erros.into_boxed_slice()); - // SAFETY: TypeParserError and BNTypeParserError are transparent - *result = Default::default(); - *errors = inner_errors.as_ptr() as *mut BNTypeParserError; - *error_count = inner_errors.len(); - false - } - } -} +} unsafe extern "C" fn cb_parse_type_string( - ctxt: *mut ffi::c_void, - source: *const ffi::c_char, + ctxt: *mut c_void, + source: *const c_char, platform: *mut BNPlatform, existing_types: *mut BNTypeContainer, result: *mut BNQualifiedNameAndType, @@ -1643,327 +613,41 @@ unsafe extern "C" fn cb_parse_type_string( error_count: *mut usize, ) -> bool { let ctxt: &mut T = &mut *(ctxt as *mut T); - let source = ffi::CStr::from_ptr(source); let platform = Platform { handle: platform }; - let existing_types = TypeContainer::ref_from_raw(&existing_types); - match ctxt.parse_type_string(&source.to_string_lossy(), &platform, existing_types) { + let existing_types_ptr = NonNull::new(existing_types).unwrap(); + let existing_types = TypeContainer::from_raw(existing_types_ptr); + match ctxt.parse_type_string(&raw_to_string(source).unwrap(), &platform, &existing_types) { Ok(inner_result) => { - // TODO: SAFETY: dropped by the function ?BNFreeQualifiedNameAndType? - *result = mem::ManuallyDrop::new(inner_result).0; - *errors = ptr::null_mut(); + *result = inner_result.into(); + *errors = std::ptr::null_mut(); *error_count = 0; true } Err(inner_errors) => { - // SAFETY drop by the function cb_free_error_list - let inner_errors = Box::leak(inner_errors.into_boxed_slice()); - // SAFETY: TypeParserError and BNTypeParserError are transparent - *result = Default::default(); - *errors = inner_errors.as_ptr() as *mut BNTypeParserError; *error_count = inner_errors.len(); + let inner_errors: Box<[_]> = inner_errors.into_iter().map(Into::into).collect(); + *result = Default::default(); + // NOTE: Dropped by cb_free_error_list + *errors = Box::leak(inner_errors).as_mut_ptr(); false } } } -unsafe extern "C" fn cb_free_string(_ctxt: *mut ffi::c_void, string: *mut ffi::c_char) { - // SAFETY the returned string is just BnString - drop(BnString::from_raw(string)) +unsafe extern "C" fn cb_free_string(_ctxt: *mut c_void, string: *mut c_char) { + // SAFETY: The returned string is just BnString + let _ = BnString::from_raw(string); } -unsafe extern "C" fn cb_free_result(_ctxt: *mut ffi::c_void, result: *mut BNTypeParserResult) { - drop(TypeParserResultUser::from_user_raw(*result)) +unsafe extern "C" fn cb_free_result(_ctxt: *mut c_void, result: *mut BNTypeParserResult) { + let _ = Box::from_raw(result); } unsafe extern "C" fn cb_free_error_list( - _ctxt: *mut ffi::c_void, + _ctxt: *mut c_void, errors: *mut BNTypeParserError, error_count: usize, ) { - // SAFETY TypeParserError and BNTypeParserError are transparent, and error - // originally was an TypeParserError transmuted into BNTypeParserError - let errors = - core::ptr::slice_from_raw_parts_mut(errors, error_count) as *mut [TypeParserError]; - let errors: Box<[TypeParserError]> = Box::from_raw(errors); - drop(errors) -} - -unsafe extern "C" fn cb_get_type_tokens( - ctxt: *mut ::std::os::raw::c_void, - type_: *mut BNType, - platform: *mut BNPlatform, - name: *mut BNQualifiedName, - base_confidence: u8, - escaping: BNTokenEscapingType, - result: *mut *mut BNInstructionTextToken, - result_count: *mut usize, -) -> bool { - let ctxt: &mut T = &mut *(ctxt as *mut T); - let inner_result = ctxt.get_type_tokens( - &Type { handle: type_ }, - &Platform { handle: platform }, - &*mem::ManuallyDrop::new(QualifiedName(*name)), - base_confidence, - 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; - true - } else { - *result = ptr::null_mut(); - *result_count = 0; - false - } -} - -unsafe extern "C" fn cb_get_type_tokens_before_name( - ctxt: *mut ::std::os::raw::c_void, - type_: *mut BNType, - platform: *mut BNPlatform, - base_confidence: u8, - parent_type: *mut BNType, - escaping: BNTokenEscapingType, - result: *mut *mut BNInstructionTextToken, - result_count: *mut usize, -) -> bool { - let ctxt: &mut T = &mut *(ctxt as *mut T); - let inner_result = ctxt.get_type_tokens_before_name( - &Type { handle: type_ }, - &Platform { handle: platform }, - base_confidence, - &Type { - handle: parent_type, - }, - 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; - true - } else { - *result = ptr::null_mut(); - *result_count = 0; - false - } -} - -unsafe extern "C" fn cb_get_type_tokens_after_name( - ctxt: *mut ::std::os::raw::c_void, - type_: *mut BNType, - platform: *mut BNPlatform, - base_confidence: u8, - parent_type: *mut BNType, - escaping: BNTokenEscapingType, - result: *mut *mut BNInstructionTextToken, - result_count: *mut usize, -) -> bool { - let ctxt: &mut T = &mut *(ctxt as *mut T); - let inner_result = ctxt.get_type_tokens_after_name( - &Type { handle: type_ }, - &Platform { handle: platform }, - base_confidence, - &Type { - handle: parent_type, - }, - 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; - true - } else { - *result = ptr::null_mut(); - *result_count = 0; - false - } -} - -unsafe extern "C" fn cb_get_type_string( - ctxt: *mut ::std::os::raw::c_void, - type_: *mut BNType, - platform: *mut BNPlatform, - name: *mut BNQualifiedName, - escaping: BNTokenEscapingType, - result: *mut *mut ::std::os::raw::c_char, -) -> bool { - let ctxt: &mut T = &mut *(ctxt as *mut T); - let inner_result = ctxt.get_type_string( - &Type { handle: type_ }, - &Platform { handle: platform }, - &*mem::ManuallyDrop::new(QualifiedName(*name)), - escaping, - ); - if let Some(inner_result) = inner_result { - // SAFETY dropped by the cb_free_string - *result = inner_result.into_raw(); - true - } else { - *result = ptr::null_mut(); - false - } -} - -unsafe extern "C" fn cb_get_type_string_before_name( - ctxt: *mut ::std::os::raw::c_void, - type_: *mut BNType, - platform: *mut BNPlatform, - escaping: BNTokenEscapingType, - result: *mut *mut ::std::os::raw::c_char, -) -> bool { - let ctxt: &mut T = &mut *(ctxt as *mut T); - let inner_result = ctxt.get_type_string_before_name( - &Type { handle: type_ }, - &Platform { handle: platform }, - escaping, - ); - if let Some(inner_result) = inner_result { - // SAFETY dropped by the cb_free_string - *result = inner_result.into_raw(); - true - } else { - *result = ptr::null_mut(); - false - } -} - -unsafe extern "C" fn cb_get_type_string_after_name( - ctxt: *mut ::std::os::raw::c_void, - type_: *mut BNType, - platform: *mut BNPlatform, - escaping: BNTokenEscapingType, - result: *mut *mut ::std::os::raw::c_char, -) -> bool { - let ctxt: &mut T = &mut *(ctxt as *mut T); - let inner_result = ctxt.get_type_string_after_name( - &Type { handle: type_ }, - &Platform { handle: platform }, - escaping, - ); - if let Some(inner_result) = inner_result { - // SAFETY dropped by the cb_free_string - *result = inner_result.into_raw(); - true - } else { - *result = ptr::null_mut(); - false - } -} - -unsafe extern "C" fn cb_get_type_lines( - ctxt: *mut ::std::os::raw::c_void, - type_: *mut BNType, - types: *mut BNTypeContainer, - name: *mut BNQualifiedName, - padding_cols: ::std::os::raw::c_int, - collapsed: bool, - escaping: BNTokenEscapingType, - result: *mut *mut BNTypeDefinitionLine, - result_count: *mut usize, -) -> bool { - let ctxt: &mut T = &mut *(ctxt as *mut T); - let inner_result = ctxt.get_type_lines( - &Type { handle: type_ }, - TypeContainer::ref_from_raw(&types), - &*mem::ManuallyDrop::new(QualifiedName(*name)), - padding_cols, - collapsed, - escaping, - ); - if let Some(inner_result) = inner_result { - // SAFETY dropped by the cb_free_lines - let inner_result = Box::leak(inner_result.into_iter().map(|x| x.into_raw()).collect()); - // SAFETY TypeDefinitionLineRaw and BNTypeDefinitionLine are transparent - *result_count = inner_result.len(); - let inner_result_ptr = inner_result.as_ptr() as *const BNTypeDefinitionLine; - *result = inner_result_ptr as *mut BNTypeDefinitionLine; - true - } else { - *result = ptr::null_mut(); - *result_count = 0; - false - } -} - -unsafe extern "C" fn cb_print_all_types( - ctxt: *mut ::std::os::raw::c_void, - names: *mut BNQualifiedName, - types: *mut *mut BNType, - type_count: usize, - data: *mut BNBinaryView, - padding_cols: ::std::os::raw::c_int, - escaping: BNTokenEscapingType, - result: *mut *mut ::std::os::raw::c_char, -) -> bool { - let ctxt: &mut T = &mut *(ctxt as *mut T); - let inner_result = ctxt.print_all_types( - &*mem::ManuallyDrop::new(QualifiedName(*names)), - //SAFETY *mut BNType and Type are transparent - core::slice::from_raw_parts(types as *mut Type, type_count), - &BinaryView { handle: data }, - padding_cols, - escaping, - ); - if let Some(inner_result) = inner_result { - // SAFETY dropped by the cb_free_string - *result = inner_result.into_raw(); - true - } else { - *result = ptr::null_mut(); - false - } -} - -unsafe extern "C" fn cb_progress_nop( - _ctxt: *mut ::std::os::raw::c_void, - _progress: usize, - _total: usize, -) -> bool { - true -} - -unsafe extern "C" fn cb_progress bool>( - ctxt: *mut ::std::os::raw::c_void, - progress: usize, - total: usize, -) -> bool { - let ctxt = &mut *(ctxt as *mut F); - ctxt(progress, total) -} - -unsafe extern "C" fn cb_free_tokens( - _ctxt: *mut ::std::os::raw::c_void, - tokens: *mut BNInstructionTextToken, - count: usize, -) { - drop(Box::from_raw(core::ptr::slice_from_raw_parts_mut( - tokens as *mut InstructionTextToken, - count, - ))); -} - -unsafe extern "C" fn cb_free_lines( - _ctxt: *mut ::std::os::raw::c_void, - lines: *mut BNTypeDefinitionLine, - count: usize, -) { - drop(Box::from_raw(core::ptr::slice_from_raw_parts_mut( - lines as *mut TypeDefinitionLineRaw, - count, - ))); + let errors = std::ptr::slice_from_raw_parts_mut(errors, error_count); + let _ = Box::from_raw(errors); } diff --git a/rust/src/typeprinter.rs b/rust/src/typeprinter.rs new file mode 100644 index 000000000..b9145b430 --- /dev/null +++ b/rust/src/typeprinter.rs @@ -0,0 +1,921 @@ +use crate::binaryview::BinaryView; +use crate::disassembly::InstructionTextToken; +use crate::platform::Platform; +use crate::rc::{Array, CoreArrayProvider, CoreArrayProviderInner, Ref}; +use crate::string::{raw_to_string, BnStrCompatible, BnString}; +use crate::typecontainer::TypeContainer; +use crate::types::{NamedTypeReference, QualifiedName, QualifiedNameAndType, Type}; +use binaryninjacore_sys::*; +use std::ffi::{c_char, c_int, c_void}; +use std::ptr::NonNull; + +pub type TokenEscapingType = BNTokenEscapingType; +pub type TypeDefinitionLineType = BNTypeDefinitionLineType; + +/// Register a custom parser with the API +pub fn register_type_printer( + name: S, + parser: T, +) -> (&'static mut T, CoreTypePrinter) { + let parser = Box::leak(Box::new(parser)); + let mut callback = BNTypePrinterCallbacks { + context: parser as *mut _ as *mut c_void, + getTypeTokens: Some(cb_get_type_tokens::), + getTypeTokensBeforeName: Some(cb_get_type_tokens_before_name::), + getTypeTokensAfterName: Some(cb_get_type_tokens_after_name::), + getTypeString: Some(cb_get_type_string::), + getTypeStringBeforeName: Some(cb_get_type_string_before_name::), + getTypeStringAfterName: Some(cb_get_type_string_after_name::), + getTypeLines: Some(cb_get_type_lines::), + printAllTypes: Some(cb_print_all_types::), + freeTokens: Some(cb_free_tokens), + freeString: Some(cb_free_string), + freeLines: Some(cb_free_lines), + }; + let result = unsafe { + BNRegisterTypePrinter( + name.into_bytes_with_nul().as_ref().as_ptr() as *const c_char, + &mut callback, + ) + }; + let core = unsafe { CoreTypePrinter::from_raw(NonNull::new(result).unwrap()) }; + (parser, core) +} + +#[repr(transparent)] +pub struct CoreTypePrinter { + handle: NonNull, +} + +impl CoreTypePrinter { + pub(crate) unsafe fn from_raw(handle: NonNull) -> CoreTypePrinter { + Self { handle } + } + + pub fn printers() -> Array { + let mut count = 0; + let result = unsafe { BNGetTypePrinterList(&mut count) }; + assert!(!result.is_null()); + unsafe { Array::new(result, count, ()) } + } + + pub fn printer_by_name(name: S) -> Option { + let name_raw = name.into_bytes_with_nul(); + let result = unsafe { BNGetTypePrinterByName(name_raw.as_ref().as_ptr() as *const c_char) }; + NonNull::new(result).map(|x| unsafe { Self::from_raw(x) }) + } + + pub fn name(&self) -> BnString { + let result = unsafe { BNGetTypePrinterName(self.handle.as_ptr()) }; + assert!(!result.is_null()); + unsafe { BnString::from_raw(result) } + } + + pub fn get_type_tokens>( + &self, + type_: &Type, + platform: &Platform, + name: T, + base_confidence: u8, + escaping: TokenEscapingType, + ) -> Option> { + let mut result_count = 0; + let mut result = std::ptr::null_mut(); + let mut raw_name = BNQualifiedName::from(name.into()); + let success = unsafe { + BNGetTypePrinterTypeTokens( + self.handle.as_ptr(), + type_.handle, + platform.handle, + &mut raw_name, + base_confidence, + escaping, + &mut result, + &mut result_count, + ) + }; + success.then(|| { + assert!(!result.is_null()); + unsafe { Array::new(result, result_count, ()) } + }) + } + + pub fn get_type_tokens_before_name( + &self, + type_: &Type, + platform: &Platform, + base_confidence: u8, + parent_type: &Type, + escaping: TokenEscapingType, + ) -> Option> { + let mut result_count = 0; + let mut result = std::ptr::null_mut(); + let success = unsafe { + BNGetTypePrinterTypeTokensBeforeName( + self.handle.as_ptr(), + type_.handle, + platform.handle, + base_confidence, + parent_type.handle, + escaping, + &mut result, + &mut result_count, + ) + }; + success.then(|| { + assert!(!result.is_null()); + unsafe { Array::new(result, result_count, ()) } + }) + } + + pub fn get_type_tokens_after_name( + &self, + type_: &Type, + platform: &Platform, + base_confidence: u8, + parent_type: &Type, + escaping: TokenEscapingType, + ) -> Option> { + let mut result_count = 0; + let mut result = std::ptr::null_mut(); + let success = unsafe { + BNGetTypePrinterTypeTokensAfterName( + self.handle.as_ptr(), + type_.handle, + platform.handle, + base_confidence, + parent_type.handle, + escaping, + &mut result, + &mut result_count, + ) + }; + success.then(|| { + assert!(!result.is_null()); + unsafe { Array::new(result, result_count, ()) } + }) + } + + pub fn get_type_string>( + &self, + type_: &Type, + platform: &Platform, + name: T, + escaping: TokenEscapingType, + ) -> Option { + let mut result = std::ptr::null_mut(); + let mut raw_name = BNQualifiedName::from(name.into()); + let success = unsafe { + BNGetTypePrinterTypeString( + self.handle.as_ptr(), + type_.handle, + platform.handle, + &mut raw_name, + escaping, + &mut result, + ) + }; + success.then(|| unsafe { + assert!(!result.is_null()); + BnString::from_raw(result) + }) + } + + pub fn get_type_string_before_name( + &self, + type_: &Type, + platform: &Platform, + escaping: BNTokenEscapingType, + ) -> Option { + let mut result = std::ptr::null_mut(); + let success = unsafe { + BNGetTypePrinterTypeStringAfterName( + self.handle.as_ptr(), + type_.handle, + platform.handle, + escaping, + &mut result, + ) + }; + success.then(|| unsafe { + assert!(!result.is_null()); + BnString::from_raw(result) + }) + } + + pub fn get_type_string_after_name( + &self, + type_: &Type, + platform: &Platform, + escaping: TokenEscapingType, + ) -> Option { + let mut result = std::ptr::null_mut(); + let success = unsafe { + BNGetTypePrinterTypeStringBeforeName( + self.handle.as_ptr(), + type_.handle, + platform.handle, + escaping, + &mut result, + ) + }; + success.then(|| unsafe { + assert!(!result.is_null()); + BnString::from_raw(result) + }) + } + + pub fn get_type_lines>( + &self, + type_: &Type, + types: &TypeContainer, + name: T, + padding_cols: isize, + collapsed: bool, + escaping: TokenEscapingType, + ) -> Option> { + let mut result_count = 0; + let mut result = std::ptr::null_mut(); + let mut raw_name = BNQualifiedName::from(name.into()); + let success = unsafe { + BNGetTypePrinterTypeLines( + self.handle.as_ptr(), + type_.handle, + types.handle.as_ptr(), + &mut raw_name, + padding_cols as c_int, + collapsed, + escaping, + &mut result, + &mut result_count, + ) + }; + success.then(|| { + assert!(!result.is_null()); + unsafe { Array::::new(result, result_count, ()) } + }) + } + + /// Print all types to a single big string, including headers, sections, etc + /// + /// * `types` - All types to print + /// * `data` - Binary View in which all the types are defined + /// * `padding_cols` - Maximum number of bytes represented by each padding line + /// * `escaping` - Style of escaping literals which may not be parsable + pub fn default_print_all_types( + &self, + types: T, + data: &BinaryView, + padding_cols: isize, + escaping: TokenEscapingType, + ) -> Option + where + T: Iterator, + I: Into, + { + let mut result = std::ptr::null_mut(); + let (mut raw_names, mut raw_types): (Vec, Vec<_>) = types + .map(|t| { + let t = t.into(); + (BNQualifiedName::from(t.name), t.ty.handle) + }) + .unzip(); + let success = unsafe { + BNTypePrinterDefaultPrintAllTypes( + self.handle.as_ptr(), + raw_names.as_mut_ptr(), + raw_types.as_mut_ptr(), + raw_types.len(), + data.handle, + padding_cols as c_int, + escaping, + &mut result, + ) + }; + success.then(|| unsafe { + assert!(!result.is_null()); + BnString::from_raw(result) + }) + } + + pub fn print_all_types( + &self, + types: T, + data: &BinaryView, + padding_cols: isize, + escaping: TokenEscapingType, + ) -> Option + where + T: Iterator, + I: Into, + { + let mut result = std::ptr::null_mut(); + // TODO: I dislike how this iter unzip looks like... but its how to avoid allocating again... + let (mut raw_names, mut raw_types): (Vec, Vec<_>) = types + .map(|t| { + let t = t.into(); + (BNQualifiedName::from(t.name), t.ty.handle) + }) + .unzip(); + let success = unsafe { + BNTypePrinterPrintAllTypes( + self.handle.as_ptr(), + raw_names.as_mut_ptr(), + raw_types.as_mut_ptr(), + raw_types.len(), + data.handle, + padding_cols as c_int, + escaping, + &mut result, + ) + }; + success.then(|| unsafe { + assert!(!result.is_null()); + BnString::from_raw(result) + }) + } +} + +impl Default for CoreTypePrinter { + fn default() -> Self { + // TODO: Remove this entirely, there is no "default", its view specific lets not make this some defined behavior. + let default_settings = crate::settings::Settings::default(); + let name = default_settings.get_string( + std::ffi::CStr::from_bytes_with_nul(b"analysis.types.printerName\x00").unwrap(), + None, + None, + ); + Self::printer_by_name(name).unwrap() + } +} + +impl CoreArrayProvider for CoreTypePrinter { + type Raw = *mut BNTypePrinter; + type Context = (); + type Wrapped<'a> = Self; +} + +unsafe impl CoreArrayProviderInner for CoreTypePrinter { + unsafe fn free(raw: *mut Self::Raw, _count: usize, _context: &Self::Context) { + BNFreeTypePrinterList(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(); + CoreTypePrinter::from_raw(handle) + } +} + +pub trait TypePrinter { + /// Generate a single-line text representation of a type, Returns a List + /// of text tokens representing the type. + /// + /// * `type_` - Type to print + /// * `platform` - Platform responsible for this type + /// * `name` - Name of the type + /// * `base_confidence` - Confidence to use for tokens created for this type + /// * `escaping` - Style of escaping literals which may not be parsable + fn get_type_tokens>( + &self, + type_: Ref, + platform: Option>, + name: T, + base_confidence: u8, + escaping: TokenEscapingType, + ) -> Option>; + + /// In a single-line text representation of a type, generate the tokens that + /// should be printed before the type's name. Returns a list of text tokens + /// representing the type + /// + /// * `type_` - Type to print + /// * `platform` - Platform responsible for this type + /// * `base_confidence` - Confidence to use for tokens created for this type + /// * `parent_type` - Type of the parent of this type, or None + /// * `escaping` - Style of escaping literals which may not be parsable + fn get_type_tokens_before_name( + &self, + type_: Ref, + platform: Option>, + base_confidence: u8, + parent_type: Option>, + escaping: TokenEscapingType, + ) -> Option>; + + /// In a single-line text representation of a type, generate the tokens + /// that should be printed after the type's name. Returns a ist of text + /// tokens representing the type + /// + /// * `type_` - Type to print + /// * `platform` - Platform responsible for this type + /// * `base_confidence` - Confidence to use for tokens created for this type + /// * `parent_type` - Type of the parent of this type, or None + /// * `escaping` - Style of escaping literals which may not be parsable + fn get_type_tokens_after_name( + &self, + type_: Ref, + platform: Option>, + base_confidence: u8, + parent_type: Option>, + escaping: TokenEscapingType, + ) -> Option>; + + /// Generate a single-line text representation of a type. Returns a string + /// representing the type + /// + /// * `type_` - Type to print + /// * `platform` - Platform responsible for this type + /// * `name` - Name of the type + /// * `escaping` - Style of escaping literals which may not be parsable + fn get_type_string>( + &self, + type_: Ref, + platform: Option>, + name: T, + escaping: TokenEscapingType, + ) -> Option; + + /// In a single-line text representation of a type, generate the string that + /// should be printed before the type's name. Returns a string representing + /// the type + /// + /// * `type_` - Type to print + /// * `platform` - Platform responsible for this type + /// * `escaping` - Style of escaping literals which may not be parsable + fn get_type_string_before_name( + &self, + type_: Ref, + platform: Option>, + escaping: TokenEscapingType, + ) -> Option; + + /// In a single-line text representation of a type, generate the string that + /// should be printed after the type's name. Returns a string representing + /// the type + /// + /// * `type_` - Type to print + /// * `platform` - Platform responsible for this type + /// * `escaping` - Style of escaping literals which may not be parsable + fn get_type_string_after_name( + &self, + type_: Ref, + platform: Option>, + escaping: TokenEscapingType, + ) -> Option; + + /// Generate a multi-line representation of a type. Returns a list of type + /// definition lines + /// + /// * `type_` - Type to print + /// * `types` - Type Container containing the type and dependencies + /// * `name` - Name of the type + /// * `padding_cols` - Maximum number of bytes represented by each padding line + /// * `collapsed` - Whether to collapse structure/enum blocks + /// * `escaping` - Style of escaping literals which may not be parsable + fn get_type_lines>( + &self, + type_: Ref, + types: &TypeContainer, + name: T, + padding_cols: isize, + collapsed: bool, + escaping: TokenEscapingType, + ) -> Option>; + + /// Print all types to a single big string, including headers, sections, + /// etc. + /// + /// * `types` - All types to print + /// * `data` - Binary View in which all the types are defined + /// * `padding_cols` - Maximum number of bytes represented by each padding line + /// * `escaping` - Style of escaping literals which may not be parsable + fn print_all_types( + &self, + names: Vec, + types: Vec>, + data: Ref, + padding_cols: isize, + escaping: TokenEscapingType, + ) -> Option; +} + +// TODO: This needs an extreme amount of documentation... +#[derive(Clone)] +pub struct TypeDefinitionLine { + pub line_type: TypeDefinitionLineType, + pub tokens: Vec, + pub ty: Ref, + pub parent_type: Option>, + // TODO: Document what the root type is. + pub root_type: Option>, + pub root_type_name: Option, + // TODO: Document the base type, and why its a ntr instead of type + name like root type + pub base_type: Option>, + // TODO: These can also be optional? + pub base_offset: u64, + pub offset: u64, + 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 From<&BNTypeDefinitionLine> for TypeDefinitionLine { + fn from(value: &BNTypeDefinitionLine) -> Self { + Self { + line_type: value.lineType, + // TODO: Tokens are busted. + tokens: vec![], + 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() }), + true => None, + }, + root_type: match value.rootType.is_null() { + false => Some(unsafe { Type::from_raw(value.rootType).to_owned() }), + true => None, + }, + root_type_name: match value.rootTypeName.is_null() { + false => Some(raw_to_string(value.rootTypeName).unwrap()), + true => None, + }, + base_type: match value.baseType.is_null() { + false => Some(unsafe { NamedTypeReference::from_raw(value.baseType).to_owned() }), + true => None, + }, + base_offset: value.baseOffset, + offset: value.offset, + field_index: value.fieldIndex, + } + } +} + +impl From for BNTypeDefinitionLine { + fn from(value: TypeDefinitionLine) -> Self { + Self { + lineType: value.line_type, + tokens: todo!("this is busted!"), + count: todo!("see above"), + type_: value.ty.handle, + parentType: value + .parent_type + .map(|t| t.handle) + .unwrap_or(std::ptr::null_mut()), + rootType: value + .root_type + .map(|t| t.handle) + .unwrap_or(std::ptr::null_mut()), + rootTypeName: value + .root_type_name + .map(|s| BnString::new(s).into_raw()) + .unwrap_or(std::ptr::null_mut()), + baseType: value + .base_type + .map(|t| t.handle) + .unwrap_or(std::ptr::null_mut()), + baseOffset: value.base_offset, + offset: value.offset, + fieldIndex: value.field_index, + } + } +} + +impl CoreArrayProvider for TypeDefinitionLine { + type Raw = BNTypeDefinitionLine; + type Context = (); + type Wrapped<'a> = Self; +} + +unsafe impl CoreArrayProviderInner for TypeDefinitionLine { + unsafe fn free(raw: *mut Self::Raw, count: usize, _context: &Self::Context) { + unsafe { BNFreeTypeDefinitionLineList(raw, count) }; + } + + unsafe fn wrap_raw<'a>(raw: &'a Self::Raw, _context: &'a Self::Context) -> Self::Wrapped<'a> { + Self::from(raw) + } +} + +unsafe extern "C" fn cb_get_type_tokens( + ctxt: *mut ::std::os::raw::c_void, + type_: *mut BNType, + platform: *mut BNPlatform, + name: *mut BNQualifiedName, + base_confidence: u8, + escaping: BNTokenEscapingType, + result: *mut *mut BNInstructionTextToken, + result_count: *mut usize, +) -> bool { + let ctxt: &mut T = &mut *(ctxt as *mut T); + let qualified_name = QualifiedName::from(*name); + let inner_result = ctxt.get_type_tokens( + unsafe { Type::ref_from_raw(type_) }, + match platform.is_null() { + false => Some(Platform::ref_from_raw(platform)), + true => None, + }, + qualified_name, + base_confidence, + 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; + true + } else { + *result = std::ptr::null_mut(); + *result_count = 0; + false + } +} + +unsafe extern "C" fn cb_get_type_tokens_before_name( + ctxt: *mut ::std::os::raw::c_void, + type_: *mut BNType, + platform: *mut BNPlatform, + base_confidence: u8, + parent_type: *mut BNType, + escaping: BNTokenEscapingType, + result: *mut *mut BNInstructionTextToken, + result_count: *mut usize, +) -> bool { + let ctxt: &mut T = &mut *(ctxt as *mut T); + let inner_result = ctxt.get_type_tokens_before_name( + Type::ref_from_raw(type_), + match platform.is_null() { + false => Some(Platform::ref_from_raw(platform)), + true => None, + }, + base_confidence, + match parent_type.is_null() { + false => Some(Type::ref_from_raw(parent_type)), + true => None, + }, + 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; + true + } else { + *result = std::ptr::null_mut(); + *result_count = 0; + false + } +} + +unsafe extern "C" fn cb_get_type_tokens_after_name( + ctxt: *mut ::std::os::raw::c_void, + type_: *mut BNType, + platform: *mut BNPlatform, + base_confidence: u8, + parent_type: *mut BNType, + escaping: BNTokenEscapingType, + result: *mut *mut BNInstructionTextToken, + result_count: *mut usize, +) -> bool { + let ctxt: &mut T = &mut *(ctxt as *mut T); + let inner_result = ctxt.get_type_tokens_after_name( + Type::ref_from_raw(type_), + match platform.is_null() { + false => Some(Platform::ref_from_raw(platform)), + true => None, + }, + base_confidence, + match parent_type.is_null() { + false => Some(Type::ref_from_raw(parent_type)), + true => None, + }, + 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; + true + } else { + *result = std::ptr::null_mut(); + *result_count = 0; + false + } +} + +unsafe extern "C" fn cb_get_type_string( + ctxt: *mut ::std::os::raw::c_void, + type_: *mut BNType, + platform: *mut BNPlatform, + name: *mut BNQualifiedName, + escaping: BNTokenEscapingType, + result: *mut *mut ::std::os::raw::c_char, +) -> bool { + let ctxt: &mut T = &mut *(ctxt as *mut T); + let qualified_name = QualifiedName::from(*name); + let inner_result = ctxt.get_type_string( + Type::ref_from_raw(type_), + match platform.is_null() { + false => Some(Platform::ref_from_raw(platform)), + true => None, + }, + qualified_name, + escaping, + ); + if let Some(inner_result) = inner_result { + // NOTE: Dropped by `cb_free_string` + let raw_string = BnString::new(inner_result); + *result = raw_string.into_raw(); + true + } else { + *result = std::ptr::null_mut(); + false + } +} + +unsafe extern "C" fn cb_get_type_string_before_name( + ctxt: *mut ::std::os::raw::c_void, + type_: *mut BNType, + platform: *mut BNPlatform, + escaping: BNTokenEscapingType, + result: *mut *mut ::std::os::raw::c_char, +) -> bool { + let ctxt: &mut T = &mut *(ctxt as *mut T); + let inner_result = ctxt.get_type_string_before_name( + Type::ref_from_raw(type_), + match platform.is_null() { + false => Some(Platform::ref_from_raw(platform)), + true => None, + }, + escaping, + ); + if let Some(inner_result) = inner_result { + // NOTE: Dropped by `cb_free_string` + let raw_string = BnString::new(inner_result); + *result = raw_string.into_raw(); + true + } else { + *result = std::ptr::null_mut(); + false + } +} + +unsafe extern "C" fn cb_get_type_string_after_name( + ctxt: *mut ::std::os::raw::c_void, + type_: *mut BNType, + platform: *mut BNPlatform, + escaping: BNTokenEscapingType, + result: *mut *mut ::std::os::raw::c_char, +) -> bool { + let ctxt: &mut T = &mut *(ctxt as *mut T); + let inner_result = ctxt.get_type_string_after_name( + Type::ref_from_raw(type_), + match platform.is_null() { + false => Some(Platform::ref_from_raw(platform)), + true => None, + }, + escaping, + ); + if let Some(inner_result) = inner_result { + // NOTE: Dropped by `cb_free_string` + let raw_string = BnString::new(inner_result); + *result = raw_string.into_raw(); + true + } else { + *result = std::ptr::null_mut(); + false + } +} + +unsafe extern "C" fn cb_get_type_lines( + ctxt: *mut ::std::os::raw::c_void, + type_: *mut BNType, + types: *mut BNTypeContainer, + name: *mut BNQualifiedName, + padding_cols: ::std::os::raw::c_int, + collapsed: bool, + escaping: BNTokenEscapingType, + result: *mut *mut BNTypeDefinitionLine, + result_count: *mut usize, +) -> bool { + let ctxt: &mut T = &mut *(ctxt as *mut T); + let qualified_name = QualifiedName::from(*name); + let types_ptr = NonNull::new(types).unwrap(); + let types = TypeContainer::from_raw(types_ptr); + let inner_result = ctxt.get_type_lines( + Type::ref_from_raw(type_), + &types, + qualified_name, + padding_cols as isize, + collapsed, + 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(); + // NOTE: Dropped by `cb_free_lines` + *result = Box::leak(boxed_raw_lines).as_mut_ptr(); + true + } else { + *result = std::ptr::null_mut(); + *result_count = 0; + false + } +} + +unsafe extern "C" fn cb_print_all_types( + ctxt: *mut ::std::os::raw::c_void, + names: *mut BNQualifiedName, + types: *mut *mut BNType, + type_count: usize, + data: *mut BNBinaryView, + padding_cols: ::std::os::raw::c_int, + escaping: BNTokenEscapingType, + result: *mut *mut ::std::os::raw::c_char, +) -> bool { + let ctxt: &mut T = &mut *(ctxt as *mut T); + let raw_names = std::slice::from_raw_parts(names, type_count); + let names: Vec<_> = raw_names.iter().map(Into::into).collect(); + let raw_types = std::slice::from_raw_parts(types, type_count); + let types: Vec<_> = raw_types.iter().map(|&t| Type::ref_from_raw(t)).collect(); + let inner_result = ctxt.print_all_types( + names, + types, + BinaryView::ref_from_raw(data), + padding_cols as isize, + escaping, + ); + if let Some(inner_result) = inner_result { + // NOTE: Dropped by `cb_free_string` + let raw_string = BnString::new(inner_result); + *result = raw_string.into_raw(); + true + } else { + *result = std::ptr::null_mut(); + false + } +} + +unsafe extern "C" fn cb_free_string(_ctxt: *mut c_void, string: *mut c_char) { + // SAFETY: The returned string is just BnString + let _ = BnString::from_raw(string); +} + +unsafe extern "C" fn cb_free_tokens( + _ctxt: *mut ::std::os::raw::c_void, + tokens: *mut BNInstructionTextToken, + count: usize, +) { + let errors = std::ptr::slice_from_raw_parts_mut(tokens, count); + let _ = Box::from_raw(errors); +} + +unsafe extern "C" fn cb_free_lines( + _ctxt: *mut ::std::os::raw::c_void, + lines: *mut BNTypeDefinitionLine, + count: usize, +) { + let errors = std::ptr::slice_from_raw_parts_mut(lines, count); + let _ = Box::from_raw(errors); +} diff --git a/rust/src/types.rs b/rust/src/types.rs index f986a5d68..e27ddf73f 100644 --- a/rust/src/types.rs +++ b/rust/src/types.rs @@ -960,6 +960,23 @@ impl ToOwned for Type { } } +impl CoreArrayProvider for Type { + type Raw = *mut BNType; + type Context = (); + type Wrapped<'a> = &'a Self; +} + +unsafe impl CoreArrayProviderInner for Type { + unsafe fn free(raw: *mut Self::Raw, count: usize, _context: &Self::Context) { + BNFreeTypeList(raw, count) + } + + unsafe fn wrap_raw<'a>(raw: &'a Self::Raw, _context: &'a Self::Context) -> Self::Wrapped<'a> { + // TODO: This is assuming &'a Type is &*mut BNType + std::mem::transmute(raw) + } +} + // TODO: Remove this struct, or make it not a ZST with a terrible array provider. /// ZST used only for `Array`. pub struct ComponentReferencedType; @@ -1726,7 +1743,7 @@ impl From for BNStructureMember { impl CoreArrayProvider for StructureMember { type Raw = BNStructureMember; type Context = (); - type Wrapped<'a> = StructureMember; + type Wrapped<'a> = Self; } unsafe impl CoreArrayProviderInner for StructureMember { @@ -1815,6 +1832,11 @@ pub struct NamedTypeReference { } impl NamedTypeReference { + pub(crate) unsafe fn from_raw(handle: *mut BNNamedTypeReference) -> Self { + debug_assert!(!handle.is_null()); + Self { handle } + } + pub(crate) unsafe fn ref_from_raw(handle: *mut BNNamedTypeReference) -> Ref { debug_assert!(!handle.is_null()); Ref::new(Self { handle }) @@ -1825,12 +1847,12 @@ impl NamedTypeReference { /// You should not assign type ids yourself, that is the responsibility of the BinaryView /// implementation after your types have been added. Just make sure the names match up and /// the core will do the id stuff for you. - pub fn new(type_class: NamedTypeReferenceClass, name: QualifiedName) -> Ref { - let mut raw_name = BNQualifiedName::from(name); + pub fn new>(type_class: NamedTypeReferenceClass, name: T) -> Ref { + let mut raw_name = BNQualifiedName::from(name.into()); unsafe { Self::ref_from_raw(BNCreateNamedType( type_class, - std::ptr::null() as *const _, + std::ptr::null(), &mut raw_name, )) } @@ -1841,13 +1863,13 @@ impl NamedTypeReference { /// You should not assign type ids yourself: if you use this to reference a type you are going /// to create but have not yet created, you may run into problems when giving your types to /// a BinaryView. - pub fn new_with_id( + pub fn new_with_id, S: BnStrCompatible>( type_class: NamedTypeReferenceClass, type_id: S, - name: QualifiedName, + name: T, ) -> Ref { let type_id = type_id.into_bytes_with_nul(); - let mut raw_name = BNQualifiedName::from(name); + let mut raw_name = BNQualifiedName::from(name.into()); unsafe { Self::ref_from_raw(BNCreateNamedType( @@ -1923,9 +1945,9 @@ impl Debug for NamedTypeReference { // TODO: Document usage, specifically how to make a qualified name and why it exists. #[derive(Default, Debug, Clone, Hash, PartialEq, Eq, Ord, PartialOrd)] pub struct QualifiedName { - pub items: Vec, // TODO: Make this Option where default is "::". pub seperator: String, + pub items: Vec, } impl QualifiedName { diff --git a/rust/tests/demangler.rs b/rust/tests/demangler.rs index acaa0a87b..c2f47bf6e 100644 --- a/rust/tests/demangler.rs +++ b/rust/tests/demangler.rs @@ -20,13 +20,13 @@ fn test_demangler_simple(_session: &Session) { // Example LLVM-style mangled name let llvm_mangled = "_Z3fooi"; // "foo(int)" in LLVM mangling let llvm_demangled = demangle_llvm(llvm_mangled, true).unwrap(); - assert_eq!(llvm_demangled, vec!["foo(int)"]); + assert_eq!(llvm_demangled, "foo(int)".into()); // Example GNU-style mangled name let gnu_mangled = "_Z3bari"; // "bar(int)" in GNU mangling let (gnu_demangled_name, gnu_demangled_type) = demangle_gnu3(&placeholder_arch, gnu_mangled, true).unwrap(); - assert_eq!(gnu_demangled_name, vec!["bar"]); + assert_eq!(gnu_demangled_name, "bar".into()); // TODO: We check the type display because other means include things such as confidence which is hard to get 1:1 assert_eq!( gnu_demangled_type.unwrap().to_string(), @@ -37,7 +37,7 @@ fn test_demangler_simple(_session: &Session) { let msvc_mangled = "?baz@@YAHH@Z"; // "int __cdecl baz(int)" in MSVC mangling let (msvc_demangled_name, msvc_demangled_type) = demangle_ms(&placeholder_arch, msvc_mangled, true).unwrap(); - assert_eq!(msvc_demangled_name, vec!["baz"]); + assert_eq!(msvc_demangled_name, "baz".into()); // TODO: We check the type display because other means include things such as confidence which is hard to get 1:1 assert_eq!( msvc_demangled_type.unwrap().to_string(), diff --git a/rust/tests/typearchive.rs b/rust/tests/typearchive.rs index 2a179f108..0b0164955 100644 --- a/rust/tests/typearchive.rs +++ b/rust/tests/typearchive.rs @@ -19,7 +19,7 @@ fn empty_view() -> Ref { } #[rstest] -fn test_main_thread_different(_session: &Session) { +fn test_create_archive(_session: &Session) { let placeholder_platform = Platform::by_name("x86_64").expect("Failed to get platform"); let temp_dir = tempfile::tempdir().unwrap(); diff --git a/rust/tests/typecontainer.rs b/rust/tests/typecontainer.rs new file mode 100644 index 000000000..0457fe09f --- /dev/null +++ b/rust/tests/typecontainer.rs @@ -0,0 +1,127 @@ +use binaryninja::binaryview::{BinaryView, BinaryViewExt}; +use binaryninja::filemetadata::FileMetadata; +use binaryninja::headless::Session; +use binaryninja::platform::Platform; +use binaryninja::rc::Ref; +use binaryninja::types::Type; +use rstest::*; + +#[fixture] +#[once] +fn session() -> Session { + Session::new() +} + +#[fixture] +#[once] +fn platform() -> Ref { + // TODO: Because some behavior might be platform specific we might need to move this back into each test. + // TODO: See test_parse_type + Platform::by_name("windows-x86_64").expect("windows-x86_64 exists") +} + +#[fixture] +#[once] +fn empty_view() -> Ref { + BinaryView::from_data(&FileMetadata::new(), &[]).expect("Failed to create view") +} + +#[rstest] +fn test_types(_session: &Session, platform: &Platform) { + let type_container = platform.type_container(); + let types = type_container.types().unwrap(); + let types_len = types.len(); + // windows-x86_64 has a few thousand, not zero. + assert_ne!(types_len, 0); +} + +#[rstest] +fn test_type_id(_session: &Session, platform: &Platform) { + let type_container = platform.type_container(); + let type_ids = type_container.type_ids().unwrap(); + let first_type_id = type_ids.iter().next().unwrap(); + let found_type = type_container + .type_by_id(first_type_id) + .expect("Type ID not valid!"); + let found_type_name = type_container + .type_name(first_type_id) + .expect("Type name not found for Type ID"); + let found_type_for_type_name = type_container + .type_by_name(found_type_name) + .expect("Found type name not valid!"); + // These _should_ be the same type. + assert_eq!(found_type, found_type_for_type_name); +} + +#[rstest] +fn test_add_delete_type(_session: &Session, empty_view: &BinaryView) { + let view_type_container = empty_view.type_container(); + let test_type = Type::int(4, true); + assert!( + view_type_container.add_types([("mytype", test_type)]), + "Failed to add types!" + ); + let my_type_id = view_type_container + .type_id("mytype") + .expect("mytype not found"); + assert!( + view_type_container.delete_type(my_type_id), + "Type was deleted!" + ); + // There should be no type ids if the type was actually deleted + assert_eq!(view_type_container.type_ids().unwrap().len(), 0) +} + +#[rstest] +fn test_immutable_container(_session: &Session, platform: &Platform) { + // Platform type containers are immutable, so we shouldn't be able to delete/add/rename types. + let plat_type_container = platform.type_container(); + assert!( + !plat_type_container.is_mutable(), + "Platform should NOT be mutable!" + ); + let type_ids = plat_type_container.type_ids().unwrap(); + let first_type_id = type_ids.iter().next().unwrap(); + // Platform type containers are immutable so these should be false! + assert!( + !plat_type_container.delete_type(first_type_id), + "Type was deleted!" + ); + assert!( + !plat_type_container.add_types([("new_type", Type::int(4, true))]), + "Type was added!" + ); + assert!( + !plat_type_container.rename_type(first_type_id, "renamed_type"), + "Type was renamed!" + ); +} + +#[rstest] +fn test_parse_type(_session: &Session, platform: &Platform) { + let type_container = platform.type_container(); + // HANDLE will be pulled in from the platform, which is `windows-x86_64`. + let parsed_type = type_container + .parse_type_string("typedef HANDLE test;", false) + .map_err(|e| e.to_vec()) + .expect("Failed to parse type"); + assert_eq!(parsed_type.name, "test".into()); + assert_eq!(parsed_type.ty.to_string(), "HANDLE"); +} + +#[rstest] +fn test_container_lifetime(_session: &Session, platform: &Platform, empty_view: &BinaryView) { + let plat_type_container_0 = platform.type_container(); + let view_type_container_0 = empty_view.type_container(); + let test_type = Type::int(4, true); + view_type_container_0.add_types([("mytype", test_type)]); + let plat_types_0 = plat_type_container_0.types(); + let view_types_0 = view_type_container_0.types(); + let plat_type_container_1 = platform.type_container(); + let view_type_container_1 = empty_view.type_container(); + let plat_types_1 = plat_type_container_1.types(); + let view_types_1 = view_type_container_1.types(); + // If the types do not equal the container is being freed from the first set of calls. + assert_eq!(plat_types_0, plat_types_1); + assert_eq!(view_types_0, view_types_1); +} diff --git a/rust/tests/typeparser.rs b/rust/tests/typeparser.rs new file mode 100644 index 000000000..b86bb6808 --- /dev/null +++ b/rust/tests/typeparser.rs @@ -0,0 +1,85 @@ +use binaryninja::headless::Session; +use binaryninja::platform::Platform; +use binaryninja::typeparser::{CoreTypeParser, TypeParser, TypeParserError}; +use binaryninja::types::Type; +use binaryninjacore_sys::BNTypeParserErrorSeverity::ErrorSeverity; +use rstest::*; + +const TEST_TYPES: &str = r#" +typedef int int32_t; +typedef unsigned int uint32_t; +typedef float float_t; +typedef double double_t; +typedef char char_t; +typedef unsigned char uchar_t; +typedef short short_t; +typedef unsigned short ushort_t; +typedef long long_t; +typedef unsigned long ulong_t; +typedef long long longlong_t; +typedef unsigned long long ulonglong_t; +typedef void* void_ptr_t; +typedef int (*function_type)(int arg1, float arg2); +// This should be 2 types +typedef struct { + int a; + float b; +} struct_type; +"#; + +#[fixture] +#[once] +fn session() -> Session { + Session::new() +} + +#[rstest] +fn test_string_to_type(_session: &Session) { + let platform = Platform::by_name("windows-x86_64").expect("windows-x86_64 exists"); + let plat_type_container = platform.type_container(); + let parser = CoreTypeParser::default(); + let parsed_type = parser + .parse_type_string("int32_t", &platform, &plat_type_container) + .expect("Parsed int32_t"); + let test_type = Type::int(4, true); + assert_eq!(test_type, parsed_type.ty); +} + +#[rstest] +fn test_string_to_types(_session: &Session) { + let platform = Platform::by_name("windows-x86_64").expect("windows-x86_64 exists"); + let plat_type_container = platform.type_container(); + let parser = CoreTypeParser::default(); + let parsed_type = parser + .parse_types_from_source( + TEST_TYPES, + "test_file.h", + &platform, + &plat_type_container, + &[], + &[], + "", + ) + .expect("Parsed types"); + assert_eq!(14, parsed_type.types.len()); +} + +#[rstest] +fn test_parse_error(_session: &Session) { + let platform = Platform::by_name("windows-x86_64").expect("windows-x86_64 exists"); + let plat_type_container = platform.type_container(); + let parser = CoreTypeParser::default(); + let parser_error = parser + .parse_type_string("AAAAA", &platform, &plat_type_container) + .expect_err("Parsing should fail!"); + assert_eq!( + parser_error, + vec![TypeParserError { + severity: ErrorSeverity, + message: "a type specifier is required for all declarations".to_string(), + file_name: "string.hpp".to_string(), + line: 1, + column: 1 + }] + ); +} From 22a4b3ca8e4a816b9055c07a86d8fef7359ad18f Mon Sep 17 00:00:00 2001 From: Mason Reed Date: Thu, 9 Jan 2025 20:15:15 -0500 Subject: [PATCH 33/93] Oh your suppose to do this --- .github/workflows/rust.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index 5a16661d0..dc0b8bc64 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -24,4 +24,4 @@ jobs: - name: Rustfmt Check uses: actions-rust-lang/rustfmt@v1 with: - manifest-path: ./rust + manifest-path: ./rust/Cargo.toml From 0f4cff210db4ab9a61f0b0ac1148ac29d715f91f Mon Sep 17 00:00:00 2001 From: Mason Reed Date: Thu, 9 Jan 2025 20:16:40 -0500 Subject: [PATCH 34/93] Add workflow_dispatch trigger to rust.yml --- .github/workflows/rust.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index dc0b8bc64..dcab7247d 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -1,6 +1,7 @@ name: Rust CI on: + workflow_dispatch: push: paths: - 'rust/**' From 8817d934c4d71fee8b81707b41e2886f72a756eb Mon Sep 17 00:00:00 2001 From: Mason Reed Date: Sat, 11 Jan 2025 18:23:51 -0500 Subject: [PATCH 35/93] More rust fixes - Swapped some usage of raw 255 to MAX_CONFIDENCE, no one likes magic numbers - New InstructionTextToken API, complete with owned data, this still needs a lot of testing. - InstructionTextTokenKind describes a destructured InstructionTextToken, this should make token usage much clearer, some docs pending - Added some misc Default and Debug impls - Updated TypePrinter and architectures to use new InstructionTextToken API --- arch/msp430/src/architecture.rs | 138 ++-- arch/riscv/src/lib.rs | 29 +- plugins/dwarf/dwarfdump/src/lib.rs | 92 ++- plugins/warp/src/cache.rs | 3 +- plugins/warp/src/convert.rs | 4 +- plugins/warp/src/lib.rs | 4 +- rust/examples/flowgraph.rs | 28 +- rust/src/architecture.rs | 18 +- rust/src/binaryview.rs | 4 +- rust/src/disassembly.rs | 1198 +++++++++++++++++++--------- rust/src/flowgraph.rs | 20 +- rust/src/function.rs | 9 + rust/src/linearview.rs | 2 +- rust/src/tags.rs | 67 +- rust/src/typeprinter.rs | 137 ++-- 15 files changed, 1172 insertions(+), 581 deletions(-) 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); + } } From 12fe44d235be513d860b71b2a6d25ee64ff62c08 Mon Sep 17 00:00:00 2001 From: Mason Reed Date: Sat, 11 Jan 2025 19:01:55 -0500 Subject: [PATCH 36/93] Misc formatting changes --- rust/binaryninjacore-sys/build.rs | 6 +----- rust/src/disassembly.rs | 2 +- rust/src/typeprinter.rs | 2 +- 3 files changed, 3 insertions(+), 7 deletions(-) diff --git a/rust/binaryninjacore-sys/build.rs b/rust/binaryninjacore-sys/build.rs index 26ab55df2..800b8373f 100644 --- a/rust/binaryninjacore-sys/build.rs +++ b/rust/binaryninjacore-sys/build.rs @@ -63,11 +63,7 @@ fn main() { // Make a symlink "libbinaryninjacore.so" pointing to "libbinaryninjacore.so.1" if symlink_target.exists() && symlink_source.symlink_metadata().is_err() { use std::os::unix::fs; - fs::symlink( - symlink_target, - symlink_source, - ) - .expect("failed to create required symlink"); + fs::symlink(symlink_target, symlink_source).expect("failed to create required symlink"); } println!("cargo:rustc-link-search={}", out_dir); } diff --git a/rust/src/disassembly.rs b/rust/src/disassembly.rs index b34a01c6a..2a4db4f68 100644 --- a/rust/src/disassembly.rs +++ b/rust/src/disassembly.rs @@ -254,7 +254,7 @@ impl InstructionTextToken { kind, } } - + pub(crate) unsafe fn free_raw(raw: BNInstructionTextToken) { if !raw.text.is_null() { BNFreeString(raw.text); diff --git a/rust/src/typeprinter.rs b/rust/src/typeprinter.rs index 0f109e1d5..ffbd0d06e 100644 --- a/rust/src/typeprinter.rs +++ b/rust/src/typeprinter.rs @@ -594,7 +594,7 @@ impl From for BNTypeDefinitionLine { // 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) }, + 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 From 807526d2967f67c7f64eeacae2f03af5d39102ea Mon Sep 17 00:00:00 2001 From: Mason Reed Date: Sun, 12 Jan 2025 23:25:37 -0500 Subject: [PATCH 37/93] More rust cleanup - Fixed MANY memory leaks (most due to QualifiedName) - Made StructureBuilder more builder and less structure - Fixed CMakeLists.txt that were globbing entire api, resulting in 100 second slowdown on cmake generation - Added more Debug impl's - Added some more tests - Fixed TypeParserResult UB - Moved the From impls to blank impl for clarity, we have multiple different variants of core to rust for some structures, it should be explicit which one you are choosing. - PossibleValueSet should now be able to allocate so we can go from rust to core with those variants that require allocation - Misc doc code formatting --- arch/riscv/src/lib.rs | 2 +- plugins/dwarf/dwarf_export/CMakeLists.txt | 12 +- plugins/dwarf/dwarf_import/CMakeLists.txt | 12 +- .../dwarf/dwarf_import/src/die_handlers.rs | 4 +- .../dwarf/dwarf_import/src/dwarfdebuginfo.rs | 2 +- plugins/dwarf/dwarf_import/src/types.rs | 8 +- plugins/idb_import/CMakeLists.txt | 12 +- plugins/idb_import/src/types.rs | 18 +- plugins/pdb-ng/CMakeLists.txt | 12 +- plugins/pdb-ng/src/parser.rs | 12 +- plugins/pdb-ng/src/struct_grouper.rs | 8 +- plugins/pdb-ng/src/symbol_parser.rs | 8 +- plugins/pdb-ng/src/type_parser.rs | 68 +- plugins/warp/src/bin/sigem.rs | 6 + plugins/warp/src/convert.rs | 16 +- rust/examples/type_printer.rs | 38 + rust/src/architecture.rs | 93 +- rust/src/binaryview.rs | 87 +- rust/src/callingconvention.rs | 63 +- rust/src/confidence.rs | 119 ++- rust/src/debuginfo.rs | 90 +- rust/src/demangle.rs | 28 +- rust/src/disassembly.rs | 357 +++---- rust/src/flowgraph.rs | 7 +- rust/src/function.rs | 39 +- rust/src/headless.rs | 12 +- rust/src/interaction.rs | 4 +- rust/src/lib.rs | 5 +- rust/src/linearview.rs | 2 +- rust/src/logger.rs | 1 - rust/src/mlil/function.rs | 30 +- rust/src/mlil/instruction.rs | 48 +- rust/src/platform.rs | 37 +- rust/src/project.rs | 8 +- rust/src/rc.rs | 11 +- rust/src/string.rs | 6 +- rust/src/symbol.rs | 5 +- rust/src/templatesimplifier.rs | 2 +- rust/src/typearchive.rs | 152 +-- rust/src/typecontainer.rs | 48 +- rust/src/typelibrary.rs | 25 +- rust/src/typeparser.rs | 262 +++-- rust/src/typeprinter.rs | 195 ++-- rust/src/types.rs | 944 +++++++++--------- rust/src/variable.rs | 392 ++++---- rust/tests/platform.rs | 34 + rust/tests/typecontainer.rs | 10 +- 47 files changed, 1841 insertions(+), 1513 deletions(-) create mode 100644 rust/examples/type_printer.rs create mode 100644 rust/tests/platform.rs diff --git a/arch/riscv/src/lib.rs b/arch/riscv/src/lib.rs index f3db64e0f..fc5589ad3 100644 --- a/arch/riscv/src/lib.rs +++ b/arch/riscv/src/lib.rs @@ -264,7 +264,7 @@ impl PartialEq for Register { impl Eq for Register {} -impl fmt::Debug for Register { +impl fmt::Debug for Register { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { f.write_str(self.name().as_ref()) } diff --git a/plugins/dwarf/dwarf_export/CMakeLists.txt b/plugins/dwarf/dwarf_export/CMakeLists.txt index 5827c1d2c..fc54f2eef 100644 --- a/plugins/dwarf/dwarf_export/CMakeLists.txt +++ b/plugins/dwarf/dwarf_export/CMakeLists.txt @@ -9,12 +9,12 @@ file(GLOB PLUGIN_SOURCES CONFIGURE_DEPENDS ${PROJECT_SOURCE_DIR}/../shared/src/*.rs) file(GLOB_RECURSE API_SOURCES CONFIGURE_DEPENDS - ${PROJECT_SOURCE_DIR}/../../../../binaryninjacore.h - ${PROJECT_SOURCE_DIR}/../../../binaryninjacore-sys/build.rs - ${PROJECT_SOURCE_DIR}/../../../binaryninjacore-sys/Cargo.toml - ${PROJECT_SOURCE_DIR}/../../../binaryninjacore-sys/src/* - ${PROJECT_SOURCE_DIR}/../../../Cargo.toml - ${PROJECT_SOURCE_DIR}/../../../src/*.rs) + ${PROJECT_SOURCE_DIR}/../../binaryninjacore.h + ${PROJECT_SOURCE_DIR}/../../rust/binaryninjacore-sys/build.rs + ${PROJECT_SOURCE_DIR}/../../rust/binaryninjacore-sys/Cargo.toml + ${PROJECT_SOURCE_DIR}/../../rust/binaryninjacore-sys/src/* + ${PROJECT_SOURCE_DIR}/../../rust/Cargo.toml + ${PROJECT_SOURCE_DIR}/../../rust/src/*.rs) if(CMAKE_BUILD_TYPE MATCHES Debug) set(TARGET_DIR ${PROJECT_BINARY_DIR}/target/debug) diff --git a/plugins/dwarf/dwarf_import/CMakeLists.txt b/plugins/dwarf/dwarf_import/CMakeLists.txt index 9115b7a3e..480fc442b 100644 --- a/plugins/dwarf/dwarf_import/CMakeLists.txt +++ b/plugins/dwarf/dwarf_import/CMakeLists.txt @@ -9,12 +9,12 @@ file(GLOB PLUGIN_SOURCES CONFIGURE_DEPENDS ${PROJECT_SOURCE_DIR}/../shared/src/*.rs) file(GLOB_RECURSE API_SOURCES CONFIGURE_DEPENDS - ${PROJECT_SOURCE_DIR}/../../../../binaryninjacore.h - ${PROJECT_SOURCE_DIR}/../../../binaryninjacore-sys/build.rs - ${PROJECT_SOURCE_DIR}/../../../binaryninjacore-sys/Cargo.toml - ${PROJECT_SOURCE_DIR}/../../../binaryninjacore-sys/src/* - ${PROJECT_SOURCE_DIR}/../../../Cargo.toml - ${PROJECT_SOURCE_DIR}/../../../src/*.rs) + ${PROJECT_SOURCE_DIR}/../../binaryninjacore.h + ${PROJECT_SOURCE_DIR}/../../rust/binaryninjacore-sys/build.rs + ${PROJECT_SOURCE_DIR}/../../rust/binaryninjacore-sys/Cargo.toml + ${PROJECT_SOURCE_DIR}/../../rust/binaryninjacore-sys/src/* + ${PROJECT_SOURCE_DIR}/../../rust/Cargo.toml + ${PROJECT_SOURCE_DIR}/../../rust/src/*.rs) if(CMAKE_BUILD_TYPE MATCHES Debug) set(TARGET_DIR ${PROJECT_BINARY_DIR}/target/debug) diff --git a/plugins/dwarf/dwarf_import/src/die_handlers.rs b/plugins/dwarf/dwarf_import/src/die_handlers.rs index ea551bd5a..f7bcc22e6 100644 --- a/plugins/dwarf/dwarf_import/src/die_handlers.rs +++ b/plugins/dwarf/dwarf_import/src/die_handlers.rs @@ -306,7 +306,7 @@ pub(crate) fn handle_function( if let Some(name) = debug_info_builder_context.get_name(dwarf, unit, entry) { let ntr = Type::named_type_from_type( &name, - &Type::function(return_type.as_ref(), &[], false), + &Type::function(return_type.as_ref(), vec![], false), ); debug_info_builder.add_type( get_uid(dwarf, unit, entry), @@ -350,7 +350,7 @@ pub(crate) fn handle_function( Some(Type::function( return_type.as_ref(), - ¶meters, + parameters, variable_arguments, )) } diff --git a/plugins/dwarf/dwarf_import/src/dwarfdebuginfo.rs b/plugins/dwarf/dwarf_import/src/dwarfdebuginfo.rs index 0f66f5cbc..1d9a7c7e0 100644 --- a/plugins/dwarf/dwarf_import/src/dwarfdebuginfo.rs +++ b/plugins/dwarf/dwarf_import/src/dwarfdebuginfo.rs @@ -563,7 +563,7 @@ impl DebugInfoBuilder { }) .collect(); - Type::function(&return_type, ¶meters, function.variable_arguments) + Type::function(&return_type, parameters, function.variable_arguments) } fn commit_functions(&self, debug_info: &mut DebugInfo) { diff --git a/plugins/dwarf/dwarf_import/src/types.rs b/plugins/dwarf/dwarf_import/src/types.rs index 700df3da6..77b3fb647 100644 --- a/plugins/dwarf/dwarf_import/src/types.rs +++ b/plugins/dwarf/dwarf_import/src/types.rs @@ -132,11 +132,11 @@ fn do_structure_parse( // Create structure with proper size let size = get_size_as_u64(entry).unwrap_or(0); - let structure_builder: StructureBuilder = StructureBuilder::new(); + let mut structure_builder = StructureBuilder::new(); structure_builder - .set_packed(true) - .set_width(size) - .set_structure_type(structure_type); + .packed(true) + .width(size) + .structure_type(structure_type); // This reference type will be used by any children to grab while we're still building this type // it will also be how any other types refer to this struct diff --git a/plugins/idb_import/CMakeLists.txt b/plugins/idb_import/CMakeLists.txt index 5ae517162..6a6104d9c 100644 --- a/plugins/idb_import/CMakeLists.txt +++ b/plugins/idb_import/CMakeLists.txt @@ -7,12 +7,12 @@ file(GLOB_RECURSE PLUGIN_SOURCES CONFIGURE_DEPENDS ${PROJECT_SOURCE_DIR}/src/*.rs) file(GLOB_RECURSE API_SOURCES CONFIGURE_DEPENDS - ${PROJECT_SOURCE_DIR}/../../../binaryninjacore.h - ${PROJECT_SOURCE_DIR}/../../binaryninjacore-sys/build.rs - ${PROJECT_SOURCE_DIR}/../../binaryninjacore-sys/Cargo.toml - ${PROJECT_SOURCE_DIR}/../../binaryninjacore-sys/src/* - ${PROJECT_SOURCE_DIR}/../../Cargo.toml - ${PROJECT_SOURCE_DIR}/../../src/*.rs) + ${PROJECT_SOURCE_DIR}/../../binaryninjacore.h + ${PROJECT_SOURCE_DIR}/../../rust/binaryninjacore-sys/build.rs + ${PROJECT_SOURCE_DIR}/../../rust/binaryninjacore-sys/Cargo.toml + ${PROJECT_SOURCE_DIR}/../../rust/binaryninjacore-sys/src/* + ${PROJECT_SOURCE_DIR}/../../rust/Cargo.toml + ${PROJECT_SOURCE_DIR}/../../rust/src/*.rs) if(CMAKE_BUILD_TYPE MATCHES Debug) set(TARGET_DIR ${PROJECT_BINARY_DIR}/target/debug) diff --git a/plugins/idb_import/src/types.rs b/plugins/idb_import/src/types.rs index 547462146..b66b04067 100644 --- a/plugins/idb_import/src/types.rs +++ b/plugins/idb_import/src/types.rs @@ -251,7 +251,7 @@ impl Result<(), ()>> TranslateIDBTypes<'_, F> { bn_args.push(FunctionParameter::new(arg, name, loc)); } - let ty = Type::function(&return_ty, &bn_args, false); + let ty = Type::function(&return_ty, bn_args, false); if is_partial { let error = (errors.ret.is_some() || !errors.args.is_empty()) .then(|| BnTypeError::Function(errors)); @@ -284,7 +284,7 @@ impl Result<(), ()>> TranslateIDBTypes<'_, F> { &self, offset: usize, members_slice: &[TILStructMember], - struct_builder: &StructureBuilder, + struct_builder: &mut StructureBuilder, ) { if members_slice.is_empty() { unreachable!() @@ -301,7 +301,7 @@ impl Result<(), ()>> TranslateIDBTypes<'_, F> { let mut current_field_bits: u32 = first_field.width.into(); let mut start_idx = 0; - let create_field = |start_idx, i, bytes| { + let mut create_field = |start_idx, i, bytes| { let name = if start_idx == i - 1 { let member: &TILStructMember = &members_slice[i - 1]; member @@ -354,8 +354,8 @@ impl Result<(), ()>> TranslateIDBTypes<'_, F> { return TranslateTypeResult::Translated(Type::void()); } let mut is_partial = false; - let structure = StructureBuilder::new(); - structure.set_alignment(effective_alignment.into()); + let mut structure = StructureBuilder::new(); + structure.alignment(effective_alignment.into()); let mut errors = vec![]; let mut first_bitfield_seq = None; @@ -372,7 +372,7 @@ impl Result<(), ()>> TranslateIDBTypes<'_, F> { (_, Some(start_idx)) => { first_bitfield_seq = None; let members_bitrange = &members[start_idx..i]; - self.condensate_bitfields_from_struct(start_idx, members_bitrange, &structure); + self.condensate_bitfields_from_struct(start_idx, members_bitrange, &mut structure); } (_, None) => {} @@ -402,7 +402,7 @@ impl Result<(), ()>> TranslateIDBTypes<'_, F> { } if let Some(start_idx) = first_bitfield_seq { let members_bitrange = &members[start_idx..]; - self.condensate_bitfields_from_struct(start_idx, members_bitrange, &structure); + self.condensate_bitfields_from_struct(start_idx, members_bitrange, &mut structure); } let bn_ty = Type::structure(&structure.finalize()); if is_partial { @@ -420,8 +420,8 @@ impl Result<(), ()>> TranslateIDBTypes<'_, F> { _effective_alignment: u16, ) -> TranslateTypeResult { let mut is_partial = false; - let structure = StructureBuilder::new(); - structure.set_structure_type(StructureType::UnionStructureType); + let mut structure = StructureBuilder::new(); + structure.structure_type(StructureType::UnionStructureType); let mut errors = vec![]; for (i, (member_name, member_type)) in members.iter().enumerate() { // bitfields can be translated into complete fields diff --git a/plugins/pdb-ng/CMakeLists.txt b/plugins/pdb-ng/CMakeLists.txt index c88d4125d..d0282c1ae 100644 --- a/plugins/pdb-ng/CMakeLists.txt +++ b/plugins/pdb-ng/CMakeLists.txt @@ -7,12 +7,12 @@ file(GLOB PLUGIN_SOURCES CONFIGURE_DEPENDS ${PROJECT_SOURCE_DIR}/src/*.rs) file(GLOB_RECURSE API_SOURCES CONFIGURE_DEPENDS - ${PROJECT_SOURCE_DIR}/../../../binaryninjacore.h - ${PROJECT_SOURCE_DIR}/../../binaryninjacore-sys/build.rs - ${PROJECT_SOURCE_DIR}/../../binaryninjacore-sys/Cargo.toml - ${PROJECT_SOURCE_DIR}/../../binaryninjacore-sys/src/* - ${PROJECT_SOURCE_DIR}/../../Cargo.toml - ${PROJECT_SOURCE_DIR}/../../src/*.rs) + ${PROJECT_SOURCE_DIR}/../../binaryninjacore.h + ${PROJECT_SOURCE_DIR}/../../rust/binaryninjacore-sys/build.rs + ${PROJECT_SOURCE_DIR}/../../rust/binaryninjacore-sys/Cargo.toml + ${PROJECT_SOURCE_DIR}/../../rust/binaryninjacore-sys/src/* + ${PROJECT_SOURCE_DIR}/../../rust/Cargo.toml + ${PROJECT_SOURCE_DIR}/../../rust/src/*.rs) if(CMAKE_BUILD_TYPE MATCHES Debug) set(TARGET_DIR ${PROJECT_BINARY_DIR}/target/debug) diff --git a/plugins/pdb-ng/src/parser.rs b/plugins/pdb-ng/src/parser.rs index 931e731e4..fb1a6ad85 100644 --- a/plugins/pdb-ng/src/parser.rs +++ b/plugins/pdb-ng/src/parser.rs @@ -407,21 +407,21 @@ impl<'a, S: Source<'a> + 'a> PDBParserInstance<'a, S> { NamedTypeReferenceClass::ClassNamedTypeClass | NamedTypeReferenceClass::StructNamedTypeClass | NamedTypeReferenceClass::UnionNamedTypeClass => { - let structure = StructureBuilder::new(); + let mut structure = StructureBuilder::new(); match class { NamedTypeReferenceClass::ClassNamedTypeClass => { - structure.set_structure_type(StructureType::ClassStructureType); + structure.structure_type(StructureType::ClassStructureType); } NamedTypeReferenceClass::StructNamedTypeClass => { - structure.set_structure_type(StructureType::StructStructureType); + structure.structure_type(StructureType::StructStructureType); } NamedTypeReferenceClass::UnionNamedTypeClass => { - structure.set_structure_type(StructureType::UnionStructureType); + structure.structure_type(StructureType::UnionStructureType); } _ => {} } - structure.set_width(1); - structure.set_alignment(1); + structure.width(1); + structure.alignment(1); self.debug_info.add_type( &name, diff --git a/plugins/pdb-ng/src/struct_grouper.rs b/plugins/pdb-ng/src/struct_grouper.rs index eb4618b20..ffc1046b2 100644 --- a/plugins/pdb-ng/src/struct_grouper.rs +++ b/plugins/pdb-ng/src/struct_grouper.rs @@ -362,7 +362,7 @@ pub fn group_structure( warn!("{} Could not resolve structure groups: {}", name, e); for member in members { structure.insert( - &member.ty.clone(), + &member.ty, member.name.clone(), member.offset, false, @@ -391,7 +391,7 @@ fn apply_groups( if offset > member.offset { structure.insert( - &member.ty.clone(), + &member.ty, member.name.clone(), 0, false, @@ -400,7 +400,7 @@ fn apply_groups( ); } else { structure.insert( - &member.ty.clone(), + &member.ty, member.name.clone(), member.offset - offset, false, @@ -423,7 +423,7 @@ fn apply_groups( } ResolvedGroup::Union(inner_offset, children) => { let mut inner = StructureBuilder::new(); - inner.set_structure_type(StructureType::UnionStructureType); + inner.structure_type(StructureType::UnionStructureType); apply_groups(members, &mut inner, children, inner_offset); structure.insert( &Conf::new(Type::structure(inner.finalize().as_ref()), MAX_CONFIDENCE), diff --git a/plugins/pdb-ng/src/symbol_parser.rs b/plugins/pdb-ng/src/symbol_parser.rs index 4dee8560d..5bf636f80 100644 --- a/plugins/pdb-ng/src/symbol_parser.rs +++ b/plugins/pdb-ng/src/symbol_parser.rs @@ -1078,7 +1078,7 @@ impl<'a, S: Source<'a> + 'a> PDBParserInstance<'a, S> { .ok_or(anyhow!("no ret"))?, fancy_params.as_slice(), fancy_type.contents.has_variable_arguments().contents, - &cc, + cc, fancy_type.contents.stack_adjustment(), ), MAX_CONFIDENCE, @@ -1801,11 +1801,11 @@ impl<'a, S: Source<'a> + 'a> PDBParserInstance<'a, S> { if let [p] = parameters.as_slice() { if p.ty.contents.type_class() == TypeClass::VoidTypeClass { t = Some(Conf::new( - Type::function::<_>( + Type::function( &ty.contents .return_value() .ok_or(anyhow!("no return value"))?, - &[], + vec![], ty.contents.has_variable_arguments().contents, ), ty.confidence, @@ -2022,7 +2022,7 @@ impl<'a, S: Source<'a> + 'a> PDBParserInstance<'a, S> { } // Make a new copy of the type with the correct element count - last_member.ty.contents = Type::array(member_element.as_ref(), element_count); + last_member.ty.contents = Type::array(&member_element, element_count); Ok(Some(( Type::structure(StructureBuilder::from(members).finalize().as_ref()), diff --git a/plugins/pdb-ng/src/type_parser.rs b/plugins/pdb-ng/src/type_parser.rs index 627c20583..1af4bb84b 100644 --- a/plugins/pdb-ng/src/type_parser.rs +++ b/plugins/pdb-ng/src/type_parser.rs @@ -373,7 +373,7 @@ impl<'a, S: Source<'a> + 'a> PDBParserInstance<'a, S> { let bare_type = match type_class { NamedTypeReferenceClass::ClassNamedTypeClass => Type::structure( StructureBuilder::new() - .set_structure_type(StructureType::ClassStructureType) + .structure_type(StructureType::ClassStructureType) .finalize() .as_ref(), ), @@ -385,7 +385,7 @@ impl<'a, S: Source<'a> + 'a> PDBParserInstance<'a, S> { } NamedTypeReferenceClass::UnionNamedTypeClass => Type::structure( StructureBuilder::new() - .set_structure_type(StructureType::UnionStructureType) + .structure_type(StructureType::UnionStructureType) .finalize() .as_ref(), ), @@ -713,31 +713,31 @@ impl<'a, S: Source<'a> + 'a> PDBParserInstance<'a, S> { match data.indirection { Some(Indirection::Near16) => Ok(Some(Box::new(ParsedType::Bare(Type::pointer( &self.arch, - base.as_ref(), + &base, ))))), Some(Indirection::Far16) => Ok(Some(Box::new(ParsedType::Bare(Type::pointer( &self.arch, - base.as_ref(), + &base, ))))), Some(Indirection::Huge16) => Ok(Some(Box::new(ParsedType::Bare(Type::pointer( &self.arch, - base.as_ref(), + &base, ))))), Some(Indirection::Near32) => Ok(Some(Box::new(ParsedType::Bare(Type::pointer( &self.arch, - base.as_ref(), + &base, ))))), Some(Indirection::Far32) => Ok(Some(Box::new(ParsedType::Bare(Type::pointer( &self.arch, - base.as_ref(), + &base, ))))), Some(Indirection::Near64) => Ok(Some(Box::new(ParsedType::Bare(Type::pointer( &self.arch, - base.as_ref(), + &base, ))))), Some(Indirection::Near128) => Ok(Some(Box::new(ParsedType::Bare(Type::pointer( &self.arch, - base.as_ref(), + &base, ))))), None => Ok(Some(Box::new(ParsedType::Bare(base)))), } @@ -780,9 +780,9 @@ impl<'a, S: Source<'a> + 'a> PDBParserInstance<'a, S> { }; let mut structure = StructureBuilder::new(); - structure.set_structure_type(struct_kind); - structure.set_width(data.size); - structure.set_packed(data.properties.packed()); + structure.structure_type(struct_kind); + structure.width(data.size); + structure.packed(data.properties.packed()); if let Some(fields) = data.fields { self.namespace_stack.push(class_name.to_string()); @@ -876,7 +876,7 @@ impl<'a, S: Source<'a> + 'a> PDBParserInstance<'a, S> { match (m.bitfield_position, m.bitfield_size) { (Some(pos), Some(_size)) => { if last_bitfield_offset != m.offset || last_bitfield_pos >= pos { - if let Some(builder) = bitfield_builder.take() { + if let Some(mut builder) = bitfield_builder.take() { combined_bitfield_members.push(ParsedMember { ty: Conf::new( Type::structure(builder.finalize().as_ref()), @@ -890,9 +890,9 @@ impl<'a, S: Source<'a> + 'a> PDBParserInstance<'a, S> { bitfield_position: None, }); } - let new_builder = StructureBuilder::new(); - new_builder.set_structure_type(StructureType::UnionStructureType); - new_builder.set_width(m.ty.contents.width()); + let mut new_builder = StructureBuilder::new(); + new_builder.structure_type(StructureType::UnionStructureType); + new_builder.width(m.ty.contents.width()); bitfield_builder = Some(new_builder); if last_bitfield_offset != m.offset { @@ -910,7 +910,7 @@ impl<'a, S: Source<'a> + 'a> PDBParserInstance<'a, S> { .insert(&m.ty, m.name, 0, false, m.access, m.scope); } (None, None) => { - if let Some(builder) = bitfield_builder.take() { + if let Some(mut builder) = bitfield_builder.take() { combined_bitfield_members.push(ParsedMember { ty: Conf::new( Type::structure(builder.finalize().as_ref()), @@ -931,7 +931,7 @@ impl<'a, S: Source<'a> + 'a> PDBParserInstance<'a, S> { e => return Err(anyhow!("Unexpected bitfield parameters {:?}", e)), } } - if let Some(builder) = bitfield_builder.take() { + if let Some(mut builder) = bitfield_builder.take() { combined_bitfield_members.push(ParsedMember { ty: Conf::new( Type::structure(builder.finalize().as_ref()), @@ -1041,14 +1041,14 @@ impl<'a, S: Source<'a> + 'a> PDBParserInstance<'a, S> { .ok_or_else(|| anyhow!("Expected class in ns stack"))? ); } - structure.set_base_structures(&bases); + structure.base_structures(&bases); if self .settings .get_bool("pdb.features.generateVTables", Some(self.bv), None) && !virt_methods.is_empty() { - let vt = StructureBuilder::new(); + let mut vt = StructureBuilder::new(); let mut vt_bases = vec![]; @@ -1106,8 +1106,8 @@ impl<'a, S: Source<'a> + 'a> PDBParserInstance<'a, S> { min_width = min_width.max(base.width); } - vt.set_base_structures(&vt_bases); - vt.set_propagates_data_var_refs(true); + vt.base_structures(&vt_bases); + vt.propagates_data_var_refs(true); for (offset, (name, method)) in virt_methods { vt.insert( @@ -1124,7 +1124,7 @@ impl<'a, S: Source<'a> + 'a> PDBParserInstance<'a, S> { min_width = min_width.max((offset + self.arch.address_size()) as u64); } - vt.set_width(min_width); + vt.width(min_width); let vt_type = Type::structure(vt.finalize().as_ref()); // Need to insert a new named type for the vtable @@ -1289,7 +1289,7 @@ impl<'a, S: Source<'a> + 'a> PDBParserInstance<'a, S> { &Conf::new(return_type, MAX_CONFIDENCE), arguments.as_slice(), is_varargs, - &convention, + convention.clone(), Conf::new(0, 0), ); @@ -1297,7 +1297,7 @@ impl<'a, S: Source<'a> + 'a> PDBParserInstance<'a, S> { &Conf::new(fancy_return_type, MAX_CONFIDENCE), fancy_arguments.as_slice(), is_varargs, - &convention, + convention, Conf::new(0, 0), ); @@ -1536,7 +1536,7 @@ impl<'a, S: Source<'a> + 'a> PDBParserInstance<'a, S> { if return_stacky { // Stack return via a pointer in the first parameter fancy_return_type = - Conf::new(Type::pointer(&self.arch, &return_type), MAX_CONFIDENCE); + Conf::new(Type::pointer(&self.arch, &return_type.clone()), MAX_CONFIDENCE); fancy_arguments.insert( 0, FunctionParameter::new(fancy_return_type.clone(), "__return".to_string(), None), @@ -1553,7 +1553,7 @@ impl<'a, S: Source<'a> + 'a> PDBParserInstance<'a, S> { &return_type, arguments.as_slice(), is_varargs, - &convention, + convention.clone(), Conf::new(0, 0), ); @@ -1561,7 +1561,7 @@ impl<'a, S: Source<'a> + 'a> PDBParserInstance<'a, S> { &fancy_return_type, fancy_arguments.as_slice(), is_varargs, - &convention, + convention, Conf::new(0, 0), ); @@ -1592,7 +1592,7 @@ impl<'a, S: Source<'a> + 'a> PDBParserInstance<'a, S> { if let Some(base) = base { Ok(Some(Box::new(ParsedType::Bare(Type::pointer( &self.arch, - base.as_ref(), + &base, ))))) } else { Ok(None) @@ -1736,7 +1736,7 @@ impl<'a, S: Source<'a> + 'a> PDBParserInstance<'a, S> { counts[j] /= new_type.width(); } - new_type = Type::array(new_type.as_ref(), counts[i] as u64); + new_type = Type::array(&new_type, counts[i] as u64); } Ok(Some(Box::new(ParsedType::Bare(new_type)))) @@ -1771,8 +1771,8 @@ impl<'a, S: Source<'a> + 'a> PDBParserInstance<'a, S> { } let mut structure = StructureBuilder::new(); - structure.set_structure_type(StructureType::UnionStructureType); - structure.set_width(data.size); + structure.structure_type(StructureType::UnionStructureType); + structure.width(data.size); self.namespace_stack.push(union_name.to_string()); let success = self.parse_union_fields(&mut structure, data.fields, finder); @@ -1827,7 +1827,7 @@ impl<'a, S: Source<'a> + 'a> PDBParserInstance<'a, S> { group[0].scope, ); } else { - let inner_struct = StructureBuilder::new(); + let mut inner_struct = StructureBuilder::new(); for member in group { inner_struct.insert( &member.ty, @@ -1928,7 +1928,7 @@ impl<'a, S: Source<'a> + 'a> PDBParserInstance<'a, S> { } else { args.push(FunctionParameter::new( Conf::new( - Type::pointer(self.arch.as_ref(), ty.as_ref()), + Type::pointer(self.arch.as_ref(), &ty), MAX_CONFIDENCE, ), "".to_string(), diff --git a/plugins/warp/src/bin/sigem.rs b/plugins/warp/src/bin/sigem.rs index 6076e7d5c..ed70507e7 100644 --- a/plugins/warp/src/bin/sigem.rs +++ b/plugins/warp/src/bin/sigem.rs @@ -47,6 +47,8 @@ struct Args { /// The external debug information file to use #[arg(short, long)] debug_info: Option, + + // TODO: Add a file filter and default to filter out files starting with "." } fn default_settings(bn_settings: &Settings) -> Value { @@ -75,6 +77,10 @@ fn main() { let args = Args::parse(); env_logger::Builder::from_env(env_logger::Env::default().default_filter_or("info")).init(); + // TODO: After analysis finishes for a file we should save off the bndb to another directory called the bndb cache + // TODO: This cache should be used before opening a file for first analysis. + + // TODO: We should resolve the path to something sensible in cases where user is passing CWD. // If no output file was given, just prepend binary with extension sbin let output_file = args .output diff --git a/plugins/warp/src/convert.rs b/plugins/warp/src/convert.rs index b5f935a36..2f76031ca 100644 --- a/plugins/warp/src/convert.rs +++ b/plugins/warp/src/convert.rs @@ -401,7 +401,7 @@ pub fn to_bn_type(arch: &A, ty: &Type) -> BNRef { BNType::array(&member_type, c.length.unwrap_or(0)) } TypeClass::Structure(c) => { - let builder = BNStructureBuilder::new(); + let mut builder = BNStructureBuilder::new(); // TODO: Structure type class? // TODO: Alignment // TODO: Other modifiers? @@ -457,7 +457,7 @@ pub fn to_bn_type(arch: &A, ty: &Type) -> BNRef { } } else { builder.insert_member( - &BNStructureMember::new( + BNStructureMember::new( member_type, member_name, member_offset, @@ -468,7 +468,7 @@ pub fn to_bn_type(arch: &A, ty: &Type) -> BNRef { ); } } - builder.set_base_structures(&base_structs); + builder.base_structures(&base_structs); BNType::structure(&builder.finalize()) } TypeClass::Enumeration(c) => { @@ -486,8 +486,8 @@ pub fn to_bn_type(arch: &A, ty: &Type) -> BNRef { BNType::enumeration(&builder.finalize(), width.try_into().unwrap(), signed) } TypeClass::Union(c) => { - let builder = BNStructureBuilder::new(); - builder.set_structure_type(BNStructureType::UnionStructureType); + let mut builder = BNStructureBuilder::new(); + builder.structure_type(BNStructureType::UnionStructureType); for member in &c.members { let member_type = BNConf::new(to_bn_type(arch, &member.ty), u8::MAX); let member_name = member.name.to_owned(); @@ -502,7 +502,7 @@ pub fn to_bn_type(arch: &A, ty: &Type) -> BNRef { member_access, member_scope, ); - builder.insert_member(&structure_member, false); + builder.insert_member(structure_member, false); } BNType::structure(&builder.finalize()) } @@ -533,11 +533,11 @@ pub fn to_bn_type(arch: &A, ty: &Type) -> BNRef { &return_type, ¶ms, variable_args, - &BNConf::new(calling_convention, u8::MAX), + BNConf::new(calling_convention, u8::MAX), BNConf::new(0, 0), ) } - None => BNType::function(&return_type, ¶ms, variable_args), + None => BNType::function(&return_type, params, variable_args), } } TypeClass::Referrer(c) => { diff --git a/rust/examples/type_printer.rs b/rust/examples/type_printer.rs new file mode 100644 index 000000000..2625a8b75 --- /dev/null +++ b/rust/examples/type_printer.rs @@ -0,0 +1,38 @@ +use binaryninja::typeprinter::{CoreTypePrinter, TokenEscapingType}; +use binaryninja::types::{MemberAccess, MemberScope, Structure, StructureMember, Type}; + +fn main() { + println!("Starting session..."); + // This loads all the core architecture, platform, etc plugins + let headless_session = binaryninja::headless::Session::new(); + + println!("Loading binary..."); + let bv = headless_session + .load("/bin/cat") + .expect("Couldn't open `/bin/cat`"); + + let type_printer = CoreTypePrinter::default(); + let my_structure = Type::structure( + &Structure::builder() + .insert_member( + StructureMember::new( + Type::int(4, false).into(), + "my_field".to_string(), + 0, + MemberAccess::PublicAccess, + MemberScope::NoScope, + ), + false, + ) + .finalize(), + ); + + let printed_types = type_printer.print_all_types( + [("my_struct", my_structure)], + &bv, + 4, + TokenEscapingType::NoTokenEscapingType, + ); + + println!("{}", printed_types.unwrap()); +} diff --git a/rust/src/architecture.rs b/rust/src/architecture.rs index 588901248..43a41bab5 100644 --- a/rust/src/architecture.rs +++ b/rust/src/architecture.rs @@ -17,7 +17,7 @@ // container abstraction to avoid Vec<> (want CoreArchFlagList, CoreArchRegList) // RegisterInfo purge use binaryninjacore_sys::*; -use std::fmt::Formatter; +use std::fmt::{Debug, Formatter}; use crate::{ callingconvention::CallingConvention, @@ -280,7 +280,7 @@ pub trait RegisterInfo: Sized { fn implicit_extend(&self) -> ImplicitRegisterExtend; } -pub trait Register: Sized + Clone + Copy + Hash + Eq { +pub trait Register: Debug + Sized + Clone + Copy + Hash + Eq { type InfoType: RegisterInfo; fn name(&self) -> Cow; @@ -849,6 +849,14 @@ impl Register for CoreRegister { } } +impl Debug for CoreRegister { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + f.debug_struct("CoreRegister") + .field("id", &self.id) + .finish() + } +} + impl CoreArrayProvider for CoreRegister { type Raw = u32; type Context = CoreArchitecture; @@ -1320,8 +1328,7 @@ impl Intrinsic for CoreIntrinsic { let ret = std::slice::from_raw_parts_mut(inputs, count) .iter() - .copied() - .map(Into::into) + .map(NameAndType::from_raw) .collect(); BNFreeNameAndTypeList(inputs, count); @@ -1338,7 +1345,7 @@ impl Intrinsic for CoreIntrinsic { let ret = std::slice::from_raw_parts_mut(inputs, count) .iter() - .map(|input| (*input).into()) + .map(Conf::>::from_raw) .collect(); BNFreeOutputTypeList(inputs, count); @@ -1481,7 +1488,7 @@ impl Architecture for CoreArchitecture { ) { let instr_text_tokens = std::slice::from_raw_parts(result, count) .iter() - .map(Into::into) + .map(InstructionTextToken::from_raw) .collect(); BNFreeInstructionText(result, count); Some((consumed, instr_text_tokens)) @@ -2156,8 +2163,10 @@ where return false; }; - let res_tokens: Box<[BNInstructionTextToken]> = - res_tokens.into_iter().map(Into::into).collect(); + let res_tokens: Box<[BNInstructionTextToken]> = res_tokens + .into_iter() + .map(InstructionTextToken::into_raw) + .collect(); unsafe { // NOTE: Freed with `cb_free_instruction_text` let res_tokens = Box::leak(res_tokens); @@ -2171,7 +2180,10 @@ where extern "C" fn cb_free_instruction_text(tokens: *mut BNInstructionTextToken, count: usize) { unsafe { let raw_tokens = std::slice::from_raw_parts_mut(tokens, count); - let _ = Box::from_raw(raw_tokens); + let boxed_tokens = Box::from_raw(raw_tokens); + for token in boxed_tokens { + InstructionTextToken::free_raw(token); + } } } @@ -2209,8 +2221,8 @@ where let custom_arch = unsafe { &*(ctxt as *mut A) }; match custom_arch.register_from_id(reg.into()) { - Some(reg) => BnString::new(reg.name().as_ref()).into_raw(), - None => BnString::new("invalid_reg").into_raw(), + Some(reg) => BnString::into_raw(BnString::new(reg.name().as_ref())), + None => BnString::into_raw(BnString::new("invalid_reg")), } } @@ -2221,8 +2233,8 @@ where let custom_arch = unsafe { &*(ctxt as *mut A) }; match custom_arch.flag_from_id(flag.into()) { - Some(flag) => BnString::new(flag.name().as_ref()).into_raw(), - None => BnString::new("invalid_flag").into_raw(), + Some(flag) => BnString::into_raw(BnString::new(flag.name().as_ref())), + None => BnString::into_raw(BnString::new("invalid_flag")), } } @@ -2233,8 +2245,8 @@ where let custom_arch = unsafe { &*(ctxt as *mut A) }; match custom_arch.flag_write_from_id(flag_write.into()) { - Some(flag_write) => BnString::new(flag_write.name().as_ref()).into_raw(), - None => BnString::new("invalid_flag_write").into_raw(), + Some(flag_write) => BnString::into_raw(BnString::new(flag_write.name().as_ref())), + None => BnString::into_raw(BnString::new("invalid_flag_write")), } } @@ -2245,8 +2257,8 @@ where let custom_arch = unsafe { &*(ctxt as *mut A) }; match custom_arch.flag_class_from_id(class.into()) { - Some(class) => BnString::new(class.name().as_ref()).into_raw(), - None => BnString::new("invalid_flag_class").into_raw(), + Some(class) => BnString::into_raw(BnString::new(class.name().as_ref())), + None => BnString::into_raw(BnString::new("invalid_flag_class")), } } @@ -2257,8 +2269,8 @@ where let custom_arch = unsafe { &*(ctxt as *mut A) }; match custom_arch.flag_group_from_id(group.into()) { - Some(group) => BnString::new(group.name().as_ref()).into_raw(), - None => BnString::new("invalid_flag_group").into_raw(), + Some(group) => BnString::into_raw(BnString::new(group.name().as_ref())), + None => BnString::into_raw(BnString::new("invalid_flag_group")), } } @@ -2732,8 +2744,8 @@ where let custom_arch = unsafe { &*(ctxt as *mut A) }; match custom_arch.register_stack_from_id(RegisterStackId(stack)) { - Some(stack) => BnString::new(stack.name().as_ref()).into_raw(), - None => BnString::new("invalid_reg_stack").into_raw(), + Some(stack) => BnString::into_raw(BnString::new(stack.name().as_ref())), + None => BnString::into_raw(BnString::new("invalid_reg_stack")), } } @@ -2802,8 +2814,8 @@ where { let custom_arch = unsafe { &*(ctxt as *mut A) }; match custom_arch.intrinsic_from_id(IntrinsicId(intrinsic)) { - Some(intrinsic) => BnString::new(intrinsic.name()).into_raw(), - None => BnString::new("invalid_intrinsic").into_raw(), + Some(intrinsic) => BnString::into_raw(BnString::new(intrinsic.name())), + None => BnString::into_raw(BnString::new("invalid_intrinsic")), } } @@ -2840,7 +2852,8 @@ where }; let inputs = intrinsic.inputs(); - let mut raw_inputs: Box<[_]> = inputs.into_iter().map(Into::into).collect(); + // NOTE: The into_raw will leak and be freed later by `cb_free_name_and_types`. + let raw_inputs: Box<[_]> = inputs.into_iter().map(NameAndType::into_raw).collect(); // SAFETY: Passed in to be written unsafe { @@ -2850,10 +2863,8 @@ where if raw_inputs.is_empty() { std::ptr::null_mut() } else { - let raw_ptr = raw_inputs.as_mut_ptr(); // Core is responsible for calling back to `cb_free_name_and_types`. - std::mem::forget(raw_inputs); - raw_ptr + Box::leak(raw_inputs).as_mut_ptr() } } @@ -2871,7 +2882,10 @@ where // Reconstruct the box and drop. let nt_ptr = std::ptr::slice_from_raw_parts_mut(nt, count); // SAFETY: nt_ptr is a pointer to a Box. - let _ = unsafe { Box::from_raw(nt_ptr) }; + let boxed_name_and_types = unsafe { Box::from_raw(nt_ptr) }; + for nt in boxed_name_and_types { + NameAndType::free_raw(nt); + } } extern "C" fn cb_intrinsic_outputs( @@ -2893,8 +2907,11 @@ where }; let outputs = intrinsic.outputs(); - let mut raw_outputs: Box<[BNTypeWithConfidence]> = - outputs.into_iter().map(|o| o.as_ref().into()).collect(); + let raw_outputs: Box<[BNTypeWithConfidence]> = outputs + .into_iter() + // Leaked to be freed later by `cb_free_type_list`. + .map(Conf::>::into_raw) + .collect(); // SAFETY: Passed in to be written unsafe { @@ -2904,10 +2921,8 @@ where if raw_outputs.is_empty() { std::ptr::null_mut() } else { - let raw_ptr = raw_outputs.as_mut_ptr(); - // Core is responsible for calling back to `cb_free_name_and_types`. - std::mem::forget(raw_outputs); - raw_ptr + // Core is responsible for calling back to `cb_free_type_list`. + Box::leak(raw_outputs).as_mut_ptr() } } @@ -2919,9 +2934,9 @@ where A: 'static + Architecture> + Send + Sync, { let _custom_arch = unsafe { &*(ctxt as *mut A) }; - if !tl.is_null() { - let _type_list = - unsafe { Box::from_raw(std::ptr::slice_from_raw_parts_mut(tl, count)) }; + let boxed_types = unsafe { Box::from_raw(std::ptr::slice_from_raw_parts_mut(tl, count)) }; + for ty in boxed_types { + Conf::>::free_raw(ty); } } @@ -2951,13 +2966,13 @@ where Ok(result) => { buffer.set_data(&result); unsafe { - *errors = BnString::new("").into_raw(); + *errors = BnString::into_raw(BnString::new("")); } true } Err(result) => { unsafe { - *errors = BnString::new(result).into_raw(); + *errors = BnString::into_raw(BnString::new(result)); } false } diff --git a/rust/src/binaryview.rs b/rust/src/binaryview.rs index 47987f139..7909e8d5e 100644 --- a/rust/src/binaryview.rs +++ b/rust/src/binaryview.rs @@ -557,7 +557,7 @@ pub trait BinaryViewExt: BinaryViewBase { let mut dv = BNDataVariable::default(); unsafe { if BNGetDataVariableAtAddress(self.as_ref().handle, addr, &mut dv) { - Some(DataVariable::from(dv)) + Some(DataVariable::from_owned_raw(dv)) } else { None } @@ -565,15 +565,17 @@ pub trait BinaryViewExt: BinaryViewBase { } fn define_auto_data_var<'a, T: Into>>(&self, addr: u64, ty: T) { + let mut owned_raw_ty = Conf::<&Type>::into_raw(ty.into()); unsafe { - BNDefineDataVariable(self.as_ref().handle, addr, &mut ty.into().into()); + BNDefineDataVariable(self.as_ref().handle, addr, &mut owned_raw_ty); } } /// You likely would also like to call [`Self::define_user_symbol`] to bind this data variable with a name fn define_user_data_var<'a, T: Into>>(&self, addr: u64, ty: T) { + let mut owned_raw_ty = Conf::<&Type>::into_raw(ty.into()); unsafe { - BNDefineUserDataVariable(self.as_ref().handle, addr, &mut ty.into().into()); + BNDefineUserDataVariable(self.as_ref().handle, addr, &mut owned_raw_ty); } } @@ -595,14 +597,15 @@ pub trait BinaryViewExt: BinaryViewBase { source: S, type_obj: &Type, ) -> QualifiedName { - let mut raw_name = BNQualifiedName::from(name.into()); + let mut raw_name = QualifiedName::into_raw(name.into()); let source_str = source.into_bytes_with_nul(); let name_handle = unsafe { let id_str = BNGenerateAutoTypeId(source_str.as_ref().as_ptr() as *const _, &mut raw_name); BNDefineAnalysisType(self.as_ref().handle, id_str, &mut raw_name, type_obj.handle) }; - QualifiedName::from(name_handle) + QualifiedName::free_raw(raw_name); + QualifiedName::from_owned_raw(name_handle) } fn define_auto_type_with_id, S: BnStrCompatible>( @@ -611,7 +614,7 @@ pub trait BinaryViewExt: BinaryViewBase { id: S, type_obj: &Type, ) -> QualifiedName { - let mut raw_name = BNQualifiedName::from(name.into()); + let mut raw_name = QualifiedName::into_raw(name.into()); let id_str = id.into_bytes_with_nul(); let result_raw_name = unsafe { BNDefineAnalysisType( @@ -621,12 +624,14 @@ pub trait BinaryViewExt: BinaryViewBase { type_obj.handle, ) }; - QualifiedName::from(result_raw_name) + QualifiedName::free_raw(raw_name); + QualifiedName::from_owned_raw(result_raw_name) } fn define_user_type>(&self, name: T, type_obj: &Type) { - let mut raw_name = BNQualifiedName::from(name.into()); + let mut raw_name = QualifiedName::into_raw(name.into()); unsafe { BNDefineUserAnalysisType(self.as_ref().handle, &mut raw_name, type_obj.handle) } + QualifiedName::free_raw(raw_name); } fn define_auto_types(&self, names_sources_and_types: T) -> HashMap @@ -648,7 +653,7 @@ pub trait BinaryViewExt: BinaryViewBase { { let mut types: Vec = names_sources_and_types .map(Into::into) - .map(Into::into) + .map(QualifiedNameTypeAndId::into_raw) .collect(); let mut progress_raw = ProgressContext(progress); let mut result_ids: *mut *mut c_char = std::ptr::null_mut(); @@ -665,6 +670,10 @@ pub trait BinaryViewExt: BinaryViewBase { ) }; + for ty in types { + QualifiedNameTypeAndId::free_raw(ty); + } + let id_array = unsafe { Array::::new(result_ids, result_count, ()) }; let name_array = unsafe { Array::::new(result_names, result_count, ()) }; id_array @@ -690,8 +699,10 @@ pub trait BinaryViewExt: BinaryViewBase { T: Iterator, I: Into, { - let mut types: Vec = - names_and_types.map(Into::into).map(Into::into).collect(); + let mut types: Vec = names_and_types + .map(Into::into) + .map(QualifiedNameAndType::into_raw) + .collect(); let mut progress_raw = ProgressContext(progress); unsafe { BNDefineUserAnalysisTypes( @@ -702,6 +713,9 @@ pub trait BinaryViewExt: BinaryViewBase { &mut progress_raw as *mut _ as *mut c_void, ) }; + for ty in types { + QualifiedNameAndType::free_raw(ty); + } } fn undefine_auto_type(&self, id: S) { @@ -712,8 +726,9 @@ pub trait BinaryViewExt: BinaryViewBase { } fn undefine_user_type>(&self, name: T) { - let mut raw_name = BNQualifiedName::from(name.into()); + let mut raw_name = QualifiedName::into_raw(name.into()); unsafe { BNUndefineUserAnalysisType(self.as_ref().handle, &mut raw_name) } + QualifiedName::free_raw(raw_name); } fn types(&self) -> Array { @@ -733,9 +748,10 @@ pub trait BinaryViewExt: BinaryViewBase { } fn get_type_by_name>(&self, name: T) -> Option> { - let mut raw_name = BNQualifiedName::from(name.into()); + let mut raw_name = QualifiedName::into_raw(name.into()); unsafe { let type_handle = BNGetAnalysisTypeByName(self.as_ref().handle, &mut raw_name); + QualifiedName::free_raw(raw_name); if type_handle.is_null() { return None; } @@ -770,7 +786,7 @@ pub trait BinaryViewExt: BinaryViewBase { let id_str = id.into_bytes_with_nul(); let name_handle = BNGetAnalysisTypeNameById(self.as_ref().handle, id_str.as_ref().as_ptr() as *mut _); - let name = QualifiedName::from(name_handle); + let name = QualifiedName::from_owned_raw(name_handle); // The core will return an empty qualified name if no type name was found. match name.items.is_empty() { true => None, @@ -780,9 +796,10 @@ pub trait BinaryViewExt: BinaryViewBase { } fn get_type_id>(&self, name: T) -> Option { - let mut raw_name = BNQualifiedName::from(name.into()); + let mut raw_name = QualifiedName::into_raw(name.into()); unsafe { let id_cstr = BNGetAnalysisTypeId(self.as_ref().handle, &mut raw_name); + QualifiedName::free_raw(raw_name); let id = BnString::from_raw(id_cstr); match id.is_empty() { true => None, @@ -792,8 +809,10 @@ pub trait BinaryViewExt: BinaryViewBase { } fn is_type_auto_defined>(&self, name: T) -> bool { - let mut raw_name = BNQualifiedName::from(name.into()); - unsafe { BNIsAnalysisTypeAutoDefined(self.as_ref().handle, &mut raw_name) } + let mut raw_name = QualifiedName::into_raw(name.into()); + let result = unsafe { BNIsAnalysisTypeAutoDefined(self.as_ref().handle, &mut raw_name) }; + QualifiedName::free_raw(raw_name); + result } fn segments(&self) -> Array { @@ -1376,22 +1395,24 @@ pub trait BinaryViewExt: BinaryViewBase { /// Retrieves a list of [CodeReference]s for locations in code that use a given named type. fn get_code_refs_for_type>(&self, name: T) -> Array { - let mut raw_name = BNQualifiedName::from(name.into()); + let mut raw_name = QualifiedName::into_raw(name.into()); unsafe { let mut count = 0; let handle = BNGetCodeReferencesForType(self.as_ref().handle, &mut raw_name, &mut count); + QualifiedName::free_raw(raw_name); Array::new(handle, count, ()) } } /// Retrieves a list of [DataReference]s instances of a given named type in data. fn get_data_refs_for_type>(&self, name: T) -> Array { - let mut raw_name = BNQualifiedName::from(name.into()); + let mut raw_name = QualifiedName::into_raw(name.into()); unsafe { let mut count = 0; let handle = BNGetDataReferencesForType(self.as_ref().handle, &mut raw_name, &mut count); + QualifiedName::free_raw(raw_name); Array::new(handle, count, ()) } } @@ -1534,7 +1555,7 @@ pub trait BinaryViewExt: BinaryViewBase { addr: u64, platform: &Platform, ) { - let mut raw_name = BNQualifiedName::from(name.into()); + let mut raw_name = QualifiedName::into_raw(name.into()); unsafe { BNBinaryViewRecordImportedObjectLibrary( self.as_ref().handle, @@ -1544,6 +1565,7 @@ pub trait BinaryViewExt: BinaryViewBase { &mut raw_name, ) } + QualifiedName::free_raw(raw_name); } /// Recursively imports a type from the specified type library, or, if @@ -1565,10 +1587,11 @@ pub trait BinaryViewExt: BinaryViewBase { .as_mut() .map(|l| unsafe { l.as_raw() } as *mut _) .unwrap_or(std::ptr::null_mut()); - let mut raw_name = BNQualifiedName::from(name.into()); + let mut raw_name = QualifiedName::into_raw(name.into()); let result = unsafe { BNBinaryViewImportTypeLibraryType(self.as_ref().handle, &mut lib_ref, &mut raw_name) }; + QualifiedName::free_raw(raw_name); (!result.is_null()).then(|| unsafe { Type::ref_from_raw(result) }) } @@ -1590,10 +1613,11 @@ pub trait BinaryViewExt: BinaryViewBase { .as_mut() .map(|l| unsafe { l.as_raw() } as *mut _) .unwrap_or(std::ptr::null_mut()); - let mut raw_name = BNQualifiedName::from(name.into()); + let mut raw_name = QualifiedName::into_raw(name.into()); let result = unsafe { BNBinaryViewImportTypeLibraryObject(self.as_ref().handle, &mut lib_ref, &mut raw_name) }; + QualifiedName::free_raw(raw_name); (!result.is_null()).then(|| unsafe { Type::ref_from_raw(result) }) } @@ -1624,7 +1648,7 @@ pub trait BinaryViewExt: BinaryViewBase { name: T, type_obj: &Type, ) { - let mut raw_name = BNQualifiedName::from(name.into()); + let mut raw_name = QualifiedName::into_raw(name.into()); unsafe { BNBinaryViewExportTypeToTypeLibrary( self.as_ref().handle, @@ -1633,6 +1657,7 @@ pub trait BinaryViewExt: BinaryViewBase { type_obj.handle, ) } + QualifiedName::free_raw(raw_name); } /// Recursively exports `type_obj` into `lib` as a type with name `name` @@ -1645,7 +1670,7 @@ pub trait BinaryViewExt: BinaryViewBase { name: T, type_obj: &Type, ) { - let mut raw_name = BNQualifiedName::from(name.into()); + let mut raw_name = QualifiedName::into_raw(name.into()); unsafe { BNBinaryViewExportObjectToTypeLibrary( self.as_ref().handle, @@ -1654,6 +1679,7 @@ pub trait BinaryViewExt: BinaryViewBase { type_obj.handle, ) } + QualifiedName::free_raw(raw_name); } /// Gives you details of which type library and name was used to determine @@ -1681,7 +1707,7 @@ pub trait BinaryViewExt: BinaryViewBase { return None; } let lib = unsafe { TypeLibrary::from_raw(NonNull::new(result_lib)?) }; - let name = QualifiedName::from(result_name); + let name = QualifiedName::from_owned_raw(result_name); Some((lib, name)) } @@ -1692,7 +1718,7 @@ pub trait BinaryViewExt: BinaryViewBase { &self, name: T, ) -> Option<(TypeLibrary, QualifiedName)> { - let raw_name = BNQualifiedName::from(name.into()); + let raw_name = QualifiedName::into_raw(name.into()); let mut result_lib = std::ptr::null_mut(); let mut result_name = BNQualifiedName::default(); let success = unsafe { @@ -1703,11 +1729,12 @@ pub trait BinaryViewExt: BinaryViewBase { &mut result_name, ) }; + QualifiedName::free_raw(raw_name); if !success { return None; } let lib = unsafe { TypeLibrary::from_raw(NonNull::new(result_lib)?) }; - let name = QualifiedName::from(result_name); + let name = QualifiedName::from_owned_raw(result_name); Some((lib, name)) } // @@ -1914,7 +1941,9 @@ pub type BinaryViewEventType = BNBinaryViewEventType; /// # Example /// /// ```no_run -/// use binaryninja::binaryview::{BinaryView, BinaryViewEventHandler, BinaryViewEventType, register_binary_view_event}; +/// use binaryninja::binaryview::{ +/// register_binary_view_event, BinaryView, BinaryViewEventHandler, BinaryViewEventType, +/// }; /// /// struct EventHandlerContext { /// // Context holding state available to event handler @@ -1928,7 +1957,7 @@ pub type BinaryViewEventType = BNBinaryViewEventType; /// /// #[no_mangle] /// pub extern "C" fn CorePluginInit() { -/// let context = EventHandlerContext { }; +/// let context = EventHandlerContext {}; /// /// register_binary_view_event( /// BinaryViewEventType::BinaryViewInitialAnalysisCompletionEvent, diff --git a/rust/src/callingconvention.rs b/rust/src/callingconvention.rs index 2b338c694..9b7507bc7 100644 --- a/rust/src/callingconvention.rs +++ b/rust/src/callingconvention.rs @@ -22,7 +22,7 @@ use std::marker::PhantomData; use binaryninjacore_sys::*; -use crate::architecture::{Architecture, ArchitectureExt, CoreArchitecture, Register, RegisterId}; +use crate::architecture::{Architecture, ArchitectureExt, Register, RegisterId}; use crate::rc::{CoreArrayProvider, CoreArrayProviderInner, Guard, Ref, RefCountable}; use crate::string::*; use crate::types::FunctionParameter; @@ -443,6 +443,14 @@ pub struct CallingConvention { } impl CallingConvention { + pub(crate) unsafe fn from_raw(handle: *mut BNCallingConvention, arch: A::Handle) -> Self { + CallingConvention { + handle, + arch_handle: arch, + _arch: PhantomData, + } + } + pub(crate) unsafe fn ref_from_raw( handle: *mut BNCallingConvention, arch: A::Handle, @@ -464,7 +472,11 @@ impl CallingConvention { permitted_registers: Option<&[A::Register]>, ) -> Vec { let mut count: usize = 0; - let raw_params: Vec = params.iter().cloned().map(Into::into).collect(); + let raw_params: Vec = params + .iter() + .cloned() + .map(FunctionParameter::into_raw) + .collect(); let raw_vars_ptr: *mut BNVariable = if let Some(permitted_args) = permitted_registers { let permitted_regs = permitted_args.iter().map(|r| r.id().0).collect::>(); @@ -489,6 +501,10 @@ impl CallingConvention { } }; + for raw_param in raw_params { + FunctionParameter::free_raw(raw_param); + } + let raw_vars = unsafe { std::slice::from_raw_parts(raw_vars_ptr, count) }; let vars: Vec<_> = raw_vars.iter().copied().map(Into::into).collect(); unsafe { BNFreeVariableList(raw_vars_ptr) }; @@ -506,6 +522,43 @@ impl PartialEq for CallingConvention { } } +impl Debug for CallingConvention { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + f.debug_struct("CallingConvention") + .field("name", &self.name()) + .field("caller_saved_registers", &self.caller_saved_registers()) + .field("callee_saved_registers", &self.callee_saved_registers()) + .field("int_arg_registers", &self.int_arg_registers()) + .field("float_arg_registers", &self.float_arg_registers()) + .field( + "arg_registers_shared_index", + &self.arg_registers_shared_index(), + ) + .field( + "reserved_stack_space_for_arg_registers", + &self.reserved_stack_space_for_arg_registers(), + ) + .field("stack_adjusted_on_return", &self.stack_adjusted_on_return()) + .field( + "is_eligible_for_heuristics", + &self.is_eligible_for_heuristics(), + ) + .field("return_int_reg", &self.return_int_reg()) + .field("return_hi_int_reg", &self.return_hi_int_reg()) + .field("return_float_reg", &self.return_float_reg()) + .field("global_pointer_reg", &self.global_pointer_reg()) + .field( + "implicitly_defined_registers", + &self.implicitly_defined_registers(), + ) + .field( + "are_argument_registers_used_for_var_args", + &self.are_argument_registers_used_for_var_args(), + ) + .finish() + } +} + impl Hash for CallingConvention { fn hash(&self, state: &mut H) { self.handle.hash(state); @@ -685,12 +738,6 @@ unsafe impl CoreArrayProviderInner for CallingConvention { } } -impl Debug for CallingConvention { - fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { - write!(f, "", self.name(), self.arch_handle.name()) - } -} - pub struct ConventionBuilder { caller_saved_registers: Vec, callee_saved_registers: Vec, diff --git a/rust/src/confidence.rs b/rust/src/confidence.rs index 83c6511c1..5a23c88b5 100644 --- a/rust/src/confidence.rs +++ b/rust/src/confidence.rs @@ -1,3 +1,5 @@ +#![allow(unused)] + use crate::architecture::{Architecture, CoreArchitecture}; use crate::callingconvention::CallingConvention; use crate::rc::{Ref, RefCountable}; @@ -187,77 +189,102 @@ impl From for Conf { } } -impl From for Conf> { - fn from(type_with_confidence: BNTypeWithConfidence) -> Self { +impl<'a> Conf<&'a Type> { + pub(crate) fn into_raw(value: Self) -> BNTypeWithConfidence { + BNTypeWithConfidence { + type_: value.contents.handle, + confidence: value.confidence, + } + } +} + +impl Conf> { + pub(crate) fn from_raw(value: &BNTypeWithConfidence) -> Self { Self::new( - unsafe { Type::ref_from_raw(type_with_confidence.type_) }, - type_with_confidence.confidence, + unsafe { Type::from_raw(value.type_) }.to_owned(), + value.confidence, ) } -} -impl From for Conf { - fn from(bool_with_confidence: BNBoolWithConfidence) -> Self { - Self::new(bool_with_confidence.value, bool_with_confidence.confidence) + pub(crate) fn from_owned_raw(value: BNTypeWithConfidence) -> Self { + let owned = Self::from_raw(&value); + Self::free_raw(value); + owned } -} -impl From for Conf>> { - fn from(cc_with_confidence: BNCallingConventionWithConfidence) -> Self { - Self::new( - unsafe { - CallingConvention::ref_from_raw( - cc_with_confidence.convention, - CoreArchitecture::from_raw(BNGetCallingConventionArchitecture( - cc_with_confidence.convention, - )), - ) - }, - cc_with_confidence.confidence, - ) + pub(crate) fn into_raw(value: Self) -> BNTypeWithConfidence { + BNTypeWithConfidence { + type_: unsafe { Ref::into_raw(value.contents) }.handle, + confidence: value.confidence, + } + } + + pub(crate) fn free_raw(value: BNTypeWithConfidence) { + let _ = unsafe { Type::ref_from_raw(value.type_) }; } } -impl From for Conf { - fn from(offset_with_confidence: BNOffsetWithConfidence) -> Self { +impl Conf>> { + pub(crate) fn from_raw(value: &BNCallingConventionWithConfidence) -> Self { + let arch = unsafe { + CoreArchitecture::from_raw(BNGetCallingConventionArchitecture(value.convention)) + }; Self::new( - offset_with_confidence.value, - offset_with_confidence.confidence, + unsafe { CallingConvention::from_raw(value.convention, arch).to_owned() }, + value.confidence, ) } + + pub(crate) fn from_owned_raw(value: BNCallingConventionWithConfidence) -> Self { + let owned = Self::from_raw(&value); + Self::free_raw(value); + owned + } } -impl From>> for BNTypeWithConfidence { - fn from(conf: Conf>) -> Self { - Self { - type_: conf.contents.handle, - confidence: conf.confidence, +impl Conf>> { + pub(crate) fn into_raw(value: Self) -> BNCallingConventionWithConfidence { + BNCallingConventionWithConfidence { + convention: unsafe { Ref::into_raw(value.contents) }.handle, + confidence: value.confidence, } } -} -impl From> for BNTypeWithConfidence { - fn from(conf: Conf<&Type>) -> Self { - Self { - type_: conf.contents.handle, - confidence: conf.confidence, + pub(crate) fn into_owned_raw(value: &Self) -> BNCallingConventionWithConfidence { + BNCallingConventionWithConfidence { + convention: value.contents.handle, + confidence: value.confidence, } } + + pub(crate) fn free_raw(value: BNCallingConventionWithConfidence) { + let arch = unsafe { + CoreArchitecture::from_raw(BNGetCallingConventionArchitecture(value.convention)) + }; + let _ = + unsafe { CallingConvention::::ref_from_raw(value.convention, arch) }; + } } -impl From> for BNBoolWithConfidence { - fn from(conf: Conf) -> Self { - Self { - value: conf.contents, - confidence: conf.confidence, - } +impl From for Conf { + fn from(bool_with_confidence: BNBoolWithConfidence) -> Self { + Self::new(bool_with_confidence.value, bool_with_confidence.confidence) } } -impl From>> for BNCallingConventionWithConfidence { - fn from(conf: Conf<&CallingConvention>) -> Self { +impl From for Conf { + fn from(offset_with_confidence: BNOffsetWithConfidence) -> Self { + Self::new( + offset_with_confidence.value, + offset_with_confidence.confidence, + ) + } +} + +impl From> for BNBoolWithConfidence { + fn from(conf: Conf) -> Self { Self { - convention: conf.contents.handle, + value: conf.contents, confidence: conf.confidence, } } diff --git a/rust/src/debuginfo.rs b/rust/src/debuginfo.rs index 03739e0d4..832f0d457 100644 --- a/rust/src/debuginfo.rs +++ b/rust/src/debuginfo.rs @@ -40,7 +40,13 @@ //! true //! } //! -//! fn parse_info(&self, _debug_info: &mut DebugInfo, _view: &BinaryView, _debug_file: &BinaryView, _progress: Box Result<(), ()>>) -> bool { +//! fn parse_info( +//! &self, +//! _debug_info: &mut DebugInfo, +//! _view: &BinaryView, +//! _debug_file: &BinaryView, +//! _progress: Box Result<(), ()>>, +//! ) -> bool { //! println!("Parsing info"); //! true //! } @@ -306,37 +312,37 @@ pub struct DebugFunctionInfo { local_variables: Vec, } -impl From<&BNDebugFunctionInfo> for DebugFunctionInfo { - fn from(raw: &BNDebugFunctionInfo) -> Self { - let raw_components = unsafe { std::slice::from_raw_parts(raw.components, raw.componentN) }; +impl DebugFunctionInfo { + pub(crate) fn from_raw(value: &BNDebugFunctionInfo) -> Self { + let raw_components = + unsafe { std::slice::from_raw_parts(value.components, value.componentN) }; + let components = raw_components + .iter() + .filter_map(|&c| raw_to_string(c)) + .collect(); let raw_local_variables = - unsafe { std::slice::from_raw_parts(raw.localVariables, raw.localVariableN) }; - + unsafe { std::slice::from_raw_parts(value.localVariables, value.localVariableN) }; + let local_variables = raw_local_variables + .iter() + .map(NamedVariableWithType::from_raw) + .collect(); Self { - short_name: raw_to_string(raw.shortName), - full_name: raw_to_string(raw.fullName), - raw_name: raw_to_string(raw.rawName), - type_: if raw.type_.is_null() { + short_name: raw_to_string(value.shortName), + full_name: raw_to_string(value.fullName), + raw_name: raw_to_string(value.rawName), + type_: if value.type_.is_null() { None } else { - Some(unsafe { Type::ref_from_raw(raw.type_) }) + Some(unsafe { Type::from_raw(value.type_) }.to_owned()) }, - address: raw.address, - platform: if raw.platform.is_null() { + address: value.address, + platform: if value.platform.is_null() { None } else { - Some(unsafe { Platform::ref_from_raw(raw.platform) }) + Some(unsafe { Platform::from_raw(value.platform) }.to_owned()) }, - components: raw_components - .iter() - .copied() - .filter_map(|c| raw_to_string(c)) - .collect(), - local_variables: raw_local_variables - .iter() - .copied() - .map(Into::into) - .collect(), + components, + local_variables, } } } @@ -408,8 +414,7 @@ impl DebugInfo { let result: Vec<_> = unsafe { std::slice::from_raw_parts_mut(debug_types_ptr, count) .iter() - .copied() - .map(Into::into) + .map(NameAndType::from_raw) .collect() }; @@ -424,8 +429,7 @@ impl DebugInfo { let result: Vec<_> = unsafe { std::slice::from_raw_parts_mut(debug_types_ptr, count) .iter() - .copied() - .map(Into::into) + .map(NameAndType::from_raw) .collect() }; @@ -449,7 +453,7 @@ impl DebugInfo { let result: Vec = unsafe { std::slice::from_raw_parts_mut(functions_ptr, count) .iter() - .map(DebugFunctionInfo::from) + .map(DebugFunctionInfo::from_raw) .collect() }; @@ -465,7 +469,7 @@ impl DebugInfo { let result: Vec = unsafe { std::slice::from_raw_parts_mut(functions_ptr, count) .iter() - .map(DebugFunctionInfo::from) + .map(DebugFunctionInfo::from_raw) .collect() }; @@ -492,8 +496,7 @@ impl DebugInfo { let result: Vec = unsafe { std::slice::from_raw_parts_mut(data_variables_ptr, count) .iter() - .copied() - .map(Into::into) + .map(NamedDataVariableWithType::from_raw) .collect() }; @@ -509,8 +512,7 @@ impl DebugInfo { let result: Vec = unsafe { std::slice::from_raw_parts_mut(data_variables_ptr, count) .iter() - .copied() - .map(Into::into) + .map(NamedDataVariableWithType::from_raw) .collect() }; @@ -552,9 +554,7 @@ impl DebugInfo { }; if !raw_named_var.is_null() { - let result = unsafe { raw_named_var.read() }; - unsafe { BNFreeDataVariableAndName(raw_named_var) }; - Some(NamedDataVariableWithType::from(result)) + Some(unsafe { NamedDataVariableWithType::from_ref_raw(raw_named_var) }) } else { None } @@ -575,9 +575,7 @@ impl DebugInfo { }; if !raw_named_var.is_null() { - let result = unsafe { raw_named_var.read() }; - unsafe { BNFreeDataVariableAndName(raw_named_var) }; - Some(NamedDataVariableWithType::from(result)) + Some(unsafe { NamedDataVariableWithType::from_ref_raw(raw_named_var) }) } else { None } @@ -596,8 +594,7 @@ impl DebugInfo { let names_and_types = raw_names_and_types .iter() - .copied() - .map(Into::into) + .map(NameAndType::from_raw) .collect(); unsafe { BNFreeNameAndTypeList(raw_names_and_types_ptr, count) }; @@ -626,7 +623,7 @@ impl DebugInfo { ( raw_to_string((*variable_and_name).name).unwrap(), (*variable_and_name).address, - Type::ref_from_raw(BNNewTypeReference((*variable_and_name).type_)), + Type::from_raw((*variable_and_name).type_).to_owned(), ) }) .collect(); @@ -651,7 +648,7 @@ impl DebugInfo { ( raw_to_string((*variable_and_name).parser).unwrap(), raw_to_string((*variable_and_name).name).unwrap(), - Type::ref_from_raw(BNNewTypeReference((*variable_and_name).type_)), + Type::from_raw((*variable_and_name).type_).to_owned(), ) }) .collect(); @@ -870,7 +867,10 @@ impl DebugInfo { } pub fn add_data_variable_info(&self, var: NamedDataVariableWithType) -> bool { - unsafe { BNAddDebugDataVariableInfo(self.handle, &var.into()) } + let raw_data_var = NamedDataVariableWithType::into_raw(var); + let success = unsafe { BNAddDebugDataVariableInfo(self.handle, &raw_data_var) }; + NamedDataVariableWithType::free_raw(raw_data_var); + success } } diff --git a/rust/src/demangle.rs b/rust/src/demangle.rs index 831157b28..43d30133b 100644 --- a/rust/src/demangle.rs +++ b/rust/src/demangle.rs @@ -52,7 +52,7 @@ pub fn demangle_generic( true => None, false => Some(unsafe { Type::ref_from_raw(out_type) }), }; - Some((out_name.into(), out_type)) + Some((QualifiedName::from_owned_raw(out_name), out_type)) } else { None } @@ -226,7 +226,7 @@ impl Demangler { false => Some(unsafe { Type::ref_from_raw(out_type) }), }; - Some((out_var_name.into(), var_type)) + Some((QualifiedName::from_owned_raw(out_var_name), var_type)) } false => None, } @@ -257,9 +257,7 @@ impl Demangler { { ffi_wrap!("CustomDemangler::cb_is_mangled_string", unsafe { let cmd = &*(ctxt as *const C); - let name = if let Some(n) = raw_to_string(name) { - n - } else { + let Some(name) = raw_to_string(name) else { return false; }; cmd.is_mangled_string(&name) @@ -279,24 +277,23 @@ impl Demangler { ffi_wrap!("CustomDemangler::cb_demangle", unsafe { let cmd = &*(ctxt as *const C); let arch = CoreArchitecture::from_raw(arch); - let name = if let Some(n) = raw_to_string(name) { - n - } else { + let Some(name) = raw_to_string(name) else { return false; }; - let view = if view.is_null() { - None - } else { - Some(BinaryView::ref_from_raw(BNNewViewReference(view))) + let view = match view.is_null() { + false => Some(BinaryView::from_raw(view).to_owned()), + true => None, }; match cmd.demangle(&arch, &name, view) { Some((name, ty)) => { + // NOTE: Leaked to the caller, who must pick the ref up. *out_type = match ty { - Some(t) => RefCountable::inc_ref(t.as_ref()).handle, + Some(t) => Ref::into_raw(t).handle, None => std::ptr::null_mut(), }; - *out_var_name = name.into(); + // NOTE: Leaked to be freed with `cb_free_var_name`. + *out_var_name = QualifiedName::into_raw(name); true } None => false, @@ -310,8 +307,7 @@ impl Demangler { { ffi_wrap!("CustomDemangler::cb_free_var_name", unsafe { // TODO: What is the point of this free callback? - // TODO: The core can just call BNFreeQualifiedName. - BNFreeQualifiedName(name); + QualifiedName::free_raw(*name) }) } diff --git a/rust/src/disassembly.rs b/rust/src/disassembly.rs index 2a4db4f68..cb4b9cfea 100644 --- a/rust/src/disassembly.rs +++ b/rust/src/disassembly.rs @@ -11,6 +11,7 @@ // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. +#![allow(unused)] use binaryninjacore_sys::*; @@ -40,86 +41,87 @@ pub struct DisassemblyTextLine { } impl DisassemblyTextLine { - pub fn new(tokens: Vec) -> Self { + pub(crate) fn from_raw(value: &BNDisassemblyTextLine) -> Self { + let raw_tokens = unsafe { std::slice::from_raw_parts(value.tokens, value.count) }; + let tokens: Vec<_> = raw_tokens + .iter() + .map(InstructionTextToken::from_raw) + .collect(); + // SAFETY: Increment the tag ref as we are going from ref to owned. + let raw_tags = unsafe { std::slice::from_raw_parts(value.tags, value.tagCount) }; + 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, - ..Default::default() + highlight: value.highlight.into(), + tags, + type_info: DisassemblyTextLineTypeInfo::from_raw(&value.typeInfo), } } - // 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 { + pub(crate) fn into_raw(value: 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 + let tokens: Box<[BNInstructionTextToken]> = value + .tokens + .into_iter() + .map(InstructionTextToken::into_raw) + .collect(); + let tags: Box<[*mut BNTag]> = value .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 + unsafe { Ref::into_raw(t) }.handle }) .collect(); - let tokens_len = tokens.len(); - let tags_len = tags.len(); BNDisassemblyTextLine { - addr: self.address, - instrIndex: self.instruction_index, + addr: value.address, + instrIndex: value.instruction_index, + count: tokens.len(), // 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(), + highlight: value.highlight.into(), + tagCount: tags.len(), // 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(), + typeInfo: DisassemblyTextLineTypeInfo::into_raw(value.type_info), } } - // 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) { + pub(crate) fn free_raw(value: 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); + let raw_tokens = unsafe { std::slice::from_raw_parts_mut(value.tokens, value.count) }; + let boxed_tokens = unsafe { 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); + let raw_tags = unsafe { std::slice::from_raw_parts_mut(value.tags, value.tagCount) }; + let boxed_tags = unsafe { 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) }; + let _ = unsafe { Tag::ref_from_raw(tag) }; } + // Free the type info + DisassemblyTextLineTypeInfo::free_raw(value.typeInfo); } -} -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(); + pub fn new(tokens: Vec) -> Self { Self { - address: value.addr, - instruction_index: value.instrIndex, tokens, - highlight: value.highlight.into(), - tags, - type_info: value.typeInfo.into(), + ..Default::default() } } } @@ -163,7 +165,7 @@ unsafe impl CoreArrayProviderInner for DisassemblyTextLine { } unsafe fn wrap_raw<'a>(raw: &'a Self::Raw, _context: &'a Self::Context) -> Self::Wrapped<'a> { - Self::from(raw) + Self::from_raw(raw) } } @@ -175,46 +177,61 @@ pub struct DisassemblyTextLineTypeInfo { pub offset: u64, } -impl From for DisassemblyTextLineTypeInfo { - fn from(value: BNDisassemblyTextLineTypeInfo) -> Self { +impl DisassemblyTextLineTypeInfo { + pub(crate) fn from_raw(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) }), + false => Some(unsafe { Type::from_raw(value.parentType).to_owned() }), true => None, }, field_index: value.fieldIndex, offset: value.offset, } } -} -impl From<&BNDisassemblyTextLineTypeInfo> for DisassemblyTextLineTypeInfo { - fn from(value: &BNDisassemblyTextLineTypeInfo) -> Self { + pub(crate) fn from_owned_raw(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() }), + false => Some(unsafe { Type::ref_from_raw(value.parentType) }), true => None, }, field_index: value.fieldIndex, offset: value.offset, } } -} -impl From for BNDisassemblyTextLineTypeInfo { - fn from(value: DisassemblyTextLineTypeInfo) -> Self { - Self { + pub(crate) fn into_raw(value: Self) -> BNDisassemblyTextLineTypeInfo { + BNDisassemblyTextLineTypeInfo { + hasTypeInfo: value.has_type_info, + parentType: value + .parent_type + .map(|t| unsafe { Ref::into_raw(t) }.handle) + .unwrap_or(std::ptr::null_mut()), + fieldIndex: value.field_index, + offset: value.offset, + } + } + + pub(crate) fn into_owned_raw(value: &Self) -> BNDisassemblyTextLineTypeInfo { + BNDisassemblyTextLineTypeInfo { hasTypeInfo: value.has_type_info, parentType: value .parent_type + .as_ref() .map(|t| t.handle) .unwrap_or(std::ptr::null_mut()), fieldIndex: value.field_index, offset: value.offset, } } + + pub(crate) fn free_raw(value: BNDisassemblyTextLineTypeInfo) { + if !value.parentType.is_null() { + let _ = unsafe { Type::ref_from_raw(value.parentType) }; + } + } } #[derive(Debug, Clone, PartialEq)] @@ -229,67 +246,28 @@ pub struct InstructionTextToken { } impl InstructionTextToken { - pub fn new(text: impl Into, kind: InstructionTextTokenKind) -> Self { - Self { - address: 0, - text: text.into(), - confidence: MAX_CONFIDENCE, - context: InstructionTextTokenContext::Normal, - expr_index: 0, - kind, - } - } - - 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 From<&BNInstructionTextToken> for InstructionTextToken { - fn from(value: &BNInstructionTextToken) -> Self { + pub(crate) fn from_raw(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), + kind: InstructionTextTokenKind::from_raw(value), } } -} -impl From for BNInstructionTextToken { - fn from(value: InstructionTextToken) -> Self { + pub(crate) fn into_raw(value: Self) -> BNInstructionTextToken { 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 { + BNInstructionTextToken { type_: value.kind.into(), - // Expected to be freed with `InstructionTextToken::free_raw`. - text: bn_text.into_raw(), + // NOTE: Expected to be freed with `InstructionTextToken::free_raw`. + text: BnString::into_raw(bn_text), value: kind_value, // TODO: Where is this even used? width: 0, @@ -298,12 +276,47 @@ impl From for BNInstructionTextToken { context: value.context.into(), confidence: value.confidence, address: value.address, - // Expected to be freed with `InstructionTextToken::free_raw`. + // NOTE: Expected to be freed with `InstructionTextToken::free_raw`. typeNames: strings_to_string_list(&type_names), namesCount: type_names.len(), exprIndex: value.expr_index, } } + + pub(crate) fn free_raw(value: BNInstructionTextToken) { + if !value.text.is_null() { + unsafe { BNFreeString(value.text) }; + } + if !value.typeNames.is_null() { + unsafe { BNFreeStringList(value.typeNames, value.namesCount) }; + } + } + + pub fn new(text: impl Into, kind: InstructionTextTokenKind) -> Self { + Self { + address: 0, + text: text.into(), + confidence: MAX_CONFIDENCE, + context: InstructionTextTokenContext::Normal, + expr_index: 0, + kind, + } + } + + 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, + } + } } impl Display for InstructionTextToken { @@ -325,7 +338,7 @@ unsafe impl CoreArrayProviderInner for InstructionTextToken { } unsafe fn wrap_raw<'a>(raw: &'a Self::Raw, _context: &'a Self::Context) -> Self::Wrapped<'a> { - Self::from(raw) + Self::from_raw(raw) } } @@ -508,80 +521,7 @@ pub enum InstructionTextTokenKind { } 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, - } - } - - /// 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, - } - } - - /// 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, - } - } - - /// 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, - } - } -} - -impl From<&BNInstructionTextToken> for InstructionTextTokenKind { - fn from(value: &BNInstructionTextToken) -> Self { + pub(crate) fn from_raw(value: &BNInstructionTextToken) -> Self { match value.type_ { BNInstructionTextTokenType::TextToken => Self::Text, BNInstructionTextTokenType::InstructionToken => Self::Instruction, @@ -741,6 +681,77 @@ impl From<&BNInstructionTextToken> for 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, + } + } + + /// 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, + } + } + + /// 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, + } + } + + /// 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, + } + } } impl From for BNInstructionTextTokenType { diff --git a/rust/src/flowgraph.rs b/rust/src/flowgraph.rs index 0d490e577..36f66c732 100644 --- a/rust/src/flowgraph.rs +++ b/rust/src/flowgraph.rs @@ -70,9 +70,10 @@ impl<'a> FlowGraphNode<'a> { 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(); + let mut raw_lines: Vec = lines + .into_iter() + .map(DisassemblyTextLine::into_raw) + .collect(); unsafe { BNSetFlowGraphNodeLines(self.handle, raw_lines.as_mut_ptr(), raw_lines.len()); for raw_line in raw_lines { diff --git a/rust/src/function.rs b/rust/src/function.rs index 61f0df4b0..c8b695bcd 100644 --- a/rust/src/function.rs +++ b/rust/src/function.rs @@ -518,15 +518,15 @@ impl Function { } pub fn return_type(&self) -> Conf> { - unsafe { BNGetFunctionReturnType(self.handle) }.into() + let raw_return_type = unsafe { BNGetFunctionReturnType(self.handle) }; + Conf::>::from_owned_raw(raw_return_type) } pub fn set_auto_return_type<'a, C>(&self, return_type: C) where C: Into>, { - let return_type: Conf<&Type> = return_type.into(); - let mut raw_return_type = BNTypeWithConfidence::from(return_type); + let mut raw_return_type = Conf::<&Type>::into_raw(return_type.into()); unsafe { BNSetAutoFunctionReturnType(self.handle, &mut raw_return_type) } } @@ -534,8 +534,7 @@ impl Function { where C: Into>, { - let return_type: Conf<&Type> = return_type.into(); - let mut raw_return_type = BNTypeWithConfidence::from(return_type); + let mut raw_return_type = Conf::<&Type>::into_raw(return_type.into()); unsafe { BNSetUserFunctionReturnType(self.handle, &mut raw_return_type) } } @@ -644,7 +643,7 @@ impl Function { let arch = arch.unwrap_or_else(|| self.arch()); let result = unsafe { BNGetCallTypeAdjustment(self.handle, arch.handle, addr) }; match result.type_.is_null() { - false => Some(result.into()), + false => Some(Conf::>::from_owned_raw(result)), true => None, } } @@ -1046,7 +1045,13 @@ impl Function { /// # let fun: Function = todo!(); /// # let bv: BinaryView = todo!(); /// let important = bv.create_tag_type("Important", "⚠️"); - /// fun.add_tag(&important, "I think this is the main function", None, false, None); + /// fun.add_tag( + /// &important, + /// "I think this is the main function", + /// None, + /// false, + /// None, + /// ); /// let crash = bv.create_tag_type("Crashes", "🎯"); /// fun.add_tag(&crash, "Nullpointer dereference", Some(0x1337), false, None); /// ``` @@ -1202,8 +1207,9 @@ impl Function { arch: Option, ) { let arch = arch.unwrap_or_else(|| self.arch()); - let mut raw_name = BNQualifiedName::from(name.into()); - unsafe { BNAddUserTypeReference(self.handle, arch.handle, from_addr, &mut raw_name) } + let mut raw_name = QualifiedName::into_raw(name.into()); + unsafe { BNAddUserTypeReference(self.handle, arch.handle, from_addr, &mut raw_name) }; + QualifiedName::free_raw(raw_name); } /// Removes a user-defined type cross-reference. @@ -1227,8 +1233,9 @@ impl Function { arch: Option, ) { let arch = arch.unwrap_or_else(|| self.arch()); - let mut raw_name = BNQualifiedName::from(name.into()); - unsafe { BNRemoveUserTypeReference(self.handle, arch.handle, from_addr, &mut raw_name) } + let mut raw_name = QualifiedName::into_raw(name.into()); + unsafe { BNRemoveUserTypeReference(self.handle, arch.handle, from_addr, &mut raw_name) }; + QualifiedName::free_raw(raw_name); } /// Places a user-defined type field cross-reference from the @@ -1258,7 +1265,7 @@ impl Function { ) { let size = size.unwrap_or(0); let arch = arch.unwrap_or_else(|| self.arch()); - let mut raw_name = BNQualifiedName::from(name.into()); + let mut raw_name = QualifiedName::into_raw(name.into()); unsafe { BNAddUserTypeFieldReference( self.handle, @@ -1268,7 +1275,8 @@ impl Function { offset, size, ) - } + }; + QualifiedName::free_raw(raw_name); } /// Removes a user-defined type field cross-reference. @@ -1297,7 +1305,7 @@ impl Function { ) { let size = size.unwrap_or(0); let arch = arch.unwrap_or_else(|| self.arch()); - let mut raw_name = BNQualifiedName::from(name.into()); + let mut raw_name = QualifiedName::into_raw(name.into()); unsafe { BNRemoveUserTypeFieldReference( self.handle, @@ -1308,6 +1316,7 @@ impl Function { size, ) } + QualifiedName::free_raw(raw_name); } pub fn constant_data( @@ -1587,7 +1596,7 @@ impl Function { /// # let function: binaryninja::function::Function = todo!(); /// let color = HighlightColor::StandardHighlightColor { /// color: HighlightStandardColor::RedHighlightColor, - /// alpha: u8::MAX + /// alpha: u8::MAX, /// }; /// function.set_user_instr_highlight(0x1337, color, None); /// ``` diff --git a/rust/src/headless.rs b/rust/src/headless.rs index a72fd669c..269bd0cd4 100644 --- a/rust/src/headless.rs +++ b/rust/src/headless.rs @@ -123,7 +123,9 @@ impl Session { /// ```no_run /// let headless_session = binaryninja::headless::Session::new(); /// - /// let bv = headless_session.load("/bin/cat").expect("Couldn't open `/bin/cat`"); + /// let bv = headless_session + /// .load("/bin/cat") + /// .expect("Couldn't open `/bin/cat`"); /// ``` pub fn load(&self, filename: &str) -> Option> { crate::load(filename) @@ -133,12 +135,12 @@ impl Session { /// use binaryninja::{metadata::Metadata, rc::Ref}; /// use std::collections::HashMap; /// - /// let settings: Ref = HashMap::from([ - /// ("analysis.linearSweep.autorun", false.into()), - /// ]).into(); + /// let settings: Ref = + /// HashMap::from([("analysis.linearSweep.autorun", false.into())]).into(); /// let headless_session = binaryninja::headless::Session::new(); /// - /// let bv = headless_session.load_with_options("/bin/cat", true, Some(settings)) + /// let bv = headless_session + /// .load_with_options("/bin/cat", true, Some(settings)) /// .expect("Couldn't open `/bin/cat`"); /// ``` pub fn load_with_options( diff --git a/rust/src/interaction.rs b/rust/src/interaction.rs index 9067b5ff4..6c8715fc9 100644 --- a/rust/src/interaction.rs +++ b/rust/src/interaction.rs @@ -482,10 +482,10 @@ impl FormInputBuilder { /// }; /// /// let FormResponses::String(last_name) = &responses[0] else { - /// unreachable!() + /// unreachable!() /// }; /// let FormResponses::String(first_name) = &responses[1] else { - /// unreachable!() + /// unreachable!() /// }; /// /// println!("{} {} likes {}", &first_name, &last_name, food); diff --git a/rust/src/lib.rs b/rust/src/lib.rs index b29d857a0..23d690b86 100644 --- a/rust/src/lib.rs +++ b/rust/src/lib.rs @@ -82,7 +82,9 @@ //! let headless_session = binaryninja::headless::Session::new(); //! //! println!("Loading binary..."); -//! let bv = headless_session.load("/bin/cat").expect("Couldn't open `/bin/cat`"); +//! let bv = headless_session +//! .load("/bin/cat") +//! .expect("Couldn't open `/bin/cat`"); //! //! // Your code here... //! ``` @@ -104,7 +106,6 @@ //! [the only official method of providing linker arguments to a crate is through that crate's `build.rs`]: https://github.com/rust-lang/cargo/issues/9554 //! [`build.rs` is here]: https://github.com/Vector35/binaryninja-api/blob/dev/rust/examples/template/build.rs //! [examples]: https://github.com/Vector35/binaryninja-api/tree/dev/rust/examples -//! #[doc(hidden)] pub extern crate binaryninjacore_sys; diff --git a/rust/src/linearview.rs b/rust/src/linearview.rs index ce0b39c2e..0590410b9 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::from(&raw.contents)); + let contents = mem::ManuallyDrop::new(DisassemblyTextLine::from_raw(&raw.contents)); Self { t: linetype, function, diff --git a/rust/src/logger.rs b/rust/src/logger.rs index dd0a942de..c928287ff 100644 --- a/rust/src/logger.rs +++ b/rust/src/logger.rs @@ -27,7 +27,6 @@ //! true //! } //! ``` -//! pub use binaryninjacore_sys::BNLogLevel as Level; use binaryninjacore_sys::{ diff --git a/rust/src/mlil/function.rs b/rust/src/mlil/function.rs index b983dd006..5db55d37f 100644 --- a/rust/src/mlil/function.rs +++ b/rust/src/mlil/function.rs @@ -117,14 +117,13 @@ impl MediumLevelILFunction { var_type: C, name: S, ) { - let var_type = var_type.into(); - let mut raw_var_type: BNTypeWithConfidence = var_type.into(); + let mut owned_raw_var_ty = Conf::<&Type>::into_raw(var_type.into()); let name = name.into_bytes_with_nul(); unsafe { BNCreateUserStackVariable( self.get_function().handle, offset, - &mut raw_var_type, + &mut owned_raw_var_ty, name.as_ref().as_ptr() as *const c_char, ) } @@ -141,15 +140,14 @@ impl MediumLevelILFunction { name: S, ignore_disjoint_uses: bool, ) { - let var_type = var_type.into(); let raw_var = BNVariable::from(var); - let raw_var_type: BNTypeWithConfidence = var_type.into(); + let mut owned_raw_var_ty = Conf::<&Type>::into_raw(var_type.into()); let name = name.into_bytes_with_nul(); unsafe { BNCreateUserVariable( self.get_function().handle, &raw_var, - &raw_var_type as *const _ as *mut _, + &mut owned_raw_var_ty, name.as_ref().as_ptr() as *const _, ignore_disjoint_uses, ) @@ -186,8 +184,10 @@ impl MediumLevelILFunction { /// # let mlil_fun: MediumLevelILFunction = todo!(); /// let user_var_val = mlil_fun.user_var_values().iter().next().unwrap(); /// let def_address = user_var_val.def_site.addr; - /// let var_value = PossibleValueSet::ConstantValue{value: 5}; - /// mlil_fun.set_user_var_value(&user_var_val.variable, def_address, var_value).unwrap(); + /// let var_value = PossibleValueSet::ConstantValue { value: 5 }; + /// mlil_fun + /// .set_user_var_value(&user_var_val.variable, def_address, var_value) + /// .unwrap(); /// ``` pub fn set_user_var_value( &self, @@ -209,9 +209,9 @@ impl MediumLevelILFunction { address: addr, }; let raw_var = BNVariable::from(var); - let raw_value = BNPossibleValueSet::from(value); - + let raw_value = PossibleValueSet::into_raw(value); unsafe { BNSetUserVariableValue(function.handle, &raw_var, &def_site, &raw_value) } + PossibleValueSet::free_owned_raw(raw_value); Ok(()) } @@ -264,15 +264,14 @@ impl MediumLevelILFunction { var_type: T, name: S, ) { - let var_type: Conf<&Type> = var_type.into(); - let mut var_type = var_type.into(); + let mut owned_raw_var_ty = Conf::<&Type>::into_raw(var_type.into()); let name = name.into_bytes_with_nul(); let name_c_str = name.as_ref(); unsafe { BNCreateAutoStackVariable( self.get_function().handle, offset, - &mut var_type, + &mut owned_raw_var_ty, name_c_str.as_ptr() as *const c_char, ) } @@ -290,15 +289,14 @@ impl MediumLevelILFunction { ignore_disjoint_uses: bool, ) { let raw_var = BNVariable::from(var); - let var_type: Conf<&Type> = var_type.into(); - let mut var_type = var_type.into(); + let mut owned_raw_var_ty = Conf::<&Type>::into_raw(var_type.into()); let name = name.into_bytes_with_nul(); let name_c_str = name.as_ref(); unsafe { BNCreateAutoVariable( self.get_function().handle, &raw_var, - &mut var_type, + &mut owned_raw_var_ty, name_c_str.as_ptr() as *const c_char, ignore_disjoint_uses, ) diff --git a/rust/src/mlil/instruction.rs b/rust/src/mlil/instruction.rs index c55ec8cb8..47f76566c 100644 --- a/rust/src/mlil/instruction.rs +++ b/rust/src/mlil/instruction.rs @@ -1073,7 +1073,7 @@ impl MediumLevelILInstruction { /// Possible values of expression using path-sensitive static data flow analysis pub fn possible_values_with_opts(&self, options: &[DataFlowQueryOption]) -> PossibleValueSet { - let mut value = unsafe { + let value = unsafe { BNGetMediumLevelILPossibleExprValues( self.function.handle, self.index, @@ -1081,9 +1081,7 @@ impl MediumLevelILInstruction { options.len(), ) }; - let result = PossibleValueSet::from(value); - unsafe { BNFreePossibleValueSet(&mut value) } - result + PossibleValueSet::from_owned_raw(value) } pub fn possible_ssa_variable_values(&self, ssa_var: SSAVariable) -> PossibleValueSet { @@ -1096,7 +1094,7 @@ impl MediumLevelILInstruction { options: &[DataFlowQueryOption], ) -> PossibleValueSet { let raw_var = BNVariable::from(ssa_var.variable); - let mut value = unsafe { + let value = unsafe { BNGetMediumLevelILPossibleSSAVarValues( self.function.handle, &raw_var, @@ -1106,9 +1104,7 @@ impl MediumLevelILInstruction { options.len(), ) }; - let result = PossibleValueSet::from(value); - unsafe { BNFreePossibleValueSet(&mut value) } - result + PossibleValueSet::from_owned_raw(value) } /// return the variable version used at this instruction @@ -1237,7 +1233,7 @@ impl MediumLevelILInstruction { reg_id: u32, options: &[DataFlowQueryOption], ) -> PossibleValueSet { - let mut value = unsafe { + let value = unsafe { BNGetMediumLevelILPossibleRegisterValuesAtInstruction( self.function.handle, reg_id, @@ -1246,9 +1242,7 @@ impl MediumLevelILInstruction { options.len(), ) }; - let result = PossibleValueSet::from(value); - unsafe { BNFreePossibleValueSet(&mut value) } - result + PossibleValueSet::from_owned_raw(value) } pub fn possible_register_values_after(&self, reg_id: u32) -> PossibleValueSet { @@ -1260,7 +1254,7 @@ impl MediumLevelILInstruction { reg_id: u32, options: &[DataFlowQueryOption], ) -> PossibleValueSet { - let mut value = unsafe { + let value = unsafe { BNGetMediumLevelILPossibleRegisterValuesAfterInstruction( self.function.handle, reg_id, @@ -1269,9 +1263,7 @@ impl MediumLevelILInstruction { options.len(), ) }; - let result = PossibleValueSet::from(value); - unsafe { BNFreePossibleValueSet(&mut value) } - result + PossibleValueSet::from_owned_raw(value) } pub fn flag_value(&self, flag_id: u32) -> RegisterValue { @@ -1297,7 +1289,7 @@ impl MediumLevelILInstruction { flag_id: u32, options: &[DataFlowQueryOption], ) -> PossibleValueSet { - let mut value = unsafe { + let value = unsafe { BNGetMediumLevelILPossibleFlagValuesAtInstruction( self.function.handle, flag_id, @@ -1306,9 +1298,7 @@ impl MediumLevelILInstruction { options.len(), ) }; - let result = PossibleValueSet::from(value); - unsafe { BNFreePossibleValueSet(&mut value) } - result + PossibleValueSet::from_owned_raw(value) } pub fn possible_flag_values_after_with_opts( @@ -1316,7 +1306,7 @@ impl MediumLevelILInstruction { flag_id: u32, options: &[DataFlowQueryOption], ) -> PossibleValueSet { - let mut value = unsafe { + let value = unsafe { BNGetMediumLevelILPossibleFlagValuesAfterInstruction( self.function.handle, flag_id, @@ -1325,9 +1315,7 @@ impl MediumLevelILInstruction { options.len(), ) }; - let result = PossibleValueSet::from(value); - unsafe { BNFreePossibleValueSet(&mut value) } - result + PossibleValueSet::from_owned_raw(value) } pub fn stack_contents(&self, offset: i64, size: usize) -> RegisterValue { @@ -1360,7 +1348,7 @@ impl MediumLevelILInstruction { size: usize, options: &[DataFlowQueryOption], ) -> PossibleValueSet { - let mut value = unsafe { + let value = unsafe { BNGetMediumLevelILPossibleStackContentsAtInstruction( self.function.handle, offset, @@ -1370,9 +1358,7 @@ impl MediumLevelILInstruction { options.len(), ) }; - let result = PossibleValueSet::from(value); - unsafe { BNFreePossibleValueSet(&mut value) } - result + PossibleValueSet::from_owned_raw(value) } pub fn possible_stack_contents_after_with_opts( @@ -1381,7 +1367,7 @@ impl MediumLevelILInstruction { size: usize, options: &[DataFlowQueryOption], ) -> PossibleValueSet { - let mut value = unsafe { + let value = unsafe { BNGetMediumLevelILPossibleStackContentsAfterInstruction( self.function.handle, offset, @@ -1391,9 +1377,7 @@ impl MediumLevelILInstruction { options.len(), ) }; - let result = PossibleValueSet::from(value); - unsafe { BNFreePossibleValueSet(&mut value) } - result + PossibleValueSet::from_owned_raw(value) } /// Gets the unique variable for a definition instruction. This unique variable can be passed diff --git a/rust/src/platform.rs b/rust/src/platform.rs index 08d0beafe..04e9618fa 100644 --- a/rust/src/platform.rs +++ b/rust/src/platform.rs @@ -14,10 +14,6 @@ //! Contains all information related to the execution environment of the binary, mainly the calling conventions used -use binaryninjacore_sys::*; -use std::ptr::NonNull; -use std::{borrow::Borrow, ffi, ptr}; - use crate::typecontainer::TypeContainer; use crate::typeparser::{TypeParserError, TypeParserErrorSeverity, TypeParserResult}; use crate::{ @@ -28,6 +24,10 @@ use crate::{ typelibrary::TypeLibrary, types::QualifiedNameAndType, }; +use binaryninjacore_sys::*; +use std::fmt::Debug; +use std::ptr::NonNull; +use std::{borrow::Borrow, ffi, ptr}; #[derive(PartialEq, Eq, Hash)] pub struct Platform { @@ -152,9 +152,7 @@ impl Platform { let name = name.into_bytes_with_nul(); unsafe { let handle = BNCreatePlatform(arch.as_ref().handle, name.as_ref().as_ptr() as *mut _); - assert!(!handle.is_null()); - Ref::new(Self { handle }) } } @@ -239,7 +237,6 @@ impl Platform { unsafe { let mut count = 0; let handles = BNGetPlatformCallingConventions(self.handle, &mut count); - Array::new(handles, count, self.arch()) } } @@ -248,7 +245,6 @@ impl Platform { unsafe { let mut count = 0; let handles = BNGetPlatformTypes(self.handle, &mut count); - Array::new(handles, count, ()) } } @@ -257,7 +253,6 @@ impl Platform { unsafe { let mut count = 0; let handles = BNGetPlatformVariables(self.handle, &mut count); - Array::new(handles, count, ()) } } @@ -266,7 +261,6 @@ impl Platform { unsafe { let mut count = 0; let handles = BNGetPlatformFunctions(self.handle, &mut count); - Array::new(handles, count, ()) } } @@ -337,7 +331,10 @@ impl Platform { }; if success { - Ok(raw_result.into()) + let result = TypeParserResult::from_raw(&raw_result); + // NOTE: This is safe because the core allocated the TypeParserResult + TypeParserResult::free_raw(raw_result); + Ok(result) } else { assert!(!error_string.is_null()); Err(TypeParserError::new( @@ -360,13 +357,13 @@ impl Platform { let file_name_cstr = BnString::new(filename); let auto_type_source = BnString::new(auto_type_source); - let mut result = BNTypeParserResult::default(); + let mut raw_result = BNTypeParserResult::default(); let mut error_string = ptr::null_mut(); let success = unsafe { BNParseTypesFromSourceFile( self.handle, file_name_cstr.as_ptr(), - &mut result, + &mut raw_result, &mut error_string, include_dirs.as_ptr() as *mut *const ffi::c_char, include_dirs.len(), @@ -375,7 +372,10 @@ impl Platform { }; if success { - Ok(result.into()) + let result = TypeParserResult::from_raw(&raw_result); + // NOTE: This is safe because the core allocated the TypeParserResult + TypeParserResult::free_raw(raw_result); + Ok(result) } else { assert!(!error_string.is_null()); Err(TypeParserError::new( @@ -389,6 +389,15 @@ impl Platform { } } +impl Debug for Platform { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("Platform") + .field("name", &self.name()) + .field("arch", &self.arch()) + .finish() + } +} + impl ToOwned for Platform { type Owned = Ref; diff --git a/rust/src/project.rs b/rust/src/project.rs index 12eac228a..45e3a2b82 100644 --- a/rust/src/project.rs +++ b/rust/src/project.rs @@ -739,12 +739,8 @@ impl Project { /// let file = file.unwrap(); /// let file_type = file.file_type().unwrap(); /// if file_type.is_file() && !file_type.is_symlink() { - /// bulk.create_file_from_path( - /// "/bin/", - /// None, - /// &file.file_name().to_string_lossy(), - /// "", - /// ).unwrap(); + /// bulk.create_file_from_path("/bin/", None, &file.file_name().to_string_lossy(), "") + /// .unwrap(); /// } /// } /// } diff --git a/rust/src/rc.rs b/rust/src/rc.rs index 87ec99d21..a096d0629 100644 --- a/rust/src/rc.rs +++ b/rust/src/rc.rs @@ -57,9 +57,7 @@ impl Ref { pub(crate) unsafe fn into_raw(obj: Self) -> T { let res = ptr::read(&obj.contents); - mem::forget(obj); - res } } @@ -153,6 +151,15 @@ impl<'a, T> Guard<'a, T> { } } +impl Debug for Guard<'_, T> +where + T: Debug, +{ + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + self.contents.fmt(f) + } +} + #[allow(private_bounds)] impl Guard<'_, T> where diff --git a/rust/src/string.rs b/rust/src/string.rs index c660aa301..dde8f5507 100644 --- a/rust/src/string.rs +++ b/rust/src/string.rs @@ -80,11 +80,11 @@ impl BnString { /// memory previously managed by the `BnString`. /// /// This is typically used to pass a string back through the core where the core is expected to free. - pub fn into_raw(self) -> *mut c_char { - let res = self.raw; + pub fn into_raw(value: Self) -> *mut c_char { + let res = value.raw; // we're surrendering ownership over the *mut c_char to // the core, so ensure we don't free it - mem::forget(self); + mem::forget(value); res } diff --git a/rust/src/symbol.rs b/rust/src/symbol.rs index c73546f97..337e034bd 100644 --- a/rust/src/symbol.rs +++ b/rust/src/symbol.rs @@ -233,7 +233,10 @@ impl Symbol { /// ```no_run /// # use binaryninja::symbol::Symbol; /// # use binaryninja::symbol::SymbolType; - /// Symbol::builder(SymbolType::Data, "hello", 0x1337).short_name("hello").full_name("hello").create(); + /// Symbol::builder(SymbolType::Data, "hello", 0x1337) + /// .short_name("hello") + /// .full_name("hello") + /// .create(); /// ``` pub fn builder(ty: SymbolType, raw_name: &str, addr: u64) -> SymbolBuilder { SymbolBuilder::new(ty, raw_name, addr) diff --git a/rust/src/templatesimplifier.rs b/rust/src/templatesimplifier.rs index 2958161bb..dd815f69a 100644 --- a/rust/src/templatesimplifier.rs +++ b/rust/src/templatesimplifier.rs @@ -12,7 +12,7 @@ pub fn simplify_str_to_str(input: S) -> BnString { pub fn simplify_str_to_fqn(input: S, simplify: bool) -> QualifiedName { let name = input.into_bytes_with_nul(); unsafe { - QualifiedName::from(BNRustSimplifyStrToFQN( + QualifiedName::from_owned_raw(BNRustSimplifyStrToFQN( name.as_ref().as_ptr() as *mut _, simplify, )) diff --git a/rust/src/typearchive.rs b/rust/src/typearchive.rs index 4a0640fb4..3060a4798 100644 --- a/rust/src/typearchive.rs +++ b/rust/src/typearchive.rs @@ -1,6 +1,5 @@ use binaryninjacore_sys::*; use std::ffi::{c_char, c_void, CStr}; -use std::mem::ManuallyDrop; use std::path::{Path, PathBuf}; use std::ptr::NonNull; @@ -19,39 +18,6 @@ pub struct TypeArchive { handle: NonNull, } -impl Drop for TypeArchive { - fn drop(&mut self) { - unsafe { BNFreeTypeArchiveReference(self.as_raw()) } - } -} - -impl Clone for TypeArchive { - fn clone(&self) -> Self { - unsafe { Self::from_raw(NonNull::new(BNNewTypeArchiveReference(self.as_raw())).unwrap()) } - } -} - -impl PartialEq for TypeArchive { - fn eq(&self, other: &Self) -> bool { - self.id() == other.id() - } -} -impl Eq for TypeArchive {} - -impl core::hash::Hash for TypeArchive { - fn hash(&self, state: &mut H) { - (self.handle.as_ptr() as usize).hash(state); - } -} - -impl core::fmt::Debug for TypeArchive { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - f.debug_struct("TypeArchive") - .field("path", &self.path()) - .finish() - } -} - impl TypeArchive { pub(crate) unsafe fn from_raw(handle: NonNull) -> Self { Self { handle } @@ -193,48 +159,53 @@ impl TypeArchive { (!result.is_null()).then(|| unsafe { Array::new(result, count, ()) }) } - /// Add named types to the type archive. Type must have all dependant named types added + /// Add a named type to the type archive. Type must have all dependant named types added /// prior to being added, or this function will fail. /// If the type already exists, it will be overwritten. /// - /// * `name` - Name of new type - /// * `type` - Definition of new type - pub fn add_type(&self, name: &QualifiedNameAndType) { - self.add_types(core::slice::from_ref(name)) + /// * `named_type` - Named type to add + pub fn add_type(&self, named_type: QualifiedNameAndType) -> bool { + self.add_types(vec![named_type]) } /// Add named types to the type archive. Types must have all dependant named /// types prior to being added, or included in the list, or this function will fail. /// Types already existing with any added names will be overwritten. /// - /// * `new_types` - Names and definitions of new types - pub fn add_types(&self, new_types: &[QualifiedNameAndType]) { - // SAFETY BNQualifiedNameAndType and QualifiedNameAndType are transparent - let new_types_raw: &[BNQualifiedNameAndType] = unsafe { std::mem::transmute(new_types) }; + /// * `named_types` - Names and definitions of new types + pub fn add_types(&self, named_types: Vec) -> bool { + let new_types_raw: Vec<_> = named_types + .into_iter() + .map(QualifiedNameAndType::into_raw) + .collect(); let result = unsafe { - BNAddTypeArchiveTypes(self.as_raw(), new_types_raw.as_ptr(), new_types.len()) + BNAddTypeArchiveTypes(self.as_raw(), new_types_raw.as_ptr(), new_types_raw.len()) }; - assert!(result); + for new_type in new_types_raw { + QualifiedNameAndType::free_raw(new_type); + } + result } - /// Change the name of an existing type in the type archive. + /// Change the name of an existing type in the type archive. Returns false if failed. /// /// * `old_name` - Old type name in archive /// * `new_name` - New type name - pub fn rename_type(&self, old_name: &QualifiedName, new_name: &QualifiedNameAndType) { - let id = self - .get_type_id(old_name, self.current_snapshot_id()) - .unwrap(); - self.rename_type_by_id(id, &new_name.name) + pub fn rename_type(&self, old_name: QualifiedName, new_name: QualifiedName) -> bool { + if let Some(id) = self.get_type_id(old_name, self.current_snapshot_id()) { + self.rename_type_by_id(id, new_name) + } else { + false + } } - /// Change the name of an existing type in the type archive. + /// Change the name of an existing type in the type archive. Returns false if failed. /// /// * `id` - Old id of type in archive /// * `new_name` - New type name - pub fn rename_type_by_id(&self, id: S, new_name: &QualifiedName) { + pub fn rename_type_by_id(&self, id: S, new_name: QualifiedName) -> bool { let id = id.into_bytes_with_nul(); - let raw_name = BNQualifiedName::from(new_name); + let raw_name = QualifiedName::into_raw(new_name); let result = unsafe { BNRenameTypeArchiveType( self.as_raw(), @@ -242,25 +213,26 @@ impl TypeArchive { &raw_name, ) }; - assert!(result); + QualifiedName::free_raw(raw_name); + result } /// Delete an existing type in the type archive. - pub fn delete_type(&self, name: &QualifiedName) { - let id = self.get_type_id(name, self.current_snapshot_id()); - let Some(id) = id else { - panic!("Unknown type {}", name) - }; - self.delete_type_by_id(id); + pub fn delete_type(&self, name: QualifiedName) -> bool { + if let Some(type_id) = self.get_type_id(name, self.current_snapshot_id()) { + self.delete_type_by_id(type_id) + } else { + false + } } /// Delete an existing type in the type archive. - pub fn delete_type_by_id(&self, id: S) { + pub fn delete_type_by_id(&self, id: S) -> bool { let id = id.into_bytes_with_nul(); let result = unsafe { BNDeleteTypeArchiveType(self.as_raw(), id.as_ref().as_ptr() as *const c_char) }; - assert!(result); + result } /// Retrieve a stored type in the archive @@ -269,11 +241,11 @@ impl TypeArchive { /// * `snapshot` - Snapshot id to search for types pub fn get_type_by_name( &self, - name: &QualifiedName, + name: QualifiedName, snapshot: S, ) -> Option> { let snapshot = snapshot.into_bytes_with_nul(); - let raw_name = BNQualifiedName::from(name); + let raw_name = QualifiedName::into_raw(name); let result = unsafe { BNGetTypeArchiveTypeByName( self.as_raw(), @@ -281,6 +253,7 @@ impl TypeArchive { snapshot.as_ref().as_ptr() as *const c_char, ) }; + QualifiedName::free_raw(raw_name); (!result.is_null()).then(|| unsafe { Type::ref_from_raw(result) }) } @@ -323,7 +296,7 @@ impl TypeArchive { snapshot.as_ref().as_ptr() as *const c_char, ) }; - QualifiedName::from(result) + QualifiedName::from_owned_raw(result) } /// Retrieve a type's id by its name @@ -332,11 +305,11 @@ impl TypeArchive { /// * `snapshot` - Snapshot id to search for types pub fn get_type_id( &self, - name: &QualifiedName, + name: QualifiedName, snapshot: S, ) -> Option { let snapshot = snapshot.into_bytes_with_nul(); - let raw_name = BNQualifiedName::from(name); + let raw_name = QualifiedName::into_raw(name); let result = unsafe { BNGetTypeArchiveTypeId( self.as_raw(), @@ -344,6 +317,7 @@ impl TypeArchive { snapshot.as_ref().as_ptr() as *const c_char, ) }; + QualifiedName::free_raw(raw_name); (!result.is_null()).then(|| unsafe { BnString::from_raw(result) }) } @@ -755,6 +729,42 @@ impl TypeArchive { } } +impl Drop for TypeArchive { + fn drop(&mut self) { + unsafe { BNFreeTypeArchiveReference(self.as_raw()) } + } +} + +impl Clone for TypeArchive { + fn clone(&self) -> Self { + unsafe { Self::from_raw(NonNull::new(BNNewTypeArchiveReference(self.as_raw())).unwrap()) } + } +} + +impl PartialEq for TypeArchive { + fn eq(&self, other: &Self) -> bool { + self.id() == other.id() + } +} +impl Eq for TypeArchive {} + +impl core::hash::Hash for TypeArchive { + fn hash(&self, state: &mut H) { + (self.handle.as_ptr() as usize).hash(state); + } +} + +impl core::fmt::Debug for TypeArchive { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("TypeArchive") + .field("id", &self.id()) + .field("path", &self.path()) + .field("current_snapshot_id", &self.current_snapshot_id()) + .field("platform", &self.platform()) + .finish() + } +} + impl CoreArrayProvider for TypeArchive { type Raw = *mut BNTypeArchive; type Context = (); @@ -929,8 +939,10 @@ unsafe extern "C" fn cb_type_renamed( new_name: *const BNQualifiedName, ) { let ctxt: &mut T = &mut *(ctxt as *mut T); - let old_name = ManuallyDrop::new(QualifiedName::from(*old_name)); - let new_name = ManuallyDrop::new(QualifiedName::from(*new_name)); + // `old_name` is freed by the caller + let old_name = QualifiedName::from_raw(&*old_name); + // `new_name` is freed by the caller + let new_name = QualifiedName::from_raw(&*new_name); ctxt.type_renamed( unsafe { TypeArchive::ref_from_raw(&archive) }, unsafe { CStr::from_ptr(id).to_string_lossy().as_ref() }, diff --git a/rust/src/typecontainer.rs b/rust/src/typecontainer.rs index bd71b177f..3acb79747 100644 --- a/rust/src/typecontainer.rs +++ b/rust/src/typecontainer.rs @@ -16,6 +16,7 @@ use crate::types::{QualifiedName, QualifiedNameAndType, Type}; use binaryninjacore_sys::*; use std::collections::HashMap; use std::ffi::{c_char, c_void}; +use std::fmt::{Debug, Formatter}; use std::ptr::NonNull; pub type TypeContainerType = BNTypeContainerType; @@ -88,7 +89,10 @@ impl TypeContainer { .into_iter() .map(|t| { let t = t.into(); - (BNQualifiedName::from(t.name), t.ty.handle) + ( + QualifiedName::into_raw(t.name), + unsafe { Ref::into_raw(t.ty) }.handle, + ) }) .unzip(); @@ -121,7 +125,10 @@ impl TypeContainer { .into_iter() .map(|t| { let t = t.into(); - (BNQualifiedName::from(t.name), t.ty.handle) + ( + QualifiedName::into_raw(t.name), + unsafe { Ref::into_raw(t.ty) }.handle, + ) }) .unzip(); @@ -153,14 +160,16 @@ impl TypeContainer { type_id: S, ) -> bool { let type_id = type_id.into_bytes_with_nul(); - let raw_name = BNQualifiedName::from(name.into()); - unsafe { + let raw_name = QualifiedName::into_raw(name.into()); + let success = unsafe { BNTypeContainerRenameType( self.handle.as_ptr(), type_id.as_ref().as_ptr() as *const c_char, &raw_name, ) - } + }; + QualifiedName::free_raw(raw_name); + success } /// Delete a type in the Type Container. Behavior of references to this type is @@ -182,9 +191,10 @@ impl TypeContainer { /// If no type with that name exists, returns None. pub fn type_id>(&self, name: T) -> Option { let mut result = std::ptr::null_mut(); - let raw_name = BNQualifiedName::from(name.into()); + let raw_name = QualifiedName::into_raw(name.into()); let success = unsafe { BNTypeContainerGetTypeId(self.handle.as_ptr(), &raw_name, &mut result) }; + QualifiedName::free_raw(raw_name); success.then(|| unsafe { BnString::from_raw(result) }) } @@ -201,7 +211,7 @@ impl TypeContainer { &mut result, ) }; - success.then(|| QualifiedName::from(result)) + success.then(|| QualifiedName::from_owned_raw(result)) } /// Get the definition of the type in the Type Container with the given id. @@ -225,14 +235,14 @@ impl TypeContainer { /// If no type with that name exists, returns None. pub fn type_by_name>(&self, name: T) -> Option> { let mut result = std::ptr::null_mut(); - let raw_name = BNQualifiedName::from(name.into()); + let raw_name = QualifiedName::into_raw(name.into()); let success = unsafe { BNTypeContainerGetTypeByName(self.handle.as_ptr(), &raw_name, &mut result) }; + QualifiedName::free_raw(raw_name); success.then(|| unsafe { Type::ref_from_raw(result) }) } /// Get a mapping of all types in a Type Container. - // TODO: This needs to be redone... somehow all of these need to be merged into one array... pub fn types(&self) -> Option)>> { let mut type_ids = std::ptr::null_mut(); let mut type_names = std::ptr::null_mut(); @@ -255,7 +265,7 @@ impl TypeContainer { for (idx, raw_id) in raw_ids.iter().enumerate() { let id = raw_to_string(*raw_id).expect("Valid string"); // Take the qualified name as a ref as the name should not be freed. - let name = QualifiedName::from(&raw_names[idx]); + let name = QualifiedName::from_raw(&raw_names[idx]); // Take the type as an owned ref, as the returned type was not already incremented. let ty = Type::from_raw(raw_types[idx]).to_owned(); map.insert(id, (name, ty)); @@ -332,7 +342,7 @@ impl TypeContainer { ) }; if success { - Ok(QualifiedNameAndType::from(result)) + Ok(QualifiedNameAndType::from_owned_raw(result)) } else { assert!(!errors.is_null()); Err(unsafe { Array::new(errors, error_count, ()) }) @@ -405,7 +415,10 @@ impl TypeContainer { ) }; if success { - Ok(raw_result.into()) + let result = TypeParserResult::from_raw(&raw_result); + // NOTE: This is safe because the core allocated the TypeParserResult + TypeParserResult::free_raw(raw_result); + Ok(result) } else { assert!(!errors.is_null()); Err(unsafe { Array::new(errors, error_count, ()) }) @@ -413,6 +426,17 @@ impl TypeContainer { } } +impl Debug for TypeContainer { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + f.debug_struct("TypeContainer") + .field("id", &self.id()) + .field("name", &self.name()) + .field("container_type", &self.container_type()) + .field("is_mutable", &self.is_mutable()) + .finish() + } +} + impl Drop for TypeContainer { fn drop(&mut self) { unsafe { BNFreeTypeContainer(self.handle.as_ptr()) } diff --git a/rust/src/typelibrary.rs b/rust/src/typelibrary.rs index 4561877d9..ee978513e 100644 --- a/rust/src/typelibrary.rs +++ b/rust/src/typelibrary.rs @@ -272,9 +272,10 @@ impl TypeLibrary { /// To add types and objects from an existing BinaryView, it is recommended to use /// `export_object_to_library `, which will automatically pull in /// all referenced types and record additional dependencies as needed. - pub fn add_named_object(&self, name: &QualifiedName, type_: &Type) { - let mut raw_name = BNQualifiedName::from(name); + pub fn add_named_object(&self, name: QualifiedName, type_: &Type) { + let mut raw_name = QualifiedName::into_raw(name); unsafe { BNAddTypeLibraryNamedObject(self.as_raw(), &mut raw_name, type_.handle) } + QualifiedName::free_raw(raw_name); } /// Directly inserts a named object into the type library's object store. @@ -284,9 +285,10 @@ impl TypeLibrary { /// To add types and objects from an existing BinaryView, it is recommended to use /// `export_type_to_library `, which will automatically pull in /// all referenced types and record additional dependencies as needed. - pub fn add_named_type(&self, name: &QualifiedName, type_: &Type) { - let mut raw_name = BNQualifiedName::from(name); + pub fn add_named_type(&self, name: QualifiedName, type_: &Type) { + let mut raw_name = QualifiedName::into_raw(name); unsafe { BNAddTypeLibraryNamedType(self.as_raw(), &mut raw_name, type_.handle) } + QualifiedName::free_raw(raw_name); } /// Manually flag NamedTypeReferences to the given QualifiedName as originating from another source @@ -297,9 +299,9 @@ impl TypeLibrary { /// Use this api with extreme caution. /// /// - pub fn add_type_source(&self, name: &QualifiedName, source: S) { + pub fn add_type_source(&self, name: QualifiedName, source: S) { let source = source.into_bytes_with_nul(); - let mut raw_name = BNQualifiedName::from(name); + let mut raw_name = QualifiedName::into_raw(name); unsafe { BNAddTypeLibraryNamedTypeSource( self.as_raw(), @@ -307,23 +309,26 @@ impl TypeLibrary { source.as_ref().as_ptr() as *const ffi::c_char, ) } + QualifiedName::free_raw(raw_name); } /// Direct extracts a reference to a contained object -- when /// attempting to extract types from a library into a BinaryView, consider using /// `import_library_object ` instead. - pub fn get_named_object(&self, name: &QualifiedName) -> Option> { - let mut raw_name = BNQualifiedName::from(name); + pub fn get_named_object(&self, name: QualifiedName) -> Option> { + let mut raw_name = QualifiedName::into_raw(name); let t = unsafe { BNGetTypeLibraryNamedObject(self.as_raw(), &mut raw_name) }; + QualifiedName::free_raw(raw_name); (!t.is_null()).then(|| unsafe { Type::ref_from_raw(t) }) } /// Direct extracts a reference to a contained type -- when /// attempting to extract types from a library into a BinaryView, consider using /// `import_library_type ` instead. - pub fn get_named_type(&self, name: &QualifiedName) -> Option> { - let mut raw_name = BNQualifiedName::from(name); + pub fn get_named_type(&self, name: QualifiedName) -> Option> { + let mut raw_name = QualifiedName::into_raw(name); let t = unsafe { BNGetTypeLibraryNamedType(self.as_raw(), &mut raw_name) }; + QualifiedName::free_raw(raw_name); (!t.is_null()).then(|| unsafe { Type::ref_from_raw(t) }) } diff --git a/rust/src/typeparser.rs b/rust/src/typeparser.rs index 111ad9df8..7d597ea46 100644 --- a/rust/src/typeparser.rs +++ b/rust/src/typeparser.rs @@ -1,3 +1,4 @@ +#![allow(unused)] use binaryninjacore_sys::*; use std::ffi::{c_char, c_void}; use std::fmt::Debug; @@ -159,7 +160,10 @@ impl TypeParser for CoreTypeParser { ) }; if success { - Ok(raw_result.into()) + let result = TypeParserResult::from_raw(&raw_result); + // NOTE: This is safe because the core allocated the TypeParserResult + TypeParserResult::free_raw(raw_result); + Ok(result) } else { let errors: Array = unsafe { Array::new(errors, error_count, ()) }; Err(errors.to_vec()) @@ -188,7 +192,7 @@ impl TypeParser for CoreTypeParser { ) }; if result { - Ok(QualifiedNameAndType::from(output)) + Ok(QualifiedNameAndType::from_owned_raw(output)) } else { let errors: Array = unsafe { Array::new(errors, error_count, ()) }; Err(errors.to_vec()) @@ -292,55 +296,50 @@ pub struct TypeParserError { } impl TypeParserError { - pub fn new( - severity: TypeParserErrorSeverity, - message: String, - file_name: String, - line: u64, - column: u64, - ) -> Self { - Self { - severity, - message, - file_name, - line, - column, - } - } -} - -impl From for TypeParserError { - fn from(value: BNTypeParserError) -> Self { + pub(crate) fn from_raw(value: &BNTypeParserError) -> Self { Self { severity: value.severity, - message: unsafe { BnString::from_raw(value.message).to_string() }, - file_name: unsafe { BnString::from_raw(value.fileName).to_string() }, + message: raw_to_string(value.message).unwrap(), + file_name: raw_to_string(value.fileName).unwrap(), line: value.line, column: value.column, } } -} -impl From<&BNTypeParserError> for TypeParserError { - fn from(value: &BNTypeParserError) -> Self { - Self { + pub(crate) fn from_owned_raw(value: BNTypeParserError) -> Self { + let owned = Self::from_raw(&value); + Self::free_raw(value); + owned + } + + pub(crate) fn into_raw(value: Self) -> BNTypeParserError { + BNTypeParserError { severity: value.severity, - message: raw_to_string(value.message).unwrap(), - file_name: raw_to_string(value.fileName).unwrap(), + message: BnString::into_raw(BnString::new(value.message)), + fileName: BnString::into_raw(BnString::new(value.file_name)), line: value.line, column: value.column, } } -} -impl From for BNTypeParserError { - fn from(value: TypeParserError) -> Self { + pub(crate) fn free_raw(value: BNTypeParserError) { + let _ = unsafe { BnString::from_raw(value.message) }; + let _ = unsafe { BnString::from_raw(value.fileName) }; + } + + pub fn new( + severity: TypeParserErrorSeverity, + message: String, + file_name: String, + line: u64, + column: u64, + ) -> Self { Self { - severity: value.severity, - message: BnString::new(value.message).into_raw(), - fileName: BnString::new(value.file_name).into_raw(), - line: value.line, - column: value.column, + severity, + message, + file_name, + line, + column, } } } @@ -357,7 +356,7 @@ unsafe impl CoreArrayProviderInner for TypeParserError { } unsafe fn wrap_raw<'a>(raw: &'a Self::Raw, _context: &'a Self::Context) -> Self::Wrapped<'a> { - Self::from(raw) + Self::from_raw(raw) } } @@ -368,21 +367,85 @@ pub struct TypeParserResult { pub functions: Vec, } -impl From for TypeParserResult { - fn from(mut value: BNTypeParserResult) -> Self { +impl TypeParserResult { + pub(crate) fn from_raw(value: &BNTypeParserResult) -> Self { let raw_types = unsafe { std::slice::from_raw_parts(value.types, value.typeCount) }; + let types = raw_types.iter().map(ParsedType::from_raw).collect(); let raw_variables = unsafe { std::slice::from_raw_parts(value.variables, value.variableCount) }; + let variables = raw_variables.iter().map(ParsedType::from_raw).collect(); let raw_functions = unsafe { std::slice::from_raw_parts(value.functions, value.functionCount) }; - let result = TypeParserResult { - types: raw_types.iter().map(|t| ParsedType::from(t)).collect(), - variables: raw_variables.iter().map(|t| ParsedType::from(t)).collect(), - functions: raw_functions.iter().map(|t| ParsedType::from(t)).collect(), - }; + let functions = raw_functions.iter().map(ParsedType::from_raw).collect(); + TypeParserResult { + types, + variables, + functions, + } + } + + /// Return a rust allocated type parser result, free using [`Self::free_owned_raw`]. + /// + /// Under no circumstance should you call [`Self::free_raw`] on the returned result. + pub(crate) fn into_raw(value: Self) -> BNTypeParserResult { + let boxed_raw_types: Box<[BNParsedType]> = value + .types + .into_iter() + // NOTE: Freed with [`Self::free_owned_raw`]. + .map(ParsedType::into_raw) + .collect(); + let boxed_raw_variables: Box<[BNParsedType]> = value + .variables + .into_iter() + // NOTE: Freed with [`Self::free_owned_raw`]. + .map(ParsedType::into_raw) + .collect(); + let boxed_raw_functions: Box<[BNParsedType]> = value + .functions + .into_iter() + // NOTE: Freed with [`Self::free_owned_raw`]. + .map(ParsedType::into_raw) + .collect(); + BNTypeParserResult { + typeCount: boxed_raw_types.len(), + // NOTE: Freed with [`Self::free_owned_raw`]. + types: Box::leak(boxed_raw_types).as_mut_ptr(), + variableCount: boxed_raw_variables.len(), + // NOTE: Freed with [`Self::free_owned_raw`]. + variables: Box::leak(boxed_raw_variables).as_mut_ptr(), + functionCount: boxed_raw_functions.len(), + // NOTE: Freed with [`Self::free_owned_raw`]. + functions: Box::leak(boxed_raw_functions).as_mut_ptr(), + } + } + + pub(crate) fn free_raw(mut value: BNTypeParserResult) { // SAFETY: `value` must be a properly initialized BNTypeParserResult. + // SAFETY: `value` must be core allocated. unsafe { BNFreeTypeParserResult(&mut value) }; - result + } + + pub(crate) fn free_owned_raw(value: BNTypeParserResult) { + let raw_types = std::ptr::slice_from_raw_parts_mut(value.types, value.typeCount); + // Free the rust allocated types list + let boxed_types = unsafe { Box::from_raw(raw_types) }; + for parsed_type in boxed_types { + ParsedType::free_raw(parsed_type); + } + let raw_variables = + std::ptr::slice_from_raw_parts_mut(value.variables, value.variableCount); + // Free the rust allocated variables list + let boxed_variables = unsafe { Box::from_raw(raw_variables) }; + for parsed_type in boxed_variables { + ParsedType::free_raw(parsed_type); + } + let raw_functions = + std::ptr::slice_from_raw_parts_mut(value.functions, value.functionCount); + // Free the rust allocated functions list + let boxed_functions = unsafe { Box::from_raw(raw_functions) }; + for parsed_type in boxed_functions { + ParsedType::free_raw(parsed_type); + } } } @@ -394,39 +457,36 @@ pub struct ParsedType { } impl ParsedType { - pub fn new(name: QualifiedName, ty: Ref, user: bool) -> Self { - Self { name, ty, user } - } -} - -impl From for ParsedType { - fn from(value: BNParsedType) -> Self { + pub(crate) fn from_raw(value: &BNParsedType) -> Self { Self { - name: value.name.into(), - ty: unsafe { Type::ref_from_raw(value.type_) }, + name: QualifiedName::from_raw(&value.name), + ty: unsafe { Type::from_raw(value.type_).to_owned() }, user: value.isUser, } } -} -impl From<&BNParsedType> for ParsedType { - fn from(value: &BNParsedType) -> Self { - Self { - name: QualifiedName::from(&value.name), - ty: unsafe { Type::from_raw(value.type_).to_owned() }, - user: value.isUser, - } + pub(crate) fn from_owned_raw(value: BNParsedType) -> Self { + let owned = Self::from_raw(&value); + Self::free_raw(value); + owned } -} -impl From for BNParsedType { - fn from(value: ParsedType) -> Self { - Self { - name: value.name.into(), - type_: value.ty.handle, + pub(crate) fn into_raw(value: Self) -> BNParsedType { + BNParsedType { + name: QualifiedName::into_raw(value.name), + type_: unsafe { Ref::into_raw(value.ty) }.handle, isUser: value.user, } } + + pub(crate) fn free_raw(value: BNParsedType) { + QualifiedName::free_raw(value.name); + let _ = unsafe { Type::ref_from_raw(value.type_) }; + } + + pub fn new(name: QualifiedName, ty: Ref, user: bool) -> Self { + Self { name, ty, user } + } } impl CoreArrayProvider for ParsedType { @@ -442,7 +502,7 @@ unsafe impl CoreArrayProviderInner for ParsedType { } unsafe fn wrap_raw<'b>(raw: &'b Self::Raw, _context: &'b Self::Context) -> Self::Wrapped<'b> { - ParsedType::from(raw) + ParsedType::from_raw(raw) } } @@ -456,7 +516,7 @@ unsafe extern "C" fn cb_get_option_text( if let Some(inner_result) = ctxt.get_option_text(option, &raw_to_string(value).unwrap()) { let bn_inner_result = BnString::new(inner_result); // NOTE: Dropped by `cb_free_string` - *result = bn_inner_result.into_raw(); + *result = BnString::into_raw(bn_inner_result); true } else { *result = std::ptr::null_mut(); @@ -503,15 +563,19 @@ unsafe extern "C" fn cb_preprocess_source( Ok(inner_result) => { let bn_inner_result = BnString::new(inner_result); // NOTE: Dropped by `cb_free_string` - *result = bn_inner_result.into_raw(); + *result = BnString::into_raw(bn_inner_result); *errors = std::ptr::null_mut(); *error_count = 0; true } Err(inner_errors) => { - *error_count = inner_errors.len(); - let inner_errors: Box<[_]> = inner_errors.into_iter().map(Into::into).collect(); *result = std::ptr::null_mut(); + *error_count = inner_errors.len(); + // NOTE: Leaking errors here, dropped by `cb_free_error_list`. + let inner_errors: Box<[_]> = inner_errors + .into_iter() + .map(TypeParserError::into_raw) + .collect(); // NOTE: Dropped by `cb_free_error_list` *errors = Box::leak(inner_errors).as_mut_ptr(); false @@ -558,43 +622,17 @@ unsafe extern "C" fn cb_parse_types_from_source( &raw_to_string(auto_type_source).unwrap(), ) { Ok(type_parser_result) => { - let boxed_raw_types: Box<[BNParsedType]> = type_parser_result - .types - .into_iter() - .map(Into::into) - .collect(); - let boxed_raw_variables: Box<[BNParsedType]> = type_parser_result - .variables - .into_iter() - .map(Into::into) - .collect(); - let boxed_raw_functions: Box<[BNParsedType]> = type_parser_result - .functions - .into_iter() - .map(Into::into) - .collect(); - let type_count = boxed_raw_types.len(); - let variable_count = boxed_raw_variables.len(); - let function_count = boxed_raw_functions.len(); - let raw_result = BNTypeParserResult { - // NOTE: Freed with `cb_free_result`. - types: Box::leak(boxed_raw_types).as_mut_ptr(), - // NOTE: Freed with `cb_free_result`. - variables: Box::leak(boxed_raw_variables).as_mut_ptr(), - // NOTE: Freed with `cb_free_result`. - functions: Box::leak(boxed_raw_functions).as_mut_ptr(), - typeCount: type_count, - variableCount: variable_count, - functionCount: function_count, - }; - *result = raw_result; + *result = TypeParserResult::into_raw(type_parser_result); *errors = std::ptr::null_mut(); *error_count = 0; true } Err(inner_errors) => { *error_count = inner_errors.len(); - let inner_errors: Box<[_]> = inner_errors.into_iter().map(Into::into).collect(); + let inner_errors: Box<[_]> = inner_errors + .into_iter() + .map(TypeParserError::into_raw) + .collect(); *result = Default::default(); // NOTE: Dropped by cb_free_error_list *errors = Box::leak(inner_errors).as_mut_ptr(); @@ -618,14 +656,17 @@ unsafe extern "C" fn cb_parse_type_string( let existing_types = TypeContainer::from_raw(existing_types_ptr); match ctxt.parse_type_string(&raw_to_string(source).unwrap(), &platform, &existing_types) { Ok(inner_result) => { - *result = inner_result.into(); + *result = QualifiedNameAndType::into_raw(inner_result); *errors = std::ptr::null_mut(); *error_count = 0; true } Err(inner_errors) => { *error_count = inner_errors.len(); - let inner_errors: Box<[_]> = inner_errors.into_iter().map(Into::into).collect(); + let inner_errors: Box<[_]> = inner_errors + .into_iter() + .map(TypeParserError::into_raw) + .collect(); *result = Default::default(); // NOTE: Dropped by cb_free_error_list *errors = Box::leak(inner_errors).as_mut_ptr(); @@ -640,7 +681,7 @@ unsafe extern "C" fn cb_free_string(_ctxt: *mut c_void, string: *mut c_char) { } unsafe extern "C" fn cb_free_result(_ctxt: *mut c_void, result: *mut BNTypeParserResult) { - let _ = Box::from_raw(result); + TypeParserResult::free_owned_raw(*result); } unsafe extern "C" fn cb_free_error_list( @@ -649,5 +690,8 @@ unsafe extern "C" fn cb_free_error_list( error_count: usize, ) { let errors = std::ptr::slice_from_raw_parts_mut(errors, error_count); - let _ = Box::from_raw(errors); + let boxed_errors = Box::from_raw(errors); + for error in boxed_errors { + TypeParserError::free_raw(error); + } } diff --git a/rust/src/typeprinter.rs b/rust/src/typeprinter.rs index ffbd0d06e..44fb3173b 100644 --- a/rust/src/typeprinter.rs +++ b/rust/src/typeprinter.rs @@ -1,3 +1,5 @@ +#![allow(unused)] + use crate::binaryview::BinaryView; use crate::disassembly::InstructionTextToken; use crate::platform::Platform; @@ -81,7 +83,7 @@ impl CoreTypePrinter { ) -> Option> { let mut result_count = 0; let mut result = std::ptr::null_mut(); - let mut raw_name = BNQualifiedName::from(name.into()); + let mut raw_name = QualifiedName::into_raw(name.into()); let success = unsafe { BNGetTypePrinterTypeTokens( self.handle.as_ptr(), @@ -94,6 +96,7 @@ impl CoreTypePrinter { &mut result_count, ) }; + QualifiedName::free_raw(raw_name); success.then(|| { assert!(!result.is_null()); unsafe { Array::new(result, result_count, ()) } @@ -164,7 +167,7 @@ impl CoreTypePrinter { escaping: TokenEscapingType, ) -> Option { let mut result = std::ptr::null_mut(); - let mut raw_name = BNQualifiedName::from(name.into()); + let mut raw_name = QualifiedName::into_raw(name.into()); let success = unsafe { BNGetTypePrinterTypeString( self.handle.as_ptr(), @@ -175,6 +178,7 @@ impl CoreTypePrinter { &mut result, ) }; + QualifiedName::free_raw(raw_name); success.then(|| unsafe { assert!(!result.is_null()); BnString::from_raw(result) @@ -236,7 +240,7 @@ impl CoreTypePrinter { ) -> Option> { let mut result_count = 0; let mut result = std::ptr::null_mut(); - let mut raw_name = BNQualifiedName::from(name.into()); + let mut raw_name = QualifiedName::into_raw(name.into()); let success = unsafe { BNGetTypePrinterTypeLines( self.handle.as_ptr(), @@ -250,6 +254,7 @@ impl CoreTypePrinter { &mut result_count, ) }; + QualifiedName::free_raw(raw_name); success.then(|| { assert!(!result.is_null()); unsafe { Array::::new(result, result_count, ()) } @@ -277,7 +282,11 @@ impl CoreTypePrinter { let (mut raw_names, mut raw_types): (Vec, Vec<_>) = types .map(|t| { let t = t.into(); - (BNQualifiedName::from(t.name), t.ty.handle) + // Leak both to the core and then free afterwards. + ( + QualifiedName::into_raw(t.name), + unsafe { Ref::into_raw(t.ty) }.handle, + ) }) .unzip(); let success = unsafe { @@ -292,6 +301,12 @@ impl CoreTypePrinter { &mut result, ) }; + for raw_name in raw_names { + QualifiedName::free_raw(raw_name); + } + for raw_type in raw_types { + let _ = unsafe { Type::ref_from_raw(raw_type) }; + } success.then(|| unsafe { assert!(!result.is_null()); BnString::from_raw(result) @@ -306,15 +321,20 @@ impl CoreTypePrinter { escaping: TokenEscapingType, ) -> Option where - T: Iterator, + T: IntoIterator, I: Into, { let mut result = std::ptr::null_mut(); // TODO: I dislike how this iter unzip looks like... but its how to avoid allocating again... let (mut raw_names, mut raw_types): (Vec, Vec<_>) = types + .into_iter() .map(|t| { let t = t.into(); - (BNQualifiedName::from(t.name), t.ty.handle) + // Leak both to the core and then free afterwards. + ( + QualifiedName::into_raw(t.name), + unsafe { Ref::into_raw(t.ty) }.handle, + ) }) .unzip(); let success = unsafe { @@ -329,6 +349,12 @@ impl CoreTypePrinter { &mut result, ) }; + for raw_name in raw_names { + QualifiedName::free_raw(raw_name); + } + for raw_type in raw_types { + let _ = unsafe { Type::ref_from_raw(raw_type) }; + } success.then(|| unsafe { assert!(!result.is_null()); BnString::from_raw(result) @@ -519,45 +545,15 @@ pub struct TypeDefinitionLine { } 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); - } - } -} - -impl From<&BNTypeDefinitionLine> for TypeDefinitionLine { - fn from(value: &BNTypeDefinitionLine) -> Self { + pub(crate) fn from_raw(value: &BNTypeDefinitionLine) -> Self { Self { line_type: value.lineType, tokens: { let raw_tokens = unsafe { std::slice::from_raw_parts(value.tokens, value.count) }; - raw_tokens.iter().map(Into::into).collect() + raw_tokens + .iter() + .map(InstructionTextToken::from_raw) + .collect() }, ty: unsafe { Type::from_raw(value.type_).to_owned() }, parent_type: match value.parentType.is_null() { @@ -581,45 +577,85 @@ impl From<&BNTypeDefinitionLine> for TypeDefinitionLine { field_index: value.fieldIndex, } } -} -impl From for BNTypeDefinitionLine { - fn from(value: TypeDefinitionLine) -> Self { + /// The raw value must have been allocated by rust. See [`Self::free_owned_raw`] for details. + pub(crate) fn from_owned_raw(value: BNTypeDefinitionLine) -> Self { + let owned = Self::from_raw(&value); + Self::free_owned_raw(value); + owned + } + + pub(crate) fn into_raw(value: Self) -> BNTypeDefinitionLine { // NOTE: This is leaking [BNInstructionTextToken::text], [BNInstructionTextToken::typeNames]. - let tokens: Box<[BNInstructionTextToken]> = - value.tokens.into_iter().map(Into::into).collect(); - Self { + let tokens: Box<[BNInstructionTextToken]> = value + .tokens + .into_iter() + .map(InstructionTextToken::into_raw) + .collect(); + BNTypeDefinitionLine { lineType: value.line_type, 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) }, + type_: unsafe { Ref::into_raw(value.ty) }.handle, // NOTE: This is leaking a ref to parent_type. Must free with `cb_free_lines`. parentType: value .parent_type - .map(|t| unsafe { BNNewTypeReference(t.handle) }) + .map(|t| unsafe { Ref::into_raw(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| unsafe { BNNewTypeReference(t.handle) }) + .map(|t| unsafe { Ref::into_raw(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()) + .map(|s| BnString::into_raw(BnString::new(s))) .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| unsafe { BNNewNamedTypeReference(t.handle) }) + .map(|t| unsafe { Ref::into_raw(t) }.handle) .unwrap_or(std::ptr::null_mut()), baseOffset: value.base_offset, offset: value.offset, fieldIndex: value.field_index, } } + + /// This is unique from the typical `from_raw` as the allocation of InstructionTextToken requires it be from rust, hence the "owned" free. + pub(crate) fn free_owned_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 = unsafe { 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 + let _ = unsafe { Type::ref_from_raw(raw.type_) }; + } + if !raw.parentType.is_null() { + // SAFETY: raw.parentType must have been ref incremented in conjunction with this free + let _ = unsafe { Type::ref_from_raw(raw.parentType) }; + } + if !raw.rootType.is_null() { + // SAFETY: raw.rootType must have been ref incremented in conjunction with this free + let _ = unsafe { Type::ref_from_raw(raw.rootType) }; + } + if !raw.rootTypeName.is_null() { + // SAFETY: raw.rootTypeName must have been ref incremented in conjunction with this free + let _ = unsafe { BnString::from_raw(raw.rootTypeName) }; + } + if !raw.baseType.is_null() { + // SAFETY: raw.baseType must have been ref incremented in conjunction with this free + let _ = unsafe { NamedTypeReference::ref_from_raw(raw.baseType) }; + } + } } impl CoreArrayProvider for TypeDefinitionLine { @@ -634,7 +670,7 @@ unsafe impl CoreArrayProviderInner for TypeDefinitionLine { } unsafe fn wrap_raw<'a>(raw: &'a Self::Raw, _context: &'a Self::Context) -> Self::Wrapped<'a> { - Self::from(raw) + Self::from_raw(raw) } } @@ -649,7 +685,8 @@ unsafe extern "C" fn cb_get_type_tokens( result_count: *mut usize, ) -> bool { let ctxt: &mut T = &mut *(ctxt as *mut T); - let qualified_name = QualifiedName::from(*name); + // NOTE: The caller is responsible for freeing name. + let qualified_name = QualifiedName::from_raw(&*name); let inner_result = ctxt.get_type_tokens( unsafe { Type::ref_from_raw(type_) }, match platform.is_null() { @@ -661,8 +698,10 @@ unsafe extern "C" fn cb_get_type_tokens( escaping, ); if let Some(inner_result) = inner_result { - let raw_text_tokens: Box<[BNInstructionTextToken]> = - inner_result.into_iter().map(Into::into).collect(); + let raw_text_tokens: Box<[BNInstructionTextToken]> = inner_result + .into_iter() + .map(InstructionTextToken::into_raw) + .collect(); *result_count = raw_text_tokens.len(); // NOTE: Dropped by the cb_free_tokens *result = Box::leak(raw_text_tokens).as_mut_ptr(); @@ -699,8 +738,10 @@ unsafe extern "C" fn cb_get_type_tokens_before_name( escaping, ); if let Some(inner_result) = inner_result { - let raw_text_tokens: Box<[BNInstructionTextToken]> = - inner_result.into_iter().map(Into::into).collect(); + let raw_text_tokens: Box<[BNInstructionTextToken]> = inner_result + .into_iter() + .map(InstructionTextToken::into_raw) + .collect(); *result_count = raw_text_tokens.len(); // NOTE: Dropped by the cb_free_tokens *result = Box::leak(raw_text_tokens).as_mut_ptr(); @@ -737,8 +778,10 @@ unsafe extern "C" fn cb_get_type_tokens_after_name( escaping, ); if let Some(inner_result) = inner_result { - let raw_text_tokens: Box<[BNInstructionTextToken]> = - inner_result.into_iter().map(Into::into).collect(); + let raw_text_tokens: Box<[BNInstructionTextToken]> = inner_result + .into_iter() + .map(InstructionTextToken::into_raw) + .collect(); *result_count = raw_text_tokens.len(); // NOTE: Dropped by the cb_free_tokens *result = Box::leak(raw_text_tokens).as_mut_ptr(); @@ -759,7 +802,8 @@ unsafe extern "C" fn cb_get_type_string( result: *mut *mut ::std::os::raw::c_char, ) -> bool { let ctxt: &mut T = &mut *(ctxt as *mut T); - let qualified_name = QualifiedName::from(*name); + // NOTE: The caller is responsible for freeing name. + let qualified_name = QualifiedName::from_raw(&*name); let inner_result = ctxt.get_type_string( Type::ref_from_raw(type_), match platform.is_null() { @@ -770,9 +814,9 @@ unsafe extern "C" fn cb_get_type_string( escaping, ); if let Some(inner_result) = inner_result { - // NOTE: Dropped by `cb_free_string` let raw_string = BnString::new(inner_result); - *result = raw_string.into_raw(); + // NOTE: Dropped by `cb_free_string` + *result = BnString::into_raw(raw_string); true } else { *result = std::ptr::null_mut(); @@ -799,7 +843,7 @@ unsafe extern "C" fn cb_get_type_string_before_name( if let Some(inner_result) = inner_result { // NOTE: Dropped by `cb_free_string` let raw_string = BnString::new(inner_result); - *result = raw_string.into_raw(); + *result = BnString::into_raw(raw_string); true } else { *result = std::ptr::null_mut(); @@ -824,9 +868,9 @@ unsafe extern "C" fn cb_get_type_string_after_name( escaping, ); if let Some(inner_result) = inner_result { - // NOTE: Dropped by `cb_free_string` let raw_string = BnString::new(inner_result); - *result = raw_string.into_raw(); + // NOTE: Dropped by `cb_free_string` + *result = BnString::into_raw(raw_string); true } else { *result = std::ptr::null_mut(); @@ -846,7 +890,8 @@ unsafe extern "C" fn cb_get_type_lines( result_count: *mut usize, ) -> bool { let ctxt: &mut T = &mut *(ctxt as *mut T); - let qualified_name = QualifiedName::from(*name); + // NOTE: The caller is responsible for freeing name. + let qualified_name = QualifiedName::from_raw(&*name); let types_ptr = NonNull::new(types).unwrap(); let types = TypeContainer::from_raw(types_ptr); let inner_result = ctxt.get_type_lines( @@ -858,7 +903,10 @@ unsafe extern "C" fn cb_get_type_lines( escaping, ); if let Some(inner_result) = inner_result { - let boxed_raw_lines: Box<[_]> = inner_result.into_iter().map(Into::into).collect(); + let boxed_raw_lines: Box<[_]> = inner_result + .into_iter() + .map(TypeDefinitionLine::into_raw) + .collect(); *result_count = boxed_raw_lines.len(); // NOTE: Dropped by `cb_free_lines` *result = Box::leak(boxed_raw_lines).as_mut_ptr(); @@ -882,8 +930,10 @@ unsafe extern "C" fn cb_print_all_types( ) -> bool { let ctxt: &mut T = &mut *(ctxt as *mut T); let raw_names = std::slice::from_raw_parts(names, type_count); - let names: Vec<_> = raw_names.iter().map(Into::into).collect(); + // NOTE: The caller is responsible for freeing raw_names. + let names: Vec<_> = raw_names.into_iter().map(QualifiedName::from_raw).collect(); let raw_types = std::slice::from_raw_parts(types, type_count); + // NOTE: The caller is responsible for freeing raw_types. let types: Vec<_> = raw_types.iter().map(|&t| Type::ref_from_raw(t)).collect(); let inner_result = ctxt.print_all_types( names, @@ -893,9 +943,9 @@ unsafe extern "C" fn cb_print_all_types( escaping, ); if let Some(inner_result) = inner_result { - // NOTE: Dropped by `cb_free_string` let raw_string = BnString::new(inner_result); - *result = raw_string.into_raw(); + // NOTE: Dropped by `cb_free_string` + *result = BnString::into_raw(raw_string); true } else { *result = std::ptr::null_mut(); @@ -927,8 +977,9 @@ unsafe extern "C" fn cb_free_lines( count: usize, ) { let lines = std::ptr::slice_from_raw_parts_mut(lines, count); + // SAFETY: lines must have been allocated by rust. let boxes_lines = Box::from_raw(lines); for line in boxes_lines { - TypeDefinitionLine::free_raw(line); + TypeDefinitionLine::free_owned_raw(line); } } diff --git a/rust/src/types.rs b/rust/src/types.rs index e27ddf73f..cccde1bc2 100644 --- a/rust/src/types.rs +++ b/rust/src/types.rs @@ -11,6 +11,7 @@ // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. +#![allow(unused)] // TODO : More widely enforce the use of ref_from_raw vs just from_raw to simplify internal binding usage? Perhaps remove from_raw functions? // TODO : Add documentation and fix examples @@ -127,7 +128,7 @@ impl TypeBuilder { pub fn child_type(&self) -> Option>> { let raw_target = unsafe { BNGetTypeBuilderChildType(self.handle) }; match raw_target.type_.is_null() { - false => Some(raw_target.into()), + false => Some(Conf::>::from_owned_raw(raw_target)), true => None, } } @@ -150,7 +151,9 @@ impl TypeBuilder { pub fn calling_convention(&self) -> Option>>> { let raw_convention_confidence = unsafe { BNGetTypeBuilderCallingConvention(self.handle) }; match raw_convention_confidence.convention.is_null() { - false => Some(raw_convention_confidence.into()), + false => Some(Conf::>>::from_owned_raw( + raw_convention_confidence, + )), true => None, } } @@ -162,7 +165,10 @@ impl TypeBuilder { match raw_parameters_ptr.is_null() { false => { let raw_parameters = std::slice::from_raw_parts(raw_parameters_ptr, count); - let parameters = raw_parameters.iter().map(Into::into).collect(); + let parameters = raw_parameters + .iter() + .map(FunctionParameter::from_raw) + .collect(); BNFreeTypeParameterList(raw_parameters_ptr, count); Some(parameters) } @@ -287,8 +293,9 @@ impl TypeBuilder { } } - pub fn array<'a, T: Into>>(t: T, count: u64) -> Self { - unsafe { Self::from_raw(BNCreateArrayTypeBuilder(&t.into().into(), count)) } + pub fn array<'a, T: Into>>(ty: T, count: u64) -> Self { + let owned_raw_ty = Conf::<&Type>::into_raw(ty.into()); + unsafe { Self::from_raw(BNCreateArrayTypeBuilder(&owned_raw_ty, count)) } } /// ## NOTE @@ -331,29 +338,30 @@ impl TypeBuilder { } pub fn named_type_from_type>(name: T, t: &Type) -> Self { - let mut raw_name = BNQualifiedName::from(name.into()); - // TODO: This cant be right... - let id = BnString::new(""); + let mut raw_name = QualifiedName::into_raw(name.into()); + let id = CStr::from_bytes_with_nul(b"\0").unwrap(); - unsafe { + let result = unsafe { Self::from_raw(BNCreateNamedTypeReferenceBuilderFromTypeAndId( id.as_ptr() as *mut _, &mut raw_name, t.handle, )) - } + }; + QualifiedName::free_raw(raw_name); + result } // TODO : BNCreateFunctionTypeBuilder - pub fn pointer<'a, A: Architecture, T: Into>>(arch: &A, t: T) -> Self { + pub fn pointer<'a, A: Architecture, T: Into>>(arch: &A, ty: T) -> Self { let mut is_const = Conf::new(false, MIN_CONFIDENCE).into(); let mut is_volatile = Conf::new(false, MIN_CONFIDENCE).into(); - + let owned_raw_ty = Conf::<&Type>::into_raw(ty.into()); unsafe { Self::from_raw(BNCreatePointerTypeBuilder( arch.as_ref().handle, - &t.into().into(), + &owned_raw_ty, &mut is_const, &mut is_volatile, ReferenceType::PointerReferenceType, @@ -361,14 +369,14 @@ impl TypeBuilder { } } - pub fn const_pointer<'a, A: Architecture, T: Into>>(arch: &A, t: T) -> Self { + pub fn const_pointer<'a, A: Architecture, T: Into>>(arch: &A, ty: T) -> Self { let mut is_const = Conf::new(true, MAX_CONFIDENCE).into(); let mut is_volatile = Conf::new(false, MIN_CONFIDENCE).into(); - + let owned_raw_ty = Conf::<&Type>::into_raw(ty.into()); unsafe { Self::from_raw(BNCreatePointerTypeBuilder( arch.as_ref().handle, - &t.into().into(), + &owned_raw_ty, &mut is_const, &mut is_volatile, ReferenceType::PointerReferenceType, @@ -377,7 +385,7 @@ impl TypeBuilder { } pub fn pointer_of_width<'a, T: Into>>( - t: T, + ty: T, size: usize, is_const: bool, is_volatile: bool, @@ -385,11 +393,11 @@ impl TypeBuilder { ) -> Self { let mut is_const = Conf::new(is_const, MAX_CONFIDENCE).into(); let mut is_volatile = Conf::new(is_volatile, MAX_CONFIDENCE).into(); - + let owned_raw_ty = Conf::<&Type>::into_raw(ty.into()); unsafe { Self::from_raw(BNCreatePointerTypeBuilderOfWidth( size, - &t.into().into(), + &owned_raw_ty, &mut is_const, &mut is_volatile, ref_type.unwrap_or(ReferenceType::PointerReferenceType), @@ -399,17 +407,18 @@ impl TypeBuilder { pub fn pointer_with_options<'a, A: Architecture, T: Into>>( arch: &A, - t: T, + ty: T, is_const: bool, is_volatile: bool, ref_type: Option, ) -> Self { let mut is_const = Conf::new(is_const, MAX_CONFIDENCE).into(); let mut is_volatile = Conf::new(is_volatile, MAX_CONFIDENCE).into(); + let owned_raw_ty = Conf::<&Type>::into_raw(ty.into()); unsafe { Self::from_raw(BNCreatePointerTypeBuilder( arch.as_ref().handle, - &t.into().into(), + &owned_raw_ty, &mut is_const, &mut is_volatile, ref_type.unwrap_or(ReferenceType::PointerReferenceType), @@ -494,7 +503,7 @@ impl Type { pub fn child_type(&self) -> Option>> { let raw_target = unsafe { BNGetChildType(self.handle) }; match raw_target.type_.is_null() { - false => Some(raw_target.into()), + false => Some(Conf::>::from_owned_raw(raw_target)), true => None, } } @@ -517,7 +526,9 @@ impl Type { pub fn calling_convention(&self) -> Option>>> { let convention_confidence = unsafe { BNGetTypeCallingConvention(self.handle) }; match convention_confidence.convention.is_null() { - false => Some(convention_confidence.into()), + false => Some(Conf::>>::from_owned_raw( + convention_confidence, + )), true => None, } } @@ -529,8 +540,11 @@ impl Type { match raw_parameters_ptr.is_null() { false => { let raw_parameters = std::slice::from_raw_parts(raw_parameters_ptr, count); - let parameters = raw_parameters.iter().map(Into::into).collect(); - //BNFreeTypeParameterList(raw_parameters_ptr, count); + let parameters = raw_parameters + .iter() + .map(FunctionParameter::from_raw) + .collect(); + BNFreeTypeParameterList(raw_parameters_ptr, count); Some(parameters) } true => None, @@ -665,8 +679,9 @@ impl Type { unsafe { Self::ref_from_raw(BNCreateFloatType(width, alt_name.as_ref().as_ptr() as _)) } } - pub fn array<'a, T: Into>>(t: T, count: u64) -> Ref { - unsafe { Self::ref_from_raw(BNCreateArrayType(&t.into().into(), count)) } + pub fn array<'a, T: Into>>(ty: T, count: u64) -> Ref { + let owned_raw_ty = Conf::<&Type>::into_raw(ty.into()); + unsafe { Self::ref_from_raw(BNCreateArrayType(&owned_raw_ty, count)) } } /// ## NOTE @@ -709,26 +724,28 @@ impl Type { } pub fn named_type_from_type>(name: T, t: &Type) -> Ref { - let mut raw_name = BNQualifiedName::from(name.into()); + let mut raw_name = QualifiedName::into_raw(name.into()); // TODO: No id is present for this call? - let id = BnString::new(""); + let id = CStr::from_bytes_with_nul(b"\0").unwrap(); - unsafe { + let result = unsafe { Self::ref_from_raw(BNCreateNamedTypeReferenceFromTypeAndId( id.as_ptr(), &mut raw_name, t.handle, )) - } + }; + QualifiedName::free_raw(raw_name); + result } // TODO: FunctionBuilder pub fn function<'a, T: Into>>( return_type: T, - parameters: &[FunctionParameter], + parameters: Vec, variable_arguments: bool, ) -> Ref { - let mut return_type = return_type.into().into(); + let mut owned_raw_return_type = Conf::<&Type>::into_raw(return_type.into()); let mut variable_arguments = Conf::new(variable_arguments, MAX_CONFIDENCE).into(); let mut can_return = Conf::new(true, MIN_CONFIDENCE).into(); let mut pure = Conf::new(false, MIN_CONFIDENCE).into(); @@ -741,9 +758,8 @@ impl Type { let mut stack_adjust = Conf::new(0, MIN_CONFIDENCE).into(); let mut raw_parameters = parameters - .iter() - .cloned() - .map(Into::into) + .into_iter() + .map(FunctionParameter::into_raw) .collect::>(); let reg_stack_adjust_regs = std::ptr::null_mut(); let reg_stack_adjust_values = std::ptr::null_mut(); @@ -754,9 +770,9 @@ impl Type { confidence: 0, }; - unsafe { + let result = unsafe { Self::ref_from_raw(BNNewTypeReference(BNCreateFunctionType( - &mut return_type, + &mut owned_raw_return_type, &mut raw_calling_convention, raw_parameters.as_mut_ptr(), raw_parameters.len(), @@ -770,7 +786,13 @@ impl Type { BNNameType::NoNameType, &mut pure, ))) + }; + + for raw_param in raw_parameters { + FunctionParameter::free_raw(raw_param); } + + result } // TODO: FunctionBuilder @@ -778,7 +800,7 @@ impl Type { 'a, A: Architecture, T: Into>, - C: Into>>, + C: Into>>>, >( return_type: T, parameters: &[FunctionParameter], @@ -786,19 +808,19 @@ impl Type { calling_convention: C, stack_adjust: Conf, ) -> Ref { - let mut return_type = return_type.into().into(); + let mut owned_raw_return_type = Conf::<&Type>::into_raw(return_type.into()); let mut variable_arguments = Conf::new(variable_arguments, MAX_CONFIDENCE).into(); let mut can_return = Conf::new(true, MIN_CONFIDENCE).into(); let mut pure = Conf::new(false, MIN_CONFIDENCE).into(); - let mut raw_calling_convention: BNCallingConventionWithConfidence = - calling_convention.into().into(); + let mut owned_raw_calling_convention = + Conf::>>::into_owned_raw(&calling_convention.into()); let mut stack_adjust = stack_adjust.into(); let mut raw_parameters = parameters .iter() .cloned() - .map(Into::into) + .map(FunctionParameter::into_raw) .collect::>(); // TODO: Update type signature and include these (will be a breaking change) @@ -811,10 +833,10 @@ impl Type { confidence: 0, }; - unsafe { + let result = unsafe { Self::ref_from_raw(BNCreateFunctionType( - &mut return_type, - &mut raw_calling_convention, + &mut owned_raw_return_type, + &mut owned_raw_calling_convention, raw_parameters.as_mut_ptr(), raw_parameters.len(), &mut variable_arguments, @@ -827,16 +849,23 @@ impl Type { BNNameType::NoNameType, &mut pure, )) + }; + + for raw_param in raw_parameters { + FunctionParameter::free_raw(raw_param); } + + result } - pub fn pointer<'a, A: Architecture, T: Into>>(arch: &A, t: T) -> Ref { + pub fn pointer<'a, A: Architecture, T: Into>>(arch: &A, ty: T) -> Ref { let mut is_const = Conf::new(false, MIN_CONFIDENCE).into(); let mut is_volatile = Conf::new(false, MIN_CONFIDENCE).into(); + let owned_raw_ty = Conf::<&Type>::into_raw(ty.into()); unsafe { Self::ref_from_raw(BNCreatePointerType( arch.as_ref().handle, - &t.into().into(), + &owned_raw_ty, &mut is_const, &mut is_volatile, ReferenceType::PointerReferenceType, @@ -846,14 +875,15 @@ impl Type { pub fn const_pointer<'a, A: Architecture, T: Into>>( arch: &A, - t: T, + ty: T, ) -> Ref { let mut is_const = Conf::new(true, MAX_CONFIDENCE).into(); let mut is_volatile = Conf::new(false, MIN_CONFIDENCE).into(); + let owned_raw_ty = Conf::<&Type>::into_raw(ty.into()); unsafe { Self::ref_from_raw(BNCreatePointerType( arch.as_ref().handle, - &t.into().into(), + &owned_raw_ty, &mut is_const, &mut is_volatile, ReferenceType::PointerReferenceType, @@ -862,7 +892,7 @@ impl Type { } pub fn pointer_of_width<'a, T: Into>>( - t: T, + ty: T, size: usize, is_const: bool, is_volatile: bool, @@ -870,10 +900,11 @@ impl Type { ) -> Ref { let mut is_const = Conf::new(is_const, MAX_CONFIDENCE).into(); let mut is_volatile = Conf::new(is_volatile, MAX_CONFIDENCE).into(); + let owned_raw_ty = Conf::<&Type>::into_raw(ty.into()); unsafe { Self::ref_from_raw(BNCreatePointerTypeOfWidth( size, - &t.into().into(), + &owned_raw_ty, &mut is_const, &mut is_volatile, ref_type.unwrap_or(ReferenceType::PointerReferenceType), @@ -883,17 +914,18 @@ impl Type { pub fn pointer_with_options<'a, A: Architecture, T: Into>>( arch: &A, - t: T, + ty: T, is_const: bool, is_volatile: bool, ref_type: Option, ) -> Ref { let mut is_const = Conf::new(is_const, MAX_CONFIDENCE).into(); let mut is_volatile = Conf::new(is_volatile, MAX_CONFIDENCE).into(); + let owned_raw_ty = Conf::<&Type>::into_raw(ty.into()); unsafe { Self::ref_from_raw(BNCreatePointerType( arch.as_ref().handle, - &t.into().into(), + &owned_raw_ty, &mut is_const, &mut is_volatile, ref_type.unwrap_or(ReferenceType::PointerReferenceType), @@ -902,18 +934,14 @@ impl Type { } pub fn generate_auto_demangled_type_id>(name: T) -> BnString { - let mut raw_name = BNQualifiedName::from(name.into()); - unsafe { BnString::from_raw(BNGenerateAutoDemangledTypeId(&mut raw_name)) } + let mut raw_name = QualifiedName::into_raw(name.into()); + let type_id = unsafe { BnString::from_raw(BNGenerateAutoDemangledTypeId(&mut raw_name)) }; + QualifiedName::free_raw(raw_name); + type_id } } impl Display for Type { - fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { - Debug::fmt(self, f) - } -} - -impl Debug for Type { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { write!(f, "{}", unsafe { BnString::from_raw(BNGetTypeString( @@ -925,6 +953,37 @@ impl Debug for Type { } } +impl Debug for Type { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + // You might be tempted to rip this atrocity out and make this more "sensible". READ BELOW! + // Type is a one-size fits all structure, these are actually its fields! If we wanted to + // omit some fields for different type classes what you really want to do is implement your + // own formatter. This is supposed to represent the structure entirely, it's not supposed to be pretty! + f.debug_struct("Type") + .field("type_class", &self.type_class()) + .field("width", &self.width()) + .field("alignment", &self.alignment()) + .field("is_signed", &self.is_signed()) + .field("is_const", &self.is_const()) + .field("is_volatile", &self.is_volatile()) + .field("is_floating_point", &self.is_floating_point()) + .field("child_type", &self.child_type()) + .field("calling_convention", &self.calling_convention()) + .field("parameters", &self.parameters()) + .field("has_variable_arguments", &self.has_variable_arguments()) + .field("can_return", &self.can_return()) + .field("pure", &self.pure()) + .field("get_structure", &self.get_structure()) + .field("get_enumeration", &self.get_enumeration()) + .field("get_named_type_reference", &self.get_named_type_reference()) + .field("count", &self.count()) + .field("offset", &self.offset()) + .field("stack_adjustment", &self.stack_adjustment()) + .field("registered_name", &self.registered_name()) + .finish() + } +} + impl PartialEq for Type { fn eq(&self, other: &Self) -> bool { unsafe { BNTypesEqual(self.handle, other.handle) } @@ -1006,47 +1065,7 @@ pub struct FunctionParameter { } impl FunctionParameter { - pub fn new>>>(ty: T, name: String, location: Option) -> Self { - Self { - ty: ty.into(), - name, - location, - } - } -} - -impl From for FunctionParameter { - fn from(value: BNFunctionParameter) -> Self { - // TODO: I copied this from the original `from_raw` function. - // TODO: So this actually needs to be audited later. - let name = if value.name.is_null() { - if value.location.type_ == VariableSourceType::RegisterVariableSourceType { - format!("reg_{}", value.location.storage) - } else if value.location.type_ == VariableSourceType::StackVariableSourceType { - format!("arg_{}", value.location.storage) - } else { - String::new() - } - } else { - unsafe { BnString::from_raw(value.name) }.to_string() - }; - - Self { - ty: Conf::new( - unsafe { Type::ref_from_raw(value.type_) }, - value.typeConfidence, - ), - name, - location: match value.defaultLocation { - false => Some(Variable::from(value.location)), - true => None, - }, - } - } -} - -impl From<&BNFunctionParameter> for FunctionParameter { - fn from(value: &BNFunctionParameter) -> Self { + pub(crate) fn from_raw(value: &BNFunctionParameter) -> Self { // TODO: I copied this from the original `from_raw` function. // TODO: So this actually needs to be audited later. let name = if value.name.is_null() { @@ -1073,19 +1092,36 @@ impl From<&BNFunctionParameter> for FunctionParameter { }, } } -} -impl From for BNFunctionParameter { - fn from(value: FunctionParameter) -> Self { + pub(crate) fn from_owned_raw(value: BNFunctionParameter) -> Self { + let owned = Self::from_raw(&value); + Self::free_raw(value); + owned + } + + pub(crate) fn into_raw(value: Self) -> BNFunctionParameter { let bn_name = BnString::new(value.name); - Self { - name: bn_name.into_raw(), - type_: value.ty.contents.handle, + BNFunctionParameter { + name: BnString::into_raw(bn_name), + type_: unsafe { Ref::into_raw(value.ty.contents) }.handle, typeConfidence: value.ty.confidence, defaultLocation: value.location.is_none(), location: value.location.map(Into::into).unwrap_or_default(), } } + + pub(crate) fn free_raw(value: BNFunctionParameter) { + let _ = unsafe { BnString::from_raw(value.name) }; + let _ = unsafe { Type::ref_from_raw(value.type_) }; + } + + pub fn new>>>(ty: T, name: String, location: Option) -> Self { + Self { + ty: ty.into(), + name, + location, + } + } } // Name, Variable and Type @@ -1124,34 +1160,40 @@ pub struct EnumerationMember { } impl EnumerationMember { - pub fn new(name: String, value: u64, default: bool) -> Self { - Self { - name, - value, - default, - } - } -} - -impl From for EnumerationMember { - fn from(value: BNEnumerationMember) -> Self { + pub(crate) fn from_raw(value: &BNEnumerationMember) -> Self { Self { - name: unsafe { BnString::from_raw(value.name).to_string() }, + name: raw_to_string(value.name).unwrap(), value: value.value, default: value.isDefault, } } -} -impl From for BNEnumerationMember { - fn from(value: EnumerationMember) -> Self { + pub(crate) fn from_owned_raw(value: BNEnumerationMember) -> Self { + let owned = Self::from_raw(&value); + Self::free_raw(value); + owned + } + + pub(crate) fn into_raw(value: Self) -> BNEnumerationMember { let bn_name = BnString::new(value.name); - Self { - name: bn_name.into_raw(), + BNEnumerationMember { + name: BnString::into_raw(bn_name), value: value.value, isDefault: value.default, } } + + pub(crate) fn free_raw(value: BNEnumerationMember) { + let _ = unsafe { BnString::from_raw(value.name) }; + } + + pub fn new(name: String, value: u64, default: bool) -> Self { + Self { + name, + value, + default, + } + } } #[derive(PartialEq, Eq, Hash)] @@ -1212,7 +1254,10 @@ impl EnumerationBuilder { let members_raw_ptr = BNGetEnumerationBuilderMembers(self.handle, &mut count); let members_raw: &[BNEnumerationMember] = std::slice::from_raw_parts(members_raw_ptr, count); - let members = members_raw.iter().copied().map(Into::into).collect(); + let members = members_raw + .iter() + .map(EnumerationMember::from_raw) + .collect(); BNFreeEnumerationMemberList(members_raw_ptr, count); members } @@ -1260,15 +1305,27 @@ impl Enumeration { unsafe { let mut count = 0; let members_raw_ptr = BNGetEnumerationMembers(self.handle, &mut count); + debug_assert!(!members_raw_ptr.is_null()); let members_raw: &[BNEnumerationMember] = std::slice::from_raw_parts(members_raw_ptr, count); - let members = members_raw.iter().copied().map(Into::into).collect(); + let members = members_raw + .iter() + .map(EnumerationMember::from_raw) + .collect(); BNFreeEnumerationMemberList(members_raw_ptr, count); members } } } +impl Debug for Enumeration { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + f.debug_struct("Enumeration") + .field("members", &self.members()) + .finish() + } +} + unsafe impl RefCountable for Enumeration { unsafe fn inc_ref(handle: &Self) -> Ref { Self::ref_from_raw(BNNewEnumerationReference(handle.handle)) @@ -1295,19 +1352,46 @@ pub struct StructureBuilder { /// ```no_run /// // Includes /// # use binaryninja::binaryview::BinaryViewExt; -/// use binaryninja::types::{Structure, StructureBuilder, Type, MemberAccess, MemberScope}; +/// use binaryninja::types::{MemberAccess, MemberScope, Structure, StructureBuilder, Type}; /// -/// // Define struct, set size (in bytes) -/// let mut my_custom_struct = StructureBuilder::new(); -/// let field_1 = Type::named_int(5, false, "my_weird_int_type"); -/// let field_2 = Type::int(4, false); -/// let field_3 = Type::int(8, false); +/// // Types to use in the members +/// let field_1_ty = Type::named_int(5, false, "my_weird_int_type"); +/// let field_2_ty = Type::int(4, false); +/// let field_3_ty = Type::int(8, false); /// /// // Assign those fields -/// my_custom_struct.insert(&field_1, "field_1", 0, false, MemberAccess::PublicAccess, MemberScope::NoScope); -/// my_custom_struct.insert(&field_2, "field_2", 5, false, MemberAccess::PublicAccess, MemberScope::NoScope); -/// my_custom_struct.insert(&field_3, "field_3", 9, false, MemberAccess::PublicAccess, MemberScope::NoScope); -/// my_custom_struct.append(&field_1, "field_4", MemberAccess::PublicAccess, MemberScope::NoScope); +/// let mut my_custom_struct = StructureBuilder::new(); +/// my_custom_struct +/// .insert( +/// &field_1_ty, +/// "field_1", +/// 0, +/// false, +/// MemberAccess::PublicAccess, +/// MemberScope::NoScope, +/// ) +/// .insert( +/// &field_2_ty, +/// "field_2", +/// 5, +/// false, +/// MemberAccess::PublicAccess, +/// MemberScope::NoScope, +/// ) +/// .insert( +/// &field_3_ty, +/// "field_3", +/// 9, +/// false, +/// MemberAccess::PublicAccess, +/// MemberScope::NoScope, +/// ) +/// .append( +/// &field_1_ty, +/// "field_4", +/// MemberAccess::PublicAccess, +/// MemberScope::NoScope, +/// ); /// /// // Convert structure to type /// let my_custom_structure_type = Type::structure(&my_custom_struct.finalize()); @@ -1328,52 +1412,59 @@ impl StructureBuilder { Self { handle } } - pub fn finalize(&self) -> Ref { - unsafe { Structure::ref_from_raw(BNFinalizeStructureBuilder(self.handle)) } + // TODO: Document the width adjustment with alignment. + pub fn finalize(&mut self) -> Ref { + let raw_struct_ptr = unsafe { BNFinalizeStructureBuilder(self.handle) }; + unsafe { Structure::ref_from_raw(raw_struct_ptr) } } - pub fn set_width(&self, width: u64) -> &Self { + /// Sets the width of the [`StructureBuilder`] to the new width. + /// + /// This will remove all previously inserted members outside the new width. This is done by computing + /// the member access range (member offset + member width) and if it is larger than the new width + /// it will be removed. + pub fn width(&mut self, width: u64) -> &mut Self { unsafe { BNSetStructureBuilderWidth(self.handle, width); } - self } - pub fn set_alignment(&self, alignment: usize) -> &Self { + pub fn alignment(&mut self, alignment: usize) -> &mut Self { unsafe { BNSetStructureBuilderAlignment(self.handle, alignment); } - self } - pub fn set_packed(&self, packed: bool) -> &Self { + /// Sets whether the [`StructureBuilder`] is packed. + /// + /// If set the alignment of the structure will be `1`. You do not need to set the alignment to `1`. + pub fn packed(&mut self, packed: bool) -> &mut Self { unsafe { BNSetStructureBuilderPacked(self.handle, packed); } - self } - pub fn set_structure_type(&self, t: StructureType) -> &Self { + pub fn structure_type(&mut self, t: StructureType) -> &mut Self { unsafe { BNSetStructureBuilderType(self.handle, t) }; self } - pub fn set_pointer_offset(&self, offset: i64) -> &Self { + pub fn pointer_offset(&mut self, offset: i64) -> &mut Self { unsafe { BNSetStructureBuilderPointerOffset(self.handle, offset) }; self } - pub fn set_propagates_data_var_refs(&self, does: bool) -> &Self { - unsafe { BNSetStructureBuilderPropagatesDataVariableReferences(self.handle, does) }; + pub fn propagates_data_var_refs(&mut self, propagates: bool) -> &mut Self { + unsafe { BNSetStructureBuilderPropagatesDataVariableReferences(self.handle, propagates) }; self } - pub fn set_base_structures(&self, bases: &[BaseStructure]) -> &Self { - let raw_base_structs: Vec = bases.iter().map(Into::into).collect(); - + pub fn base_structures(&mut self, bases: &[BaseStructure]) -> &mut Self { + let raw_base_structs: Vec = + bases.iter().map(BaseStructure::into_owned_raw).collect(); unsafe { BNSetBaseStructuresForStructureBuilder( self.handle, @@ -1381,35 +1472,38 @@ impl StructureBuilder { raw_base_structs.len(), ) }; - self } pub fn append<'a, S: BnStrCompatible, T: Into>>( - &self, - t: T, + &mut self, + ty: T, name: S, access: MemberAccess, scope: MemberScope, - ) -> &Self { + ) -> &mut Self { let name = name.into_bytes_with_nul(); + let owned_raw_ty = Conf::<&Type>::into_raw(ty.into()); unsafe { BNAddStructureBuilderMember( self.handle, - &t.into().into(), + &owned_raw_ty, name.as_ref().as_ptr() as _, access, scope, ); } - self } - pub fn insert_member(&self, member: &StructureMember, overwrite_existing: bool) -> &Self { + pub fn insert_member( + &mut self, + member: StructureMember, + overwrite_existing: bool, + ) -> &mut Self { self.insert( &member.ty, - member.name.clone(), + member.name, member.offset, overwrite_existing, member.access, @@ -1419,19 +1513,20 @@ impl StructureBuilder { } pub fn insert<'a, S: BnStrCompatible, T: Into>>( - &self, - t: T, + &mut self, + ty: T, name: S, offset: u64, overwrite_existing: bool, access: MemberAccess, scope: MemberScope, - ) -> &Self { + ) -> &mut Self { let name = name.into_bytes_with_nul(); + let owned_raw_ty = Conf::<&Type>::into_raw(ty.into()); unsafe { BNAddStructureBuilderMemberAtOffset( self.handle, - &t.into().into(), + &owned_raw_ty, name.as_ref().as_ptr() as _, offset, overwrite_existing, @@ -1439,108 +1534,33 @@ impl StructureBuilder { scope, ); } - self } - pub fn with_members<'a, S: BnStrCompatible, T: Into>>( - &self, - members: impl IntoIterator, - ) -> &Self { - for (t, name) in members { - self.append(t, name, MemberAccess::NoAccess, MemberScope::NoScope); + pub fn replace<'a, S: BnStrCompatible, T: Into>>( + &mut self, + index: usize, + ty: T, + name: S, + overwrite_existing: bool, + ) -> &mut Self { + let name = name.into_bytes_with_nul(); + let owned_raw_ty = Conf::<&Type>::into_raw(ty.into()); + unsafe { + BNReplaceStructureBuilderMember( + self.handle, + index, + &owned_raw_ty, + name.as_ref().as_ptr() as _, + overwrite_existing, + ) } self } - pub fn width(&self) -> u64 { - unsafe { BNGetStructureBuilderWidth(self.handle) } - } - - pub fn alignment(&self) -> usize { - unsafe { BNGetStructureBuilderAlignment(self.handle) } - } - - pub fn packed(&self) -> bool { - unsafe { BNIsStructureBuilderPacked(self.handle) } - } - - pub fn structure_type(&self) -> StructureType { - unsafe { BNGetStructureBuilderType(self.handle) } - } - - pub fn pointer_offset(&self) -> i64 { - unsafe { BNGetStructureBuilderPointerOffset(self.handle) } - } - - pub fn propagates_data_var_refs(&self) -> bool { - unsafe { BNStructureBuilderPropagatesDataVariableReferences(self.handle) } - } - - pub fn base_structures(&self) -> Option> { - let mut count = 0usize; - let bases_raw_ptr = - unsafe { BNGetBaseStructuresForStructureBuilder(self.handle, &mut count) }; - match bases_raw_ptr.is_null() { - false => { - let bases_raw = unsafe { std::slice::from_raw_parts(bases_raw_ptr, count) }; - let bases = bases_raw.iter().copied().map(Into::into).collect(); - unsafe { BNFreeBaseStructureList(bases_raw_ptr, count) }; - Some(bases) - } - true => None, - } - } - - pub fn members(&self) -> Array { - let mut count = 0; - let members_raw = unsafe { BNGetStructureBuilderMembers(self.handle, &mut count) }; - unsafe { Array::new(members_raw, count, ()) } - } - - pub fn index_by_name(&self, name: &str) -> Option { - self.members().iter().position(|member| member.name == name) - } - - pub fn index_by_offset(&self, offset: u64) -> Option { - self.members() - .iter() - .position(|member| member.offset == offset) - } - - pub fn clear_members(&self) { - let len = self.members().len(); - for idx in (0..len).rev() { - self.remove(idx) - } - } - - pub fn add_members<'a>(&self, members: impl IntoIterator) { - for member in members { - self.append(&member.ty, &member.name, member.access, member.scope); - } - } - - pub fn set_members<'a>(&self, members: impl IntoIterator) { - self.clear_members(); - self.add_members(members); - } - - pub fn remove(&self, index: usize) { - unsafe { BNRemoveStructureBuilderMember(self.handle, index) } - } - - pub fn replace(&self, index: usize, type_: Conf<&Type>, name: &str, overwrite: bool) { - let name = name.into_bytes_with_nul(); - let name_ptr = name.as_ptr() as *const _; - - let raw_type_ = BNTypeWithConfidence { - type_: type_.contents as *const Type as *mut _, - confidence: type_.confidence, - }; - unsafe { - BNReplaceStructureBuilderMember(self.handle, index, &raw_type_, name_ptr, overwrite) - } + pub fn remove(&mut self, index: usize) -> &mut Self { + unsafe { BNRemoveStructureBuilderMember(self.handle, index) }; + self } } @@ -1552,20 +1572,14 @@ impl From<&Structure> for StructureBuilder { impl From> for StructureBuilder { fn from(members: Vec) -> StructureBuilder { - let builder = StructureBuilder::new(); - for m in members { - builder.insert_member(&m, false); + let mut builder = StructureBuilder::new(); + for member in members { + builder.insert_member(member, false); } builder } } -impl Debug for StructureBuilder { - fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { - write!(f, "StructureBuilder {{ ... }}") - } -} - impl Drop for StructureBuilder { fn drop(&mut self) { unsafe { BNFreeStructureBuilder(self.handle) }; @@ -1607,15 +1621,10 @@ impl Structure { let members_raw_ptr: *mut BNStructureMember = BNGetStructureMembers(self.handle, &mut count); debug_assert!(!members_raw_ptr.is_null()); - match members_raw_ptr.is_null() { - false => { - let members_raw = std::slice::from_raw_parts(members_raw_ptr, count); - let members = members_raw.iter().map(Into::into).collect(); - BNFreeStructureMemberList(members_raw_ptr, count); - members - } - true => vec![], - } + let members_raw = std::slice::from_raw_parts(members_raw_ptr, count); + let members = members_raw.iter().map(StructureMember::from_raw).collect(); + BNFreeStructureMemberList(members_raw_ptr, count); + members } } @@ -1623,15 +1632,10 @@ impl Structure { let mut count = 0; let bases_raw_ptr = unsafe { BNGetBaseStructuresForStructure(self.handle, &mut count) }; debug_assert!(!bases_raw_ptr.is_null()); - match bases_raw_ptr.is_null() { - false => { - let bases_raw = unsafe { std::slice::from_raw_parts(bases_raw_ptr, count) }; - let bases = bases_raw.iter().copied().map(Into::into).collect(); - unsafe { BNFreeBaseStructureList(bases_raw_ptr, count) }; - bases - } - true => vec![], - } + let bases_raw = unsafe { std::slice::from_raw_parts(bases_raw_ptr, count) }; + let bases = bases_raw.iter().map(BaseStructure::from_raw).collect(); + unsafe { BNFreeBaseStructureList(bases_raw_ptr, count) }; + bases } // TODO : The other methods in the python version (alignment, packed, type, members, remove, replace, etc) @@ -1677,43 +1681,10 @@ pub struct StructureMember { } impl StructureMember { - pub fn new( - ty: Conf>, - name: String, - offset: u64, - access: MemberAccess, - scope: MemberScope, - ) -> Self { - Self { - ty, - name, - offset, - access, - scope, - } - } -} - -impl From for StructureMember { - fn from(value: BNStructureMember) -> Self { - Self { - ty: Conf::new( - unsafe { Type::ref_from_raw(value.type_) }, - value.typeConfidence, - ), - name: unsafe { BnString::from_raw(value.name) }.to_string(), - offset: value.offset, - access: value.access, - scope: value.scope, - } - } -} - -impl From<&BNStructureMember> for StructureMember { - fn from(value: &BNStructureMember) -> Self { + pub(crate) fn from_raw(value: &BNStructureMember) -> Self { Self { ty: Conf::new( - unsafe { Type::from_raw(value.type_).to_owned() }, + unsafe { Type::from_raw(value.type_) }.to_owned(), value.typeConfidence, ), // TODO: I dislike using this function here. @@ -1723,21 +1694,45 @@ impl From<&BNStructureMember> for StructureMember { scope: value.scope, } } -} -impl From for BNStructureMember { - fn from(value: StructureMember) -> Self { + pub(crate) fn from_owned_raw(value: BNStructureMember) -> Self { + let owned = Self::from_raw(&value); + Self::free_raw(value); + owned + } + + pub(crate) fn into_raw(value: Self) -> BNStructureMember { let bn_name = BnString::new(value.name); - // TODO: Dec ref here? - Self { - type_: value.ty.contents.handle, - name: bn_name.into_raw(), + BNStructureMember { + type_: unsafe { Ref::into_raw(value.ty.contents) }.handle, + name: BnString::into_raw(bn_name), offset: value.offset, typeConfidence: value.ty.confidence, access: value.access, scope: value.scope, } } + + pub(crate) fn free_raw(value: BNStructureMember) { + let _ = unsafe { Type::ref_from_raw(value.type_) }; + let _ = unsafe { BnString::from_raw(value.name) }; + } + + pub fn new( + ty: Conf>, + name: String, + offset: u64, + access: MemberAccess, + scope: MemberScope, + ) -> Self { + Self { + ty, + name, + offset, + access, + scope, + } + } } impl CoreArrayProvider for StructureMember { @@ -1752,7 +1747,7 @@ unsafe impl CoreArrayProviderInner for StructureMember { } unsafe fn wrap_raw<'a>(raw: &'a Self::Raw, _context: &'a Self::Context) -> Self::Wrapped<'a> { - Self::from(raw) + Self::from_raw(raw) } } @@ -1788,42 +1783,43 @@ pub struct BaseStructure { } impl BaseStructure { - pub fn new(ty: Ref, offset: u64, width: u64) -> Self { - Self { ty, offset, width } - } -} - -impl From for BaseStructure { - fn from(value: BNBaseStructure) -> Self { + pub(crate) fn from_raw(value: &BNBaseStructure) -> Self { Self { - ty: unsafe { NamedTypeReference::ref_from_raw(value.type_) }, + ty: unsafe { NamedTypeReference::from_raw(value.type_) }.to_owned(), offset: value.offset, width: value.width, } } -} -impl From for BNBaseStructure { - fn from(value: BaseStructure) -> Self { - Self { - type_: value.ty.handle, + pub(crate) fn from_owned_raw(value: BNBaseStructure) -> Self { + let owned = Self::from_raw(&value); + Self::free_raw(value); + owned + } + + pub(crate) fn into_raw(value: Self) -> BNBaseStructure { + BNBaseStructure { + type_: unsafe { Ref::into_raw(value.ty) }.handle, offset: value.offset, width: value.width, } } -} -impl From<&BaseStructure> for BNBaseStructure { - fn from(value: &BaseStructure) -> Self { - Self { - // TODO: In the core there doesn't appear to be a ref increment. - // TODO: Do we want to increment the ref here for the &BaseStructure impl? - // TODO: See BNSetBaseStructuresForStructureBuilder for an example. + pub(crate) fn into_owned_raw(value: &Self) -> BNBaseStructure { + BNBaseStructure { type_: value.ty.handle, offset: value.offset, width: value.width, } } + + pub(crate) fn free_raw(value: BNBaseStructure) { + let _ = unsafe { NamedTypeReference::ref_from_raw(value.type_) }; + } + + pub fn new(ty: Ref, offset: u64, width: u64) -> Self { + Self { ty, offset, width } + } } #[derive(PartialEq, Eq, Hash)] @@ -1848,7 +1844,7 @@ impl NamedTypeReference { /// implementation after your types have been added. Just make sure the names match up and /// the core will do the id stuff for you. pub fn new>(type_class: NamedTypeReferenceClass, name: T) -> Ref { - let mut raw_name = BNQualifiedName::from(name.into()); + let mut raw_name = QualifiedName::into_raw(name.into()); unsafe { Self::ref_from_raw(BNCreateNamedType( type_class, @@ -1869,7 +1865,7 @@ impl NamedTypeReference { name: T, ) -> Ref { let type_id = type_id.into_bytes_with_nul(); - let mut raw_name = BNQualifiedName::from(name.into()); + let mut raw_name = QualifiedName::into_raw(name.into()); unsafe { Self::ref_from_raw(BNCreateNamedType( @@ -1882,7 +1878,7 @@ impl NamedTypeReference { pub fn name(&self) -> QualifiedName { let raw_name = unsafe { BNGetTypeReferenceName(self.handle) }; - QualifiedName::from(raw_name) + QualifiedName::from_owned_raw(raw_name) } pub fn id(&self) -> BnString { @@ -1951,6 +1947,39 @@ pub struct QualifiedName { } impl QualifiedName { + pub(crate) fn from_raw(value: &BNQualifiedName) -> Self { + // TODO: This could be improved... + let raw_names = unsafe { std::slice::from_raw_parts(value.name, value.nameCount) }; + let items = raw_names + .iter() + .filter_map(|&raw_name| raw_to_string(raw_name as *const _)) + .collect(); + let seperator = raw_to_string(value.join).unwrap(); + Self { items, seperator } + } + + pub(crate) fn from_owned_raw(value: BNQualifiedName) -> Self { + let result = Self::from_raw(&value); + Self::free_raw(value); + result + } + + pub fn into_raw(value: Self) -> BNQualifiedName { + let bn_join = BnString::new(&value.seperator); + BNQualifiedName { + // NOTE: Leaking string list must be freed by core or us! + name: strings_to_string_list(&value.items), + // NOTE: Leaking string must be freed by core or us! + join: BnString::into_raw(bn_join), + nameCount: value.items.len(), + } + } + + pub(crate) fn free_raw(value: BNQualifiedName) { + unsafe { BNFreeString(value.join) }; + unsafe { BNFreeStringList(value.name, value.nameCount) }; + } + pub fn new(items: Vec) -> Self { Self::new_with_seperator(items, "::".to_string()) } @@ -1996,9 +2025,13 @@ impl QualifiedName { /// ``` /// use binaryninja::types::QualifiedName; /// - /// let qualified_name = QualifiedName::new(vec!["my::namespace".to_string(), "mytype".to_string()]); + /// let qualified_name = + /// QualifiedName::new(vec!["my::namespace".to_string(), "mytype".to_string()]); /// let replaced = qualified_name.replace("my", "your"); - /// assert_eq!(replaced.items, vec!["your::namespace".to_string(), "yourtype".to_string()]); + /// assert_eq!( + /// replaced.items, + /// vec!["your::namespace".to_string(), "yourtype".to_string()] + /// ); /// ``` pub fn replace(&self, from: &str, to: &str) -> Self { Self { @@ -2028,65 +2061,12 @@ impl QualifiedName { /// A [`QualifiedName`] is empty if it has no items. /// /// If you want to know if the unqualified name is empty (i.e. no characters) - /// you must first convert the qualified name to unqualified via [`QualifiedName::to_string`]. + /// you must first convert the qualified name to unqualified via the `to_string` method. pub fn is_empty(&self) -> bool { self.items.is_empty() } } -impl From for QualifiedName { - fn from(value: BNQualifiedName) -> Self { - // TODO: This could be improved... - let raw_names = unsafe { std::slice::from_raw_parts(value.name, value.nameCount) }; - let items = raw_names - .iter() - .filter_map(|&raw_name| raw_to_string(raw_name as *const _)) - .collect(); - let seperator = raw_to_string(value.join).unwrap(); - unsafe { BNFreeStringList(value.name, value.nameCount) }; - unsafe { BNFreeString(value.join) }; - Self { items, seperator } - } -} - -impl From<&BNQualifiedName> for QualifiedName { - fn from(value: &BNQualifiedName) -> Self { - // TODO: This could be improved... - // Taking this as a ref, we should not free the underlying data... - let raw_names = unsafe { std::slice::from_raw_parts(value.name, value.nameCount) }; - let items = raw_names - .iter() - .filter_map(|&raw_name| raw_to_string(raw_name as *const _)) - .collect(); - let seperator = raw_to_string(value.join).unwrap(); - Self { items, seperator } - } -} - -impl From for BNQualifiedName { - fn from(value: QualifiedName) -> Self { - let bn_join = BnString::new(value.seperator); - Self { - // TODO: Check this to make sure this isnt leaking... - name: strings_to_string_list(&value.items), - join: bn_join.into_raw(), - nameCount: value.items.len(), - } - } -} - -impl From<&QualifiedName> for BNQualifiedName { - fn from(value: &QualifiedName) -> Self { - let bn_join = BnString::new(&value.seperator); - Self { - // TODO: Check this to make sure this isnt leaking... - name: strings_to_string_list(&value.items), - join: bn_join.into_raw(), - nameCount: value.items.len(), - } - } -} - impl From for QualifiedName { fn from(value: String) -> Self { Self { @@ -2169,7 +2149,7 @@ unsafe impl CoreArrayProviderInner for QualifiedName { } unsafe fn wrap_raw<'a>(raw: &'a Self::Raw, _context: &'a Self::Context) -> Self::Wrapped<'a> { - Self::from(raw) + QualifiedName::from_raw(raw) } } @@ -2180,44 +2160,33 @@ pub struct QualifiedNameAndType { } impl QualifiedNameAndType { - pub fn new(name: QualifiedName, ty: Ref) -> Self { - Self { name, ty } - } -} - -impl From for QualifiedNameAndType { - fn from(value: BNQualifiedNameAndType) -> Self { + pub(crate) fn from_raw(value: &BNQualifiedNameAndType) -> Self { Self { - name: QualifiedName::from(value.name), - ty: unsafe { Type::ref_from_raw(value.type_) }, + name: QualifiedName::from_raw(&value.name), + ty: unsafe { Type::from_raw(value.type_).to_owned() }, } } -} -impl From<&BNQualifiedNameAndType> for QualifiedNameAndType { - fn from(value: &BNQualifiedNameAndType) -> Self { - Self { - name: QualifiedName::from(&value.name), - ty: unsafe { Type::from_raw(value.type_).to_owned() }, - } + pub(crate) fn from_owned_raw(value: BNQualifiedNameAndType) -> Self { + let owned = Self::from_raw(&value); + Self::free_raw(value); + owned } -} -impl From for BNQualifiedNameAndType { - fn from(value: QualifiedNameAndType) -> Self { - Self { - name: value.name.into(), - type_: value.ty.handle, + pub(crate) fn into_raw(value: Self) -> BNQualifiedNameAndType { + BNQualifiedNameAndType { + name: QualifiedName::into_raw(value.name), + type_: unsafe { Ref::into_raw(value.ty).handle }, } } -} -impl From<&QualifiedNameAndType> for BNQualifiedNameAndType { - fn from(value: &QualifiedNameAndType) -> Self { - Self { - name: BNQualifiedName::from(&value.name), - type_: value.ty.handle, - } + pub(crate) fn free_raw(value: BNQualifiedNameAndType) { + QualifiedName::free_raw(value.name); + let _ = unsafe { Type::ref_from_raw(value.type_) }; + } + + pub fn new(name: QualifiedName, ty: Ref) -> Self { + Self { name, ty } } } @@ -2258,7 +2227,7 @@ unsafe impl CoreArrayProviderInner for QualifiedNameAndType { } unsafe fn wrap_raw<'a>(raw: &'a Self::Raw, _context: &'a Self::Context) -> Self::Wrapped<'a> { - Self::from(raw) + QualifiedNameAndType::from_raw(raw) } } @@ -2269,35 +2238,35 @@ pub struct QualifiedNameTypeAndId { pub id: String, } -impl From for QualifiedNameTypeAndId { - fn from(value: BNQualifiedNameTypeAndId) -> Self { +impl QualifiedNameTypeAndId { + pub(crate) fn from_raw(value: &BNQualifiedNameTypeAndId) -> Self { Self { - name: QualifiedName::from(value.name), - ty: unsafe { Type::ref_from_raw(value.type_) }, - id: unsafe { BnString::from_raw(value.id) }.to_string(), + name: QualifiedName::from_raw(&value.name), + ty: unsafe { Type::from_raw(value.type_) }.to_owned(), + id: raw_to_string(value.id).unwrap(), } } -} -impl From<&BNQualifiedNameTypeAndId> for QualifiedNameTypeAndId { - fn from(value: &BNQualifiedNameTypeAndId) -> Self { - Self { - name: QualifiedName::from(&value.name), - ty: unsafe { Type::from_raw(value.type_).to_owned() }, - id: raw_to_string(value.id as *mut _).unwrap(), - } + pub(crate) fn from_owned_raw(value: BNQualifiedNameTypeAndId) -> Self { + let owned = Self::from_raw(&value); + Self::free_raw(value); + owned } -} -impl From for BNQualifiedNameTypeAndId { - fn from(value: QualifiedNameTypeAndId) -> Self { + pub(crate) fn into_raw(value: Self) -> BNQualifiedNameTypeAndId { let bn_id = BnString::new(value.id); - Self { - name: value.name.into(), - id: bn_id.into_raw(), - type_: value.ty.handle, + BNQualifiedNameTypeAndId { + name: QualifiedName::into_raw(value.name), + id: BnString::into_raw(bn_id), + type_: unsafe { Ref::into_raw(value.ty) }.handle, } } + + pub(crate) fn free_raw(value: BNQualifiedNameTypeAndId) { + QualifiedName::free_raw(value.name); + let _ = unsafe { Type::ref_from_raw(value.type_) }; + let _ = unsafe { BnString::from_raw(value.id) }; + } } impl CoreArrayProvider for QualifiedNameTypeAndId { @@ -2312,7 +2281,7 @@ unsafe impl CoreArrayProviderInner for QualifiedNameTypeAndId { } unsafe fn wrap_raw<'a>(raw: &'a Self::Raw, _context: &'a Self::Context) -> Self::Wrapped<'a> { - Self::from(raw) + QualifiedNameTypeAndId::from_raw(raw) } } @@ -2327,28 +2296,7 @@ pub struct NameAndType { } impl NameAndType { - pub fn new(name: impl Into, ty: Conf>) -> Self { - Self { - name: name.into(), - ty, - } - } -} - -impl From for NameAndType { - fn from(value: BNNameAndType) -> Self { - Self { - name: unsafe { BnString::from_raw(value.name) }.to_string(), - ty: Conf::new( - unsafe { Type::ref_from_raw(value.type_) }, - value.typeConfidence, - ), - } - } -} - -impl From<&BNNameAndType> for NameAndType { - fn from(value: &BNNameAndType) -> Self { + pub(crate) fn from_raw(value: &BNNameAndType) -> Self { Self { // TODO: I dislike using this function here. name: raw_to_string(value.name as *mut _).unwrap(), @@ -2358,17 +2306,33 @@ impl From<&BNNameAndType> for NameAndType { ), } } -} -impl From for BNNameAndType { - fn from(value: NameAndType) -> Self { + pub(crate) fn from_owned_raw(value: BNNameAndType) -> Self { + let owned = Self::from_raw(&value); + Self::free_raw(value); + owned + } + + pub(crate) fn into_raw(value: Self) -> BNNameAndType { let bn_name = BnString::new(value.name); - Self { - name: bn_name.into_raw(), - type_: value.ty.contents.handle, + BNNameAndType { + name: BnString::into_raw(bn_name), + type_: unsafe { Ref::into_raw(value.ty.contents) }.handle, typeConfidence: value.ty.confidence, } } + + pub(crate) fn free_raw(value: BNNameAndType) { + let _ = unsafe { BnString::from_raw(value.name) }; + let _ = unsafe { Type::ref_from_raw(value.type_) }; + } + + pub fn new(name: impl Into, ty: Conf>) -> Self { + Self { + name: name.into(), + ty, + } + } } impl CoreArrayProvider for NameAndType { @@ -2383,6 +2347,6 @@ unsafe impl CoreArrayProviderInner for NameAndType { } unsafe fn wrap_raw<'a>(raw: &'a Self::Raw, _context: &'a Self::Context) -> Self::Wrapped<'a> { - raw.into() + NameAndType::from_raw(raw) } } diff --git a/rust/src/variable.rs b/rust/src/variable.rs index 93f2e4f9d..e868663ee 100644 --- a/rust/src/variable.rs +++ b/rust/src/variable.rs @@ -1,16 +1,18 @@ +#![allow(unused)] + use crate::confidence::Conf; use crate::function::{Function, Location}; use crate::rc::{CoreArrayProvider, CoreArrayProviderInner, Ref}; use crate::string::{raw_to_string, BnString}; use crate::types::Type; use binaryninjacore_sys::{ - BNDataVariable, BNDataVariableAndName, BNFreeDataVariables, BNFreeILInstructionList, - BNFreeIndirectBranchList, BNFreeMergedVariableList, BNFreeStackVariableReferenceList, - BNFreeUserVariableValues, BNFreeVariableList, BNFreeVariableNameAndTypeList, - BNFromVariableIdentifier, BNIndirectBranchInfo, BNLookupTableEntry, BNMergedVariable, - BNPossibleValueSet, BNRegisterValue, BNRegisterValueType, BNStackVariableReference, - BNToVariableIdentifier, BNUserVariableValue, BNValueRange, BNVariable, BNVariableNameAndType, - BNVariableSourceType, + BNDataVariable, BNDataVariableAndName, BNFreeDataVariableAndName, BNFreeDataVariables, + BNFreeILInstructionList, BNFreeIndirectBranchList, BNFreeMergedVariableList, + BNFreePossibleValueSet, BNFreeStackVariableReferenceList, BNFreeUserVariableValues, + BNFreeVariableList, BNFreeVariableNameAndTypeList, BNFromVariableIdentifier, + BNIndirectBranchInfo, BNLookupTableEntry, BNMergedVariable, BNPossibleValueSet, + BNRegisterValue, BNRegisterValueType, BNStackVariableReference, BNToVariableIdentifier, + BNUserVariableValue, BNValueRange, BNVariable, BNVariableNameAndType, BNVariableSourceType, }; use std::collections::HashSet; @@ -25,37 +27,50 @@ pub struct DataVariable { } impl DataVariable { - pub fn new(address: u64, ty: Conf>, auto_discovered: bool) -> Self { - Self { - address, - ty, - auto_discovered, - } - } -} - -impl From for DataVariable { - fn from(value: BNDataVariable) -> Self { + pub(crate) fn from_raw(value: &BNDataVariable) -> Self { Self { address: value.address, ty: Conf::new( - unsafe { Type::ref_from_raw(value.type_) }, + unsafe { Type::from_raw(value.type_).to_owned() }, value.typeConfidence, ), auto_discovered: value.autoDiscovered, } } -} -impl From<&BNDataVariable> for DataVariable { - fn from(value: &BNDataVariable) -> Self { - Self { + pub(crate) fn from_owned_raw(value: BNDataVariable) -> Self { + let owned = Self::from_raw(&value); + Self::free_raw(value); + owned + } + + pub(crate) fn into_raw(value: Self) -> BNDataVariable { + BNDataVariable { address: value.address, - ty: Conf::new( - unsafe { Type::from_raw(value.type_).to_owned() }, - value.typeConfidence, - ), - auto_discovered: value.autoDiscovered, + type_: unsafe { Ref::into_raw(value.ty.contents) }.handle, + autoDiscovered: value.auto_discovered, + typeConfidence: value.ty.confidence, + } + } + + pub(crate) fn into_owned_raw(value: &Self) -> BNDataVariable { + BNDataVariable { + address: value.address, + type_: value.ty.contents.handle, + autoDiscovered: value.auto_discovered, + typeConfidence: value.ty.confidence, + } + } + + pub(crate) fn free_raw(value: BNDataVariable) { + let _ = unsafe { Type::ref_from_raw(value.type_) }; + } + + pub fn new(address: u64, ty: Conf>, auto_discovered: bool) -> Self { + Self { + address, + ty, + auto_discovered, } } } @@ -72,7 +87,7 @@ unsafe impl CoreArrayProviderInner for DataVariable { } unsafe fn wrap_raw<'a>(raw: &'a Self::Raw, _context: &'a Self::Context) -> Self::Wrapped<'a> { - raw.into() + DataVariable::from_raw(raw) } } @@ -85,32 +100,7 @@ pub struct NamedDataVariableWithType { } impl NamedDataVariableWithType { - pub fn new(address: u64, ty: Conf>, name: String, auto_discovered: bool) -> Self { - Self { - address, - ty, - name, - auto_discovered, - } - } -} - -impl From for NamedDataVariableWithType { - fn from(value: BNDataVariableAndName) -> Self { - Self { - address: value.address, - ty: Conf::new( - unsafe { Type::ref_from_raw(value.type_) }, - value.typeConfidence, - ), - name: unsafe { BnString::from_raw(value.name) }.to_string(), - auto_discovered: value.autoDiscovered, - } - } -} - -impl From<&BNDataVariableAndName> for NamedDataVariableWithType { - fn from(value: &BNDataVariableAndName) -> Self { + pub(crate) fn from_raw(value: &BNDataVariableAndName) -> Self { Self { address: value.address, ty: Conf::new( @@ -122,60 +112,63 @@ impl From<&BNDataVariableAndName> for NamedDataVariableWithType { auto_discovered: value.autoDiscovered, } } -} -impl From for BNDataVariableAndName { - fn from(value: NamedDataVariableWithType) -> Self { + pub(crate) fn from_owned_raw(value: BNDataVariableAndName) -> Self { + let owned = Self::from_raw(&value); + Self::free_raw(value); + owned + } + + pub(crate) unsafe fn from_ref_raw(value: *mut BNDataVariableAndName) -> Self { + let owned = Self::from_raw(&*value); + Self::free_ref_raw(value); + owned + } + + pub(crate) fn into_raw(value: Self) -> BNDataVariableAndName { let bn_name = BnString::new(value.name); - Self { + BNDataVariableAndName { address: value.address, - type_: value.ty.contents.handle, - name: bn_name.into_raw(), + type_: unsafe { Ref::into_raw(value.ty.contents) }.handle, + name: BnString::into_raw(bn_name), autoDiscovered: value.auto_discovered, typeConfidence: value.ty.confidence, } } -} -#[derive(Debug, Clone, Hash, PartialEq, Eq)] -pub struct NamedVariableWithType { - pub variable: Variable, - pub ty: Conf>, - pub name: String, - pub auto_defined: bool, -} + pub(crate) fn free_ref_raw(value: *mut BNDataVariableAndName) { + unsafe { BNFreeDataVariableAndName(value) } + } -impl NamedVariableWithType { - pub fn new(variable: Variable, ty: Conf>, name: String, auto_defined: bool) -> Self { + pub(crate) fn free_raw(value: BNDataVariableAndName) { + let _ = unsafe { Type::ref_from_raw(value.type_) }; + let _ = unsafe { BnString::from_raw(value.name) }; + } + + pub fn new(address: u64, ty: Conf>, name: String, auto_discovered: bool) -> Self { Self { - variable, + address, ty, name, - auto_defined, + auto_discovered, } } } -impl From for NamedVariableWithType { - fn from(value: BNVariableNameAndType) -> Self { - Self { - variable: value.var.into(), - ty: Conf::new( - unsafe { Type::ref_from_raw(value.type_) }, - value.typeConfidence, - ), - name: unsafe { BnString::from_raw(value.name) }.to_string(), - auto_defined: value.autoDefined, - } - } +#[derive(Debug, Clone, Hash, PartialEq, Eq)] +pub struct NamedVariableWithType { + pub variable: Variable, + pub ty: Conf>, + pub name: String, + pub auto_defined: bool, } -impl From<&BNVariableNameAndType> for NamedVariableWithType { - fn from(value: &BNVariableNameAndType) -> Self { +impl NamedVariableWithType { + pub(crate) fn from_raw(value: &BNVariableNameAndType) -> Self { Self { variable: value.var.into(), ty: Conf::new( - unsafe { Type::from_raw(value.type_).to_owned() }, + unsafe { Type::from_raw(value.type_) }.to_owned(), value.typeConfidence, ), // TODO: I dislike using this function here. @@ -183,19 +176,37 @@ impl From<&BNVariableNameAndType> for NamedVariableWithType { auto_defined: value.autoDefined, } } -} -impl From for BNVariableNameAndType { - fn from(value: NamedVariableWithType) -> Self { + pub(crate) fn from_owned_raw(value: BNVariableNameAndType) -> Self { + let owned = Self::from_raw(&value); + Self::free_raw(value); + owned + } + + pub(crate) fn into_raw(value: Self) -> BNVariableNameAndType { let bn_name = BnString::new(value.name); - Self { + BNVariableNameAndType { var: value.variable.into(), - type_: value.ty.contents.handle, - name: bn_name.into_raw(), + type_: unsafe { Ref::into_raw(value.ty.contents) }.handle, + name: BnString::into_raw(bn_name), autoDefined: value.auto_defined, typeConfidence: value.ty.confidence, } } + + pub(crate) fn free_raw(value: BNVariableNameAndType) { + let _ = unsafe { Type::ref_from_raw(value.type_) }; + let _ = unsafe { BnString::from_raw(value.name) }; + } + + pub fn new(variable: Variable, ty: Conf>, name: String, auto_defined: bool) -> Self { + Self { + variable, + ty, + name, + auto_defined, + } + } } impl CoreArrayProvider for NamedVariableWithType { @@ -210,7 +221,7 @@ unsafe impl CoreArrayProviderInner for NamedVariableWithType { } unsafe fn wrap_raw<'a>(raw: &'a Self::Raw, _context: &'a Self::Context) -> Self::Wrapped<'a> { - Self::from(raw) + Self::from_raw(raw) } } @@ -221,22 +232,20 @@ pub struct UserVariableValue { pub value: PossibleValueSet, } -impl From for UserVariableValue { - fn from(value: BNUserVariableValue) -> Self { +impl UserVariableValue { + pub(crate) fn from_raw(value: &BNUserVariableValue) -> Self { Self { variable: value.var.into(), def_site: value.defSite.into(), - value: value.value.into(), + value: PossibleValueSet::from_raw(&value.value), } } -} -impl From for BNUserVariableValue { - fn from(value: UserVariableValue) -> Self { - Self { + pub(crate) fn into_raw(value: Self) -> BNUserVariableValue { + BNUserVariableValue { var: value.variable.into(), defSite: value.def_site.into(), - value: value.value.into(), + value: PossibleValueSet::into_raw(value.value), } } } @@ -253,7 +262,7 @@ unsafe impl CoreArrayProviderInner for UserVariableValue { } unsafe fn wrap_raw<'a>(raw: &'a Self::Raw, _context: &'a Self::Context) -> Self::Wrapped<'a> { - UserVariableValue::from(*raw) + UserVariableValue::from_raw(raw) } } @@ -267,54 +276,46 @@ pub struct StackVariableReference { pub size: usize, } -impl From for StackVariableReference { - fn from(value: BNStackVariableReference) -> Self { +impl StackVariableReference { + pub(crate) fn from_raw(value: &BNStackVariableReference) -> Self { Self { source_operand: value.sourceOperand, variable_type: Conf::new( - unsafe { Type::ref_from_raw(value.type_) }, + unsafe { Type::from_raw(value.type_) }.to_owned(), value.typeConfidence, ), - name: unsafe { BnString::from_raw(value.name) }.to_string(), + // TODO: I dislike using this function here. + name: raw_to_string(value.name).unwrap(), // TODO: It might be beneficial to newtype the identifier as VariableIdentifier. variable: Variable::from_identifier(value.varIdentifier), offset: value.referencedOffset, size: value.size, } } -} -impl From<&BNStackVariableReference> for StackVariableReference { - fn from(value: &BNStackVariableReference) -> Self { - Self { - source_operand: value.sourceOperand, - variable_type: Conf::new( - unsafe { Type::from_raw(value.type_).to_owned() }, - value.typeConfidence, - ), - // TODO: I dislike using this function here. - name: raw_to_string(value.name as *mut _).unwrap(), - // TODO: It might be beneficial to newtype the identifier as VariableIdentifier. - variable: Variable::from_identifier(value.varIdentifier), - offset: value.referencedOffset, - size: value.size, - } + pub(crate) fn from_owned_raw(value: BNStackVariableReference) -> Self { + let owned = Self::from_raw(&value); + Self::free_raw(value); + owned } -} -impl From for BNStackVariableReference { - fn from(value: StackVariableReference) -> Self { + pub(crate) fn into_raw(value: Self) -> BNStackVariableReference { let bn_name = BnString::new(value.name); - Self { + BNStackVariableReference { sourceOperand: value.source_operand, typeConfidence: value.variable_type.confidence, - type_: value.variable_type.contents.handle, - name: bn_name.into_raw(), + type_: unsafe { Ref::into_raw(value.variable_type.contents) }.handle, + name: BnString::into_raw(bn_name), varIdentifier: value.variable.to_identifier(), referencedOffset: value.offset, size: value.size, } } + + pub(crate) fn free_raw(value: BNStackVariableReference) { + let _ = unsafe { Type::ref_from_raw(value.type_) }; + let _ = unsafe { BnString::from_raw(value.name) }; + } } impl CoreArrayProvider for StackVariableReference { @@ -329,7 +330,7 @@ unsafe impl CoreArrayProviderInner for StackVariableReference { } unsafe fn wrap_raw<'a>(raw: &'a Self::Raw, _context: &'a Self::Context) -> Self::Wrapped<'a> { - raw.into() + StackVariableReference::from_raw(raw) } } @@ -435,7 +436,7 @@ unsafe impl CoreArrayProviderInner for Variable { } unsafe fn wrap_raw<'a>(raw: &'a Self::Raw, _context: &'a Self::Context) -> Self::Wrapped<'a> { - Variable::from(*raw) + Variable::from(raw) } } @@ -445,20 +446,16 @@ pub struct MergedVariable { pub sources: Vec, } -impl From for MergedVariable { - fn from(value: BNMergedVariable) -> Self { - let sources = unsafe { - std::slice::from_raw_parts(value.sources, value.sourceCount) - .iter() - .copied() - .map(Into::into) - .collect() - }; +impl MergedVariable { + pub(crate) fn from_raw(value: &BNMergedVariable) -> Self { + let raw_sources = unsafe { std::slice::from_raw_parts(value.sources, value.sourceCount) }; Self { target: value.target.into(), - sources, + sources: raw_sources.iter().map(Into::into).collect(), } } + + // TODO: If we want from_owned_raw/free_raw/into_raw we need a way to allocate sources. } impl CoreArrayProvider for MergedVariable { @@ -473,7 +470,7 @@ unsafe impl CoreArrayProviderInner for MergedVariable { } unsafe fn wrap_raw<'a>(raw: &'a Self::Raw, _context: &'a Self::Context) -> Self::Wrapped<'a> { - Self::from(*raw) + Self::from_raw(raw) } } @@ -590,12 +587,12 @@ pub struct LookupTableEntry { to: i64, } -impl From for LookupTableEntry { - fn from(value: BNLookupTableEntry) -> Self { +impl LookupTableEntry { + pub(crate) fn from_raw(value: &BNLookupTableEntry) -> Self { let from_values = unsafe { std::slice::from_raw_parts(value.fromValues, value.fromCount) }; Self { // TODO: Better way to construct HashSet? - from: from_values.iter().copied().collect(), + from: HashSet::from_iter(from_values.iter().copied()), to: value.toValue, } } @@ -667,41 +664,7 @@ pub enum PossibleValueSet { } impl PossibleValueSet { - pub fn value_type(&self) -> RegisterValueType { - match self { - PossibleValueSet::UndeterminedValue => RegisterValueType::UndeterminedValue, - PossibleValueSet::EntryValue { .. } => RegisterValueType::EntryValue, - PossibleValueSet::ConstantValue { .. } => RegisterValueType::ConstantValue, - PossibleValueSet::ConstantPointerValue { .. } => { - RegisterValueType::ConstantPointerValue - } - PossibleValueSet::ExternalPointerValue { .. } => { - RegisterValueType::ExternalPointerValue - } - PossibleValueSet::StackFrameOffset { .. } => RegisterValueType::StackFrameOffset, - PossibleValueSet::ReturnAddressValue => RegisterValueType::ReturnAddressValue, - PossibleValueSet::ImportedAddressValue => RegisterValueType::ImportedAddressValue, - PossibleValueSet::SignedRangeValue { .. } => RegisterValueType::SignedRangeValue, - PossibleValueSet::UnsignedRangeValue { .. } => RegisterValueType::UnsignedRangeValue, - PossibleValueSet::LookupTableValue { .. } => RegisterValueType::LookupTableValue, - PossibleValueSet::InSetOfValues { .. } => RegisterValueType::InSetOfValues, - PossibleValueSet::NotInSetOfValues { .. } => RegisterValueType::NotInSetOfValues, - PossibleValueSet::ConstantDataValue { .. } => RegisterValueType::ConstantDataValue, - PossibleValueSet::ConstantDataZeroExtendValue { .. } => { - RegisterValueType::ConstantDataZeroExtendValue - } - PossibleValueSet::ConstantDataSignExtendValue { .. } => { - RegisterValueType::ConstantDataSignExtendValue - } - PossibleValueSet::ConstantDataAggregateValue { .. } => { - RegisterValueType::ConstantDataAggregateValue - } - } - } -} - -impl From for PossibleValueSet { - fn from(value: BNPossibleValueSet) -> Self { + pub(crate) fn from_raw(value: &BNPossibleValueSet) -> Self { match value.state { RegisterValueType::UndeterminedValue => Self::UndeterminedValue, RegisterValueType::EntryValue => Self::EntryValue { reg: value.value }, @@ -733,7 +696,7 @@ impl From for PossibleValueSet { RegisterValueType::LookupTableValue => { let raw_entries = unsafe { std::slice::from_raw_parts(value.table, value.count) }; Self::LookupTableValue { - table: raw_entries.iter().map(|&r| r.into()).collect(), + table: raw_entries.iter().map(LookupTableEntry::from_raw).collect(), } } RegisterValueType::InSetOfValues => { @@ -766,11 +729,16 @@ impl From for PossibleValueSet { }, } } -} -// TODO: Anything requiring core allocation is missing! -impl From for BNPossibleValueSet { - fn from(value: PossibleValueSet) -> Self { + /// Take ownership over an "owned" core allocated value. Do not call this for a rust allocated value. + pub(crate) fn from_owned_raw(mut value: BNPossibleValueSet) -> Self { + let owned = Self::from_raw(&value); + // TODO: This entire function is a little wonky. + Self::free_raw(&mut value); + owned + } + + pub(crate) fn into_raw(value: Self) -> BNPossibleValueSet { let mut raw = BNPossibleValueSet::default(); raw.state = value.value_type(); match value { @@ -793,29 +761,29 @@ impl From for BNPossibleValueSet { } PossibleValueSet::ReturnAddressValue => {} PossibleValueSet::ImportedAddressValue => {} - PossibleValueSet::SignedRangeValue { value, .. } => { + PossibleValueSet::SignedRangeValue { value, ranges } => { raw.value = value; // TODO: raw.ranges // TODO: requires core allocation and freeing. // TODO: See `BNFreePossibleValueSet` for why this sucks. } - PossibleValueSet::UnsignedRangeValue { value, .. } => { + PossibleValueSet::UnsignedRangeValue { value, ranges } => { raw.value = value; // TODO: raw.ranges // TODO: requires core allocation and freeing. // TODO: See `BNFreePossibleValueSet` for why this sucks. } - PossibleValueSet::LookupTableValue { .. } => { + PossibleValueSet::LookupTableValue { table } => { // TODO: raw.table // TODO: requires core allocation and freeing. // TODO: See `BNFreePossibleValueSet` for why this sucks. } - PossibleValueSet::InSetOfValues { .. } => { + PossibleValueSet::InSetOfValues { values } => { // TODO: raw.valueSet // TODO: requires core allocation and freeing. // TODO: See `BNFreePossibleValueSet` for why this sucks. } - PossibleValueSet::NotInSetOfValues { .. } => { + PossibleValueSet::NotInSetOfValues { values } => { // TODO: raw.valueSet // TODO: requires core allocation and freeing. // TODO: See `BNFreePossibleValueSet` for why this sucks. @@ -839,6 +807,48 @@ impl From for BNPossibleValueSet { }; raw } + + /// Free a CORE ALLOCATED possible value set. Do not use this with [Self::into_raw] values. + pub(crate) fn free_raw(value: &mut BNPossibleValueSet) { + unsafe { BNFreePossibleValueSet(value) } + } + + /// Free a RUST ALLOCATED possible value set. Do not use this with CORE ALLOCATED values. + pub(crate) fn free_owned_raw(value: BNPossibleValueSet) { + // TODO: Once we fill out allocation of the possible value set then we should fill this out as well. + } + + pub fn value_type(&self) -> RegisterValueType { + match self { + PossibleValueSet::UndeterminedValue => RegisterValueType::UndeterminedValue, + PossibleValueSet::EntryValue { .. } => RegisterValueType::EntryValue, + PossibleValueSet::ConstantValue { .. } => RegisterValueType::ConstantValue, + PossibleValueSet::ConstantPointerValue { .. } => { + RegisterValueType::ConstantPointerValue + } + PossibleValueSet::ExternalPointerValue { .. } => { + RegisterValueType::ExternalPointerValue + } + PossibleValueSet::StackFrameOffset { .. } => RegisterValueType::StackFrameOffset, + PossibleValueSet::ReturnAddressValue => RegisterValueType::ReturnAddressValue, + PossibleValueSet::ImportedAddressValue => RegisterValueType::ImportedAddressValue, + PossibleValueSet::SignedRangeValue { .. } => RegisterValueType::SignedRangeValue, + PossibleValueSet::UnsignedRangeValue { .. } => RegisterValueType::UnsignedRangeValue, + PossibleValueSet::LookupTableValue { .. } => RegisterValueType::LookupTableValue, + PossibleValueSet::InSetOfValues { .. } => RegisterValueType::InSetOfValues, + PossibleValueSet::NotInSetOfValues { .. } => RegisterValueType::NotInSetOfValues, + PossibleValueSet::ConstantDataValue { .. } => RegisterValueType::ConstantDataValue, + PossibleValueSet::ConstantDataZeroExtendValue { .. } => { + RegisterValueType::ConstantDataZeroExtendValue + } + PossibleValueSet::ConstantDataSignExtendValue { .. } => { + RegisterValueType::ConstantDataSignExtendValue + } + PossibleValueSet::ConstantDataAggregateValue { .. } => { + RegisterValueType::ConstantDataAggregateValue + } + } + } } #[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)] diff --git a/rust/tests/platform.rs b/rust/tests/platform.rs new file mode 100644 index 000000000..85f9c1aea --- /dev/null +++ b/rust/tests/platform.rs @@ -0,0 +1,34 @@ +use binaryninja::headless::Session; +use binaryninja::platform::Platform; +use rstest::*; + +#[fixture] +#[once] +fn session() -> Session { + Session::new() +} + +#[rstest] +fn test_platform_lifetime(_session: &Session) { + let platform_0 = Platform::by_name("windows-x86_64").expect("windows-x86_64 exists"); + let platform_types_0 = platform_0.types(); + let platform_1 = Platform::by_name("windows-x86_64").expect("windows-x86_64 exists"); + let platform_types_1 = platform_1.types(); + assert_eq!(platform_types_0.len(), platform_types_1.len()); +} + +#[rstest] +fn test_platform_types(_session: &Session) { + let platform = Platform::by_name("windows-x86_64").expect("windows-x86_64 exists"); + let platform_types = platform.types(); + // windows-x86_64 has a few thousand, not zero. + assert_ne!(platform_types.len(), 0); +} + +#[rstest] +fn test_platform_calling_conventions(_session: &Session) { + let platform = Platform::by_name("windows-x86_64").expect("windows-x86_64 exists"); + for cc in platform.calling_conventions().iter() { + println!("{:#?}", cc); + } +} diff --git a/rust/tests/typecontainer.rs b/rust/tests/typecontainer.rs index 0457fe09f..d0dae9019 100644 --- a/rust/tests/typecontainer.rs +++ b/rust/tests/typecontainer.rs @@ -30,9 +30,8 @@ fn empty_view() -> Ref { fn test_types(_session: &Session, platform: &Platform) { let type_container = platform.type_container(); let types = type_container.types().unwrap(); - let types_len = types.len(); // windows-x86_64 has a few thousand, not zero. - assert_ne!(types_len, 0); + assert_eq!(types.len(), platform.types().len()); } #[rstest] @@ -80,6 +79,7 @@ fn test_immutable_container(_session: &Session, platform: &Platform) { !plat_type_container.is_mutable(), "Platform should NOT be mutable!" ); + assert_ne!(platform.types().len(), 0); let type_ids = plat_type_container.type_ids().unwrap(); let first_type_id = type_ids.iter().next().unwrap(); // Platform type containers are immutable so these should be false! @@ -111,6 +111,12 @@ fn test_parse_type(_session: &Session, platform: &Platform) { #[rstest] fn test_container_lifetime(_session: &Session, platform: &Platform, empty_view: &BinaryView) { + let plat_type_container_dropped = platform.type_container(); + let view_type_container_dropped = empty_view.type_container(); + let _plat_types_dropped = plat_type_container_dropped.types(); + let _view_types_dropped = view_type_container_dropped.types(); + drop(plat_type_container_dropped); + drop(view_type_container_dropped); let plat_type_container_0 = platform.type_container(); let view_type_container_0 = empty_view.type_container(); let test_type = Type::int(4, true); From 3ea9c4eb4eef2949b2fc41065cb00f187e3d564d Mon Sep 17 00:00:00 2001 From: Mason Reed Date: Sun, 12 Jan 2025 23:59:46 -0500 Subject: [PATCH 38/93] Misc clippy lints and clippy CI Co-authored-by: Michael Krasnitski --- .github/workflows/rust.yml | 17 ++++++++++++++++- rust/build.rs | 2 +- rust/examples/workflow.rs | 25 +++++++++++-------------- rust/src/architecture.rs | 1 + rust/src/basicblock.rs | 2 +- rust/src/binaryview.rs | 2 +- rust/src/component.rs | 3 --- rust/src/confidence.rs | 2 +- rust/src/databuffer.rs | 2 +- rust/src/demangle.rs | 7 ++----- rust/src/disassembly.rs | 2 +- rust/src/enterprise.rs | 3 +++ rust/src/flowgraph.rs | 4 ++-- rust/src/hlil/instruction.rs | 2 +- rust/src/lib.rs | 3 ++- rust/src/llil/block.rs | 10 ++++++---- rust/src/llil/function.rs | 27 +++++++++++++++------------ rust/src/mlil/instruction.rs | 2 +- rust/src/project.rs | 2 +- rust/src/section.rs | 2 +- rust/src/settings.rs | 4 ---- rust/src/typearchive.rs | 2 +- rust/src/typecontainer.rs | 2 +- rust/src/typeprinter.rs | 10 +++------- rust/src/types.rs | 8 +++++--- rust/src/variable.rs | 6 ++++-- rust/src/workflow.rs | 20 ++++---------------- rust/tests/databuffer.rs | 2 +- 28 files changed, 87 insertions(+), 87 deletions(-) diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index dcab7247d..0f643de73 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -10,8 +10,23 @@ on: - 'rust/**' jobs: - # TODO: Cargo clippy (just warnings don't fail) # TODO: Cargo test (we would need to pull in binary ninja) + # Check lints with clippy + clippy: + name: cargo clippy + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + # Ensure clippy is installed + - uses: actions-rust-lang/setup-rust-toolchain@v1 + with: + components: clippy + - name: Clippy Check + - uses: clechasseur/rs-clippy-check@v4 + with: + working-directory: ./rust + args: --all-features + # Check formatting with rustfmt formatting: name: cargo fmt diff --git a/rust/build.rs b/rust/build.rs index 18983aee9..46b518e00 100644 --- a/rust/build.rs +++ b/rust/build.rs @@ -5,7 +5,7 @@ fn main() { let _ = std::fs::create_dir("target/doc"); let _ = std::fs::copy("../docs/img/favicon.ico", "target/doc/favicon.ico"); let _ = std::fs::copy( - "under_construction.png", + "assets/under_construction.png", "target/doc/under_construction.png", ); let _ = std::fs::copy("../docs/img/logo.png", "target/doc/logo.png"); diff --git a/rust/examples/workflow.rs b/rust/examples/workflow.rs index 6722ba680..60e7e2c0a 100644 --- a/rust/examples/workflow.rs +++ b/rust/examples/workflow.rs @@ -2,8 +2,8 @@ use binaryninja::binaryview::BinaryViewExt; use binaryninja::llil::{ExprInfo, LiftedNonSSA, NonSSA, VisitorAction}; use binaryninja::workflow::{Activity, AnalysisContext, Workflow}; -const RUST_ACTIVITY_NAME: &'static str = "analysis.plugins.rustexample"; -const RUST_ACTIVITY_CONFIG: &'static str = r#"{ +const RUST_ACTIVITY_NAME: &str = "analysis.plugins.rustexample"; +const RUST_ACTIVITY_CONFIG: &str = r#"{ "name": "analysis.plugins.rustexample", "title" : "Rust Example", "description": "This analysis step logs out some information about the function...", @@ -27,18 +27,15 @@ fn example_activity(analysis_context: &AnalysisContext) { for instr in basic_block.iter() { if let Some(llil_instr) = llil.instruction_at(instr) { llil_instr.visit_tree(&mut |expr, info| { - match info { - ExprInfo::Const(_op) => { - // Replace all consts with 0x1337. - println!( - "Replacing llil expression @ 0x{:x} : {}", - instr, expr.index - ); - unsafe { - llil.replace_expression(expr.index, llil.const_int(4, 0x1337)) - }; - } - _ => {} + if let ExprInfo::Const(_op) = info { + // Replace all consts with 0x1337. + println!( + "Replacing llil expression @ 0x{:x} : {}", + instr, expr.index + ); + unsafe { + llil.replace_expression(expr.index, llil.const_int(4, 0x1337)) + }; } VisitorAction::Descend }); diff --git a/rust/src/architecture.rs b/rust/src/architecture.rs index 43a41bab5..2fbd3cb43 100644 --- a/rust/src/architecture.rs +++ b/rust/src/architecture.rs @@ -200,6 +200,7 @@ impl From for InstructionInfo { fn from(value: BNInstructionInfo) -> Self { // TODO: This is quite ugly, but we destructure the branch info so this will have to do. let mut branch_info = [None; NUM_BRANCH_INFO]; + #[allow(clippy::needless_range_loop)] for i in 0..value.branchCount.min(NUM_BRANCH_INFO) { let branch_target = value.branchTarget[i]; branch_info[i] = Some(BranchInfo { diff --git a/rust/src/basicblock.rs b/rust/src/basicblock.rs index 56ff0abeb..96406fd11 100644 --- a/rust/src/basicblock.rs +++ b/rust/src/basicblock.rs @@ -240,7 +240,7 @@ impl BasicBlock { // TODO iterated dominance frontier } -impl<'a, C: BlockContext> IntoIterator for &'a BasicBlock { +impl IntoIterator for &BasicBlock { type Item = C::Instruction; type IntoIter = C::Iter; diff --git a/rust/src/binaryview.rs b/rust/src/binaryview.rs index 7909e8d5e..6a9246fb7 100644 --- a/rust/src/binaryview.rs +++ b/rust/src/binaryview.rs @@ -678,7 +678,7 @@ pub trait BinaryViewExt: BinaryViewBase { let name_array = unsafe { Array::::new(result_names, result_count, ()) }; id_array .into_iter() - .zip(name_array.into_iter()) + .zip(&name_array) .map(|(id, name)| (id.to_owned(), name)) .collect() } diff --git a/rust/src/component.rs b/rust/src/component.rs index 7ed514509..b705e3093 100644 --- a/rust/src/component.rs +++ b/rust/src/component.rs @@ -146,13 +146,10 @@ impl Component { } /// Original name set for this component - /// :note: The `.display_name` property should be used for `bv.get_component_by_path()` lookups. - /// This can differ from the .display_name property if one of its sibling components has the same .original_name; In that /// case, .name will be an automatically generated unique name (e.g. "MyComponentName (1)") while .original_name will /// remain what was originally set (e.g. "MyComponentName") - /// If this component has a duplicate name and is moved to a component where none of its siblings share its name, /// .name will return the original "MyComponentName" pub fn name(&self) -> BnString { diff --git a/rust/src/confidence.rs b/rust/src/confidence.rs index 5a23c88b5..792e46be0 100644 --- a/rust/src/confidence.rs +++ b/rust/src/confidence.rs @@ -189,7 +189,7 @@ impl From for Conf { } } -impl<'a> Conf<&'a Type> { +impl Conf<&'_ Type> { pub(crate) fn into_raw(value: Self) -> BNTypeWithConfidence { BNTypeWithConfidence { type_: value.contents.handle, diff --git a/rust/src/databuffer.rs b/rust/src/databuffer.rs index 3bc833b23..5b4d6515a 100644 --- a/rust/src/databuffer.rs +++ b/rust/src/databuffer.rs @@ -172,7 +172,7 @@ impl DataBuffer { impl Default for DataBuffer { fn default() -> Self { - Self(unsafe { BNCreateDataBuffer([].as_ptr() as *const c_void, 0) }) + Self(unsafe { BNCreateDataBuffer([].as_ptr(), 0) }) } } diff --git a/rust/src/demangle.rs b/rust/src/demangle.rs index 43d30133b..3a3d924dd 100644 --- a/rust/src/demangle.rs +++ b/rust/src/demangle.rs @@ -301,10 +301,7 @@ impl Demangler { }) } - extern "C" fn cb_free_var_name(_ctxt: *mut c_void, name: *mut BNQualifiedName) - where - C: CustomDemangler, - { + extern "C" fn cb_free_var_name(_ctxt: *mut c_void, name: *mut BNQualifiedName) { ffi_wrap!("CustomDemangler::cb_free_var_name", unsafe { // TODO: What is the point of this free callback? QualifiedName::free_raw(*name) @@ -319,7 +316,7 @@ impl Demangler { context: ctxt as *mut c_void, isMangledString: Some(cb_is_mangled_string::), demangle: Some(cb_demangle::), - freeVarName: Some(cb_free_var_name::), + freeVarName: Some(cb_free_var_name), }; unsafe { diff --git a/rust/src/disassembly.rs b/rust/src/disassembly.rs index cb4b9cfea..99938e5fd 100644 --- a/rust/src/disassembly.rs +++ b/rust/src/disassembly.rs @@ -263,7 +263,7 @@ impl InstructionTextToken { 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![]); + let type_names = value.kind.try_type_names().unwrap_or_default(); BNInstructionTextToken { type_: value.kind.into(), // NOTE: Expected to be freed with `InstructionTextToken::free_raw`. diff --git a/rust/src/enterprise.rs b/rust/src/enterprise.rs index adb1a32a6..092733843 100644 --- a/rust/src/enterprise.rs +++ b/rust/src/enterprise.rs @@ -20,6 +20,7 @@ pub fn checkout_license(duration: Duration) -> Result<(), EnterpriseCheckoutErro return Ok(()); } + #[allow(clippy::collapsible_if)] if !is_server_initialized() { if !initialize_server() && is_server_floating_license() { return Err(EnterpriseCheckoutError(server_last_error().to_string())); @@ -31,6 +32,7 @@ pub fn checkout_license(duration: Duration) -> Result<(), EnterpriseCheckoutErro return Err(EnterpriseCheckoutError(server_last_error().to_string())); } + #[allow(clippy::collapsible_if)] if !is_server_authenticated() { if !authenticate_server_with_method("Keychain", false) { let Some(username) = std::env::var("BN_ENTERPRISE_USERNAME").ok() else { @@ -50,6 +52,7 @@ pub fn checkout_license(duration: Duration) -> Result<(), EnterpriseCheckoutErro } } + #[allow(clippy::collapsible_if)] if !is_server_license_still_activated() || (!is_server_floating_license() && crate::license_expiration_time() < SystemTime::now()) { diff --git a/rust/src/flowgraph.rs b/rust/src/flowgraph.rs index 36f66c732..d7d873d95 100644 --- a/rust/src/flowgraph.rs +++ b/rust/src/flowgraph.rs @@ -92,7 +92,7 @@ impl<'a> FlowGraphNode<'a> { } } -unsafe impl<'a> RefCountable for FlowGraphNode<'a> { +unsafe impl RefCountable for FlowGraphNode<'_> { unsafe fn inc_ref(handle: &Self) -> Ref { Ref::new(Self { handle: BNNewFlowGraphNodeReference(handle.handle), @@ -105,7 +105,7 @@ unsafe impl<'a> RefCountable for FlowGraphNode<'a> { } } -impl<'a> ToOwned for FlowGraphNode<'a> { +impl ToOwned for FlowGraphNode<'_> { type Owned = Ref; fn to_owned(&self) -> Self::Owned { diff --git a/rust/src/hlil/instruction.rs b/rust/src/hlil/instruction.rs index 664faf3c3..b114f02ba 100644 --- a/rust/src/hlil/instruction.rs +++ b/rust/src/hlil/instruction.rs @@ -754,7 +754,7 @@ impl HighLevelILInstruction { // TODO: Replace with a From for RegisterValueType. // TODO: We might also want to change the type of `op.constant_data_kind` // TODO: To RegisterValueType and do the conversion when creating instruction. - state: unsafe { std::mem::transmute(op.constant_data_kind) }, + state: unsafe { std::mem::transmute::(op.constant_data_kind) }, value: op.constant_data_value, offset: 0, size: op.size, diff --git a/rust/src/lib.rs b/rust/src/lib.rs index 23d690b86..5e55e6722 100644 --- a/rust/src/lib.rs +++ b/rust/src/lib.rs @@ -16,6 +16,7 @@ #![allow(clippy::missing_safety_doc)] #![allow(clippy::result_unit_err)] #![allow(clippy::type_complexity)] +#![allow(clippy::too_many_arguments)] #![doc(html_root_url = "https://dev-rust.binary.ninja/")] #![doc(html_favicon_url = "/favicon.ico")] #![doc(html_logo_url = "/logo.png")] @@ -211,7 +212,7 @@ unsafe extern "C" fn cb_progress_func bool>( if ctxt.is_null() { return true; } - let closure: &mut F = std::mem::transmute(ctxt); + let closure = &mut *(ctxt as *mut F); closure(progress, total) } diff --git a/rust/src/llil/block.rs b/rust/src/llil/block.rs index 9068bb7a3..da3215785 100644 --- a/rust/src/llil/block.rs +++ b/rust/src/llil/block.rs @@ -12,6 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. +use std::fmt::Debug; use std::ops::Range; use crate::architecture::Architecture; @@ -78,15 +79,16 @@ where } } -impl<'func, A, M, F> fmt::Debug for LowLevelILBlock<'func, A, M, F> +impl<'func, A, M, F> Debug for LowLevelILBlock<'func, A, M, F> where - A: 'func + Architecture, + A: 'func + Architecture + Debug, M: FunctionMutability, F: FunctionForm, { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - // TODO: Make this better - write!(f, "llil_bb {:?}", self.function) + f.debug_struct("LowLevelILBlock") + .field("function", &self.function) + .finish() } } diff --git a/rust/src/llil/function.rs b/rust/src/llil/function.rs index cd1f8e39c..cdb8744fe 100644 --- a/rust/src/llil/function.rs +++ b/rust/src/llil/function.rs @@ -18,6 +18,7 @@ use binaryninjacore_sys::BNLowLevelILFunction; use binaryninjacore_sys::BNNewLowLevelILFunctionReference; use std::borrow::Borrow; +use std::fmt::Debug; use std::hash::{Hash, Hasher}; use std::marker::PhantomData; @@ -63,9 +64,9 @@ pub struct LowLevelILFunction, } -impl<'func, A, M, F> LowLevelILFunction +impl LowLevelILFunction where - A: 'func + Architecture, + A: Architecture, M: FunctionMutability, F: FunctionForm, { @@ -137,9 +138,9 @@ where // LLIL basic blocks are not available until the function object // is finalized, so ensure we can't try requesting basic blocks // during lifting -impl<'func, A, F> LowLevelILFunction +impl LowLevelILFunction where - A: 'func + Architecture, + A: Architecture, F: FunctionForm, { pub fn basic_blocks(&self) -> Array>> { @@ -176,9 +177,9 @@ impl LowLevelILFunction> { } } -impl<'func, A, M, F> ToOwned for LowLevelILFunction +impl ToOwned for LowLevelILFunction where - A: 'func + Architecture, + A: Architecture, M: FunctionMutability, F: FunctionForm, { @@ -189,9 +190,9 @@ where } } -unsafe impl<'func, A, M, F> RefCountable for LowLevelILFunction +unsafe impl RefCountable for LowLevelILFunction where - A: 'func + Architecture, + A: Architecture, M: FunctionMutability, F: FunctionForm, { @@ -210,15 +211,17 @@ where } } -impl<'func, A, M, F> fmt::Debug for LowLevelILFunction +impl Debug for LowLevelILFunction where - A: 'func + Architecture, + A: Architecture + Debug, M: FunctionMutability, F: FunctionForm, { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - // TODO: Make this better - write!(f, "", self.handle) + f.debug_struct("LowLevelILFunction") + .field("arch", &self.arch()) + .field("instruction_count", &self.instruction_count()) + .finish() } } diff --git a/rust/src/mlil/instruction.rs b/rust/src/mlil/instruction.rs index 47f76566c..6892d7176 100644 --- a/rust/src/mlil/instruction.rs +++ b/rust/src/mlil/instruction.rs @@ -753,7 +753,7 @@ impl MediumLevelILInstruction { // TODO: Replace with a From for RegisterValueType. // TODO: We might also want to change the type of `op.constant_data_kind` // TODO: To RegisterValueType and do the conversion when creating instruction. - state: unsafe { std::mem::transmute(op.constant_data_kind) }, + state: unsafe { std::mem::transmute::(op.constant_data_kind) }, value: op.constant_data_value, offset: 0, size: op.size, diff --git a/rust/src/project.rs b/rust/src/project.rs index 45e3a2b82..edea8b234 100644 --- a/rust/src/project.rs +++ b/rust/src/project.rs @@ -1103,7 +1103,7 @@ unsafe extern "C" fn cb_progress_func bool>( if ctxt.is_null() { return true; } - let closure: &mut F = mem::transmute(ctxt); + let closure = &mut *(ctxt as *mut F); closure(progress, total) } diff --git a/rust/src/section.rs b/rust/src/section.rs index cf66c3f99..7df3d014d 100644 --- a/rust/src/section.rs +++ b/rust/src/section.rs @@ -273,7 +273,7 @@ impl SectionBuilder { let len = self.range.end.wrapping_sub(start); unsafe { - let nul_str = std::ffi::CStr::from_bytes_with_nul_unchecked(b"\x00").as_ptr(); + let nul_str = c"".as_ptr(); let name_ptr = name.as_ref().as_ptr() as *mut _; let ty_ptr = ty.map_or(nul_str, |s| s.as_ref().as_ptr() as *mut _); let linked_section_ptr = diff --git a/rust/src/settings.rs b/rust/src/settings.rs index 0c9fd7877..f37a5a348 100644 --- a/rust/src/settings.rs +++ b/rust/src/settings.rs @@ -50,10 +50,6 @@ impl Settings { } } - pub fn default() -> Ref { - Self::new("default") - } - pub fn set_resource_id(&self, resource_id: S) { let resource_id = resource_id.into_bytes_with_nul(); unsafe { BNSettingsSetResourceId(self.handle, resource_id.as_ref().as_ptr() as *mut _) }; diff --git a/rust/src/typearchive.rs b/rust/src/typearchive.rs index 3060a4798..058c6f446 100644 --- a/rust/src/typearchive.rs +++ b/rust/src/typearchive.rs @@ -376,7 +376,7 @@ impl TypeArchive { } /// Get a list of all types' names and ids in the archive at a current snapshot - + /// /// * `snapshot` - Snapshot id to search for types pub fn get_type_names_and_ids( &self, diff --git a/rust/src/typecontainer.rs b/rust/src/typecontainer.rs index 3acb79747..341a44796 100644 --- a/rust/src/typecontainer.rs +++ b/rust/src/typecontainer.rs @@ -351,7 +351,7 @@ impl TypeContainer { /// Parse an entire block of source into types, variables, and functions, with /// knowledge of the types in the Type Container. - + /// /// * `source` - Source code to parse /// * `file_name` - Name of the file containing the source (optional: exists on disk) /// * `options` - String arguments to pass as options, e.g. command line arguments diff --git a/rust/src/typeprinter.rs b/rust/src/typeprinter.rs index 44fb3173b..e9c36f967 100644 --- a/rust/src/typeprinter.rs +++ b/rust/src/typeprinter.rs @@ -365,12 +365,8 @@ impl CoreTypePrinter { impl Default for CoreTypePrinter { fn default() -> Self { // TODO: Remove this entirely, there is no "default", its view specific lets not make this some defined behavior. - let default_settings = crate::settings::Settings::default(); - let name = default_settings.get_string( - std::ffi::CStr::from_bytes_with_nul(b"analysis.types.printerName\x00").unwrap(), - None, - None, - ); + let default_settings = crate::settings::Settings::new("default"); + let name = default_settings.get_string("analysis.types.printerName", None, None); Self::printer_by_name(name).unwrap() } } @@ -931,7 +927,7 @@ unsafe extern "C" fn cb_print_all_types( let ctxt: &mut T = &mut *(ctxt as *mut T); let raw_names = std::slice::from_raw_parts(names, type_count); // NOTE: The caller is responsible for freeing raw_names. - let names: Vec<_> = raw_names.into_iter().map(QualifiedName::from_raw).collect(); + let names: Vec<_> = raw_names.iter().map(QualifiedName::from_raw).collect(); let raw_types = std::slice::from_raw_parts(types, type_count); // NOTE: The caller is responsible for freeing raw_types. let types: Vec<_> = raw_types.iter().map(|&t| Type::ref_from_raw(t)).collect(); diff --git a/rust/src/types.rs b/rust/src/types.rs index cccde1bc2..ff5f67b97 100644 --- a/rust/src/types.rs +++ b/rust/src/types.rs @@ -339,7 +339,7 @@ impl TypeBuilder { pub fn named_type_from_type>(name: T, t: &Type) -> Self { let mut raw_name = QualifiedName::into_raw(name.into()); - let id = CStr::from_bytes_with_nul(b"\0").unwrap(); + let id = c""; let result = unsafe { Self::from_raw(BNCreateNamedTypeReferenceBuilderFromTypeAndId( @@ -726,7 +726,7 @@ impl Type { pub fn named_type_from_type>(name: T, t: &Type) -> Ref { let mut raw_name = QualifiedName::into_raw(name.into()); // TODO: No id is present for this call? - let id = CStr::from_bytes_with_nul(b"\0").unwrap(); + let id = c""; let result = unsafe { Self::ref_from_raw(BNCreateNamedTypeReferenceFromTypeAndId( @@ -1124,6 +1124,7 @@ impl FunctionParameter { } } +// TODO: We need to delete this... // Name, Variable and Type impl CoreArrayProvider for (&str, Variable, &Type) { type Raw = BNVariableNameAndType; @@ -1134,6 +1135,7 @@ impl CoreArrayProvider for (&str, Variable, &Type) { Self: 'a; } +// TODO: This needs to go! unsafe impl CoreArrayProviderInner for (&str, Variable, &Type) { unsafe fn free(raw: *mut Self::Raw, count: usize, _context: &Self::Context) { BNFreeVariableNameAndTypeList(raw, count) @@ -1145,7 +1147,7 @@ unsafe impl CoreArrayProviderInner for (&str, Variable, &Type) { ) -> (&'a str, Variable, &'a Type) { let name = CStr::from_ptr(raw.name).to_str().unwrap(); let var = Variable::from(raw.var); - let var_type = std::mem::transmute(&raw.type_); + let var_type = &*(raw.type_ as *mut Type); (name, var, var_type) } } diff --git a/rust/src/variable.rs b/rust/src/variable.rs index e868663ee..e0bbc2f43 100644 --- a/rust/src/variable.rs +++ b/rust/src/variable.rs @@ -739,8 +739,10 @@ impl PossibleValueSet { } pub(crate) fn into_raw(value: Self) -> BNPossibleValueSet { - let mut raw = BNPossibleValueSet::default(); - raw.state = value.value_type(); + let mut raw = BNPossibleValueSet { + state: value.value_type(), + ..Default::default() + }; match value { PossibleValueSet::UndeterminedValue => {} PossibleValueSet::EntryValue { reg } => { diff --git a/rust/src/workflow.rs b/rust/src/workflow.rs index 236e0cb73..f8042ccc7 100644 --- a/rust/src/workflow.rs +++ b/rust/src/workflow.rs @@ -192,7 +192,7 @@ impl Activity { ctxt: *mut c_void, analysis: *mut BNAnalysisContext, ) { - let ctxt: &mut F = core::mem::transmute(ctxt); + let ctxt = &mut *(ctxt as *mut F); if let Some(analysis) = NonNull::new(analysis) { ctxt(&AnalysisContext::from_raw(analysis)) } @@ -567,29 +567,17 @@ impl Workflow { /// Not yet implemented. pub fn show_metrics(&self) { - unsafe { - BNWorkflowShowReport( - self.handle.as_ptr(), - b"metrics\x00".as_ptr() as *const c_char, - ) - } + unsafe { BNWorkflowShowReport(self.handle.as_ptr(), c"metrics".as_ptr()) } } /// Show the Workflow topology in the UI. pub fn show_topology(&self) { - unsafe { - BNWorkflowShowReport( - self.handle.as_ptr(), - b"topology\x00".as_ptr() as *const c_char, - ) - } + unsafe { BNWorkflowShowReport(self.handle.as_ptr(), c"topology".as_ptr()) } } /// Not yet implemented. pub fn show_trace(&self) { - unsafe { - BNWorkflowShowReport(self.handle.as_ptr(), b"trace\x00".as_ptr() as *const c_char) - } + unsafe { BNWorkflowShowReport(self.handle.as_ptr(), c"trace".as_ptr()) } } } diff --git a/rust/tests/databuffer.rs b/rust/tests/databuffer.rs index 631748a36..441210b6e 100644 --- a/rust/tests/databuffer.rs +++ b/rust/tests/databuffer.rs @@ -22,7 +22,7 @@ fn set_len_write() { // and is not using the original pointer contents.as_mut_slice().fill(0x55); drop(contents); - assert_eq!(data.get_data(), &DUMMY_DATA_0[..]); + assert_eq!(data.get_data(), DUMMY_DATA_0); // make sure the new len truncate the original data unsafe { data.set_len(13) }; From 3edc540b621195af44e0490039e446e499855b95 Mon Sep 17 00:00:00 2001 From: Mason Reed Date: Mon, 13 Jan 2025 00:03:45 -0500 Subject: [PATCH 39/93] Fix typo in rust CI --- .github/workflows/rust.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index 0f643de73..e82ebb5e1 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -22,7 +22,7 @@ jobs: with: components: clippy - name: Clippy Check - - uses: clechasseur/rs-clippy-check@v4 + uses: clechasseur/rs-clippy-check@v4 with: working-directory: ./rust args: --all-features From 449809f8a808da72ffa665a3ba0e12b2574c2bda Mon Sep 17 00:00:00 2001 From: Mason Reed Date: Mon, 13 Jan 2025 00:04:44 -0500 Subject: [PATCH 40/93] Misc rust formatting --- rust/examples/workflow.rs | 5 +---- rust/src/hlil/instruction.rs | 4 +++- rust/src/mlil/instruction.rs | 4 +++- rust/src/typecontainer.rs | 2 +- 4 files changed, 8 insertions(+), 7 deletions(-) diff --git a/rust/examples/workflow.rs b/rust/examples/workflow.rs index 60e7e2c0a..189b14968 100644 --- a/rust/examples/workflow.rs +++ b/rust/examples/workflow.rs @@ -29,10 +29,7 @@ fn example_activity(analysis_context: &AnalysisContext) { llil_instr.visit_tree(&mut |expr, info| { if let ExprInfo::Const(_op) = info { // Replace all consts with 0x1337. - println!( - "Replacing llil expression @ 0x{:x} : {}", - instr, expr.index - ); + println!("Replacing llil expression @ 0x{:x} : {}", instr, expr.index); unsafe { llil.replace_expression(expr.index, llil.const_int(4, 0x1337)) }; diff --git a/rust/src/hlil/instruction.rs b/rust/src/hlil/instruction.rs index b114f02ba..59c3876b3 100644 --- a/rust/src/hlil/instruction.rs +++ b/rust/src/hlil/instruction.rs @@ -754,7 +754,9 @@ impl HighLevelILInstruction { // TODO: Replace with a From for RegisterValueType. // TODO: We might also want to change the type of `op.constant_data_kind` // TODO: To RegisterValueType and do the conversion when creating instruction. - state: unsafe { std::mem::transmute::(op.constant_data_kind) }, + state: unsafe { + std::mem::transmute::(op.constant_data_kind) + }, value: op.constant_data_value, offset: 0, size: op.size, diff --git a/rust/src/mlil/instruction.rs b/rust/src/mlil/instruction.rs index 6892d7176..5c070779c 100644 --- a/rust/src/mlil/instruction.rs +++ b/rust/src/mlil/instruction.rs @@ -753,7 +753,9 @@ impl MediumLevelILInstruction { // TODO: Replace with a From for RegisterValueType. // TODO: We might also want to change the type of `op.constant_data_kind` // TODO: To RegisterValueType and do the conversion when creating instruction. - state: unsafe { std::mem::transmute::(op.constant_data_kind) }, + state: unsafe { + std::mem::transmute::(op.constant_data_kind) + }, value: op.constant_data_value, offset: 0, size: op.size, diff --git a/rust/src/typecontainer.rs b/rust/src/typecontainer.rs index 341a44796..5453e3d84 100644 --- a/rust/src/typecontainer.rs +++ b/rust/src/typecontainer.rs @@ -351,7 +351,7 @@ impl TypeContainer { /// Parse an entire block of source into types, variables, and functions, with /// knowledge of the types in the Type Container. - /// + /// /// * `source` - Source code to parse /// * `file_name` - Name of the file containing the source (optional: exists on disk) /// * `options` - String arguments to pass as options, e.g. command line arguments From ab0607b4959f43eb8feec9b462f7e54d263656ba Mon Sep 17 00:00:00 2001 From: Mason Reed Date: Mon, 13 Jan 2025 15:12:03 -0500 Subject: [PATCH 41/93] Fix misc typos and add typos to rust CI --- .github/workflows/rust.yml | 11 +++++++++++ plugins/pdb-ng/src/symbol_parser.rs | 2 +- rust/src/binaryview.rs | 6 +++--- rust/src/confidence.rs | 10 +++++----- rust/src/custombinaryview.rs | 4 ++-- rust/src/function.rs | 6 +++--- rust/src/interaction.rs | 2 +- rust/src/project.rs | 2 +- rust/src/symbol.rs | 2 +- rust/src/typeprinter.rs | 2 +- rust/src/types.rs | 24 ++++++++++++------------ 11 files changed, 41 insertions(+), 30 deletions(-) diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index e82ebb5e1..b1e69106b 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -41,3 +41,14 @@ jobs: uses: actions-rust-lang/rustfmt@v1 with: manifest-path: ./rust/Cargo.toml + + # Check spelling with typos + spelling: + name: typos + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - name: Typo Check + uses: crate-ci/typos@v1.29.4 + with: + files: ./rust \ No newline at end of file diff --git a/plugins/pdb-ng/src/symbol_parser.rs b/plugins/pdb-ng/src/symbol_parser.rs index 5bf636f80..51e18d1c0 100644 --- a/plugins/pdb-ng/src/symbol_parser.rs +++ b/plugins/pdb-ng/src/symbol_parser.rs @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -use binaryninja::confidence::ConfMergable; +use binaryninja::confidence::ConfMergeable; use std::collections::{BTreeMap, HashMap, HashSet}; use std::mem; use std::sync::OnceLock; diff --git a/rust/src/binaryview.rs b/rust/src/binaryview.rs index 6a9246fb7..c28c2c594 100644 --- a/rust/src/binaryview.rs +++ b/rust/src/binaryview.rs @@ -844,13 +844,13 @@ pub trait BinaryViewExt: BinaryViewBase { // TODO: Replace with BulkModify guard. /// Start adding segments in bulk. Useful for adding large numbers of segments. /// - /// After calling this any call to [BinaryViewExt::add_segment] will be uncommited until a call to + /// After calling this any call to [BinaryViewExt::add_segment] will be uncommitted until a call to /// [BinaryViewExt::end_bulk_add_segments] /// - /// If you wish to discard the uncommited segments you can call [BinaryViewExt::cancel_bulk_add_segments]. + /// If you wish to discard the uncommitted segments you can call [BinaryViewExt::cancel_bulk_add_segments]. /// /// NOTE: This **must** be paired with a later call to [BinaryViewExt::end_bulk_add_segments] or - /// [BinaryViewExt::cancel_bulk_add_segments], otherwise segments added after this call will stay uncommited. + /// [BinaryViewExt::cancel_bulk_add_segments], otherwise segments added after this call will stay uncommitted. fn begin_bulk_add_segments(&self) { unsafe { BNBeginBulkAddSegments(self.as_ref().handle) } } diff --git a/rust/src/confidence.rs b/rust/src/confidence.rs index 792e46be0..4d9152c5f 100644 --- a/rust/src/confidence.rs +++ b/rust/src/confidence.rs @@ -24,7 +24,7 @@ pub struct Conf { pub confidence: u8, } -pub trait ConfMergable { +pub trait ConfMergeable { type Result; /// Merge two confidence types' values depending on whichever has higher confidence /// In the event of a tie, the LHS (caller's) value is used. @@ -57,7 +57,7 @@ impl Conf { /// Returns best value or LHS on tie /// /// `Conf` + `Conf` → `Conf` -impl ConfMergable> for Conf { +impl ConfMergeable> for Conf { type Result = Conf; fn merge(self, other: Conf) -> Conf { if other.confidence > self.confidence { @@ -71,7 +71,7 @@ impl ConfMergable> for Conf { /// Returns LHS if RHS is None /// /// `Conf` + `Option>` → `Conf` -impl ConfMergable>> for Conf { +impl ConfMergeable>> for Conf { type Result = Conf; fn merge(self, other: Option>) -> Conf { match other { @@ -84,7 +84,7 @@ impl ConfMergable>> for Conf { /// Returns RHS if LHS is None /// /// `Option>` + `Conf` → `Conf` -impl ConfMergable> for Option> { +impl ConfMergeable> for Option> { type Result = Conf; fn merge(self, other: Conf) -> Conf { match self { @@ -97,7 +97,7 @@ impl ConfMergable> for Option> { /// Returns best non-None value or None /// /// `Option>` + `Option>` → `Option>` -impl ConfMergable>> for Option> { +impl ConfMergeable>> for Option> { type Result = Option>; fn merge(self, other: Option>) -> Option> { match (self, other) { diff --git a/rust/src/custombinaryview.rs b/rust/src/custombinaryview.rs index cfef7886b..b8a11df9f 100644 --- a/rust/src/custombinaryview.rs +++ b/rust/src/custombinaryview.rs @@ -430,7 +430,7 @@ impl<'a, T: CustomBinaryViewType> CustomViewBuilder<'a, T> { /// /// The `BinaryView` argument passed to the constructor function is the object that is expected /// to be returned by the `AsRef` implementation required by the `BinaryViewBase` trait. - /// TODO FIXME welp this is broke going to need 2 init callbacks + /// TODO FIXME whelp this is broke going to need 2 init callbacks pub fn create(self, parent: &BinaryView, view_args: V::Args) -> Result> where V: CustomBinaryView, @@ -545,7 +545,7 @@ impl<'a, T: CustomBinaryViewType> CustomViewBuilder<'a, T> { // prohibited, so an API contract was violated in order to get here. // // if we're here, it's too late to do anything about it, though we can at least not - // run the destructor on the custom view since that memory is unitialized. + // run the destructor on the custom view since that memory is uninitialized. log::error!( "BinaryViewBase::freeObject called on partially initialized object! crash imminent!" ); diff --git a/rust/src/function.rs b/rust/src/function.rs index c8b695bcd..7e480896a 100644 --- a/rust/src/function.rs +++ b/rust/src/function.rs @@ -2014,7 +2014,7 @@ impl Function { unsafe { Array::new(vars, count, ()) } } - /// Merge one or more varibles in `sources` into the `target` variable. All + /// Merge one or more variables in `sources` into the `target` variable. All /// variable accesses to the variables in `sources` will be rewritten to use `target`. /// /// * `target` - target variable @@ -2058,7 +2058,7 @@ impl Function { } } - /// Splits a varible at the definition site. The given `var` must be the + /// Splits a variable at the definition site. The given `var` must be the /// variable unique to the definition and should be obtained by using /// [crate::mlil::MediumLevelILInstruction::get_split_var_for_definition] at the definition site. /// @@ -2081,7 +2081,7 @@ impl Function { unsafe { BNSplitVariable(self.handle, &raw_var) } } - /// Undoes varible splitting performed with [Function::split_variable]. The given `var` + /// Undoes variable splitting performed with [Function::split_variable]. The given `var` /// must be the variable unique to the definition and should be obtained by using /// [crate::mlil::MediumLevelILInstruction::get_split_var_for_definition] at the definition site. /// diff --git a/rust/src/interaction.rs b/rust/src/interaction.rs index 6c8715fc9..15a47daec 100644 --- a/rust/src/interaction.rs +++ b/rust/src/interaction.rs @@ -218,7 +218,7 @@ impl FormInputBuilder { } /// Form Field: Vertical spacing - pub fn seperator_field(mut self) -> Self { + pub fn separator_field(mut self) -> Self { let mut result = unsafe { std::mem::zeroed::() }; result.type_ = BNFormInputFieldType::SeparatorFormField; result.hasDefault = false; diff --git a/rust/src/project.rs b/rust/src/project.rs index edea8b234..9ea8ba3cd 100644 --- a/rust/src/project.rs +++ b/rust/src/project.rs @@ -745,7 +745,7 @@ impl Project { /// } /// } /// ``` - // NOTE mut is used here, so only one lock can be aquired at once + // NOTE mut is used here, so only one lock can be acquired at once pub fn bulk_operation(&mut self) -> Result { Ok(ProjectBulkOperationLock::lock(self)) } diff --git a/rust/src/symbol.rs b/rust/src/symbol.rs index 337e034bd..21df31a7e 100644 --- a/rust/src/symbol.rs +++ b/rust/src/symbol.rs @@ -270,7 +270,7 @@ impl Symbol { unsafe { BNIsSymbolAutoDefined(self.handle) } } - /// Wether this symbol has external linkage + /// Whether this symbol has external linkage pub fn external(&self) -> bool { self.binding() == Binding::Weak || self.binding() == Binding::Global } diff --git a/rust/src/typeprinter.rs b/rust/src/typeprinter.rs index e9c36f967..a5c75bf06 100644 --- a/rust/src/typeprinter.rs +++ b/rust/src/typeprinter.rs @@ -426,7 +426,7 @@ pub trait TypePrinter { ) -> Option>; /// In a single-line text representation of a type, generate the tokens - /// that should be printed after the type's name. Returns a ist of text + /// that should be printed after the type's name. Returns a list of text /// tokens representing the type /// /// * `type_` - Type to print diff --git a/rust/src/types.rs b/rust/src/types.rs index ff5f67b97..73d79c3b2 100644 --- a/rust/src/types.rs +++ b/rust/src/types.rs @@ -1944,7 +1944,7 @@ impl Debug for NamedTypeReference { #[derive(Default, Debug, Clone, Hash, PartialEq, Eq, Ord, PartialOrd)] pub struct QualifiedName { // TODO: Make this Option where default is "::". - pub seperator: String, + pub separator: String, pub items: Vec, } @@ -1956,8 +1956,8 @@ impl QualifiedName { .iter() .filter_map(|&raw_name| raw_to_string(raw_name as *const _)) .collect(); - let seperator = raw_to_string(value.join).unwrap(); - Self { items, seperator } + let separator = raw_to_string(value.join).unwrap(); + Self { items, separator } } pub(crate) fn from_owned_raw(value: BNQualifiedName) -> Self { @@ -1967,7 +1967,7 @@ impl QualifiedName { } pub fn into_raw(value: Self) -> BNQualifiedName { - let bn_join = BnString::new(&value.seperator); + let bn_join = BnString::new(&value.separator); BNQualifiedName { // NOTE: Leaking string list must be freed by core or us! name: strings_to_string_list(&value.items), @@ -1983,17 +1983,17 @@ impl QualifiedName { } pub fn new(items: Vec) -> Self { - Self::new_with_seperator(items, "::".to_string()) + Self::new_with_separator(items, "::".to_string()) } - pub fn new_with_seperator(items: Vec, seperator: String) -> Self { - Self { items, seperator } + pub fn new_with_separator(items: Vec, separator: String) -> Self { + Self { items, separator } } pub fn with_item(&self, item: impl Into) -> Self { let mut items = self.items.clone(); items.push(item.into()); - Self::new_with_seperator(items, self.seperator.clone()) + Self::new_with_separator(items, self.separator.clone()) } pub fn push(&mut self, item: String) { @@ -2014,7 +2014,7 @@ impl QualifiedName { self.items.split_last().map(|(a, b)| { ( a.to_owned(), - QualifiedName::new_with_seperator(b.to_vec(), self.seperator.clone()), + QualifiedName::new_with_separator(b.to_vec(), self.separator.clone()), ) }) } @@ -2042,7 +2042,7 @@ impl QualifiedName { .iter() .map(|item| item.replace(from, to)) .collect(), - seperator: self.seperator.clone(), + separator: self.separator.clone(), } } @@ -2074,7 +2074,7 @@ impl From for QualifiedName { Self { items: vec![value], // TODO: See comment in struct def. - seperator: String::from("::"), + separator: String::from("::"), } } } @@ -2135,7 +2135,7 @@ impl IndexMut for QualifiedName { impl Display for QualifiedName { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { - write!(f, "{}", self.items.join(&self.seperator)) + write!(f, "{}", self.items.join(&self.separator)) } } From 840c95cc734aecf4f7f5cab64f7f1fddb0d7fdac Mon Sep 17 00:00:00 2001 From: Mason Reed Date: Mon, 13 Jan 2025 16:05:25 -0500 Subject: [PATCH 42/93] Add cargo workspace This will help tooling and external contributors get a map of the rust crates within binaryninja-api --- .gitignore | 1 + Cargo.toml | 20 ++++++++++++++++++++ 2 files changed, 21 insertions(+) create mode 100644 Cargo.toml diff --git a/.gitignore b/.gitignore index 91a0f3abb..adb601d32 100644 --- a/.gitignore +++ b/.gitignore @@ -84,6 +84,7 @@ rust/target/ rust/binaryninjacore-sys/target/ rust/examples/*/target/ rust/examples/dwarf/*/target/ +Cargo.lock # Debugger docs docs/img/debugger diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 000000000..686430cda --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,20 @@ +# This is the workspace for all rust projects in binaryninja-api +# This is not a part of the build process, rather a way to link all rust code together so that external +# contributors have a map of all rust code. The main benefit to providing this workspace is tooling, as cloning +# binaryninja-api will result in this root workspace being picked up and used for LSP and cargo commands automatically. +[workspace] +resolver = "2" +members = [ + "rust", + "rust/binaryninjacore-sys", + "arch/riscv", + "arch/msp430", + "plugins/dwarf/dwarf_import", + "plugins/dwarf/dwarf_export", + "plugins/dwarf/dwarfdump", + "plugins/dwarf/shared", + "plugins/idb_import", + "plugins/minidump", + "plugins/pdb-ng", + "plugins/warp" +] \ No newline at end of file From 5b974d7ff9464cc3729d5ab99c584ac755861e05 Mon Sep 17 00:00:00 2001 From: Mason Reed Date: Mon, 13 Jan 2025 22:56:57 -0500 Subject: [PATCH 43/93] More rust cleanup - Format all rust plugins - Fix some tests that were out of date - Simplify WARP tests to only binaries, building object files from source is a pain - Link to core in all rust plugins - Fix some memory leaks - Update warp insta snapshots - Fix some misc clippy lints --- arch/msp430/Cargo.toml | 1 + arch/msp430/build.rs | 15 ++ arch/msp430/src/architecture.rs | 144 +++++------- arch/msp430/src/flag.rs | 6 +- arch/msp430/src/lib.rs | 18 +- arch/msp430/src/lift.rs | 11 +- arch/msp430/src/register.rs | 3 +- arch/riscv/Cargo.toml | 1 + arch/riscv/build.rs | 15 ++ arch/riscv/disasm/src/lib.rs | 24 +- arch/riscv/src/lib.rs | 46 ++-- plugins/dwarf/dwarf_export/Cargo.toml | 1 + plugins/dwarf/dwarf_export/build.rs | 15 ++ plugins/dwarf/dwarf_export/src/lib.rs | 16 +- plugins/dwarf/dwarf_import/Cargo.toml | 1 + plugins/dwarf/dwarf_import/build.rs | 15 ++ .../dwarf/dwarf_import/src/die_handlers.rs | 60 ++--- .../dwarf/dwarf_import/src/dwarfdebuginfo.rs | 147 +++++++------ plugins/dwarf/dwarf_import/src/functions.rs | 74 ++++--- plugins/dwarf/dwarf_import/src/helpers.rs | 200 +++++++++-------- plugins/dwarf/dwarf_import/src/lib.rs | 208 ++++++++++-------- plugins/dwarf/dwarf_import/src/types.rs | 101 ++++----- plugins/dwarf/dwarfdump/Cargo.toml | 1 + plugins/dwarf/dwarfdump/build.rs | 15 ++ plugins/dwarf/dwarfdump/src/lib.rs | 13 +- plugins/dwarf/shared/Cargo.toml | 1 + plugins/dwarf/shared/build.rs | 15 ++ plugins/dwarf/shared/src/lib.rs | 23 +- plugins/idb_import/Cargo.toml | 1 + plugins/idb_import/build.rs | 15 ++ plugins/idb_import/src/lib.rs | 4 +- plugins/idb_import/src/types.rs | 10 +- plugins/minidump/build.rs | 8 +- plugins/minidump/src/lib.rs | 6 +- plugins/pdb-ng/Cargo.toml | 1 + plugins/pdb-ng/build.rs | 15 ++ plugins/pdb-ng/src/lib.rs | 74 +++---- plugins/pdb-ng/src/parser.rs | 29 ++- plugins/pdb-ng/src/struct_grouper.rs | 24 +- plugins/pdb-ng/src/symbol_parser.rs | 123 +++++------ plugins/pdb-ng/src/type_parser.rs | 171 +++++++------- plugins/warp/Cargo.toml | 5 +- plugins/warp/build.rs | 121 ++-------- plugins/warp/fixtures/src/library.c | 33 --- plugins/warp/fixtures/src/library.h | 11 - plugins/warp/fixtures/src/simple.c | 15 -- plugins/warp/src/bin/sigem.rs | 3 +- plugins/warp/src/cache.rs | 10 +- plugins/warp/src/convert.rs | 6 +- plugins/warp/src/lib.rs | 10 +- ...rp_ninja__tests__insta_signatures.snap.new | 40 ++++ rust/src/platform.rs | 3 + rust/src/typecontainer.rs | 20 +- rust/src/types.rs | 13 +- rust/tests/platform.rs | 4 +- rust/tests/typecontainer.rs | 2 +- rust/tests/types.rs | 37 +++- 57 files changed, 1044 insertions(+), 960 deletions(-) create mode 100644 arch/msp430/build.rs create mode 100644 arch/riscv/build.rs create mode 100644 plugins/dwarf/dwarf_export/build.rs create mode 100644 plugins/dwarf/dwarf_import/build.rs create mode 100644 plugins/dwarf/dwarfdump/build.rs create mode 100644 plugins/dwarf/shared/build.rs create mode 100644 plugins/idb_import/build.rs create mode 100644 plugins/pdb-ng/build.rs delete mode 100644 plugins/warp/fixtures/src/library.c delete mode 100644 plugins/warp/fixtures/src/library.h delete mode 100644 plugins/warp/fixtures/src/simple.c create mode 100644 plugins/warp/src/snapshots/warp_ninja__tests__insta_signatures.snap.new diff --git a/arch/msp430/Cargo.toml b/arch/msp430/Cargo.toml index d97b8fc8d..cba425996 100644 --- a/arch/msp430/Cargo.toml +++ b/arch/msp430/Cargo.toml @@ -6,6 +6,7 @@ edition = "2021" [dependencies] binaryninja = { path = "../../rust" } +binaryninjacore-sys = { path = "../../rust/binaryninjacore-sys" } log = "0.4" msp430-asm = "^0.2" diff --git a/arch/msp430/build.rs b/arch/msp430/build.rs new file mode 100644 index 000000000..ed6cec7d2 --- /dev/null +++ b/arch/msp430/build.rs @@ -0,0 +1,15 @@ +fn main() { + let link_path = std::env::var_os("DEP_BINARYNINJACORE_PATH") + .expect("DEP_BINARYNINJACORE_PATH not specified"); + + println!("cargo::rustc-link-lib=dylib=binaryninjacore"); + println!("cargo::rustc-link-search={}", link_path.to_str().unwrap()); + + #[cfg(not(target_os = "windows"))] + { + println!( + "cargo::rustc-link-arg=-Wl,-rpath,{0},-L{0}", + link_path.to_string_lossy() + ); + } +} diff --git a/arch/msp430/src/architecture.rs b/arch/msp430/src/architecture.rs index 55d39f0cc..4cf0cc27e 100644 --- a/arch/msp430/src/architecture.rs +++ b/arch/msp430/src/architecture.rs @@ -4,8 +4,8 @@ use crate::register::Register; use binaryninja::{ architecture::{ - Architecture, CoreArchitecture, CustomArchitectureHandle, FlagCondition, - InstructionInfo, UnusedIntrinsic, UnusedRegisterStack, UnusedRegisterStackInfo, + Architecture, CoreArchitecture, CustomArchitectureHandle, FlagCondition, InstructionInfo, + UnusedIntrinsic, UnusedRegisterStack, UnusedRegisterStackInfo, }, disassembly::{InstructionTextToken, InstructionTextTokenKind}, llil::{LiftedExpr, Lifter}, @@ -17,8 +17,10 @@ use msp430_asm::{ single_operand::SingleOperand, two_operand::TwoOperand, }; +use binaryninja::architecture::{ + BranchKind, FlagClassId, FlagGroupId, FlagId, FlagWriteId, RegisterId, +}; use log::error; -use binaryninja::architecture::{BranchKind, FlagClassId, FlagGroupId, FlagId, FlagWriteId, RegisterId}; const MIN_MNEMONIC: usize = 9; @@ -83,81 +85,51 @@ impl Architecture for Msp430 { match inst { Instruction::Jnz(inst) => { - info.add_branch( - BranchKind::True(offset_to_absolute(addr, inst.offset())), - ); - info.add_branch( - BranchKind::False(addr + inst.size() as u64), - ); + info.add_branch(BranchKind::True(offset_to_absolute(addr, inst.offset()))); + info.add_branch(BranchKind::False(addr + inst.size() as u64)); } Instruction::Jz(inst) => { - info.add_branch( - BranchKind::True(offset_to_absolute(addr, inst.offset())), - ); - info.add_branch( - BranchKind::False(addr + inst.size() as u64), - ); + info.add_branch(BranchKind::True(offset_to_absolute(addr, inst.offset()))); + info.add_branch(BranchKind::False(addr + inst.size() as u64)); } Instruction::Jlo(inst) => { - info.add_branch( - BranchKind::True(offset_to_absolute(addr, inst.offset())), - ); - info.add_branch( - BranchKind::False(addr + inst.size() as u64), - ); + info.add_branch(BranchKind::True(offset_to_absolute(addr, inst.offset()))); + info.add_branch(BranchKind::False(addr + inst.size() as u64)); } Instruction::Jc(inst) => { - info.add_branch( - BranchKind::True(offset_to_absolute(addr, inst.offset())), - ); - info.add_branch( - BranchKind::False(addr + inst.size() as u64), - ); + info.add_branch(BranchKind::True(offset_to_absolute(addr, inst.offset()))); + info.add_branch(BranchKind::False(addr + inst.size() as u64)); } Instruction::Jn(inst) => { - info.add_branch( - BranchKind::True(offset_to_absolute(addr, inst.offset())), - ); - info.add_branch( - BranchKind::False(addr + inst.size() as u64), - ); + info.add_branch(BranchKind::True(offset_to_absolute(addr, inst.offset()))); + info.add_branch(BranchKind::False(addr + inst.size() as u64)); } Instruction::Jge(inst) => { - info.add_branch( - BranchKind::True(offset_to_absolute(addr, inst.offset())), - ); - info.add_branch( - BranchKind::False(addr + inst.size() as u64), - ); + info.add_branch(BranchKind::True(offset_to_absolute(addr, inst.offset()))); + info.add_branch(BranchKind::False(addr + inst.size() as u64)); } Instruction::Jl(inst) => { - info.add_branch( - BranchKind::True(offset_to_absolute(addr, inst.offset())), - ); - info.add_branch( - BranchKind::False(addr + inst.size() as u64), - ); + info.add_branch(BranchKind::True(offset_to_absolute(addr, inst.offset()))); + info.add_branch(BranchKind::False(addr + inst.size() as u64)); } Instruction::Jmp(inst) => { - info.add_branch( - BranchKind::Unconditional(offset_to_absolute(addr, inst.offset())), - ); + info.add_branch(BranchKind::Unconditional(offset_to_absolute( + addr, + inst.offset(), + ))); } Instruction::Br(inst) => match inst.destination() { - Some(Operand::RegisterDirect(_)) => { - info.add_branch(BranchKind::Indirect) + Some(Operand::RegisterDirect(_)) => info.add_branch(BranchKind::Indirect), + Some(Operand::Indexed(_)) => info.add_branch(BranchKind::Indirect), + Some(Operand::Absolute(value)) => { + info.add_branch(BranchKind::Unconditional(*value as u64)) } - Some(Operand::Indexed(_)) => { - info.add_branch(BranchKind::Indirect) - } - Some(Operand::Absolute(value)) => info.add_branch( - BranchKind::Unconditional(*value as u64), - ), Some(Operand::Symbolic(offset)) => info.add_branch( BranchKind::Unconditional((addr as i64 + *offset as i64) as u64), ), - Some(Operand::Immediate(addr)) => info - .add_branch(BranchKind::Unconditional(*addr as u64)), + Some(Operand::Immediate(addr)) => { + info.add_branch(BranchKind::Unconditional(*addr as u64)) + } Some(Operand::Constant(_)) => { info.add_branch(BranchKind::Unconditional(addr)) } @@ -168,24 +140,16 @@ impl Architecture for Msp430 { None => {} }, Instruction::Call(inst) => match inst.source() { - Operand::RegisterDirect(_) => { - info.add_branch(BranchKind::Indirect) - } - Operand::Indexed(_) => { - info.add_branch(BranchKind::Indirect) - } + Operand::RegisterDirect(_) => info.add_branch(BranchKind::Indirect), + Operand::Indexed(_) => info.add_branch(BranchKind::Indirect), Operand::Absolute(value) => { info.add_branch(BranchKind::Call(*value as u64)) } - Operand::Symbolic(offset) => info.add_branch( - BranchKind::Call((addr as i64 + *offset as i64) as u64), - ), - Operand::Immediate(addr) => { - info.add_branch(BranchKind::Call(*addr as u64)) - } - Operand::Constant(_) => { - info.add_branch(BranchKind::Call(addr)) + Operand::Symbolic(offset) => { + info.add_branch(BranchKind::Call((addr as i64 + *offset as i64) as u64)) } + Operand::Immediate(addr) => info.add_branch(BranchKind::Call(*addr as u64)), + Operand::Constant(_) => info.add_branch(BranchKind::Call(addr)), Operand::RegisterIndirect(_) | Operand::RegisterIndirectAutoIncrement(_) => { info.add_branch(BranchKind::Indirect) @@ -468,7 +432,7 @@ fn generate_single_operand_tokens( if inst.mnemonic().len() < MIN_MNEMONIC { let padding = " ".repeat(MIN_MNEMONIC - inst.mnemonic().len()); res.push(InstructionTextToken::new( - &padding, + padding, InstructionTextTokenKind::Text, )) } @@ -489,13 +453,13 @@ fn generate_jxx_tokens(inst: &impl Jxx, addr: u64) -> Vec if inst.mnemonic().len() < MIN_MNEMONIC { let padding = " ".repeat(MIN_MNEMONIC - inst.mnemonic().len()); res.push(InstructionTextToken::new( - &padding, + padding, InstructionTextTokenKind::Text, )) } res.push(InstructionTextToken::new( - &format!("0x{fixed_addr:4x}"), + format!("0x{fixed_addr:4x}"), InstructionTextTokenKind::CodeRelativeAddress { value: fixed_addr, size: None, @@ -514,7 +478,7 @@ fn generate_two_operand_tokens(inst: &impl TwoOperand, addr: u64) -> Vec Vec vec![InstructionTextToken::new( - &format!("r{r}"), + format!("r{r}"), InstructionTextTokenKind::Register, )], }, @@ -674,10 +638,7 @@ fn generate_operand_tokens(source: &Operand, addr: u64, call: bool) -> Vec Vec { @@ -703,24 +664,21 @@ fn generate_operand_tokens(source: &Operand, addr: u64, call: bool) -> Vec { let value = (addr as i64 + *i as i64) as u64; vec![InstructionTextToken::new( - &format!("{value:#x}"), - InstructionTextTokenKind::CodeRelativeAddress { - value, - size: None, - }, + format!("{value:#x}"), + InstructionTextTokenKind::CodeRelativeAddress { value, size: None }, )] } Operand::Immediate(i) => { if call { vec![InstructionTextToken::new( - &format!("{i:#x}"), + format!("{i:#x}"), InstructionTextTokenKind::CodeRelativeAddress { value: *i as u64, size: None, @@ -728,7 +686,7 @@ fn generate_operand_tokens(source: &Operand, addr: u64, call: bool) -> Vec Vec { if call { vec![InstructionTextToken::new( - &format!("{a:#x}"), + format!("{a:#x}"), InstructionTextTokenKind::CodeRelativeAddress { value: *a as u64, size: None, @@ -747,7 +705,7 @@ fn generate_operand_tokens(source: &Operand, addr: u64, call: bool) -> Vec Vec 1, Self::N => 2, Self::V => 8, - }.into() + } + .into() } } @@ -125,7 +126,8 @@ impl architecture::FlagWrite for FlagWrite { Self::Nz => 2, Self::Nvz => 3, Self::Cnz => 4, - }.into() + } + .into() } fn flags_written(&self) -> Vec { diff --git a/arch/msp430/src/lib.rs b/arch/msp430/src/lib.rs index 7654b33c2..c8428209f 100644 --- a/arch/msp430/src/lib.rs +++ b/arch/msp430/src/lib.rs @@ -2,8 +2,14 @@ extern crate binaryninja; extern crate log; extern crate msp430_asm; +use binaryninja::{ + add_optional_plugin_dependency, + architecture::ArchitectureExt, + callingconvention, + custombinaryview::{BinaryViewType, BinaryViewTypeExt}, + Endianness, +}; use log::LevelFilter; -use binaryninja::{add_optional_plugin_dependency, architecture::ArchitectureExt, callingconvention, custombinaryview::{BinaryViewType, BinaryViewTypeExt}, Endianness}; mod architecture; mod flag; @@ -17,10 +23,10 @@ use binaryninja::logger::Logger; #[allow(non_snake_case)] pub extern "C" fn CorePluginInit() -> bool { Logger::new("MSP430").with_level(LevelFilter::Info).init(); - let arch = binaryninja::architecture::register_architecture( - "msp430", - |custom_handle, handle| Msp430::new(handle, custom_handle), - ); + let arch = + binaryninja::architecture::register_architecture("msp430", |custom_handle, handle| { + Msp430::new(handle, custom_handle) + }); // we may need to introduce additional calling conventions here to // support additional ABIs. MSPGCC's calling convention (what @@ -55,4 +61,4 @@ pub extern "C" fn CorePluginInit() -> bool { #[allow(non_snake_case)] pub extern "C" fn CorePluginDependencies() { add_optional_plugin_dependency("view_elf"); -} \ No newline at end of file +} diff --git a/arch/msp430/src/lift.rs b/arch/msp430/src/lift.rs index 1c09f7485..85dd7326a 100644 --- a/arch/msp430/src/lift.rs +++ b/arch/msp430/src/lift.rs @@ -140,26 +140,27 @@ macro_rules! conditional_jump { let false_addr = $addr + $inst.size() as u64; let mut new_true = true; let mut new_false = false; - + let mut true_label = $il.label_for_address(true_addr).unwrap_or_else(|| { new_true = true; Label::new() }); - + let mut false_label = $il.label_for_address(false_addr).unwrap_or_else(|| { new_false = true; Label::new() }); - $il.if_expr($cond, &mut true_label, &mut false_label).append(); - + $il.if_expr($cond, &mut true_label, &mut false_label) + .append(); + if new_true { $il.mark_label(&mut true_label); $il.jump($il.const_ptr(true_addr)).append(); } else { $il.update_label_for_address(true_addr, true_label); } - + if new_false { $il.mark_label(&mut false_label); } else { diff --git a/arch/msp430/src/register.rs b/arch/msp430/src/register.rs index 6bba3df18..a49a4e645 100644 --- a/arch/msp430/src/register.rs +++ b/arch/msp430/src/register.rs @@ -108,7 +108,8 @@ impl architecture::Register for Register { Self::R13 => 13, Self::R14 => 14, Self::R15 => 15, - }.into() + } + .into() } } diff --git a/arch/riscv/Cargo.toml b/arch/riscv/Cargo.toml index 974535b77..77fc45fd5 100644 --- a/arch/riscv/Cargo.toml +++ b/arch/riscv/Cargo.toml @@ -6,6 +6,7 @@ edition = "2021" [dependencies] binaryninja = { path = "../../rust" } +binaryninjacore-sys = { path = "../../rust/binaryninjacore-sys" } riscv-dis = { path = "disasm" } log = "0.4" rayon = { version = "1.0", optional = true } diff --git a/arch/riscv/build.rs b/arch/riscv/build.rs new file mode 100644 index 000000000..ed6cec7d2 --- /dev/null +++ b/arch/riscv/build.rs @@ -0,0 +1,15 @@ +fn main() { + let link_path = std::env::var_os("DEP_BINARYNINJACORE_PATH") + .expect("DEP_BINARYNINJACORE_PATH not specified"); + + println!("cargo::rustc-link-lib=dylib=binaryninjacore"); + println!("cargo::rustc-link-search={}", link_path.to_str().unwrap()); + + #[cfg(not(target_os = "windows"))] + { + println!( + "cargo::rustc-link-arg=-Wl,-rpath,{0},-L{0}", + link_path.to_string_lossy() + ); + } +} diff --git a/arch/riscv/disasm/src/lib.rs b/arch/riscv/disasm/src/lib.rs index ad612e7af..41c711cec 100644 --- a/arch/riscv/disasm/src/lib.rs +++ b/arch/riscv/disasm/src/lib.rs @@ -4,8 +4,6 @@ // finish transition to from_instr32 from 'new' // make the various component structs smaller (8 bit IntReg/FloatReg etc.) -extern crate byteorder; - use std::borrow::Cow; use std::fmt; use std::marker::PhantomData; @@ -345,21 +343,21 @@ pub enum Operand { impl fmt::Display for Operand { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - match self { - &Operand::R(ref r) => write!(f, "x{}", r.id()), - &Operand::F(ref r) => write!(f, "f{}", r.id()), - &Operand::I(i) => match i { + match *self { + Operand::R(r) => write!(f, "x{}", r.id()), + Operand::F(r) => write!(f, "f{}", r.id()), + Operand::I(i) => match i { -0x80000..=-1 => write!(f, "-{:x}", -i), _ => write!(f, "{:x}", i), }, - &Operand::M(i, ref r) => { + Operand::M(i, r) => { if i < 0 { write!(f, "-{:x}(x{})", -i, r.id()) } else { write!(f, "{:x}(x{})", i, r.id()) } } - &Operand::RM(ref r) => write!(f, "{}", r.name()), + Operand::RM(r) => write!(f, "{}", r.name()), } } } @@ -1777,7 +1775,7 @@ pub enum Instr { impl Instr { pub fn mnem(&self) -> Mnem { - Mnem(&self) + Mnem(self) } pub fn operands(&self) -> Vec> { @@ -2300,7 +2298,7 @@ impl<'a, D: RiscVDisassembler + 'a> Mnem<'a, D> { } } -impl<'a, D: RiscVDisassembler> fmt::Display for Mnem<'a, D> { +impl fmt::Display for Mnem<'_, D> { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match (self.mnem(), self.suffix()) { (m, None) => f.pad(m), @@ -3141,9 +3139,7 @@ pub trait RiscVDisassembler: Sized + Copy + Clone { f if (f & 0xfe0) == 0x120 => { Op::SfenceVma(RTypeIntInst::new(inst)?) } - 0x104 => { - Op::SfenceVm(RTypeIntInst::new(inst)?) - } + 0x104 => Op::SfenceVm(RTypeIntInst::new(inst)?), 0x000 => Op::Ecall, 0x001 => Op::Ebreak, @@ -3171,7 +3167,7 @@ pub trait RiscVDisassembler: Sized + Copy + Clone { Ok(Instr::Rv32(decoded)) } - _ => return Err(TooShort), + _ => Err(TooShort), } } } diff --git a/arch/riscv/src/lib.rs b/arch/riscv/src/lib.rs index fc5589ad3..8331a4343 100644 --- a/arch/riscv/src/lib.rs +++ b/arch/riscv/src/lib.rs @@ -1,3 +1,4 @@ +#![allow(clippy::unusual_byte_groupings)] // Option -> Result // rework operands/instruction text // helper func for reading/writing to registers @@ -38,13 +39,13 @@ use std::fmt; use std::hash::Hash; use std::marker::PhantomData; +use binaryninja::architecture::{BranchKind, IntrinsicId, RegisterId}; +use binaryninja::confidence::{Conf, MAX_CONFIDENCE, MIN_CONFIDENCE}; use binaryninja::logger::Logger; use riscv_dis::{ FloatReg, FloatRegType, Instr, IntRegType, Op, RegFile, Register as RiscVRegister, RiscVDisassembler, RoundMode, }; -use binaryninja::architecture::{BranchKind, IntrinsicId, RegisterId}; -use binaryninja::confidence::{Conf, MAX_CONFIDENCE, MIN_CONFIDENCE}; enum RegType { Integer(u32), @@ -133,9 +134,9 @@ impl From> for Register { } } -impl Into>> for Register { - fn into(self) -> llil::Register> { - llil::Register::ArchReg(self) +impl From> for llil::Register> { + fn from(reg: Register) -> Self { + llil::Register::ArchReg(reg) } } @@ -271,7 +272,12 @@ impl fmt::Debug for Register { } impl RiscVIntrinsic { - fn id_from_parts(id: u32, sz1: Option, sz2: Option, rm: Option) -> IntrinsicId { + fn id_from_parts( + id: u32, + sz1: Option, + sz2: Option, + rm: Option, + ) -> IntrinsicId { let sz1 = sz1.unwrap_or(0); let sz2 = sz2.unwrap_or(0); let rm = match rm { @@ -520,7 +526,7 @@ impl architecture::Intrinsic for RiscVIntrinsic { Intrinsic::Csrrd => { vec![NameAndType::new( "csr", - Conf::new(Type::int(4, false), MAX_CONFIDENCE) + Conf::new(Type::int(4, false), MAX_CONFIDENCE), )] } Intrinsic::Csrrw | Intrinsic::Csrwr | Intrinsic::Csrrs | Intrinsic::Csrrc => { @@ -528,7 +534,10 @@ impl architecture::Intrinsic for RiscVIntrinsic { NameAndType::new("csr", Conf::new(Type::int(4, false), MAX_CONFIDENCE)), NameAndType::new( "value", - Conf::new(Type::int(::Int::width(), false), MIN_CONFIDENCE) + Conf::new( + Type::int(::Int::width(), false), + MIN_CONFIDENCE, + ), ), ] } @@ -553,23 +562,26 @@ impl architecture::Intrinsic for RiscVIntrinsic { | Intrinsic::FcvtFToU(size, _, _) => { vec![NameAndType::new( "", - Conf::new(Type::float(size as usize), MAX_CONFIDENCE) + Conf::new(Type::float(size as usize), MAX_CONFIDENCE), )] } Intrinsic::FcvtIToF(size, _, _) => { vec![NameAndType::new( "", - Conf::new(Type::int(size as usize, true), MAX_CONFIDENCE) + Conf::new(Type::int(size as usize, true), MAX_CONFIDENCE), )] } Intrinsic::FcvtUToF(size, _, _) => { vec![NameAndType::new( "", - Conf::new(Type::int(size as usize, false), MAX_CONFIDENCE) + Conf::new(Type::int(size as usize, false), MAX_CONFIDENCE), )] } Intrinsic::Fence => { - vec![NameAndType::new("", Conf::new(Type::int(4, false), MIN_CONFIDENCE))] + vec![NameAndType::new( + "", + Conf::new(Type::int(4, false), MIN_CONFIDENCE), + )] } } } @@ -713,9 +725,7 @@ impl architecture::Architecture fo | Op::BltU(ref b) | Op::BgeU(ref b) => { res.add_branch(BranchKind::False(addr.wrapping_add(inst_len as u64))); - res.add_branch( - BranchKind::True(addr.wrapping_add(b.imm() as i64 as u64)), - ); + res.add_branch(BranchKind::True(addr.wrapping_add(b.imm() as i64 as u64))); } Op::Ecall => { res.add_branch(BranchKind::SystemCall); @@ -997,7 +1007,7 @@ impl architecture::Architecture fo let target = addr.wrapping_add(i as i64 as u64); res.push(InstructionTextToken::new( - &format!("0x{:x}", target), + format!("0x{:x}", target), CodeRelativeAddress { value: target, size: Some(self.address_size()), @@ -1210,7 +1220,7 @@ impl architecture::Architecture fo let jump_expr = il.goto(&mut l); il.update_label_for_address(target, l); jump_expr - }, + } (0, None) => il.jump(il.const_ptr(target)), (_, _) => il.call(il.const_ptr(target)), } @@ -1284,7 +1294,7 @@ impl architecture::Architecture fo }); il.if_expr(cond_expr, &mut t, &mut f).append(); - + if new_true { il.mark_label(&mut t); il.jump(il.const_ptr(tt)).append(); diff --git a/plugins/dwarf/dwarf_export/Cargo.toml b/plugins/dwarf/dwarf_export/Cargo.toml index 7507977ea..29dee106c 100644 --- a/plugins/dwarf/dwarf_export/Cargo.toml +++ b/plugins/dwarf/dwarf_export/Cargo.toml @@ -8,6 +8,7 @@ crate-type = ["cdylib"] [dependencies] binaryninja = { path = "../../../rust" } +binaryninjacore-sys = { path = "../../../rust/binaryninjacore-sys" } gimli = "^0.31" log = "^0.4" object = { version = "0.32.1", features = ["write"] } diff --git a/plugins/dwarf/dwarf_export/build.rs b/plugins/dwarf/dwarf_export/build.rs new file mode 100644 index 000000000..ed6cec7d2 --- /dev/null +++ b/plugins/dwarf/dwarf_export/build.rs @@ -0,0 +1,15 @@ +fn main() { + let link_path = std::env::var_os("DEP_BINARYNINJACORE_PATH") + .expect("DEP_BINARYNINJACORE_PATH not specified"); + + println!("cargo::rustc-link-lib=dylib=binaryninjacore"); + println!("cargo::rustc-link-search={}", link_path.to_str().unwrap()); + + #[cfg(not(target_os = "windows"))] + { + println!( + "cargo::rustc-link-arg=-Wl,-rpath,{0},-L{0}", + link_path.to_string_lossy() + ); + } +} diff --git a/plugins/dwarf/dwarf_export/src/lib.rs b/plugins/dwarf/dwarf_export/src/lib.rs index 4bc0ab94b..a5d67321d 100644 --- a/plugins/dwarf/dwarf_export/src/lib.rs +++ b/plugins/dwarf/dwarf_export/src/lib.rs @@ -10,19 +10,19 @@ use gimli::{ use object::{write, Architecture, BinaryFormat, SectionKind}; use std::fs; +use binaryninja::logger::Logger; use binaryninja::{ binaryview::{BinaryView, BinaryViewBase, BinaryViewExt}, command::{register, Command}, + confidence::Conf, interaction, interaction::{FormResponses, FormResponses::Index}, rc::Ref, string::BnString, symbol::SymbolType, - confidence::Conf, types::{MemberAccess, StructureType, Type, TypeClass}, }; use log::{error, info, LevelFilter}; -use binaryninja::logger::Logger; fn export_type( name: String, @@ -377,13 +377,7 @@ fn export_types( defined_types: &mut Vec<(Ref, UnitEntryId)>, ) { for t in &bv.types() { - export_type( - t.name.to_string(), - &t.ty, - bv, - defined_types, - dwarf, - ); + export_type(t.name.to_string(), &t.ty, bv, defined_types, dwarf); } } @@ -783,7 +777,9 @@ impl Command for MyCommand { #[no_mangle] pub extern "C" fn CorePluginInit() -> bool { - Logger::new("DWARF Export").with_level(LevelFilter::Debug).init(); + Logger::new("DWARF Export") + .with_level(LevelFilter::Debug) + .init(); register( "Export as DWARF", diff --git a/plugins/dwarf/dwarf_import/Cargo.toml b/plugins/dwarf/dwarf_import/Cargo.toml index 61e0016c4..58a36e5fc 100644 --- a/plugins/dwarf/dwarf_import/Cargo.toml +++ b/plugins/dwarf/dwarf_import/Cargo.toml @@ -9,6 +9,7 @@ crate-type = ["cdylib"] [dependencies] dwarfreader = { path = "../shared/" } binaryninja = { path = "../../../rust" } +binaryninjacore-sys = { path = "../../../rust/binaryninjacore-sys" } gimli = "0.31" log = "0.4.20" iset = "0.2.2" diff --git a/plugins/dwarf/dwarf_import/build.rs b/plugins/dwarf/dwarf_import/build.rs new file mode 100644 index 000000000..ed6cec7d2 --- /dev/null +++ b/plugins/dwarf/dwarf_import/build.rs @@ -0,0 +1,15 @@ +fn main() { + let link_path = std::env::var_os("DEP_BINARYNINJACORE_PATH") + .expect("DEP_BINARYNINJACORE_PATH not specified"); + + println!("cargo::rustc-link-lib=dylib=binaryninjacore"); + println!("cargo::rustc-link-search={}", link_path.to_str().unwrap()); + + #[cfg(not(target_os = "windows"))] + { + println!( + "cargo::rustc-link-arg=-Wl,-rpath,{0},-L{0}", + link_path.to_string_lossy() + ); + } +} diff --git a/plugins/dwarf/dwarf_import/src/die_handlers.rs b/plugins/dwarf/dwarf_import/src/die_handlers.rs index f7bcc22e6..a2db2fb3c 100644 --- a/plugins/dwarf/dwarf_import/src/die_handlers.rs +++ b/plugins/dwarf/dwarf_import/src/die_handlers.rs @@ -13,8 +13,8 @@ // limitations under the License. use crate::dwarfdebuginfo::{DebugInfoBuilder, DebugInfoBuilderContext, TypeUID}; -use crate::{helpers::*, ReaderType}; use crate::types::get_type; +use crate::{helpers::*, ReaderType}; use binaryninja::{ rc::*, @@ -125,10 +125,9 @@ pub(crate) fn handle_enum( } } - let width = match get_size_as_usize(entry).unwrap_or(8) - { + let width = match get_size_as_usize(entry).unwrap_or(8) { 0 => debug_info_builder_context.default_address_size(), - x => x + x => x, }; Some(Type::enumeration( @@ -179,7 +178,10 @@ pub(crate) fn handle_pointer( if let Some(pointer_size) = get_size_as_usize(entry) { if let Some(entry_type_offset) = entry_type { - let parent_type = debug_info_builder.get_type(entry_type_offset).unwrap().get_type(); + let parent_type = debug_info_builder + .get_type(entry_type_offset) + .unwrap() + .get_type(); Some(Type::pointer_of_width( parent_type.as_ref(), pointer_size, @@ -197,7 +199,10 @@ pub(crate) fn handle_pointer( )) } } else if let Some(entry_type_offset) = entry_type { - let parent_type = debug_info_builder.get_type(entry_type_offset).unwrap().get_type(); + let parent_type = debug_info_builder + .get_type(entry_type_offset) + .unwrap() + .get_type(); Some(Type::pointer_of_width( parent_type.as_ref(), debug_info_builder_context.default_address_size(), @@ -235,7 +240,10 @@ pub(crate) fn handle_array( // For multidimensional arrays, DW_TAG_subrange_type or DW_TAG_enumeration_type if let Some(entry_type_offset) = entry_type { - let parent_type = debug_info_builder.get_type(entry_type_offset).unwrap().get_type(); + let parent_type = debug_info_builder + .get_type(entry_type_offset) + .unwrap() + .get_type(); let mut tree = unit.entries_tree(Some(entry.offset())).unwrap(); let mut children = tree.root().unwrap().children(); @@ -293,27 +301,18 @@ pub(crate) fn handle_function( // or is otherwise DW_TAG_unspecified_parameters let return_type = match entry_type { - Some(entry_type_offset) => { - debug_info_builder - .get_type(entry_type_offset) - .expect("Subroutine return type was not processed") - .get_type() - } + Some(entry_type_offset) => debug_info_builder + .get_type(entry_type_offset) + .expect("Subroutine return type was not processed") + .get_type(), None => Type::void(), }; // Alias function type in the case that it contains itself if let Some(name) = debug_info_builder_context.get_name(dwarf, unit, entry) { - let ntr = Type::named_type_from_type( - &name, - &Type::function(return_type.as_ref(), vec![], false), - ); - debug_info_builder.add_type( - get_uid(dwarf, unit, entry), - name, - ntr, - false, - ); + let ntr = + Type::named_type_from_type(&name, &Type::function(return_type.as_ref(), vec![], false)); + debug_info_builder.add_type(get_uid(dwarf, unit, entry), name, ntr, false); } let mut parameters: Vec = vec![]; @@ -344,7 +343,10 @@ pub(crate) fn handle_function( } } - if debug_info_builder_context.get_name(dwarf, unit, entry).is_some() { + if debug_info_builder_context + .get_name(dwarf, unit, entry) + .is_some() + { debug_info_builder.remove_type(get_uid(dwarf, unit, entry)); } @@ -368,7 +370,10 @@ pub(crate) fn handle_const( // ?DW_AT_type if let Some(entry_type_offset) = entry_type { - let parent_type = debug_info_builder.get_type(entry_type_offset).unwrap().get_type(); + let parent_type = debug_info_builder + .get_type(entry_type_offset) + .unwrap() + .get_type(); Some((*parent_type).to_builder().set_const(true).finalize()) } else { Some(TypeBuilder::void().set_const(true).finalize()) @@ -388,7 +393,10 @@ pub(crate) fn handle_volatile( // ?DW_AT_type if let Some(entry_type_offset) = entry_type { - let parent_type = debug_info_builder.get_type(entry_type_offset).unwrap().get_type(); + let parent_type = debug_info_builder + .get_type(entry_type_offset) + .unwrap() + .get_type(); Some((*parent_type).to_builder().set_volatile(true).finalize()) } else { Some(TypeBuilder::void().set_volatile(true).finalize()) diff --git a/plugins/dwarf/dwarf_import/src/dwarfdebuginfo.rs b/plugins/dwarf/dwarf_import/src/dwarfdebuginfo.rs index 1d9a7c7e0..f85d1724b 100644 --- a/plugins/dwarf/dwarf_import/src/dwarfdebuginfo.rs +++ b/plugins/dwarf/dwarf_import/src/dwarfdebuginfo.rs @@ -12,7 +12,10 @@ // See the License for the specific language governing permissions and // limitations under the License. -use crate::{helpers::{get_uid, resolve_specification, DieReference}, ReaderType}; +use crate::{ + helpers::{get_uid, resolve_specification, DieReference}, + ReaderType, +}; use binaryninja::{ binaryview::{BinaryView, BinaryViewBase, BinaryViewExt}, @@ -21,21 +24,17 @@ use binaryninja::{ rc::*, symbol::SymbolType, templatesimplifier::simplify_str_to_fqn, - variable::NamedVariableWithType, types::{FunctionParameter, Type}, + variable::NamedVariableWithType, }; use gimli::{DebuggingInformationEntry, Dwarf, Unit}; -use indexmap::{map::Values, IndexMap}; -use log::{debug, error, warn}; -use std::{ - cmp::Ordering, - collections::HashMap, - hash::Hash, -}; use binaryninja::confidence::Conf; use binaryninja::variable::{Variable, VariableSourceType}; +use indexmap::{map::Values, IndexMap}; +use log::{debug, error, warn}; +use std::{cmp::Ordering, collections::HashMap, hash::Hash}; pub(crate) type TypeUID = usize; @@ -81,7 +80,7 @@ impl FunctionInfoBuilder { self.address = address; } - for (i, new_parameter) in parameters.into_iter().enumerate() { + for (i, new_parameter) in parameters.iter().enumerate() { match self.parameters.get(i) { Some(None) => self.parameters[i] = new_parameter.clone(), Some(Some(_)) => (), @@ -122,7 +121,6 @@ pub(crate) struct DebugInfoBuilderContext { impl DebugInfoBuilderContext { pub(crate) fn new(view: &BinaryView, dwarf: &Dwarf) -> Option { - let mut units = vec![]; let mut iter = dwarf.units(); while let Ok(Some(header)) = iter.next() { @@ -203,7 +201,7 @@ pub(crate) struct DebugInfoBuilder { full_function_name_indices: HashMap, types: IndexMap, data_variables: HashMap, TypeUID)>, - range_data_offsets: iset::IntervalMap + range_data_offsets: iset::IntervalMap, } impl DebugInfoBuilder { @@ -238,10 +236,10 @@ impl DebugInfoBuilder { // TODO : Consider further falling back on address/architecture /* - If it has a raw_name and we know it, update it and return - Else if it has a full_name and we know it, update it and return - Else Add a new entry if we don't know the full_name or raw_name - */ + If it has a raw_name and we know it, update it and return + Else if it has a full_name and we know it, update it and return + Else Add a new entry if we don't know the full_name or raw_name + */ if let Some(ident) = &raw_name { // check if we already know about this raw name's index @@ -252,19 +250,20 @@ impl DebugInfoBuilder { let function = self.functions.get_mut(*idx).unwrap(); if function.full_name.is_some() && function.full_name != full_name { - self.full_function_name_indices.remove(function.full_name.as_ref().unwrap()); + self.full_function_name_indices + .remove(function.full_name.as_ref().unwrap()); } function.update(full_name, raw_name, return_type, address, parameters); - if function.full_name.is_some() { - self.full_function_name_indices.insert(function.full_name.clone().unwrap(), *idx); + if function.full_name.is_some() { + self.full_function_name_indices + .insert(function.full_name.clone().unwrap(), *idx); } return Some(*idx); } - } - else if let Some(ident) = &full_name { + } else if let Some(ident) = &full_name { // check if we already know about this full name's index // if we do, and the raw name will change, remove the known raw index if it exists // update the function @@ -273,19 +272,20 @@ impl DebugInfoBuilder { let function = self.functions.get_mut(*idx).unwrap(); if function.raw_name.is_some() && function.raw_name != raw_name { - self.raw_function_name_indices.remove(function.raw_name.as_ref().unwrap()); + self.raw_function_name_indices + .remove(function.raw_name.as_ref().unwrap()); } function.update(full_name, raw_name, return_type, address, parameters); - if function.raw_name.is_some() { - self.raw_function_name_indices.insert(function.raw_name.clone().unwrap(), *idx); + if function.raw_name.is_some() { + self.raw_function_name_indices + .insert(function.raw_name.clone().unwrap(), *idx); } return Some(*idx); } - } - else { + } else { debug!("Function entry in DWARF without full or raw name."); return None; } @@ -303,15 +303,17 @@ impl DebugInfoBuilder { }; if let Some(n) = &function.full_name { - self.full_function_name_indices.insert(n.clone(), self.functions.len()); + self.full_function_name_indices + .insert(n.clone(), self.functions.len()); } if let Some(n) = &function.raw_name { - self.raw_function_name_indices.insert(n.clone(), self.functions.len()); + self.raw_function_name_indices + .insert(n.clone(), self.functions.len()); } self.functions.push(function); - Some(self.functions.len()-1) + Some(self.functions.len() - 1) } pub(crate) fn functions(&self) -> &[FunctionInfoBuilder] { @@ -359,7 +361,6 @@ impl DebugInfoBuilder { self.types.contains_key(&type_uid) } - pub(crate) fn add_stack_variable( &mut self, fn_idx: Option, @@ -373,11 +374,10 @@ impl DebugInfoBuilder { if x.len() == 1 && x.chars().next() == Some('\x00') { // Anonymous variable, generate name format!("debug_var_{}", offset) - } - else { + } else { x } - }, + } None => { // Anonymous variable, generate name format!("debug_var_{}", offset) @@ -386,14 +386,16 @@ impl DebugInfoBuilder { let Some(function_index) = fn_idx else { // If we somehow lost track of what subprogram we're in or we're not actually in a subprogram - error!("Trying to add a local variable outside of a subprogram. Please report this issue."); + error!( + "Trying to add a local variable outside of a subprogram. Please report this issue." + ); return; }; // Either get the known type or use a 0 confidence void type so we at least get the name applied let ty = match type_uid { Some(uid) => Conf::new(self.get_type(uid).unwrap().ty.clone(), 128), - None => Conf::new(Type::void(), 0) + None => Conf::new(Type::void(), 0), }; let function = &mut self.functions[function_index]; @@ -405,20 +407,22 @@ impl DebugInfoBuilder { return; }; - let adjusted_offset; - - let Some(adjustment_at_variable_lifetime_start) = lexical_block.and_then(|block_ranges| { - block_ranges - .unsorted_iter() - .find_map(|x| self.range_data_offsets.values_overlap(x.start).next()) - }).or_else(|| { - // Try using the offset at the adjustment 4 bytes after the function start, in case the function starts with a stack adjustment - // TODO: This is a decent heuristic but not perfect, since further adjustments could still be made - self.range_data_offsets.values_overlap(func_addr+4).next() - }).or_else(|| { - // If all else fails, use the function start address - self.range_data_offsets.values_overlap(func_addr).next() - }) else { + let Some(adjustment_at_variable_lifetime_start) = lexical_block + .and_then(|block_ranges| { + block_ranges + .unsorted_iter() + .find_map(|x| self.range_data_offsets.values_overlap(x.start).next()) + }) + .or_else(|| { + // Try using the offset at the adjustment 4 bytes after the function start, in case the function starts with a stack adjustment + // TODO: This is a decent heuristic but not perfect, since further adjustments could still be made + self.range_data_offsets.values_overlap(func_addr + 4).next() + }) + .or_else(|| { + // If all else fails, use the function start address + self.range_data_offsets.values_overlap(func_addr).next() + }) + else { // Unknown why, but this is happening with MachO + external dSYM debug!("Refusing to add a local variable ({}@{}) to function at {} without a known CIE offset.", name, offset, func_addr); return; @@ -426,20 +430,21 @@ impl DebugInfoBuilder { // TODO: handle non-sp frame bases // TODO: if not in a lexical block these can be wrong, see https://github.com/Vector35/binaryninja-api/issues/5882#issuecomment-2406065057 - if function.use_cfa { + let adjusted_offset = if function.use_cfa { // Apply CFA offset to variable storage offset if DW_AT_frame_base is frame base is CFA - adjusted_offset = offset + adjustment_at_variable_lifetime_start; - } - else { + offset + adjustment_at_variable_lifetime_start + } else { // If it's using SP, we know the SP offset is + ( - ) - let Some(adjustment_at_entry) = self.range_data_offsets.values_overlap(func_addr).next() else { + let Some(adjustment_at_entry) = + self.range_data_offsets.values_overlap(func_addr).next() + else { // Unknown why, but this is happening with MachO + external dSYM debug!("Refusing to add a local variable ({}@{}) to function at {} without a known CIE offset for function start.", name, offset, func_addr); return; }; - adjusted_offset = offset + (adjustment_at_entry - adjustment_at_variable_lifetime_start); - } + offset + (adjustment_at_entry - adjustment_at_variable_lifetime_start) + }; if adjusted_offset > 0 { // If we somehow end up with a positive sp offset @@ -447,9 +452,14 @@ impl DebugInfoBuilder { return; } - let var = Variable::new(VariableSourceType::StackVariableSourceType, 0, adjusted_offset); - function.stack_variables.push(NamedVariableWithType::new(var, ty, name, false)); - + let var = Variable::new( + VariableSourceType::StackVariableSourceType, + 0, + adjusted_offset, + ); + function + .stack_variables + .push(NamedVariableWithType::new(var, ty, name, false)); } pub(crate) fn add_data_variable( @@ -512,8 +522,7 @@ impl DebugInfoBuilder { debug_type_name = format!("{}_{}", debug_type.name, i); i += 1; - } - else { + } else { // We found a unique name break; } @@ -545,7 +554,9 @@ impl DebugInfoBuilder { fn get_function_type(&self, function: &FunctionInfoBuilder) -> Ref { let return_type = match function.return_type { - Some(return_type_id) => Conf::new(self.get_type(return_type_id).unwrap().ty.clone(), 128), + Some(return_type_id) => { + Conf::new(self.get_type(return_type_id).unwrap().ty.clone(), 128) + } _ => Conf::new(Type::void(), 0), }; @@ -577,7 +588,7 @@ impl DebugInfoBuilder { Some(self.get_function_type(function)), function.address, function.platform.clone(), - vec![], // TODO : Components + vec![], // TODO : Components function.stack_variables.clone(), // TODO: local non-stack variables )); } @@ -607,7 +618,9 @@ impl DebugInfoBuilder { // If our name has fewer namespaces than the existing name, assume we lost the namespace info if simplify_str_to_fqn(func_full_name, true).items.len() - < simplify_str_to_fqn(symbol_full_name.clone(), true).items.len() + < simplify_str_to_fqn(symbol_full_name.clone(), true) + .items + .len() { func.full_name = Some(symbol_full_name.to_string()); } @@ -618,13 +631,15 @@ impl DebugInfoBuilder { if let Some(address) = func.address.as_mut() { let (diff, overflowed) = bv.start().overflowing_sub(bv.original_image_base()); if !overflowed { - *address = (*address).overflowing_add(diff).0; // rebase the address + *address = (*address).overflowing_add(diff).0; // rebase the address let existing_functions = bv.functions_at(*address); match existing_functions.len().cmp(&1) { Ordering::Greater => { warn!("Multiple existing functions at address {address:08x}. One or more functions at this address may have the wrong platform information. Please report this binary."); } - Ordering::Equal => func.platform = Some(existing_functions.get(0).platform()), + Ordering::Equal => { + func.platform = Some(existing_functions.get(0).platform()) + } Ordering::Less => {} } } diff --git a/plugins/dwarf/dwarf_import/src/functions.rs b/plugins/dwarf/dwarf_import/src/functions.rs index 43b69ca36..800d281e8 100644 --- a/plugins/dwarf/dwarf_import/src/functions.rs +++ b/plugins/dwarf/dwarf_import/src/functions.rs @@ -15,8 +15,8 @@ use std::sync::OnceLock; use crate::dwarfdebuginfo::{DebugInfoBuilder, DebugInfoBuilderContext, TypeUID}; -use crate::{helpers::*, ReaderType}; use crate::types::get_type; +use crate::{helpers::*, ReaderType}; use binaryninja::templatesimplifier::simplify_str_to_str; use cpp_demangle::DemangleOptions; @@ -82,9 +82,21 @@ pub(crate) fn parse_function_entry( ) -> Option { // Collect function properties (if they exist in this DIE) let raw_name = get_raw_name(dwarf, unit, entry); - let return_type = get_type(dwarf, unit, entry, debug_info_builder_context, debug_info_builder); + let return_type = get_type( + dwarf, + unit, + entry, + debug_info_builder_context, + debug_info_builder, + ); let address = get_start_address(dwarf, unit, entry); - let (parameters, variable_arguments) = get_parameters(dwarf, unit, entry, debug_info_builder_context, debug_info_builder); + let (parameters, variable_arguments) = get_parameters( + dwarf, + unit, + entry, + debug_info_builder_context, + debug_info_builder, + ); // If we have a raw name, it might be mangled, see if we can demangle it into full_name // raw_name should contain a superset of the info we have in full_name @@ -100,9 +112,7 @@ pub(crate) fn parse_function_entry( }); static ABI_REGEX_MEM: OnceLock = OnceLock::new(); - let abi_regex = ABI_REGEX_MEM.get_or_init(|| { - Regex::new(r"\[abi:v\d+\]").unwrap() - }); + let abi_regex = ABI_REGEX_MEM.get_or_init(|| Regex::new(r"\[abi:v\d+\]").unwrap()); if let Ok(sym) = cpp_demangle::Symbol::new(possibly_mangled_name) { if let Ok(demangled) = sym.demangle(demangle_options) { let cleaned = abi_regex.replace_all(&demangled, ""); @@ -127,21 +137,29 @@ pub(crate) fn parse_function_entry( } let use_cfa; - if let Ok(Some(AttributeValue::Exprloc(mut expression))) = entry.attr_value(constants::DW_AT_frame_base) { + if let Ok(Some(AttributeValue::Exprloc(mut expression))) = + entry.attr_value(constants::DW_AT_frame_base) + { use_cfa = match Operation::parse(&mut expression.0, unit.encoding()) { Ok(Operation::Register { register: _ }) => false, // TODO: handle register-relative encodings later Ok(Operation::CallFrameCFA) => true, - _ => false + _ => false, }; - } - else { + } else { use_cfa = false; } - debug_info_builder.insert_function(full_name, raw_name, return_type, address, ¶meters, variable_arguments, use_cfa) + debug_info_builder.insert_function( + full_name, + raw_name, + return_type, + address, + ¶meters, + variable_arguments, + use_cfa, + ) } - pub(crate) fn parse_lexical_block( dwarf: &Dwarf, unit: &Unit, @@ -151,24 +169,24 @@ pub(crate) fn parse_lexical_block( // Must have either DW_AT_ranges or DW_AT_low_pc and DW_AT_high_pc let mut result = iset::IntervalSet::new(); if let Ok(Some(attr_value)) = entry.attr_value(constants::DW_AT_ranges) { - if let Ok(Some(ranges_offset)) = dwarf.attr_ranges_offset(unit, attr_value) - { - if let Ok(mut ranges) = dwarf.ranges(unit, ranges_offset) - { + if let Ok(Some(ranges_offset)) = dwarf.attr_ranges_offset(unit, attr_value) { + if let Ok(mut ranges) = dwarf.ranges(unit, ranges_offset) { while let Ok(Some(range)) = ranges.next() { // Ranges where start == end may be ignored (DWARFv5 spec, 2.17.3 line 17) if range.begin == range.end { - continue + continue; } result.insert(range.begin..range.end); } } } - } - else if let Ok(Some(low_pc_value)) = entry.attr_value(constants::DW_AT_low_pc) { + } else if let Ok(Some(low_pc_value)) = entry.attr_value(constants::DW_AT_low_pc) { let Ok(Some(low_pc)) = dwarf.attr_address(unit, low_pc_value.clone()) else { let unit_base: usize = unit.header.offset().as_debug_info_offset().unwrap().0; - error!("Failed to read lexical block low_pc for entry {:#x}, please report this bug.", unit_base + entry.offset().0); + error!( + "Failed to read lexical block low_pc for entry {:#x}, please report this bug.", + unit_base + entry.offset().0 + ); return None; }; @@ -184,18 +202,22 @@ pub(crate) fn parse_lexical_block( .or_else(|| dwarf.attr_address(unit, high_pc_value).unwrap_or(None)) else { let unit_base: usize = unit.header.offset().as_debug_info_offset().unwrap().0; - error!("Failed to read lexical block high_pc for entry {:#x}, please report this bug.", unit_base + entry.offset().0); + error!( + "Failed to read lexical block high_pc for entry {:#x}, please report this bug.", + unit_base + entry.offset().0 + ); return None; }; if low_pc < high_pc { result.insert(low_pc..high_pc); + } else { + error!( + "Invalid lexical block range: {:#x} -> {:#x}", + low_pc, high_pc + ); } - else { - error!("Invalid lexical block range: {:#x} -> {:#x}", low_pc, high_pc); - } - } - else { + } else { // If neither case is hit the lexical block doesn't define any ranges and we should ignore it return None; } diff --git a/plugins/dwarf/dwarf_import/src/helpers.rs b/plugins/dwarf/dwarf_import/src/helpers.rs index d8c2a4c7c..54b5f4b26 100644 --- a/plugins/dwarf/dwarf_import/src/helpers.rs +++ b/plugins/dwarf/dwarf_import/src/helpers.rs @@ -14,18 +14,18 @@ use std::ffi::OsStr; use std::path::{Path, PathBuf}; -use std::{ - collections::HashMap, - ops::Deref, - sync::mpsc, - str::FromStr -}; +use std::{collections::HashMap, ops::Deref, str::FromStr, sync::mpsc}; use crate::{DebugInfoBuilderContext, ReaderType}; use binaryninja::binaryview::BinaryViewBase; use binaryninja::filemetadata::FileMetadata; use binaryninja::Endianness; -use binaryninja::{binaryview::{BinaryView, BinaryViewExt}, downloadprovider::{DownloadInstanceInputOutputCallbacks, DownloadProvider}, rc::Ref, settings::Settings}; +use binaryninja::{ + binaryview::{BinaryView, BinaryViewExt}, + downloadprovider::{DownloadInstanceInputOutputCallbacks, DownloadProvider}, + rc::Ref, + settings::Settings, +}; use gimli::Dwarf; use gimli::{ constants, Attribute, AttributeValue, @@ -70,40 +70,55 @@ pub(crate) fn get_attr_die<'a, R: ReaderType>( if dwarf.sup().is_some() { for source_unit in debug_info_builder_context.units() { if let Some(new_offset) = offset.to_unit_offset(&source_unit.header) { - return Some(DieReference::UnitAndOffset((dwarf, source_unit, new_offset))); + return Some(DieReference::UnitAndOffset(( + dwarf, + source_unit, + new_offset, + ))); } } - } - else { + } else { // This could either have no supplementary file because it is one or because it just doesn't have one // operate on supplementary file if dwarf is a supplementary file, else self // It's possible this is a reference in the supplementary file to itself for source_unit in debug_info_builder_context.sup_units() { if let Some(new_offset) = offset.to_unit_offset(&source_unit.header) { - return Some(DieReference::UnitAndOffset((dwarf, source_unit, new_offset))); + return Some(DieReference::UnitAndOffset(( + dwarf, + source_unit, + new_offset, + ))); } } // ... or it just doesn't have a supplementary file for source_unit in debug_info_builder_context.units() { if let Some(new_offset) = offset.to_unit_offset(&source_unit.header) { - return Some(DieReference::UnitAndOffset((dwarf, source_unit, new_offset))); + return Some(DieReference::UnitAndOffset(( + dwarf, + source_unit, + new_offset, + ))); } } } None - }, + } Ok(Some(DebugInfoRefSup(offset))) => { for source_unit in debug_info_builder_context.sup_units() { if let Some(new_offset) = offset.to_unit_offset(&source_unit.header) { - return Some(DieReference::UnitAndOffset((dwarf.sup().unwrap(), source_unit, new_offset))); + return Some(DieReference::UnitAndOffset(( + dwarf.sup().unwrap(), + source_unit, + new_offset, + ))); } } warn!("Failed to fetch DIE. Supplementary debug information may be incomplete."); None - }, + } _ => None, } } @@ -141,7 +156,9 @@ pub(crate) fn resolve_specification<'a, R: ReaderType>( ) { match die_reference { DieReference::UnitAndOffset((dwarf, entry_unit, entry_offset)) => { - if entry_offset == entry.offset() && unit.header.offset() == entry_unit.header.offset() { + if entry_offset == entry.offset() + && unit.header.offset() == entry_unit.header.offset() + { warn!("DWARF information is invalid (infinite abstract origin reference cycle). Debug information may be incomplete."); DieReference::Err } else if let Ok(new_entry) = entry_unit.entry(entry_offset) { @@ -172,15 +189,12 @@ pub(crate) fn get_name( .unwrap() .attr_value(constants::DW_AT_name) { - if let Ok(attr_string) = dwarf.attr_string(entry_unit, attr_val.clone()) - { + if let Ok(attr_string) = dwarf.attr_string(entry_unit, attr_val.clone()) { if let Ok(attr_string) = attr_string.to_string() { return Some(attr_string.to_string()); } - } - else if let Some(dwarf) = &dwarf.sup { - if let Ok(attr_string) = dwarf.attr_string(entry_unit, attr_val) - { + } else if let Some(dwarf) = &dwarf.sup { + if let Ok(attr_string) = dwarf.attr_string(entry_unit, attr_val) { if let Ok(attr_string) = attr_string.to_string() { return Some(attr_string.to_string()); } @@ -208,15 +222,12 @@ pub(crate) fn get_raw_name( entry: &DebuggingInformationEntry, ) -> Option { if let Ok(Some(attr_val)) = entry.attr_value(constants::DW_AT_linkage_name) { - if let Ok(attr_string) = dwarf.attr_string(unit, attr_val.clone()) - { + if let Ok(attr_string) = dwarf.attr_string(unit, attr_val.clone()) { if let Ok(attr_string) = attr_string.to_string() { return Some(attr_string.to_string()); } - } - else if let Some(dwarf) = dwarf.sup() { - if let Ok(attr_string) = dwarf.attr_string(unit, attr_val) - { + } else if let Some(dwarf) = dwarf.sup() { + if let Ok(attr_string) = dwarf.attr_string(unit, attr_val) { if let Ok(attr_string) = attr_string.to_string() { return Some(attr_string.to_string()); } @@ -240,9 +251,7 @@ pub(crate) fn get_size_as_usize( } // Get the size of an object as a u64 -pub(crate) fn get_size_as_u64( - entry: &DebuggingInformationEntry, -) -> Option { +pub(crate) fn get_size_as_u64(entry: &DebuggingInformationEntry) -> Option { if let Ok(Some(attr)) = entry.attr(constants::DW_AT_byte_size) { get_attr_as_u64(&attr) } else if let Ok(Some(attr)) = entry.attr(constants::DW_AT_bit_size) { @@ -253,9 +262,7 @@ pub(crate) fn get_size_as_u64( } // Get the size of a subrange as a u64 -pub(crate) fn get_subrange_size( - entry: &DebuggingInformationEntry, -) -> u64 { +pub(crate) fn get_subrange_size(entry: &DebuggingInformationEntry) -> u64 { if let Ok(Some(attr)) = entry.attr(constants::DW_AT_upper_bound) { get_attr_as_u64(&attr).map_or(0, |v| v + 1) } else if let Ok(Some(attr)) = entry.attr(constants::DW_AT_count) { @@ -274,22 +281,18 @@ pub(crate) fn get_start_address( entry: &DebuggingInformationEntry, ) -> Option { if let Ok(Some(attr_val)) = entry.attr_value(constants::DW_AT_low_pc) { - match dwarf.attr_address(unit, attr_val) - { + match dwarf.attr_address(unit, attr_val) { Ok(Some(val)) => Some(val), _ => None, } } else if let Ok(Some(attr_val)) = entry.attr_value(constants::DW_AT_entry_pc) { - match dwarf.attr_address(unit, attr_val) - { + match dwarf.attr_address(unit, attr_val) { Ok(Some(val)) => Some(val), _ => None, } } else if let Ok(Some(attr_value)) = entry.attr_value(constants::DW_AT_ranges) { - if let Ok(Some(ranges_offset)) = dwarf.attr_ranges_offset(unit, attr_value) - { - if let Ok(mut ranges) = dwarf.ranges(unit, ranges_offset) - { + if let Ok(Some(ranges_offset)) = dwarf.attr_ranges_offset(unit, attr_value) { + if let Ok(mut ranges) = dwarf.ranges(unit, ranges_offset) { if let Ok(Some(range)) = ranges.next() { return Some(range.begin); } @@ -313,7 +316,7 @@ pub(crate) fn get_attr_as_u64(attr: &Attribute) -> Option 2 => data.read_u16().map(u64::from).ok(), 4 => data.read_u32().map(u64::from).ok(), 8 => data.read_u64().ok(), - _ => None + _ => None, } } else { None @@ -335,24 +338,20 @@ pub(crate) fn get_attr_as_usize(attr: Attribute) -> Option( - unit: &Unit, - attr: Attribute, -) -> Option { +pub(crate) fn get_expr_value(unit: &Unit, attr: Attribute) -> Option { if let AttributeValue::Exprloc(mut expression) = attr.value() { match Operation::parse(&mut expression.0, unit.encoding()) { Ok(Operation::PlusConstant { value }) => Some(value), Ok(Operation::UnsignedConstant { value }) => Some(value), Ok(Operation::Address { address: 0 }) => None, Ok(Operation::Address { address }) => Some(address), - _ => None + _ => None, } } else { None } } - pub(crate) fn get_build_id(view: &BinaryView) -> Result { let mut build_id: Option = None; @@ -363,7 +362,8 @@ pub(crate) fn get_build_id(view: &BinaryView) -> Result { // Type - 4 bytes // Name - n bytes // Desc - n bytes - let build_id_bytes = raw_view.read_vec(build_id_section.start(), build_id_section.len()); + let build_id_bytes = + raw_view.read_vec(build_id_section.start(), build_id_section.len()); if build_id_bytes.len() < 12 { return Err("Build id section must be at least 12 bytes".to_string()); } @@ -376,7 +376,7 @@ pub(crate) fn get_build_id(view: &BinaryView) -> Result { name_len = u32::from_le_bytes(build_id_bytes[0..4].try_into().unwrap()); desc_len = u32::from_le_bytes(build_id_bytes[4..8].try_into().unwrap()); note_type = u32::from_le_bytes(build_id_bytes[8..12].try_into().unwrap()); - }, + } Endianness::BigEndian => { name_len = u32::from_be_bytes(build_id_bytes[0..4].try_into().unwrap()); desc_len = u32::from_be_bytes(build_id_bytes[4..8].try_into().unwrap()); @@ -391,24 +391,29 @@ pub(crate) fn get_build_id(view: &BinaryView) -> Result { let expected_len = (12 + name_len + desc_len) as usize; if build_id_bytes.len() < expected_len { - return Err(format!("Build id section not expected length: expected {}, got {}", expected_len, build_id_bytes.len())); + return Err(format!( + "Build id section not expected length: expected {}, got {}", + expected_len, + build_id_bytes.len() + )); } - let desc: &[u8] = &build_id_bytes[(12+name_len as usize)..expected_len]; + let desc: &[u8] = &build_id_bytes[(12 + name_len as usize)..expected_len]; build_id = Some(desc.iter().map(|b| format!("{:02x}", b)).collect()); } } if let Some(x) = build_id { Ok(x) - } - else { + } else { Err("Failed to get build id".to_string()) } } - -pub(crate) fn download_debug_info(build_id: &str, view: &BinaryView) -> Result, String> { +pub(crate) fn download_debug_info( + build_id: &str, + view: &BinaryView, +) -> Result, String> { let settings = Settings::new(""); let debug_server_urls = settings.get_string_list("network.debuginfodServers", Some(view), None); @@ -419,7 +424,7 @@ pub(crate) fn download_debug_info(build_id: &str, view: &BinaryView) -> Result usize { - if let Ok(_) = tx.send(Vec::from(data)) { + if tx.send(Vec::from(data)).is_ok() { data.len() } else { 0 @@ -475,30 +480,34 @@ pub(crate) fn download_debug_info(build_id: &str, view: &BinaryView) -> Result Option { +pub(crate) fn find_local_debug_file_for_build_id( + build_id: &str, + view: &BinaryView, +) -> Option { let settings = Settings::new(""); - let debug_dirs_enabled = settings.get_bool("analysis.debugInfo.enableDebugDirectories", Some(view), None); + let debug_dirs_enabled = settings.get_bool( + "analysis.debugInfo.enableDebugDirectories", + Some(view), + None, + ); if !debug_dirs_enabled { return None; } - let debug_info_paths = settings.get_string_list("analysis.debugInfo.debugDirectories", Some(view), None); + let debug_info_paths = + settings.get_string_list("analysis.debugInfo.debugDirectories", Some(view), None); if debug_info_paths.is_empty() { - return None + return None; } for debug_info_path in debug_info_paths.into_iter() { let path = PathBuf::from(debug_info_path); - let elf_path = path - .join(&build_id[..2]) - .join(&build_id[2..]) - .join("elf"); + let elf_path = path.join(&build_id[..2]).join(&build_id[2..]).join("elf"); let debug_ext_path = path .join(&build_id[..2]) @@ -506,47 +515,40 @@ pub(crate) fn find_local_debug_file_for_build_id(build_id: &str, view: &BinaryVi let final_path = if debug_ext_path.exists() { debug_ext_path - } - else if elf_path.exists() { + } else if elf_path.exists() { elf_path - } - else { + } else { // No paths exist in this dir, try the next one continue; }; - return final_path - .to_str() - .and_then(|x| Some(x.to_string())); + return final_path.to_str().and_then(|x| Some(x.to_string())); } None } - -pub(crate) fn load_debug_info_for_build_id(build_id: &str, view: &BinaryView) -> (Option>, bool) { +pub(crate) fn load_debug_info_for_build_id( + build_id: &str, + view: &BinaryView, +) -> (Option>, bool) { if let Some(debug_file_path) = find_local_debug_file_for_build_id(build_id, view) { - return - ( + return ( binaryninja::load_with_options( debug_file_path, false, - Some("{\"analysis.debugInfo.internal\": false}") + Some("{\"analysis.debugInfo.internal\": false}"), ), - false - ); - } - else if Settings::new("").get_bool("network.enableDebuginfod", Some(view), None) { - return ( - download_debug_info(build_id, view).ok(), - true + false, ); + } else if Settings::new("").get_bool("network.enableDebuginfod", Some(view), None) { + return (download_debug_info(build_id, view).ok(), true); } (None, false) } - pub(crate) fn find_sibling_debug_file(view: &BinaryView) -> Option { let settings = Settings::new(""); - let load_sibling_debug = settings.get_bool("analysis.debugInfo.loadSiblingDebugFiles", Some(view), None); + let load_sibling_debug = + settings.get_bool("analysis.debugInfo.loadSiblingDebugFiles", Some(view), None); if !load_sibling_debug { return None; @@ -565,9 +567,7 @@ pub(crate) fn find_sibling_debug_file(view: &BinaryView) -> Option { .file_name() .unwrap_or(OsStr::new("")); - let dsym_file = dsym_folder - .join("Contents/Resources/DWARF/") - .join(filename); // TODO: should this just pull any file out? Can there be multiple files? + let dsym_file = dsym_folder.join("Contents/Resources/DWARF/").join(filename); // TODO: should this just pull any file out? Can there be multiple files? if dsym_file.exists() { return Some(dsym_file.to_string_lossy().to_string()); } @@ -576,23 +576,21 @@ pub(crate) fn find_sibling_debug_file(view: &BinaryView) -> Option { None } - pub(crate) fn load_sibling_debug_file(view: &BinaryView) -> (Option>, bool) { let Some(debug_file) = find_sibling_debug_file(view) else { return (None, false); }; let load_settings = match view.default_platform() { - Some(plat) => format!("{{\"analysis.debugInfo.internal\": false, \"loader.platform\": \"{}\"}}", plat.name()), - None => "{\"analysis.debugInfo.internal\": false}".to_string() + Some(plat) => format!( + "{{\"analysis.debugInfo.internal\": false, \"loader.platform\": \"{}\"}}", + plat.name() + ), + None => "{\"analysis.debugInfo.internal\": false}".to_string(), }; ( - binaryninja::load_with_options( - debug_file, - false, - Some(load_settings) - ), - false + binaryninja::load_with_options(debug_file, false, Some(load_settings)), + false, ) } diff --git a/plugins/dwarf/dwarf_import/src/lib.rs b/plugins/dwarf/dwarf_import/src/lib.rs index 7b6334fec..f2c066421 100644 --- a/plugins/dwarf/dwarf_import/src/lib.rs +++ b/plugins/dwarf/dwarf_import/src/lib.rs @@ -37,16 +37,18 @@ use dwarfreader::{ }; use functions::parse_lexical_block; -use gimli::{constants, CfaRule, DebuggingInformationEntry, Dwarf, DwarfFileType, Reader, Section, SectionId, Unit, UnwindContext, UnwindSection}; +use gimli::{ + constants, CfaRule, DebuggingInformationEntry, Dwarf, DwarfFileType, Reader, Section, + SectionId, Unit, UnwindContext, UnwindSection, +}; +use binaryninja::logger::Logger; use helpers::{get_build_id, load_debug_info_for_build_id}; use log::{debug, error, warn}; -use binaryninja::logger::Logger; trait ReaderType: Reader {} impl> ReaderType for T {} - pub(crate) fn split_progress<'b, F: Fn(usize, usize) -> Result<(), ()> + 'b>( original_fn: F, subpart: usize, @@ -82,16 +84,13 @@ pub(crate) fn split_progress<'b, F: Fn(usize, usize) -> Result<(), ()> + 'b>( }) } - fn calculate_total_unit_bytes( dwarf: &Dwarf, debug_info_builder_context: &mut DebugInfoBuilderContext, -) -{ +) { let mut iter = dwarf.units(); let mut total_size: usize = 0; - while let Ok(Some(header)) = iter.next() - { + while let Ok(Some(header)) = iter.next() { total_size += header.length_including_self(); } debug_info_builder_context.total_unit_size_bytes = total_size; @@ -102,7 +101,6 @@ fn recover_names( debug_info_builder_context: &mut DebugInfoBuilderContext, progress: &dyn Fn(usize, usize) -> Result<(), ()>, ) -> bool { - let mut res = true; if let Some(sup_dwarf) = dwarf.sup() { res = recover_names_internal(sup_dwarf, debug_info_builder_context, progress); @@ -137,7 +135,12 @@ fn recover_names_internal( while let Ok(Some((delta_depth, entry))) = entries.next_dfs() { debug_info_builder_context.total_die_count += 1; - if (*progress)(current_byte_offset, debug_info_builder_context.total_unit_size_bytes).is_err() { + if (*progress)( + current_byte_offset, + debug_info_builder_context.total_unit_size_bytes, + ) + .is_err() + { return false; // Parsing canceled }; current_byte_offset = unit_offset + entry.offset().0; @@ -296,51 +299,67 @@ fn parse_unit( if let Some((_fn_idx, depth)) = functions_by_depth.last() { if current_depth <= *depth { functions_by_depth.pop(); + } else { + break; } - else { - break - } - } - else { + } else { break; } if let Some((_lexical_block, depth)) = lexical_blocks_by_depth.last() { if current_depth <= *depth { lexical_blocks_by_depth.pop(); + } else { + break; } - else { - break - } - } - else { + } else { break; } } match entry.tag() { constants::DW_TAG_subprogram => { - let fn_idx = parse_function_entry(dwarf, unit, entry, debug_info_builder_context, debug_info_builder); + let fn_idx = parse_function_entry( + dwarf, + unit, + entry, + debug_info_builder_context, + debug_info_builder, + ); functions_by_depth.push((fn_idx, current_depth)); - }, + } constants::DW_TAG_lexical_block => { if let Some(block_ranges) = parse_lexical_block(dwarf, unit, entry) { lexical_blocks_by_depth.push((block_ranges, current_depth)); } - }, + } constants::DW_TAG_variable => { let current_fn_idx = functions_by_depth.last().and_then(|x| x.0); let current_lexical_block = lexical_blocks_by_depth.last().and_then(|x| Some(&x.0)); - parse_variable(dwarf, unit, entry, debug_info_builder_context, debug_info_builder, current_fn_idx, current_lexical_block) - }, - constants::DW_TAG_class_type | - constants::DW_TAG_enumeration_type | - constants::DW_TAG_structure_type | - constants::DW_TAG_union_type | - constants::DW_TAG_typedef => { + parse_variable( + dwarf, + unit, + entry, + debug_info_builder_context, + debug_info_builder, + current_fn_idx, + current_lexical_block, + ) + } + constants::DW_TAG_class_type + | constants::DW_TAG_enumeration_type + | constants::DW_TAG_structure_type + | constants::DW_TAG_union_type + | constants::DW_TAG_typedef => { // Ensure types are loaded even if they're unused - types::get_type(dwarf, unit, entry, debug_info_builder_context, debug_info_builder); - }, + types::get_type( + dwarf, + unit, + entry, + debug_info_builder_context, + debug_info_builder, + ); + } _ => (), } } @@ -350,24 +369,41 @@ fn parse_unwind_section>( view: &BinaryView, unwind_section: U, ) -> gimli::Result> -where >::Offset: std::hash::Hash { +where + >::Offset: std::hash::Hash, +{ let mut bases = gimli::BaseAddresses::default(); - if let Some(section) = view.section_by_name(".eh_frame_hdr").or(view.section_by_name("__eh_frame_hdr")) { + if let Some(section) = view + .section_by_name(".eh_frame_hdr") + .or(view.section_by_name("__eh_frame_hdr")) + { bases = bases.set_eh_frame_hdr(section.start()); } - if let Some(section) = view.section_by_name(".eh_frame").or(view.section_by_name("__eh_frame")) { + if let Some(section) = view + .section_by_name(".eh_frame") + .or(view.section_by_name("__eh_frame")) + { bases = bases.set_eh_frame(section.start()); - } else if let Some(section) = view.section_by_name(".debug_frame").or(view.section_by_name("__debug_frame")) { + } else if let Some(section) = view + .section_by_name(".debug_frame") + .or(view.section_by_name("__debug_frame")) + { bases = bases.set_eh_frame(section.start()); } - if let Some(section) = view.section_by_name(".text").or(view.section_by_name("__text")) { + if let Some(section) = view + .section_by_name(".text") + .or(view.section_by_name("__text")) + { bases = bases.set_text(section.start()); } - if let Some(section) = view.section_by_name(".got").or(view.section_by_name("__got")) { + if let Some(section) = view + .section_by_name(".got") + .or(view.section_by_name("__got")) + { bases = bases.set_got(section.start()); } @@ -401,30 +437,38 @@ where >::Offset: std::hash::Hash { } if fde.initial_address().overflowing_add(fde.len()).1 { - warn!("FDE at offset {:?} exceeds bounds of memory space! {:#x} + length {:#x}", fde.offset(), fde.initial_address(), fde.len()); + warn!( + "FDE at offset {:?} exceeds bounds of memory space! {:#x} + length {:#x}", + fde.offset(), + fde.initial_address(), + fde.len() + ); } else { // Walk the FDE table rows and store their CFA let mut fde_table = fde.rows(&unwind_section, &bases, &mut unwind_context)?; while let Some(row) = fde_table.next_row()? { match row.cfa() { - CfaRule::RegisterAndOffset {register: _, offset} => { + CfaRule::RegisterAndOffset { + register: _, + offset, + } => { // TODO: we should store offsets by register if row.start_address() < row.end_address() { - cfa_offsets.insert( - row.start_address()..row.end_address(), - *offset, + cfa_offsets + .insert(row.start_address()..row.end_address(), *offset); + } else { + debug!( + "Invalid FDE table row addresses: {:#x}..{:#x}", + row.start_address(), + row.end_address() ); } - else { - debug!("Invalid FDE table row addresses: {:#x}..{:#x}", row.start_address(), row.end_address()); - } - }, + } CfaRule::Expression(_) => { debug!("Unhandled CFA expression when determining offset"); } }; - } } } @@ -447,11 +491,8 @@ fn get_supplementary_build_id(bv: &BinaryView) -> Option { .read_vec(start, len) .splitn(2, |x| *x == 0) .last() - .map(|a| { - a.iter().map(|b| format!("{:02x}", b)).collect() - }) - } - else { + .map(|a| a.iter().map(|b| format!("{:02x}", b)).collect()) + } else { None } } @@ -482,7 +523,7 @@ fn parse_dwarf( let mut section_reader = |section_id: SectionId| -> _ { create_section_reader(section_id, view, endian, dwo_file) }; - let mut dwarf = match Dwarf::load(&mut section_reader) { + let mut dwarf = match Dwarf::load(&mut section_reader) { Ok(x) => x, Err(e) => { error!("Failed to load DWARF info: {}", e); @@ -492,16 +533,16 @@ fn parse_dwarf( if dwo_file { dwarf.file_type = DwarfFileType::Dwo; - } - else { + } else { dwarf.file_type = DwarfFileType::Main; } if let Some(sup_bv) = supplementary_bv { let sup_endian = get_endian(sup_bv); let sup_dwo_file = is_dwo_dwarf(sup_bv) || is_raw_dwo_dwarf(sup_bv); - let sup_section_reader = - |section_id: SectionId| -> _ { create_section_reader(section_id, sup_bv, sup_endian, sup_dwo_file) }; + let sup_section_reader = |section_id: SectionId| -> _ { + create_section_reader(section_id, sup_bv, sup_endian, sup_dwo_file) + }; if let Err(e) = dwarf.load_sup(sup_section_reader) { error!("Failed to load supplementary file: {}", e); } @@ -510,27 +551,28 @@ fn parse_dwarf( let range_data_offsets; if view.section_by_name(".eh_frame").is_some() || view.section_by_name("__eh_frame").is_some() { let eh_frame_endian = get_endian(view); - let mut eh_frame_section_reader = - |section_id: SectionId| -> _ { create_section_reader(section_id, view, eh_frame_endian, dwo_file) }; - let mut eh_frame = gimli::EhFrame::load(&mut eh_frame_section_reader).unwrap(); + let eh_frame_section_reader = |section_id: SectionId| -> _ { + create_section_reader(section_id, view, eh_frame_endian, dwo_file) + }; + let mut eh_frame = gimli::EhFrame::load(eh_frame_section_reader).unwrap(); eh_frame.set_address_size(view.address_size() as u8); range_data_offsets = parse_unwind_section(view, eh_frame) .map_err(|e| error!("Error parsing .eh_frame: {}", e))?; - } - else if view.section_by_name(".debug_frame").is_some() || view.section_by_name("__debug_frame").is_some() { + } else if view.section_by_name(".debug_frame").is_some() + || view.section_by_name("__debug_frame").is_some() + { let debug_frame_endian = get_endian(view); - let mut debug_frame_section_reader = - |section_id: SectionId| -> _ { create_section_reader(section_id, view, debug_frame_endian, dwo_file) }; - let mut debug_frame = gimli::DebugFrame::load(&mut debug_frame_section_reader).unwrap(); + let debug_frame_section_reader = |section_id: SectionId| -> _ { + create_section_reader(section_id, view, debug_frame_endian, dwo_file) + }; + let mut debug_frame = gimli::DebugFrame::load(debug_frame_section_reader).unwrap(); debug_frame.set_address_size(view.address_size() as u8); range_data_offsets = parse_unwind_section(view, debug_frame) .map_err(|e| error!("Error parsing .debug_frame: {}", e))?; - } - else { + } else { range_data_offsets = Default::default(); } - // Create debug info builder and recover name mapping first // Since DWARF is stored as a tree with arbitrary implicit edges among leaves, // it is not possible to correctly track namespaces while you're parsing "in order" without backtracking, @@ -557,7 +599,7 @@ fn parse_dwarf( for unit in debug_info_builder_context.sup_units() { parse_unit( dwarf.sup().unwrap(), - &unit, + unit, &debug_info_builder_context, &mut debug_info_builder, &parse_progress, @@ -568,7 +610,7 @@ fn parse_dwarf( for unit in debug_info_builder_context.units() { parse_unit( &dwarf, - &unit, + unit, &debug_info_builder_context, &mut debug_info_builder, &parse_progress, @@ -610,36 +652,28 @@ impl CustomDebugInfoParser for DWARFParser { let (external_file, close_external) = if !dwarfreader::is_valid(bv) { if let (Some(debug_view), x) = helpers::load_sibling_debug_file(bv) { (Some(debug_view), x) - } - else if let Ok(build_id) = get_build_id(bv) { + } else if let Ok(build_id) = get_build_id(bv) { load_debug_info_for_build_id(&build_id, bv) - } - else { + } else { (None, false) } - } - else { + } else { (None, false) }; - let sup_bv = get_supplementary_build_id( - external_file - .as_deref() - .unwrap_or(debug_file) - ) + let sup_bv = get_supplementary_build_id(external_file.as_deref().unwrap_or(debug_file)) .and_then(|build_id| { load_debug_info_for_build_id(&build_id, bv) - .0 - .map(|x| x.raw_view().unwrap()) + .0 + .map(|x| x.raw_view().unwrap()) }); let result = match parse_dwarf( bv, external_file.as_deref().unwrap_or(debug_file), sup_bv.as_deref(), - progress - ) - { + progress, + ) { Ok(mut builder) => { builder.post_process(bv, debug_info).commit_info(debug_info); true diff --git a/plugins/dwarf/dwarf_import/src/types.rs b/plugins/dwarf/dwarf_import/src/types.rs index 77b3fb647..f5d5a4440 100644 --- a/plugins/dwarf/dwarf_import/src/types.rs +++ b/plugins/dwarf/dwarf_import/src/types.rs @@ -12,9 +12,9 @@ // See the License for the specific language governing permissions and // limitations under the License. -use crate::{die_handlers::*, ReaderType}; use crate::dwarfdebuginfo::{DebugInfoBuilder, DebugInfoBuilderContext, TypeUID}; use crate::helpers::*; +use crate::{die_handlers::*, ReaderType}; use binaryninja::{ rc::*, @@ -37,20 +37,32 @@ pub(crate) fn parse_variable( lexical_block: Option<&iset::IntervalSet>, ) { let full_name = debug_info_builder_context.get_name(dwarf, unit, entry); - let type_uid = get_type(dwarf, unit, entry, debug_info_builder_context, debug_info_builder); + let type_uid = get_type( + dwarf, + unit, + entry, + debug_info_builder_context, + debug_info_builder, + ); let Ok(Some(attr)) = entry.attr(constants::DW_AT_location) else { - return + return; }; let AttributeValue::Exprloc(mut expression) = attr.value() else { - return + return; }; match Operation::parse(&mut expression.0, unit.encoding()) { Ok(Operation::FrameOffset { offset }) => { - debug_info_builder.add_stack_variable(function_index, offset, full_name, type_uid, lexical_block); - }, + debug_info_builder.add_stack_variable( + function_index, + offset, + full_name, + type_uid, + lexical_block, + ); + } //Ok(Operation::RegisterOffset { register: _, offset: _, base_type: _ }) => { // //TODO: look up register by index (binja register indexes don't match processor indexes?) // //TODO: calculate absolute stack offset @@ -60,22 +72,23 @@ pub(crate) fn parse_variable( if let Some(uid) = type_uid { debug_info_builder.add_data_variable(address, full_name, uid) } - }, + } Ok(Operation::AddressIndex { index }) => { if let Some(uid) = type_uid { if let Ok(address) = dwarf.address(unit, index) { debug_info_builder.add_data_variable(address, full_name, uid) - } - else - { + } else { warn!("Invalid index into IAT: {}", index.0); } } - }, + } Ok(op) => { debug!("Unhandled operation type for variable: {:?}", op); - }, - Err(e) => error!("Error parsing operation type for variable {:?}: {}", full_name, e) + } + Err(e) => error!( + "Error parsing operation type for variable {:?}: {}", + full_name, e + ), } } @@ -141,10 +154,8 @@ fn do_structure_parse( // This reference type will be used by any children to grab while we're still building this type // it will also be how any other types refer to this struct if let Some(full_name) = &full_name { - let ntr = Type::named_type_from_type( - full_name, - &Type::structure(&structure_builder.finalize()), - ); + let ntr = + Type::named_type_from_type(full_name, &Type::structure(&structure_builder.finalize())); debug_info_builder.add_type( get_uid(dwarf, unit, entry), full_name.to_owned(), @@ -156,16 +167,9 @@ fn do_structure_parse( // These get overwritten in the last step with the actual type, however, so this // is either perfectly fine or breaking a bunch of NTRs let full_name = format!("anonymous_structure_{:x}", get_uid(dwarf, unit, entry)); - let ntr = Type::named_type_from_type( - &full_name, - &Type::structure(&structure_builder.finalize()), - ); - debug_info_builder.add_type( - get_uid(dwarf, unit, entry), - full_name, - ntr, - false, - ); + let ntr = + Type::named_type_from_type(&full_name, &Type::structure(&structure_builder.finalize())); + debug_info_builder.add_type(get_uid(dwarf, unit, entry), full_name, ntr, false); } // Get all the children and populate @@ -272,15 +276,13 @@ pub(crate) fn get_type( ) { // This needs to recurse first (before the early return below) to ensure all sub-types have been parsed match die_reference { - DieReference::UnitAndOffset((dwarf, entry_unit, entry_offset)) => { - get_type( - dwarf, - entry_unit, - &entry_unit.entry(entry_offset).unwrap(), - debug_info_builder_context, - debug_info_builder, - ) - } + DieReference::UnitAndOffset((dwarf, entry_unit, entry_offset)) => get_type( + dwarf, + entry_unit, + &entry_unit.entry(entry_offset).unwrap(), + debug_info_builder_context, + debug_info_builder, + ), DieReference::Err => { warn!("Failed to fetch DIE when getting type through DW_AT_type. Debug information may be incomplete."); None @@ -295,15 +297,13 @@ pub(crate) fn get_type( ) { // This needs to recurse first (before the early return below) to ensure all sub-types have been parsed match die_reference { - DieReference::UnitAndOffset((dwarf, entry_unit, entry_offset)) => { - get_type( - dwarf, - entry_unit, - &entry_unit.entry(entry_offset).unwrap(), - debug_info_builder_context, - debug_info_builder, - ) - } + DieReference::UnitAndOffset((dwarf, entry_unit, entry_offset)) => get_type( + dwarf, + entry_unit, + &entry_unit.entry(entry_offset).unwrap(), + debug_info_builder_context, + debug_info_builder, + ), DieReference::Err => { warn!("Failed to fetch DIE when getting type through DW_AT_abstract_origin. Debug information may be incomplete."); None @@ -326,7 +326,9 @@ pub(crate) fn get_type( } DieReference::UnitAndOffset(_) => None, DieReference::Err => { - warn!("Failed to fetch DIE when getting type. Debug information may be incomplete."); + warn!( + "Failed to fetch DIE when getting type. Debug information may be incomplete." + ); None } } @@ -378,9 +380,10 @@ pub(crate) fn get_type( } // Enum - constants::DW_TAG_enumeration_type => { - (handle_enum(dwarf, unit, entry, debug_info_builder_context), true) - } + constants::DW_TAG_enumeration_type => ( + handle_enum(dwarf, unit, entry, debug_info_builder_context), + true, + ), // Basic types constants::DW_TAG_typedef => { diff --git a/plugins/dwarf/dwarfdump/Cargo.toml b/plugins/dwarf/dwarfdump/Cargo.toml index 01ac6bbbb..815f25fef 100644 --- a/plugins/dwarf/dwarfdump/Cargo.toml +++ b/plugins/dwarf/dwarfdump/Cargo.toml @@ -10,4 +10,5 @@ crate-type = ["cdylib"] [dependencies] dwarfreader = { path = "../shared/" } binaryninja = { path = "../../../rust" } +binaryninjacore-sys = { path = "../../../rust/binaryninjacore-sys" } gimli = "0.31" diff --git a/plugins/dwarf/dwarfdump/build.rs b/plugins/dwarf/dwarfdump/build.rs new file mode 100644 index 000000000..ed6cec7d2 --- /dev/null +++ b/plugins/dwarf/dwarfdump/build.rs @@ -0,0 +1,15 @@ +fn main() { + let link_path = std::env::var_os("DEP_BINARYNINJACORE_PATH") + .expect("DEP_BINARYNINJACORE_PATH not specified"); + + println!("cargo::rustc-link-lib=dylib=binaryninjacore"); + println!("cargo::rustc-link-search={}", link_path.to_str().unwrap()); + + #[cfg(not(target_os = "windows"))] + { + println!( + "cargo::rustc-link-arg=-Wl,-rpath,{0},-L{0}", + link_path.to_string_lossy() + ); + } +} diff --git a/plugins/dwarf/dwarfdump/src/lib.rs b/plugins/dwarf/dwarfdump/src/lib.rs index c96fe06c5..f24ce000d 100644 --- a/plugins/dwarf/dwarfdump/src/lib.rs +++ b/plugins/dwarf/dwarfdump/src/lib.rs @@ -20,6 +20,7 @@ use binaryninja::{ }; use dwarfreader::is_valid; +use binaryninja::disassembly::StringType; use gimli::{ AttributeValue::{Encoding, Flag, UnitRef}, // BigEndian, @@ -32,7 +33,6 @@ use gimli::{ Unit, UnitSectionOffset, }; -use binaryninja::disassembly::StringType; static PADDING: [&str; 23] = [ "", @@ -62,7 +62,7 @@ static PADDING: [&str; 23] = [ // TODO : This is very much not comprehensive: see https://github.com/gimli-rs/gimli/blob/master/examples/dwarfdump.rs fn get_info_string( - view: &BinaryView, + _view: &BinaryView, dwarf: &Dwarf, unit: &Unit, die_node: &DebuggingInformationEntry, @@ -171,9 +171,7 @@ fn get_info_string( let addr_string = format!("#0x{:08x}", addr); attr_line.push(InstructionTextToken::new( &addr_string, - InstructionTextTokenKind::GotoLabel { - target: addr, - }, + InstructionTextTokenKind::GotoLabel { target: addr }, )); } else if let Flag(true) = attr.value() { attr_line.push(InstructionTextToken::new( @@ -215,10 +213,7 @@ fn get_info_string( let value_string = format!("{}", value); attr_line.push(InstructionTextToken::new( &value_string, - InstructionTextTokenKind::Integer { - value: value, - size: None, - }, + InstructionTextTokenKind::Integer { value, size: None }, )); } else if let Some(value) = attr.sdata_value() { let value_string = format!("{}", value); diff --git a/plugins/dwarf/shared/Cargo.toml b/plugins/dwarf/shared/Cargo.toml index cb6edac9a..7e3a6d936 100644 --- a/plugins/dwarf/shared/Cargo.toml +++ b/plugins/dwarf/shared/Cargo.toml @@ -6,6 +6,7 @@ edition = "2021" [dependencies] binaryninja = { path = "../../../rust" } +binaryninjacore-sys = { path = "../../../rust/binaryninjacore-sys" } gimli = "0.31" zstd = "0.13.2" thiserror = "1.0" diff --git a/plugins/dwarf/shared/build.rs b/plugins/dwarf/shared/build.rs new file mode 100644 index 000000000..ed6cec7d2 --- /dev/null +++ b/plugins/dwarf/shared/build.rs @@ -0,0 +1,15 @@ +fn main() { + let link_path = std::env::var_os("DEP_BINARYNINJACORE_PATH") + .expect("DEP_BINARYNINJACORE_PATH not specified"); + + println!("cargo::rustc-link-lib=dylib=binaryninjacore"); + println!("cargo::rustc-link-search={}", link_path.to_str().unwrap()); + + #[cfg(not(target_os = "windows"))] + { + println!( + "cargo::rustc-link-arg=-Wl,-rpath,{0},-L{0}", + link_path.to_string_lossy() + ); + } +} diff --git a/plugins/dwarf/shared/src/lib.rs b/plugins/dwarf/shared/src/lib.rs index c5fa28f44..de1f65357 100644 --- a/plugins/dwarf/shared/src/lib.rs +++ b/plugins/dwarf/shared/src/lib.rs @@ -16,8 +16,8 @@ use gimli::{EndianRcSlice, Endianity, RunTimeEndian, SectionId}; use binaryninja::{ binaryview::{BinaryView, BinaryViewBase, BinaryViewExt}, - Endianness, settings::Settings, + Endianness, }; use std::rc::Rc; @@ -63,14 +63,13 @@ pub fn is_raw_dwo_dwarf(view: &BinaryView) -> bool { } pub fn can_use_debuginfod(view: &BinaryView) -> bool { - has_build_id_section(view) && - Settings::new("") - .get_bool("network.enableDebuginfod", Some(view), None) + has_build_id_section(view) + && Settings::new("").get_bool("network.enableDebuginfod", Some(view), None) } pub fn has_build_id_section(view: &BinaryView) -> bool { if let Some(raw_view) = view.raw_view() { - return raw_view.section_by_name(".note.gnu.build-id").is_some() + return raw_view.section_by_name(".note.gnu.build-id").is_some(); } false } @@ -123,22 +122,20 @@ pub fn create_section_reader<'a, Endian: 'a + Endianity>( .find(|section_header| { if view.address_size() == 4 { endian.read_u32(§ion_header[16..20]) as u64 == section.start() - } - else { + } else { endian.read_u64(§ion_header[24..32]) == section.start() } }) { let section_flags = if view.address_size() == 4 { endian.read_u32(¤t_section_header[8..12]) as u64 - } - else { + } else { endian.read_u64(¤t_section_header[8..16]) }; // If the section has the compressed bit set if (section_flags & 2048) != 0 { // Get section, trim header, decompress, return - let compressed_header_size = view.address_size()*3; + let compressed_header_size = view.address_size() * 3; let offset = section.start() + compressed_header_size as u64; let len = section.len() - compressed_header_size; @@ -153,13 +150,13 @@ pub fn create_section_reader<'a, Endian: 'a + Endianity>( buffer.zlib_decompress().get_data().into(), endian, )); - }, + } 2 => { return Ok(EndianRcSlice::new( zstd::decode_all(buffer.get_data())?.as_slice().into(), - endian + endian, )); - }, + } x => { return Err(Error::UnknownCompressionMethod(x)); } diff --git a/plugins/idb_import/Cargo.toml b/plugins/idb_import/Cargo.toml index 04c8e7bc1..01622826b 100644 --- a/plugins/idb_import/Cargo.toml +++ b/plugins/idb_import/Cargo.toml @@ -10,6 +10,7 @@ crate-type = ["cdylib"] [dependencies] anyhow = { version = "1.0.86", features = ["backtrace"] } binaryninja = { path = "../../rust" } +binaryninjacore-sys = { path = "../../rust/binaryninjacore-sys" } idb-rs = { git = "https://github.com/Vector35/idb-rs", tag = "0.1.6" } log = "0.4.20" diff --git a/plugins/idb_import/build.rs b/plugins/idb_import/build.rs new file mode 100644 index 000000000..ed6cec7d2 --- /dev/null +++ b/plugins/idb_import/build.rs @@ -0,0 +1,15 @@ +fn main() { + let link_path = std::env::var_os("DEP_BINARYNINJACORE_PATH") + .expect("DEP_BINARYNINJACORE_PATH not specified"); + + println!("cargo::rustc-link-lib=dylib=binaryninjacore"); + println!("cargo::rustc-link-search={}", link_path.to_str().unwrap()); + + #[cfg(not(target_os = "windows"))] + { + println!( + "cargo::rustc-link-arg=-Wl,-rpath,{0},-L{0}", + link_path.to_string_lossy() + ); + } +} diff --git a/plugins/idb_import/src/lib.rs b/plugins/idb_import/src/lib.rs index 48d1b8fde..f7d59d829 100644 --- a/plugins/idb_import/src/lib.rs +++ b/plugins/idb_import/src/lib.rs @@ -200,7 +200,7 @@ pub fn import_til_section( if let TranslateTypeResult::Translated(bn_ty) | TranslateTypeResult::PartiallyTranslated(bn_ty, _) = &ty.ty { - if !debug_info.add_type(&String::from_utf8_lossy(&ty.name), bn_ty, &[/* TODO */]) { + if !debug_info.add_type(String::from_utf8_lossy(&ty.name), bn_ty, &[/* TODO */]) { error!( "Unable to add type `{}`", &String::from_utf8_lossy(&ty.name) @@ -214,7 +214,7 @@ pub fn import_til_section( if let TranslateTypeResult::Translated(bn_ty) | TranslateTypeResult::PartiallyTranslated(bn_ty, _) = &ty.ty { - if !debug_info.add_type(&String::from_utf8_lossy(&ty.name), bn_ty, &[/* TODO */]) { + if !debug_info.add_type(String::from_utf8_lossy(&ty.name), bn_ty, &[/* TODO */]) { error!( "Unable to fix type `{}`", &String::from_utf8_lossy(&ty.name) diff --git a/plugins/idb_import/src/types.rs b/plugins/idb_import/src/types.rs index b66b04067..34b04c588 100644 --- a/plugins/idb_import/src/types.rs +++ b/plugins/idb_import/src/types.rs @@ -4,6 +4,7 @@ use anyhow::{anyhow, Result}; use binaryninja::architecture::CoreArchitecture; use binaryninja::binaryninjacore_sys::{BNMemberAccess, BNMemberScope}; use binaryninja::binaryview::{BinaryView, BinaryViewExt}; +use binaryninja::confidence::Conf; use binaryninja::rc::Ref; use binaryninja::types::{ EnumerationBuilder, FunctionParameter, StructureBuilder, StructureType, Type, @@ -13,7 +14,6 @@ use idb_rs::til::{ r#struct::Struct as TILStruct, r#struct::StructMember as TILStructMember, section::TILSection, union::Union as TILUnion, TILTypeInfo, Type as TILType, Typedef as TILTypedef, }; -use binaryninja::confidence::Conf; #[derive(Debug, Clone)] pub enum BnTypeError { @@ -254,7 +254,7 @@ impl Result<(), ()>> TranslateIDBTypes<'_, F> { let ty = Type::function(&return_ty, bn_args, false); if is_partial { let error = (errors.ret.is_some() || !errors.args.is_empty()) - .then(|| BnTypeError::Function(errors)); + .then_some(BnTypeError::Function(errors)); TranslateTypeResult::PartiallyTranslated(ty, error) } else { assert!(errors.ret.is_none() && errors.args.is_empty()); @@ -372,7 +372,11 @@ impl Result<(), ()>> TranslateIDBTypes<'_, F> { (_, Some(start_idx)) => { first_bitfield_seq = None; let members_bitrange = &members[start_idx..i]; - self.condensate_bitfields_from_struct(start_idx, members_bitrange, &mut structure); + self.condensate_bitfields_from_struct( + start_idx, + members_bitrange, + &mut structure, + ); } (_, None) => {} diff --git a/plugins/minidump/build.rs b/plugins/minidump/build.rs index 5270252d2..ed6cec7d2 100644 --- a/plugins/minidump/build.rs +++ b/plugins/minidump/build.rs @@ -1,10 +1,10 @@ fn main() { - let link_path = - std::env::var_os("DEP_BINARYNINJACORE_PATH").expect("DEP_BINARYNINJACORE_PATH specified"); - + let link_path = std::env::var_os("DEP_BINARYNINJACORE_PATH") + .expect("DEP_BINARYNINJACORE_PATH not specified"); + println!("cargo::rustc-link-lib=dylib=binaryninjacore"); println!("cargo::rustc-link-search={}", link_path.to_str().unwrap()); - + #[cfg(not(target_os = "windows"))] { println!( diff --git a/plugins/minidump/src/lib.rs b/plugins/minidump/src/lib.rs index 628983016..8c5d4b0fa 100644 --- a/plugins/minidump/src/lib.rs +++ b/plugins/minidump/src/lib.rs @@ -1,8 +1,8 @@ use binaryninja::binaryview::BinaryView; use binaryninja::command::{register, Command}; use binaryninja::custombinaryview::register_view_type; -use log::{debug, LevelFilter}; use binaryninja::logger::Logger; +use log::{debug, LevelFilter}; mod command; mod view; @@ -22,7 +22,9 @@ impl Command for PrintMemoryInformationCommand { #[no_mangle] #[allow(non_snake_case)] pub extern "C" fn CorePluginInit() -> bool { - Logger::new("Minidump").with_level(LevelFilter::Trace).init(); + Logger::new("Minidump") + .with_level(LevelFilter::Trace) + .init(); debug!("Registering minidump binary view type"); register_view_type("Minidump", "Minidump", view::MinidumpBinaryViewType::new); diff --git a/plugins/pdb-ng/Cargo.toml b/plugins/pdb-ng/Cargo.toml index e0ea97bd6..8c34a3c68 100644 --- a/plugins/pdb-ng/Cargo.toml +++ b/plugins/pdb-ng/Cargo.toml @@ -9,6 +9,7 @@ crate-type = ["cdylib"] [dependencies] anyhow = "^1.0" binaryninja = { path = "../../rust" } +binaryninjacore-sys = { path = "../../rust/binaryninjacore-sys" } itertools = "^0.11" log = "^0.4" pdb = { git = "https://github.com/Vector35/pdb-rs", rev = "6016177" } diff --git a/plugins/pdb-ng/build.rs b/plugins/pdb-ng/build.rs new file mode 100644 index 000000000..ed6cec7d2 --- /dev/null +++ b/plugins/pdb-ng/build.rs @@ -0,0 +1,15 @@ +fn main() { + let link_path = std::env::var_os("DEP_BINARYNINJACORE_PATH") + .expect("DEP_BINARYNINJACORE_PATH not specified"); + + println!("cargo::rustc-link-lib=dylib=binaryninjacore"); + println!("cargo::rustc-link-search={}", link_path.to_str().unwrap()); + + #[cfg(not(target_os = "windows"))] + { + println!( + "cargo::rustc-link-arg=-Wl,-rpath,{0},-L{0}", + link_path.to_string_lossy() + ); + } +} diff --git a/plugins/pdb-ng/src/lib.rs b/plugins/pdb-ng/src/lib.rs index 3e55ca463..75d2e21cd 100644 --- a/plugins/pdb-ng/src/lib.rs +++ b/plugins/pdb-ng/src/lib.rs @@ -29,10 +29,10 @@ use binaryninja::binaryview::{BinaryView, BinaryViewBase, BinaryViewExt}; use binaryninja::debuginfo::{CustomDebugInfoParser, DebugInfo, DebugInfoParser}; use binaryninja::downloadprovider::{DownloadInstanceInputOutputCallbacks, DownloadProvider}; use binaryninja::interaction::{MessageBoxButtonResult, MessageBoxButtonSet}; +use binaryninja::logger::Logger; use binaryninja::settings::Settings; use binaryninja::string::BnString; use binaryninja::{add_optional_plugin_dependency, interaction, user_directory}; -use binaryninja::logger::Logger; use parser::PDBParserInstance; /// PDB Parser!! @@ -124,10 +124,7 @@ fn active_local_cache(view: Option<&BinaryView>) -> Result { } } -fn parse_sym_srv( - symbol_path: &String, - default_store: &String, -) -> Result>> { +fn parse_sym_srv(symbol_path: &str, default_store: String) -> Result> { // https://docs.microsoft.com/en-us/windows/win32/debug/using-symsrv // Why @@ -144,7 +141,7 @@ fn parse_sym_srv( // ... symbol servers are made up of symbol store elements separated by asterisks. There can // be up to 10 symbol stores after the "srv*" prefix. - let mut sym_srv_results = vec![]; + let mut sym_srv_results: Vec = vec![]; // 'path elements separated by semicolons' for path_element in symbol_path.split(';') { @@ -167,10 +164,10 @@ fn parse_sym_srv( } } - Ok(Box::new(sym_srv_results.into_iter())) + Ok(sym_srv_results.into_iter()) } -fn read_from_sym_store(bv: &BinaryView, path: &String) -> Result<(bool, Vec)> { +fn read_from_sym_store(bv: &BinaryView, path: &str) -> Result<(bool, Vec)> { if !path.contains("://") { // Local file info!("Read local file: {}", path); @@ -185,7 +182,7 @@ fn read_from_sym_store(bv: &BinaryView, path: &String) -> Result<(bool, Vec) // Download from remote let (tx, rx) = mpsc::channel(); let write = move |data: &[u8]| -> usize { - if let Ok(_) = tx.send(Vec::from(data)) { + if tx.send(Vec::from(data)).is_ok() { data.len() } else { 0 @@ -202,7 +199,7 @@ fn read_from_sym_store(bv: &BinaryView, path: &String) -> Result<(bool, Vec) let result = inst .perform_custom_request( "GET", - path.clone(), + path, HashMap::::new(), DownloadInstanceInputOutputCallbacks { read: None, @@ -240,7 +237,11 @@ fn read_from_sym_store(bv: &BinaryView, path: &String) -> Result<(bool, Vec) Ok((true, data)) } -fn search_sym_store(bv: &BinaryView, store_path: &String, pdb_info: &PDBInfo) -> Result>> { +fn search_sym_store( + bv: &BinaryView, + store_path: String, + pdb_info: &PDBInfo, +) -> Result>> { // https://www.technlg.net/windows/symbol-server-path-windbg-debugging/ // For symbol servers, to identify the files path easily, Windbg uses the format // binaryname.pdb/GUID @@ -249,8 +250,7 @@ fn search_sym_store(bv: &BinaryView, store_path: &String, pdb_info: &PDBInfo) -> // https://docs.microsoft.com/en-us/windows/win32/debug/using-symstore // In this example, the lookup path for the acpi.dbg symbol file might look something // like this: \\mybuilds\symsrv\acpi.dbg\37cdb03962040. - let base_path = - store_path.clone() + "/" + &pdb_info.file_name + "/" + &pdb_info.guid_age_string; + let base_path = store_path + "/" + &pdb_info.file_name + "/" + &pdb_info.guid_age_string; // Three files may exist inside the lookup directory: // 1. If the file was stored, then acpi.dbg will exist there. @@ -271,14 +271,14 @@ fn search_sym_store(bv: &BinaryView, store_path: &String, pdb_info: &PDBInfo) -> if let Ok((_remote, conts)) = read_from_sym_store(bv, &file_ptr) { let path = String::from_utf8(conts)?; // PATH:https://full/path - if path.starts_with("PATH:") { - if let Ok((_remote, conts)) = read_from_sym_store(bv, &path[5..].to_string()) { + if let Some(stripped) = path.strip_prefix("PATH:") { + if let Ok((_remote, conts)) = read_from_sym_store(bv, stripped) { return Ok(Some(conts)); } } } - return Ok(None); + Ok(None) } fn parse_pdb_info(view: &BinaryView) -> Option { @@ -360,7 +360,7 @@ impl PDBParser { conts: &Vec, debug_info: &mut DebugInfo, view: &BinaryView, - progress: &Box Result<(), ()>>, + progress: &dyn Fn(usize, usize) -> Result<(), ()>, check_guid: bool, did_download: bool, ) -> Result<()> { @@ -374,11 +374,8 @@ impl PDBParser { if check_guid { return Err(anyhow!("PDB GUID does not match")); } else { - let ask = settings.get_string( - "pdb.features.loadMismatchedPDB", - Some(view), - None, - ); + let ask = + settings.get_string("pdb.features.loadMismatchedPDB", Some(view), None); match ask.as_str() { "true" => {}, @@ -438,7 +435,7 @@ impl PDBParser { }; if has_dir { cab_path.push(&info.file_name); - match fs::write(&cab_path, &conts) { + match fs::write(&cab_path, conts) { Ok(_) => { info!("Downloaded to: {}", cab_path.to_string_lossy()); } @@ -473,7 +470,7 @@ impl PDBParser { }; if has_dir { cab_path.push(&info.file_name); - match fs::write(&cab_path, &conts) { + match fs::write(&cab_path, conts) { Ok(_) => { info!("Downloaded to: {}", cab_path.to_string_lossy()); } @@ -489,11 +486,7 @@ impl PDBParser { if check_guid { return Err(anyhow!("File not compiled with PDB information")); } else { - let ask = settings.get_string( - "pdb.features.loadMismatchedPDB", - Some(view), - None, - ); + let ask = settings.get_string("pdb.features.loadMismatchedPDB", Some(view), None); match ask.as_str() { "true" => {}, @@ -578,13 +571,13 @@ impl CustomDebugInfoParser for PDBParser { // First, check _NT_SYMBOL_PATH if let Ok(sym_path) = env::var("_NT_SYMBOL_PATH") { let stores = if let Ok(default_cache) = active_local_cache(Some(view)) { - parse_sym_srv(&sym_path, &default_cache) + parse_sym_srv(&sym_path, default_cache) } else { Err(anyhow!("No local cache found")) }; if let Ok(stores) = stores { for store in stores { - match search_sym_store(view, &store, &info) { + match search_sym_store(view, store.clone(), &info) { Ok(Some(conts)) => { match self .load_from_file(&conts, debug_info, view, &progress, true, true) @@ -622,10 +615,9 @@ impl CustomDebugInfoParser for PDBParser { potential_path.push(&info.file_name); if potential_path.exists() { match fs::read( - &potential_path + potential_path .to_str() - .expect("Potential path is a real string") - .to_string(), + .expect("Potential path is a real string"), ) { Ok(conts) => match self .load_from_file(&conts, debug_info, view, &progress, true, false) @@ -641,7 +633,7 @@ impl CustomDebugInfoParser for PDBParser { // Check the local symbol store if let Ok(local_store_path) = active_local_cache(Some(view)) { - match search_sym_store(view, &local_store_path, &info) { + match search_sym_store(view, local_store_path.clone(), &info) { Ok(Some(conts)) => { match self.load_from_file(&conts, debug_info, view, &progress, true, false) { @@ -663,7 +655,7 @@ impl CustomDebugInfoParser for PDBParser { Settings::new("").get_string_list("pdb.files.symbolServerList", Some(view), None); for server in server_list.iter() { - match search_sym_store(view, &server.to_string(), &info) { + match search_sym_store(view, server.to_string(), &info) { Ok(Some(conts)) => { match self.load_from_file(&conts, debug_info, view, &progress, true, true) { Ok(_) => return true, @@ -880,7 +872,7 @@ fn test_sym_srv() { assert_eq!( parse_sym_srv( &r"srv*\\mybuilds\mysymbols".to_string(), - &r"DEFAULT_STORE".to_string() + r"DEFAULT_STORE".to_string() ) .expect("parse success") .collect::>(), @@ -889,7 +881,7 @@ fn test_sym_srv() { assert_eq!( parse_sym_srv( &r"srv*c:\localsymbols*\\mybuilds\mysymbols".to_string(), - &r"DEFAULT_STORE".to_string() + r"DEFAULT_STORE".to_string() ) .expect("parse success") .collect::>(), @@ -901,7 +893,7 @@ fn test_sym_srv() { assert_eq!( parse_sym_srv( &r"srv**\\mybuilds\mysymbols".to_string(), - &r"DEFAULT_STORE".to_string() + r"DEFAULT_STORE".to_string() ) .expect("parse success") .collect::>(), @@ -913,7 +905,7 @@ fn test_sym_srv() { assert_eq!( parse_sym_srv( &r"srv*c:\localsymbols*\\NearbyServer\store*https://DistantServer".to_string(), - &r"DEFAULT_STORE".to_string() + r"DEFAULT_STORE".to_string() ) .expect("parse success") .collect::>(), @@ -926,7 +918,7 @@ fn test_sym_srv() { assert_eq!( parse_sym_srv( &r"srv*c:\DownstreamStore*https://msdl.microsoft.com/download/symbols".to_string(), - &r"DEFAULT_STORE".to_string() + r"DEFAULT_STORE".to_string() ) .expect("parse success") .collect::>(), diff --git a/plugins/pdb-ng/src/parser.rs b/plugins/pdb-ng/src/parser.rs index fb1a6ad85..a1083527d 100644 --- a/plugins/pdb-ng/src/parser.rs +++ b/plugins/pdb-ng/src/parser.rs @@ -21,6 +21,8 @@ use anyhow::{anyhow, Result}; use log::{debug, info}; use pdb::*; +use crate::symbol_parser::{ParsedDataSymbol, ParsedProcedure, ParsedSymbol}; +use crate::type_parser::ParsedType; use binaryninja::architecture::{Architecture, CoreArchitecture}; use binaryninja::binaryview::{BinaryView, BinaryViewExt}; use binaryninja::callingconvention::CallingConvention; @@ -29,10 +31,11 @@ use binaryninja::debuginfo::{DebugFunctionInfo, DebugInfo}; use binaryninja::platform::Platform; use binaryninja::rc::Ref; use binaryninja::settings::Settings; -use binaryninja::types::{EnumerationBuilder, NamedTypeReference, NamedTypeReferenceClass, QualifiedName, StructureBuilder, StructureType, Type, TypeClass}; +use binaryninja::types::{ + EnumerationBuilder, NamedTypeReference, NamedTypeReferenceClass, QualifiedName, + StructureBuilder, StructureType, Type, TypeClass, +}; use binaryninja::variable::NamedDataVariableWithType; -use crate::symbol_parser::{ParsedDataSymbol, ParsedProcedure, ParsedSymbol}; -use crate::type_parser::ParsedType; /// Megastruct for all the parsing /// Certain fields are only used by specific files, as marked below. @@ -208,8 +211,7 @@ impl<'a, S: Source<'a> + 'a> PDBParserInstance<'a, S> { type_, .. }) => { - let real_type = - type_.as_ref().unwrap_or(&min_confidence_type); + let real_type = type_.as_ref().unwrap_or(&min_confidence_type); if real_type.contents.type_class() == TypeClass::VoidTypeClass { if !allow_void { @@ -287,9 +289,7 @@ impl<'a, S: Source<'a> + 'a> PDBParserInstance<'a, S> { unknown_names: &mut HashMap, ) { let used_name = name.name(); - if let Some(&found) = - unknown_names.get(&used_name) - { + if let Some(&found) = unknown_names.get(&used_name) { if found != name.class() { // Interesting case, not sure we care self.log(|| { @@ -369,7 +369,7 @@ impl<'a, S: Source<'a> + 'a> PDBParserInstance<'a, S> { } let count = symbols.len(); - for (i, sym) in symbols.into_iter().enumerate() { + for (i, sym) in symbols.iter().enumerate() { match sym { ParsedSymbol::Data(ParsedDataSymbol { type_: Some(type_), .. @@ -390,7 +390,7 @@ impl<'a, S: Source<'a> + 'a> PDBParserInstance<'a, S> { } _ => {} } - (&progress)(i, count)?; + progress(i, count)?; } for (name, class) in unknown_names.into_iter() { @@ -402,7 +402,8 @@ impl<'a, S: Source<'a> + 'a> PDBParserInstance<'a, S> { match class { NamedTypeReferenceClass::UnknownNamedTypeClass | NamedTypeReferenceClass::TypedefNamedTypeClass => { - self.debug_info.add_type(&name, Type::void().as_ref(), &[]); // TODO : Components + self.debug_info.add_type(&name, Type::void().as_ref(), &[]); + // TODO : Components } NamedTypeReferenceClass::ClassNamedTypeClass | NamedTypeReferenceClass::StructNamedTypeClass @@ -451,16 +452,14 @@ impl<'a, S: Source<'a> + 'a> PDBParserInstance<'a, S> { /// Lazy logging function that prints like 20MB of messages pub(crate) fn log D, D: Display>(&self, msg: F) { static MEM: OnceLock = OnceLock::new(); - let debug_pdb = MEM.get_or_init(|| { - env::var("BN_DEBUG_PDB").is_ok() - }); + let debug_pdb = MEM.get_or_init(|| env::var("BN_DEBUG_PDB").is_ok()); if *debug_pdb { let space = "\t".repeat(self.type_stack.len()) + &"\t".repeat(self.symbol_stack.len()); let msg = format!("{}", msg()); debug!( "{}{}", space, - msg.replace("\n", &*("\n".to_string() + &space)) + msg.replace("\n", &("\n".to_string() + &space)) ); } } diff --git a/plugins/pdb-ng/src/struct_grouper.rs b/plugins/pdb-ng/src/struct_grouper.rs index ffc1046b2..3c0fc7b82 100644 --- a/plugins/pdb-ng/src/struct_grouper.rs +++ b/plugins/pdb-ng/src/struct_grouper.rs @@ -12,15 +12,13 @@ // See the License for the specific language governing permissions and // limitations under the License. +use anyhow::{anyhow, Result}; +use binaryninja::confidence::{Conf, MAX_CONFIDENCE}; +use binaryninja::types::{MemberAccess, MemberScope, StructureBuilder, StructureType, Type}; +use log::{debug, warn}; use std::cmp::Ordering; use std::env; use std::fmt::{Debug, Display, Formatter}; -use anyhow::{anyhow, Result}; -use log::{debug, warn}; -use binaryninja::confidence::{Conf, MAX_CONFIDENCE}; -use binaryninja::types::{ - MemberAccess, MemberScope, StructureBuilder, StructureType, Type, -}; use crate::type_parser::ParsedMember; @@ -63,7 +61,7 @@ impl PartialOrd for WorkingStruct { impl Debug for WorkingStruct { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { - if self.children.len() == 0 { + if self.children.is_empty() { write!(f, "{:X} -> {:X}", self.start(), self.end())?; if let Some(index) = self.index { write!(f, " (#{:X})", index)?; @@ -128,11 +126,11 @@ impl WorkingStruct { pub fn insert(&mut self, other: WorkingStruct, recursion: usize) -> Result<()> { log(|| { format!("{}self: {:#?}", " ".repeat(recursion), self) - .replace("\n", &*("\n".to_owned() + &" ".repeat(recursion))) + .replace("\n", &("\n".to_owned() + &" ".repeat(recursion))) }); log(|| { format!("{}other: {:#?}", " ".repeat(recursion), other) - .replace("\n", &*("\n".to_owned() + &" ".repeat(recursion))) + .replace("\n", &("\n".to_owned() + &" ".repeat(recursion))) }); self.extend_to(other.end()); @@ -142,7 +140,7 @@ impl WorkingStruct { // b. `other` starts before the end of the last group => collect all the children inserted after it starts and put them into a struct // start a new struct with `other` - if self.children.len() == 0 { + if self.children.is_empty() { self.children.push(other); return Ok(()); } @@ -362,7 +360,7 @@ pub fn group_structure( warn!("{} Could not resolve structure groups: {}", name, e); for member in members { structure.insert( - &member.ty, + &member.ty.clone(), member.name.clone(), member.offset, false, @@ -391,7 +389,7 @@ fn apply_groups( if offset > member.offset { structure.insert( - &member.ty, + &member.ty.clone(), member.name.clone(), 0, false, @@ -400,7 +398,7 @@ fn apply_groups( ); } else { structure.insert( - &member.ty, + &member.ty.clone(), member.name.clone(), member.offset - offset, false, diff --git a/plugins/pdb-ng/src/symbol_parser.rs b/plugins/pdb-ng/src/symbol_parser.rs index 51e18d1c0..bdc12ca63 100644 --- a/plugins/pdb-ng/src/symbol_parser.rs +++ b/plugins/pdb-ng/src/symbol_parser.rs @@ -35,17 +35,14 @@ use pdb::{ UserDefinedTypeSymbol, UsingNamespaceSymbol, }; +use crate::PDBParserInstance; use binaryninja::architecture::{Architecture, ArchitectureExt, Register, RegisterId}; use binaryninja::binaryview::BinaryViewBase; use binaryninja::confidence::{Conf, MAX_CONFIDENCE, MIN_CONFIDENCE}; use binaryninja::demangle::demangle_ms; use binaryninja::rc::Ref; -use binaryninja::types::{ - FunctionParameter, QualifiedName, - StructureBuilder, Type, TypeClass, -}; +use binaryninja::types::{FunctionParameter, QualifiedName, StructureBuilder, Type, TypeClass}; use binaryninja::variable::{Variable, VariableSourceType}; -use crate::PDBParserInstance; const DEMANGLE_CONFIDENCE: u8 = 32; @@ -500,7 +497,7 @@ impl<'a, S: Source<'a> + 'a> PDBParserInstance<'a, S> { } assert!(self.symbol_stack.is_empty()); // Add thunks at the end as per above - top_level_syms.extend(thunk_syms.into_iter()); + top_level_syms.extend(thunk_syms); // Restart and do the processing for real this time if let Some(first) = first { @@ -592,7 +589,7 @@ impl<'a, S: Source<'a> + 'a> PDBParserInstance<'a, S> { } children.push(sym); - return children; + children } /// Direct children of symbol index (only during this module parse) @@ -607,7 +604,7 @@ impl<'a, S: Source<'a> + 'a> PDBParserInstance<'a, S> { /// Direct parent of symbol index (only during this module parse) #[allow(dead_code)] fn symbol_parent(&self, sym: SymbolIndex) -> Option { - self.symbol_parents.get(&sym).map(|idx| *idx) + self.symbol_parents.get(&sym).copied() } /// Find symbol by index (only during this module parse) @@ -653,62 +650,58 @@ impl<'a, S: Source<'a> + 'a> PDBParserInstance<'a, S> { ) -> Result> { match data { SymbolData::ScopeEnd => self.handle_scope_end_symbol(index), - SymbolData::ObjName(data) => self.handle_obj_name_symbol(index, &data), - SymbolData::RegisterVariable(data) => { - self.handle_register_variable_symbol(index, &data) - } - SymbolData::Constant(data) => self.handle_constant_symbol(index, &data), - SymbolData::UserDefinedType(data) => self.handle_user_defined_type_symbol(index, &data), + SymbolData::ObjName(data) => self.handle_obj_name_symbol(index, data), + SymbolData::RegisterVariable(data) => self.handle_register_variable_symbol(index, data), + SymbolData::Constant(data) => self.handle_constant_symbol(index, data), + SymbolData::UserDefinedType(data) => self.handle_user_defined_type_symbol(index, data), SymbolData::MultiRegisterVariable(data) => { - self.handle_multi_register_variable_symbol(index, &data) + self.handle_multi_register_variable_symbol(index, data) } - SymbolData::Data(data) => self.handle_data_symbol(index, &data), - SymbolData::Public(data) => self.handle_public_symbol(index, &data), - SymbolData::Procedure(data) => self.handle_procedure_symbol(index, &data), - SymbolData::ThreadStorage(data) => self.handle_thread_storage_symbol(index, &data), - SymbolData::CompileFlags(data) => self.handle_compile_flags_symbol(index, &data), - SymbolData::UsingNamespace(data) => self.handle_using_namespace_symbol(index, &data), + SymbolData::Data(data) => self.handle_data_symbol(index, data), + SymbolData::Public(data) => self.handle_public_symbol(index, data), + SymbolData::Procedure(data) => self.handle_procedure_symbol(index, data), + SymbolData::ThreadStorage(data) => self.handle_thread_storage_symbol(index, data), + SymbolData::CompileFlags(data) => self.handle_compile_flags_symbol(index, data), + SymbolData::UsingNamespace(data) => self.handle_using_namespace_symbol(index, data), SymbolData::ProcedureReference(data) => { self.handle_procedure_reference_symbol(index, &data) } - SymbolData::DataReference(data) => self.handle_data_reference_symbol(index, &data), + SymbolData::DataReference(data) => self.handle_data_reference_symbol(index, data), SymbolData::AnnotationReference(data) => { - self.handle_annotation_reference_symbol(index, &data) + self.handle_annotation_reference_symbol(index, data) } - SymbolData::Trampoline(data) => self.handle_trampoline_symbol(index, &data), - SymbolData::Export(data) => self.handle_export_symbol(index, &data), - SymbolData::Local(data) => self.handle_local_symbol(index, &data), - SymbolData::BuildInfo(data) => self.handle_build_info_symbol(index, &data), - SymbolData::InlineSite(data) => self.handle_inline_site_symbol(index, &data), + SymbolData::Trampoline(data) => self.handle_trampoline_symbol(index, data), + SymbolData::Export(data) => self.handle_export_symbol(index, data), + SymbolData::Local(data) => self.handle_local_symbol(index, data), + SymbolData::BuildInfo(data) => self.handle_build_info_symbol(index, data), + SymbolData::InlineSite(data) => self.handle_inline_site_symbol(index, data), SymbolData::InlineSiteEnd => self.handle_inline_site_end_symbol(index), SymbolData::ProcedureEnd => self.handle_procedure_end_symbol(index), - SymbolData::Label(data) => self.handle_label_symbol(index, &data), - SymbolData::Block(data) => self.handle_block_symbol(index, &data), - SymbolData::RegisterRelative(data) => { - self.handle_register_relative_symbol(index, &data) - } - SymbolData::Thunk(data) => self.handle_thunk_symbol(index, &data), - SymbolData::SeparatedCode(data) => self.handle_separated_code_symbol(index, &data), + SymbolData::Label(data) => self.handle_label_symbol(index, data), + SymbolData::Block(data) => self.handle_block_symbol(index, data), + SymbolData::RegisterRelative(data) => self.handle_register_relative_symbol(index, data), + SymbolData::Thunk(data) => self.handle_thunk_symbol(index, data), + SymbolData::SeparatedCode(data) => self.handle_separated_code_symbol(index, data), SymbolData::DefRange(data) => self.handle_def_range(index, &data), - SymbolData::DefRangeSubField(data) => self.handle_def_range_sub_field(index, &data), - SymbolData::DefRangeRegister(data) => self.handle_def_range_register(index, &data), + SymbolData::DefRangeSubField(data) => self.handle_def_range_sub_field(index, data), + SymbolData::DefRangeRegister(data) => self.handle_def_range_register(index, data), SymbolData::DefRangeFramePointerRelative(data) => { - self.handle_def_range_frame_pointer_relative_symbol(index, &data) + self.handle_def_range_frame_pointer_relative_symbol(index, data) } SymbolData::DefRangeFramePointerRelativeFullScope(data) => { - self.handle_def_range_frame_pointer_relative_full_scope_symbol(index, &data) + self.handle_def_range_frame_pointer_relative_full_scope_symbol(index, data) } SymbolData::DefRangeSubFieldRegister(data) => { - self.handle_def_range_sub_field_register_symbol(index, &data) + self.handle_def_range_sub_field_register_symbol(index, data) } SymbolData::DefRangeRegisterRelative(data) => { - self.handle_def_range_register_relative_symbol(index, &data) + self.handle_def_range_register_relative_symbol(index, data) } SymbolData::BasePointerRelative(data) => { - self.handle_base_pointer_relative_symbol(index, &data) + self.handle_base_pointer_relative_symbol(index, data) } - SymbolData::FrameProcedure(data) => self.handle_frame_procedure_symbol(index, &data), - SymbolData::CallSiteInfo(data) => self.handle_call_site_info(index, &data), + SymbolData::FrameProcedure(data) => self.handle_frame_procedure_symbol(index, data), + SymbolData::CallSiteInfo(data) => self.handle_call_site_info(index, data), e => Err(anyhow!("Unhandled symbol type {:?}", e)), } } @@ -952,10 +945,7 @@ impl<'a, S: Source<'a> + 'a> PDBParserInstance<'a, S> { _ => return Ok((fancier_type, vec![])), }; - let raw_params = raw_type - .contents - .parameters() - .ok_or(anyhow!("no params"))?; + let raw_params = raw_type.contents.parameters().ok_or(anyhow!("no params"))?; let mut fancy_params = fancy_type .contents .parameters() @@ -970,7 +960,7 @@ impl<'a, S: Source<'a> + 'a> PDBParserInstance<'a, S> { MIN_CONFIDENCE, )), p.name.clone(), - p.storage.get(0).map(|loc| loc.location.clone()), + p.storage.first().map(|loc| loc.location), ); // Ignore thisptr because it's not technically part of the raw type signature if p.name != "this" { @@ -985,7 +975,7 @@ impl<'a, S: Source<'a> + 'a> PDBParserInstance<'a, S> { MIN_CONFIDENCE, )), p.name.clone(), - p.storage.get(0).map(|loc| loc.location.clone()), + p.storage.first().map(|loc| loc.location), ); // Ignore thisptr because it's not technically part of the raw type signature if p.name != "this" { @@ -998,10 +988,7 @@ impl<'a, S: Source<'a> + 'a> PDBParserInstance<'a, S> { self.log(|| format!("Parsed params: {:#x?}", parsed_params)); // We expect one parameter for each unnamed parameter in the marked up type - let expected_param_count = fancy_params - .iter() - .filter(|p| p.name.as_str().is_empty()) - .count(); + let expected_param_count = fancy_params.iter().filter(|p| p.name.is_empty()).count(); // Sanity if expected_param_count != raw_params.len() { return Err(anyhow!( @@ -1018,7 +1005,7 @@ impl<'a, S: Source<'a> + 'a> PDBParserInstance<'a, S> { if expected_param_count > (parsed_params.len() + parsed_locals.len()) { return Ok((fancier_type, vec![])); } - parsed_params.extend(parsed_locals.into_iter()); + parsed_params.extend(parsed_locals); } let expected_parsed_params = parsed_params .drain(0..expected_param_count) @@ -1029,7 +1016,7 @@ impl<'a, S: Source<'a> + 'a> PDBParserInstance<'a, S> { let mut i = 0; for p in fancy_params.iter_mut() { - if p.name.as_str().is_empty() { + if p.name.is_empty() { if p.ty.contents != expected_parsed_params[i].ty.contents { self.log(|| { format!( @@ -1344,11 +1331,11 @@ impl<'a, S: Source<'a> + 'a> PDBParserInstance<'a, S> { ) -> Result> { self.log(|| format!("Got Local symbol: {:?}", data)); // Look for definition ranges for this symbol - let mut locations = vec![]; + let mut locations: Vec = vec![]; for child in self.symbol_children(index) { match self.lookup_symbol(&child) { Some(ParsedSymbol::Location(loc)) => { - locations.push(loc.clone()); + locations.push(*loc); } _ => {} } @@ -1545,7 +1532,7 @@ impl<'a, S: Source<'a> + 'a> PDBParserInstance<'a, S> { Ok(Some(ParsedSymbol::Procedure(ParsedProcedure { is_public: false, - address: address, + address, name, type_: fn_type, locals, @@ -1794,10 +1781,7 @@ impl<'a, S: Source<'a> + 'a> PDBParserInstance<'a, S> { if let Some(ty) = t.as_ref() { if ty.contents.type_class() == TypeClass::FunctionTypeClass { // demangler makes (void) into (void arg1) which is wrong - let parameters = ty - .contents - .parameters() - .ok_or(anyhow!("no parameters"))?; + let parameters = ty.contents.parameters().ok_or(anyhow!("no parameters"))?; if let [p] = parameters.as_slice() { if p.ty.contents.type_class() == TypeClass::VoidTypeClass { t = Some(Conf::new( @@ -1959,14 +1943,14 @@ impl<'a, S: Source<'a> + 'a> PDBParserInstance<'a, S> { let name = if name.len() == 1 && &name[0] == raw_name && raw_name.starts_with('?') { None - } else if name.len() == 1 && name[0] == "" { + } else if name.len() == 1 && name[0].is_empty() { None } else if name.len() > 0 && name[0].starts_with("\x7f") { // Not sure why these exist but they do Weird Stuff name[0].drain(0..1); - Some(QualifiedName::from(name)) + Some(name) } else { - Some(QualifiedName::from(name)) + Some(name) }; Ok((t, name)) @@ -1984,9 +1968,7 @@ impl<'a, S: Source<'a> + 'a> PDBParserInstance<'a, S> { .get_structure() .ok_or(anyhow!("Expected structure"))?; let mut members = structure.members(); - let last_member = members - .last_mut() - .ok_or(anyhow!("Not enough members"))?; + let last_member = members.last_mut().ok_or(anyhow!("Not enough members"))?; if last_member.ty.contents.type_class() != TypeClass::ArrayTypeClass { return Ok(None); @@ -2006,8 +1988,7 @@ impl<'a, S: Source<'a> + 'a> PDBParserInstance<'a, S> { // Read member_width bytes from bv starting at that member, until we read all zeroes let member_address = base_address + last_member.offset; - let mut bytes = Vec::::new(); - bytes.resize(member_width as usize, 0); + let mut bytes = vec![0; member_width as usize]; let mut element_count = 0; while self.bv.read( diff --git a/plugins/pdb-ng/src/type_parser.rs b/plugins/pdb-ng/src/type_parser.rs index 1af4bb84b..9c4e14f5c 100644 --- a/plugins/pdb-ng/src/type_parser.rs +++ b/plugins/pdb-ng/src/type_parser.rs @@ -15,16 +15,19 @@ use std::collections::HashMap; use std::sync::OnceLock; +use crate::struct_grouper::group_structure; +use crate::PDBParserInstance; use anyhow::{anyhow, Result}; use binaryninja::architecture::{Architecture, CoreArchitecture}; use binaryninja::binaryview::BinaryViewExt; use binaryninja::callingconvention::CallingConvention; +use binaryninja::confidence::{Conf, MAX_CONFIDENCE}; use binaryninja::platform::Platform; use binaryninja::rc::Ref; use binaryninja::types::{ - BaseStructure, EnumerationBuilder, EnumerationMember, FunctionParameter, - MemberAccess, MemberScope, NamedTypeReference, NamedTypeReferenceClass, QualifiedName, - StructureBuilder, StructureMember, StructureType, Type, TypeBuilder, TypeClass, + BaseStructure, EnumerationBuilder, EnumerationMember, FunctionParameter, MemberAccess, + MemberScope, NamedTypeReference, NamedTypeReferenceClass, QualifiedName, StructureBuilder, + StructureMember, StructureType, Type, TypeBuilder, TypeClass, }; use log::warn; use pdb::Error::UnimplementedTypeKind; @@ -37,11 +40,8 @@ use pdb::{ VirtualFunctionTablePointerType, VirtualFunctionTableType, VirtualTableShapeType, }; use regex::Regex; -use binaryninja::confidence::{Conf, MAX_CONFIDENCE}; -use crate::struct_grouper::group_structure; -use crate::PDBParserInstance; -static BUILTIN_NAMES: &[&'static str] = &[ +static BUILTIN_NAMES: &[&str] = &[ "size_t", "ssize_t", "ptrdiff_t", @@ -311,7 +311,7 @@ impl<'a, S: Source<'a> + 'a> PDBParserInstance<'a, S> { } } - self.log(|| format!("Now parsing named types")); + self.log(|| "Now parsing named types"); // Parse the types we care about, so that recursion gives us parent relationships for free let mut types = type_information.iter(); @@ -331,7 +331,7 @@ impl<'a, S: Source<'a> + 'a> PDBParserInstance<'a, S> { assert!(self.type_stack.is_empty()); } - self.log(|| format!("Now parsing unused floating types")); + self.log(|| "Now parsing unused floating types"); // Parse the rest because symbols often use them let mut postpass_types = type_information.iter(); @@ -342,10 +342,10 @@ impl<'a, S: Source<'a> + 'a> PDBParserInstance<'a, S> { self.handle_type_index(ty.index(), &mut finder)?; } - self.log(|| format!("Now adding all unreferenced named types")); + self.log(|| "Now adding all unreferenced named types"); // Any referenced named types that are only forward-declared will cause missing type references, // so create empty types for those here. - for (_, parsed) in &self.indexed_types { + for parsed in self.indexed_types.values() { match parsed { ParsedType::Bare(ty) if ty.type_class() == TypeClass::NamedTypeReferenceClass => { // See if we have this type @@ -360,7 +360,7 @@ impl<'a, S: Source<'a> + 'a> PDBParserInstance<'a, S> { continue; } // If the bv has this type, DebugInfo will just update us to reference it - if let Some(_) = self.bv.get_type_by_name(name.to_owned()) { + if self.bv.get_type_by_name(name.to_owned()).is_some() { continue; } @@ -413,16 +413,12 @@ impl<'a, S: Source<'a> + 'a> PDBParserInstance<'a, S> { } static MEM: OnceLock = OnceLock::new(); - let uint_regex = MEM.get_or_init(|| { - Regex::new(r"u?int\d+_t").unwrap() - }); + let uint_regex = MEM.get_or_init(|| Regex::new(r"u?int\d+_t").unwrap()); - let float_regex = MEM.get_or_init(|| { - Regex::new(r"float\d+").unwrap() - }); + let float_regex = MEM.get_or_init(|| Regex::new(r"float\d+").unwrap()); let mut remove_names = vec![]; - for (name, _) in &self.named_types { + for name in self.named_types.keys() { let name_str = name.to_string(); if uint_regex.is_match(&name_str) { remove_names.push(name.clone()); @@ -447,7 +443,9 @@ impl<'a, S: Source<'a> + 'a> PDBParserInstance<'a, S> { ) -> Result>> { match self.indexed_types.get(index) { Some(ParsedType::Bare(ty)) => Ok(Some(ty.clone())), - Some(ParsedType::Named(name, ty)) => Ok(Some(Type::named_type_from_type(name.clone(), &ty))), + Some(ParsedType::Named(name, ty)) => { + Ok(Some(Type::named_type_from_type(name.clone(), ty))) + } Some(ParsedType::Procedure(ParsedProcedureType { method_type, raw_method_type, @@ -590,40 +588,40 @@ impl<'a, S: Source<'a> + 'a> PDBParserInstance<'a, S> { finder: &mut ItemFinder, ) -> Result>> { match data { - TypeData::Primitive(data) => Ok(self.handle_primitive_type(&data, finder)?), - TypeData::Class(data) => Ok(self.handle_class_type(&data, finder)?), - TypeData::Member(data) => Ok(self.handle_member_type(&data, finder)?), - TypeData::MemberFunction(data) => Ok(self.handle_member_function_type(&data, finder)?), + TypeData::Primitive(data) => Ok(self.handle_primitive_type(data, finder)?), + TypeData::Class(data) => Ok(self.handle_class_type(data, finder)?), + TypeData::Member(data) => Ok(self.handle_member_type(data, finder)?), + TypeData::MemberFunction(data) => Ok(self.handle_member_function_type(data, finder)?), TypeData::OverloadedMethod(data) => { - Ok(self.handle_overloaded_method_type(&data, finder)?) + Ok(self.handle_overloaded_method_type(data, finder)?) } - TypeData::Method(data) => Ok(self.handle_method_type(&data, finder)?), - TypeData::StaticMember(data) => Ok(self.handle_static_member_type(&data, finder)?), - TypeData::Nested(data) => Ok(self.handle_nested_type(&data, finder)?), - TypeData::BaseClass(data) => Ok(self.handle_base_class_type(&data, finder)?), + TypeData::Method(data) => Ok(self.handle_method_type(data, finder)?), + TypeData::StaticMember(data) => Ok(self.handle_static_member_type(data, finder)?), + TypeData::Nested(data) => Ok(self.handle_nested_type(data, finder)?), + TypeData::BaseClass(data) => Ok(self.handle_base_class_type(data, finder)?), TypeData::VirtualBaseClass(data) => { - Ok(self.handle_virtual_base_class_type(&data, finder)?) + Ok(self.handle_virtual_base_class_type(data, finder)?) } TypeData::VirtualFunctionTable(data) => { - Ok(self.handle_virtual_function_table_type(&data, finder)?) + Ok(self.handle_virtual_function_table_type(data, finder)?) } TypeData::VirtualTableShape(data) => { - Ok(self.handle_virtual_table_shape_type(&data, finder)?) + Ok(self.handle_virtual_table_shape_type(data, finder)?) } TypeData::VirtualFunctionTablePointer(data) => { - Ok(self.handle_virtual_function_table_pointer_type(&data, finder)?) - } - TypeData::Procedure(data) => Ok(self.handle_procedure_type(&data, finder)?), - TypeData::Pointer(data) => Ok(self.handle_pointer_type(&data, finder)?), - TypeData::Modifier(data) => Ok(self.handle_modifier_type(&data, finder)?), - TypeData::Enumeration(data) => Ok(self.handle_enumeration_type(&data, finder)?), - TypeData::Enumerate(data) => Ok(self.handle_enumerate_type(&data, finder)?), - TypeData::Array(data) => Ok(self.handle_array_type(&data, finder)?), - TypeData::Union(data) => Ok(self.handle_union_type(&data, finder)?), - TypeData::Bitfield(data) => Ok(self.handle_bitfield_type(&data, finder)?), - TypeData::FieldList(data) => Ok(self.handle_field_list_type(&data, finder)?), - TypeData::ArgumentList(data) => Ok(self.handle_argument_list_type(&data, finder)?), - TypeData::MethodList(data) => Ok(self.handle_method_list_type(&data, finder)?), + Ok(self.handle_virtual_function_table_pointer_type(data, finder)?) + } + TypeData::Procedure(data) => Ok(self.handle_procedure_type(data, finder)?), + TypeData::Pointer(data) => Ok(self.handle_pointer_type(data, finder)?), + TypeData::Modifier(data) => Ok(self.handle_modifier_type(data, finder)?), + TypeData::Enumeration(data) => Ok(self.handle_enumeration_type(data, finder)?), + TypeData::Enumerate(data) => Ok(self.handle_enumerate_type(data, finder)?), + TypeData::Array(data) => Ok(self.handle_array_type(data, finder)?), + TypeData::Union(data) => Ok(self.handle_union_type(data, finder)?), + TypeData::Bitfield(data) => Ok(self.handle_bitfield_type(data, finder)?), + TypeData::FieldList(data) => Ok(self.handle_field_list_type(data, finder)?), + TypeData::ArgumentList(data) => Ok(self.handle_argument_list_type(data, finder)?), + TypeData::MethodList(data) => Ok(self.handle_method_list_type(data, finder)?), _ => Err(anyhow!("Unknown typedata")), } } @@ -712,32 +710,25 @@ impl<'a, S: Source<'a> + 'a> PDBParserInstance<'a, S> { // TODO: Pointer suffix is not exposed match data.indirection { Some(Indirection::Near16) => Ok(Some(Box::new(ParsedType::Bare(Type::pointer( - &self.arch, - &base, + &self.arch, &base, ))))), Some(Indirection::Far16) => Ok(Some(Box::new(ParsedType::Bare(Type::pointer( - &self.arch, - &base, + &self.arch, &base, ))))), Some(Indirection::Huge16) => Ok(Some(Box::new(ParsedType::Bare(Type::pointer( - &self.arch, - &base, + &self.arch, &base, ))))), Some(Indirection::Near32) => Ok(Some(Box::new(ParsedType::Bare(Type::pointer( - &self.arch, - &base, + &self.arch, &base, ))))), Some(Indirection::Far32) => Ok(Some(Box::new(ParsedType::Bare(Type::pointer( - &self.arch, - &base, + &self.arch, &base, ))))), Some(Indirection::Near64) => Ok(Some(Box::new(ParsedType::Bare(Type::pointer( - &self.arch, - &base, + &self.arch, &base, ))))), Some(Indirection::Near128) => Ok(Some(Box::new(ParsedType::Bare(Type::pointer( - &self.arch, - &base, + &self.arch, &base, ))))), None => Ok(Some(Box::new(ParsedType::Bare(base)))), } @@ -769,7 +760,7 @@ impl<'a, S: Source<'a> + 'a> PDBParserInstance<'a, S> { ClassKind::Interface => NamedTypeReferenceClass::StructNamedTypeClass, }; return Ok(Some(Box::new(ParsedType::Bare(Type::named_type( - &*NamedTypeReference::new(ntr_class, class_name), + &NamedTypeReference::new(ntr_class, class_name), ))))); } @@ -933,10 +924,7 @@ impl<'a, S: Source<'a> + 'a> PDBParserInstance<'a, S> { } if let Some(mut builder) = bitfield_builder.take() { combined_bitfield_members.push(ParsedMember { - ty: Conf::new( - Type::structure(builder.finalize().as_ref()), - MAX_CONFIDENCE, - ), + ty: Conf::new(Type::structure(builder.finalize().as_ref()), MAX_CONFIDENCE), name: bitfield_name(last_bitfield_offset, last_bitfield_idx), offset: last_bitfield_offset, access: MemberAccess::PublicAccess, @@ -1131,7 +1119,7 @@ impl<'a, S: Source<'a> + 'a> PDBParserInstance<'a, S> { if self.namespace_stack.is_empty() { return Err(anyhow!("Expected class in ns stack")); } - + let mut vt_name = self.namespace_stack.clone(); vt_name.items.push("VTable".to_string()); self.named_types.insert(vt_name.clone(), vt_type.clone()); @@ -1254,10 +1242,8 @@ impl<'a, S: Source<'a> + 'a> PDBParserInstance<'a, S> { { // Return UDT?? // This probably means the return value got pushed to the stack - fancy_return_type = Type::pointer( - &self.arch, - &Conf::new(return_type.clone(), MAX_CONFIDENCE), - ); + fancy_return_type = + Type::pointer(&self.arch, &Conf::new(return_type.clone(), MAX_CONFIDENCE)); fancy_arguments.insert( 0, FunctionParameter::new( @@ -1370,10 +1356,7 @@ impl<'a, S: Source<'a> + 'a> PDBParserInstance<'a, S> { let mut class_name_ns = self.namespace_stack.clone(); class_name_ns.push(data.name.to_string().into()); let ty = self.type_index_to_bare(data.nested_type, finder, false)?; - Ok(Some(Box::new(ParsedType::Named( - class_name_ns, - ty, - )))) + Ok(Some(Box::new(ParsedType::Named(class_name_ns, ty)))) } fn handle_base_class_type( @@ -1535,8 +1518,10 @@ impl<'a, S: Source<'a> + 'a> PDBParserInstance<'a, S> { } if return_stacky { // Stack return via a pointer in the first parameter - fancy_return_type = - Conf::new(Type::pointer(&self.arch, &return_type.clone()), MAX_CONFIDENCE); + fancy_return_type = Conf::new( + Type::pointer(&self.arch, &return_type.clone()), + MAX_CONFIDENCE, + ); fancy_arguments.insert( 0, FunctionParameter::new(fancy_return_type.clone(), "__return".to_string(), None), @@ -1591,8 +1576,7 @@ impl<'a, S: Source<'a> + 'a> PDBParserInstance<'a, S> { if let Some(base) = base { Ok(Some(Box::new(ParsedType::Bare(Type::pointer( - &self.arch, - &base, + &self.arch, &base, ))))) } else { Ok(None) @@ -1638,7 +1622,7 @@ impl<'a, S: Source<'a> + 'a> PDBParserInstance<'a, S> { let ntr_class = NamedTypeReferenceClass::EnumNamedTypeClass; return Ok(Some(Box::new(ParsedType::Bare(Type::named_type( - &*NamedTypeReference::new(ntr_class, enum_name), + &NamedTypeReference::new(ntr_class, enum_name), ))))); } @@ -1736,7 +1720,7 @@ impl<'a, S: Source<'a> + 'a> PDBParserInstance<'a, S> { counts[j] /= new_type.width(); } - new_type = Type::array(&new_type, counts[i] as u64); + new_type = Type::array(&new_type, counts[i]); } Ok(Some(Box::new(ParsedType::Bare(new_type)))) @@ -1766,7 +1750,7 @@ impl<'a, S: Source<'a> + 'a> PDBParserInstance<'a, S> { let ntr_class = NamedTypeReferenceClass::UnionNamedTypeClass; return Ok(Some(Box::new(ParsedType::Bare(Type::named_type( - &*NamedTypeReference::new(ntr_class, union_name), + &NamedTypeReference::new(ntr_class, union_name), ))))); } @@ -1927,10 +1911,7 @@ impl<'a, S: Source<'a> + 'a> PDBParserInstance<'a, S> { )); } else { args.push(FunctionParameter::new( - Conf::new( - Type::pointer(self.arch.as_ref(), &ty), - MAX_CONFIDENCE, - ), + Conf::new(Type::pointer(self.arch.as_ref(), &ty), MAX_CONFIDENCE), "".to_string(), None, )); @@ -1992,9 +1973,10 @@ impl<'a, S: Source<'a> + 'a> PDBParserInstance<'a, S> { ) -> Result>> { let (mut type_, inner) = match self.handle_type_index(index, finder)? { Some(ParsedType::Bare(ty)) => (ty.clone(), None), - Some(ParsedType::Named(name, ty)) => { - (Type::named_type_from_type(name.to_owned(), &ty), Some(ty.clone())) - } + Some(ParsedType::Named(name, ty)) => ( + Type::named_type_from_type(name.to_owned(), ty), + Some(ty.clone()), + ), Some(ParsedType::Procedure(ParsedProcedureType { method_type, .. })) => { (method_type.clone(), Some(method_type.clone())) } @@ -2247,10 +2229,7 @@ impl<'a, S: Source<'a> + 'a> PDBParserInstance<'a, S> { } fn size_can_fit_in_register(size: u64) -> bool { - match size { - 0 | 1 | 2 | 4 | 8 => true, - _ => false, - } + matches!(size, 0 | 1 | 2 | 4 | 8) } // Memoized... because this has gotta be real slow @@ -2278,7 +2257,7 @@ impl<'a, S: Source<'a> + 'a> PDBParserInstance<'a, S> { Ok(fields .fields .into_iter() - .chain(get_fields(cont, finder)?.into_iter()) + .chain(get_fields(cont, finder)?) .collect::>()) } else { Ok(fields.fields) @@ -2337,11 +2316,11 @@ impl<'a, S: Source<'a> + 'a> PDBParserInstance<'a, S> { } // - no base classes - if let Some(_) = c.derived_from { + if c.derived_from.is_some() { return false; } // - no virtual functions - if let Some(_) = c.vtable_shape { + if c.vtable_shape.is_some() { return false; } @@ -2393,7 +2372,7 @@ impl<'a, S: Source<'a> + 'a> PDBParserInstance<'a, S> { _ => {} } } - return true; + true } TypeData::Union(u) => { if u.properties.forward_reference() { @@ -2468,7 +2447,7 @@ impl<'a, S: Source<'a> + 'a> PDBParserInstance<'a, S> { _ => {} } } - return true; + true } _ => false, } diff --git a/plugins/warp/Cargo.toml b/plugins/warp/Cargo.toml index 424840bf7..883321b28 100644 --- a/plugins/warp/Cargo.toml +++ b/plugins/warp/Cargo.toml @@ -9,7 +9,7 @@ crate-type = ["lib", "cdylib"] [dependencies] binaryninja = { path = "../../rust", features = ["rayon"] } # Used to link with for tests. -binaryninjacore-sys = { path = "../../rust/binaryninjacore-sys", optional = true } +binaryninjacore-sys = { path = "../../rust/binaryninjacore-sys" } warp = { git = "https://github.com/Vector35/warp/", rev = "0ee5a6f" } log = "0.4" arboard = "3.4" @@ -24,9 +24,6 @@ ar = { git = "https://github.com/mdsteele/rust-ar" } tempdir = "0.3.7" serde_json = "1.0" -[features] -test = ["dep:binaryninjacore-sys"] - [build-dependencies] cc = "1.1.28" diff --git a/plugins/warp/build.rs b/plugins/warp/build.rs index 18ac92313..39b3238b9 100644 --- a/plugins/warp/build.rs +++ b/plugins/warp/build.rs @@ -1,110 +1,33 @@ -#![allow(unused_imports)] -use std::env; use std::path::PathBuf; -use std::process::Command; - -#[cfg(target_os = "macos")] -static LASTRUN_PATH: (&str, &str) = ("HOME", "Library/Application Support/Binary Ninja/lastrun"); - -#[cfg(target_os = "linux")] -static LASTRUN_PATH: (&str, &str) = ("HOME", ".binaryninja/lastrun"); - -#[cfg(windows)] -static LASTRUN_PATH: (&str, &str) = ("APPDATA", "Binary Ninja\\lastrun"); - -// Check last run location for path to BinaryNinja; Otherwise check the default install locations -fn link_path() -> PathBuf { - use std::io::prelude::*; - use std::io::BufReader; - - let home = PathBuf::from(env::var(LASTRUN_PATH.0).unwrap()); - let lastrun = PathBuf::from(&home).join(LASTRUN_PATH.1); - - std::fs::File::open(lastrun) - .and_then(|f| { - let mut binja_path = String::new(); - let mut reader = BufReader::new(f); - - reader.read_line(&mut binja_path)?; - Ok(PathBuf::from(binja_path.trim())) - }) - .unwrap_or_else(|_| { - #[cfg(target_os = "macos")] - return PathBuf::from("/Applications/Binary Ninja.app/Contents/MacOS"); - - #[cfg(target_os = "linux")] - return home.join("binaryninja"); - - #[cfg(windows)] - return PathBuf::from(env::var("PROGRAMFILES").unwrap()) - .join("Vector35\\BinaryNinja\\"); - }) -} - -#[cfg(feature = "test")] -fn compile_rust(file: PathBuf) -> bool { - let out_dir = std::env::var_os("OUT_DIR").unwrap(); - let rustc = std::env::var_os("RUSTC").unwrap(); - let rustc = rustc.to_str().unwrap(); - let mut rustc = rustc.split('\x1f'); - let mut cmd = Command::new(rustc.next().unwrap()); - cmd.args(rustc) - .arg("--crate-type=rlib") - .arg("--out-dir") - .arg(out_dir) - .arg(file); - cmd.status().expect("failed to invoke rustc").success() -} fn main() { - // Use BINARYNINJADIR first for custom BN builds/configurations (BN devs/build server), fallback on defaults - let link_path = env::var("BINARYNINJADIR") - .map(PathBuf::from) - .unwrap_or_else(|_| link_path()); - let link_path_str = link_path.to_str().unwrap(); + let link_path = std::env::var_os("DEP_BINARYNINJACORE_PATH") + .expect("DEP_BINARYNINJACORE_PATH not specified"); + println!("cargo::rustc-link-lib=dylib=binaryninjacore"); - println!("cargo::rustc-link-search={}", link_path_str); + println!("cargo::rustc-link-search={}", link_path.to_str().unwrap()); + #[cfg(not(target_os = "windows"))] { - println!("cargo::rustc-link-arg=-Wl,-rpath,{0},-L{0}", link_path_str); + println!( + "cargo::rustc-link-arg=-Wl,-rpath,{0},-L{0}", + link_path.to_string_lossy() + ); } - #[cfg(feature = "test")] - { - let out_dir = std::env::var("OUT_DIR").expect("OUT_DIR specified"); - let out_dir_path = PathBuf::from(out_dir); - - // Copy all binaries to OUT_DIR for unit tests. - let bin_dir: PathBuf = "fixtures/bin".into(); - if let Ok(entries) = std::fs::read_dir(bin_dir) { - for entry in entries { - let entry = entry.unwrap(); - let path = entry.path(); - if path.is_file() { - let file_name = path.file_name().unwrap(); - let dest_path = out_dir_path.join(file_name); - std::fs::copy(&path, &dest_path).expect("failed to copy binary to OUT_DIR"); - } - } - } - - // Compile all .c files in fixtures/src directory for unit tests. - let src_dir: PathBuf = "fixtures/src".into(); - if let Ok(entries) = std::fs::read_dir(src_dir) { - for entry in entries { - let entry = entry.unwrap(); - let path = entry.path(); - match path.extension().map(|s| s.to_str().unwrap()) { - Some("c") => { - cc::Build::new() - .file(&path) - .compile(path.file_stem().unwrap().to_str().unwrap()); - } - Some("rs") => { - compile_rust(path); - } - _ => {} - } + let out_dir = std::env::var("OUT_DIR").expect("OUT_DIR specified"); + let out_dir_path = PathBuf::from(out_dir); + + // Copy all binaries to OUT_DIR for unit tests. + let bin_dir: PathBuf = "fixtures/bin".into(); + if let Ok(entries) = std::fs::read_dir(bin_dir) { + for entry in entries { + let entry = entry.unwrap(); + let path = entry.path(); + if path.is_file() { + let file_name = path.file_name().unwrap(); + let dest_path = out_dir_path.join(file_name); + std::fs::copy(&path, &dest_path).expect("failed to copy binary to OUT_DIR"); } } } diff --git a/plugins/warp/fixtures/src/library.c b/plugins/warp/fixtures/src/library.c deleted file mode 100644 index 66a84ad5d..000000000 --- a/plugins/warp/fixtures/src/library.c +++ /dev/null @@ -1,33 +0,0 @@ -#include - -#include "library.h" - -int myFunction(int x) -{ - printf("%d\n", x); - return x; -} - -int recursiveFunc(int x); -int otherFunction(int x) -{ - x += 5; - if (x < 10) return otherFunction(x); - return recursiveFunc(x); -} - -int recursiveFunc(int x) -{ - if (x <= 0) return 0; - return x + otherFunction(x - 1); -} - -struct MyStruct myFunction2(int x) -{ - printf("MyStruct %d\n", x); - struct MyStruct myStruct; - myStruct.a = recursiveFunc(x); - myStruct.b = x * 10; - myStruct.c = "my struct"; - return myStruct; -} \ No newline at end of file diff --git a/plugins/warp/fixtures/src/library.h b/plugins/warp/fixtures/src/library.h deleted file mode 100644 index ec7a6e63f..000000000 --- a/plugins/warp/fixtures/src/library.h +++ /dev/null @@ -1,11 +0,0 @@ -#pragma once - -struct MyStruct { - int a; - int b; - const char* c; - struct MyStruct* d; -}; - -int myFunction(int x); -struct MyStruct myFunction2(int x); \ No newline at end of file diff --git a/plugins/warp/fixtures/src/simple.c b/plugins/warp/fixtures/src/simple.c deleted file mode 100644 index 6e7fbd431..000000000 --- a/plugins/warp/fixtures/src/simple.c +++ /dev/null @@ -1,15 +0,0 @@ -#include - -#include "library.h" - -int simple() { - printf("This is main!\n"); - - printf("calling myFunction\n"); - int returnVal = myFunction(55); - - printf("calling myFunction2\n"); - struct MyStruct myStruct = myFunction2(returnVal); - - return myStruct.b; -} \ No newline at end of file diff --git a/plugins/warp/src/bin/sigem.rs b/plugins/warp/src/bin/sigem.rs index ed70507e7..22f926d47 100644 --- a/plugins/warp/src/bin/sigem.rs +++ b/plugins/warp/src/bin/sigem.rs @@ -47,7 +47,6 @@ struct Args { /// The external debug information file to use #[arg(short, long)] debug_info: Option, - // TODO: Add a file filter and default to filter out files starting with "." } @@ -79,7 +78,7 @@ fn main() { // TODO: After analysis finishes for a file we should save off the bndb to another directory called the bndb cache // TODO: This cache should be used before opening a file for first analysis. - + // TODO: We should resolve the path to something sensible in cases where user is passing CWD. // If no output file was given, just prepend binary with extension sbin let output_file = args diff --git a/plugins/warp/src/cache.rs b/plugins/warp/src/cache.rs index ed1b78443..ccd3de66a 100644 --- a/plugins/warp/src/cache.rs +++ b/plugins/warp/src/cache.rs @@ -2,6 +2,7 @@ use crate::convert::{from_bn_symbol, from_bn_type_internal}; use crate::{build_function, function_guid}; use binaryninja::architecture::Architecture; use binaryninja::binaryview::{BinaryView, BinaryViewExt}; +use binaryninja::confidence::MAX_CONFIDENCE; use binaryninja::function::Function as BNFunction; use binaryninja::llil::{FunctionMutability, NonSSA, NonSSAVariant}; use binaryninja::rc::Guard; @@ -17,7 +18,6 @@ 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,8 +369,12 @@ impl TypeRefCache { Some(cache) => cache.to_owned(), None => match type_ref.target(view) { Some(raw_ty) => { - let computed_ty = - ComputedType::new(from_bn_type_internal(view, visited_refs, &raw_ty, MAX_CONFIDENCE)); + let computed_ty = 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 2f76031ca..a37ad7766 100644 --- a/plugins/warp/src/convert.rs +++ b/plugins/warp/src/convert.rs @@ -4,9 +4,9 @@ use binaryninja::architecture::Architecture as BNArchitecture; use binaryninja::architecture::ArchitectureExt; use binaryninja::binaryview::{BinaryView, BinaryViewExt}; use binaryninja::callingconvention::CallingConvention as BNCallingConvention; +use binaryninja::confidence::{Conf as BNConf, MAX_CONFIDENCE}; use binaryninja::rc::Ref as BNRef; use binaryninja::symbol::{Symbol as BNSymbol, SymbolType as BNSymbolType}; -use binaryninja::confidence::{Conf as BNConf, MAX_CONFIDENCE}; use binaryninja::types::{ BaseStructure as BNBaseStructure, EnumerationBuilder as BNEnumerationBuilder, FunctionParameter as BNFunctionParameter, MemberAccess as BNMemberAccess, MemberAccess, @@ -629,7 +629,7 @@ mod tests { .collect(); assert_eq!(types_len, converted_types.len()); // Hold on to a reference to the core to prevent view getting dropped in worker thread. - let core_ref = inital_bv + let _core_ref = inital_bv .functions() .iter() .next() @@ -650,7 +650,7 @@ mod tests { .collect(); assert_eq!(types_len, converted_types.len()); // Hold on to a reference to the core to prevent view getting dropped in worker thread. - let core_ref = second_bv + let _core_ref = second_bv .functions() .iter() .next() diff --git a/plugins/warp/src/lib.rs b/plugins/warp/src/lib.rs index c805a712f..f269df6ea 100644 --- a/plugins/warp/src/lib.rs +++ b/plugins/warp/src/lib.rs @@ -1,8 +1,13 @@ +use crate::cache::{ + cached_adjacency_constraints, cached_call_site_constraints, cached_function_guid, +}; +use crate::convert::{from_bn_symbol, from_bn_type}; use binaryninja::architecture::{ Architecture, ImplicitRegisterExtend, Register as BNRegister, RegisterInfo, }; use binaryninja::basicblock::BasicBlock as BNBasicBlock; use binaryninja::binaryview::BinaryViewExt; +use binaryninja::confidence::MAX_CONFIDENCE; use binaryninja::function::{Function as BNFunction, NativeBlock}; use binaryninja::llil; use binaryninja::llil::{ @@ -14,11 +19,6 @@ 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, -}; -use crate::convert::{from_bn_symbol, from_bn_type}; pub mod cache; pub mod convert; diff --git a/plugins/warp/src/snapshots/warp_ninja__tests__insta_signatures.snap.new b/plugins/warp/src/snapshots/warp_ninja__tests__insta_signatures.snap.new new file mode 100644 index 000000000..293a051d8 --- /dev/null +++ b/plugins/warp/src/snapshots/warp_ninja__tests__insta_signatures.snap.new @@ -0,0 +1,40 @@ +--- +source: plugins/warp/src/lib.rs +assertion_line: 208 +expression: functions +--- +[ + FunctionGUID { + guid: 067161e1-6070-5366-97d1-6d5465b03307, + }, + FunctionGUID { + guid: 067161e1-6070-5366-97d1-6d5465b03307, + }, + FunctionGUID { + guid: 4d32bdfe-5c39-5fc1-8636-1f8b540a7d50, + }, + FunctionGUID { + guid: 623a8338-34d6-5a6e-8c4e-36a1a071117e, + }, + FunctionGUID { + guid: 76e8d9e9-3179-5b05-b7a9-810f6bb701cf, + }, + FunctionGUID { + guid: 7b6feaff-8729-5eac-bf50-984b04851892, + }, + FunctionGUID { + guid: 8c2a884f-92b5-5792-ae72-9d108849391a, + }, + FunctionGUID { + guid: 9a3e480c-5ebd-5278-8e33-4a6e982167fb, + }, + FunctionGUID { + guid: b2b44e31-d6d2-520b-9c36-52573b6d725c, + }, + FunctionGUID { + guid: b2d9afe0-7ea5-5e39-863b-33772784cfb8, + }, + FunctionGUID { + guid: e637db6e-c078-5b26-a155-c60bede9f626, + }, +] diff --git a/rust/src/platform.rs b/rust/src/platform.rs index 04e9618fa..7c77a38dc 100644 --- a/rust/src/platform.rs +++ b/rust/src/platform.rs @@ -264,6 +264,9 @@ impl Platform { Array::new(handles, count, ()) } } + + // TODO: system_calls + // TODO: add a helper function to define a system call (platform function with a specific type) // TODO: Documentation, specifically how this differs from the TypeParser impl pub fn preprocess_source( diff --git a/rust/src/typecontainer.rs b/rust/src/typecontainer.rs index 5453e3d84..6cf18ff5e 100644 --- a/rust/src/typecontainer.rs +++ b/rust/src/typecontainer.rs @@ -89,6 +89,7 @@ impl TypeContainer { .into_iter() .map(|t| { let t = t.into(); + // Leaked to be freed after the call to core. ( QualifiedName::into_raw(t.name), unsafe { Ref::into_raw(t.ty) }.handle, @@ -99,7 +100,7 @@ impl TypeContainer { let mut result_names = std::ptr::null_mut(); let mut result_ids = std::ptr::null_mut(); let mut result_count = 0; - unsafe { + let success = unsafe { BNTypeContainerAddTypes( self.handle.as_ptr(), raw_names.as_ptr(), @@ -111,7 +112,14 @@ impl TypeContainer { &mut result_ids, &mut result_count, ) + }; + for name in raw_names { + QualifiedName::free_raw(name); } + for ty in raw_types { + let _ = unsafe { Type::ref_from_raw(ty) } ; + } + success } pub fn add_types_with_progress(&self, types: I, mut progress: F) -> bool @@ -125,6 +133,7 @@ impl TypeContainer { .into_iter() .map(|t| { let t = t.into(); + // Leaked to be freed after the call to core. ( QualifiedName::into_raw(t.name), unsafe { Ref::into_raw(t.ty) }.handle, @@ -135,7 +144,7 @@ impl TypeContainer { let mut result_names = std::ptr::null_mut(); let mut result_ids = std::ptr::null_mut(); let mut result_count = 0; - unsafe { + let success = unsafe { BNTypeContainerAddTypes( self.handle.as_ptr(), raw_names.as_ptr(), @@ -147,7 +156,14 @@ impl TypeContainer { &mut result_ids, &mut result_count, ) + }; + for name in raw_names { + QualifiedName::free_raw(name); } + for ty in raw_types { + let _ = unsafe { Type::ref_from_raw(ty) } ; + } + success } /// Rename a type in the Type Container. All references to this type will be updated diff --git a/rust/src/types.rs b/rust/src/types.rs index 73d79c3b2..b5a7d3de3 100644 --- a/rust/src/types.rs +++ b/rust/src/types.rs @@ -1847,13 +1847,15 @@ impl NamedTypeReference { /// the core will do the id stuff for you. pub fn new>(type_class: NamedTypeReferenceClass, name: T) -> Ref { let mut raw_name = QualifiedName::into_raw(name.into()); - unsafe { + let result = unsafe { Self::ref_from_raw(BNCreateNamedType( type_class, std::ptr::null(), &mut raw_name, )) - } + }; + QualifiedName::free_raw(raw_name); + result } /// Create an NTR to a type with an existing type id, which generally means it came directly @@ -1868,14 +1870,15 @@ impl NamedTypeReference { ) -> Ref { let type_id = type_id.into_bytes_with_nul(); let mut raw_name = QualifiedName::into_raw(name.into()); - - unsafe { + let result = unsafe { Self::ref_from_raw(BNCreateNamedType( type_class, type_id.as_ref().as_ptr() as _, &mut raw_name, )) - } + }; + QualifiedName::free_raw(raw_name); + result } pub fn name(&self) -> QualifiedName { diff --git a/rust/tests/platform.rs b/rust/tests/platform.rs index 85f9c1aea..cce965bac 100644 --- a/rust/tests/platform.rs +++ b/rust/tests/platform.rs @@ -28,7 +28,5 @@ fn test_platform_types(_session: &Session) { #[rstest] fn test_platform_calling_conventions(_session: &Session) { let platform = Platform::by_name("windows-x86_64").expect("windows-x86_64 exists"); - for cc in platform.calling_conventions().iter() { - println!("{:#?}", cc); - } + assert_eq!(platform.calling_conventions().len(), 1); } diff --git a/rust/tests/typecontainer.rs b/rust/tests/typecontainer.rs index d0dae9019..5c01879d7 100644 --- a/rust/tests/typecontainer.rs +++ b/rust/tests/typecontainer.rs @@ -79,7 +79,7 @@ fn test_immutable_container(_session: &Session, platform: &Platform) { !plat_type_container.is_mutable(), "Platform should NOT be mutable!" ); - assert_ne!(platform.types().len(), 0); + assert_ne!(platform.types().len(), 0, "Something deleted all the platform types!"); let type_ids = plat_type_container.type_ids().unwrap(); let first_type_id = type_ids.iter().next().unwrap(); // Platform type containers are immutable so these should be false! diff --git a/rust/tests/types.rs b/rust/tests/types.rs index 856d8b21a..9244e9d35 100644 --- a/rust/tests/types.rs +++ b/rust/tests/types.rs @@ -1,5 +1,5 @@ use binaryninja::headless::Session; -use binaryninja::types::Type; +use binaryninja::types::{MemberAccess, MemberScope, StructureBuilder, StructureMember, Type}; use rstest::*; #[fixture] @@ -13,3 +13,38 @@ fn test_type_to_string(_session: &Session) { let test_type = Type::int(4, true); assert_eq!(test_type.to_string(), "int32_t".to_string()); } + +#[rstest] +fn test_structure_builder(_session: &Session) { + let mut builder = StructureBuilder::new(); + builder.insert( + &Type::int(4, true), + "field_1", + 0, + false, + MemberAccess::PrivateAccess, + MemberScope::FriendScope, + ); + builder.insert( + &Type::float(8), + "field_2", + 4, + false, + MemberAccess::PublicAccess, + MemberScope::NoScope, + ); + + let structure = builder.finalize(); + let members = structure.members(); + assert_eq!(members.len(), 2); + assert_eq!( + members[0], + StructureMember { + name: "field_1".to_string(), + ty: Type::int(4, true).into(), + offset: 0, + access: MemberAccess::PrivateAccess, + scope: MemberScope::FriendScope, + } + ); +} From 059baa5e0e95ea689f0af5a136a66271b747b413 Mon Sep 17 00:00:00 2001 From: Mason Reed Date: Tue, 14 Jan 2025 12:28:31 -0500 Subject: [PATCH 44/93] Run rust tests in CI This commit also coincides with the creation of the "testing" environment which exposes a BN_SERIAL secret for pulling a headless Binary Ninja --- .github/workflows/rust.yml | 23 ++++++++++++++++++++--- 1 file changed, 20 insertions(+), 3 deletions(-) diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index b1e69106b..60186ab4b 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -10,7 +10,25 @@ on: - 'rust/**' jobs: - # TODO: Cargo test (we would need to pull in binary ninja) + # Check that code compiles and tests pass + test: + # The testing environment is used to access the BN_SERIAL secret. + environment: testing + name: cargo test + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + # Pull in Binary Ninja + - name: Setup Binary Ninja + uses: Vector35/setup-binary-ninja@v1-beta + with: + license: '${{ secrets.BN_SERIAL }}' + python-support: 'false' + - uses: actions-rust-lang/setup-rust-toolchain@v1 + - name: Test + # For now, we run the tests single threaded, there are some data races in core around platform types + run: cargo test --all-features -- --test-threads=1 + # Check lints with clippy clippy: name: cargo clippy @@ -24,6 +42,7 @@ jobs: - name: Clippy Check uses: clechasseur/rs-clippy-check@v4 with: + # We do not run clippy on plugins. working-directory: ./rust args: --all-features @@ -39,8 +58,6 @@ jobs: components: rustfmt - name: Rustfmt Check uses: actions-rust-lang/rustfmt@v1 - with: - manifest-path: ./rust/Cargo.toml # Check spelling with typos spelling: From bc8cdca5f6eb63fd32bac1fd3c9c65af78d90a6b Mon Sep 17 00:00:00 2001 From: Mason Reed Date: Tue, 14 Jan 2025 12:44:35 -0500 Subject: [PATCH 45/93] Install missing wayland dependency in github CI Apparently its needed for linux file picker for the WARP integration --- .github/workflows/rust.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index 60186ab4b..7878ce0d7 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -18,6 +18,9 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 + # We need to add wayland as it's used for file picker in the WARP integration + - name: Install system dependencies + run: sudo apt-get install libwayland-dev # Pull in Binary Ninja - name: Setup Binary Ninja uses: Vector35/setup-binary-ninja@v1-beta From f35517d75b1becd589c25a596b578afda1038cfd Mon Sep 17 00:00:00 2001 From: Mason Reed Date: Tue, 14 Jan 2025 12:55:41 -0500 Subject: [PATCH 46/93] Set the BINARYNINJADIR so rust can find binaryninjacore in CI The chances of this working are low --- .github/workflows/rust.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index 7878ce0d7..e6737ea5d 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -23,6 +23,7 @@ jobs: run: sudo apt-get install libwayland-dev # Pull in Binary Ninja - name: Setup Binary Ninja + id: setup-binja uses: Vector35/setup-binary-ninja@v1-beta with: license: '${{ secrets.BN_SERIAL }}' @@ -31,6 +32,8 @@ jobs: - name: Test # For now, we run the tests single threaded, there are some data races in core around platform types run: cargo test --all-features -- --test-threads=1 + env: + BINARYNINJADIR: ${{ steps.setup-binja.outputs.install-path }} # Check lints with clippy clippy: From 8b677e7857c44b368325b94a4477bc053f8c1f27 Mon Sep 17 00:00:00 2001 From: Mason Reed Date: Tue, 14 Jan 2025 13:02:22 -0500 Subject: [PATCH 47/93] Misc remove unused dependency --- plugins/pdb-ng/src/lib.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/plugins/pdb-ng/src/lib.rs b/plugins/pdb-ng/src/lib.rs index 75d2e21cd..b6a905478 100644 --- a/plugins/pdb-ng/src/lib.rs +++ b/plugins/pdb-ng/src/lib.rs @@ -32,7 +32,7 @@ use binaryninja::interaction::{MessageBoxButtonResult, MessageBoxButtonSet}; use binaryninja::logger::Logger; use binaryninja::settings::Settings; use binaryninja::string::BnString; -use binaryninja::{add_optional_plugin_dependency, interaction, user_directory}; +use binaryninja::{interaction, user_directory}; use parser::PDBParserInstance; /// PDB Parser!! @@ -675,6 +675,7 @@ impl CustomDebugInfoParser for PDBParser { #[cfg(not(feature = "demo"))] #[no_mangle] pub extern "C" fn CorePluginDependencies() { + use binaryninja::add_optional_plugin_dependency; add_optional_plugin_dependency("view_pe"); } From 3d1cfffa61be9525936336a84dff887bcd80ecff Mon Sep 17 00:00:00 2001 From: Mason Reed Date: Tue, 14 Jan 2025 13:04:48 -0500 Subject: [PATCH 48/93] Rust misc formatting fixes --- rust/src/platform.rs | 2 +- rust/src/typecontainer.rs | 4 ++-- rust/tests/typecontainer.rs | 6 +++++- 3 files changed, 8 insertions(+), 4 deletions(-) diff --git a/rust/src/platform.rs b/rust/src/platform.rs index 7c77a38dc..a192dfa75 100644 --- a/rust/src/platform.rs +++ b/rust/src/platform.rs @@ -264,7 +264,7 @@ impl Platform { Array::new(handles, count, ()) } } - + // TODO: system_calls // TODO: add a helper function to define a system call (platform function with a specific type) diff --git a/rust/src/typecontainer.rs b/rust/src/typecontainer.rs index 6cf18ff5e..6a6170779 100644 --- a/rust/src/typecontainer.rs +++ b/rust/src/typecontainer.rs @@ -117,7 +117,7 @@ impl TypeContainer { QualifiedName::free_raw(name); } for ty in raw_types { - let _ = unsafe { Type::ref_from_raw(ty) } ; + let _ = unsafe { Type::ref_from_raw(ty) }; } success } @@ -161,7 +161,7 @@ impl TypeContainer { QualifiedName::free_raw(name); } for ty in raw_types { - let _ = unsafe { Type::ref_from_raw(ty) } ; + let _ = unsafe { Type::ref_from_raw(ty) }; } success } diff --git a/rust/tests/typecontainer.rs b/rust/tests/typecontainer.rs index 5c01879d7..7937de8b1 100644 --- a/rust/tests/typecontainer.rs +++ b/rust/tests/typecontainer.rs @@ -79,7 +79,11 @@ fn test_immutable_container(_session: &Session, platform: &Platform) { !plat_type_container.is_mutable(), "Platform should NOT be mutable!" ); - assert_ne!(platform.types().len(), 0, "Something deleted all the platform types!"); + assert_ne!( + platform.types().len(), + 0, + "Something deleted all the platform types!" + ); let type_ids = plat_type_container.type_ids().unwrap(); let first_type_id = type_ids.iter().next().unwrap(); // Platform type containers are immutable so these should be false! From 5b36220435cd5bfa81f81c711598583e969fabd7 Mon Sep 17 00:00:00 2001 From: Mason Reed Date: Tue, 14 Jan 2025 15:15:19 -0500 Subject: [PATCH 49/93] Improve initialization in rust headless scripts Provide sensible errors and validation to rust headless scripts, solves https://github.com/Vector35/binaryninja-api/issues/5796 --- plugins/pdb-ng/src/lib.rs | 21 ++---- plugins/warp/benches/convert.rs | 2 +- plugins/warp/benches/function.rs | 2 +- plugins/warp/benches/guid.rs | 2 +- plugins/warp/src/bin/sigem.rs | 6 +- plugins/warp/src/convert.rs | 2 +- plugins/warp/src/lib.rs | 6 +- rust/Cargo.toml | 1 + rust/examples/decompile.rs | 3 +- rust/examples/demangler.rs | 3 +- rust/examples/flowgraph.rs | 3 +- rust/examples/hlil.rs | 3 +- rust/examples/mlil.rs | 3 +- rust/examples/simple.rs | 3 +- rust/examples/type_printer.rs | 3 +- rust/examples/workflow.rs | 3 +- rust/src/enterprise.rs | 61 +++++++++-------- rust/src/headless.rs | 110 +++++++++++++++++++++++-------- rust/src/lib.rs | 66 +++++++++++-------- rust/tests/demangler.rs | 2 +- rust/tests/mainthread.rs | 2 +- rust/tests/platform.rs | 2 +- rust/tests/project.rs | 2 +- rust/tests/typearchive.rs | 2 +- rust/tests/typecontainer.rs | 2 +- rust/tests/typeparser.rs | 2 +- rust/tests/types.rs | 2 +- 27 files changed, 198 insertions(+), 121 deletions(-) diff --git a/plugins/pdb-ng/src/lib.rs b/plugins/pdb-ng/src/lib.rs index b6a905478..d7faef1f5 100644 --- a/plugins/pdb-ng/src/lib.rs +++ b/plugins/pdb-ng/src/lib.rs @@ -91,20 +91,13 @@ fn active_local_cache(view: Option<&BinaryView>) -> Result { .get_string("pdb.files.localStoreAbsolute", view, None) .to_string(); if local_store_path.is_empty() { - local_store_path = match user_directory() { - Ok(mut dir) => { - dir.push( - Settings::new("") - .get_string("pdb.files.localStoreRelative", view, None) - .to_string(), - ); - match dir.to_str() { - Some(s) => s.to_string(), - _ => "".to_string(), - } - } - _ => "".to_string(), - }; + let relative_local_store = Settings::new("") + .get_string("pdb.files.localStoreRelative", view, None) + .to_string(); + local_store_path = user_directory() + .join(relative_local_store) + .to_string_lossy() + .to_string(); } if !local_store_path.is_empty() { Ok(local_store_path) diff --git a/plugins/warp/benches/convert.rs b/plugins/warp/benches/convert.rs index 49a2df011..c06092719 100644 --- a/plugins/warp/benches/convert.rs +++ b/plugins/warp/benches/convert.rs @@ -5,7 +5,7 @@ use std::path::PathBuf; use warp_ninja::convert::from_bn_type; pub fn type_conversion_benchmark(c: &mut Criterion) { - let session = Session::new(); + let session = Session::new().expect("Failed to initialize session"); let out_dir = env!("OUT_DIR").parse::().unwrap(); for entry in std::fs::read_dir(out_dir).expect("Failed to read OUT_DIR") { let entry = entry.expect("Failed to read directory entry"); diff --git a/plugins/warp/benches/function.rs b/plugins/warp/benches/function.rs index 487a33781..6aeea6f42 100644 --- a/plugins/warp/benches/function.rs +++ b/plugins/warp/benches/function.rs @@ -6,7 +6,7 @@ use warp_ninja::build_function; use warp_ninja::cache::FunctionCache; pub fn function_benchmark(c: &mut Criterion) { - let session = Session::new(); + let session = Session::new().expect("Failed to initialize session"); let bv = session.load(env!("TEST_BIN_LIBRARY_OBJ")).unwrap(); let functions = bv.functions(); assert_eq!(functions.len(), 6); diff --git a/plugins/warp/benches/guid.rs b/plugins/warp/benches/guid.rs index 04f856a7b..f9f80c95d 100644 --- a/plugins/warp/benches/guid.rs +++ b/plugins/warp/benches/guid.rs @@ -4,7 +4,7 @@ use criterion::{criterion_group, criterion_main, Criterion}; use warp_ninja::function_guid; pub fn guid_benchmark(c: &mut Criterion) { - let session = Session::new(); + let session = Session::new().expect("Failed to initialize session"); let bv = session.load(env!("TEST_BIN_LIBRARY_OBJ")).unwrap(); let functions = bv.functions(); assert_eq!(functions.len(), 6); diff --git a/plugins/warp/src/bin/sigem.rs b/plugins/warp/src/bin/sigem.rs index 22f926d47..097bc0ef3 100644 --- a/plugins/warp/src/bin/sigem.rs +++ b/plugins/warp/src/bin/sigem.rs @@ -92,7 +92,8 @@ fn main() { } log::debug!("Starting Binary Ninja session..."); - let _headless_session = binaryninja::headless::Session::new(); + let _headless_session = + binaryninja::headless::Session::new().expect("Failed to initialize session"); // Adjust the amount of worker threads so that we can actually free BinaryViews. let bn_settings = Settings::new(""); @@ -258,7 +259,8 @@ mod tests { env_logger::init(); // TODO: Store oracles here to get more out of this test. let out_dir = env!("OUT_DIR").parse::().unwrap(); - let _headless_session = binaryninja::headless::Session::new(); + let _headless_session = + binaryninja::headless::Session::new().expect("Failed to initialize session"); let bn_settings = Settings::new(""); let settings = default_settings(&bn_settings); for entry in std::fs::read_dir(out_dir).expect("Failed to read OUT_DIR") { diff --git a/plugins/warp/src/convert.rs b/plugins/warp/src/convert.rs index a37ad7766..b96260866 100644 --- a/plugins/warp/src/convert.rs +++ b/plugins/warp/src/convert.rs @@ -582,7 +582,7 @@ mod tests { static INIT: OnceLock = OnceLock::new(); fn get_session<'a>() -> &'a Session { - INIT.get_or_init(|| Session::new()) + INIT.get_or_init(|| Session::new().expect("Failed to initialize session")) } #[test] diff --git a/plugins/warp/src/lib.rs b/plugins/warp/src/lib.rs index f269df6ea..d1f029e7f 100644 --- a/plugins/warp/src/lib.rs +++ b/plugins/warp/src/lib.rs @@ -28,7 +28,7 @@ mod plugin; pub fn core_signature_dir() -> PathBuf { // Get core signatures for the given platform - let install_dir = binaryninja::install_directory().unwrap(); + let install_dir = binaryninja::install_directory(); // macOS core dir is separate from the install dir. #[cfg(target_os = "macos")] let core_dir = install_dir.parent().unwrap().join("Resources"); @@ -38,7 +38,7 @@ pub fn core_signature_dir() -> PathBuf { } pub fn user_signature_dir() -> PathBuf { - binaryninja::user_directory().unwrap().join("signatures/") + binaryninja::user_directory().join("signatures/") } pub fn build_function( @@ -185,7 +185,7 @@ mod tests { fn get_session<'a>() -> &'a Session { // TODO: This is not shared between other test modules, should still be fine (mutex in core now). - INIT.get_or_init(|| Session::new()) + INIT.get_or_init(|| Session::new().expect("Failed to initialize session")) } #[test] diff --git a/rust/Cargo.toml b/rust/Cargo.toml index 46c557615..16437b968 100644 --- a/rust/Cargo.toml +++ b/rust/Cargo.toml @@ -14,6 +14,7 @@ no_exports = [] log = { version = "0.4", features = ["std"] } rayon = { version = "1.8", optional = true } binaryninjacore-sys = { path = "binaryninjacore-sys" } +thiserror = "2.0" [dev-dependencies] rstest = "0.24.0" diff --git a/rust/examples/decompile.rs b/rust/examples/decompile.rs index 2dc7da0d4..a79f99574 100644 --- a/rust/examples/decompile.rs +++ b/rust/examples/decompile.rs @@ -31,7 +31,8 @@ pub fn main() { println!("Starting session..."); // This loads all the core architecture, platform, etc plugins - let headless_session = binaryninja::headless::Session::new(); + let headless_session = + binaryninja::headless::Session::new().expect("Failed to initialize session"); println!("Loading binary..."); let bv = headless_session diff --git a/rust/examples/demangler.rs b/rust/examples/demangler.rs index 9a1af4f99..8b40c3545 100644 --- a/rust/examples/demangler.rs +++ b/rust/examples/demangler.rs @@ -28,7 +28,8 @@ impl CustomDemangler for TestDemangler { fn main() { println!("Starting session..."); // This loads all the core architecture, platform, etc plugins - let _headless_session = binaryninja::headless::Session::new(); + let _headless_session = + binaryninja::headless::Session::new().expect("Failed to initialize session"); println!("Registering demangler..."); Demangler::register("Test", TestDemangler); diff --git a/rust/examples/flowgraph.rs b/rust/examples/flowgraph.rs index 2117e92fc..8ef35cbda 100644 --- a/rust/examples/flowgraph.rs +++ b/rust/examples/flowgraph.rs @@ -41,7 +41,8 @@ fn test_graph(view: &BinaryView) { fn main() { println!("Starting session..."); // This loads all the core architecture, platform, etc plugins - let headless_session = binaryninja::headless::Session::new(); + let headless_session = + binaryninja::headless::Session::new().expect("Failed to initialize session"); println!("Loading binary..."); let bv = headless_session diff --git a/rust/examples/hlil.rs b/rust/examples/hlil.rs index 9e2bfd49c..ae06014b3 100644 --- a/rust/examples/hlil.rs +++ b/rust/examples/hlil.rs @@ -3,7 +3,8 @@ use binaryninja::binaryview::{BinaryViewBase, BinaryViewExt}; fn main() { println!("Starting session..."); // This loads all the core architecture, platform, etc plugins - let headless_session = binaryninja::headless::Session::new(); + let headless_session = + binaryninja::headless::Session::new().expect("Failed to initialize session"); println!("Loading binary..."); let bv = headless_session diff --git a/rust/examples/mlil.rs b/rust/examples/mlil.rs index c3a477dc9..7b9a83843 100644 --- a/rust/examples/mlil.rs +++ b/rust/examples/mlil.rs @@ -3,7 +3,8 @@ use binaryninja::binaryview::{BinaryViewBase, BinaryViewExt}; fn main() { println!("Starting session..."); // This loads all the core architecture, platform, etc plugins - let headless_session = binaryninja::headless::Session::new(); + let headless_session = + binaryninja::headless::Session::new().expect("Failed to initialize session"); println!("Loading binary..."); let bv = headless_session diff --git a/rust/examples/simple.rs b/rust/examples/simple.rs index 568e7b17c..7ca3a5b17 100644 --- a/rust/examples/simple.rs +++ b/rust/examples/simple.rs @@ -4,7 +4,8 @@ use binaryninja::binaryview::{BinaryViewBase, BinaryViewExt}; fn main() { println!("Starting session..."); // This loads all the core architecture, platform, etc plugins - let headless_session = binaryninja::headless::Session::new(); + let headless_session = + binaryninja::headless::Session::new().expect("Failed to initialize session"); println!("Loading binary..."); let bv = headless_session diff --git a/rust/examples/type_printer.rs b/rust/examples/type_printer.rs index 2625a8b75..104aecd42 100644 --- a/rust/examples/type_printer.rs +++ b/rust/examples/type_printer.rs @@ -4,7 +4,8 @@ use binaryninja::types::{MemberAccess, MemberScope, Structure, StructureMember, fn main() { println!("Starting session..."); // This loads all the core architecture, platform, etc plugins - let headless_session = binaryninja::headless::Session::new(); + let headless_session = + binaryninja::headless::Session::new().expect("Failed to initialize session"); println!("Loading binary..."); let bv = headless_session diff --git a/rust/examples/workflow.rs b/rust/examples/workflow.rs index 189b14968..ec46a233c 100644 --- a/rust/examples/workflow.rs +++ b/rust/examples/workflow.rs @@ -46,7 +46,8 @@ fn example_activity(analysis_context: &AnalysisContext) { pub fn main() { println!("Starting session..."); // This loads all the core architecture, platform, etc plugins - let headless_session = binaryninja::headless::Session::new(); + let headless_session = + binaryninja::headless::Session::new().expect("Failed to initialize session"); println!("Registering workflow..."); let meta_workflow = Workflow::new_from_copy("core.function.metaAnalysis"); diff --git a/rust/src/enterprise.rs b/rust/src/enterprise.rs index 092733843..0e131afad 100644 --- a/rust/src/enterprise.rs +++ b/rust/src/enterprise.rs @@ -1,52 +1,55 @@ -use std::marker::PhantomData; -use std::time::{Duration, SystemTime, UNIX_EPOCH}; - use crate::rc::Array; use crate::string::{BnStrCompatible, BnString}; - -#[derive(Debug)] -pub struct EnterpriseCheckoutError(pub String); - -impl std::fmt::Display for EnterpriseCheckoutError { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "{}", self.0) - } +use std::marker::PhantomData; +use std::time::{Duration, SystemTime, UNIX_EPOCH}; +use thiserror::Error; + +#[derive(Error, Debug)] +pub enum EnterpriseCheckoutError { + #[error("enterprise server returned error: {0}")] + ServerError(String), + #[error("no username set for credential authentication")] + NoUsername, + #[error("no password set for credential authentication")] + NoPassword, + #[error("failed to authenticate with username and password")] + NotAuthenticated, + #[error("failed to refresh expired license")] + RefreshExpiredLicenseFailed, } -impl std::error::Error for EnterpriseCheckoutError {} - pub fn checkout_license(duration: Duration) -> Result<(), EnterpriseCheckoutError> { if crate::is_ui_enabled() { + // We only need to check out a license if running headlessly. return Ok(()); } #[allow(clippy::collapsible_if)] if !is_server_initialized() { + // We need to first initialize the server. if !initialize_server() && is_server_floating_license() { - return Err(EnterpriseCheckoutError(server_last_error().to_string())); + let last_error = server_last_error().to_string(); + return Err(EnterpriseCheckoutError::ServerError(last_error)); } } if is_server_floating_license() { if !is_server_connected() && !connect_server() { - return Err(EnterpriseCheckoutError(server_last_error().to_string())); + let last_error = server_last_error().to_string(); + return Err(EnterpriseCheckoutError::ServerError(last_error)); } #[allow(clippy::collapsible_if)] if !is_server_authenticated() { + // We have yet to authenticate with the server, we should try all available authentication methods. if !authenticate_server_with_method("Keychain", false) { - let Some(username) = std::env::var("BN_ENTERPRISE_USERNAME").ok() else { - return Err(EnterpriseCheckoutError("BN_ENTERPRISE_USERNAME not set when attempting to authenticate with credentials".to_string())); - }; - let Some(password) = std::env::var("BN_ENTERPRISE_PASSWORD").ok() else { - return Err(EnterpriseCheckoutError("BN_ENTERPRISE_PASSWORD not set when attempting to authenticate with credentials".to_string())); - }; + // We could not authenticate with the system keychain, we should try with credentials. + let username = std::env::var("BN_ENTERPRISE_USERNAME") + .map_err(|_| EnterpriseCheckoutError::NoUsername)?; + let password = std::env::var("BN_ENTERPRISE_PASSWORD") + .map_err(|_| EnterpriseCheckoutError::NoPassword)?; if !authenticate_server_with_credentials(username, password, true) { - let failed_message = "Could not checkout a license: Not authenticated. Try one of the following: \n \ - - Log in and check out a license for an extended time\n \ - - Set BN_ENTERPRISE_USERNAME and BN_ENTERPRISE_PASSWORD environment variables\n \ - - Use binaryninja::enterprise::{authenticate_server_with_method OR authenticate_server_with_credentials} in your code"; - return Err(EnterpriseCheckoutError(failed_message.to_string())); + return Err(EnterpriseCheckoutError::NotAuthenticated); } } } @@ -56,10 +59,9 @@ pub fn checkout_license(duration: Duration) -> Result<(), EnterpriseCheckoutErro if !is_server_license_still_activated() || (!is_server_floating_license() && crate::license_expiration_time() < SystemTime::now()) { + // If the license is expired we should refresh the license. if !update_server_license(duration) { - return Err(EnterpriseCheckoutError( - "Failed to refresh expired license".to_string(), - )); + return Err(EnterpriseCheckoutError::RefreshExpiredLicenseFailed); } } @@ -68,6 +70,7 @@ pub fn checkout_license(duration: Duration) -> Result<(), EnterpriseCheckoutErro pub fn release_license() { if !crate::is_ui_enabled() { + // We should only release the license if we are running headlessly. release_server_license(); } } diff --git a/rust/src/headless.rs b/rust/src/headless.rs index 269bd0cd4..c7d6c824b 100644 --- a/rust/src/headless.rs +++ b/rust/src/headless.rs @@ -13,9 +13,11 @@ // limitations under the License. use crate::{ - binaryview, bundled_plugin_directory, is_main_thread, rc, set_bundled_plugin_directory, - string::IntoJson, + binaryview, bundled_plugin_directory, enterprise, is_license_validated, is_main_thread, + license_path, set_bundled_plugin_directory, set_license, string::IntoJson, }; +use std::io; +use thiserror::Error; use crate::mainthread::{MainThreadAction, MainThreadHandler}; use crate::rc::Ref; @@ -23,6 +25,18 @@ use binaryninjacore_sys::{BNInitPlugins, BNInitRepoPlugins}; use std::sync::mpsc::Sender; use std::time::Duration; +#[derive(Error, Debug)] +pub enum InitializationError { + #[error("main thread could not be started: {0}")] + MainThreadNotStarted(#[from] io::Error), + #[error("enterprise license checkout failed: {0:?}")] + FailedEnterpriseCheckout(#[from] enterprise::EnterpriseCheckoutError), + #[error("invalid license")] + InvalidLicense, + #[error("no license could located, please see `binaryninja::set_license` for details")] + NoLicenseFound, +} + #[derive(Debug)] pub struct HeadlessMainThreadSender { sender: Sender>, @@ -49,7 +63,7 @@ impl MainThreadHandler for HeadlessMainThreadSender { /// You can instead call this through [`Session`]. /// /// If you are using a custom [`MainThreadHandler`] than use [`init_without_main_thread`] instead. -pub fn init() { +pub fn init() -> Result<(), InitializationError> { // If we are the main thread that means there is no main thread. if is_main_thread() { let (sender, receiver) = std::sync::mpsc::channel(); @@ -64,21 +78,19 @@ pub fn init() { while let Ok(action) = receiver.recv() { action.execute(); } - }) - .expect("Failed to spawn main thread"); + })?; } - init_without_main_thread(); + init_without_main_thread() } /// This initializes the core without registering a main thread handler. /// /// Call this if you have previously registered a [`MainThreadHandler`]. -pub fn init_without_main_thread() { +pub fn init_without_main_thread() -> Result<(), InitializationError> { match crate::product().as_str() { "Binary Ninja Enterprise Client" | "Binary Ninja Ultimate" => { - crate::enterprise::checkout_license(Duration::from_secs(900)) - .expect("Failed to checkout license"); + enterprise::checkout_license(Duration::from_secs(900))?; } _ => {} } @@ -91,16 +103,23 @@ pub fn init_without_main_thread() { BNInitPlugins(true); BNInitRepoPlugins(); } + + if !is_license_validated() { + // Unfortunately you must have a valid license to use Binary Ninja. + Err(InitializationError::InvalidLicense) + } else { + Ok(()) + } } -/// Unloads plugins, stops all worker threads, and closes open logs +/// Unloads plugins, stops all worker threads, and closes open logs. +/// +/// If the core was initialized using an enterprise license, that will also be freed. /// /// ⚠️ Important! Must be called at the end of scripts. ⚠️ pub fn shutdown() { match crate::product().as_str() { - "Binary Ninja Enterprise Client" | "Binary Ninja Ultimate" => { - crate::enterprise::release_license() - } + "Binary Ninja Enterprise Client" | "Binary Ninja Ultimate" => enterprise::release_license(), _ => {} } @@ -111,23 +130,68 @@ pub fn is_shutdown_requested() -> bool { unsafe { binaryninjacore_sys::BNIsShutdownRequested() } } +#[derive(Debug)] +pub enum LicenseLocation { + /// The license used when initializing will be the environment variable `BN_LICENSE`. + EnvironmentVariable, + /// The license used when initializing will be the file in the Binary Ninja user directory. + File, +} + +/// Attempts to identify the license location type, this follows the same order as core initialization. +/// +/// This is useful if you want to know whether the core will use your license. If this returns `None` +/// you should look setting the `BN_LICENSE` environment variable, or calling [`set_license`]. +pub fn license_location() -> Option { + match std::env::var("BN_LICENSE_FILE") { + Ok(_) => Some(LicenseLocation::EnvironmentVariable), + Err(_) => { + // Check the license_path to see if a file is there. + if license_path().exists() { + Some(LicenseLocation::File) + } else { + None + } + } + } +} + /// Wrapper for [`init`] and [`shutdown`]. Instantiating this at the top of your script will initialize everything correctly and then clean itself up at exit as well. pub struct Session {} impl Session { - pub fn new() -> Self { - init(); - Self {} + /// Before calling new you must make sure that the license is retrievable, otherwise the core won't be able to initialize. + /// + /// If you cannot otherwise provide a license via `BN_LICENSE_FILE` environment variable or the Binary Ninja user directory + /// you can call [`Session::new_with_license`] instead of this function. + pub fn new() -> Result { + if license_location().is_some() { + // We were able to locate a license, continue with initialization. + init()?; + Ok(Self {}) + } else { + // There was no license that could be automatically retrieved, you must call [Self::new_with_license]. + Err(InitializationError::NoLicenseFound) + } + } + + /// Initialize with a provided license, this is useful if you need to manage multiple licenses. + /// + /// If you do not need to manage multiple licenses you may also set the `BN_LICENSE` environment variable. + pub fn new_with_license(license: &str) -> Result { + set_license(license); + init()?; + Ok(Self {}) } /// ```no_run - /// let headless_session = binaryninja::headless::Session::new(); + /// let headless_session = binaryninja::headless::Session::new().unwrap(); /// /// let bv = headless_session /// .load("/bin/cat") /// .expect("Couldn't open `/bin/cat`"); /// ``` - pub fn load(&self, filename: &str) -> Option> { + pub fn load(&self, filename: &str) -> Option> { crate::load(filename) } @@ -137,7 +201,7 @@ impl Session { /// /// let settings: Ref = /// HashMap::from([("analysis.linearSweep.autorun", false.into())]).into(); - /// let headless_session = binaryninja::headless::Session::new(); + /// let headless_session = binaryninja::headless::Session::new().unwrap(); /// /// let bv = headless_session /// .load_with_options("/bin/cat", true, Some(settings)) @@ -148,17 +212,11 @@ impl Session { filename: &str, update_analysis_and_wait: bool, options: Option, - ) -> Option> { + ) -> Option> { crate::load_with_options(filename, update_analysis_and_wait, options) } } -impl Default for Session { - fn default() -> Self { - Self::new() - } -} - impl Drop for Session { fn drop(&mut self) { shutdown() diff --git a/rust/src/lib.rs b/rust/src/lib.rs index 5e55e6722..1fd35e49f 100644 --- a/rust/src/lib.rs +++ b/rust/src/lib.rs @@ -80,7 +80,7 @@ //! ```no_run //! // This loads all the core architecture, platform, etc plugins //! // Standalone executables need to call this, but plugins do not -//! let headless_session = binaryninja::headless::Session::new(); +//! let headless_session = binaryninja::headless::Session::new().unwrap(); //! //! println!("Loading binary..."); //! let bv = headless_session @@ -465,12 +465,11 @@ where } } -pub fn install_directory() -> Result { - let s: *mut c_char = unsafe { BNGetInstallDirectory() }; - if s.is_null() { - return Err(()); - } - Ok(PathBuf::from(unsafe { BnString::from_raw(s) }.to_string())) +pub fn install_directory() -> PathBuf { + let install_dir_ptr: *mut c_char = unsafe { BNGetInstallDirectory() }; + assert!(!install_dir_ptr.is_null()); + let bn_install_dir = unsafe { BnString::from_raw(install_dir_ptr) }; + PathBuf::from(bn_install_dir.to_string()) } pub fn bundled_plugin_directory() -> Result { @@ -487,12 +486,11 @@ pub fn set_bundled_plugin_directory(new_dir: S) { }; } -pub fn user_directory() -> Result { - let s: *mut c_char = unsafe { BNGetUserDirectory() }; - if s.is_null() { - return Err(()); - } - Ok(PathBuf::from(unsafe { BnString::from_raw(s) }.to_string())) +pub fn user_directory() -> PathBuf { + let user_dir_ptr: *mut c_char = unsafe { BNGetUserDirectory() }; + assert!(!user_dir_ptr.is_null()); + let bn_user_dir = unsafe { BnString::from_raw(user_dir_ptr) }; + PathBuf::from(bn_user_dir.to_string()) } pub fn user_plugin_directory() -> Result { @@ -511,12 +509,11 @@ pub fn repositories_directory() -> Result { Ok(PathBuf::from(unsafe { BnString::from_raw(s) }.to_string())) } -pub fn settings_file_name() -> Result { - let s: *mut c_char = unsafe { BNGetSettingsFileName() }; - if s.is_null() { - return Err(()); - } - Ok(PathBuf::from(unsafe { BnString::from_raw(s) }.to_string())) +pub fn settings_file_name() -> PathBuf { + let settings_file_name_ptr: *mut c_char = unsafe { BNGetSettingsFileName() }; + assert!(!settings_file_name_ptr.is_null()); + let bn_settings_file_name = unsafe { BnString::from_raw(settings_file_name_ptr) }; + PathBuf::from(bn_settings_file_name.to_string()) } /// Write the installation directory of the currently running core instance to disk. @@ -568,9 +565,9 @@ pub fn path_relative_to_user_directory(path: S) -> R /// Returns if the running thread is the "main thread" /// -/// If there is no registered main thread than this will return the current thread. +/// If there is no registered main thread than this will always return true. pub fn is_main_thread() -> bool { - unsafe { binaryninjacore_sys::BNIsMainThread() } + unsafe { BNIsMainThread() } } pub fn memory_info() -> HashMap { @@ -652,14 +649,20 @@ pub struct VersionInfo { pub channel: String, } +impl VersionInfo { + pub(crate) fn from_owned_raw(value: BNVersionInfo) -> Self { + Self { + major: value.major, + minor: value.minor, + build: value.build, + channel: unsafe { BnString::from_raw(value.channel) }.to_string(), + } + } +} + pub fn version_info() -> VersionInfo { let info_raw = unsafe { BNGetVersionInfo() }; - VersionInfo { - major: info_raw.major, - minor: info_raw.minor, - build: info_raw.build, - channel: unsafe { BnString::from_raw(info_raw.channel).to_string() }, - } + VersionInfo::from_owned_raw(info_raw) } pub fn serial_number() -> BnString { @@ -674,10 +677,19 @@ pub fn licensed_user_email() -> BnString { unsafe { BnString::from_raw(BNGetLicensedUserEmail()) } } +pub fn license_path() -> PathBuf { + user_directory().join("license.dat") +} + pub fn license_count() -> i32 { unsafe { BNGetLicenseCount() } } +/// Set the license that will be used once the core initializes. +/// +/// If not set the normal license retrieval will occur: +/// 1. Check the BN_LICENSE environment variable +/// 2. Check the Binary Ninja user directory for license.dat pub fn set_license(license: S) { let license = license.into_bytes_with_nul(); let license_slice = license.as_ref(); diff --git a/rust/tests/demangler.rs b/rust/tests/demangler.rs index c2f47bf6e..7f4f3e01e 100644 --- a/rust/tests/demangler.rs +++ b/rust/tests/demangler.rs @@ -11,7 +11,7 @@ use rstest::*; #[fixture] #[once] fn session() -> Session { - Session::new() + Session::new().expect("Failed to initialize session") } #[rstest] diff --git a/rust/tests/mainthread.rs b/rust/tests/mainthread.rs index 1358ae131..a067631fe 100644 --- a/rust/tests/mainthread.rs +++ b/rust/tests/mainthread.rs @@ -6,7 +6,7 @@ use rstest::*; #[fixture] #[once] fn session() -> Session { - Session::new() + Session::new().expect("Failed to initialize session") } #[rstest] diff --git a/rust/tests/platform.rs b/rust/tests/platform.rs index cce965bac..c6c861fb7 100644 --- a/rust/tests/platform.rs +++ b/rust/tests/platform.rs @@ -5,7 +5,7 @@ use rstest::*; #[fixture] #[once] fn session() -> Session { - Session::new() + Session::new().expect("Failed to initialize session") } #[rstest] diff --git a/rust/tests/project.rs b/rust/tests/project.rs index c3225d242..1ecf5a997 100644 --- a/rust/tests/project.rs +++ b/rust/tests/project.rs @@ -10,7 +10,7 @@ use std::time::SystemTime; #[fixture] #[once] fn session() -> Session { - Session::new() + Session::new().expect("Failed to initialize session") } fn unique_project(name: &str) -> String { diff --git a/rust/tests/typearchive.rs b/rust/tests/typearchive.rs index 0b0164955..b533a60fa 100644 --- a/rust/tests/typearchive.rs +++ b/rust/tests/typearchive.rs @@ -9,7 +9,7 @@ use rstest::*; #[fixture] #[once] fn session() -> Session { - Session::new() + Session::new().expect("Failed to initialize session") } #[fixture] diff --git a/rust/tests/typecontainer.rs b/rust/tests/typecontainer.rs index 7937de8b1..d67bad1d4 100644 --- a/rust/tests/typecontainer.rs +++ b/rust/tests/typecontainer.rs @@ -9,7 +9,7 @@ use rstest::*; #[fixture] #[once] fn session() -> Session { - Session::new() + Session::new().expect("Failed to initialize session") } #[fixture] diff --git a/rust/tests/typeparser.rs b/rust/tests/typeparser.rs index b86bb6808..dde49896b 100644 --- a/rust/tests/typeparser.rs +++ b/rust/tests/typeparser.rs @@ -30,7 +30,7 @@ typedef struct { #[fixture] #[once] fn session() -> Session { - Session::new() + Session::new().expect("Failed to initialize session") } #[rstest] diff --git a/rust/tests/types.rs b/rust/tests/types.rs index 9244e9d35..f745de0c9 100644 --- a/rust/tests/types.rs +++ b/rust/tests/types.rs @@ -5,7 +5,7 @@ use rstest::*; #[fixture] #[once] fn session() -> Session { - Session::new() + Session::new().expect("Failed to initialize session") } #[rstest] From 6de52a22685dfa25475a5aeca11a8192765b8664 Mon Sep 17 00:00:00 2001 From: Mason Reed Date: Tue, 14 Jan 2025 15:17:18 -0500 Subject: [PATCH 50/93] Add BN_LICENSE environment variable to rust CI We pass the serial to download binary ninja, but we never provided the license for core initialization --- .github/workflows/rust.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index e6737ea5d..31a010f58 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -34,6 +34,7 @@ jobs: run: cargo test --all-features -- --test-threads=1 env: BINARYNINJADIR: ${{ steps.setup-binja.outputs.install-path }} + BN_LICENSE: ${{ secrets.BN_LICENSE }} # Check lints with clippy clippy: From 098c507972a77e272b8c207b6983530b692dacef Mon Sep 17 00:00:00 2001 From: Mason Reed Date: Tue, 14 Jan 2025 15:20:51 -0500 Subject: [PATCH 51/93] Fix typo --- rust/src/headless.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rust/src/headless.rs b/rust/src/headless.rs index c7d6c824b..5ef8a7b6b 100644 --- a/rust/src/headless.rs +++ b/rust/src/headless.rs @@ -143,7 +143,7 @@ pub enum LicenseLocation { /// This is useful if you want to know whether the core will use your license. If this returns `None` /// you should look setting the `BN_LICENSE` environment variable, or calling [`set_license`]. pub fn license_location() -> Option { - match std::env::var("BN_LICENSE_FILE") { + match std::env::var("BN_LICENSE") { Ok(_) => Some(LicenseLocation::EnvironmentVariable), Err(_) => { // Check the license_path to see if a file is there. From 556918b981edb4b5ca6b4d8ae7409d0fb0e44a4e Mon Sep 17 00:00:00 2001 From: Mason Reed Date: Tue, 14 Jan 2025 18:31:37 -0500 Subject: [PATCH 52/93] More rust cleanup - Improved binary view initialization (see init_with_opts) - Allow floating license to free itself before initialization - Add initialization unit test - Add better Debug impls for some common types - Use Path api for opening binary views, this is not breaking as it uses the AsRef impl - Bump rayon dependency and constrain dependencies to x.x --- rust/Cargo.toml | 7 +- rust/src/binaryview.rs | 52 +++++----- rust/src/component.rs | 4 + rust/src/enterprise.rs | 11 ++ rust/src/filemetadata.rs | 27 +++-- rust/src/headless.rs | 195 +++++++++++++++++++++++++---------- rust/src/lib.rs | 100 +++++++----------- rust/src/section.rs | 19 ++-- rust/src/segment.rs | 18 ++++ rust/tests/initialization.rs | 37 +++++++ 10 files changed, 312 insertions(+), 158 deletions(-) create mode 100644 rust/tests/initialization.rs diff --git a/rust/Cargo.toml b/rust/Cargo.toml index 16437b968..86e527d60 100644 --- a/rust/Cargo.toml +++ b/rust/Cargo.toml @@ -9,13 +9,12 @@ rust-version = "1.83.0" # This is used when statically linking to prevent exporting CorePluginABIVersion and UiPluginABIVersion. no_exports = [] - [dependencies] log = { version = "0.4", features = ["std"] } -rayon = { version = "1.8", optional = true } +rayon = { version = "1.10", optional = true } binaryninjacore-sys = { path = "binaryninjacore-sys" } thiserror = "2.0" [dev-dependencies] -rstest = "0.24.0" -tempfile = "3" \ No newline at end of file +rstest = "0.24" +tempfile = "3.15" \ No newline at end of file diff --git a/rust/src/binaryview.rs b/rust/src/binaryview.rs index c28c2c594..3e354221d 100644 --- a/rust/src/binaryview.rs +++ b/rust/src/binaryview.rs @@ -39,28 +39,28 @@ use crate::function::{Function, NativeBlock}; use crate::linearview::{LinearDisassemblyLine, LinearViewCursor}; use crate::metadata::Metadata; use crate::platform::Platform; +use crate::rc::*; +use crate::references::{CodeReference, DataReference}; use crate::relocation::Relocation; use crate::section::{Section, SectionBuilder}; use crate::segment::{Segment, SegmentBuilder}; use crate::settings::Settings; +use crate::string::*; use crate::symbol::{Symbol, SymbolType}; use crate::tags::{Tag, TagType}; +use crate::typecontainer::TypeContainer; use crate::typelibrary::TypeLibrary; use crate::types::{ NamedTypeReference, QualifiedName, QualifiedNameAndType, QualifiedNameTypeAndId, Type, }; +use crate::variable::DataVariable; use crate::Endianness; use std::collections::HashMap; use std::ffi::{c_char, c_void}; use std::ops::Range; +use std::path::Path; use std::ptr::NonNull; use std::{ops, result, slice}; - -use crate::rc::*; -use crate::references::{CodeReference, DataReference}; -use crate::string::*; -use crate::typecontainer::TypeContainer; -use crate::variable::DataVariable; // TODO : general reorg of modules related to bv pub type Result = result::Result; @@ -190,7 +190,6 @@ pub trait BinaryViewExt: BinaryViewBase { fn file(&self) -> Ref { unsafe { let raw = BNGetFileForView(self.as_ref().handle); - Ref::new(FileMetadata::from_raw(raw)) } } @@ -1774,15 +1773,10 @@ impl BinaryView { Ref::new(Self { handle }) } - pub fn from_filename( - meta: &mut FileMetadata, - filename: S, - ) -> Result> { - let file = filename.into_bytes_with_nul(); - - let handle = unsafe { - BNCreateBinaryDataViewFromFilename(meta.handle, file.as_ref().as_ptr() as *mut _) - }; + pub fn from_path(meta: &mut FileMetadata, file_path: impl AsRef) -> Result> { + let file = file_path.as_ref().into_bytes_with_nul(); + let handle = + unsafe { BNCreateBinaryDataViewFromFilename(meta.handle, file.as_ptr() as *mut _) }; if handle.is_null() { return Err(()); @@ -1920,13 +1914,25 @@ unsafe impl Sync for BinaryView {} impl std::fmt::Debug for BinaryView { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!( - f, - "BinaryView (type: `{}`): '{}', len {:#x}", - self.view_type(), - self.file().filename(), - self.len() - ) + f.debug_struct("BinaryView") + .field("type_name", &self.type_name()) + .field("file", &self.file()) + .field("original_image_base", &self.original_image_base()) + .field("start", &self.start()) + .field("end", &self.end()) + .field("len", &self.len()) + .field("default_platform", &self.default_platform()) + .field("default_arch", &self.default_arch()) + .field("default_endianness", &self.default_endianness()) + .field("entry_point", &self.entry_point()) + .field( + "entry_point_functions", + &self.entry_point_functions().to_vec(), + ) + .field("address_size", &self.address_size()) + .field("sections", &self.sections().to_vec()) + .field("segments", &self.segments().to_vec()) + .finish() } } diff --git a/rust/src/component.rs b/rust/src/component.rs index b705e3093..02685ab48 100644 --- a/rust/src/component.rs +++ b/rust/src/component.rs @@ -10,8 +10,11 @@ use crate::variable::DataVariable; use binaryninjacore_sys::*; pub struct ComponentBuilder { + // TODO: This should be a ref bv: *mut BNBinaryView, + // TODO: Make owned string parent: Option, + // TODO: Make owned string name: Option, } @@ -75,6 +78,7 @@ impl Component { Self { handle } } + // TODO: Ref<> pub(crate) unsafe fn ref_from_raw(handle: &*mut BNComponent) -> &Self { assert!(!handle.is_null()); mem::transmute(handle) diff --git a/rust/src/enterprise.rs b/rust/src/enterprise.rs index 0e131afad..235a0e0ac 100644 --- a/rust/src/enterprise.rs +++ b/rust/src/enterprise.rs @@ -18,6 +18,7 @@ pub enum EnterpriseCheckoutError { RefreshExpiredLicenseFailed, } +/// Initialize the enterprise server connection to check out a floating license. pub fn checkout_license(duration: Duration) -> Result<(), EnterpriseCheckoutError> { if crate::is_ui_enabled() { // We only need to check out a license if running headlessly. @@ -70,6 +71,16 @@ pub fn checkout_license(duration: Duration) -> Result<(), EnterpriseCheckoutErro pub fn release_license() { if !crate::is_ui_enabled() { + // This might look dumb, why would we want to connect to the server, would that not just mean + // we don't need to release the license? Well no, you could have run a script, acquired a license for 10 hours + // then you WOULD want to call release license, and your expectation is that acquired license + // will now be released. To release that you must have an active connection which is what this does. + if !is_server_initialized() { + initialize_server(); + } + if !is_server_connected() { + connect_server(); + } // We should only release the license if we are running headlessly. release_server_license(); } diff --git a/rust/src/filemetadata.rs b/rust/src/filemetadata.rs index fc454e4bd..45d83bae6 100644 --- a/rust/src/filemetadata.rs +++ b/rust/src/filemetadata.rs @@ -12,6 +12,9 @@ // See the License for the specific language governing permissions and // limitations under the License. +use crate::binaryview::BinaryView; +use crate::database::Database; +use crate::project::ProjectFile; use binaryninjacore_sys::{ BNBeginUndoActions, BNCloseFile, BNCommitUndoActions, BNCreateDatabase, BNCreateFileMetadata, BNFileMetadata, BNFileMetadataGetSessionId, BNFreeFileMetadata, BNGetCurrentOffset, @@ -23,10 +26,7 @@ use binaryninjacore_sys::{ }; use binaryninjacore_sys::{BNCreateDatabaseWithProgress, BNOpenExistingDatabaseWithProgress}; use std::ffi::c_void; - -use crate::binaryview::BinaryView; -use crate::database::Database; -use crate::project::ProjectFile; +use std::fmt::Debug; use crate::rc::*; use crate::string::*; @@ -38,9 +38,6 @@ pub struct FileMetadata { pub(crate) handle: *mut BNFileMetadata, } -unsafe impl Send for FileMetadata {} -unsafe impl Sync for FileMetadata {} - impl FileMetadata { pub(crate) fn from_raw(handle: *mut BNFileMetadata) -> Self { Self { handle } @@ -285,6 +282,22 @@ impl FileMetadata { } } +impl Debug for FileMetadata { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("FileMetadata") + .field("filename", &self.filename()) + .field("session_id", &self.session_id()) + .field("modified", &self.modified()) + .field("is_analysis_changed", &self.is_analysis_changed()) + .field("current_view", &self.current_view()) + .field("current_view", &self.current_offset()) + .finish() + } +} + +unsafe impl Send for FileMetadata {} +unsafe impl Sync for FileMetadata {} + impl ToOwned for FileMetadata { type Owned = Ref; diff --git a/rust/src/headless.rs b/rust/src/headless.rs index 5ef8a7b6b..f489b3984 100644 --- a/rust/src/headless.rs +++ b/rust/src/headless.rs @@ -17,14 +17,20 @@ use crate::{ license_path, set_bundled_plugin_directory, set_license, string::IntoJson, }; use std::io; +use std::path::{Path, PathBuf}; use thiserror::Error; +use crate::enterprise::release_license; use crate::mainthread::{MainThreadAction, MainThreadHandler}; use crate::rc::Ref; use binaryninjacore_sys::{BNInitPlugins, BNInitRepoPlugins}; use std::sync::mpsc::Sender; +use std::sync::Mutex; +use std::thread::JoinHandle; use std::time::Duration; +static MAIN_THREAD_HANDLE: Mutex>> = Mutex::new(None); + #[derive(Error, Debug)] pub enum InitializationError { #[error("main thread could not be started: {0}")] @@ -37,40 +43,114 @@ pub enum InitializationError { NoLicenseFound, } -#[derive(Debug)] -pub struct HeadlessMainThreadSender { - sender: Sender>, +/// Loads plugins, core architecture, platform, etc. +/// +/// ⚠️ Important! Must be called at the beginning of scripts. Plugins do not need to call this. ⚠️ +/// +/// You can instead call this through [`Session`]. +/// +/// If you need to customize initialization, use [`init_with_opts`] instead. +pub fn init() -> Result<(), InitializationError> { + let options = InitializationOptions::default(); + init_with_opts(options) } -impl HeadlessMainThreadSender { - pub fn new(sender: Sender>) -> Self { - Self { sender } +/// Unloads plugins, stops all worker threads, and closes open logs. +/// +/// If the core was initialized using an enterprise license, that will also be freed. +/// +/// ⚠️ Important! Must be called at the end of scripts. ⚠️ +pub fn shutdown() { + match crate::product().as_str() { + "Binary Ninja Enterprise Client" | "Binary Ninja Ultimate" => enterprise::release_license(), + _ => {} } + unsafe { binaryninjacore_sys::BNShutdown() }; + release_license(); + // TODO: We might want to drop the main thread here, however that requires getting the handler ctx to drop the sender. } -impl MainThreadHandler for HeadlessMainThreadSender { - fn add_action(&self, action: Ref) { - self.sender - .send(action) - .expect("Failed to send action to main thread"); +pub fn is_shutdown_requested() -> bool { + unsafe { binaryninjacore_sys::BNIsShutdownRequested() } +} + +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct InitializationOptions { + /// A license to override with, you can use this to make sure you initialize with a specific license. + pub license: Option, + /// If you need to make sure that you do not check out a license set this to false. + /// + /// This is really only useful if you have a headless license but are using an enterprise enabled core. + pub checkout_license: bool, + /// Whether to register the default main thread handler. + /// + /// Set this to false if you have your own main thread handler. + pub register_main_thread_handler: bool, + /// How long you want to check out for. + pub floating_license_duration: Duration, + /// The bundled plugin directory to use. + pub bundled_plugin_directory: PathBuf, +} + +impl InitializationOptions { + pub fn new() -> Self { + Self::default() + } + + /// A license to override with, you can use this to make sure you initialize with a specific license. + pub fn with_license(mut self, license: impl Into) -> Self { + self.license = Some(license.into()); + self + } + + /// If you need to make sure that you do not check out a license set this to false. + /// + /// This is really only useful if you have a headless license but are using an enterprise enabled core. + pub fn with_checkout_license(mut self, should_checkout: bool) -> Self { + self.checkout_license = should_checkout; + self + } + + /// Whether to register the default main thread handler. + /// + /// Set this to false if you have your own main thread handler. + pub fn with_main_thread_handler(mut self, should_register: bool) -> Self { + self.register_main_thread_handler = should_register; + self + } + + /// How long you want to check out for, only used if you are using a floating license. + pub fn with_floating_license_duration(mut self, duration: Duration) -> Self { + self.floating_license_duration = duration; + self } } -/// Loads plugins, core architecture, platform, etc. -/// -/// ⚠️ Important! Must be called at the beginning of scripts. Plugins do not need to call this. ⚠️ -/// -/// You can instead call this through [`Session`]. -/// -/// If you are using a custom [`MainThreadHandler`] than use [`init_without_main_thread`] instead. -pub fn init() -> Result<(), InitializationError> { - // If we are the main thread that means there is no main thread. - if is_main_thread() { +impl Default for InitializationOptions { + fn default() -> Self { + Self { + license: None, + checkout_license: true, + register_main_thread_handler: true, + floating_license_duration: Duration::from_secs(900), + bundled_plugin_directory: bundled_plugin_directory() + .expect("Failed to get bundled plugin directory"), + } + } +} + +/// This initializes the core with the given [`InitializationOptions`]. +pub fn init_with_opts(options: InitializationOptions) -> Result<(), InitializationError> { + // If we are the main thread that means there is no main thread, we should register a main thread handler. + if options.register_main_thread_handler + && is_main_thread() + && MAIN_THREAD_HANDLE.lock().unwrap().is_none() + { let (sender, receiver) = std::sync::mpsc::channel(); let main_thread = HeadlessMainThreadSender::new(sender); // This thread will act as our main thread. - std::thread::Builder::new() + let main_thread_handle = std::thread::Builder::new() .name("HeadlessMainThread".to_string()) .spawn(move || { // We must register the main thread within said thread. @@ -79,25 +159,27 @@ pub fn init() -> Result<(), InitializationError> { action.execute(); } })?; - } - init_without_main_thread() -} + // Set the static MAIN_THREAD_HANDLER so that we can close the thread on shutdown. + *MAIN_THREAD_HANDLE.lock().unwrap() = Some(main_thread_handle); + } -/// This initializes the core without registering a main thread handler. -/// -/// Call this if you have previously registered a [`MainThreadHandler`]. -pub fn init_without_main_thread() -> Result<(), InitializationError> { match crate::product().as_str() { "Binary Ninja Enterprise Client" | "Binary Ninja Ultimate" => { - enterprise::checkout_license(Duration::from_secs(900))?; + if options.checkout_license { + // We are allowed to check out a license, so do it! + enterprise::checkout_license(options.floating_license_duration)?; + } } _ => {} } - let bundled_plugin_dir = - bundled_plugin_directory().expect("Failed to get bundled plugin directory"); - set_bundled_plugin_directory(bundled_plugin_dir); + if let Some(license) = options.license { + // We were given a license override, use it! + set_license(Some(license)); + } + + set_bundled_plugin_directory(options.bundled_plugin_directory); unsafe { BNInitPlugins(true); @@ -112,25 +194,26 @@ pub fn init_without_main_thread() -> Result<(), InitializationError> { } } -/// Unloads plugins, stops all worker threads, and closes open logs. -/// -/// If the core was initialized using an enterprise license, that will also be freed. -/// -/// ⚠️ Important! Must be called at the end of scripts. ⚠️ -pub fn shutdown() { - match crate::product().as_str() { - "Binary Ninja Enterprise Client" | "Binary Ninja Ultimate" => enterprise::release_license(), - _ => {} - } +#[derive(Debug)] +pub struct HeadlessMainThreadSender { + sender: Sender>, +} - unsafe { binaryninjacore_sys::BNShutdown() }; +impl HeadlessMainThreadSender { + pub fn new(sender: Sender>) -> Self { + Self { sender } + } } -pub fn is_shutdown_requested() -> bool { - unsafe { binaryninjacore_sys::BNIsShutdownRequested() } +impl MainThreadHandler for HeadlessMainThreadSender { + fn add_action(&self, action: Ref) { + self.sender + .send(action) + .expect("Failed to send action to main thread"); + } } -#[derive(Debug)] +#[derive(Debug, PartialEq, Eq, Clone, Copy, Hash)] pub enum LicenseLocation { /// The license used when initializing will be the environment variable `BN_LICENSE`. EnvironmentVariable, @@ -175,12 +258,12 @@ impl Session { } } - /// Initialize with a provided license, this is useful if you need to manage multiple licenses. + /// Initialize with options, the same rules apply as [`Session::new`], see [`InitializationOptions::default`] for the regular options passed. /// - /// If you do not need to manage multiple licenses you may also set the `BN_LICENSE` environment variable. - pub fn new_with_license(license: &str) -> Result { - set_license(license); - init()?; + /// This differs from [`Session::new`] in that it does not check to see if there is a license that the core + /// can discover by itself, therefor it is expected that you know where your license is when calling this directly. + pub fn new_with_opts(options: InitializationOptions) -> Result { + init_with_opts(options)?; Ok(Self {}) } @@ -191,8 +274,8 @@ impl Session { /// .load("/bin/cat") /// .expect("Couldn't open `/bin/cat`"); /// ``` - pub fn load(&self, filename: &str) -> Option> { - crate::load(filename) + pub fn load(&self, file_path: impl AsRef) -> Option> { + crate::load(file_path) } /// ```no_run @@ -209,11 +292,11 @@ impl Session { /// ``` pub fn load_with_options( &self, - filename: &str, + file_path: impl AsRef, update_analysis_and_wait: bool, options: Option, ) -> Option> { - crate::load_with_options(filename, update_analysis_and_wait, options) + crate::load_with_options(file_path, update_analysis_and_wait, options) } } diff --git a/rust/src/lib.rs b/rust/src/lib.rs index 1fd35e49f..b8f61da51 100644 --- a/rust/src/lib.rs +++ b/rust/src/lib.rs @@ -191,7 +191,7 @@ use metadata::MetadataType; use rc::Ref; use std::collections::HashMap; use std::ffi::{c_char, c_void, CStr}; -use std::path::PathBuf; +use std::path::{Path, PathBuf}; use string::BnStrCompatible; use string::BnString; use string::IntoJson; @@ -221,23 +221,18 @@ unsafe extern "C" fn cb_progress_nop(_ctxt: *mut c_void, _arg1: usize, _arg2: us } /// The main way to open and load files into Binary Ninja. Make sure you've properly initialized the core before calling this function. See [`crate::headless::init()`] -pub fn load(filename: S) -> Option> -where - S: BnStrCompatible, -{ - let filename = filename.into_bytes_with_nul(); - let options = "\x00"; - +pub fn load(file_path: impl AsRef) -> Option> { + let file_path = file_path.as_ref().into_bytes_with_nul(); + let options = c""; let handle = unsafe { BNLoadFilename( - filename.as_ref().as_ptr() as *mut _, + file_path.as_ptr() as *mut _, true, options.as_ptr() as *mut c_char, Some(cb_progress_nop), std::ptr::null_mut(), ) }; - if handle.is_null() { None } else { @@ -245,26 +240,25 @@ where } } -pub fn load_with_progress(filename: S, mut progress: F) -> Option> +pub fn load_with_progress( + file_path: impl AsRef, + mut progress: F, +) -> Option> where - S: BnStrCompatible, F: FnMut(usize, usize) -> bool, { - let filename = filename.into_bytes_with_nul(); - let options = "\x00"; - + let file_path = file_path.as_ref().into_bytes_with_nul(); + let options = c""; let progress_ctx = &mut progress as *mut F as *mut c_void; - let handle = unsafe { BNLoadFilename( - filename.as_ref().as_ptr() as *mut _, + file_path.as_ptr() as *mut _, true, options.as_ptr() as *mut c_char, Some(cb_progress_func::), progress_ctx, ) }; - if handle.is_null() { None } else { @@ -289,17 +283,15 @@ where /// let bv = binaryninja::load_with_options("/bin/cat", true, Some(json!("analysis.linearSweep.autorun": false).to_string())) /// .expect("Couldn't open `/bin/cat`"); /// ``` -pub fn load_with_options( - filename: S, +pub fn load_with_options( + file_path: impl AsRef, update_analysis_and_wait: bool, options: Option, ) -> Option> where - S: BnStrCompatible, O: IntoJson, { - let filename = filename.into_bytes_with_nul(); - + let file_path = file_path.as_ref().into_bytes_with_nul(); let options_or_default = if let Some(opt) = options { opt.get_json_string() .ok()? @@ -316,7 +308,7 @@ where let handle = unsafe { BNLoadFilename( - filename.as_ref().as_ptr() as *mut _, + file_path.as_ptr() as *mut _, update_analysis_and_wait, options_or_default.as_ptr() as *mut c_char, Some(cb_progress_nop), @@ -332,7 +324,7 @@ where } pub fn load_with_options_and_progress( - filename: S, + file_path: impl AsRef, update_analysis_and_wait: bool, options: Option, progress: Option, @@ -342,8 +334,7 @@ where O: IntoJson, F: FnMut(usize, usize) -> bool, { - let filename = filename.into_bytes_with_nul(); - + let file_path = file_path.as_ref().into_bytes_with_nul(); let options_or_default = if let Some(opt) = options { opt.get_json_string() .ok()? @@ -365,7 +356,7 @@ where let handle = unsafe { BNLoadFilename( - filename.as_ref().as_ptr() as *mut _, + file_path.as_ptr() as *mut _, update_analysis_and_wait, options_or_default.as_ptr() as *mut c_char, Some(cb_progress_func::), @@ -480,10 +471,9 @@ pub fn bundled_plugin_directory() -> Result { Ok(PathBuf::from(unsafe { BnString::from_raw(s) }.to_string())) } -pub fn set_bundled_plugin_directory(new_dir: S) { - unsafe { - BNSetBundledPluginDirectory(new_dir.into_bytes_with_nul().as_ref().as_ptr() as *const c_char) - }; +pub fn set_bundled_plugin_directory(new_dir: impl AsRef) { + let new_dir = new_dir.as_ref().into_bytes_with_nul(); + unsafe { BNSetBundledPluginDirectory(new_dir.as_ptr() as *const c_char) }; } pub fn user_directory() -> PathBuf { @@ -523,40 +513,30 @@ pub fn save_last_run() { unsafe { BNSaveLastRun() }; } -pub fn path_relative_to_bundled_plugin_directory( - path: S, -) -> Result { - let s: *mut c_char = unsafe { - BNGetPathRelativeToBundledPluginDirectory( - path.into_bytes_with_nul().as_ref().as_ptr() as *const c_char - ) - }; +pub fn path_relative_to_bundled_plugin_directory(path: impl AsRef) -> Result { + let path_raw = path.as_ref().into_bytes_with_nul(); + let s: *mut c_char = + unsafe { BNGetPathRelativeToBundledPluginDirectory(path_raw.as_ptr() as *const c_char) }; if s.is_null() { return Err(()); } Ok(PathBuf::from(unsafe { BnString::from_raw(s) }.to_string())) } -pub fn path_relative_to_user_plugin_directory( - path: S, -) -> Result { - let s: *mut c_char = unsafe { - BNGetPathRelativeToUserPluginDirectory( - path.into_bytes_with_nul().as_ref().as_ptr() as *const c_char - ) - }; +pub fn path_relative_to_user_plugin_directory(path: impl AsRef) -> Result { + let path_raw = path.as_ref().into_bytes_with_nul(); + let s: *mut c_char = + unsafe { BNGetPathRelativeToUserPluginDirectory(path_raw.as_ptr() as *const c_char) }; if s.is_null() { return Err(()); } Ok(PathBuf::from(unsafe { BnString::from_raw(s) }.to_string())) } -pub fn path_relative_to_user_directory(path: S) -> Result { - let s: *mut c_char = unsafe { - BNGetPathRelativeToUserDirectory( - path.into_bytes_with_nul().as_ref().as_ptr() as *const c_char - ) - }; +pub fn path_relative_to_user_directory(path: impl AsRef) -> Result { + let path_raw = path.as_ref().into_bytes_with_nul(); + let s: *mut c_char = + unsafe { BNGetPathRelativeToUserDirectory(path_raw.as_ptr() as *const c_char) }; if s.is_null() { return Err(()); } @@ -685,13 +665,13 @@ pub fn license_count() -> i32 { unsafe { BNGetLicenseCount() } } -/// Set the license that will be used once the core initializes. +/// Set the license that will be used once the core initializes. You can reset the license by passing `None`. /// /// If not set the normal license retrieval will occur: /// 1. Check the BN_LICENSE environment variable /// 2. Check the Binary Ninja user directory for license.dat -pub fn set_license(license: S) { - let license = license.into_bytes_with_nul(); +pub fn set_license(license: Option) { + let license = license.unwrap_or_default().into_bytes_with_nul(); let license_slice = license.as_ref(); unsafe { BNSetLicense(license_slice.as_ptr() as *const c_char) } } @@ -713,7 +693,7 @@ pub fn is_ui_enabled() -> bool { unsafe { BNIsUIEnabled() } } -pub fn is_database(filename: S) -> bool { +pub fn is_database(filename: S) -> bool { let filename = filename.into_bytes_with_nul(); let filename_slice = filename.as_ref(); unsafe { BNIsDatabase(filename_slice.as_ptr() as *const c_char) } @@ -743,13 +723,13 @@ pub fn plugin_ui_abi_minimum_version() -> u32 { BN_MINIMUM_UI_ABI_VERSION } -pub fn add_required_plugin_dependency(name: S) { +pub fn add_required_plugin_dependency(name: S) { unsafe { BNAddRequiredPluginDependency(name.into_bytes_with_nul().as_ref().as_ptr() as *const c_char) }; } -pub fn add_optional_plugin_dependency(name: S) { +pub fn add_optional_plugin_dependency(name: S) { unsafe { BNAddOptionalPluginDependency(name.into_bytes_with_nul().as_ref().as_ptr() as *const c_char) }; diff --git a/rust/src/section.rs b/rust/src/section.rs index 7df3d014d..80ee35cca 100644 --- a/rust/src/section.rs +++ b/rust/src/section.rs @@ -109,7 +109,7 @@ impl Section { } pub fn is_empty(&self) -> bool { - unsafe { BNSectionGetLength(self.handle) as usize == 0 } + self.len() == 0 } pub fn address_range(&self) -> Range { @@ -147,13 +147,16 @@ impl Section { impl fmt::Debug for Section { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!( - f, - "