diff --git a/Cargo.lock b/Cargo.lock index 7fea9b147b..8e08425f05 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -358,6 +358,7 @@ dependencies = [ "log", "rayon", "rstest", + "serde_json", "serial_test", "tempfile", "thiserror 2.0.11", diff --git a/arch/msp430/src/architecture.rs b/arch/msp430/src/architecture.rs index 938e9ec580..91826193a1 100644 --- a/arch/msp430/src/architecture.rs +++ b/arch/msp430/src/architecture.rs @@ -20,11 +20,12 @@ use binaryninja::architecture::{ BranchKind, FlagClassId, FlagGroupId, FlagId, FlagWriteId, RegisterId, }; use binaryninja::low_level_il::expression::ValueExpr; -use binaryninja::low_level_il::{MutableLiftedILExpr, MutableLiftedILFunction}; +use binaryninja::low_level_il::{LowLevelILMutableExpression, LowLevelILMutableFunction}; use log::error; const MIN_MNEMONIC: usize = 9; +#[derive(Debug)] pub struct Msp430 { handle: CoreArchitecture, custom_handle: CustomArchitectureHandle, @@ -193,7 +194,7 @@ impl Architecture for Msp430 { &self, data: &[u8], addr: u64, - il: &mut MutableLiftedILFunction, + il: &LowLevelILMutableFunction, ) -> Option<(usize, bool)> { match msp430_asm::decode(data) { Ok(inst) => { @@ -225,8 +226,8 @@ impl Architecture for Msp430 { fn flag_group_llil<'a>( &self, _group: Self::FlagGroup, - _il: &'a mut MutableLiftedILFunction, - ) -> Option> { + _il: &'a LowLevelILMutableFunction, + ) -> Option> { None } diff --git a/arch/msp430/src/lift.rs b/arch/msp430/src/lift.rs index 6ad7b67bd5..3e69b5a481 100644 --- a/arch/msp430/src/lift.rs +++ b/arch/msp430/src/lift.rs @@ -1,7 +1,6 @@ use crate::architecture::offset_to_absolute; use crate::flag::{Flag, FlagWrite}; use crate::register::Register; -use crate::Msp430; use binaryninja::{architecture::FlagCondition, low_level_il::lifting::LowLevelILLabel}; @@ -13,7 +12,7 @@ use msp430_asm::single_operand::SingleOperand; use msp430_asm::two_operand::TwoOperand; use binaryninja::low_level_il::expression::ValueExpr; -use binaryninja::low_level_il::{MutableLiftedILExpr, MutableLiftedILFunction}; +use binaryninja::low_level_il::{LowLevelILMutableExpression, LowLevelILMutableFunction}; use log::info; macro_rules! auto_increment { @@ -164,11 +163,7 @@ macro_rules! conditional_jump { }; } -pub(crate) fn lift_instruction( - inst: &Instruction, - addr: u64, - il: &MutableLiftedILFunction, -) { +pub(crate) fn lift_instruction(inst: &Instruction, addr: u64, il: &LowLevelILMutableFunction) { match inst { Instruction::Rrc(inst) => { let size = match inst.operand_width() { @@ -628,8 +623,8 @@ pub(crate) fn lift_instruction( fn lift_source_operand<'a>( operand: &Operand, size: usize, - il: &'a MutableLiftedILFunction, -) -> MutableLiftedILExpr<'a, Msp430, ValueExpr> { + il: &'a LowLevelILMutableFunction, +) -> LowLevelILMutableExpression<'a, ValueExpr> { match operand { Operand::RegisterDirect(r) => il.reg(size, Register::try_from(*r as u32).unwrap()), Operand::Indexed((r, offset)) => il diff --git a/arch/msp430/src/register.rs b/arch/msp430/src/register.rs index 0886e537be..31339ea077 100644 --- a/arch/msp430/src/register.rs +++ b/arch/msp430/src/register.rs @@ -1,7 +1,7 @@ use binaryninja::architecture; use binaryninja::architecture::{ImplicitRegisterExtend, RegisterId}; -use binaryninja::low_level_il::LowLevelILRegister; +use binaryninja::low_level_il::LowLevelILRegisterKind; use std::borrow::Cow; #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] @@ -134,8 +134,8 @@ impl architecture::RegisterInfo for Register { } } -impl From for LowLevelILRegister { +impl From for LowLevelILRegisterKind { fn from(register: Register) -> Self { - LowLevelILRegister::ArchReg(register) + LowLevelILRegisterKind::Arch(register) } } diff --git a/arch/riscv/disasm/src/lib.rs b/arch/riscv/disasm/src/lib.rs index a25516a627..152817c606 100644 --- a/arch/riscv/disasm/src/lib.rs +++ b/arch/riscv/disasm/src/lib.rs @@ -291,7 +291,7 @@ impl FloatRegType for () {} impl FloatRegType for f32 {} impl FloatRegType for f64 {} -pub trait RegFile: Debug + Sized + Copy + Clone { +pub trait RegFile: Debug + Sized + Copy + Clone + Send + Sync + 'static { type Int: IntRegType; type Float: FloatRegType; @@ -2331,7 +2331,7 @@ impl StandardExtension for ExtensionSupported { } } -pub trait RiscVDisassembler: Debug + Sized + Copy + Clone { +pub trait RiscVDisassembler: 'static + Debug + Sized + Copy + Clone + Send + Sync { type RegFile: RegFile; type MulDivExtension: StandardExtension; type AtomicExtension: StandardExtension; @@ -3175,6 +3175,7 @@ pub trait RiscVDisassembler: Debug + Sized + Copy + Clone { #[derive(Copy, Clone, Debug)] pub struct RiscVIMACDisassembler(PhantomData); + impl RiscVDisassembler for RiscVIMACDisassembler { type RegFile = RF; type MulDivExtension = ExtensionSupported; diff --git a/arch/riscv/src/lib.rs b/arch/riscv/src/lib.rs index 1abce973a4..f59e1ebbda 100644 --- a/arch/riscv/src/lib.rs +++ b/arch/riscv/src/lib.rs @@ -43,8 +43,8 @@ use binaryninja::low_level_il::lifting::{ LiftableLowLevelIL, LiftableLowLevelILWithSize, LowLevelILLabel, }; use binaryninja::low_level_il::{ - expression::ExpressionHandler, instruction::InstructionHandler, LowLevelILRegister, - MutableLiftedILExpr, MutableLiftedILFunction, RegularLowLevelILFunction, + expression::ExpressionHandler, instruction::InstructionHandler, LowLevelILMutableExpression, + LowLevelILMutableFunction, LowLevelILRegisterKind, LowLevelILRegularFunction, }; use riscv_dis::{ FloatReg, FloatRegType, Instr, IntRegType, Op, RegFile, Register as RiscVRegister, @@ -88,18 +88,18 @@ enum Intrinsic { } #[derive(Copy, Clone)] -struct Register { +struct Register { id: RegisterId, _dis: PhantomData, } #[derive(Debug, Copy, Clone)] -struct RiscVIntrinsic { +struct RiscVIntrinsic { id: Intrinsic, _dis: PhantomData, } -impl Register { +impl Register { fn new(id: RegisterId) -> Self { Self { id, @@ -118,7 +118,7 @@ impl Register { } } -impl From> for Register { +impl From> for Register { fn from(reg: riscv_dis::IntReg) -> Self { Self { id: RegisterId(reg.id()), @@ -127,7 +127,7 @@ impl From> for Register } } -impl From> for Register { +impl From> for Register { fn from(reg: FloatReg) -> Self { let int_reg_count = ::int_reg_count(); @@ -138,13 +138,13 @@ impl From> for Register { } } -impl From> for LowLevelILRegister> { +impl From> for LowLevelILRegisterKind> { fn from(reg: Register) -> Self { - LowLevelILRegister::ArchReg(reg) + LowLevelILRegisterKind::Arch(reg) } } -impl RegisterInfo for Register { +impl RegisterInfo for Register { type RegType = Self; fn parent(&self) -> Option { @@ -166,7 +166,7 @@ impl RegisterInfo for Register { } } -impl architecture::Register for Register { +impl architecture::Register for Register { type InfoType = Self; fn name(&self) -> Cow { @@ -204,15 +204,13 @@ impl architecture::Register for Register { } } -impl<'a, D: 'static + RiscVDisassembler + Send + Sync> LiftableLowLevelIL<'a, RiscVArch> - for Register -{ +impl<'a, D: RiscVDisassembler> LiftableLowLevelIL<'a> for Register { type Result = ValueExpr; fn lift( - il: &'a MutableLiftedILFunction>, + il: &'a LowLevelILMutableFunction, reg: Self, - ) -> MutableLiftedILExpr<'a, RiscVArch, Self::Result> { + ) -> LowLevelILMutableExpression<'a, Self::Result> { match reg.reg_type() { RegType::Integer(0) => il.const_int(reg.size(), 0), RegType::Integer(_) => il.reg(reg.size(), reg), @@ -221,14 +219,12 @@ impl<'a, D: 'static + RiscVDisassembler + Send + Sync> LiftableLowLevelIL<'a, Ri } } -impl<'a, D: 'static + RiscVDisassembler + Send + Sync> LiftableLowLevelILWithSize<'a, RiscVArch> - for Register -{ +impl<'a, D: RiscVDisassembler> LiftableLowLevelILWithSize<'a> for Register { fn lift_with_size( - il: &'a MutableLiftedILFunction>, + il: &'a LowLevelILMutableFunction, reg: Self, size: usize, - ) -> MutableLiftedILExpr<'a, RiscVArch, ValueExpr> { + ) -> LowLevelILMutableExpression<'a, ValueExpr> { #[cfg(debug_assertions)] { if reg.size() < size { @@ -257,21 +253,21 @@ impl<'a, D: 'static + RiscVDisassembler + Send + Sync> LiftableLowLevelILWithSiz } } -impl Hash for Register { +impl Hash for Register { fn hash(&self, state: &mut H) { self.id.hash(state); } } -impl PartialEq for Register { +impl PartialEq for Register { fn eq(&self, other: &Self) -> bool { self.id == other.id } } -impl Eq 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()) } @@ -636,13 +632,14 @@ impl architecture::Intrinsic for RiscVIntrinsic { } } -struct RiscVArch { +#[derive(Debug)] +struct RiscVArch { handle: CoreArchitecture, custom_handle: CustomArchitectureHandle>, _dis: PhantomData, } -impl architecture::Architecture for RiscVArch { +impl Architecture for RiscVArch { type Handle = CustomArchitectureHandle; type RegisterInfo = Register; @@ -1068,7 +1065,7 @@ impl architecture::Architecture fo &self, data: &[u8], addr: u64, - il: &mut MutableLiftedILFunction, + il: &LowLevelILMutableFunction, ) -> Option<(usize, bool)> { let max_width = self.default_integer_size(); @@ -1241,7 +1238,8 @@ impl architecture::Architecture fo (0, _, _) => il.jump(target).append(), // indirect jump (rd_id, rs1_id, _) if rd_id == rs1_id => { // store the target in a temporary register so we don't clobber it when rd == rs1 - let tmp_reg: LowLevelILRegister> = LowLevelILRegister::Temp(0); + let tmp_reg: LowLevelILRegisterKind> = + LowLevelILRegisterKind::from_temp(0); il.set_reg(max_width, tmp_reg, target).append(); // indirect jump with storage of next address to non-`ra` register il.set_reg( @@ -1311,42 +1309,42 @@ impl architecture::Architecture fo Op::Ebreak => il.bp().append(), Op::Uret => { il.intrinsic( - MutableLiftedILFunction::::NO_OUTPUTS, - Intrinsic::Uret, - MutableLiftedILFunction::::NO_INPUTS, + LowLevelILMutableFunction::NO_OUTPUTS, + RiscVIntrinsic::::from(Intrinsic::Uret), + LowLevelILMutableFunction::NO_INPUTS, ) .append(); il.no_ret().append(); } Op::Sret => { il.intrinsic( - MutableLiftedILFunction::::NO_OUTPUTS, - Intrinsic::Sret, - MutableLiftedILFunction::::NO_INPUTS, + LowLevelILMutableFunction::NO_OUTPUTS, + RiscVIntrinsic::::from(Intrinsic::Sret), + LowLevelILMutableFunction::NO_INPUTS, ) .append(); il.no_ret().append(); } Op::Mret => { il.intrinsic( - MutableLiftedILFunction::::NO_OUTPUTS, - Intrinsic::Mret, - MutableLiftedILFunction::::NO_INPUTS, + LowLevelILMutableFunction::NO_OUTPUTS, + RiscVIntrinsic::::from(Intrinsic::Mret), + LowLevelILMutableFunction::NO_INPUTS, ) .append(); il.no_ret().append(); } Op::Wfi => il .intrinsic( - MutableLiftedILFunction::::NO_OUTPUTS, - Intrinsic::Wfi, - MutableLiftedILFunction::::NO_INPUTS, + LowLevelILMutableFunction::NO_OUTPUTS, + RiscVIntrinsic::::from(Intrinsic::Wfi), + LowLevelILMutableFunction::NO_INPUTS, ) .append(), Op::Fence(i) => il .intrinsic( - MutableLiftedILFunction::::NO_OUTPUTS, - Intrinsic::Fence, + LowLevelILMutableFunction::NO_OUTPUTS, + RiscVIntrinsic::::from(Intrinsic::Fence), [il.const_int(4, i.imm() as u32 as u64)], ) .append(), @@ -1358,13 +1356,14 @@ impl architecture::Architecture fo if i.rd().id() == 0 { il.intrinsic( - MutableLiftedILFunction::::NO_OUTPUTS, - Intrinsic::Csrwr, + LowLevelILMutableFunction::NO_OUTPUTS, + RiscVIntrinsic::::from(Intrinsic::Csrwr), [csr, rs1], ) .append(); } else { - il.intrinsic([rd], Intrinsic::Csrrw, [rs1]).append(); + il.intrinsic([rd], RiscVIntrinsic::::from(Intrinsic::Csrrw), [rs1]) + .append(); } } Op::Csrrs(i) => { @@ -1373,9 +1372,15 @@ impl architecture::Architecture fo let csr = il.const_int(4, i.csr() as u64); if i.rs1().id() == 0 { - il.intrinsic([rd], Intrinsic::Csrrd, [csr]).append(); + il.intrinsic([rd], RiscVIntrinsic::::from(Intrinsic::Csrrd), [csr]) + .append(); } else { - il.intrinsic([rd], Intrinsic::Csrrs, [csr, rs1]).append(); + il.intrinsic( + [rd], + RiscVIntrinsic::::from(Intrinsic::Csrrs), + [csr, rs1], + ) + .append(); } } Op::Csrrc(i) => { @@ -1384,9 +1389,15 @@ impl architecture::Architecture fo let csr = il.const_int(4, i.csr() as u64); if i.rs1().id() == 0 { - il.intrinsic([rd], Intrinsic::Csrrd, [csr]).append(); + il.intrinsic([rd], RiscVIntrinsic::::from(Intrinsic::Csrrd), [csr]) + .append(); } else { - il.intrinsic([rd], Intrinsic::Csrrc, [csr, rs1]).append(); + il.intrinsic( + [rd], + RiscVIntrinsic::::from(Intrinsic::Csrrc), + [csr, rs1], + ) + .append(); } } Op::CsrrwI(i) => { @@ -1396,13 +1407,18 @@ impl architecture::Architecture fo if i.rd().id() == 0 { il.intrinsic( - MutableLiftedILFunction::::NO_OUTPUTS, - Intrinsic::Csrwr, + LowLevelILMutableFunction::NO_OUTPUTS, + RiscVIntrinsic::::from(Intrinsic::Csrwr), [csr, imm], ) .append(); } else { - il.intrinsic([rd], Intrinsic::Csrrw, [csr, imm]).append(); + il.intrinsic( + [rd], + RiscVIntrinsic::::from(Intrinsic::Csrrw), + [csr, imm], + ) + .append(); } } Op::CsrrsI(i) => { @@ -1411,9 +1427,15 @@ impl architecture::Architecture fo let imm = il.const_int(max_width, i.imm() as u64); if i.imm() == 0 { - il.intrinsic([rd], Intrinsic::Csrrd, [csr]).append(); + il.intrinsic([rd], RiscVIntrinsic::::from(Intrinsic::Csrrd), [csr]) + .append(); } else { - il.intrinsic([rd], Intrinsic::Csrrs, [csr, imm]).append(); + il.intrinsic( + [rd], + RiscVIntrinsic::::from(Intrinsic::Csrrs), + [csr, imm], + ) + .append(); } } Op::CsrrcI(i) => { @@ -1422,9 +1444,15 @@ impl architecture::Architecture fo let imm = il.const_int(max_width, i.imm() as u64); if i.imm() == 0 { - il.intrinsic([rd], Intrinsic::Csrrd, [csr]).append(); + il.intrinsic([rd], RiscVIntrinsic::::from(Intrinsic::Csrrd), [csr]) + .append(); } else { - il.intrinsic([rd], Intrinsic::Csrrc, [csr, imm]).append(); + il.intrinsic( + [rd], + RiscVIntrinsic::::from(Intrinsic::Csrrc), + [csr, imm], + ) + .append(); } } @@ -1443,7 +1471,7 @@ impl architecture::Architecture fo let rd = a.rd(); let dest_reg = match rd.id() { - 0 => LowLevelILRegister::Temp(0), + 0 => LowLevelILRegisterKind::from_temp(0), _ => Register::from(rd).into(), }; @@ -1491,14 +1519,14 @@ impl architecture::Architecture fo let rs2 = a.rs2(); let dest_reg = match rd.id() { - 0 => LowLevelILRegister::Temp(0), + 0 => LowLevelILRegisterKind::from_temp(0), _ => Register::from(rd).into(), }; let mut next_temp_reg = 1; let mut alloc_reg = |rs: riscv_dis::IntReg| match (rs.id(), rd.id()) { (id, r) if id != 0 && id == r => { - let reg = LowLevelILRegister::Temp(next_temp_reg); + let reg = LowLevelILRegisterKind::from_temp(next_temp_reg); next_temp_reg += 1; il.set_reg(max_width, reg, Register::from(rs)).append(); @@ -1578,10 +1606,11 @@ impl architecture::Architecture fo }; il.set_reg(width, rd, result).append(); } else { - let product = LowLevelILRegister::Temp(0); + let product: LowLevelILRegisterKind = + LowLevelILRegisterKind::from_temp(0); il.intrinsic( [product], - Intrinsic::Fmul(f.width(), f.rm()), + RiscVIntrinsic::::from(Intrinsic::Fmul(f.width(), f.rm())), [il.reg(width, rs1), il.reg(width, rs2)], ) .append(); @@ -1589,21 +1618,21 @@ impl architecture::Architecture fo Op::Fmadd(..) => il .intrinsic( [rd], - Intrinsic::Fmul(f.width(), f.rm()), + RiscVIntrinsic::::from(Intrinsic::Fmul(f.width(), f.rm())), [il.reg(width, product), il.reg(width, rs3)], ) .append(), Op::Fmsub(..) => il .intrinsic( [rd], - Intrinsic::Fsub(f.width(), f.rm()), + RiscVIntrinsic::::from(Intrinsic::Fsub(f.width(), f.rm())), [il.reg(width, product), il.reg(width, rs3)], ) .append(), Op::Fnmadd(..) => il .intrinsic( [rd], - Intrinsic::Fsub(f.width(), f.rm()), + RiscVIntrinsic::::from(Intrinsic::Fsub(f.width(), f.rm())), [ il.fneg(width, il.reg(width, product)).build(), il.reg(width, rs3), @@ -1613,7 +1642,7 @@ impl architecture::Architecture fo Op::Fnmsub(..) => il .intrinsic( [rd], - Intrinsic::Fadd(f.width(), f.rm()), + RiscVIntrinsic::::from(Intrinsic::Fadd(f.width(), f.rm())), [ il.fneg(width, il.reg(width, product)).build(), il.reg(width, rs3), @@ -1640,10 +1669,18 @@ impl architecture::Architecture fo il.set_reg(width, rd, result).append(); } else { let intrinsic = match op { - Op::Fadd(..) => Intrinsic::Fadd(f.width(), f.rm()), - Op::Fsub(..) => Intrinsic::Fsub(f.width(), f.rm()), - Op::Fmul(..) => Intrinsic::Fmul(f.width(), f.rm()), - Op::Fdiv(..) => Intrinsic::Fdiv(f.width(), f.rm()), + Op::Fadd(..) => { + RiscVIntrinsic::::from(Intrinsic::Fadd(f.width(), f.rm())) + } + Op::Fsub(..) => { + RiscVIntrinsic::::from(Intrinsic::Fsub(f.width(), f.rm())) + } + Op::Fmul(..) => { + RiscVIntrinsic::::from(Intrinsic::Fmul(f.width(), f.rm())) + } + Op::Fdiv(..) => { + RiscVIntrinsic::::from(Intrinsic::Fdiv(f.width(), f.rm())) + } _ => unreachable!(), }; il.intrinsic([rd], intrinsic, [il.reg(width, rs1), il.reg(width, rs2)]) @@ -1660,7 +1697,7 @@ impl architecture::Architecture fo } else { il.intrinsic( [rd], - Intrinsic::Fsgnj(f.width()), + RiscVIntrinsic::::from(Intrinsic::Fsgnj(f.width())), [il.reg(width, rs1), il.reg(width, rs2)], ) .append(); @@ -1677,7 +1714,7 @@ impl architecture::Architecture fo } else { il.intrinsic( [rd], - Intrinsic::Fsgnjn(f.width()), + RiscVIntrinsic::::from(Intrinsic::Fsgnjn(f.width())), [il.reg(width, rs1), il.reg(width, rs2)], ) .append(); @@ -1694,7 +1731,7 @@ impl architecture::Architecture fo } else { il.intrinsic( [rd], - Intrinsic::Fsgnjx(f.width()), + RiscVIntrinsic::::from(Intrinsic::Fsgnjx(f.width())), [il.reg(width, rs1), il.reg(width, rs2)], ) .append(); @@ -1714,7 +1751,7 @@ impl architecture::Architecture fo let width = f.width() as usize; il.intrinsic( [rd], - Intrinsic::Fmin(f.width()), + RiscVIntrinsic::::from(Intrinsic::Fmin(f.width())), [il.reg(width, rs1), il.reg(width, rs2)], ) .append(); @@ -1726,14 +1763,14 @@ impl architecture::Architecture fo let width = f.width() as usize; il.intrinsic( [rd], - Intrinsic::Fmax(f.width()), + RiscVIntrinsic::::from(Intrinsic::Fmax(f.width())), [il.reg(width, rs1), il.reg(width, rs2)], ) .append(); } Op::Fle(f) | Op::Flt(f) | Op::Feq(f) => { let rd = match f.rd().id() { - 0 => LowLevelILRegister::Temp(0), + 0 => LowLevelILRegisterKind::from_temp(0), _ => Register::from(f.rd()).into(), }; let left = Register::from(f.rs1()); @@ -1759,7 +1796,11 @@ impl architecture::Architecture fo } else { il.intrinsic( [rd], - Intrinsic::FcvtFToF(f.rs1_width(), f.rd_width(), f.rm()), + RiscVIntrinsic::::from(Intrinsic::FcvtFToF( + f.rs1_width(), + f.rd_width(), + f.rm(), + )), [il.reg(rs1_width, rs1)], ) .append(); @@ -1767,7 +1808,7 @@ impl architecture::Architecture fo } Op::FcvtToInt(f) => { let rd = match f.rd().id() { - 0 => LowLevelILRegister::Temp(0), + 0 => LowLevelILRegisterKind::from_temp(0), _ => Register::from(f.rd()).into(), }; let rs1 = Register::from(f.rs1()); @@ -1776,14 +1817,22 @@ impl architecture::Architecture fo if f.zx() { il.intrinsic( [rd], - Intrinsic::FcvtFToU(f.rs1_width(), f.rd_width(), f.rm()), + RiscVIntrinsic::::from(Intrinsic::FcvtFToU( + f.rs1_width(), + f.rd_width(), + f.rm(), + )), [il.reg(rs1_width, rs1)], ) .append(); } else if f.rm() != RoundMode::Dynamic { il.intrinsic( [rd], - Intrinsic::FcvtFToI(f.rs1_width(), f.rd_width(), f.rm()), + RiscVIntrinsic::::from(Intrinsic::FcvtFToI( + f.rs1_width(), + f.rd_width(), + f.rm(), + )), [il.reg(rs1_width, rs1)], ) .append(); @@ -1806,14 +1855,22 @@ impl architecture::Architecture fo if f.zx() { il.intrinsic( [rd], - Intrinsic::FcvtUToF(f.rs1_width(), f.rd_width(), f.rm()), + RiscVIntrinsic::::from(Intrinsic::FcvtUToF( + f.rs1_width(), + f.rd_width(), + f.rm(), + )), [rs1], ) .append(); } else if f.rm() != RoundMode::Dynamic { il.intrinsic( [rd], - Intrinsic::FcvtIToF(f.rs1_width(), f.rd_width(), f.rm()), + RiscVIntrinsic::::from(Intrinsic::FcvtIToF( + f.rs1_width(), + f.rd_width(), + f.rm(), + )), [rs1], ) .append(); @@ -1824,7 +1881,7 @@ impl architecture::Architecture fo } Op::FmvToInt(f) => { let rd = match f.rd().id() { - 0 => LowLevelILRegister::Temp(0), + 0 => LowLevelILRegisterKind::from_temp(0), _ => Register::from(f.rd()).into(), }; let rs1 = Register::from(f.rs1()); @@ -1847,8 +1904,12 @@ impl architecture::Architecture fo let rd = Register::from(f.rd()); let rs1 = Register::from(f.rs1()); let width = f.width() as usize; - il.intrinsic([rd], Intrinsic::Fclass(f.width()), [il.reg(width, rs1)]) - .append(); + il.intrinsic( + [rd], + RiscVIntrinsic::::from(Intrinsic::Fclass(f.width())), + [il.reg(width, rs1)], + ) + .append(); } _ => il.unimplemented().append(), @@ -2652,25 +2713,23 @@ impl RelocationHandler } } -impl AsRef - for RiscVELFRelocationHandler -{ +impl AsRef for RiscVELFRelocationHandler { fn as_ref(&self) -> &CoreRelocationHandler { &self.handle } } -struct RiscVCC { +struct RiscVCC { _dis: PhantomData, } -impl RiscVCC { +impl RiscVCC { fn new() -> Self { RiscVCC { _dis: PhantomData } } } -impl CallingConvention for RiscVCC { +impl CallingConvention for RiscVCC { fn caller_saved_registers(&self) -> Vec { let mut regs = Vec::with_capacity(36); let int_reg_count = ::int_reg_count(); @@ -2792,7 +2851,7 @@ impl FunctionRecognizer for RiscVELFPLTRecognizer { &self, bv: &BinaryView, func: &Function, - llil: &RegularLowLevelILFunction, + llil: &LowLevelILRegularFunction, ) -> bool { // Look for the following code pattern: // t3 = plt @@ -2828,7 +2887,7 @@ impl FunctionRecognizer for RiscVELFPLTRecognizer { LowLevelILInstructionKind::SetReg(r) => match r.source_expr().kind() { LowLevelILExpressionKind::Load(l) => { let target_reg = r.dest_reg(); - let entry = match l.source_mem_expr().kind() { + let entry = match l.source_expr().kind() { LowLevelILExpressionKind::Reg(lr) if lr.source_reg() == auipc_dest => { plt_base } diff --git a/plugins/dwarf/dwarf_export/src/lib.rs b/plugins/dwarf/dwarf_export/src/lib.rs index bc159825f8..283a60abe0 100644 --- a/plugins/dwarf/dwarf_export/src/lib.rs +++ b/plugins/dwarf/dwarf_export/src/lib.rs @@ -1,28 +1,26 @@ mod edit_distance; -use gimli::{ - constants, - write::{ - Address, AttributeValue, DwarfUnit, EndianVec, Expression, Range, RangeList, Sections, - UnitEntryId, - }, -}; -use object::{write, Architecture, BinaryFormat, SectionKind}; -use std::fs; - +use binaryninja::interaction::form::{Form, FormInputField}; use binaryninja::logger::Logger; use binaryninja::{ binary_view::{BinaryView, BinaryViewBase, BinaryViewExt}, command::{register_command, Command}, confidence::Conf, - interaction, - interaction::{FormResponses, FormResponses::Index}, rc::Ref, - string::BnString, symbol::SymbolType, types::{MemberAccess, StructureType, Type, TypeClass}, }; +use gimli::{ + constants, + write::{ + Address, AttributeValue, DwarfUnit, EndianVec, Expression, Range, RangeList, Sections, + UnitEntryId, + }, +}; use log::{error, info, LevelFilter}; +use object::{write, Architecture, BinaryFormat, SectionKind}; +use std::fs; +use std::path::{Path, PathBuf}; fn export_type( name: String, @@ -422,7 +420,7 @@ fn export_functions( // Set subprogram DIE attributes dwarf.unit.get_mut(function_die_uid).set( gimli::DW_AT_name, - AttributeValue::String(function.symbol().short_name().as_bytes().to_vec()), + AttributeValue::String(function.symbol().short_name().to_bytes().to_vec()), ); // TODO : (DW_AT_main_subprogram VS DW_TAG_entry_point) @@ -557,7 +555,7 @@ fn export_data_vars( if let Some(symbol) = data_var_sym { dwarf.unit.get_mut(var_die_uid).set( gimli::DW_AT_name, - AttributeValue::String(symbol.full_name().as_bytes().to_vec()), + AttributeValue::String(symbol.full_name().to_bytes().to_vec()), ); if symbol.external() { @@ -599,10 +597,11 @@ fn export_data_vars( } } -fn present_form(bv_arch: &str) -> Vec { - // TODO : Verify inputs (like save location) so that we can fail early - // TODO : Add Language field - // TODO : Choose to export types/functions/etc +// TODO : Verify inputs (like save location) so that we can fail early +// TODO : Add Language field +// TODO : Choose to export types/functions/etc +// TODO: Add actual / better support for formats other than elf? +fn create_export_form(bv_arch: &str) -> Form { let archs = [ "Unknown", "AArch64", @@ -628,80 +627,37 @@ fn present_form(bv_arch: &str) -> Vec { "Wasm32", "Xtensa", ]; - interaction::FormInputBuilder::new() - .save_file_field( - "Save Location", - Some("Debug Files (*.dwo *.debug);;All Files (*)"), - None, - None, - ) - .choice_field( - "Architecture", - &archs, - archs - .iter() - .enumerate() - .min_by(|&(_, arch_name_1), &(_, arch_name_2)| { - edit_distance::distance(bv_arch, arch_name_1) - .cmp(&edit_distance::distance(bv_arch, arch_name_2)) - }) - .map(|(index, _)| index), - ) - // Add actual / better support for formats other than elf? - // .choice_field( - // "Container Format", - // &["Coff", "Elf", "MachO", "Pe", "Wasm", "Xcoff"], - // None, - // ) - .get_form_input("Export as DWARF") + + let mut form = Form::new("Export as DWARF"); + form.add_field(FormInputField::SaveFileName { + prompt: "Save Location".to_string(), + extension: Some("Debug Files (*.dwo *.debug);;All Files (*)".to_string()), + default_name: None, + default: None, + value: None, + }); + form.add_field(FormInputField::Choice { + prompt: "Architecture".to_string(), + choices: archs.iter().map(|arch| arch.to_string()).collect(), + default: archs + .iter() + .enumerate() + .min_by(|&(_, arch_name_1), &(_, arch_name_2)| { + edit_distance::distance(bv_arch, arch_name_1) + .cmp(&edit_distance::distance(bv_arch, arch_name_2)) + }) + .map(|(index, _)| index), + value: 0, + }); + form } fn write_dwarf( - responses: Vec, + file_path: &Path, + arch: Architecture, endian: T, dwarf: &mut DwarfUnit, ) { - if responses.len() < 2 { - return; - } - - let arch = match responses[1] { - Index(0) => Architecture::Unknown, - Index(1) => Architecture::Aarch64, - Index(2) => Architecture::Aarch64_Ilp32, - Index(3) => Architecture::Arm, - Index(4) => Architecture::Avr, - Index(5) => Architecture::Bpf, - Index(6) => Architecture::I386, - Index(7) => Architecture::X86_64, - Index(8) => Architecture::X86_64_X32, - Index(9) => Architecture::Hexagon, - Index(10) => Architecture::LoongArch64, - Index(11) => Architecture::Mips, - Index(12) => Architecture::Mips64, - Index(13) => Architecture::Msp430, - Index(14) => Architecture::PowerPc, - Index(15) => Architecture::PowerPc64, - Index(16) => Architecture::Riscv32, - Index(17) => Architecture::Riscv64, - Index(18) => Architecture::S390x, - Index(19) => Architecture::Sbf, - Index(20) => Architecture::Sparc64, - Index(21) => Architecture::Wasm32, - Index(22) => Architecture::Xtensa, - _ => Architecture::Unknown, - }; - - // let format = match responses[2] { - // Index(0) => BinaryFormat::Coff, - // Index(1) => BinaryFormat::Elf, - // Index(2) => BinaryFormat::MachO, - // Index(3) => BinaryFormat::Pe, - // Index(4) => BinaryFormat::Wasm, - // Index(5) => BinaryFormat::Xcoff, - // _ => BinaryFormat::Elf, - // }; - // TODO : Look in to other options (mangling, flags, etc (see Object::new)) let mut out_object = write::Object::new( BinaryFormat::Elf, @@ -739,16 +695,14 @@ fn write_dwarf( }) .unwrap(); - if let interaction::FormResponses::String(filename) = &responses[0] { - if let Ok(out_data) = out_object.write() { - if let Err(err) = fs::write(filename, out_data) { - error!("Failed to write DWARF file: {}", err); - } else { - info!("Successfully saved as DWARF to `{}`", filename); - } + if let Ok(out_data) = out_object.write() { + if let Err(err) = fs::write(file_path, out_data) { + error!("Failed to write DWARF file: {}", err); } else { - error!("Failed to write DWARF with requested settings"); + info!("Successfully saved as DWARF to `{:?}`", file_path); } + } else { + error!("Failed to write DWARF with requested settings"); } } @@ -756,9 +710,44 @@ fn export_dwarf(bv: &BinaryView) { let arch_name = if let Some(arch) = bv.default_arch() { arch.name() } else { - BnString::new("Unknown") + String::from("Unknown") }; - let responses = present_form(arch_name.as_str()); + let mut export_form = create_export_form(arch_name.as_str()); + if !export_form.prompt() { + return; + } + + let arch_field = export_form.get_field_with_name("Architecture").unwrap(); + let arch_field_idx = arch_field.try_value_index().unwrap_or_default(); + let arch = match arch_field_idx { + 0 => Architecture::Unknown, + 1 => Architecture::Aarch64, + 2 => Architecture::Aarch64_Ilp32, + 3 => Architecture::Arm, + 4 => Architecture::Avr, + 5 => Architecture::Bpf, + 6 => Architecture::I386, + 7 => Architecture::X86_64, + 8 => Architecture::X86_64_X32, + 9 => Architecture::Hexagon, + 10 => Architecture::LoongArch64, + 11 => Architecture::Mips, + 12 => Architecture::Mips64, + 13 => Architecture::Msp430, + 14 => Architecture::PowerPc, + 15 => Architecture::PowerPc64, + 16 => Architecture::Riscv32, + 17 => Architecture::Riscv64, + 18 => Architecture::S390x, + 19 => Architecture::Sbf, + 20 => Architecture::Sparc64, + 21 => Architecture::Wasm32, + 22 => Architecture::Xtensa, + _ => Architecture::Unknown, + }; + + let save_loc_field = export_form.get_field_with_name("Save Location").unwrap(); + let save_loc_path = PathBuf::from(save_loc_field.try_value_string().unwrap_or_default()); let encoding = gimli::Encoding { format: gimli::Format::Dwarf32, @@ -783,9 +772,9 @@ fn export_dwarf(bv: &BinaryView) { // TODO: Sections? Segments? if bv.default_endianness() == binaryninja::Endianness::LittleEndian { - write_dwarf(responses, gimli::LittleEndian, &mut dwarf); + write_dwarf(&save_loc_path, arch, gimli::LittleEndian, &mut dwarf); } else { - write_dwarf(responses, gimli::BigEndian, &mut dwarf); + write_dwarf(&save_loc_path, arch, gimli::BigEndian, &mut dwarf); }; } diff --git a/plugins/dwarf/dwarf_import/src/die_handlers.rs b/plugins/dwarf/dwarf_import/src/die_handlers.rs index 52280320a0..f71fdabcf8 100644 --- a/plugins/dwarf/dwarf_import/src/die_handlers.rs +++ b/plugins/dwarf/dwarf_import/src/die_handlers.rs @@ -47,19 +47,19 @@ pub(crate) fn handle_base_type( constants::DW_ATE_address => None, constants::DW_ATE_boolean => Some(Type::bool()), constants::DW_ATE_complex_float => None, - constants::DW_ATE_float => Some(Type::named_float(size, name)), - constants::DW_ATE_signed => Some(Type::named_int(size, true, name)), - constants::DW_ATE_signed_char => Some(Type::named_int(size, true, name)), - constants::DW_ATE_unsigned => Some(Type::named_int(size, false, name)), - constants::DW_ATE_unsigned_char => Some(Type::named_int(size, false, name)), + constants::DW_ATE_float => Some(Type::named_float(size, &name)), + constants::DW_ATE_signed => Some(Type::named_int(size, true, &name)), + constants::DW_ATE_signed_char => Some(Type::named_int(size, true, &name)), + constants::DW_ATE_unsigned => Some(Type::named_int(size, false, &name)), + constants::DW_ATE_unsigned_char => Some(Type::named_int(size, false, &name)), constants::DW_ATE_imaginary_float => None, constants::DW_ATE_packed_decimal => None, constants::DW_ATE_numeric_string => None, constants::DW_ATE_edited => None, constants::DW_ATE_signed_fixed => None, constants::DW_ATE_unsigned_fixed => None, - constants::DW_ATE_decimal_float => Some(Type::named_float(size, name)), - constants::DW_ATE_UTF => Some(Type::named_int(size, false, name)), // TODO : Verify + constants::DW_ATE_decimal_float => Some(Type::named_float(size, &name)), + constants::DW_ATE_UTF => Some(Type::named_int(size, false, &name)), // TODO : Verify constants::DW_ATE_UCS => None, constants::DW_ATE_ASCII => None, // Some sort of array? constants::DW_ATE_lo_user => None, @@ -114,7 +114,7 @@ pub(crate) fn handle_enum( match &child.entry().attr(constants::DW_AT_const_value) { Ok(Some(attr)) => { if let Some(value) = get_attr_as_u64(attr) { - enumeration_builder.insert(name, value); + enumeration_builder.insert(&name, value); } else { // Somehow the child entry is not a const value. log::error!("Unhandled enum member value type for `{}`", name); diff --git a/plugins/dwarf/dwarf_import/src/dwarfdebuginfo.rs b/plugins/dwarf/dwarf_import/src/dwarfdebuginfo.rs index 136a7a8b58..1cedbdf22c 100644 --- a/plugins/dwarf/dwarf_import/src/dwarfdebuginfo.rs +++ b/plugins/dwarf/dwarf_import/src/dwarfdebuginfo.rs @@ -546,7 +546,7 @@ impl DebugInfoBuilder { assert!(debug_info.add_data_variable( address, &self.get_type(*type_uid).unwrap().ty, - name.clone(), + name.as_deref(), &[] // TODO : Components )); } @@ -622,7 +622,7 @@ impl DebugInfoBuilder { .items .len() { - func.full_name = Some(symbol_full_name.to_string()); + func.full_name = Some(symbol_full_name.to_string_lossy().to_string()); } } } diff --git a/plugins/dwarf/dwarf_import/src/functions.rs b/plugins/dwarf/dwarf_import/src/functions.rs index 6400ea84fa..6277c6b547 100644 --- a/plugins/dwarf/dwarf_import/src/functions.rs +++ b/plugins/dwarf/dwarf_import/src/functions.rs @@ -117,7 +117,7 @@ pub(crate) fn parse_function_entry( if let Ok(demangled) = sym.demangle(demangle_options) { let cleaned = abi_regex.replace_all(&demangled, ""); let simplified = simplify_str_to_str(&cleaned); - full_name = Some(simplified.to_string()); + full_name = Some(simplified.to_string_lossy().to_string()); } } } diff --git a/plugins/dwarf/dwarf_import/src/helpers.rs b/plugins/dwarf/dwarf_import/src/helpers.rs index e23eb4988c..f2f6717029 100644 --- a/plugins/dwarf/dwarf_import/src/helpers.rs +++ b/plugins/dwarf/dwarf_import/src/helpers.rs @@ -14,7 +14,7 @@ use std::ffi::OsStr; use std::path::{Path, PathBuf}; -use std::{collections::HashMap, ops::Deref, str::FromStr, sync::mpsc}; +use std::{ops::Deref, str::FromStr, sync::mpsc}; use crate::{DebugInfoBuilderContext, ReaderType}; use binaryninja::binary_view::BinaryViewBase; @@ -450,8 +450,8 @@ pub(crate) fn download_debug_info( let result = inst .perform_custom_request( "GET", - artifact_url, - HashMap::::new(), + &artifact_url, + vec![], DownloadInstanceInputOutputCallbacks { read: None, write: Some(Box::new(write)), diff --git a/plugins/dwarf/dwarf_import/src/lib.rs b/plugins/dwarf/dwarf_import/src/lib.rs index d51b4e1db7..1e3d02d59b 100644 --- a/plugins/dwarf/dwarf_import/src/lib.rs +++ b/plugins/dwarf/dwarf_import/src/lib.rs @@ -234,6 +234,7 @@ fn recover_names_internal( .collect::>() .join("::"), ) + .to_string_lossy() .to_string(), ); } @@ -251,6 +252,7 @@ fn recover_names_internal( .collect::>() .join("::"), ) + .to_string_lossy() .to_string(), ); } diff --git a/plugins/dwarf/dwarf_import/src/types.rs b/plugins/dwarf/dwarf_import/src/types.rs index 1e9dd146c5..789925104e 100644 --- a/plugins/dwarf/dwarf_import/src/types.rs +++ b/plugins/dwarf/dwarf_import/src/types.rs @@ -215,8 +215,8 @@ fn do_structure_parse( }); structure_builder.insert( - child_type.as_ref(), - child_name, + &child_type, + &child_name, struct_offset, false, MemberAccess::NoAccess, // TODO : Resolve actual scopes, if possible @@ -224,8 +224,8 @@ fn do_structure_parse( ); } else { structure_builder.append( - child_type.as_ref(), - child_name, + &child_type, + &child_name, MemberAccess::NoAccess, MemberScope::NoScope, ); diff --git a/plugins/dwarf/shared/src/lib.rs b/plugins/dwarf/shared/src/lib.rs index b12aeab1c8..7aa3b4863f 100644 --- a/plugins/dwarf/shared/src/lib.rs +++ b/plugins/dwarf/shared/src/lib.rs @@ -106,7 +106,7 @@ pub fn create_section_reader<'a, Endian: 'a + Endianity>( if let Some(symbol) = view .symbols() .iter() - .find(|symbol| symbol.full_name().as_str() == "__elf_section_headers") + .find(|symbol| symbol.full_name().to_string_lossy() == "__elf_section_headers") { if let Some(data_var) = view .data_variables() @@ -179,9 +179,10 @@ pub fn create_section_reader<'a, Endian: 'a + Endianity>( } } // Truncate Mach-O section names to 16 bytes - else if let Some(section) = - view.section_by_name("__".to_string() + §ion_name[1..section_name.len().min(15)]) - { + else if let Some(section) = view.section_by_name(&format!( + "__{}", + §ion_name[1..section_name.len().min(15)] + )) { Ok(EndianRcSlice::new( Rc::from(view.read_vec(section.start(), section.len()).as_slice()), endian, diff --git a/plugins/idb_import/src/lib.rs b/plugins/idb_import/src/lib.rs index c554831df9..46acfd42a0 100644 --- a/plugins/idb_import/src/lib.rs +++ b/plugins/idb_import/src/lib.rs @@ -198,7 +198,7 @@ pub fn import_til_section( if let TranslateTypeResult::Translated(bn_ty) | TranslateTypeResult::PartiallyTranslated(bn_ty, _) = &ty.ty { - if !debug_info.add_type(ty.name.as_utf8_lossy(), bn_ty, &[/* TODO */]) { + if !debug_info.add_type(&ty.name.as_utf8_lossy(), bn_ty, &[/* TODO */]) { error!("Unable to add type `{}`", ty.name.as_utf8_lossy()) } } @@ -209,7 +209,7 @@ pub fn import_til_section( if let TranslateTypeResult::Translated(bn_ty) | TranslateTypeResult::PartiallyTranslated(bn_ty, _) = &ty.ty { - if !debug_info.add_type(ty.name.as_utf8_lossy(), bn_ty, &[/* TODO */]) { + if !debug_info.add_type(&ty.name.as_utf8_lossy(), bn_ty, &[/* TODO */]) { error!("Unable to fix type `{}`", ty.name.as_utf8_lossy()) } } @@ -239,10 +239,7 @@ fn parse_id0_section_info( } = info; // TODO set comments to address here for function in &bv.functions_containing(addr) { - function.set_comment_at( - addr, - String::from_utf8_lossy(&comments.join(&b"\n"[..])).to_string(), - ); + function.set_comment_at(addr, &String::from_utf8_lossy(&comments.join(&b"\n"[..]))); } let bnty = ty diff --git a/plugins/idb_import/src/types.rs b/plugins/idb_import/src/types.rs index 7b4ca67540..b3c9bea059 100644 --- a/plugins/idb_import/src/types.rs +++ b/plugins/idb_import/src/types.rs @@ -335,7 +335,7 @@ impl Result<(), ()>> TranslateIDBTypes<'_, F> { format!("bitfield_{}_{}", offset + start_idx, offset + (i - 1)) }; let field = field_from_bytes(bytes); - struct_builder.append(&field, name, MemberAccess::NoAccess, MemberScope::NoScope); + struct_builder.append(&field, &name, MemberAccess::NoAccess, MemberScope::NoScope); }; for (i, member) in members { @@ -423,7 +423,7 @@ impl Result<(), ()>> TranslateIDBTypes<'_, F> { .as_ref() .map(|name| name.as_utf8_lossy().to_string()) .unwrap_or_else(|| format!("member_{i}")); - structure.append(&mem, name, MemberAccess::NoAccess, MemberScope::NoScope); + structure.append(&mem, &name, MemberAccess::NoAccess, MemberScope::NoScope); } if let Some(start_idx) = first_bitfield_seq { let members_bitrange = &ty_struct.members[start_idx..]; @@ -470,7 +470,7 @@ impl Result<(), ()>> TranslateIDBTypes<'_, F> { .as_ref() .map(|name| name.as_utf8_lossy().to_string()) .unwrap_or_else(|| format!("member_{i}")); - structure.append(&mem, name, MemberAccess::NoAccess, MemberScope::NoScope); + structure.append(&mem, &name, MemberAccess::NoAccess, MemberScope::NoScope); } let str_ref = structure.finalize(); @@ -492,7 +492,7 @@ impl Result<(), ()>> TranslateIDBTypes<'_, F> { .as_ref() .map(|name| name.as_utf8_lossy().to_string()) .unwrap_or_else(|| format!("member_{i}")); - eb.insert(name, member.value); + eb.insert(&name, member.value); } Type::enumeration( &eb.finalize(), diff --git a/plugins/pdb-ng/src/lib.rs b/plugins/pdb-ng/src/lib.rs index 2c1049b01f..a87a807633 100644 --- a/plugins/pdb-ng/src/lib.rs +++ b/plugins/pdb-ng/src/lib.rs @@ -13,7 +13,6 @@ // limitations under the License. #![allow(dead_code)] -use std::collections::HashMap; use std::env::{current_dir, current_exe, temp_dir}; use std::io::Cursor; use std::path::PathBuf; @@ -31,7 +30,6 @@ use binaryninja::download_provider::{DownloadInstanceInputOutputCallbacks, Downl use binaryninja::interaction::{MessageBoxButtonResult, MessageBoxButtonSet}; use binaryninja::logger::Logger; use binaryninja::settings::{QueryOptions, Settings}; -use binaryninja::string::BnString; use binaryninja::{interaction, user_directory}; use parser::PDBParserInstance; @@ -196,7 +194,7 @@ fn read_from_sym_store(bv: &BinaryView, path: &str) -> Result<(bool, Vec)> { .perform_custom_request( "GET", path, - HashMap::::new(), + vec![], DownloadInstanceInputOutputCallbacks { read: None, write: Some(Box::new(write)), @@ -278,21 +276,21 @@ fn search_sym_store( } fn parse_pdb_info(view: &BinaryView) -> Option { - match view.get_metadata::("DEBUG_INFO_TYPE") { + match view.get_metadata::("DEBUG_INFO_TYPE") { Some(Ok(0x53445352 /* 'SDSR' */)) => {} _ => return None, } // This is stored in the BV by the PE loader - let file_path = match view.get_metadata::("PDB_FILENAME") { + let file_path = match view.get_metadata::("PDB_FILENAME") { Some(Ok(md)) => md, _ => return None, }; - let mut guid = match view.get_metadata::, _>("PDB_GUID") { + let mut guid = match view.get_metadata::>("PDB_GUID") { Some(Ok(md)) => md, _ => return None, }; - let age = match view.get_metadata::("PDB_AGE") { + let age = match view.get_metadata::("PDB_AGE") { Some(Ok(md)) => md as u32, _ => return None, }; @@ -540,7 +538,7 @@ impl PDBParser { impl CustomDebugInfoParser for PDBParser { fn is_valid(&self, view: &BinaryView) -> bool { - view.type_name().to_string() == "PE" || is_pdb(view) + view.type_name() == "PE" || is_pdb(view) } fn parse_info( diff --git a/plugins/pdb-ng/src/parser.rs b/plugins/pdb-ng/src/parser.rs index 783a8a2d43..2ca8e209ec 100644 --- a/plugins/pdb-ng/src/parser.rs +++ b/plugins/pdb-ng/src/parser.rs @@ -168,7 +168,8 @@ impl<'a, S: Source<'a> + 'a> PDBParserInstance<'a, S> { ) -> Result<()> { self.parse_types(Self::split_progress(&progress, 0, &[1.0, 3.0, 0.5, 0.5]))?; for (name, ty) in self.named_types.iter() { - self.debug_info.add_type(name, ty.as_ref(), &[]); // TODO : Components + self.debug_info + .add_type(&name.to_string(), ty.as_ref(), &[]); // TODO : Components } info!( @@ -406,7 +407,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(), &[]); + self.debug_info + .add_type(&name.to_string(), Type::void().as_ref(), &[]); // TODO : Components } NamedTypeReferenceClass::ClassNamedTypeClass @@ -429,7 +431,7 @@ impl<'a, S: Source<'a> + 'a> PDBParserInstance<'a, S> { structure.alignment(1); self.debug_info.add_type( - &name, + &name.to_string(), Type::structure(structure.finalize().as_ref()).as_ref(), &[], // TODO : Components ); @@ -437,7 +439,7 @@ impl<'a, S: Source<'a> + 'a> PDBParserInstance<'a, S> { NamedTypeReferenceClass::EnumNamedTypeClass => { let enumeration = EnumerationBuilder::new(); self.debug_info.add_type( - &name, + &name.to_string(), Type::enumeration( enumeration.finalize().as_ref(), self.arch.default_integer_size().try_into()?, diff --git a/plugins/pdb-ng/src/struct_grouper.rs b/plugins/pdb-ng/src/struct_grouper.rs index 09a81fa7e1..ff911afdd6 100644 --- a/plugins/pdb-ng/src/struct_grouper.rs +++ b/plugins/pdb-ng/src/struct_grouper.rs @@ -361,7 +361,7 @@ pub fn group_structure( for member in members { structure.insert( &member.ty, - member.name.clone(), + &member.name, member.offset, false, member.access, @@ -390,7 +390,7 @@ fn apply_groups( if offset > member.offset { structure.insert( &member.ty, - member.name.clone(), + &member.name, 0, false, member.access, @@ -399,7 +399,7 @@ fn apply_groups( } else { structure.insert( &member.ty, - member.name.clone(), + &member.name, member.offset - offset, false, member.access, @@ -412,7 +412,7 @@ fn apply_groups( apply_groups(members, &mut inner, children, inner_offset); structure.insert( &Conf::new(Type::structure(inner.finalize().as_ref()), MAX_CONFIDENCE), - format!("__inner{}", i), + &format!("__inner{}", i), inner_offset - offset, false, MemberAccess::PublicAccess, @@ -425,7 +425,7 @@ fn apply_groups( apply_groups(members, &mut inner, children, inner_offset); structure.insert( &Conf::new(Type::structure(inner.finalize().as_ref()), MAX_CONFIDENCE), - format!("__inner{}", i), + &format!("__inner{}", i), inner_offset - offset, false, MemberAccess::PublicAccess, diff --git a/plugins/pdb-ng/src/symbol_parser.rs b/plugins/pdb-ng/src/symbol_parser.rs index 6d6e978e57..79049d0399 100644 --- a/plugins/pdb-ng/src/symbol_parser.rs +++ b/plugins/pdb-ng/src/symbol_parser.rs @@ -2027,13 +2027,13 @@ impl<'a, S: Source<'a> + 'a> PDBParserInstance<'a, S> { Some(X86(xreg)) => { self.log(|| format!("Register {:?} ==> {:?}", reg, xreg)); self.arch - .register_by_name(xreg.to_string().to_lowercase()) + .register_by_name(&xreg.to_string().to_lowercase()) .map(|reg| reg.id()) } Some(AMD64(areg)) => { self.log(|| format!("Register {:?} ==> {:?}", reg, areg)); self.arch - .register_by_name(areg.to_string().to_lowercase()) + .register_by_name(&areg.to_string().to_lowercase()) .map(|reg| reg.id()) } // TODO: Other arches diff --git a/plugins/pdb-ng/src/type_parser.rs b/plugins/pdb-ng/src/type_parser.rs index 7910e13a86..d4d5feb41a 100644 --- a/plugins/pdb-ng/src/type_parser.rs +++ b/plugins/pdb-ng/src/type_parser.rs @@ -898,7 +898,7 @@ impl<'a, S: Source<'a> + 'a> PDBParserInstance<'a, S> { bitfield_builder .as_mut() .expect("Invariant") - .insert(&m.ty, m.name, 0, false, m.access, m.scope); + .insert(&m.ty, &m.name, 0, false, m.access, m.scope); } (None, None) => { if let Some(mut builder) = bitfield_builder.take() { @@ -1633,7 +1633,7 @@ impl<'a, S: Source<'a> + 'a> PDBParserInstance<'a, S> { for field in fields { match field { ParsedType::Enumerate(member) => { - enumeration.insert(member.name.clone(), member.value); + enumeration.insert(&member.name, member.value); } e => return Err(anyhow!("Unexpected enumerate member: {:?}", e)), } @@ -1759,9 +1759,8 @@ impl<'a, S: Source<'a> + 'a> PDBParserInstance<'a, S> { structure.width(data.size); self.namespace_stack.push(union_name.to_string()); - let success = self.parse_union_fields(&mut structure, data.fields, finder); + let _success = self.parse_union_fields(&mut structure, data.fields, finder); self.namespace_stack.pop(); - let _ = success?; let new_type = Type::structure(structure.finalize().as_ref()); Ok(Some(Box::new(ParsedType::Named(union_name, new_type)))) @@ -1804,7 +1803,7 @@ impl<'a, S: Source<'a> + 'a> PDBParserInstance<'a, S> { if group.len() == 1 { structure.insert( &group[0].ty, - group[0].name.clone(), + &group[0].name, group[0].offset, false, group[0].access, @@ -1815,7 +1814,7 @@ impl<'a, S: Source<'a> + 'a> PDBParserInstance<'a, S> { for member in group { inner_struct.insert( &member.ty, - member.name.clone(), + &member.name, member.offset, false, member.access, @@ -1827,7 +1826,7 @@ impl<'a, S: Source<'a> + 'a> PDBParserInstance<'a, S> { Type::structure(inner_struct.finalize().as_ref()), MAX_CONFIDENCE, ), - format!("__inner{:x}", i), + &format!("__inner{:x}", i), 0, false, MemberAccess::PublicAccess, @@ -2015,7 +2014,11 @@ impl<'a, S: Source<'a> + 'a> PDBParserInstance<'a, S> { // for some reason if let Some(raw_name) = Self::type_data_to_raw_name(&parsed) { if let Some(&full_index) = self.full_type_indices.get(&raw_name) { - if let None = self.type_stack.iter().find(|&&idx| idx == full_index) + if self + .type_stack + .iter() + .find(|&&idx| idx == full_index) + .is_none() { if full_index != index { return self.try_type_index_to_bare( @@ -2050,8 +2053,11 @@ impl<'a, S: Source<'a> + 'a> PDBParserInstance<'a, S> { // for some reason if let Some(raw_name) = Self::type_data_to_raw_name(&parsed) { if let Some(&full_index) = self.full_type_indices.get(&raw_name) { - if let None = - self.type_stack.iter().find(|&&idx| idx == full_index) + if self + .type_stack + .iter() + .find(|&&idx| idx == full_index) + .is_none() { if full_index != index { return self.try_type_index_to_bare( diff --git a/plugins/svd/src/mapper.rs b/plugins/svd/src/mapper.rs index 2d51161542..314fba7660 100644 --- a/plugins/svd/src/mapper.rs +++ b/plugins/svd/src/mapper.rs @@ -205,7 +205,7 @@ impl DeviceMapper { let peripheral_ty_id = format!("SVD:{}", peripheral.name); let id = view.define_auto_type_with_id( &peripheral.name, - peripheral_ty_id, + &peripheral_ty_id, &peripheral_ty, ); let ntr = @@ -224,14 +224,14 @@ impl DeviceMapper { view.define_auto_symbol(&symbol); view.set_comment_at( block_addr, - format!("Buffer block with size {}", address_block.size), + &format!("Buffer block with size {}", address_block.size), ); } AddressBlockUsage::Reserved => { // TODO: What to do for reserved blocks? view.set_comment_at( block_addr, - format!("Reserved block with size {}", address_block.size), + &format!("Reserved block with size {}", address_block.size), ); } } @@ -286,11 +286,11 @@ impl DeviceMapper { for (field_addr, comments) in unaligned_comments { let comment = comments.join("\n"); - view.set_comment_at(field_addr, comment); + view.set_comment_at(field_addr, &comment); } } } - view.file().commit_undo_actions(undo_id); + view.file().commit_undo_actions(&undo_id); } pub fn peripheral_block_memory_info( @@ -662,7 +662,7 @@ impl DeviceMapper { current_value = enumerated_value.value.unwrap_or(current_value + 1); // TODO: The Rust API needs to expose this... let _is_default = enumerated_value.is_default.unwrap_or(false); - enum_builder.insert(enumerated_value.name.to_owned(), current_value); + enum_builder.insert(&enumerated_value.name, current_value); } let enum_width = NonZeroUsize::new(byte_aligned_width as usize).unwrap(); diff --git a/plugins/svd/src/settings.rs b/plugins/svd/src/settings.rs index 9621da6415..fb2f680c96 100644 --- a/plugins/svd/src/settings.rs +++ b/plugins/svd/src/settings.rs @@ -32,7 +32,7 @@ impl LoadSettings { }); bn_settings.register_setting_json( Self::ADD_BACKING_REGIONS_SETTING, - add_backing_region_props.to_string(), + &add_backing_region_props.to_string(), ); let add_bitfields_props = json!({ @@ -41,8 +41,10 @@ impl LoadSettings { "default" : Self::ADD_BITFIELDS_DEFAULT, "description" : "Whether to add bitfields. Bitfields are not supported by Binary Ninja, so this is a workaround using unions.", }); - bn_settings - .register_setting_json(Self::ADD_BITFIELDS_SETTING, add_bitfields_props.to_string()); + bn_settings.register_setting_json( + Self::ADD_BITFIELDS_SETTING, + &add_bitfields_props.to_string(), + ); let add_comments_props = json!({ "title" : "Add Comments", @@ -51,7 +53,7 @@ impl LoadSettings { "description" : "Whether to add comments. If you see comment placement is off, try disabling this.", }); bn_settings - .register_setting_json(Self::ADD_COMMENTS_SETTING, add_comments_props.to_string()); + .register_setting_json(Self::ADD_COMMENTS_SETTING, &add_comments_props.to_string()); let file_props = json!({ "title" : "SVD File", @@ -60,7 +62,7 @@ impl LoadSettings { "description" : "The SVD File to automatically load when opening the view.", "uiSelectionAction" : "file" }); - bn_settings.register_setting_json(Self::AUTO_LOAD_FILE_SETTING, file_props.to_string()); + bn_settings.register_setting_json(Self::AUTO_LOAD_FILE_SETTING, &file_props.to_string()); } pub fn from_view_settings(view: &BinaryView) -> Self { diff --git a/plugins/warp/src/bin/sigem.rs b/plugins/warp/src/bin/sigem.rs index a455f1e8e0..a50b4ae2f3 100644 --- a/plugins/warp/src/bin/sigem.rs +++ b/plugins/warp/src/bin/sigem.rs @@ -131,7 +131,7 @@ fn main() { fn data_from_view(view: &BinaryView) -> Data { let mut data = Data::default(); let is_function_named = |f: &BNGuard| { - !f.symbol().short_name().as_str().contains("sub_") || f.has_user_annotations() + !f.symbol().short_name().to_string_lossy().contains("sub_") || f.has_user_annotations() }; data.functions = view diff --git a/plugins/warp/src/cache.rs b/plugins/warp/src/cache.rs index 4b1df5af7e..cc8dfded13 100644 --- a/plugins/warp/src/cache.rs +++ b/plugins/warp/src/cache.rs @@ -1,13 +1,10 @@ use crate::convert::{from_bn_symbol, from_bn_type_internal}; use crate::{build_function, function_guid}; -use binaryninja::architecture::Architecture; use binaryninja::binary_view::{BinaryView, BinaryViewExt}; use binaryninja::confidence::MAX_CONFIDENCE; use binaryninja::function::Function as BNFunction; -use binaryninja::low_level_il::function::{ - FunctionMutability, LowLevelILFunction, NonSSA, RegularNonSSA, -}; -use binaryninja::low_level_il::RegularLowLevelILFunction; +use binaryninja::low_level_il::function::{FunctionMutability, LowLevelILFunction, NonSSA}; +use binaryninja::low_level_il::LowLevelILRegularFunction; use binaryninja::rc::Guard; use binaryninja::rc::Ref as BNRef; use binaryninja::symbol::Symbol as BNSymbol; @@ -68,10 +65,7 @@ pub fn try_cached_function_match(function: &BNFunction) -> Option { .to_owned() } -pub fn cached_function( - function: &BNFunction, - llil: &RegularLowLevelILFunction, -) -> Function { +pub fn cached_function(function: &BNFunction, llil: &LowLevelILRegularFunction) -> Function { let view = function.view(); let view_id = ViewID::from(view.as_ref()); let function_cache = FUNCTION_CACHE.get_or_init(Default::default); @@ -122,9 +116,9 @@ where } } -pub fn cached_function_guid( +pub fn cached_function_guid( function: &BNFunction, - llil: &LowLevelILFunction>, + llil: &LowLevelILFunction, ) -> FunctionGUID { let view = function.view(); let view_id = ViewID::from(view); @@ -202,11 +196,7 @@ pub struct FunctionCache { } impl FunctionCache { - pub fn function( - &self, - function: &BNFunction, - llil: &RegularLowLevelILFunction, - ) -> Function { + pub fn function(&self, function: &BNFunction, llil: &LowLevelILRegularFunction) -> Function { let function_id = FunctionID::from(function); match self.cache.get(&function_id) { Some(function) => function.value().to_owned(), @@ -330,10 +320,10 @@ impl GUIDCache { } } - pub fn function_guid( + pub fn function_guid( &self, function: &BNFunction, - llil: &LowLevelILFunction>, + 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 01ba167cda..b6c2f7b4e4 100644 --- a/plugins/warp/src/convert.rs +++ b/plugins/warp/src/convert.rs @@ -35,7 +35,7 @@ use warp::symbol::{Symbol, SymbolModifiers}; pub fn from_bn_symbol(raw_symbol: &BNSymbol) -> Symbol { // TODO: Use this? let _is_export = raw_symbol.external(); - let symbol_name = raw_symbol.raw_name().to_string(); + let symbol_name = raw_symbol.raw_name().to_string_lossy().to_string(); match raw_symbol.sym_type() { BNSymbolType::ImportAddress => { Symbol::new( @@ -431,7 +431,7 @@ pub fn to_bn_type(arch: &A, ty: &Type) -> BNRef { let base_struct_ntr = match c.guid { Some(guid) => BNNamedTypeReference::new_with_id( NamedTypeReferenceClass::UnknownNamedTypeClass, - guid.to_string(), + &guid.to_string(), base_struct_ntr_name, ), None => BNNamedTypeReference::new( @@ -475,7 +475,7 @@ pub fn to_bn_type(arch: &A, ty: &Type) -> BNRef { // TODO: Add default name? let member_name = member.name.to_owned().unwrap_or("enum_VAL".into()); let member_value = member.constant; - builder.insert(member_name, member_value); + builder.insert(&member_name, member_value); } // TODO: Warn if enumeration has no size. let width = bits_to_bytes(c.member_type.size().unwrap()) as usize; @@ -545,7 +545,7 @@ pub fn to_bn_type(arch: &A, ty: &Type) -> BNRef { let ntr_name = c.name.to_owned().unwrap_or(guid_str.clone()); NamedTypeReference::new_with_id( NamedTypeReferenceClass::UnknownNamedTypeClass, - guid_str, + &guid_str, ntr_name, ) } diff --git a/plugins/warp/src/lib.rs b/plugins/warp/src/lib.rs index 3f2fc27624..62af0102da 100644 --- a/plugins/warp/src/lib.rs +++ b/plugins/warp/src/lib.rs @@ -10,13 +10,11 @@ use binaryninja::binary_view::BinaryViewExt; use binaryninja::confidence::MAX_CONFIDENCE; use binaryninja::function::{Function as BNFunction, NativeBlock}; use binaryninja::low_level_il::expression::{ExpressionHandler, LowLevelILExpressionKind}; -use binaryninja::low_level_il::function::{ - FunctionMutability, LowLevelILFunction, NonSSA, RegularNonSSA, -}; +use binaryninja::low_level_il::function::{FunctionMutability, LowLevelILFunction, NonSSA}; use binaryninja::low_level_il::instruction::{ InstructionHandler, LowLevelILInstruction, LowLevelILInstructionKind, }; -use binaryninja::low_level_il::{LowLevelILRegister, VisitorAction}; +use binaryninja::low_level_il::{LowLevelILRegisterKind, VisitorAction}; use binaryninja::rc::Ref as BNRef; use std::path::PathBuf; use warp::signature::basic_block::BasicBlockGUID; @@ -44,9 +42,9 @@ pub fn user_signature_dir() -> PathBuf { binaryninja::user_directory().join("signatures/") } -pub fn build_function( +pub fn build_function( func: &BNFunction, - llil: &LowLevelILFunction>, + llil: &LowLevelILFunction, ) -> Function { let bn_fn_ty = func.function_type(); Function { @@ -76,9 +74,9 @@ pub fn sorted_basic_blocks(func: &BNFunction) -> Vec( +pub fn function_guid( func: &BNFunction, - llil: &LowLevelILFunction>, + llil: &LowLevelILFunction, ) -> FunctionGUID { let basic_blocks = sorted_basic_blocks(func); let basic_block_guids = basic_blocks @@ -88,9 +86,9 @@ pub fn function_guid( FunctionGUID::from_basic_blocks(&basic_block_guids) } -pub fn basic_block_guid( +pub fn basic_block_guid( basic_block: &BNBasicBlock, - llil: &LowLevelILFunction>, + llil: &LowLevelILFunction, ) -> BasicBlockGUID { let func = basic_block.function(); let view = func.view(); @@ -98,7 +96,7 @@ pub fn basic_block_guid( let max_instr_len = arch.max_instr_len(); // NOPs and useless moves are blacklisted to allow for hot-patchable functions. - let is_blacklisted_instr = |instr: &LowLevelILInstruction>| { + let is_blacklisted_instr = |instr: &LowLevelILInstruction| { match instr.kind() { LowLevelILInstructionKind::Nop(_) => true, LowLevelILInstructionKind::SetReg(op) => { @@ -107,7 +105,7 @@ pub fn basic_block_guid( if op.dest_reg() == source_op.source_reg() => { match op.dest_reg() { - LowLevelILRegister::ArchReg(r) => { + LowLevelILRegisterKind::Arch(r) => { // If this register has no implicit extend then we can safely assume it's a NOP. // Ex. on x86_64 we don't want to remove `mov edi, edi` as it will zero the upper 32 bits. // Ex. on x86 we do want to remove `mov edi, edi` as it will not have a side effect like above. @@ -116,7 +114,7 @@ pub fn basic_block_guid( ImplicitRegisterExtend::NoExtend ) } - LowLevelILRegister::Temp(_) => false, + LowLevelILRegisterKind::Temp(_) => false, } } _ => false, @@ -126,8 +124,8 @@ pub fn basic_block_guid( } }; - let is_variant_instr = |instr: &LowLevelILInstruction>| { - let is_variant_expr = |expr: &LowLevelILExpressionKind>| { + let is_variant_instr = |instr: &LowLevelILInstruction| { + let is_variant_expr = |expr: &LowLevelILExpressionKind| { // TODO: Checking the section here is slow, we should gather all section ranges outside of this. match expr { LowLevelILExpressionKind::ConstPtr(op) diff --git a/plugins/warp/src/matcher.rs b/plugins/warp/src/matcher.rs index 81458f8da9..0ce258ade6 100644 --- a/plugins/warp/src/matcher.rs +++ b/plugins/warp/src/matcher.rs @@ -159,7 +159,7 @@ impl Matcher { if let Some(ref_guid) = c.guid { // NOTE: We do not need to check for cyclic reference here because // NOTE: GUID references are unable to be referenced by themselves. - if view.type_by_id(ref_guid.to_string()).is_none() { + if view.type_by_id(&ref_guid.to_string()).is_none() { // Add the referrer to the view if it is in the Matcher types if let Some(ref_ty) = matcher.types.get(&ref_guid) { inner_add_type_to_view(matcher, view, arch, visited_refs, &ref_ty); @@ -186,7 +186,7 @@ impl Matcher { // All nested types _should_ be added now, we can add this type. // TODO: Do we want to make unnamed types visible? I think we should, but some people might be opposed. let ty_name = ty.name.to_owned().unwrap_or_else(|| ty_id_str.clone()); - view.define_auto_type_with_id(ty_name, ty_id_str, &to_bn_type(arch, ty)); + view.define_auto_type_with_id(ty_name, &ty_id_str, &to_bn_type(arch, ty)); } _ => {} } @@ -409,7 +409,7 @@ impl MatcherSettings { }); bn_settings.register_setting_json( Self::TRIVIAL_FUNCTION_LEN_SETTING, - trivial_function_len_props.to_string(), + &trivial_function_len_props.to_string(), ); let minimum_function_len_props = json!({ @@ -421,7 +421,7 @@ impl MatcherSettings { }); bn_settings.register_setting_json( Self::MINIMUM_FUNCTION_LEN_SETTING, - minimum_function_len_props.to_string(), + &minimum_function_len_props.to_string(), ); let maximum_function_len_props = json!({ @@ -433,7 +433,7 @@ impl MatcherSettings { }); bn_settings.register_setting_json( Self::MAXIMUM_FUNCTION_LEN_SETTING, - maximum_function_len_props.to_string(), + &maximum_function_len_props.to_string(), ); let minimum_matched_constraints_props = json!({ @@ -445,7 +445,7 @@ impl MatcherSettings { }); bn_settings.register_setting_json( Self::MINIMUM_MATCHED_CONSTRAINTS_SETTING, - minimum_matched_constraints_props.to_string(), + &minimum_matched_constraints_props.to_string(), ); let trivial_function_adjacent_allowed_props = json!({ @@ -457,7 +457,7 @@ impl MatcherSettings { }); bn_settings.register_setting_json( Self::TRIVIAL_FUNCTION_ADJACENT_ALLOWED_SETTING, - trivial_function_adjacent_allowed_props.to_string(), + &trivial_function_adjacent_allowed_props.to_string(), ); } @@ -506,7 +506,7 @@ pub struct PlatformID(u64); impl From<&Platform> for PlatformID { fn from(value: &Platform) -> Self { let mut hasher = DefaultHasher::new(); - hasher.write(value.name().to_bytes()); + hasher.write(value.name().as_bytes()); Self(hasher.finish()) } } diff --git a/plugins/warp/src/plugin.rs b/plugins/warp/src/plugin.rs index b84173f79c..ec903f9cb5 100644 --- a/plugins/warp/src/plugin.rs +++ b/plugins/warp/src/plugin.rs @@ -52,7 +52,7 @@ pub fn on_matched_function(function: &Function, matched: &WarpFunction) { // TODO: Add metadata. (both binja metadata and warp metadata) function.add_tag( &get_warp_tag_type(&view), - matched.guid.to_string(), + &matched.guid.to_string(), None, true, None, diff --git a/plugins/warp/src/plugin/copy.rs b/plugins/warp/src/plugin/copy.rs index 18da7dfb7b..b9b985fcc2 100644 --- a/plugins/warp/src/plugin/copy.rs +++ b/plugins/warp/src/plugin/copy.rs @@ -14,8 +14,8 @@ impl FunctionCommand for CopyFunctionGUID { }; let guid = cached_function_guid(func, &llil); log::info!( - "Function GUID for {}... {}", - func.symbol().short_name().to_string(), + "Function GUID for {:?}... {}", + func.symbol().short_name(), guid ); if let Ok(mut clipboard) = arboard::Clipboard::new() { diff --git a/plugins/warp/src/plugin/create.rs b/plugins/warp/src/plugin/create.rs index 4beba1952a..1dd83e6ea6 100644 --- a/plugins/warp/src/plugin/create.rs +++ b/plugins/warp/src/plugin/create.rs @@ -18,7 +18,7 @@ pub struct CreateSignatureFile; impl Command for CreateSignatureFile { fn action(&self, view: &BinaryView) { let is_function_named = |f: &Guard| { - !f.symbol().short_name().as_str().contains("sub_") || f.has_user_annotations() + !f.symbol().short_name().to_string_lossy().contains("sub_") || f.has_user_annotations() }; let mut signature_dir = user_signature_dir(); if let Some(default_plat) = view.default_platform() { @@ -31,7 +31,7 @@ impl Command for CreateSignatureFile { let total_functions = view.functions().len(); let done_functions = AtomicUsize::default(); let background_task = binaryninja::background_task::BackgroundTask::new( - format!("Generating signatures... ({}/{})", 0, total_functions), + &format!("Generating signatures... ({}/{})", 0, total_functions), true, ); @@ -43,7 +43,7 @@ impl Command for CreateSignatureFile { .par_iter() .inspect(|_| { done_functions.fetch_add(1, Relaxed); - background_task.set_progress_text(format!( + background_task.set_progress_text(&format!( "Generating signatures... ({}/{})", done_functions.load(Relaxed), total_functions diff --git a/plugins/warp/src/plugin/find.rs b/plugins/warp/src/plugin/find.rs index b2102e5e21..accc015d6e 100644 --- a/plugins/warp/src/plugin/find.rs +++ b/plugins/warp/src/plugin/find.rs @@ -27,7 +27,7 @@ impl Command for FindFunctionFromGUID { let funcs = view.functions(); thread::spawn(move || { let background_task = binaryninja::background_task::BackgroundTask::new( - format!("Searching functions for GUID... {}", searched_guid), + &format!("Searching functions for GUID... {}", searched_guid), false, ); diff --git a/plugins/warp/src/plugin/types.rs b/plugins/warp/src/plugin/types.rs index 057d5f98c4..41e03cd34c 100644 --- a/plugins/warp/src/plugin/types.rs +++ b/plugins/warp/src/plugin/types.rs @@ -35,7 +35,7 @@ impl Command for LoadTypes { let view = view.to_owned(); std::thread::spawn(move || { let background_task = binaryninja::background_task::BackgroundTask::new( - format!("Applying {} types...", data.types.len()), + &format!("Applying {} types...", data.types.len()), true, ); @@ -43,7 +43,7 @@ impl Command for LoadTypes { for comp_ty in data.types { let ty_id = comp_ty.guid.to_string(); let ty_name = comp_ty.ty.name.to_owned().unwrap_or_else(|| ty_id.clone()); - view.define_auto_type_with_id(ty_name, ty_id, &to_bn_type(&arch, &comp_ty.ty)); + view.define_auto_type_with_id(ty_name, &ty_id, &to_bn_type(&arch, &comp_ty.ty)); } log::info!("Type application took {:?}", start.elapsed()); diff --git a/plugins/warp/src/plugin/workflow.rs b/plugins/warp/src/plugin/workflow.rs index 5a857fbb45..f5763116ae 100644 --- a/plugins/warp/src/plugin/workflow.rs +++ b/plugins/warp/src/plugin/workflow.rs @@ -3,7 +3,6 @@ use crate::matcher::cached_function_matcher; use binaryninja::background_task::BackgroundTask; use binaryninja::binary_view::{BinaryView, BinaryViewExt}; use binaryninja::command::Command; -use binaryninja::low_level_il::function::RegularNonSSA; use binaryninja::workflow::{Activity, AnalysisContext, Workflow}; use std::time::Instant; @@ -44,7 +43,7 @@ impl Command for RunMatcher { .for_each(|function| cached_function_matcher(&function)); log::info!("Function matching took {:?}", start.elapsed()); background_task.finish(); - view.file().commit_undo_actions(undo_id); + view.file().commit_undo_actions(&undo_id); // Now we want to trigger re-analysis. view.update_analysis(); }); @@ -66,7 +65,7 @@ pub fn insert_workflow() { .for_each(|function| cached_function_matcher(&function)); log::info!("Function matching took {:?}", start.elapsed()); background_task.finish(); - view.file().commit_undo_actions(undo_id); + view.file().commit_undo_actions(&undo_id); // Now we want to trigger re-analysis. view.update_analysis(); }; @@ -74,7 +73,7 @@ pub fn insert_workflow() { let guid_activity = |ctx: &AnalysisContext| { let function = ctx.function(); // TODO: Returning RegularNonSSA means we cant modify the il (the lifting code was written just for lifted il, that needs to be fixed) - if let Some(llil) = unsafe { ctx.llil_function::() } { + if let Some(llil) = unsafe { ctx.llil_function() } { cached_function_guid(&function, &llil); } }; diff --git a/rust/Cargo.toml b/rust/Cargo.toml index 361b2057a6..16743cd919 100644 --- a/rust/Cargo.toml +++ b/rust/Cargo.toml @@ -18,8 +18,13 @@ log = { version = "0.4", features = ["std"] } rayon = { version = "1.10", optional = true } binaryninjacore-sys = { path = "binaryninjacore-sys" } thiserror = "2.0" +# Parts of the collaboration and workflow APIs consume and produce JSON. +serde_json = "1.0" [dev-dependencies] rstest = "0.24" tempfile = "3.15" -serial_test = "3.2" \ No newline at end of file +serial_test = "3.2" + +[package.metadata.typos] +default.extend-ignore-re = ["Collapsable"] \ No newline at end of file diff --git a/rust/examples/dump_type_library.rs b/rust/examples/dump_type_library.rs new file mode 100644 index 0000000000..00e7fb097f --- /dev/null +++ b/rust/examples/dump_type_library.rs @@ -0,0 +1,44 @@ +// Usage: cargo run --example dump_type_library + +use binaryninja::binary_view::BinaryView; +use binaryninja::file_metadata::FileMetadata; +use binaryninja::type_library::TypeLibrary; +use binaryninja::type_printer::{CoreTypePrinter, TokenEscapingType}; + +fn main() { + let type_lib_str = std::env::args().nth(1).expect("No type library provided"); + let type_lib_path = std::path::Path::new(&type_lib_str); + + println!("Starting session..."); + // This loads all the core architecture, platform, etc plugins + let _headless_session = + binaryninja::headless::Session::new().expect("Failed to initialize session"); + + let type_lib = TypeLibrary::load_from_file(type_lib_path).expect("Failed to load type library"); + let named_types = type_lib.named_types(); + println!("Name: `{}`", type_lib.name()); + println!("GUID: `{}`", type_lib.guid()); + + // Print out all the types as a c header. + let type_lib_header_path = type_lib_path.with_extension("h"); + println!( + "Dumping {} types to: `{:?}`", + named_types.len(), + type_lib_header_path + ); + let type_printer = CoreTypePrinter::default(); + let empty_bv = + BinaryView::from_data(&FileMetadata::new(), &[]).expect("Failed to create empty view"); + let printed_types = type_printer + .print_all_types( + &type_lib.named_types(), + &empty_bv, + 4, + TokenEscapingType::NoTokenEscapingType, + ) + .expect("Failed to print types"); + + // Write the header to disk. + std::fs::write(type_lib_header_path, printed_types) + .expect("Failed to write type library header"); +} diff --git a/rust/examples/flowgraph.rs b/rust/examples/flowgraph.rs index 6666b7fbc7..ede7ce84af 100644 --- a/rust/examples/flowgraph.rs +++ b/rust/examples/flowgraph.rs @@ -1,10 +1,83 @@ +use binaryninja::flowgraph::edge::EdgeStyle; +use binaryninja::flowgraph::FlowGraphNode; +use binaryninja::function::FunctionViewType; +use binaryninja::interaction::form::Form; +use binaryninja::interaction::handler::{ + register_interaction_handler, InteractionHandler, InteractionHandlerTask, +}; +use binaryninja::interaction::{MessageBoxButtonResult, MessageBoxButtonSet, MessageBoxIcon}; use binaryninja::{ binary_view::{BinaryView, BinaryViewExt}, disassembly::{DisassemblyTextLine, InstructionTextToken, InstructionTextTokenKind}, - flowgraph::{BranchType, EdgePenStyle, EdgeStyle, FlowGraph, FlowGraphNode, ThemeColor}, + flowgraph::{BranchType, EdgePenStyle, FlowGraph, ThemeColor}, }; -fn test_graph(view: &BinaryView) { +pub struct GraphPrinter; + +impl GraphPrinter { + pub fn print_graph(&self, graph: &FlowGraph) { + println!("Printing flow graph:"); + for node in &graph.nodes() { + // Print all disassembly lines in the node + println!("Node @ {:?}:", node.position()); + println!("------------------"); + println!("Disassembly lines:"); + for line in &node.lines() { + println!(" {}", line); + } + + // Print outgoing edges + println!("Outgoing edges:"); + for edge in &node.outgoing_edges() { + println!(" {:?} => {:?}", edge.branch_type, edge.target.position()); + } + println!("------------------"); + } + } +} + +impl InteractionHandler for GraphPrinter { + fn show_message_box( + &mut self, + _title: &str, + _text: &str, + _buttons: MessageBoxButtonSet, + _icon: MessageBoxIcon, + ) -> MessageBoxButtonResult { + MessageBoxButtonResult::CancelButton + } + + fn open_url(&mut self, _url: &str) -> bool { + false + } + + fn run_progress_dialog( + &mut self, + _title: &str, + _can_cancel: bool, + _task: &InteractionHandlerTask, + ) -> bool { + false + } + + fn show_plain_text_report(&mut self, _view: Option<&BinaryView>, title: &str, contents: &str) { + println!("Plain text report"); + println!("Title: {}", title); + println!("Contents: {}", contents); + } + + fn show_graph_report(&mut self, _view: Option<&BinaryView>, title: &str, graph: &FlowGraph) { + println!("Graph report"); + println!("Title: {}", title); + self.print_graph(graph); + } + + fn get_form_input(&mut self, _form: &mut Form) -> bool { + false + } +} + +fn test_graph() { let graph = FlowGraph::new(); let disassembly_lines_a = vec![DisassemblyTextLine::new(vec![ @@ -15,8 +88,10 @@ fn test_graph(view: &BinaryView) { let node_a = FlowGraphNode::new(&graph); node_a.set_lines(disassembly_lines_a); + node_a.set_position(1337, 7331); let node_b = FlowGraphNode::new(&graph); + node_b.set_position(100, 200); let disassembly_lines_b = vec![DisassemblyTextLine::new(vec![ InstructionTextToken::new("Li", InstructionTextTokenKind::Text), InstructionTextToken::new("ne", InstructionTextTokenKind::Text), @@ -35,7 +110,7 @@ fn test_graph(view: &BinaryView) { EdgeStyle::default(), ); - view.show_graph_report("Rust Graph Title", &graph); + graph.show("Rust Example Graph"); } fn main() { @@ -49,8 +124,16 @@ fn main() { .load("/bin/cat") .expect("Couldn't open `/bin/cat`"); - // TODO: Register BNInteractionHandlerCallbacks with showGraphReport pointing at our function - // TODO: Idea: register showGraphReport that dumps a dotgraph to stdin + // Register the interaction handler so we can see the graph report headlessly. + register_interaction_handler(GraphPrinter); + + test_graph(); - test_graph(&bv); + for func in bv.functions().iter().take(5) { + // TODO: Why are the nodes empty? Python its empty until its shown... + let graph = func.create_graph(FunctionViewType::MediumLevelIL, None); + let func_name = func.symbol().short_name(); + let title = func_name.to_string_lossy(); + bv.show_graph_report(&title, &graph); + } } diff --git a/rust/examples/high_level_il.rs b/rust/examples/high_level_il.rs index d57509da47..ddc8f08d1b 100644 --- a/rust/examples/high_level_il.rs +++ b/rust/examples/high_level_il.rs @@ -16,7 +16,7 @@ fn main() { println!("Function count: {}", bv.functions().len()); for func in &bv.functions() { - println!("{}:", func.symbol().full_name()); + println!("{:?}:", func.symbol().full_name()); let Ok(il) = func.high_level_il(true) else { continue; diff --git a/rust/examples/medium_level_il.rs b/rust/examples/medium_level_il.rs index 61c3a33041..543a085629 100644 --- a/rust/examples/medium_level_il.rs +++ b/rust/examples/medium_level_il.rs @@ -16,7 +16,7 @@ fn main() { println!("Function count: {}", bv.functions().len()); for func in &bv.functions() { - println!("{}:", func.symbol().full_name()); + println!("{:?}:", func.symbol().full_name()); let Ok(il) = func.medium_level_il() else { continue; diff --git a/rust/examples/simple.rs b/rust/examples/simple.rs index f41cbf344f..c6065d48ef 100644 --- a/rust/examples/simple.rs +++ b/rust/examples/simple.rs @@ -17,7 +17,7 @@ fn main() { println!("Function count: {}", bv.functions().len()); for func in &bv.functions() { - println!("{}:", func.symbol().full_name()); + println!("{:?}:", func.symbol().full_name()); for basic_block in &func.basic_blocks() { // TODO : This is intended to be refactored to be more nice to work with soon(TM) for addr in basic_block.as_ref() { diff --git a/rust/examples/type_printer.rs b/rust/examples/type_printer.rs index ea9c6a4db1..846e1a0908 100644 --- a/rust/examples/type_printer.rs +++ b/rust/examples/type_printer.rs @@ -35,5 +35,5 @@ fn main() { TokenEscapingType::NoTokenEscapingType, ); - println!("{}", printed_types.unwrap()); + println!("{:?}", printed_types.unwrap()); } diff --git a/rust/examples/workflow.rs b/rust/examples/workflow.rs index 2d8f00540e..4956057935 100644 --- a/rust/examples/workflow.rs +++ b/rust/examples/workflow.rs @@ -41,7 +41,7 @@ fn example_activity(analysis_context: &AnalysisContext) { } } } - analysis_context.set_lifted_il_function(&llil); + analysis_context.set_llil_function(&llil.finalized()); } } diff --git a/rust/src/architecture.rs b/rust/src/architecture.rs index 3573f1fb23..411508402e 100644 --- a/rust/src/architecture.rs +++ b/rust/src/architecture.rs @@ -23,11 +23,10 @@ use crate::{ calling_convention::CoreCallingConvention, data_buffer::DataBuffer, disassembly::InstructionTextToken, - low_level_il::{MutableLiftedILExpr, MutableLiftedILFunction}, platform::Platform, rc::*, relocation::CoreRelocationHandler, - string::BnStrCompatible, + string::IntoCStr, string::*, types::{NameAndType, Type}, Endianness, @@ -50,6 +49,7 @@ use crate::low_level_il::expression::ValueExpr; use crate::low_level_il::lifting::{ get_default_flag_cond_llil, get_default_flag_write_llil, LowLevelILFlagWriteOp, }; +use crate::low_level_il::{LowLevelILMutableExpression, LowLevelILMutableFunction}; pub use binaryninjacore_sys::BNFlagRole as FlagRole; pub use binaryninjacore_sys::BNImplicitRegisterExtend as ImplicitRegisterExtend; pub use binaryninjacore_sys::BNLowLevelILFlagCondition as FlagCondition; @@ -80,6 +80,13 @@ macro_rules! newtype { } newtype!(RegisterId, u32); + +impl RegisterId { + pub fn is_temporary(&self) -> bool { + self.0 & 0x8000_0000 != 0 + } +} + newtype!(RegisterStackId, u32); newtype!(FlagId, u32); // TODO: Make this NonZero? @@ -460,7 +467,7 @@ pub trait Architecture: 'static + Sized + AsRef { &self, data: &[u8], addr: u64, - il: &mut MutableLiftedILFunction, + il: &LowLevelILMutableFunction, ) -> Option<(usize, bool)>; /// Fallback flag value calculation path. This method is invoked when the core is unable to @@ -477,8 +484,8 @@ pub trait Architecture: 'static + Sized + AsRef { flag: Self::Flag, flag_write_type: Self::FlagWrite, op: LowLevelILFlagWriteOp, - il: &'a mut MutableLiftedILFunction, - ) -> Option> { + il: &'a LowLevelILMutableFunction, + ) -> Option> { let role = flag.role(flag_write_type.class()); Some(get_default_flag_write_llil(self, role, op, il)) } @@ -506,8 +513,8 @@ pub trait Architecture: 'static + Sized + AsRef { &self, cond: FlagCondition, class: Option, - il: &'a mut MutableLiftedILFunction, - ) -> Option> { + il: &'a LowLevelILMutableFunction, + ) -> Option> { Some(get_default_flag_cond_llil(self, cond, class, il)) } @@ -528,8 +535,8 @@ pub trait Architecture: 'static + Sized + AsRef { fn flag_group_llil<'a>( &self, _group: Self::FlagGroup, - _il: &'a mut MutableLiftedILFunction, - ) -> Option> { + _il: &'a LowLevelILMutableFunction, + ) -> Option> { None } @@ -856,6 +863,7 @@ impl Debug for CoreRegister { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { f.debug_struct("CoreRegister") .field("id", &self.id) + .field("name", &self.name()) .finish() } } @@ -1397,16 +1405,16 @@ impl CoreArchitecture { } pub fn by_name(name: &str) -> Option { - let handle = - unsafe { BNGetArchitectureByName(name.into_bytes_with_nul().as_ptr() as *mut _) }; + let name = name.to_cstr(); + let handle = unsafe { BNGetArchitectureByName(name.as_ptr()) }; match handle.is_null() { false => Some(CoreArchitecture { handle }), true => None, } } - pub fn name(&self) -> BnString { - unsafe { BnString::from_raw(BNGetArchitectureName(self.handle)) } + pub fn name(&self) -> String { + unsafe { BnString::into_string(BNGetArchitectureName(self.handle)) } } } @@ -1505,7 +1513,7 @@ impl Architecture for CoreArchitecture { &self, data: &[u8], addr: u64, - il: &mut MutableLiftedILFunction, + il: &LowLevelILMutableFunction, ) -> Option<(usize, bool)> { let mut size = data.len(); let success = unsafe { @@ -1530,8 +1538,8 @@ impl Architecture for CoreArchitecture { _flag: Self::Flag, _flag_write: Self::FlagWrite, _op: LowLevelILFlagWriteOp, - _il: &'a mut MutableLiftedILFunction, - ) -> Option> { + _il: &'a LowLevelILMutableFunction, + ) -> Option> { None } @@ -1567,16 +1575,16 @@ impl Architecture for CoreArchitecture { &self, _cond: FlagCondition, _class: Option, - _il: &'a mut MutableLiftedILFunction, - ) -> Option> { + _il: &'a LowLevelILMutableFunction, + ) -> Option> { None } fn flag_group_llil<'a>( &self, _group: Self::FlagGroup, - _il: &'a mut MutableLiftedILFunction, - ) -> Option> { + _il: &'a LowLevelILMutableFunction, + ) -> Option> { None } @@ -1803,6 +1811,7 @@ impl Architecture for CoreArchitecture { Ok(result) => result, Err(_) => return Err("Result buffer allocation failed".to_string()), }; + // TODO: This is actually a list of errors. let mut error_raw: *mut c_char = std::ptr::null_mut(); let res = unsafe { BNAssemble( @@ -1945,12 +1954,10 @@ macro_rules! cc_func { /// Contains helper methods for all types implementing 'Architecture' pub trait ArchitectureExt: Architecture { - fn register_by_name(&self, name: S) -> Option { - let name = name.into_bytes_with_nul(); + fn register_by_name(&self, name: &str) -> Option { + let name = name.to_cstr(); - match unsafe { - BNGetArchitectureRegisterByName(self.as_ref().handle, name.as_ref().as_ptr() as *mut _) - } { + match unsafe { BNGetArchitectureRegisterByName(self.as_ref().handle, name.as_ptr()) } { 0xffff_ffff => None, reg => self.register_from_id(reg.into()), } @@ -2023,9 +2030,8 @@ pub trait ArchitectureExt: Architecture { } } - fn register_relocation_handler(&self, name: S, func: F) + fn register_relocation_handler(&self, name: &str, func: F) where - S: BnStrCompatible, R: 'static + RelocationHandler> + Send @@ -2046,9 +2052,8 @@ pub trait ArchitectureExt: Architecture { impl ArchitectureExt for T {} -pub fn register_architecture(name: S, func: F) -> &'static A +pub fn register_architecture(name: &str, func: F) -> &'static A where - S: BnStrCompatible, A: 'static + Architecture> + Send + Sync + Sized, F: FnOnce(CustomArchitectureHandle, CoreArchitecture) -> A, { @@ -2218,14 +2223,12 @@ where A: 'static + Architecture> + Send + Sync, { let custom_arch = unsafe { &*(ctxt as *mut A) }; - let custom_arch_handle = CustomArchitectureHandle { - handle: ctxt as *mut A, - }; - let data = unsafe { std::slice::from_raw_parts(data, *len) }; - let mut lifter = unsafe { MutableLiftedILFunction::from_raw(custom_arch_handle, il) }; + let lifter = unsafe { + LowLevelILMutableFunction::from_raw_with_arch(il, Some(*custom_arch.as_ref())) + }; - match custom_arch.instruction_llil(data, addr, &mut lifter) { + match custom_arch.instruction_llil(data, addr, &lifter) { Some((res_len, res_value)) => { unsafe { *len = res_len }; res_value @@ -2606,18 +2609,16 @@ where A: 'static + Architecture> + Send + Sync, { let custom_arch = unsafe { &*(ctxt as *mut A) }; - let custom_arch_handle = CustomArchitectureHandle { - handle: ctxt as *mut A, - }; - let flag_write = custom_arch.flag_write_from_id(FlagWriteId(flag_write)); let flag = custom_arch.flag_from_id(FlagId(flag)); let operands = unsafe { std::slice::from_raw_parts(operands_raw, operand_count) }; - let mut lifter = unsafe { MutableLiftedILFunction::from_raw(custom_arch_handle, il) }; + let lifter = unsafe { + LowLevelILMutableFunction::from_raw_with_arch(il, Some(*custom_arch.as_ref())) + }; if let (Some(flag_write), Some(flag)) = (flag_write, flag) { if let Some(op) = LowLevelILFlagWriteOp::from_op(custom_arch, size, op, operands) { - if let Some(expr) = custom_arch.flag_write_llil(flag, flag_write, op, &mut lifter) { + if let Some(expr) = custom_arch.flag_write_llil(flag, flag_write, op, &lifter) { // TODO verify that returned expr is a bool value return expr.index.0; } @@ -2659,14 +2660,12 @@ where A: 'static + Architecture> + Send + Sync, { let custom_arch = unsafe { &*(ctxt as *mut A) }; - let custom_arch_handle = CustomArchitectureHandle { - handle: ctxt as *mut A, - }; - let class = custom_arch.flag_class_from_id(FlagClassId(class)); - let mut lifter = unsafe { MutableLiftedILFunction::from_raw(custom_arch_handle, il) }; - if let Some(expr) = custom_arch.flag_cond_llil(cond, class, &mut lifter) { + let lifter = unsafe { + LowLevelILMutableFunction::from_raw_with_arch(il, Some(*custom_arch.as_ref())) + }; + if let Some(expr) = custom_arch.flag_cond_llil(cond, class, &lifter) { // TODO verify that returned expr is a bool value return expr.index.0; } @@ -2683,14 +2682,12 @@ where A: 'static + Architecture> + Send + Sync, { let custom_arch = unsafe { &*(ctxt as *mut A) }; - let custom_arch_handle = CustomArchitectureHandle { - handle: ctxt as *mut A, + let lifter = unsafe { + LowLevelILMutableFunction::from_raw_with_arch(il, Some(*custom_arch.as_ref())) }; - let mut lifter = unsafe { MutableLiftedILFunction::from_raw(custom_arch_handle, il) }; - if let Some(group) = custom_arch.flag_group_from_id(FlagGroupId(group)) { - if let Some(expr) = custom_arch.flag_group_llil(group, &mut lifter) { + if let Some(expr) = custom_arch.flag_group_llil(group, &lifter) { // TODO verify that returned expr is a bool value return expr.index.0; } @@ -3134,7 +3131,7 @@ where custom_arch.skip_and_return_value(data, addr, val) } - let name = name.into_bytes_with_nul(); + let name = name.to_cstr(); let uninit_arch = ArchitectureBuilder { arch: MaybeUninit::zeroed(), @@ -3225,8 +3222,7 @@ where }; unsafe { - let res = - BNRegisterArchitecture(name.as_ref().as_ptr() as *mut _, &mut custom_arch as *mut _); + let res = BNRegisterArchitecture(name.as_ptr(), &mut custom_arch as *mut _); assert!(!res.is_null()); @@ -3234,6 +3230,7 @@ where } } +#[derive(Debug)] pub struct CustomArchitectureHandle where A: 'static + Architecture> + Send + Sync, diff --git a/rust/src/background_task.rs b/rust/src/background_task.rs index c818d5b950..ea1e3b0db2 100644 --- a/rust/src/background_task.rs +++ b/rust/src/background_task.rs @@ -43,9 +43,9 @@ impl BackgroundTask { Self { handle } } - pub fn new(initial_text: S, can_cancel: bool) -> Ref { - let text = initial_text.into_bytes_with_nul(); - let handle = unsafe { BNBeginBackgroundTask(text.as_ref().as_ptr() as *mut _, can_cancel) }; + pub fn new(initial_text: &str, can_cancel: bool) -> Ref { + let text = initial_text.to_cstr(); + let handle = unsafe { BNBeginBackgroundTask(text.as_ptr(), can_cancel) }; // We should always be returned a valid task. assert!(!handle.is_null()); unsafe { Ref::new(Self { handle }) } @@ -71,15 +71,13 @@ impl BackgroundTask { unsafe { BNFinishBackgroundTask(self.handle) } } - pub fn progress_text(&self) -> BnString { - unsafe { BnString::from_raw(BNGetBackgroundTaskProgressText(self.handle)) } + pub fn progress_text(&self) -> String { + unsafe { BnString::into_string(BNGetBackgroundTaskProgressText(self.handle)) } } - pub fn set_progress_text(&self, text: S) { - let progress_text = text.into_bytes_with_nul(); - unsafe { - BNSetBackgroundTaskProgressText(self.handle, progress_text.as_ref().as_ptr() as *mut _) - } + pub fn set_progress_text(&self, text: &str) { + let progress_text = text.to_cstr(); + unsafe { BNSetBackgroundTaskProgressText(self.handle, progress_text.as_ptr()) } } pub fn running_tasks() -> Array { diff --git a/rust/src/base_detection.rs b/rust/src/base_detection.rs index 1fd45ef633..c74f3f3a82 100644 --- a/rust/src/base_detection.rs +++ b/rust/src/base_detection.rs @@ -174,9 +174,9 @@ impl BaseAddressDetectionSettings { let arch_name = value .arch .map(|a| a.name().as_ptr()) - .unwrap_or(BASE_ADDRESS_AUTO_DETECTION_ARCH.as_ptr() as *const c_char); + .unwrap_or(BASE_ADDRESS_AUTO_DETECTION_ARCH.as_ptr() as *const u8); BNBaseAddressDetectionSettings { - Architecture: arch_name, + Architecture: arch_name as *const c_char, Analysis: value.analysis.as_raw().as_ptr(), MinStrlen: value.min_string_len, Alignment: value.alignment.get(), diff --git a/rust/src/binary_view.rs b/rust/src/binary_view.rs index db4c92d749..cae53e66fa 100644 --- a/rust/src/binary_view.rs +++ b/rust/src/binary_view.rs @@ -26,13 +26,12 @@ use binaryninjacore_sys::*; use crate::architecture::{Architecture, CoreArchitecture}; use crate::base_detection::BaseAddressDetection; use crate::basic_block::BasicBlock; -use crate::binary_view::memory_map::MemoryMap; -use crate::component::{Component, IntoComponentGuid}; +use crate::component::Component; use crate::confidence::Conf; use crate::data_buffer::DataBuffer; use crate::debuginfo::DebugInfo; use crate::external_library::{ExternalLibrary, ExternalLocation}; -use crate::file_accessor::FileAccessor; +use crate::file_accessor::{Accessor, FileAccessor}; use crate::file_metadata::FileMetadata; use crate::flowgraph::FlowGraph; use crate::function::{Function, NativeBlock}; @@ -66,6 +65,12 @@ use std::{result, slice}; // TODO : general reorg of modules related to bv pub mod memory_map; +pub mod reader; +pub mod writer; + +pub use memory_map::MemoryMap; +pub use reader::BinaryReader; +pub use writer::BinaryWriter; pub type Result = result::Result; pub type BinaryViewEventType = BNBinaryViewEventType; @@ -187,9 +192,9 @@ pub trait BinaryViewExt: BinaryViewBase { } } - fn type_name(&self) -> BnString { + fn type_name(&self) -> String { let ptr: *mut c_char = unsafe { BNGetViewType(self.as_ref().handle) }; - unsafe { BnString::from_raw(ptr) } + unsafe { BnString::into_string(ptr) } } fn parent_view(&self) -> Option> { @@ -204,9 +209,9 @@ pub trait BinaryViewExt: BinaryViewBase { self.file().view_of_type("Raw") } - fn view_type(&self) -> BnString { + fn view_type(&self) -> String { let ptr: *mut c_char = unsafe { BNGetViewType(self.as_ref().handle) }; - unsafe { BnString::from_raw(ptr) } + unsafe { BnString::into_string(ptr) } } /// Reads up to `len` bytes from address `offset` @@ -266,13 +271,9 @@ pub trait BinaryViewExt: BinaryViewBase { unsafe { BNGetEndOffset(self.as_ref().handle) } } - fn add_analysis_option(&self, name: impl BnStrCompatible) { - unsafe { - BNAddAnalysisOption( - self.as_ref().handle, - name.into_bytes_with_nul().as_ref().as_ptr() as *mut _, - ) - } + fn add_analysis_option(&self, name: &str) { + let name = name.to_cstr(); + unsafe { BNAddAnalysisOption(self.as_ref().handle, name.as_ptr()) } } fn has_initial_analysis(&self) -> bool { @@ -403,13 +404,13 @@ pub trait BinaryViewExt: BinaryViewBase { } } - fn symbol_by_raw_name(&self, raw_name: S) -> Option> { - let raw_name = raw_name.into_bytes_with_nul(); + fn symbol_by_raw_name(&self, raw_name: impl IntoCStr) -> Option> { + let raw_name = raw_name.to_cstr(); unsafe { let raw_sym_ptr = BNGetSymbolByRawName( self.as_ref().handle, - raw_name.as_ref().as_ptr() as *mut _, + raw_name.as_ptr(), std::ptr::null_mut(), ); match raw_sym_ptr.is_null() { @@ -428,14 +429,14 @@ pub trait BinaryViewExt: BinaryViewBase { } } - fn symbols_by_name(&self, name: S) -> Array { - let raw_name = name.into_bytes_with_nul(); + fn symbols_by_name(&self, name: impl IntoCStr) -> Array { + let raw_name = name.to_cstr(); unsafe { let mut count = 0; let handles = BNGetSymbolsByName( self.as_ref().handle, - raw_name.as_ref().as_ptr() as *mut _, + raw_name.as_ptr(), &mut count, std::ptr::null_mut(), ); @@ -589,14 +590,14 @@ pub trait BinaryViewExt: BinaryViewBase { } } - fn define_auto_type, S: BnStrCompatible>( + fn define_auto_type>( &self, name: T, - source: S, + source: &str, type_obj: &Type, ) -> QualifiedName { let mut raw_name = QualifiedName::into_raw(name.into()); - let source_str = source.into_bytes_with_nul(); + let source_str = source.to_cstr(); let name_handle = unsafe { let id_str = BNGenerateAutoTypeId(source_str.as_ref().as_ptr() as *const _, &mut raw_name); @@ -606,14 +607,14 @@ pub trait BinaryViewExt: BinaryViewBase { QualifiedName::from_owned_raw(name_handle) } - fn define_auto_type_with_id, S: BnStrCompatible>( + fn define_auto_type_with_id>( &self, name: T, - id: S, + id: &str, type_obj: &Type, ) -> QualifiedName { let mut raw_name = QualifiedName::into_raw(name.into()); - let id_str = id.into_bytes_with_nul(); + let id_str = id.to_cstr(); let result_raw_name = unsafe { BNDefineAnalysisType( self.as_ref().handle, @@ -716,8 +717,8 @@ pub trait BinaryViewExt: BinaryViewBase { } } - fn undefine_auto_type(&self, id: S) { - let id_str = id.into_bytes_with_nul(); + fn undefine_auto_type(&self, id: &str) { + let id_str = id.to_cstr(); unsafe { BNUndefineAnalysisType(self.as_ref().handle, id_str.as_ref().as_ptr() as *const _); } @@ -767,11 +768,10 @@ pub trait BinaryViewExt: BinaryViewBase { } } - fn type_by_id(&self, id: S) -> Option> { + fn type_by_id(&self, id: &str) -> Option> { + let id_str = id.to_cstr(); unsafe { - let id_str = id.into_bytes_with_nul(); - let type_handle = - BNGetAnalysisTypeById(self.as_ref().handle, id_str.as_ref().as_ptr() as *mut _); + let type_handle = BNGetAnalysisTypeById(self.as_ref().handle, id_str.as_ptr()); if type_handle.is_null() { return None; } @@ -779,11 +779,10 @@ pub trait BinaryViewExt: BinaryViewBase { } } - fn type_name_by_id(&self, id: S) -> Option { + fn type_name_by_id(&self, id: &str) -> Option { + let id_str = id.to_cstr(); unsafe { - 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_handle = BNGetAnalysisTypeNameById(self.as_ref().handle, id_str.as_ptr()); 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() { @@ -793,12 +792,12 @@ pub trait BinaryViewExt: BinaryViewBase { } } - fn type_id_by_name>(&self, name: T) -> Option { + fn type_id_by_name>(&self, name: T) -> Option { 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); + let id = BnString::into_string(id_cstr); match id.is_empty() { true => None, false => Some(id), @@ -877,26 +876,26 @@ pub trait BinaryViewExt: BinaryViewBase { section.create(self.as_ref()); } - fn remove_auto_section(&self, name: S) { - let raw_name = name.into_bytes_with_nul(); - let raw_name_ptr = raw_name.as_ref().as_ptr() as *mut _; + fn remove_auto_section(&self, name: impl IntoCStr) { + let raw_name = name.to_cstr(); + let raw_name_ptr = raw_name.as_ptr(); unsafe { BNRemoveAutoSection(self.as_ref().handle, raw_name_ptr); } } - fn remove_user_section(&self, name: S) { - let raw_name = name.into_bytes_with_nul(); - let raw_name_ptr = raw_name.as_ref().as_ptr() as *mut _; + fn remove_user_section(&self, name: impl IntoCStr) { + let raw_name = name.to_cstr(); + let raw_name_ptr = raw_name.as_ptr(); unsafe { BNRemoveUserSection(self.as_ref().handle, raw_name_ptr); } } - fn section_by_name(&self, name: S) -> Option> { + fn section_by_name(&self, name: impl IntoCStr) -> Option> { unsafe { - let raw_name = name.into_bytes_with_nul(); - let name_ptr = raw_name.as_ref().as_ptr() as *mut _; + let raw_name = name.to_cstr(); + let name_ptr = raw_name.as_ptr(); let raw_section_ptr = BNGetSectionByName(self.as_ref().handle, name_ptr); match raw_section_ptr.is_null() { false => Some(Section::ref_from_raw(raw_section_ptr)), @@ -1109,40 +1108,72 @@ pub trait BinaryViewExt: BinaryViewBase { unsafe { BNApplyDebugInfo(self.as_ref().handle, debug_info.handle) } } - fn show_graph_report(&self, raw_name: S, graph: &FlowGraph) { - let raw_name = raw_name.into_bytes_with_nul(); + fn show_plaintext_report(&self, title: &str, plaintext: &str) { + let title = title.to_cstr(); + let plaintext = plaintext.to_cstr(); unsafe { - BNShowGraphReport( + BNShowPlainTextReport( self.as_ref().handle, - raw_name.as_ref().as_ptr() as *mut _, - graph.handle, - ); + title.as_ref().as_ptr() as *mut _, + plaintext.as_ref().as_ptr() as *mut _, + ) } } - fn load_settings(&self, view_type_name: S) -> Result> { - let view_type_name = view_type_name.into_bytes_with_nul(); - let settings_handle = unsafe { - BNBinaryViewGetLoadSettings( + fn show_markdown_report(&self, title: &str, contents: &str, plaintext: &str) { + let title = title.to_cstr(); + let contents = contents.to_cstr(); + let plaintext = plaintext.to_cstr(); + unsafe { + BNShowMarkdownReport( self.as_ref().handle, - view_type_name.as_ref().as_ptr() as *mut _, + title.as_ref().as_ptr() as *mut _, + contents.as_ref().as_ptr() as *mut _, + plaintext.as_ref().as_ptr() as *mut _, ) - }; + } + } + + fn show_html_report(&self, title: &str, contents: &str, plaintext: &str) { + let title = title.to_cstr(); + let contents = contents.to_cstr(); + let plaintext = plaintext.to_cstr(); + unsafe { + BNShowHTMLReport( + self.as_ref().handle, + title.as_ref().as_ptr() as *mut _, + contents.as_ref().as_ptr() as *mut _, + plaintext.as_ref().as_ptr() as *mut _, + ) + } + } + + fn show_graph_report(&self, raw_name: &str, graph: &FlowGraph) { + let raw_name = raw_name.to_cstr(); + unsafe { + BNShowGraphReport(self.as_ref().handle, raw_name.as_ptr(), graph.handle); + } + } + + fn load_settings(&self, view_type_name: &str) -> Result> { + let view_type_name = view_type_name.to_cstr(); + let settings_handle = + unsafe { BNBinaryViewGetLoadSettings(self.as_ref().handle, view_type_name.as_ptr()) }; if settings_handle.is_null() { Err(()) } else { - Ok(unsafe { Settings::from_raw(settings_handle) }) + Ok(unsafe { Settings::ref_from_raw(settings_handle) }) } } - fn set_load_settings(&self, view_type_name: S, settings: &Settings) { - let view_type_name = view_type_name.into_bytes_with_nul(); + fn set_load_settings(&self, view_type_name: &str, settings: &Settings) { + let view_type_name = view_type_name.to_cstr(); unsafe { BNBinaryViewSetLoadSettings( self.as_ref().handle, - view_type_name.as_ref().as_ptr() as *mut _, + view_type_name.as_ptr(), settings.handle, ) }; @@ -1153,11 +1184,7 @@ pub trait BinaryViewExt: BinaryViewBase { /// # Arguments /// * `name` - the name for the tag /// * `icon` - the icon (recommended 1 emoji or 2 chars) for the tag - fn create_tag_type( - &self, - name: N, - icon: I, - ) -> Ref { + fn create_tag_type(&self, name: &str, icon: &str) -> Ref { let tag_type = TagType::create(self.as_ref(), name, icon); unsafe { BNAddTagType(self.as_ref().handle, tag_type.handle); @@ -1171,10 +1198,10 @@ pub trait BinaryViewExt: BinaryViewBase { } /// Get a tag type by its name. - fn tag_type_by_name(&self, name: S) -> Option> { - let name = name.into_bytes_with_nul(); + fn tag_type_by_name(&self, name: &str) -> Option> { + let name = name.to_cstr(); unsafe { - let handle = BNGetTagType(self.as_ref().handle, name.as_ref().as_ptr() as *mut _); + let handle = BNGetTagType(self.as_ref().handle, name.as_ptr()); if handle.is_null() { return None; } @@ -1185,10 +1212,10 @@ pub trait BinaryViewExt: BinaryViewBase { /// Get a tag by its id. /// /// Note this does not tell you anything about where it is used. - fn tag_by_id(&self, id: S) -> Option> { - let id = id.into_bytes_with_nul(); + fn tag_by_id(&self, id: &str) -> Option> { + let id = id.to_cstr(); unsafe { - let handle = BNGetTag(self.as_ref().handle, id.as_ref().as_ptr() as *mut _); + let handle = BNGetTag(self.as_ref().handle, id.as_ptr()); if handle.is_null() { return None; } @@ -1199,7 +1226,7 @@ pub trait BinaryViewExt: BinaryViewBase { /// Creates and adds a tag to an address /// /// User tag creations will be added to the undo buffer - fn add_tag(&self, addr: u64, t: &TagType, data: S, user: bool) { + fn add_tag(&self, addr: u64, t: &TagType, data: &str, user: bool) { let tag = Tag::new(t, data); unsafe { BNAddTag(self.as_ref().handle, tag.handle, user) } @@ -1222,11 +1249,11 @@ pub trait BinaryViewExt: BinaryViewBase { unsafe { BNRemoveUserDataTag(self.as_ref().handle, addr, tag.handle) } } - fn comment_at(&self, addr: u64) -> Option { + fn comment_at(&self, addr: u64) -> Option { unsafe { let comment_raw = BNGetGlobalCommentForAddress(self.as_ref().handle, addr); match comment_raw.is_null() { - false => Some(BnString::from_raw(comment_raw)), + false => Some(BnString::into_string(comment_raw)), true => None, } } @@ -1236,15 +1263,9 @@ pub trait BinaryViewExt: BinaryViewBase { /// /// NOTE: This is different from setting a comment at the function-level. To set a comment in a /// function use [`Function::set_comment_at`] - fn set_comment_at(&self, addr: u64, comment: impl BnStrCompatible) { - let comment_raw = comment.into_bytes_with_nul(); - unsafe { - BNSetGlobalCommentForAddress( - self.as_ref().handle, - addr, - comment_raw.as_ref().as_ptr() as *const c_char, - ) - } + fn set_comment_at(&self, addr: u64, comment: &str) { + let comment_raw = comment.to_cstr(); + unsafe { BNSetGlobalCommentForAddress(self.as_ref().handle, addr, comment_raw.as_ptr()) } } /// Retrieves a list of the next disassembly lines. @@ -1295,13 +1316,10 @@ pub trait BinaryViewExt: BinaryViewBase { result } - fn query_metadata(&self, key: S) -> Option> { - let value: *mut BNMetadata = unsafe { - BNBinaryViewQueryMetadata( - self.as_ref().handle, - key.into_bytes_with_nul().as_ref().as_ptr() as *const c_char, - ) - }; + fn query_metadata(&self, key: &str) -> Option> { + let key = key.to_cstr(); + let value: *mut BNMetadata = + unsafe { BNBinaryViewQueryMetadata(self.as_ref().handle, key.as_ptr()) }; if value.is_null() { None } else { @@ -1309,7 +1327,7 @@ pub trait BinaryViewExt: BinaryViewBase { } } - fn get_metadata(&self, key: S) -> Option> + fn get_metadata(&self, key: &str) -> Option> where T: for<'a> TryFrom<&'a Metadata>, { @@ -1317,28 +1335,25 @@ pub trait BinaryViewExt: BinaryViewBase { .map(|md| T::try_from(md.as_ref()).map_err(|_| ())) } - fn store_metadata(&self, key: S, value: V, is_auto: bool) + fn store_metadata(&self, key: &str, value: V, is_auto: bool) where V: Into>, { let md = value.into(); + let key = key.to_cstr(); unsafe { BNBinaryViewStoreMetadata( self.as_ref().handle, - key.into_bytes_with_nul().as_ref().as_ptr() as *const c_char, + key.as_ptr(), md.as_ref().handle, is_auto, ) }; } - fn remove_metadata(&self, key: S) { - unsafe { - BNBinaryViewRemoveMetadata( - self.as_ref().handle, - key.into_bytes_with_nul().as_ref().as_ptr() as *const c_char, - ) - }; + fn remove_metadata(&self, key: &str) { + let key = key.to_cstr(); + unsafe { BNBinaryViewRemoveMetadata(self.as_ref().handle, key.as_ptr()) }; } /// Retrieves a list of [CodeReference]s pointing to a given address. @@ -1462,14 +1477,9 @@ pub trait BinaryViewExt: BinaryViewBase { .collect() } - fn component_by_guid(&self, guid: S) -> Option> { - let name = guid.into_bytes_with_nul(); - let result = unsafe { - BNGetComponentByGuid( - self.as_ref().handle, - name.as_ref().as_ptr() as *const c_char, - ) - }; + fn component_by_guid(&self, guid: &str) -> Option> { + let name = guid.to_cstr(); + let result = unsafe { BNGetComponentByGuid(self.as_ref().handle, name.as_ptr()) }; NonNull::new(result).map(|h| unsafe { Component::ref_from_raw(h) }) } @@ -1478,14 +1488,9 @@ pub trait BinaryViewExt: BinaryViewBase { NonNull::new(result).map(|h| unsafe { Component::ref_from_raw(h) }) } - fn component_by_path(&self, path: P) -> Option> { - let path = path.into_bytes_with_nul(); - let result = unsafe { - BNGetComponentByPath( - self.as_ref().handle, - path.as_ref().as_ptr() as *const c_char, - ) - }; + fn component_by_path(&self, path: &str) -> Option> { + let path = path.to_cstr(); + let result = unsafe { BNGetComponentByPath(self.as_ref().handle, path.as_ptr()) }; NonNull::new(result).map(|h| unsafe { Component::ref_from_raw(h) }) } @@ -1493,8 +1498,8 @@ pub trait BinaryViewExt: BinaryViewBase { unsafe { BNRemoveComponent(self.as_ref().handle, component.handle.as_ptr()) } } - fn remove_component_by_guid(&self, guid: P) -> bool { - let path = guid.component_guid(); + fn remove_component_by_guid(&self, guid: &str) -> bool { + let path = guid.to_cstr(); unsafe { BNRemoveComponentByGuid(self.as_ref().handle, path.as_ptr()) } } @@ -1516,39 +1521,30 @@ pub trait BinaryViewExt: BinaryViewBase { unsafe { Array::new(result, count, ()) } } - fn external_library(&self, name: S) -> Option> { - let name_ptr = name.into_bytes_with_nul(); - let result = unsafe { - BNBinaryViewGetExternalLibrary( - self.as_ref().handle, - name_ptr.as_ref().as_ptr() as *const c_char, - ) - }; + fn external_library(&self, name: &str) -> Option> { + let name_ptr = name.to_cstr(); + let result = + unsafe { BNBinaryViewGetExternalLibrary(self.as_ref().handle, name_ptr.as_ptr()) }; let result_ptr = NonNull::new(result)?; Some(unsafe { ExternalLibrary::ref_from_raw(result_ptr) }) } - fn remove_external_library(&self, name: S) { - let name_ptr = name.into_bytes_with_nul(); - unsafe { - BNBinaryViewRemoveExternalLibrary( - self.as_ref().handle, - name_ptr.as_ref().as_ptr() as *const c_char, - ) - }; + fn remove_external_library(&self, name: &str) { + let name_ptr = name.to_cstr(); + unsafe { BNBinaryViewRemoveExternalLibrary(self.as_ref().handle, name_ptr.as_ptr()) }; } - fn add_external_library( + fn add_external_library( &self, - name: S, + name: &str, backing_file: Option<&ProjectFile>, auto: bool, ) -> Option> { - let name_ptr = name.into_bytes_with_nul(); + let name_ptr = name.to_cstr(); let result = unsafe { BNBinaryViewAddExternalLibrary( self.as_ref().handle, - name_ptr.as_ref().as_ptr() as *const c_char, + name_ptr.as_ptr(), backing_file .map(|b| b.handle.as_ptr()) .unwrap_or(std::ptr::null_mut()), @@ -1580,15 +1576,15 @@ pub trait BinaryViewExt: BinaryViewBase { } // TODO: This is awful, rewrite this. - fn add_external_location( + fn add_external_location( &self, symbol: &Symbol, library: &ExternalLibrary, - target_symbol_name: S, + target_symbol_name: &str, target_address: Option, target_is_auto: bool, ) -> Option> { - let target_symbol_name = target_symbol_name.into_bytes_with_nul(); + let target_symbol_name = target_symbol_name.to_cstr(); let target_address_ptr = target_address .map(|a| a as *mut u64) .unwrap_or(std::ptr::null_mut()); @@ -1597,7 +1593,7 @@ pub trait BinaryViewExt: BinaryViewBase { self.as_ref().handle, symbol.handle, library.handle.as_ptr(), - target_symbol_name.as_ref().as_ptr() as *const c_char, + target_symbol_name.as_ptr(), target_address_ptr, target_is_auto, ) @@ -1638,15 +1634,10 @@ pub trait BinaryViewExt: BinaryViewBase { unsafe { BNAddBinaryViewTypeLibrary(self.as_ref().handle, library.as_raw()) } } - fn type_library_by_name(&self, name: S) -> Option { - let name = name.into_bytes_with_nul(); - let result = unsafe { - BNGetBinaryViewTypeLibrary( - self.as_ref().handle, - name.as_ref().as_ptr() as *const c_char, - ) - }; - NonNull::new(result).map(|h| unsafe { TypeLibrary::from_raw(h) }) + fn type_library_by_name(&self, name: &str) -> Option> { + let name = name.to_cstr(); + let result = unsafe { BNGetBinaryViewTypeLibrary(self.as_ref().handle, name.as_ptr()) }; + NonNull::new(result).map(|h| unsafe { TypeLibrary::ref_from_raw(h) }) } /// Should be called by custom py:py:class:`BinaryView` implementations @@ -1731,19 +1722,10 @@ pub trait BinaryViewExt: BinaryViewBase { } /// Recursively imports a type interface given its GUID. - /// - /// .. note:: To support this type of lookup a type library must have - /// contain a metadata key called "type_guids" which is a map - /// Dict[string_guid, string_type_name] or - /// Dict[string_guid, Tuple[string_type_name, type_library_name]] - fn import_type_by_guid(&self, guid: S) -> Option> { - let guid = guid.into_bytes_with_nul(); - let result = unsafe { - BNBinaryViewImportTypeLibraryTypeByGuid( - self.as_ref().handle, - guid.as_ref().as_ptr() as *const c_char, - ) - }; + fn import_type_by_guid(&self, guid: &str) -> Option> { + let guid = guid.to_cstr(); + let result = + unsafe { BNBinaryViewImportTypeLibraryTypeByGuid(self.as_ref().handle, guid.as_ptr()) }; (!result.is_null()).then(|| unsafe { Type::ref_from_raw(result) }) } @@ -1800,7 +1782,7 @@ pub trait BinaryViewExt: BinaryViewBase { &self, addr: u64, platform: &Platform, - ) -> Option<(TypeLibrary, QualifiedName)> { + ) -> Option<(Ref, QualifiedName)> { let mut result_lib = std::ptr::null_mut(); let mut result_name = BNQualifiedName::default(); let success = unsafe { @@ -1815,7 +1797,7 @@ pub trait BinaryViewExt: BinaryViewBase { if !success { return None; } - let lib = unsafe { TypeLibrary::from_raw(NonNull::new(result_lib)?) }; + let lib = unsafe { TypeLibrary::ref_from_raw(NonNull::new(result_lib)?) }; let name = QualifiedName::from_owned_raw(result_name); Some((lib, name)) } @@ -1826,7 +1808,7 @@ pub trait BinaryViewExt: BinaryViewBase { fn lookup_imported_type_library>( &self, name: T, - ) -> Option<(TypeLibrary, QualifiedName)> { + ) -> Option<(Ref, QualifiedName)> { let raw_name = QualifiedName::into_raw(name.into()); let mut result_lib = std::ptr::null_mut(); let mut result_name = BNQualifiedName::default(); @@ -1842,7 +1824,7 @@ pub trait BinaryViewExt: BinaryViewBase { if !success { return None; } - let lib = unsafe { TypeLibrary::from_raw(NonNull::new(result_lib)?) }; + let lib = unsafe { TypeLibrary::ref_from_raw(NonNull::new(result_lib)?) }; let name = QualifiedName::from_owned_raw(result_name); Some((lib, name)) } @@ -1884,7 +1866,7 @@ impl BinaryView { } pub fn from_path(meta: &mut FileMetadata, file_path: impl AsRef) -> Result> { - let file = file_path.as_ref().into_bytes_with_nul(); + let file = file_path.as_ref().to_cstr(); let handle = unsafe { BNCreateBinaryDataViewFromFilename(meta.handle, file.as_ptr() as *mut _) }; @@ -1895,8 +1877,11 @@ impl BinaryView { unsafe { Ok(Ref::new(Self { handle })) } } - pub fn from_accessor(meta: &FileMetadata, file: &mut FileAccessor) -> Result> { - let handle = unsafe { BNCreateBinaryDataViewFromFile(meta.handle, &mut file.api_object) }; + pub fn from_accessor( + meta: &FileMetadata, + file: &mut FileAccessor, + ) -> Result> { + let handle = unsafe { BNCreateBinaryDataViewFromFile(meta.handle, &mut file.raw) }; if handle.is_null() { return Err(()); @@ -1926,7 +1911,7 @@ impl BinaryView { /// To avoid the above issue use [`crate::main_thread::execute_on_main_thread_and_wait`] to verify there /// are no queued up main thread actions. pub fn save_to_path(&self, file_path: impl AsRef) -> bool { - let file = file_path.as_ref().into_bytes_with_nul(); + let file = file_path.as_ref().to_cstr(); unsafe { BNSaveToFilename(self.handle, file.as_ptr() as *mut _) } } @@ -1938,8 +1923,8 @@ impl BinaryView { /// /// To avoid the above issue use [`crate::main_thread::execute_on_main_thread_and_wait`] to verify there /// are no queued up main thread actions. - pub fn save_to_accessor(&self, file: &mut FileAccessor) -> bool { - unsafe { BNSaveToFile(self.handle, &mut file.api_object) } + pub fn save_to_accessor(&self, file: &mut FileAccessor) -> bool { + unsafe { BNSaveToFile(self.handle, &mut file.raw) } } } diff --git a/rust/src/binary_view/memory_map.rs b/rust/src/binary_view/memory_map.rs index 3e248c545b..34ebd572e7 100644 --- a/rust/src/binary_view/memory_map.rs +++ b/rust/src/binary_view/memory_map.rs @@ -1,11 +1,10 @@ use crate::binary_view::BinaryView; use crate::data_buffer::DataBuffer; -use crate::file_accessor::FileAccessor; +use crate::file_accessor::{Accessor, FileAccessor}; use crate::rc::Ref; use crate::segment::SegmentFlags; -use crate::string::{BnStrCompatible, BnString}; +use crate::string::{BnString, IntoCStr}; use binaryninjacore_sys::*; -use std::ffi::c_char; #[derive(PartialEq, Eq, Hash)] pub struct MemoryMap { @@ -22,13 +21,13 @@ impl MemoryMap { /// JSON string representation of the base [`MemoryMap`], consisting of unresolved auto and user segments. pub fn base_description(&self) -> String { let desc_raw = unsafe { BNGetBaseMemoryMapDescription(self.view.handle) }; - unsafe { BnString::from_raw(desc_raw) }.to_string() + unsafe { BnString::into_string(desc_raw) } } /// JSON string representation of the [`MemoryMap`]. pub fn description(&self) -> String { let desc_raw = unsafe { BNGetMemoryMapDescription(self.view.handle) }; - unsafe { BnString::from_raw(desc_raw) }.to_string() + unsafe { BnString::into_string(desc_raw) } } // When enabled, the memory map will present a simplified, logical view that merges and abstracts virtual memory @@ -43,16 +42,16 @@ impl MemoryMap { pub fn add_binary_memory_region( &mut self, - name: impl BnStrCompatible, + name: &str, start: u64, view: &BinaryView, segment_flags: Option, ) -> bool { - let name_raw = name.into_bytes_with_nul(); + let name_raw = name.to_cstr(); unsafe { BNAddBinaryMemoryRegion( self.view.handle, - name_raw.as_ref().as_ptr() as *const c_char, + name_raw.as_ptr(), start, view.handle, segment_flags.unwrap_or_default().into_raw(), @@ -60,18 +59,21 @@ impl MemoryMap { } } + /// Adds the memory region using a [`DataBuffer`]. + /// + /// This will add the contents of the [`DataBuffer`] to the database. pub fn add_data_memory_region( &mut self, - name: impl BnStrCompatible, + name: &str, start: u64, data: &DataBuffer, segment_flags: Option, ) -> bool { - let name_raw = name.into_bytes_with_nul(); + let name_raw = name.to_cstr(); unsafe { BNAddDataMemoryRegion( self.view.handle, - name_raw.as_ref().as_ptr() as *const c_char, + name_raw.as_ptr(), start, data.as_raw(), segment_flags.unwrap_or_default().into_raw(), @@ -79,134 +81,86 @@ impl MemoryMap { } } - pub fn add_remote_memory_region( + // TODO: This really cant be safe until BNFileAccessor is ARC'd and can be freed. Probably need another thing + // TODO: Ontop of a file accessor in the core that would manage it. (I.e. BNFileAccessorHandle) or something. + /// Adds the memory region using a [`FileAccessor`]. + /// + /// This does not add the region contents to the database, instead accesses to the contents + /// are done "remotely" to a [`FileAccessor`]. + /// + /// NOTE: The [`FileAccessor`] MUST live as long as the region is available, currently there is no gurentee by + /// the type checker that the file accessor is tied to that of the memory region. + pub fn add_remote_memory_region( &mut self, - name: impl BnStrCompatible, + name: &str, start: u64, - accessor: &mut FileAccessor, + accessor: &mut FileAccessor, segment_flags: Option, ) -> bool { - let name_raw = name.into_bytes_with_nul(); + let name_raw = name.to_cstr(); unsafe { BNAddRemoteMemoryRegion( self.view.handle, - name_raw.as_ref().as_ptr() as *const c_char, + name_raw.as_ptr(), start, - &mut accessor.api_object, + &mut accessor.raw, segment_flags.unwrap_or_default().into_raw(), ) } } - pub fn remove_memory_region(&mut self, name: impl BnStrCompatible) -> bool { - let name_raw = name.into_bytes_with_nul(); - unsafe { - BNRemoveMemoryRegion( - self.view.handle, - name_raw.as_ref().as_ptr() as *const c_char, - ) - } + pub fn remove_memory_region(&mut self, name: &str) -> bool { + let name_raw = name.to_cstr(); + unsafe { BNRemoveMemoryRegion(self.view.handle, name_raw.as_ptr()) } } - pub fn active_memory_region_at(&self, addr: u64) -> BnString { + pub fn active_memory_region_at(&self, addr: u64) -> String { unsafe { let name_raw = BNGetActiveMemoryRegionAt(self.view.handle, addr); - BnString::from_raw(name_raw) + BnString::into_string(name_raw) } } - pub fn memory_region_flags(&self, name: impl BnStrCompatible) -> SegmentFlags { - let name_raw = name.into_bytes_with_nul(); - let flags_raw = unsafe { - BNGetMemoryRegionFlags( - self.view.handle, - name_raw.as_ref().as_ptr() as *const c_char, - ) - }; + pub fn memory_region_flags(&self, name: &str) -> SegmentFlags { + let name_raw = name.to_cstr(); + let flags_raw = unsafe { BNGetMemoryRegionFlags(self.view.handle, name_raw.as_ptr()) }; SegmentFlags::from_raw(flags_raw) } - pub fn set_memory_region_flags( - &mut self, - name: impl BnStrCompatible, - flags: SegmentFlags, - ) -> bool { - let name_raw = name.into_bytes_with_nul(); - unsafe { - BNSetMemoryRegionFlags( - self.view.handle, - name_raw.as_ref().as_ptr() as *const c_char, - flags.into_raw(), - ) - } + pub fn set_memory_region_flags(&mut self, name: &str, flags: SegmentFlags) -> bool { + let name_raw = name.to_cstr(); + unsafe { BNSetMemoryRegionFlags(self.view.handle, name_raw.as_ptr(), flags.into_raw()) } } - pub fn is_memory_region_enabled(&self, name: impl BnStrCompatible) -> bool { - let name_raw = name.into_bytes_with_nul(); - unsafe { - BNIsMemoryRegionEnabled( - self.view.handle, - name_raw.as_ref().as_ptr() as *const c_char, - ) - } + pub fn is_memory_region_enabled(&self, name: &str) -> bool { + let name_raw = name.to_cstr(); + unsafe { BNIsMemoryRegionEnabled(self.view.handle, name_raw.as_ptr()) } } - pub fn set_memory_region_enabled(&mut self, name: impl BnStrCompatible, enabled: bool) -> bool { - let name_raw = name.into_bytes_with_nul(); - unsafe { - BNSetMemoryRegionEnabled( - self.view.handle, - name_raw.as_ref().as_ptr() as *const c_char, - enabled, - ) - } + pub fn set_memory_region_enabled(&mut self, name: &str, enabled: bool) -> bool { + let name_raw = name.to_cstr(); + unsafe { BNSetMemoryRegionEnabled(self.view.handle, name_raw.as_ptr(), enabled) } } // TODO: Should we just call this is_memory_region_relocatable? - pub fn is_memory_region_rebaseable(&self, name: impl BnStrCompatible) -> bool { - let name_raw = name.into_bytes_with_nul(); - unsafe { - BNIsMemoryRegionRebaseable( - self.view.handle, - name_raw.as_ref().as_ptr() as *const c_char, - ) - } + pub fn is_memory_region_rebaseable(&self, name: &str) -> bool { + let name_raw = name.to_cstr(); + unsafe { BNIsMemoryRegionRebaseable(self.view.handle, name_raw.as_ptr()) } } - pub fn set_memory_region_rebaseable( - &mut self, - name: impl BnStrCompatible, - enabled: bool, - ) -> bool { - let name_raw = name.into_bytes_with_nul(); - unsafe { - BNSetMemoryRegionRebaseable( - self.view.handle, - name_raw.as_ref().as_ptr() as *const c_char, - enabled, - ) - } + pub fn set_memory_region_rebaseable(&mut self, name: &str, enabled: bool) -> bool { + let name_raw = name.to_cstr(); + unsafe { BNSetMemoryRegionRebaseable(self.view.handle, name_raw.as_ptr(), enabled) } } - pub fn memory_region_fill(&self, name: impl BnStrCompatible) -> u8 { - let name_raw = name.into_bytes_with_nul(); - unsafe { - BNGetMemoryRegionFill( - self.view.handle, - name_raw.as_ref().as_ptr() as *const c_char, - ) - } + pub fn memory_region_fill(&self, name: &str) -> u8 { + let name_raw = name.to_cstr(); + unsafe { BNGetMemoryRegionFill(self.view.handle, name_raw.as_ptr()) } } - pub fn set_memory_region_fill(&mut self, name: impl BnStrCompatible, fill: u8) -> bool { - let name_raw = name.into_bytes_with_nul(); - unsafe { - BNSetMemoryRegionFill( - self.view.handle, - name_raw.as_ref().as_ptr() as *const c_char, - fill, - ) - } + pub fn set_memory_region_fill(&mut self, name: &str, fill: u8) -> bool { + let name_raw = name.to_cstr(); + unsafe { BNSetMemoryRegionFill(self.view.handle, name_raw.as_ptr(), fill) } } pub fn reset(&mut self) { diff --git a/rust/src/binary_reader.rs b/rust/src/binary_view/reader.rs similarity index 97% rename from rust/src/binary_reader.rs rename to rust/src/binary_view/reader.rs index e96e552aec..685914e210 100644 --- a/rust/src/binary_reader.rs +++ b/rust/src/binary_view/reader.rs @@ -68,12 +68,12 @@ impl BinaryReader { unsafe { BNSetBinaryReaderVirtualBase(self.handle, virtual_base_addr) } } - /// Prefer using [crate::binary_reader::BinaryReader::seek] over this. + /// Prefer using [BinaryReader::seek] over this. pub fn seek_to_offset(&mut self, offset: u64) { unsafe { BNSeekBinaryReader(self.handle, offset) } } - /// Prefer using [crate::binary_reader::BinaryReader::seek] over this. + /// Prefer using [BinaryReader::seek] over this. pub fn seek_to_relative_offset(&mut self, offset: i64) { unsafe { BNSeekBinaryReaderRelative(self.handle, offset) } } diff --git a/rust/src/binary_writer.rs b/rust/src/binary_view/writer.rs similarity index 100% rename from rust/src/binary_writer.rs rename to rust/src/binary_view/writer.rs diff --git a/rust/src/calling_convention.rs b/rust/src/calling_convention.rs index 2fb282e0ff..04d8539644 100644 --- a/rust/src/calling_convention.rs +++ b/rust/src/calling_convention.rs @@ -55,10 +55,9 @@ pub trait CallingConvention: Sync { fn are_argument_registers_used_for_var_args(&self) -> bool; } -pub fn register_calling_convention(arch: &A, name: N, cc: C) -> Ref +pub fn register_calling_convention(arch: &A, name: &str, cc: C) -> Ref where A: Architecture, - N: BnStrCompatible, C: 'static + CallingConvention, { struct CustomCallingConventionContext @@ -377,7 +376,7 @@ where ) } - let name = name.into_bytes_with_nul(); + let name = name.to_cstr(); let raw = Box::into_raw(Box::new(CustomCallingConventionContext { raw_handle: std::ptr::null_mut(), cc, @@ -413,7 +412,7 @@ where }; unsafe { - let cc_name = name.as_ref().as_ptr() as *mut _; + let cc_name = name.as_ptr(); let result = BNCreateCallingConvention(arch.as_ref().handle, cc_name, &mut cc); assert!(!result.is_null()); @@ -455,8 +454,8 @@ impl CoreCallingConvention { }) } - pub fn name(&self) -> BnString { - unsafe { BnString::from_raw(BNGetCallingConventionName(self.handle)) } + pub fn name(&self) -> String { + unsafe { BnString::into_string(BNGetCallingConventionName(self.handle)) } } pub fn variables_for_parameters( diff --git a/rust/src/collaboration.rs b/rust/src/collaboration.rs index c90677622b..0a17d94ca2 100644 --- a/rust/src/collaboration.rs +++ b/rust/src/collaboration.rs @@ -30,7 +30,7 @@ pub use user::*; use binaryninjacore_sys::*; use crate::rc::{Array, Ref}; -use crate::string::{BnStrCompatible, BnString}; +use crate::string::{BnString, IntoCStr}; // TODO: Should we pull metadata and information required to call a function? Or should we add documentation // TODO: on what functions need to have been called prior? I feel like we should make the user have to pull @@ -73,24 +73,23 @@ pub fn known_remotes() -> Array { } /// Get Remote by unique `id` -pub fn get_remote_by_id(id: S) -> Option> { - let id = id.into_bytes_with_nul(); - let value = unsafe { BNCollaborationGetRemoteById(id.as_ref().as_ptr() as *const c_char) }; +pub fn get_remote_by_id(id: &str) -> Option> { + let id = id.to_cstr(); + let value = unsafe { BNCollaborationGetRemoteById(id.as_ptr()) }; NonNull::new(value).map(|h| unsafe { Remote::ref_from_raw(h) }) } /// Get Remote by `address` -pub fn get_remote_by_address(address: S) -> Option> { - let address = address.into_bytes_with_nul(); - let value = - unsafe { BNCollaborationGetRemoteByAddress(address.as_ref().as_ptr() as *const c_char) }; +pub fn get_remote_by_address(address: &str) -> Option> { + let address = address.to_cstr(); + let value = unsafe { BNCollaborationGetRemoteByAddress(address.as_ptr()) }; NonNull::new(value).map(|h| unsafe { Remote::ref_from_raw(h) }) } /// Get Remote by `name` -pub fn get_remote_by_name(name: S) -> Option> { - let name = name.into_bytes_with_nul(); - let value = unsafe { BNCollaborationGetRemoteByName(name.as_ref().as_ptr() as *const c_char) }; +pub fn get_remote_by_name(name: &str) -> Option> { + let name = name.to_cstr(); + let value = unsafe { BNCollaborationGetRemoteByName(name.as_ptr()) }; NonNull::new(value).map(|h| unsafe { Remote::ref_from_raw(h) }) } @@ -104,29 +103,20 @@ pub fn save_remotes() { unsafe { BNCollaborationSaveRemotes() } } -pub fn store_data_in_keychain(key: K, data: I) -> bool +pub fn store_data_in_keychain(key: &str, data: I) -> bool where - K: BnStrCompatible, - I: IntoIterator, - DK: BnStrCompatible, - DV: BnStrCompatible, + I: IntoIterator, { - let key = key.into_bytes_with_nul(); - let (data_keys, data_values): (Vec, Vec) = data + let key = key.to_cstr(); + let (data_keys, data_values): (Vec<_>, Vec<_>) = data .into_iter() - .map(|(k, v)| (k.into_bytes_with_nul(), v.into_bytes_with_nul())) + .map(|(k, v)| (k.to_cstr(), v.to_cstr())) .unzip(); - let data_keys_ptr: Box<[*const c_char]> = data_keys - .iter() - .map(|k| k.as_ref().as_ptr() as *const c_char) - .collect(); - let data_values_ptr: Box<[*const c_char]> = data_values - .iter() - .map(|v| v.as_ref().as_ptr() as *const c_char) - .collect(); + let data_keys_ptr: Box<[*const c_char]> = data_keys.iter().map(|k| k.as_ptr()).collect(); + let data_values_ptr: Box<[*const c_char]> = data_values.iter().map(|v| v.as_ptr()).collect(); unsafe { BNCollaborationStoreDataInKeychain( - key.as_ref().as_ptr() as *const c_char, + key.as_ptr(), data_keys_ptr.as_ptr() as *mut _, data_values_ptr.as_ptr() as *mut _, data_keys.len(), @@ -134,30 +124,22 @@ where } } -pub fn has_data_in_keychain(key: K) -> bool { - let key = key.into_bytes_with_nul(); - unsafe { BNCollaborationHasDataInKeychain(key.as_ref().as_ptr() as *const c_char) } +pub fn has_data_in_keychain(key: &str) -> bool { + let key = key.to_cstr(); + unsafe { BNCollaborationHasDataInKeychain(key.as_ptr()) } } -pub fn get_data_from_keychain( - key: K, -) -> Option<(Array, Array)> { - let key = key.into_bytes_with_nul(); +pub fn get_data_from_keychain(key: &str) -> Option<(Array, Array)> { + let key = key.to_cstr(); let mut keys = std::ptr::null_mut(); let mut values = std::ptr::null_mut(); - let count = unsafe { - BNCollaborationGetDataFromKeychain( - key.as_ref().as_ptr() as *const c_char, - &mut keys, - &mut values, - ) - }; + let count = unsafe { BNCollaborationGetDataFromKeychain(key.as_ptr(), &mut keys, &mut values) }; let keys = (!keys.is_null()).then(|| unsafe { Array::new(keys, count, ()) }); let values = (!values.is_null()).then(|| unsafe { Array::new(values, count, ()) }); keys.zip(values) } -pub fn delete_data_from_keychain(key: K) -> bool { - let key = key.into_bytes_with_nul(); - unsafe { BNCollaborationDeleteDataFromKeychain(key.as_ref().as_ptr() as *const c_char) } +pub fn delete_data_from_keychain(key: &str) -> bool { + let key = key.to_cstr(); + unsafe { BNCollaborationDeleteDataFromKeychain(key.as_ptr()) } } diff --git a/rust/src/collaboration/changeset.rs b/rust/src/collaboration/changeset.rs index 9d7cdb7c3b..cce750e6e0 100644 --- a/rust/src/collaboration/changeset.rs +++ b/rust/src/collaboration/changeset.rs @@ -1,5 +1,4 @@ use binaryninjacore_sys::*; -use std::ffi::c_char; use std::ptr::NonNull; use super::{RemoteFile, RemoteUser}; @@ -7,7 +6,7 @@ use super::{RemoteFile, RemoteUser}; use crate::database::snapshot::SnapshotId; use crate::database::Database; use crate::rc::{Array, CoreArrayProvider, CoreArrayProviderInner, Guard, Ref, RefCountable}; -use crate::string::{BnStrCompatible, BnString}; +use crate::string::{BnString, IntoCStr}; /// A collection of snapshots in a local database #[repr(transparent)] @@ -59,21 +58,16 @@ impl Changeset { } /// Changeset name - pub fn name(&self) -> BnString { + pub fn name(&self) -> String { let result = unsafe { BNCollaborationChangesetGetName(self.handle.as_ptr()) }; assert!(!result.is_null()); - unsafe { BnString::from_raw(result) } + unsafe { BnString::into_string(result) } } /// Set the name of the changeset, e.g. in a name changeset function. - pub fn set_name(&self, value: S) -> bool { - let value = value.into_bytes_with_nul(); - unsafe { - BNCollaborationChangesetSetName( - self.handle.as_ptr(), - value.as_ref().as_ptr() as *const c_char, - ) - } + pub fn set_name(&self, value: &str) -> bool { + let value = value.to_cstr(); + unsafe { BNCollaborationChangesetSetName(self.handle.as_ptr(), value.as_ptr()) } } } diff --git a/rust/src/collaboration/file.rs b/rust/src/collaboration/file.rs index 2651d3c773..624a731a86 100644 --- a/rust/src/collaboration/file.rs +++ b/rust/src/collaboration/file.rs @@ -1,5 +1,6 @@ -use std::ffi::{c_char, c_void}; +use std::ffi::c_void; use std::fmt::{Debug, Formatter}; +use std::path::Path; use std::ptr::NonNull; use std::time::SystemTime; @@ -16,7 +17,7 @@ use crate::file_metadata::FileMetadata; use crate::progress::{NoProgressCallback, ProgressCallback, SplitProgressBuilder}; use crate::project::file::ProjectFile; use crate::rc::{Array, CoreArrayProvider, CoreArrayProviderInner, Guard, Ref, RefCountable}; -use crate::string::{BnStrCompatible, BnString}; +use crate::string::{BnString, IntoCStr}; pub type RemoteFileType = BNRemoteFileType; @@ -94,42 +95,37 @@ impl RemoteFile { success.then_some(()).ok_or(()) } - pub fn set_metadata(&self, folder: S) -> Result<(), ()> { - let folder_raw = folder.into_bytes_with_nul(); - let success = unsafe { - BNRemoteFileSetMetadata( - self.handle.as_ptr(), - folder_raw.as_ref().as_ptr() as *const c_char, - ) - }; + pub fn set_metadata(&self, folder: &str) -> Result<(), ()> { + let folder_raw = folder.to_cstr(); + let success = unsafe { BNRemoteFileSetMetadata(self.handle.as_ptr(), folder_raw.as_ptr()) }; success.then_some(()).ok_or(()) } /// Web API endpoint URL - pub fn url(&self) -> BnString { + pub fn url(&self) -> String { let result = unsafe { BNRemoteFileGetUrl(self.handle.as_ptr()) }; assert!(!result.is_null()); - unsafe { BnString::from_raw(result) } + unsafe { BnString::into_string(result) } } /// Chat log API endpoint URL - pub fn chat_log_url(&self) -> BnString { + pub fn chat_log_url(&self) -> String { let result = unsafe { BNRemoteFileGetChatLogUrl(self.handle.as_ptr()) }; assert!(!result.is_null()); - unsafe { BnString::from_raw(result) } + unsafe { BnString::into_string(result) } } - pub fn user_positions_url(&self) -> BnString { + pub fn user_positions_url(&self) -> String { let result = unsafe { BNRemoteFileGetUserPositionsUrl(self.handle.as_ptr()) }; assert!(!result.is_null()); - unsafe { BnString::from_raw(result) } + unsafe { BnString::into_string(result) } } /// Unique ID - pub fn id(&self) -> BnString { + pub fn id(&self) -> String { let result = unsafe { BNRemoteFileGetId(self.handle.as_ptr()) }; assert!(!result.is_null()); - unsafe { BnString::from_raw(result) } + unsafe { BnString::into_string(result) } } /// All files share the same properties, but files with different types may make different @@ -144,10 +140,10 @@ impl RemoteFile { crate::ffi::time_from_bn(result.try_into().unwrap()) } - pub fn created_by(&self) -> BnString { + pub fn created_by(&self) -> String { let result = unsafe { BNRemoteFileGetCreatedBy(self.handle.as_ptr()) }; assert!(!result.is_null()); - unsafe { BnString::from_raw(result) } + unsafe { BnString::into_string(result) } } /// Last modified of the file @@ -163,67 +159,58 @@ impl RemoteFile { } /// Username of user who pushed the last snapshot in the file - pub fn last_snapshot_by(&self) -> BnString { + pub fn last_snapshot_by(&self) -> String { let result = unsafe { BNRemoteFileGetLastSnapshotBy(self.handle.as_ptr()) }; assert!(!result.is_null()); - unsafe { BnString::from_raw(result) } + unsafe { BnString::into_string(result) } } - pub fn last_snapshot_name(&self) -> BnString { + pub fn last_snapshot_name(&self) -> String { let result = unsafe { BNRemoteFileGetLastSnapshotName(self.handle.as_ptr()) }; assert!(!result.is_null()); - unsafe { BnString::from_raw(result) } + unsafe { BnString::into_string(result) } } /// Hash of file contents (no algorithm guaranteed) - pub fn hash(&self) -> BnString { + pub fn hash(&self) -> String { let result = unsafe { BNRemoteFileGetHash(self.handle.as_ptr()) }; assert!(!result.is_null()); - unsafe { BnString::from_raw(result) } + unsafe { BnString::into_string(result) } } /// Displayed name of file - pub fn name(&self) -> BnString { + pub fn name(&self) -> String { let result = unsafe { BNRemoteFileGetName(self.handle.as_ptr()) }; assert!(!result.is_null()); - unsafe { BnString::from_raw(result) } + unsafe { BnString::into_string(result) } } /// Set the description of the file. You will need to push the file to update the remote version. - pub fn set_name(&self, name: S) -> Result<(), ()> { - let name = name.into_bytes_with_nul(); - let success = unsafe { - BNRemoteFileSetName( - self.handle.as_ptr(), - name.as_ref().as_ptr() as *const c_char, - ) - }; + pub fn set_name(&self, name: &str) -> Result<(), ()> { + let name = name.to_cstr(); + let success = unsafe { BNRemoteFileSetName(self.handle.as_ptr(), name.as_ptr()) }; success.then_some(()).ok_or(()) } /// Desciprtion of the file - pub fn description(&self) -> BnString { + pub fn description(&self) -> String { let result = unsafe { BNRemoteFileGetDescription(self.handle.as_ptr()) }; assert!(!result.is_null()); - unsafe { BnString::from_raw(result) } + unsafe { BnString::into_string(result) } } /// Set the description of the file. You will need to push the file to update the remote version. - pub fn set_description(&self, description: S) -> Result<(), ()> { - let description = description.into_bytes_with_nul(); - let success = unsafe { - BNRemoteFileSetDescription( - self.handle.as_ptr(), - description.as_ref().as_ptr() as *const c_char, - ) - }; + pub fn set_description(&self, description: &str) -> Result<(), ()> { + let description = description.to_cstr(); + let success = + unsafe { BNRemoteFileSetDescription(self.handle.as_ptr(), description.as_ptr()) }; success.then_some(()).ok_or(()) } - pub fn metadata(&self) -> BnString { + pub fn metadata(&self) -> String { let result = unsafe { BNRemoteFileGetMetadata(self.handle.as_ptr()) }; assert!(!result.is_null()); - unsafe { BnString::from_raw(result) } + unsafe { BnString::into_string(result) } } /// Size of raw content of file, in bytes @@ -234,10 +221,10 @@ impl RemoteFile { /// Get the default filepath for a remote File. This is based off the Setting for /// collaboration.directory, the file's id, the file's project's id, and the file's /// remote's id. - pub fn default_path(&self) -> BnString { + pub fn default_path(&self) -> String { let result = unsafe { BNCollaborationDefaultFilePath(self.handle.as_ptr()) }; assert!(!result.is_null()); - unsafe { BnString::from_raw(result) } + unsafe { BnString::into_string(result) } } /// If the file has pulled the snapshots yet @@ -263,18 +250,13 @@ impl RemoteFile { /// Get a specific Snapshot in the File by its id /// /// NOTE: If snapshots have not been pulled, they will be pulled upon calling this. - pub fn snapshot_by_id( - &self, - id: S, - ) -> Result>, ()> { + pub fn snapshot_by_id(&self, id: &str) -> Result>, ()> { // TODO: This sync should be removed? if !self.has_pulled_snapshots() { self.pull_snapshots()?; } - let id = id.into_bytes_with_nul(); - let result = unsafe { - BNRemoteFileGetSnapshotById(self.handle.as_ptr(), id.as_ref().as_ptr() as *const c_char) - }; + let id = id.to_cstr(); + let result = unsafe { BNRemoteFileGetSnapshotById(self.handle.as_ptr(), id.as_ptr()) }; Ok(NonNull::new(result).map(|handle| unsafe { RemoteSnapshot::ref_from_raw(handle) })) } @@ -305,18 +287,16 @@ impl RemoteFile { /// * `analysis_cache_contents` - Contents of analysis cache of snapshot /// * `file` - New file contents (if contents changed) /// * `parent_ids` - List of ids of parent snapshots (or empty if this is a root snapshot) - pub fn create_snapshot( + pub fn create_snapshot( &self, - name: S, + name: &str, contents: &mut [u8], analysis_cache_contexts: &mut [u8], file: &mut [u8], parent_ids: I, ) -> Result, ()> where - S: BnStrCompatible, - I: IntoIterator, - I::Item: BnStrCompatible, + I: IntoIterator, { self.create_snapshot_with_progress( name, @@ -336,9 +316,9 @@ impl RemoteFile { /// * `file` - New file contents (if contents changed) /// * `parent_ids` - List of ids of parent snapshots (or empty if this is a root snapshot) /// * `progress` - Function to call on progress updates - pub fn create_snapshot_with_progress( + pub fn create_snapshot_with_progress( &self, - name: S, + name: &str, contents: &mut [u8], analysis_cache_contexts: &mut [u8], file: &mut [u8], @@ -346,24 +326,16 @@ impl RemoteFile { mut progress: P, ) -> Result, ()> where - S: BnStrCompatible, + I: IntoIterator, P: ProgressCallback, - I: IntoIterator, - I::Item: BnStrCompatible, { - let name = name.into_bytes_with_nul(); - let parent_ids: Vec<_> = parent_ids - .into_iter() - .map(|id| id.into_bytes_with_nul()) - .collect(); - let mut parent_ids_raw: Vec<_> = parent_ids - .iter() - .map(|x| x.as_ref().as_ptr() as *const c_char) - .collect(); + let name = name.to_cstr(); + let parent_ids: Vec<_> = parent_ids.into_iter().map(|id| id.to_cstr()).collect(); + let mut parent_ids_raw: Vec<_> = parent_ids.iter().map(|x| x.as_ptr()).collect(); let result = unsafe { BNRemoteFileCreateSnapshot( self.handle.as_ptr(), - name.as_ref().as_ptr() as *const c_char, + name.as_ptr(), contents.as_mut_ptr(), contents.len(), analysis_cache_contexts.as_mut_ptr(), @@ -410,16 +382,16 @@ impl RemoteFile { // todo!() //} - pub fn request_user_positions(&self) -> BnString { + pub fn request_user_positions(&self) -> String { let result = unsafe { BNRemoteFileRequestUserPositions(self.handle.as_ptr()) }; assert!(!result.is_null()); - unsafe { BnString::from_raw(result) } + unsafe { BnString::into_string(result) } } - pub fn request_chat_log(&self) -> BnString { + pub fn request_chat_log(&self) -> String { let result = unsafe { BNRemoteFileRequestChatLog(self.handle.as_ptr()) }; assert!(!result.is_null()); - unsafe { BnString::from_raw(result) } + unsafe { BnString::into_string(result) } } // TODO: AsRef @@ -428,10 +400,7 @@ impl RemoteFile { /// /// * `db_path` - File path for saved database /// * `progress_function` - Function to call for progress updates - pub fn download(&self, db_path: S) -> Result, ()> - where - S: BnStrCompatible, - { + pub fn download(&self, db_path: &Path) -> Result, ()> { sync::download_file(self, db_path) } @@ -441,20 +410,19 @@ impl RemoteFile { /// /// * `db_path` - File path for saved database /// * `progress_function` - Function to call for progress updates - pub fn download_with_progress( + pub fn download_with_progress( &self, - db_path: S, + db_path: &Path, progress_function: F, ) -> Result, ()> where - S: BnStrCompatible, F: ProgressCallback, { sync::download_file_with_progress(self, db_path, progress_function) } /// Download a remote file and save it to a BNDB at the given `path`, returning the associated [`FileMetadata`]. - pub fn download_database(&self, path: S) -> Result, ()> { + pub fn download_database(&self, path: &Path) -> Result, ()> { let file = self.download(path)?; let database = file.database().ok_or(())?; self.sync(&database, DatabaseConflictHandlerFail, NoNameChangeset)?; @@ -462,11 +430,10 @@ impl RemoteFile { } // TODO: This might be a bad helper... maybe remove... - // TODO: AsRef /// Download a remote file and save it to a BNDB at the given `path`. - pub fn download_database_with_progress( + pub fn download_database_with_progress( &self, - path: S, + path: &Path, progress: impl ProgressCallback, ) -> Result, ()> { let mut progress = progress.split(&[50, 50]); diff --git a/rust/src/collaboration/folder.rs b/rust/src/collaboration/folder.rs index 90a85f1cf6..797bcdd4de 100644 --- a/rust/src/collaboration/folder.rs +++ b/rust/src/collaboration/folder.rs @@ -1,11 +1,10 @@ use super::{Remote, RemoteProject}; use binaryninjacore_sys::*; -use std::ffi::c_char; use std::ptr::NonNull; use crate::project::folder::ProjectFolder; use crate::rc::{CoreArrayProvider, CoreArrayProviderInner, Guard, Ref, RefCountable}; -use crate::string::{BnStrCompatible, BnString}; +use crate::string::{BnString, IntoCStr}; #[repr(transparent)] pub struct RemoteFolder { @@ -76,17 +75,17 @@ impl RemoteFolder { } /// Get web API endpoint URL. - pub fn url(&self) -> BnString { + pub fn url(&self) -> String { let result = unsafe { BNRemoteFolderGetUrl(self.handle.as_ptr()) }; assert!(!result.is_null()); - unsafe { BnString::from_raw(result) } + unsafe { BnString::into_string(result) } } /// Get unique ID. - pub fn id(&self) -> BnString { + pub fn id(&self) -> String { let result = unsafe { BNRemoteFolderGetId(self.handle.as_ptr()) }; assert!(!result.is_null()); - unsafe { BnString::from_raw(result) } + unsafe { BnString::into_string(result) } } /// Unique id of parent folder, if there is a parent. None, otherwise @@ -97,40 +96,31 @@ impl RemoteFolder { } /// Displayed name of folder - pub fn name(&self) -> BnString { + pub fn name(&self) -> String { let result = unsafe { BNRemoteFolderGetName(self.handle.as_ptr()) }; assert!(!result.is_null()); - unsafe { BnString::from_raw(result) } + unsafe { BnString::into_string(result) } } /// Set the display name of the folder. You will need to push the folder to update the remote version. - pub fn set_name(&self, name: S) -> Result<(), ()> { - let name = name.into_bytes_with_nul(); - let success = unsafe { - BNRemoteFolderSetName( - self.handle.as_ptr(), - name.as_ref().as_ptr() as *const c_char, - ) - }; + pub fn set_name(&self, name: &str) -> Result<(), ()> { + let name = name.to_cstr(); + let success = unsafe { BNRemoteFolderSetName(self.handle.as_ptr(), name.as_ptr()) }; success.then_some(()).ok_or(()) } /// Description of the folder - pub fn description(&self) -> BnString { + pub fn description(&self) -> String { let result = unsafe { BNRemoteFolderGetDescription(self.handle.as_ptr()) }; assert!(!result.is_null()); - unsafe { BnString::from_raw(result) } + unsafe { BnString::into_string(result) } } /// Set the description of the folder. You will need to push the folder to update the remote version. - pub fn set_description(&self, description: S) -> Result<(), ()> { - let description = description.into_bytes_with_nul(); - let success = unsafe { - BNRemoteFolderSetDescription( - self.handle.as_ptr(), - description.as_ref().as_ptr() as *const c_char, - ) - }; + pub fn set_description(&self, description: &str) -> Result<(), ()> { + let description = description.to_cstr(); + let success = + unsafe { BNRemoteFolderSetDescription(self.handle.as_ptr(), description.as_ptr()) }; success.then_some(()).ok_or(()) } } diff --git a/rust/src/collaboration/group.rs b/rust/src/collaboration/group.rs index bad09d6cb7..01f9216932 100644 --- a/rust/src/collaboration/group.rs +++ b/rust/src/collaboration/group.rs @@ -1,8 +1,7 @@ use super::Remote; use crate::rc::{Array, CoreArrayProvider, CoreArrayProviderInner, Guard, Ref, RefCountable}; -use crate::string::{BnStrCompatible, BnString}; +use crate::string::{BnString, IntoCStr}; use binaryninjacore_sys::*; -use std::ffi::c_char; use std::fmt; use std::fmt::{Display, Formatter}; use std::ptr::NonNull; @@ -30,10 +29,10 @@ impl RemoteGroup { } /// Web api endpoint url - pub fn url(&self) -> BnString { + pub fn url(&self) -> String { let value = unsafe { BNCollaborationGroupGetUrl(self.handle.as_ptr()) }; assert!(!value.is_null()); - unsafe { BnString::from_raw(value) } + unsafe { BnString::into_string(value) } } /// Unique id @@ -42,22 +41,17 @@ impl RemoteGroup { } /// Group name - pub fn name(&self) -> BnString { + pub fn name(&self) -> String { let value = unsafe { BNCollaborationGroupGetName(self.handle.as_ptr()) }; assert!(!value.is_null()); - unsafe { BnString::from_raw(value) } + unsafe { BnString::into_string(value) } } /// Set group name /// You will need to push the group to update the Remote. - pub fn set_name(&self, name: U) { - let name = name.into_bytes_with_nul(); - unsafe { - BNCollaborationGroupSetName( - self.handle.as_ptr(), - name.as_ref().as_ptr() as *const c_char, - ) - } + pub fn set_name(&self, name: &str) { + let name = name.to_cstr(); + unsafe { BNCollaborationGroupSetName(self.handle.as_ptr(), name.as_ptr()) } } /// Get list of users in the group @@ -89,17 +83,10 @@ impl RemoteGroup { /// You will need to push the group to update the Remote. pub fn set_users(&self, usernames: I) -> Result<(), ()> where - I: IntoIterator, - I::Item: BnStrCompatible, + I: IntoIterator, { - let usernames: Vec<_> = usernames - .into_iter() - .map(|u| u.into_bytes_with_nul()) - .collect(); - let mut usernames_raw: Vec<_> = usernames - .iter() - .map(|s| s.as_ref().as_ptr() as *const c_char) - .collect(); + let usernames: Vec<_> = usernames.into_iter().map(|u| u.to_cstr()).collect(); + let mut usernames_raw: Vec<_> = usernames.iter().map(|s| s.as_ptr()).collect(); // TODO: This should only fail if collaboration is not supported. // TODO: Because you should not have a RemoteGroup at that point we can ignore? // TODO: Do you need any permissions to do this? @@ -114,14 +101,9 @@ impl RemoteGroup { } /// Test if a group has a user with the given username - pub fn contains_user(&self, username: U) -> bool { - let username = username.into_bytes_with_nul(); - unsafe { - BNCollaborationGroupContainsUser( - self.handle.as_ptr(), - username.as_ref().as_ptr() as *const c_char, - ) - } + pub fn contains_user(&self, username: &str) -> bool { + let username = username.to_cstr(); + unsafe { BNCollaborationGroupContainsUser(self.handle.as_ptr(), username.as_ptr()) } } } diff --git a/rust/src/collaboration/merge.rs b/rust/src/collaboration/merge.rs index 2d28725c2b..aea117015d 100644 --- a/rust/src/collaboration/merge.rs +++ b/rust/src/collaboration/merge.rs @@ -1,11 +1,10 @@ use binaryninjacore_sys::*; -use std::ffi::c_char; use std::ptr::NonNull; use crate::database::{snapshot::Snapshot, Database}; use crate::file_metadata::FileMetadata; use crate::rc::{CoreArrayProvider, CoreArrayProviderInner, Guard, Ref, RefCountable}; -use crate::string::{BnStrCompatible, BnString}; +use crate::string::{BnString, IntoCStr}; pub type MergeConflictDataType = BNMergeConflictDataType; @@ -49,13 +48,10 @@ impl MergeConflict { NonNull::new(result).map(|handle| unsafe { Snapshot::from_raw(handle) }) } - pub fn path_item_string(&self, path: S) -> Result { - let path = path.into_bytes_with_nul(); + pub fn path_item_string(&self, path: &str) -> Result { + let path = path.to_cstr(); let result = unsafe { - BNAnalysisMergeConflictGetPathItemString( - self.handle.as_ptr(), - path.as_ref().as_ptr() as *const c_char, - ) + BNAnalysisMergeConflictGetPathItemString(self.handle.as_ptr(), path.as_ptr()) }; (!result.is_null()) .then(|| unsafe { BnString::from_raw(result) }) @@ -108,41 +104,33 @@ impl MergeConflict { /// String representing the type name of the data, not the same as data_type. /// This is like "typeName" or "tag" depending on what object the conflict represents. - pub fn conflict_type(&self) -> BnString { + pub fn conflict_type(&self) -> String { let result = unsafe { BNAnalysisMergeConflictGetType(self.handle.as_ptr()) }; assert!(!result.is_null()); - unsafe { BnString::from_raw(result) } + unsafe { BnString::into_string(result) } } /// Lookup key for the merge conflict, ideally a tree path that contains the name of the conflict /// and all the recursive children leading up to this conflict. - pub fn key(&self) -> BnString { + pub fn key(&self) -> String { let result = unsafe { BNAnalysisMergeConflictGetKey(self.handle.as_ptr()) }; assert!(!result.is_null()); - unsafe { BnString::from_raw(result) } + unsafe { BnString::into_string(result) } } /// Call this when you've resolved the conflict to save the result - pub fn success(&self, value: S) -> Result<(), ()> { - let value = value.into_bytes_with_nul(); - let success = unsafe { - BNAnalysisMergeConflictSuccess( - self.handle.as_ptr(), - value.as_ref().as_ptr() as *const c_char, - ) - }; + pub fn success(&self, value: &str) -> Result<(), ()> { + let value = value.to_cstr(); + let success = + unsafe { BNAnalysisMergeConflictSuccess(self.handle.as_ptr(), value.as_ptr()) }; success.then_some(()).ok_or(()) } // TODO: Make a safe version of this that checks the path and if it holds a number - pub unsafe fn get_path_item_number(&self, path_key: S) -> Option { - let path_key = path_key.into_bytes_with_nul(); - let value = unsafe { - BNAnalysisMergeConflictGetPathItem( - self.handle.as_ptr(), - path_key.as_ref().as_ptr() as *const c_char, - ) - }; + pub unsafe fn get_path_item_number(&self, path_key: &str) -> Option { + let path_key = path_key.to_cstr(); + let value = + unsafe { BNAnalysisMergeConflictGetPathItem(self.handle.as_ptr(), path_key.as_ptr()) }; match value.is_null() { // SAFETY: The path must be a number. false => Some(value as u64), @@ -150,13 +138,10 @@ impl MergeConflict { } } - pub unsafe fn get_path_item_string(&self, path_key: S) -> Option { - let path_key = path_key.into_bytes_with_nul(); + pub unsafe fn get_path_item_string(&self, path_key: &str) -> Option { + let path_key = path_key.to_cstr(); let value = unsafe { - BNAnalysisMergeConflictGetPathItemString( - self.handle.as_ptr(), - path_key.as_ref().as_ptr() as *const c_char, - ) + BNAnalysisMergeConflictGetPathItemString(self.handle.as_ptr(), path_key.as_ptr()) }; match value.is_null() { false => Some(unsafe { BnString::from_raw(value) }), diff --git a/rust/src/collaboration/permission.rs b/rust/src/collaboration/permission.rs index 99a7a4f28b..76b1eba3ca 100644 --- a/rust/src/collaboration/permission.rs +++ b/rust/src/collaboration/permission.rs @@ -37,17 +37,17 @@ impl Permission { } /// Web api endpoint url - pub fn url(&self) -> BnString { + pub fn url(&self) -> String { let value = unsafe { BNCollaborationPermissionGetUrl(self.handle.as_ptr()) }; assert!(!value.is_null()); - unsafe { BnString::from_raw(value) } + unsafe { BnString::into_string(value) } } /// unique id - pub fn id(&self) -> BnString { + pub fn id(&self) -> String { let value = unsafe { BNCollaborationPermissionGetId(self.handle.as_ptr()) }; assert!(!value.is_null()); - unsafe { BnString::from_raw(value) } + unsafe { BnString::into_string(value) } } /// Level of permission diff --git a/rust/src/collaboration/project.rs b/rust/src/collaboration/project.rs index 1455f6c31c..fa46e477cd 100644 --- a/rust/src/collaboration/project.rs +++ b/rust/src/collaboration/project.rs @@ -1,4 +1,5 @@ -use std::ffi::{c_char, c_void}; +use std::ffi::c_void; +use std::path::PathBuf; use std::ptr::NonNull; use std::time::SystemTime; @@ -15,7 +16,7 @@ use crate::file_metadata::FileMetadata; use crate::progress::{NoProgressCallback, ProgressCallback}; use crate::project::Project; use crate::rc::{Array, CoreArrayProvider, CoreArrayProviderInner, Guard, Ref, RefCountable}; -use crate::string::{BnStrCompatible, BnString}; +use crate::string::{BnString, IntoCStr}; #[repr(transparent)] pub struct RemoteProject { @@ -103,17 +104,17 @@ impl RemoteProject { } /// Get the URL of the project - pub fn url(&self) -> BnString { + pub fn url(&self) -> String { let result = unsafe { BNRemoteProjectGetUrl(self.handle.as_ptr()) }; assert!(!result.is_null()); - unsafe { BnString::from_raw(result) } + unsafe { BnString::into_string(result) } } /// Get the unique ID of the project - pub fn id(&self) -> BnString { + pub fn id(&self) -> String { let result = unsafe { BNRemoteProjectGetId(self.handle.as_ptr()) }; assert!(!result.is_null()); - unsafe { BnString::from_raw(result) } + unsafe { BnString::into_string(result) } } /// Created date of the project @@ -129,40 +130,31 @@ impl RemoteProject { } /// Displayed name of file - pub fn name(&self) -> BnString { + pub fn name(&self) -> String { let result = unsafe { BNRemoteProjectGetName(self.handle.as_ptr()) }; assert!(!result.is_null()); - unsafe { BnString::from_raw(result) } + unsafe { BnString::into_string(result) } } /// Set the description of the file. You will need to push the file to update the remote version. - pub fn set_name(&self, name: S) -> Result<(), ()> { - let name = name.into_bytes_with_nul(); - let success = unsafe { - BNRemoteProjectSetName( - self.handle.as_ptr(), - name.as_ref().as_ptr() as *const c_char, - ) - }; + pub fn set_name(&self, name: &str) -> Result<(), ()> { + let name = name.to_cstr(); + let success = unsafe { BNRemoteProjectSetName(self.handle.as_ptr(), name.as_ptr()) }; success.then_some(()).ok_or(()) } /// Desciprtion of the file - pub fn description(&self) -> BnString { + pub fn description(&self) -> String { let result = unsafe { BNRemoteProjectGetDescription(self.handle.as_ptr()) }; assert!(!result.is_null()); - unsafe { BnString::from_raw(result) } + unsafe { BnString::into_string(result) } } /// Set the description of the file. You will need to push the file to update the remote version. - pub fn set_description(&self, description: S) -> Result<(), ()> { - let description = description.into_bytes_with_nul(); - let success = unsafe { - BNRemoteProjectSetDescription( - self.handle.as_ptr(), - description.as_ref().as_ptr() as *const c_char, - ) - }; + pub fn set_description(&self, description: &str) -> Result<(), ()> { + let description = description.to_cstr(); + let success = + unsafe { BNRemoteProjectSetDescription(self.handle.as_ptr(), description.as_ptr()) }; success.then_some(()).ok_or(()) } @@ -178,7 +170,7 @@ impl RemoteProject { /// Get the default directory path for a remote Project. This is based off the Setting for /// collaboration.directory, the project's id, and the project's remote's id. - pub fn default_path(&self) -> Result { + pub fn default_path(&self) -> Result { sync::default_project_path(self) } @@ -230,15 +222,13 @@ impl RemoteProject { /// /// NOTE: If the project has not been opened, it will be opened upon calling this. /// NOTE: If files have not been pulled, they will be pulled upon calling this. - pub fn get_file_by_id(&self, id: S) -> Result>, ()> { + pub fn get_file_by_id(&self, id: &str) -> Result>, ()> { // TODO: This sync should be removed? if !self.has_pulled_files() { self.pull_files()?; } - let id = id.into_bytes_with_nul(); - let result = unsafe { - BNRemoteProjectGetFileById(self.handle.as_ptr(), id.as_ref().as_ptr() as *const c_char) - }; + let id = id.to_cstr(); + let result = unsafe { BNRemoteProjectGetFileById(self.handle.as_ptr(), id.as_ptr()) }; Ok(NonNull::new(result).map(|handle| unsafe { RemoteFile::ref_from_raw(handle) })) } @@ -246,21 +236,13 @@ impl RemoteProject { /// /// NOTE: If the project has not been opened, it will be opened upon calling this. /// NOTE: If files have not been pulled, they will be pulled upon calling this. - pub fn get_file_by_name( - &self, - name: S, - ) -> Result>, ()> { + pub fn get_file_by_name(&self, name: &str) -> Result>, ()> { // TODO: This sync should be removed? if !self.has_pulled_files() { self.pull_files()?; } - let id = name.into_bytes_with_nul(); - let result = unsafe { - BNRemoteProjectGetFileByName( - self.handle.as_ptr(), - id.as_ref().as_ptr() as *const c_char, - ) - }; + let id = name.to_cstr(); + let result = unsafe { BNRemoteProjectGetFileByName(self.handle.as_ptr(), id.as_ptr()) }; Ok(NonNull::new(result).map(|handle| unsafe { RemoteFile::ref_from_raw(handle) })) } @@ -301,20 +283,15 @@ impl RemoteProject { /// * `description` - File description /// * `parent_folder` - Folder that will contain the file /// * `file_type` - Type of File to create - pub fn create_file( + pub fn create_file( &self, - filename: F, + filename: &str, contents: &[u8], - name: N, - description: D, + name: &str, + description: &str, parent_folder: Option<&RemoteFolder>, file_type: RemoteFileType, - ) -> Result, ()> - where - F: BnStrCompatible, - N: BnStrCompatible, - D: BnStrCompatible, - { + ) -> Result, ()> { self.create_file_with_progress( filename, contents, @@ -337,37 +314,34 @@ impl RemoteProject { /// * `parent_folder` - Folder that will contain the file /// * `file_type` - Type of File to create /// * `progress` - Function to call on upload progress updates - pub fn create_file_with_progress( + pub fn create_file_with_progress

( &self, - filename: F, + filename: &str, contents: &[u8], - name: N, - description: D, + name: &str, + description: &str, parent_folder: Option<&RemoteFolder>, file_type: RemoteFileType, mut progress: P, ) -> Result, ()> where - F: BnStrCompatible, - N: BnStrCompatible, - D: BnStrCompatible, P: ProgressCallback, { // TODO: This sync should be removed? self.open()?; - let filename = filename.into_bytes_with_nul(); - let name = name.into_bytes_with_nul(); - let description = description.into_bytes_with_nul(); + let filename = filename.to_cstr(); + let name = name.to_cstr(); + let description = description.to_cstr(); let folder_handle = parent_folder.map_or(std::ptr::null_mut(), |f| f.handle.as_ptr()); let file_ptr = unsafe { BNRemoteProjectCreateFile( self.handle.as_ptr(), - filename.as_ref().as_ptr() as *const c_char, + filename.as_ptr(), contents.as_ptr() as *mut _, contents.len(), - name.as_ref().as_ptr() as *const c_char, - description.as_ref().as_ptr() as *const c_char, + name.as_ptr(), + description.as_ptr(), folder_handle, file_type, Some(P::cb_progress_callback), @@ -383,27 +357,19 @@ impl RemoteProject { /// Push an updated File object to the Remote /// /// NOTE: If the project has not been opened, it will be opened upon calling this. - pub fn push_file(&self, file: &RemoteFile, extra_fields: I) -> Result<(), ()> + pub fn push_file(&self, file: &RemoteFile, extra_fields: I) -> Result<(), ()> where - I: Iterator, - K: BnStrCompatible, - V: BnStrCompatible, + I: IntoIterator, { // TODO: This sync should be removed? self.open()?; let (keys, values): (Vec<_>, Vec<_>) = extra_fields .into_iter() - .map(|(k, v)| (k.into_bytes_with_nul(), v.into_bytes_with_nul())) + .map(|(k, v)| (k.to_cstr(), v.to_cstr())) .unzip(); - let mut keys_raw = keys - .iter() - .map(|s| s.as_ref().as_ptr() as *const c_char) - .collect::>(); - let mut values_raw = values - .iter() - .map(|s| s.as_ref().as_ptr() as *const c_char) - .collect::>(); + let mut keys_raw = keys.iter().map(|s| s.as_ptr()).collect::>(); + let mut values_raw = values.iter().map(|s| s.as_ptr()).collect::>(); let success = unsafe { BNRemoteProjectPushFile( self.handle.as_ptr(), @@ -446,21 +412,13 @@ impl RemoteProject { /// /// NOTE: If the project has not been opened, it will be opened upon calling this. /// NOTE: If folders have not been pulled, they will be pulled upon calling this. - pub fn get_folder_by_id( - &self, - id: S, - ) -> Result>, ()> { + pub fn get_folder_by_id(&self, id: &str) -> Result>, ()> { // TODO: This sync should be removed? if !self.has_pulled_folders() { self.pull_folders()?; } - let id = id.into_bytes_with_nul(); - let result = unsafe { - BNRemoteProjectGetFolderById( - self.handle.as_ptr(), - id.as_ref().as_ptr() as *const c_char, - ) - }; + let id = id.to_cstr(); + let result = unsafe { BNRemoteProjectGetFolderById(self.handle.as_ptr(), id.as_ptr()) }; Ok(NonNull::new(result).map(|handle| unsafe { RemoteFolder::ref_from_raw(handle) })) } @@ -498,16 +456,12 @@ impl RemoteProject { /// * `name` - Displayed folder name /// * `description` - Folder description /// * `parent` - Parent folder (optional) - pub fn create_folder( + pub fn create_folder( &self, - name: N, - description: D, + name: &str, + description: &str, parent_folder: Option<&RemoteFolder>, - ) -> Result, ()> - where - N: BnStrCompatible, - D: BnStrCompatible, - { + ) -> Result, ()> { self.create_folder_with_progress(name, description, parent_folder, NoProgressCallback) } @@ -519,29 +473,27 @@ impl RemoteProject { /// * `description` - Folder description /// * `parent` - Parent folder (optional) /// * `progress` - Function to call on upload progress updates - pub fn create_folder_with_progress( + pub fn create_folder_with_progress

( &self, - name: N, - description: D, + name: &str, + description: &str, parent_folder: Option<&RemoteFolder>, mut progress: P, ) -> Result, ()> where - N: BnStrCompatible, - D: BnStrCompatible, P: ProgressCallback, { // TODO: This sync should be removed? self.open()?; - let name = name.into_bytes_with_nul(); - let description = description.into_bytes_with_nul(); + let name = name.to_cstr(); + let description = description.to_cstr(); let folder_handle = parent_folder.map_or(std::ptr::null_mut(), |f| f.handle.as_ptr()); let file_ptr = unsafe { BNRemoteProjectCreateFolder( self.handle.as_ptr(), - name.as_ref().as_ptr() as *const c_char, - description.as_ref().as_ptr() as *const c_char, + name.as_ptr(), + description.as_ptr(), folder_handle, Some(P::cb_progress_callback), &mut progress as *mut P as *mut c_void, @@ -559,27 +511,19 @@ impl RemoteProject { /// /// * `folder` - Folder object which has been updated /// * `extra_fields` - Extra HTTP fields to send with the update - pub fn push_folder(&self, folder: &RemoteFolder, extra_fields: I) -> Result<(), ()> + pub fn push_folder(&self, folder: &RemoteFolder, extra_fields: I) -> Result<(), ()> where - I: Iterator, - K: BnStrCompatible, - V: BnStrCompatible, + I: IntoIterator, { // TODO: This sync should be removed? self.open()?; let (keys, values): (Vec<_>, Vec<_>) = extra_fields .into_iter() - .map(|(k, v)| (k.into_bytes_with_nul(), v.into_bytes_with_nul())) + .map(|(k, v)| (k.to_cstr(), v.to_cstr())) .unzip(); - let mut keys_raw = keys - .iter() - .map(|s| s.as_ref().as_ptr() as *const c_char) - .collect::>(); - let mut values_raw = values - .iter() - .map(|s| s.as_ref().as_ptr() as *const c_char) - .collect::>(); + let mut keys_raw = keys.iter().map(|s| s.as_ptr()).collect::>(); + let mut values_raw = values.iter().map(|s| s.as_ptr()).collect::>(); let success = unsafe { BNRemoteProjectPushFolder( self.handle.as_ptr(), @@ -637,10 +581,7 @@ impl RemoteProject { /// Get a specific permission in the Project by its id. /// /// NOTE: If group or user permissions have not been pulled, they will be pulled upon calling this. - pub fn get_permission_by_id( - &self, - id: S, - ) -> Result>, ()> { + pub fn get_permission_by_id(&self, id: &str) -> Result>, ()> { // TODO: This sync should be removed? if !self.has_pulled_user_permissions() { self.pull_user_permissions()?; @@ -650,7 +591,7 @@ impl RemoteProject { self.pull_group_permissions()?; } - let id = id.into_bytes_with_nul(); + let id = id.to_cstr(); let value = unsafe { BNRemoteProjectGetPermissionById(self.handle.as_ptr(), id.as_ref().as_ptr() as *const _) }; @@ -745,9 +686,9 @@ impl RemoteProject { /// /// * `user_id` - User id /// * `level` - Permission level - pub fn create_user_permission( + pub fn create_user_permission( &self, - user_id: S, + user_id: &str, level: CollaborationPermissionLevel, ) -> Result, ()> { self.create_user_permission_with_progress(user_id, level, NoProgressCallback) @@ -760,17 +701,17 @@ impl RemoteProject { /// * `user_id` - User id /// * `level` - Permission level /// * `progress` - The progress callback to call - pub fn create_user_permission_with_progress( + pub fn create_user_permission_with_progress( &self, - user_id: S, + user_id: &str, level: CollaborationPermissionLevel, mut progress: F, ) -> Result, ()> { - let user_id = user_id.into_bytes_with_nul(); + let user_id = user_id.to_cstr(); let value = unsafe { BNRemoteProjectCreateUserPermission( self.handle.as_ptr(), - user_id.as_ref().as_ptr() as *const c_char, + user_id.as_ptr(), level, Some(F::cb_progress_callback), &mut progress as *mut F as *mut c_void, @@ -788,28 +729,16 @@ impl RemoteProject { /// /// * `permission` - Permission object which has been updated /// * `extra_fields` - Extra HTTP fields to send with the update - pub fn push_permission( - &self, - permission: &Permission, - extra_fields: I, - ) -> Result<(), ()> + pub fn push_permission(&self, permission: &Permission, extra_fields: I) -> Result<(), ()> where - I: Iterator, - K: BnStrCompatible, - V: BnStrCompatible, + I: IntoIterator, { let (keys, values): (Vec<_>, Vec<_>) = extra_fields .into_iter() - .map(|(k, v)| (k.into_bytes_with_nul(), v.into_bytes_with_nul())) + .map(|(k, v)| (k.to_cstr(), v.to_cstr())) .unzip(); - let mut keys_raw = keys - .iter() - .map(|s| s.as_ref().as_ptr() as *const c_char) - .collect::>(); - let mut values_raw = values - .iter() - .map(|s| s.as_ref().as_ptr() as *const c_char) - .collect::>(); + let mut keys_raw = keys.iter().map(|s| s.as_ptr()).collect::>(); + let mut values_raw = values.iter().map(|s| s.as_ptr()).collect::>(); let success = unsafe { BNRemoteProjectPushPermission( @@ -836,14 +765,9 @@ impl RemoteProject { /// # Arguments /// /// * `username` - Username of user to check - pub fn can_user_view(&self, username: S) -> bool { - let username = username.into_bytes_with_nul(); - unsafe { - BNRemoteProjectCanUserView( - self.handle.as_ptr(), - username.as_ref().as_ptr() as *const c_char, - ) - } + pub fn can_user_view(&self, username: &str) -> bool { + let username = username.to_cstr(); + unsafe { BNRemoteProjectCanUserView(self.handle.as_ptr(), username.as_ptr()) } } /// Determine if a user is in any of the edit/admin groups. @@ -851,14 +775,9 @@ impl RemoteProject { /// # Arguments /// /// * `username` - Username of user to check - pub fn can_user_edit(&self, username: S) -> bool { - let username = username.into_bytes_with_nul(); - unsafe { - BNRemoteProjectCanUserEdit( - self.handle.as_ptr(), - username.as_ref().as_ptr() as *const c_char, - ) - } + pub fn can_user_edit(&self, username: &str) -> bool { + let username = username.to_cstr(); + unsafe { BNRemoteProjectCanUserEdit(self.handle.as_ptr(), username.as_ptr()) } } /// Determine if a user is in the admin group. @@ -866,22 +785,17 @@ impl RemoteProject { /// # Arguments /// /// * `username` - Username of user to check - pub fn can_user_admin(&self, username: S) -> bool { - let username = username.into_bytes_with_nul(); - unsafe { - BNRemoteProjectCanUserAdmin( - self.handle.as_ptr(), - username.as_ref().as_ptr() as *const c_char, - ) - } + pub fn can_user_admin(&self, username: &str) -> bool { + let username = username.to_cstr(); + unsafe { BNRemoteProjectCanUserAdmin(self.handle.as_ptr(), username.as_ptr()) } } /// Get the default directory path for a remote Project. This is based off /// the Setting for collaboration.directory, the project's id, and the /// project's remote's id. - pub fn default_project_path(&self) -> BnString { + pub fn default_project_path(&self) -> String { let result = unsafe { BNCollaborationDefaultProjectPath(self.handle.as_ptr()) }; - unsafe { BnString::from_raw(result) } + unsafe { BnString::into_string(result) } } /// Upload a file, with database, to the remote under the given project diff --git a/rust/src/collaboration/remote.rs b/rust/src/collaboration/remote.rs index c12504033b..c1ad66786c 100644 --- a/rust/src/collaboration/remote.rs +++ b/rust/src/collaboration/remote.rs @@ -1,16 +1,18 @@ +use super::{sync, GroupId, RemoteGroup, RemoteProject, RemoteUser}; use binaryninjacore_sys::*; -use std::ffi::{c_char, c_void}; +use std::env::VarError; +use std::ffi::c_void; use std::ptr::NonNull; -use super::{sync, GroupId, RemoteGroup, RemoteProject, RemoteUser}; - use crate::binary_view::BinaryView; use crate::database::Database; use crate::enterprise; use crate::progress::{NoProgressCallback, ProgressCallback}; use crate::project::Project; use crate::rc::{Array, CoreArrayProvider, CoreArrayProviderInner, Guard, Ref, RefCountable}; -use crate::string::{BnStrCompatible, BnString}; +use crate::secrets_provider::CoreSecretsProvider; +use crate::settings::Settings; +use crate::string::{BnString, IntoCStr}; #[repr(transparent)] pub struct Remote { @@ -27,15 +29,10 @@ impl Remote { } /// Create a Remote and add it to the list of known remotes (saved to Settings) - pub fn new(name: N, address: A) -> Ref { - let name = name.into_bytes_with_nul(); - let address = address.into_bytes_with_nul(); - let result = unsafe { - BNCollaborationCreateRemote( - name.as_ref().as_ptr() as *const c_char, - address.as_ref().as_ptr() as *const c_char, - ) - }; + pub fn new(name: &str, address: &str) -> Ref { + let name = name.to_cstr(); + let address = address.to_cstr(); + let result = unsafe { BNCollaborationCreateRemote(name.as_ptr(), address.as_ptr()) }; unsafe { Self::ref_from_raw(NonNull::new(result).unwrap()) } } @@ -65,17 +62,17 @@ impl Remote { } /// Gets the name of the remote. - pub fn name(&self) -> BnString { + pub fn name(&self) -> String { let result = unsafe { BNRemoteGetName(self.handle.as_ptr()) }; assert!(!result.is_null()); - unsafe { BnString::from_raw(result) } + unsafe { BnString::into_string(result) } } /// Gets the address of the remote. - pub fn address(&self) -> BnString { + pub fn address(&self) -> String { let result = unsafe { BNRemoteGetAddress(self.handle.as_ptr()) }; assert!(!result.is_null()); - unsafe { BnString::from_raw(result) } + unsafe { BnString::into_string(result) } } /// Checks if the remote is connected. @@ -84,17 +81,17 @@ impl Remote { } /// Gets the username used to connect to the remote. - pub fn username(&self) -> BnString { + pub fn username(&self) -> String { let result = unsafe { BNRemoteGetUsername(self.handle.as_ptr()) }; assert!(!result.is_null()); - unsafe { BnString::from_raw(result) } + unsafe { BnString::into_string(result) } } /// Gets the token used to connect to the remote. - pub fn token(&self) -> BnString { + pub fn token(&self) -> String { let result = unsafe { BNRemoteGetToken(self.handle.as_ptr()) }; assert!(!result.is_null()); - unsafe { BnString::from_raw(result) } + unsafe { BnString::into_string(result) } } /// Gets the server version. If metadata has not been pulled, it will be pulled upon calling this. @@ -168,24 +165,20 @@ impl Remote { } /// Requests an authentication token using a username and password. - pub fn request_authentication_token( - &self, - username: U, - password: P, - ) -> Option { - let username = username.into_bytes_with_nul(); - let password = password.into_bytes_with_nul(); + pub fn request_authentication_token(&self, username: &str, password: &str) -> Option { + let username = username.to_cstr(); + let password = password.to_cstr(); let token = unsafe { BNRemoteRequestAuthenticationToken( self.handle.as_ptr(), - username.as_ref().as_ptr() as *const c_char, - password.as_ref().as_ptr() as *const c_char, + username.as_ptr(), + password.as_ptr(), ) }; if token.is_null() { None } else { - Some(unsafe { BnString::from_raw(token) }) + Some(unsafe { BnString::into_string(token) }) } } @@ -194,20 +187,22 @@ impl Remote { /// Use [Remote::connect_with_opts] if you cannot otherwise automatically connect using enterprise. /// /// WARNING: This is currently **not** thread safe, if you try and connect/disconnect to a remote on - /// multiple threads you will be subject to race conditions. To avoid this wrap the [`Remote`] in - /// a synchronization primitive, and pass that to your threads. Or don't try and connect on multiple threads. + /// multiple threads, you will be subject to race conditions. To avoid this, wrap the [`Remote`] in + /// a synchronization primitive and pass that to your threads. Or don't try and connect on multiple threads. pub fn connect(&self) -> Result<(), ()> { - // TODO: implement SecretsProvider if self.is_enterprise()? && enterprise::is_server_authenticated() { self.connect_with_opts(ConnectionOptions::from_enterprise()?) } else { - // TODO: Make this error instead. - let username = - std::env::var("BN_ENTERPRISE_USERNAME").expect("No username for connection!"); - let password = - std::env::var("BN_ENTERPRISE_PASSWORD").expect("No password for connection!"); - let connection_opts = ConnectionOptions::new_with_password(username, password); - self.connect_with_opts(connection_opts) + // Try to load from env vars. + match ConnectionOptions::from_env_variables() { + Ok(connection_opts) => self.connect_with_opts(connection_opts), + Err(_) => { + // Try to load from the enterprise secrets provider. + let secrets_connection_opts = + ConnectionOptions::from_secrets_provider(&self.address())?; + self.connect_with_opts(secrets_connection_opts) + } + } } } @@ -224,16 +219,15 @@ impl Remote { let password = options .password .expect("No password or token for connection!"); - let token = self.request_authentication_token(&options.username, password); + let token = self.request_authentication_token(&options.username, &password); // TODO: Error if None. token.unwrap().to_string() } }; - let username = options.username.into_bytes_with_nul(); - let username_ptr = username.as_ptr() as *const c_char; - let token = token.into_bytes_with_nul(); - let token_ptr = token.as_ptr() as *const c_char; - let success = unsafe { BNRemoteConnect(self.handle.as_ptr(), username_ptr, token_ptr) }; + let username = options.username.to_cstr(); + let token = token.to_cstr(); + let success = + unsafe { BNRemoteConnect(self.handle.as_ptr(), username.as_ptr(), token.as_ptr()) }; success.then_some(()).ok_or(()) } @@ -281,39 +275,26 @@ impl Remote { /// Gets a specific project in the Remote by its id. /// /// NOTE: If projects have not been pulled, they will be pulled upon calling this. - pub fn get_project_by_id( - &self, - id: S, - ) -> Result>, ()> { + pub fn get_project_by_id(&self, id: &str) -> Result>, ()> { if !self.has_pulled_projects() { self.pull_projects()?; } - let id = id.into_bytes_with_nul(); - let value = unsafe { - BNRemoteGetProjectById(self.handle.as_ptr(), id.as_ref().as_ptr() as *const c_char) - }; + let id = id.to_cstr(); + let value = unsafe { BNRemoteGetProjectById(self.handle.as_ptr(), id.as_ptr()) }; Ok(NonNull::new(value).map(|handle| unsafe { RemoteProject::ref_from_raw(handle) })) } /// Gets a specific project in the Remote by its name. /// /// NOTE: If projects have not been pulled, they will be pulled upon calling this. - pub fn get_project_by_name( - &self, - name: S, - ) -> Result>, ()> { + pub fn get_project_by_name(&self, name: &str) -> Result>, ()> { if !self.has_pulled_projects() { self.pull_projects()?; } - let name = name.into_bytes_with_nul(); - let value = unsafe { - BNRemoteGetProjectByName( - self.handle.as_ptr(), - name.as_ref().as_ptr() as *const c_char, - ) - }; + let name = name.to_cstr(); + let value = unsafe { BNRemoteGetProjectByName(self.handle.as_ptr(), name.as_ptr()) }; Ok(NonNull::new(value).map(|handle| unsafe { RemoteProject::ref_from_raw(handle) })) } @@ -347,25 +328,17 @@ impl Remote { /// /// * `name` - Project name /// * `description` - Project description - pub fn create_project( - &self, - name: N, - description: D, - ) -> Result, ()> { + pub fn create_project(&self, name: &str, description: &str) -> Result, ()> { // TODO: Do we want this? // TODO: If you have not yet pulled projects you will have never filled the map you will be placing your // TODO: New project in. if !self.has_pulled_projects() { self.pull_projects()?; } - let name = name.into_bytes_with_nul(); - let description = description.into_bytes_with_nul(); + let name = name.to_cstr(); + let description = description.to_cstr(); let value = unsafe { - BNRemoteCreateProject( - self.handle.as_ptr(), - name.as_ref().as_ptr() as *const c_char, - description.as_ref().as_ptr() as *const c_char, - ) + BNRemoteCreateProject(self.handle.as_ptr(), name.as_ptr(), description.as_ptr()) }; NonNull::new(value) .map(|handle| unsafe { RemoteProject::ref_from_raw(handle) }) @@ -400,24 +373,16 @@ impl Remote { /// /// * `project` - Project object which has been updated /// * `extra_fields` - Extra HTTP fields to send with the update - pub fn push_project(&self, project: &RemoteProject, extra_fields: I) -> Result<(), ()> + pub fn push_project(&self, project: &RemoteProject, extra_fields: I) -> Result<(), ()> where - I: Iterator, - K: BnStrCompatible, - V: BnStrCompatible, + I: IntoIterator, { let (keys, values): (Vec<_>, Vec<_>) = extra_fields .into_iter() - .map(|(k, v)| (k.into_bytes_with_nul(), v.into_bytes_with_nul())) + .map(|(k, v)| (k.to_cstr(), v.to_cstr())) .unzip(); - let mut keys_raw = keys - .iter() - .map(|s| s.as_ref().as_ptr() as *const c_char) - .collect::>(); - let mut values_raw = values - .iter() - .map(|s| s.as_ref().as_ptr() as *const c_char) - .collect::>(); + let mut keys_raw = keys.iter().map(|s| s.as_ptr()).collect::>(); + let mut values_raw = values.iter().map(|s| s.as_ptr()).collect::>(); let success = unsafe { BNRemotePushProject( @@ -472,21 +437,13 @@ impl Remote { /// /// If groups have not been pulled, they will be pulled upon calling this. /// This function is only available to accounts with admin status on the Remote. - pub fn get_group_by_name( - &self, - name: S, - ) -> Result>, ()> { + pub fn get_group_by_name(&self, name: &str) -> Result>, ()> { if !self.has_pulled_groups() { self.pull_groups()?; } - let name = name.into_bytes_with_nul(); - let value = unsafe { - BNRemoteGetGroupByName( - self.handle.as_ptr(), - name.as_ref().as_ptr() as *const c_char, - ) - }; + let name = name.to_cstr(); + let value = unsafe { BNRemoteGetGroupByName(self.handle.as_ptr(), name.as_ptr()) }; Ok(NonNull::new(value).map(|handle| unsafe { RemoteGroup::ref_from_raw(handle) })) } @@ -496,11 +453,8 @@ impl Remote { /// # Arguments /// /// * `prefix` - Prefix of name for groups - pub fn search_groups( - &self, - prefix: S, - ) -> Result<(Array, Array), ()> { - let prefix = prefix.into_bytes_with_nul(); + pub fn search_groups(&self, prefix: &str) -> Result<(Array, Array), ()> { + let prefix = prefix.to_cstr(); let mut count = 0; let mut group_ids = std::ptr::null_mut(); let mut group_names = std::ptr::null_mut(); @@ -508,7 +462,7 @@ impl Remote { let success = unsafe { BNRemoteSearchGroups( self.handle.as_ptr(), - prefix.as_ref().as_ptr() as *const c_char, + prefix.as_ptr(), &mut group_ids, &mut group_names, &mut count, @@ -558,26 +512,18 @@ impl Remote { /// /// * `name` - Group name /// * `usernames` - List of usernames of users in the group - pub fn create_group(&self, name: N, usernames: I) -> Result, ()> + pub fn create_group(&self, name: &str, usernames: I) -> Result, ()> where - N: BnStrCompatible, - I: IntoIterator, - I::Item: BnStrCompatible, + I: IntoIterator, { - let name = name.into_bytes_with_nul(); - let usernames: Vec<_> = usernames - .into_iter() - .map(|s| s.into_bytes_with_nul()) - .collect(); - let mut username_ptrs: Vec<_> = usernames - .iter() - .map(|s| s.as_ref().as_ptr() as *const c_char) - .collect(); + let name = name.to_cstr(); + let usernames: Vec<_> = usernames.into_iter().map(|s| s.to_cstr()).collect(); + let mut username_ptrs: Vec<_> = usernames.iter().map(|s| s.as_ptr()).collect(); let value = unsafe { BNRemoteCreateGroup( self.handle.as_ptr(), - name.as_ref().as_ptr() as *const c_char, + name.as_ptr(), username_ptrs.as_mut_ptr(), username_ptrs.len(), ) @@ -594,24 +540,16 @@ impl Remote { /// /// * `group` - Group object which has been updated /// * `extra_fields` - Extra HTTP fields to send with the update - pub fn push_group(&self, group: &RemoteGroup, extra_fields: I) -> Result<(), ()> + pub fn push_group(&self, group: &RemoteGroup, extra_fields: I) -> Result<(), ()> where - I: IntoIterator, - K: BnStrCompatible, - V: BnStrCompatible, + I: IntoIterator, { let (keys, values): (Vec<_>, Vec<_>) = extra_fields .into_iter() - .map(|(k, v)| (k.into_bytes_with_nul(), v.into_bytes_with_nul())) + .map(|(k, v)| (k.to_cstr(), v.to_cstr())) .unzip(); - let mut keys_raw: Vec<_> = keys - .iter() - .map(|s| s.as_ref().as_ptr() as *const c_char) - .collect(); - let mut values_raw: Vec<_> = values - .iter() - .map(|s| s.as_ref().as_ptr() as *const c_char) - .collect(); + let mut keys_raw: Vec<_> = keys.iter().map(|s| s.as_ptr()).collect(); + let mut values_raw: Vec<_> = values.iter().map(|s| s.as_ptr()).collect(); let success = unsafe { BNRemotePushGroup( @@ -663,14 +601,12 @@ impl Remote { /// # Arguments /// /// * `id` - The identifier of the user to retrieve. - pub fn get_user_by_id(&self, id: S) -> Result>, ()> { + pub fn get_user_by_id(&self, id: &str) -> Result>, ()> { if !self.has_pulled_users() { self.pull_users()?; } - let id = id.into_bytes_with_nul(); - let value = unsafe { - BNRemoteGetUserById(self.handle.as_ptr(), id.as_ref().as_ptr() as *const c_char) - }; + let id = id.to_cstr(); + let value = unsafe { BNRemoteGetUserById(self.handle.as_ptr(), id.as_ptr()) }; Ok(NonNull::new(value).map(|handle| unsafe { RemoteUser::ref_from_raw(handle) })) } @@ -683,20 +619,12 @@ impl Remote { /// # Arguments /// /// * `username` - The username of the user to retrieve. - pub fn get_user_by_username( - &self, - username: S, - ) -> Result>, ()> { + pub fn get_user_by_username(&self, username: &str) -> Result>, ()> { if !self.has_pulled_users() { self.pull_users()?; } - let username = username.into_bytes_with_nul(); - let value = unsafe { - BNRemoteGetUserByUsername( - self.handle.as_ptr(), - username.as_ref().as_ptr() as *const c_char, - ) - }; + let username = username.to_cstr(); + let value = unsafe { BNRemoteGetUserByUsername(self.handle.as_ptr(), username.as_ptr()) }; Ok(NonNull::new(value).map(|handle| unsafe { RemoteUser::ref_from_raw(handle) })) } @@ -718,18 +646,15 @@ impl Remote { /// # Arguments /// /// * `prefix` - The prefix to search for in usernames. - pub fn search_users( - &self, - prefix: S, - ) -> Result<(Array, Array), ()> { - let prefix = prefix.into_bytes_with_nul(); + pub fn search_users(&self, prefix: &str) -> Result<(Array, Array), ()> { + let prefix = prefix.to_cstr(); let mut count = 0; let mut user_ids = std::ptr::null_mut(); let mut usernames = std::ptr::null_mut(); let success = unsafe { BNRemoteSearchUsers( self.handle.as_ptr(), - prefix.as_ref().as_ptr() as *const c_char, + prefix.as_ptr(), &mut user_ids, &mut usernames, &mut count, @@ -783,26 +708,26 @@ impl Remote { /// # Arguments /// /// * Various details about the new user to be created. - pub fn create_user( + pub fn create_user( &self, - username: U, - email: E, + username: &str, + email: &str, is_active: bool, - password: P, + password: &str, group_ids: &[u64], user_permission_ids: &[u64], ) -> Result, ()> { - let username = username.into_bytes_with_nul(); - let email = email.into_bytes_with_nul(); - let password = password.into_bytes_with_nul(); + let username = username.to_cstr(); + let email = email.to_cstr(); + let password = password.to_cstr(); let value = unsafe { BNRemoteCreateUser( self.handle.as_ptr(), - username.as_ref().as_ptr() as *const c_char, - email.as_ref().as_ptr() as *const c_char, + username.as_ptr(), + email.as_ptr(), is_active, - password.as_ref().as_ptr() as *const c_char, + password.as_ptr(), group_ids.as_ptr(), group_ids.len(), user_permission_ids.as_ptr(), @@ -822,24 +747,16 @@ impl Remote { /// /// * `user` - Reference to the `RemoteUser` object to push. /// * `extra_fields` - Optional extra fields to send with the update. - pub fn push_user(&self, user: &RemoteUser, extra_fields: I) -> Result<(), ()> + pub fn push_user(&self, user: &RemoteUser, extra_fields: I) -> Result<(), ()> where - I: Iterator, - K: BnStrCompatible, - V: BnStrCompatible, + I: IntoIterator, { let (keys, values): (Vec<_>, Vec<_>) = extra_fields .into_iter() - .map(|(k, v)| (k.into_bytes_with_nul(), v.into_bytes_with_nul())) + .map(|(k, v)| (k.to_cstr(), v.to_cstr())) .unzip(); - let mut keys_raw: Vec<_> = keys - .iter() - .map(|s| s.as_ref().as_ptr() as *const c_char) - .collect(); - let mut values_raw: Vec<_> = values - .iter() - .map(|s| s.as_ref().as_ptr() as *const c_char) - .collect(); + let mut keys_raw: Vec<_> = keys.iter().map(|s| s.as_ptr()).collect(); + let mut values_raw: Vec<_> = values.iter().map(|s| s.as_ptr()).collect(); let success = unsafe { BNRemotePushUser( self.handle.as_ptr(), @@ -957,11 +874,31 @@ impl ConnectionOptions { // TODO: Check if enterprise is initialized and error if not. let username = enterprise::server_username(); let token = enterprise::server_token(); + Ok(Self::new_with_token(username, token)) + } + + /// Retrieves the [`ConnectionOptions`] for the given address. + /// + /// NOTE: Uses the secret's provider specified by the setting "enterprise.secretsProvider". + pub fn from_secrets_provider(address: &str) -> Result { + let secrets_provider_name = Settings::new().get_string("enterprise.secretsProvider"); + let provider = CoreSecretsProvider::by_name(&secrets_provider_name).ok_or(())?; + let cred_data_str = provider.get_data(address); + if cred_data_str.is_empty() { + return Err(()); + } + let cred_data: serde_json::Value = serde_json::from_str(&cred_data_str).map_err(|_| ())?; + let username = cred_data["username"].as_str().ok_or(())?; + let token = cred_data["token"].as_str().ok_or(())?; Ok(Self::new_with_token( username.to_string(), token.to_string(), )) } - // TODO: from_secrets_provider + pub fn from_env_variables() -> Result { + let username = std::env::var("BN_ENTERPRISE_USERNAME")?; + let password = std::env::var("BN_ENTERPRISE_PASSWORD")?; + Ok(ConnectionOptions::new_with_password(username, password)) + } } diff --git a/rust/src/collaboration/snapshot.rs b/rust/src/collaboration/snapshot.rs index 9f8f36934b..203d8677fb 100644 --- a/rust/src/collaboration/snapshot.rs +++ b/rust/src/collaboration/snapshot.rs @@ -1,4 +1,4 @@ -use std::ffi::{c_char, c_void}; +use std::ffi::c_void; use std::ptr::NonNull; use std::time::SystemTime; @@ -8,7 +8,7 @@ use crate::collaboration::undo::{RemoteUndoEntry, RemoteUndoEntryId}; use crate::database::snapshot::Snapshot; use crate::progress::{NoProgressCallback, ProgressCallback}; use crate::rc::{Array, CoreArrayProvider, CoreArrayProviderInner, Guard, Ref, RefCountable}; -use crate::string::{BnStrCompatible, BnString}; +use crate::string::{BnString, IntoCStr}; use binaryninjacore_sys::*; // TODO: RemoteSnapshotId ? @@ -54,52 +54,52 @@ impl RemoteSnapshot { } /// Web api endpoint url - pub fn url(&self) -> BnString { + pub fn url(&self) -> String { let value = unsafe { BNCollaborationSnapshotGetUrl(self.handle.as_ptr()) }; assert!(!value.is_null()); - unsafe { BnString::from_raw(value) } + unsafe { BnString::into_string(value) } } /// Unique id - pub fn id(&self) -> BnString { + pub fn id(&self) -> String { let value = unsafe { BNCollaborationSnapshotGetId(self.handle.as_ptr()) }; assert!(!value.is_null()); - unsafe { BnString::from_raw(value) } + unsafe { BnString::into_string(value) } } /// Name of snapshot - pub fn name(&self) -> BnString { + pub fn name(&self) -> String { let value = unsafe { BNCollaborationSnapshotGetName(self.handle.as_ptr()) }; assert!(!value.is_null()); - unsafe { BnString::from_raw(value) } + unsafe { BnString::into_string(value) } } /// Get the title of a snapshot: the first line of its name - pub fn title(&self) -> BnString { + pub fn title(&self) -> String { let value = unsafe { BNCollaborationSnapshotGetTitle(self.handle.as_ptr()) }; assert!(!value.is_null()); - unsafe { BnString::from_raw(value) } + unsafe { BnString::into_string(value) } } /// Get the description of a snapshot: the lines of its name after the first line - pub fn description(&self) -> BnString { + pub fn description(&self) -> String { let value = unsafe { BNCollaborationSnapshotGetDescription(self.handle.as_ptr()) }; assert!(!value.is_null()); - unsafe { BnString::from_raw(value) } + unsafe { BnString::into_string(value) } } /// Get the user id of the author of a snapshot - pub fn author(&self) -> BnString { + pub fn author(&self) -> String { let value = unsafe { BNCollaborationSnapshotGetAuthor(self.handle.as_ptr()) }; assert!(!value.is_null()); - unsafe { BnString::from_raw(value) } + unsafe { BnString::into_string(value) } } /// Get the username of the author of a snapshot, if possible (vs author which is user id) - pub fn author_username(&self) -> BnString { + pub fn author_username(&self) -> String { let value = unsafe { BNCollaborationSnapshotGetAuthorUsername(self.handle.as_ptr()) }; assert!(!value.is_null()); - unsafe { BnString::from_raw(value) } + unsafe { BnString::into_string(value) } } /// Created date of Snapshot @@ -116,18 +116,18 @@ impl RemoteSnapshot { /// Hash of snapshot data (analysis and markup, etc) /// No specific hash algorithm is guaranteed - pub fn hash(&self) -> BnString { + pub fn hash(&self) -> String { let value = unsafe { BNCollaborationSnapshotGetHash(self.handle.as_ptr()) }; assert!(!value.is_null()); - unsafe { BnString::from_raw(value) } + unsafe { BnString::into_string(value) } } /// Hash of file contents in snapshot /// No specific hash algorithm is guaranteed - pub fn snapshot_file_hash(&self) -> BnString { + pub fn snapshot_file_hash(&self) -> String { let value = unsafe { BNCollaborationSnapshotGetSnapshotFileHash(self.handle.as_ptr()) }; assert!(!value.is_null()); - unsafe { BnString::from_raw(value) } + unsafe { BnString::into_string(value) } } /// If the snapshot has pulled undo entries yet @@ -226,18 +226,18 @@ impl RemoteSnapshot { } /// Create a new Undo Entry in this snapshot. - pub fn create_undo_entry( + pub fn create_undo_entry( &self, parent: Option, - data: S, + data: &str, ) -> Result, ()> { - let data = data.into_bytes_with_nul(); + let data = data.to_cstr(); let value = unsafe { BNCollaborationSnapshotCreateUndoEntry( self.handle.as_ptr(), parent.is_some(), parent.unwrap_or(0), - data.as_ref().as_ptr() as *const c_char, + data.as_ptr(), ) }; let handle = NonNull::new(value).ok_or(())?; diff --git a/rust/src/collaboration/sync.rs b/rust/src/collaboration/sync.rs index 1c11b8f060..a8006b8b3c 100644 --- a/rust/src/collaboration/sync.rs +++ b/rust/src/collaboration/sync.rs @@ -3,7 +3,7 @@ use super::{ }; use binaryninjacore_sys::*; use std::ffi::{c_char, c_void}; -use std::mem::ManuallyDrop; +use std::path::{Path, PathBuf}; use std::ptr::NonNull; use crate::binary_view::{BinaryView, BinaryViewExt}; @@ -12,62 +12,55 @@ use crate::file_metadata::FileMetadata; use crate::progress::{NoProgressCallback, ProgressCallback}; use crate::project::file::ProjectFile; use crate::rc::Ref; -use crate::string::{BnStrCompatible, BnString}; +use crate::string::{raw_to_string, BnString, IntoCStr}; use crate::type_archive::{TypeArchive, TypeArchiveMergeConflict}; -// TODO: PathBuf /// Get the default directory path for a remote Project. This is based off the Setting for /// collaboration.directory, the project's id, and the project's remote's id. -pub fn default_project_path(project: &RemoteProject) -> Result { +pub fn default_project_path(project: &RemoteProject) -> Result { let result = unsafe { BNCollaborationDefaultProjectPath(project.handle.as_ptr()) }; let success = !result.is_null(); success - .then(|| unsafe { BnString::from_raw(result) }) + .then(|| PathBuf::from(unsafe { BnString::into_string(result) })) .ok_or(()) } -// TODO: PathBuf // Get the default filepath for a remote File. This is based off the Setting for // collaboration.directory, the file's id, the file's project's id, and the file's // remote's id. -pub fn default_file_path(file: &RemoteFile) -> Result { +pub fn default_file_path(file: &RemoteFile) -> Result { let result = unsafe { BNCollaborationDefaultFilePath(file.handle.as_ptr()) }; let success = !result.is_null(); success - .then(|| unsafe { BnString::from_raw(result) }) + .then(|| PathBuf::from(unsafe { BnString::into_string(result) })) .ok_or(()) } -// TODO: AsRef /// Download a file from its remote, saving all snapshots to a database in the /// specified location. Returns a FileContext for opening the file later. /// /// * `file` - Remote File to download and open /// * `db_path` - File path for saved database -pub fn download_file( - file: &RemoteFile, - db_path: S, -) -> Result, ()> { +pub fn download_file(file: &RemoteFile, db_path: &Path) -> Result, ()> { download_file_with_progress(file, db_path, NoProgressCallback) } -// TODO: AsRef /// Download a file from its remote, saving all snapshots to a database in the /// specified location. Returns a FileContext for opening the file later. /// /// * `file` - Remote File to download and open /// * `db_path` - File path for saved database /// * `progress` - Function to call for progress updates -pub fn download_file_with_progress( +pub fn download_file_with_progress( file: &RemoteFile, - db_path: S, + db_path: &Path, mut progress: F, ) -> Result, ()> { - let db_path = db_path.into_bytes_with_nul(); + let db_path = db_path.to_cstr(); let result = unsafe { BNCollaborationDownloadFile( file.handle.as_ptr(), - db_path.as_ref().as_ptr() as *const c_char, + db_path.as_ptr(), Some(F::cb_progress_callback), &mut progress as *mut F as *mut c_void, ) @@ -222,31 +215,27 @@ pub fn get_local_snapshot_for_remote( .ok_or(()) } -pub fn download_database(file: &RemoteFile, location: S, force: bool) -> Result<(), ()> -where - S: BnStrCompatible, -{ +pub fn download_database(file: &RemoteFile, location: &Path, force: bool) -> Result<(), ()> { download_database_with_progress(file, location, force, NoProgressCallback) } -pub fn download_database_with_progress( +pub fn download_database_with_progress( file: &RemoteFile, - location: S, + location: &Path, force: bool, - mut progress: F, + mut progress: PC, ) -> Result<(), ()> where - S: BnStrCompatible, - F: ProgressCallback, + PC: ProgressCallback, { - let db_path = location.into_bytes_with_nul(); + let db_path = location.to_cstr(); let success = unsafe { BNCollaborationDownloadDatabaseForFile( file.handle.as_ptr(), - db_path.as_ref().as_ptr() as *const c_char, + db_path.as_ptr(), force, - Some(F::cb_progress_callback), - &mut progress as *mut _ as *mut c_void, + Some(PC::cb_progress_callback), + &mut progress as *mut PC as *mut c_void, ) }; success.then_some(()).ok_or(()) @@ -479,17 +468,17 @@ pub fn get_snapshot_author( /// * `database` - Parent database /// * `snapshot` - Snapshot to edit /// * `author` - Target author -pub fn set_snapshot_author( +pub fn set_snapshot_author( database: &Database, snapshot: &Snapshot, - author: S, + author: &str, ) -> Result<(), ()> { - let author = author.into_bytes_with_nul(); + let author = author.to_cstr(); let success = unsafe { BNCollaborationSetSnapshotAuthor( database.handle.as_ptr(), snapshot.handle.as_ptr(), - author.as_ref().as_ptr() as *const c_char, + author.as_ptr(), ) }; success.then_some(()).ok_or(()) @@ -654,15 +643,15 @@ pub fn get_remote_file_for_local_type_archive(database: &TypeArchive) -> Option< } /// Get the remote snapshot associated with a local snapshot (if it exists) in a Type Archive -pub fn get_remote_snapshot_from_local_type_archive( +pub fn get_remote_snapshot_from_local_type_archive( type_archive: &TypeArchive, - snapshot_id: S, + snapshot_id: &str, ) -> Option> { - let snapshot_id = snapshot_id.into_bytes_with_nul(); + let snapshot_id = snapshot_id.to_cstr(); let value = unsafe { BNCollaborationGetRemoteSnapshotFromLocalTypeArchive( type_archive.handle.as_ptr(), - snapshot_id.as_ref().as_ptr() as *const c_char, + snapshot_id.as_ptr(), ) }; NonNull::new(value).map(|handle| unsafe { RemoteSnapshot::ref_from_raw(handle) }) @@ -683,43 +672,40 @@ pub fn get_local_snapshot_from_remote_type_archive( } /// Test if a snapshot is ignored from the archive -pub fn is_type_archive_snapshot_ignored( - type_archive: &TypeArchive, - snapshot_id: S, -) -> bool { - let snapshot_id = snapshot_id.into_bytes_with_nul(); +pub fn is_type_archive_snapshot_ignored(type_archive: &TypeArchive, snapshot_id: &str) -> bool { + let snapshot_id = snapshot_id.to_cstr(); unsafe { BNCollaborationIsTypeArchiveSnapshotIgnored( type_archive.handle.as_ptr(), - snapshot_id.as_ref().as_ptr() as *const c_char, + snapshot_id.as_ptr(), ) } } /// Download a type archive from its remote, saving all snapshots to an archive in the /// specified `location`. Returns a [`TypeArchive`] for using later. -pub fn download_type_archive( +pub fn download_type_archive( file: &RemoteFile, - location: S, + location: &Path, ) -> Result>, ()> { download_type_archive_with_progress(file, location, NoProgressCallback) } /// Download a type archive from its remote, saving all snapshots to an archive in the /// specified `location`. Returns a [`TypeArchive`] for using later. -pub fn download_type_archive_with_progress( +pub fn download_type_archive_with_progress( file: &RemoteFile, - location: S, - mut progress: F, + location: &Path, + mut progress: PC, ) -> Result>, ()> { let mut value = std::ptr::null_mut(); - let db_path = location.into_bytes_with_nul(); + let db_path = location.to_cstr(); let success = unsafe { BNCollaborationDownloadTypeArchive( file.handle.as_ptr(), - db_path.as_ref().as_ptr() as *const c_char, - Some(F::cb_progress_callback), - &mut progress as *mut F as *mut c_void, + db_path.as_ptr(), + Some(PC::cb_progress_callback), + &mut progress as *mut PC as *mut c_void, &mut value, ) }; @@ -859,13 +845,11 @@ pub trait DatabaseConflictHandler: Sized { let keys = core::slice::from_raw_parts(keys, conflict_count); let conflicts = core::slice::from_raw_parts(conflicts, conflict_count); keys.iter().zip(conflicts.iter()).all(|(key, conflict)| { - // NOTE this is a reference, not owned, so ManuallyDrop is required, or just implement `ref_from_raw` - // TODO: Replace with raw_to_string - let key = ManuallyDrop::new(BnString::from_raw(*key as *mut _)); + let key = raw_to_string(*key).unwrap(); // TODO I guess dont drop here? let raw_ptr = NonNull::new(*conflict).unwrap(); let conflict = MergeConflict::from_raw(raw_ptr); - ctxt.handle_conflict(key.as_str(), &conflict) + ctxt.handle_conflict(&key, &conflict) }) } } diff --git a/rust/src/collaboration/undo.rs b/rust/src/collaboration/undo.rs index 9f1cc5f086..0dde860fde 100644 --- a/rust/src/collaboration/undo.rs +++ b/rust/src/collaboration/undo.rs @@ -57,10 +57,10 @@ impl RemoteUndoEntry { } /// Web api endpoint url - pub fn url(&self) -> BnString { + pub fn url(&self) -> String { let value = unsafe { BNCollaborationUndoEntryGetUrl(self.handle.as_ptr()) }; assert!(!value.is_null()); - unsafe { BnString::from_raw(value) } + unsafe { BnString::into_string(value) } } /// Unique id diff --git a/rust/src/collaboration/user.rs b/rust/src/collaboration/user.rs index 0e3433d319..51bdebb54a 100644 --- a/rust/src/collaboration/user.rs +++ b/rust/src/collaboration/user.rs @@ -1,10 +1,9 @@ use super::Remote; use binaryninjacore_sys::*; -use std::ffi::c_char; use std::ptr::NonNull; use crate::rc::{CoreArrayProvider, CoreArrayProviderInner, Guard, Ref, RefCountable}; -use crate::string::{BnStrCompatible, BnString}; +use crate::string::{BnString, IntoCStr}; #[repr(transparent)] pub struct RemoteUser { @@ -28,35 +27,31 @@ impl RemoteUser { } /// Web api endpoint url - pub fn url(&self) -> BnString { + pub fn url(&self) -> String { let value = unsafe { BNCollaborationUserGetUrl(self.handle.as_ptr()) }; assert!(!value.is_null()); - unsafe { BnString::from_raw(value) } + unsafe { BnString::into_string(value) } } /// Unique id - pub fn id(&self) -> BnString { + pub fn id(&self) -> String { let value = unsafe { BNCollaborationUserGetId(self.handle.as_ptr()) }; assert!(!value.is_null()); - unsafe { BnString::from_raw(value) } + unsafe { BnString::into_string(value) } } /// User's login username - pub fn username(&self) -> BnString { + pub fn username(&self) -> String { let value = unsafe { BNCollaborationUserGetUsername(self.handle.as_ptr()) }; assert!(!value.is_null()); - unsafe { BnString::from_raw(value) } + unsafe { BnString::into_string(value) } } /// Set user's username. You will need to push the user to update the Remote - pub fn set_username(&self, username: U) -> Result<(), ()> { - let username = username.into_bytes_with_nul(); - let result = unsafe { - BNCollaborationUserSetUsername( - self.handle.as_ptr(), - username.as_ref().as_ptr() as *const c_char, - ) - }; + pub fn set_username(&self, username: &str) -> Result<(), ()> { + let username = username.to_cstr(); + let result = + unsafe { BNCollaborationUserSetUsername(self.handle.as_ptr(), username.as_ptr()) }; if result { Ok(()) } else { @@ -65,21 +60,17 @@ impl RemoteUser { } /// User's email address - pub fn email(&self) -> BnString { + pub fn email(&self) -> String { let value = unsafe { BNCollaborationUserGetEmail(self.handle.as_ptr()) }; assert!(!value.is_null()); - unsafe { BnString::from_raw(value) } + unsafe { BnString::into_string(value) } } /// Set user's email. You will need to push the user to update the Remote - pub fn set_email(&self, email: U) -> Result<(), ()> { - let username = email.into_bytes_with_nul(); - let result = unsafe { - BNCollaborationUserSetEmail( - self.handle.as_ptr(), - username.as_ref().as_ptr() as *const c_char, - ) - }; + pub fn set_email(&self, email: &str) -> Result<(), ()> { + let username = email.to_cstr(); + let result = + unsafe { BNCollaborationUserSetEmail(self.handle.as_ptr(), username.as_ptr()) }; if result { Ok(()) } else { @@ -88,10 +79,10 @@ impl RemoteUser { } /// String representing the last date the user logged in - pub fn last_login(&self) -> BnString { + pub fn last_login(&self) -> String { let value = unsafe { BNCollaborationUserGetLastLogin(self.handle.as_ptr()) }; assert!(!value.is_null()); - unsafe { BnString::from_raw(value) } + unsafe { BnString::into_string(value) } } /// If the user account is active and can log in diff --git a/rust/src/command.rs b/rust/src/command.rs index 48a911645b..46f43bbd51 100644 --- a/rust/src/command.rs +++ b/rust/src/command.rs @@ -42,7 +42,7 @@ use std::os::raw::c_void; use crate::binary_view::BinaryView; use crate::function::Function; -use crate::string::BnStrCompatible; +use crate::string::IntoCStr; /// The trait required for generic commands. See [register_command] for example usage. pub trait Command: 'static + Sync { @@ -93,11 +93,7 @@ where /// true /// } /// ``` -pub fn register_command(name: S, desc: S, command: C) -where - S: BnStrCompatible, - C: Command, -{ +pub fn register_command(name: &str, desc: &str, command: C) { extern "C" fn cb_action(ctxt: *mut c_void, view: *mut BNBinaryView) where C: Command, @@ -126,11 +122,11 @@ where }) } - let name = name.into_bytes_with_nul(); - let desc = desc.into_bytes_with_nul(); + let name = name.to_cstr(); + let desc = desc.to_cstr(); - let name_ptr = name.as_ref().as_ptr() as *mut _; - let desc_ptr = desc.as_ref().as_ptr() as *mut _; + let name_ptr = name.as_ptr(); + let desc_ptr = desc.as_ptr(); let ctxt = Box::into_raw(Box::new(command)); @@ -194,11 +190,7 @@ where /// true /// } /// ``` -pub fn register_command_for_address(name: S, desc: S, command: C) -where - S: BnStrCompatible, - C: AddressCommand, -{ +pub fn register_command_for_address(name: &str, desc: &str, command: C) { extern "C" fn cb_action(ctxt: *mut c_void, view: *mut BNBinaryView, addr: u64) where C: AddressCommand, @@ -227,11 +219,11 @@ where }) } - let name = name.into_bytes_with_nul(); - let desc = desc.into_bytes_with_nul(); + let name = name.to_cstr(); + let desc = desc.to_cstr(); - let name_ptr = name.as_ref().as_ptr() as *mut _; - let desc_ptr = desc.as_ref().as_ptr() as *mut _; + let name_ptr = name.as_ptr(); + let desc_ptr = desc.as_ptr(); let ctxt = Box::into_raw(Box::new(command)); @@ -296,9 +288,8 @@ where /// true /// } /// ``` -pub fn register_command_for_range(name: S, desc: S, command: C) +pub fn register_command_for_range(name: &str, desc: &str, command: C) where - S: BnStrCompatible, C: RangeCommand, { extern "C" fn cb_action(ctxt: *mut c_void, view: *mut BNBinaryView, addr: u64, len: u64) @@ -334,11 +325,11 @@ where }) } - let name = name.into_bytes_with_nul(); - let desc = desc.into_bytes_with_nul(); + let name = name.to_cstr(); + let desc = desc.to_cstr(); - let name_ptr = name.as_ref().as_ptr() as *mut _; - let desc_ptr = desc.as_ref().as_ptr() as *mut _; + let name_ptr = name.as_ptr(); + let desc_ptr = desc.as_ptr(); let ctxt = Box::into_raw(Box::new(command)); @@ -403,11 +394,7 @@ where /// true /// } /// ``` -pub fn register_command_for_function(name: S, desc: S, command: C) -where - S: BnStrCompatible, - C: FunctionCommand, -{ +pub fn register_command_for_function(name: &str, desc: &str, command: C) { extern "C" fn cb_action(ctxt: *mut c_void, view: *mut BNBinaryView, func: *mut BNFunction) where C: FunctionCommand, @@ -446,11 +433,11 @@ where }) } - let name = name.into_bytes_with_nul(); - let desc = desc.into_bytes_with_nul(); + let name = name.to_cstr(); + let desc = desc.to_cstr(); - let name_ptr = name.as_ref().as_ptr() as *mut _; - let desc_ptr = desc.as_ref().as_ptr() as *mut _; + let name_ptr = name.as_ptr(); + let desc_ptr = desc.as_ptr(); let ctxt = Box::into_raw(Box::new(command)); diff --git a/rust/src/component.rs b/rust/src/component.rs index 497c1f5723..4c5d5992d2 100644 --- a/rust/src/component.rs +++ b/rust/src/component.rs @@ -1,7 +1,7 @@ use crate::binary_view::{BinaryView, BinaryViewExt}; use crate::function::Function; use crate::rc::{Array, CoreArrayProvider, CoreArrayProviderInner, Guard, Ref, RefCountable}; -use crate::string::{BnStrCompatible, BnString}; +use crate::string::{BnString, IntoCStr}; use crate::types::ComponentReferencedType; use std::ffi::c_char; use std::fmt::Debug; @@ -39,20 +39,20 @@ impl ComponentBuilder { let result = match (&self.parent, &self.name) { (None, None) => unsafe { BNCreateComponent(self.view.handle) }, (None, Some(name)) => { - let name_raw = name.into_bytes_with_nul(); + let name_raw = name.to_cstr(); unsafe { BNCreateComponentWithName(self.view.handle, name_raw.as_ptr() as *mut c_char) } } (Some(guid), None) => { - let guid_raw = guid.into_bytes_with_nul(); + let guid_raw = guid.to_cstr(); unsafe { BNCreateComponentWithParent(self.view.handle, guid_raw.as_ptr() as *mut c_char) } } (Some(guid), Some(name)) => { - let guid_raw = guid.into_bytes_with_nul(); - let name_raw = name.into_bytes_with_nul(); + let guid_raw = guid.to_cstr(); + let name_raw = name.to_cstr(); unsafe { BNCreateComponentWithParentAndName( self.view.handle, @@ -86,10 +86,10 @@ impl Component { Ref::new(Self { handle }) } - pub fn guid(&self) -> BnString { + pub fn guid(&self) -> String { let result = unsafe { BNComponentGetGuid(self.handle.as_ptr()) }; assert!(!result.is_null()); - unsafe { BnString::from_raw(result) } + unsafe { BnString::into_string(result) } } /// Add function to this component. @@ -145,10 +145,10 @@ impl Component { } /// Original name of the component - pub fn display_name(&self) -> BnString { + pub fn display_name(&self) -> String { let result = unsafe { BNComponentGetDisplayName(self.handle.as_ptr()) }; assert!(!result.is_null()); - unsafe { BnString::from_raw(result) } + unsafe { BnString::into_string(result) } } /// Original name set for this component @@ -158,20 +158,15 @@ impl Component { /// 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 { + pub fn name(&self) -> String { let result = unsafe { BNComponentGetOriginalName(self.handle.as_ptr()) }; assert!(!result.is_null()); - unsafe { BnString::from_raw(result) } + unsafe { BnString::into_string(result) } } - pub fn set_name(&self, name: S) { - let name = name.into_bytes_with_nul(); - unsafe { - BNComponentSetName( - self.handle.as_ptr(), - name.as_ref().as_ptr() as *const c_char, - ) - } + pub fn set_name(&self, name: &str) { + let name = name.to_cstr(); + unsafe { BNComponentSetName(self.handle.as_ptr(), name.as_ptr()) } } /// The component that contains this component, if it exists. @@ -309,20 +304,3 @@ unsafe impl CoreArrayProviderInner for Component { Guard::new(Self::from_raw(raw_ptr), context) } } - -// TODO: Should we keep this? -pub trait IntoComponentGuid { - fn component_guid(self) -> BnString; -} - -impl IntoComponentGuid for &Component { - fn component_guid(self) -> BnString { - self.guid() - } -} - -impl IntoComponentGuid for S { - fn component_guid(self) -> BnString { - BnString::new(self) - } -} diff --git a/rust/src/custom_binary_view.rs b/rust/src/custom_binary_view.rs index a43bbd4b57..a0b1df2142 100644 --- a/rust/src/custom_binary_view.rs +++ b/rust/src/custom_binary_view.rs @@ -39,9 +39,8 @@ use crate::Endianness; /// the core. The `BinaryViewType` argument passed to `constructor` is the object that the /// `AsRef` /// implementation of the `CustomBinaryViewType` must return. -pub fn register_view_type(name: S, long_name: S, constructor: F) -> &'static T +pub fn register_view_type(name: &str, long_name: &str, constructor: F) -> &'static T where - S: BnStrCompatible, T: CustomBinaryViewType, F: FnOnce(BinaryViewType) -> T, { @@ -148,11 +147,11 @@ where }) } - let name = name.into_bytes_with_nul(); - let name_ptr = name.as_ref().as_ptr() as *mut _; + let name = name.to_cstr(); + let name_ptr = name.as_ptr(); - let long_name = long_name.into_bytes_with_nul(); - let long_name_ptr = long_name.as_ref().as_ptr() as *mut _; + let long_name = long_name.to_cstr(); + let long_name_ptr = long_name.as_ptr(); let ctxt = Box::leak(Box::new(MaybeUninit::zeroed())); @@ -201,7 +200,7 @@ pub trait BinaryViewTypeBase: AsRef { if settings_handle.is_null() { None } else { - unsafe { Some(Settings::from_raw(settings_handle)) } + unsafe { Some(Settings::ref_from_raw(settings_handle)) } } } @@ -211,12 +210,12 @@ pub trait BinaryViewTypeBase: AsRef { } pub trait BinaryViewTypeExt: BinaryViewTypeBase { - fn name(&self) -> BnString { - unsafe { BnString::from_raw(BNGetBinaryViewTypeName(self.as_ref().handle)) } + fn name(&self) -> String { + unsafe { BnString::into_string(BNGetBinaryViewTypeName(self.as_ref().handle)) } } - fn long_name(&self) -> BnString { - unsafe { BnString::from_raw(BNGetBinaryViewTypeLongName(self.as_ref().handle)) } + fn long_name(&self) -> String { + unsafe { BnString::into_string(BNGetBinaryViewTypeLongName(self.as_ref().handle)) } } fn register_arch(&self, id: u32, endianness: Endianness, arch: &A) { @@ -359,8 +358,8 @@ impl BinaryViewType { } /// Looks up a BinaryViewType by its short name - pub fn by_name(name: N) -> Result { - let bytes = name.into_bytes_with_nul(); + pub fn by_name(name: &str) -> Result { + let bytes = name.to_cstr(); let handle = unsafe { BNGetBinaryViewTypeByName(bytes.as_ref().as_ptr() as *const _) }; match handle.is_null() { false => Ok(unsafe { BinaryViewType::from_raw(handle) }), @@ -389,7 +388,7 @@ impl BinaryViewTypeBase for BinaryViewType { if settings_handle.is_null() { None } else { - unsafe { Some(Settings::from_raw(settings_handle)) } + unsafe { Some(Settings::ref_from_raw(settings_handle)) } } } } @@ -502,7 +501,7 @@ impl<'a, T: CustomBinaryViewType> CustomViewBuilder<'a, T> { let view_name = view_type.name(); - if let Some(bv) = file.view_of_type(view_name.as_str()) { + if let Some(bv) = file.view_of_type(&view_name) { // 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 @@ -514,7 +513,7 @@ impl<'a, T: CustomBinaryViewType> CustomViewBuilder<'a, T> { // going to try and stop this from happening in the first place. log::error!( "attempt to create duplicate view of type '{}' (existing: {:?})", - view_name.as_str(), + view_name, bv.handle ); @@ -877,6 +876,7 @@ impl<'a, T: CustomBinaryViewType> CustomViewBuilder<'a, T> { save: Some(cb_save::), }; + let view_name = view_name.to_cstr(); unsafe { let res = BNCreateCustomBinaryView( view_name.as_ptr(), diff --git a/rust/src/data_buffer.rs b/rust/src/data_buffer.rs index 5b7d1ae61a..a3f51d54ae 100644 --- a/rust/src/data_buffer.rs +++ b/rust/src/data_buffer.rs @@ -19,7 +19,7 @@ use binaryninjacore_sys::*; use std::ffi::c_void; use std::slice; -use crate::string::BnString; +use crate::string::{BnString, IntoCStr}; pub struct DataBuffer(*mut BNDataBuffer); @@ -110,9 +110,9 @@ impl DataBuffer { } } - pub fn to_escaped_string(&self, null_terminates: bool, escape_printable: bool) -> BnString { + pub fn to_escaped_string(&self, null_terminates: bool, escape_printable: bool) -> String { unsafe { - BnString::from_raw(BNDataBufferToEscapedString( + BnString::into_string(BNDataBufferToEscapedString( self.0, null_terminates, escape_printable, @@ -120,16 +120,18 @@ impl DataBuffer { } } - pub fn from_escaped_string(value: &BnString) -> Self { + pub fn from_escaped_string(value: &str) -> Self { + let value = value.to_cstr(); Self(unsafe { BNDecodeEscapedString(value.as_ptr()) }) } - pub fn to_base64(&self) -> BnString { - unsafe { BnString::from_raw(BNDataBufferToBase64(self.0)) } + pub fn to_base64(&self) -> String { + unsafe { BnString::into_string(BNDataBufferToBase64(self.0)) } } - pub fn from_base64(value: &BnString) -> Self { - Self(unsafe { BNDecodeBase64(value.as_ptr()) }) + pub fn from_base64(value: &str) -> Self { + let t = value.to_cstr(); + Self(unsafe { BNDecodeBase64(t.as_ptr()) }) } pub fn zlib_compress(&self) -> Self { diff --git a/rust/src/database.rs b/rust/src/database.rs index 7174ebe8ff..4f2b3f6a8e 100644 --- a/rust/src/database.rs +++ b/rust/src/database.rs @@ -4,7 +4,7 @@ pub mod undo; use binaryninjacore_sys::*; use std::collections::HashMap; -use std::ffi::{c_char, c_void}; +use std::ffi::c_void; use std::fmt::Debug; use std::ptr::NonNull; @@ -15,7 +15,7 @@ use crate::database::snapshot::{Snapshot, SnapshotId}; use crate::file_metadata::FileMetadata; use crate::progress::{NoProgressCallback, ProgressCallback}; use crate::rc::{Array, Ref, RefCountable}; -use crate::string::{BnStrCompatible, BnString}; +use crate::string::{BnString, IntoCStr}; pub struct Database { pub(crate) handle: NonNull, @@ -62,11 +62,11 @@ impl Database { unsafe { BNSetDatabaseCurrentSnapshot(self.handle.as_ptr(), id.0) } } - pub fn write_snapshot_data( + pub fn write_snapshot_data( &self, parents: &[SnapshotId], file: &BinaryView, - name: N, + name: &str, data: &KeyValueStore, auto_save: bool, ) -> SnapshotId { @@ -80,21 +80,20 @@ impl Database { ) } - pub fn write_snapshot_data_with_progress( + pub fn write_snapshot_data_with_progress

( &self, parents: &[SnapshotId], file: &BinaryView, - name: N, + name: &str, data: &KeyValueStore, auto_save: bool, mut progress: P, ) -> SnapshotId where - N: BnStrCompatible, P: ProgressCallback, { - let name_raw = name.into_bytes_with_nul(); - let name_ptr = name_raw.as_ref().as_ptr() as *const c_char; + let name_raw = name.to_cstr(); + let name_ptr = name_raw.as_ptr(); let new_id = unsafe { BNWriteDatabaseSnapshotData( @@ -133,10 +132,9 @@ impl Database { Err(()) } } - pub fn has_global(&self, key: S) -> bool { - let key_raw = key.into_bytes_with_nul(); - let key_ptr = key_raw.as_ref().as_ptr() as *const c_char; - unsafe { BNDatabaseHasGlobal(self.handle.as_ptr(), key_ptr) != 0 } + pub fn has_global(&self, key: &str) -> bool { + let key_raw = key.to_cstr(); + unsafe { BNDatabaseHasGlobal(self.handle.as_ptr(), key_raw.as_ptr()) != 0 } } /// Get a list of keys for all globals in the database @@ -148,43 +146,38 @@ impl Database { } /// Get a dictionary of all globals - pub fn globals(&self) -> HashMap { + pub fn globals(&self) -> HashMap { self.global_keys() .iter() - .filter_map(|key| Some((key.to_string(), self.read_global(key)?.to_string()))) + .filter_map(|key| Some((key.to_string(), self.read_global(key)?))) .collect() } /// Get a specific global by key - pub fn read_global(&self, key: S) -> Option { - let key_raw = key.into_bytes_with_nul(); - let key_ptr = key_raw.as_ref().as_ptr() as *const c_char; - let result = unsafe { BNReadDatabaseGlobal(self.handle.as_ptr(), key_ptr) }; + pub fn read_global(&self, key: &str) -> Option { + let key_raw = key.to_cstr(); + let result = unsafe { BNReadDatabaseGlobal(self.handle.as_ptr(), key_raw.as_ptr()) }; unsafe { NonNull::new(result).map(|_| BnString::from_raw(result)) } } /// Write a global into the database - pub fn write_global(&self, key: K, value: V) -> bool { - let key_raw = key.into_bytes_with_nul(); - let key_ptr = key_raw.as_ref().as_ptr() as *const c_char; - let value_raw = value.into_bytes_with_nul(); - let value_ptr = value_raw.as_ref().as_ptr() as *const c_char; - unsafe { BNWriteDatabaseGlobal(self.handle.as_ptr(), key_ptr, value_ptr) } + pub fn write_global(&self, key: &str, value: &str) -> bool { + let key_raw = key.to_cstr(); + let value_raw = value.to_cstr(); + unsafe { BNWriteDatabaseGlobal(self.handle.as_ptr(), key_raw.as_ptr(), value_raw.as_ptr()) } } /// Get a specific global by key, as a binary buffer - pub fn read_global_data(&self, key: S) -> Option { - let key_raw = key.into_bytes_with_nul(); - let key_ptr = key_raw.as_ref().as_ptr() as *const c_char; - let result = unsafe { BNReadDatabaseGlobalData(self.handle.as_ptr(), key_ptr) }; + pub fn read_global_data(&self, key: &str) -> Option { + let key_raw = key.to_cstr(); + let result = unsafe { BNReadDatabaseGlobalData(self.handle.as_ptr(), key_raw.as_ptr()) }; NonNull::new(result).map(|_| DataBuffer::from_raw(result)) } /// Write a binary buffer into a global in the database - pub fn write_global_data(&self, key: K, value: &DataBuffer) -> bool { - let key_raw = key.into_bytes_with_nul(); - let key_ptr = key_raw.as_ref().as_ptr() as *const c_char; - unsafe { BNWriteDatabaseGlobalData(self.handle.as_ptr(), key_ptr, value.as_raw()) } + pub fn write_global_data(&self, key: &str, value: &DataBuffer) -> bool { + let key_raw = key.to_cstr(); + unsafe { BNWriteDatabaseGlobalData(self.handle.as_ptr(), key_raw.as_ptr(), value.as_raw()) } } /// Get the owning FileMetadata diff --git a/rust/src/database/kvs.rs b/rust/src/database/kvs.rs index 4b77bbdbf8..13b1796218 100644 --- a/rust/src/database/kvs.rs +++ b/rust/src/database/kvs.rs @@ -1,6 +1,6 @@ use crate::data_buffer::DataBuffer; use crate::rc::{Array, Ref, RefCountable}; -use crate::string::{BnStrCompatible, BnString}; +use crate::string::{BnString, IntoCStr}; use binaryninjacore_sys::{ BNBeginKeyValueStoreNamespace, BNEndKeyValueStoreNamespace, BNFreeKeyValueStore, BNGetKeyValueStoreBuffer, BNGetKeyValueStoreDataSize, BNGetKeyValueStoreKeys, @@ -9,7 +9,6 @@ use binaryninjacore_sys::{ BNNewKeyValueStoreReference, BNSetKeyValueStoreBuffer, }; use std::collections::HashMap; -use std::ffi::c_char; use std::fmt::Debug; use std::ptr::NonNull; @@ -42,17 +41,17 @@ impl KeyValueStore { } /// Get the value for a single key - pub fn value(&self, key: S) -> Option { - let key_raw = key.into_bytes_with_nul(); - let key_ptr = key_raw.as_ref().as_ptr() as *const c_char; + pub fn value(&self, key: &str) -> Option { + let key_raw = key.to_cstr(); + let key_ptr = key_raw.as_ptr(); let result = unsafe { BNGetKeyValueStoreBuffer(self.handle.as_ptr(), key_ptr) }; NonNull::new(result).map(|_| DataBuffer::from_raw(result)) } /// Set the value for a single key - pub fn set_value(&self, key: S, value: &DataBuffer) -> bool { - let key_raw = key.into_bytes_with_nul(); - let key_ptr = key_raw.as_ref().as_ptr() as *const c_char; + pub fn set_value(&self, key: &str, value: &DataBuffer) -> bool { + let key_raw = key.to_cstr(); + let key_ptr = key_raw.as_ptr(); unsafe { BNSetKeyValueStoreBuffer(self.handle.as_ptr(), key_ptr, value.as_raw()) } } @@ -64,9 +63,9 @@ impl KeyValueStore { } /// Begin storing new keys into a namespace - pub fn begin_namespace(&self, name: S) { - let name_raw = name.into_bytes_with_nul(); - let name_ptr = name_raw.as_ref().as_ptr() as *const c_char; + pub fn begin_namespace(&self, name: &str) { + let name_raw = name.to_cstr(); + let name_ptr = name_raw.as_ptr(); unsafe { BNBeginKeyValueStoreNamespace(self.handle.as_ptr(), name_ptr) } } diff --git a/rust/src/database/snapshot.rs b/rust/src/database/snapshot.rs index 35b0dc46d8..85c57f4ea0 100644 --- a/rust/src/database/snapshot.rs +++ b/rust/src/database/snapshot.rs @@ -4,7 +4,7 @@ use crate::database::undo::UndoEntry; use crate::database::Database; use crate::progress::ProgressCallback; use crate::rc::{Array, CoreArrayProvider, CoreArrayProviderInner, Guard, Ref, RefCountable}; -use crate::string::{BnStrCompatible, BnString}; +use crate::string::{BnString, IntoCStr}; use binaryninjacore_sys::{ BNCollaborationFreeSnapshotIdList, BNFreeSnapshot, BNFreeSnapshotList, BNGetSnapshotChildren, BNGetSnapshotDatabase, BNGetSnapshotFileContents, BNGetSnapshotFileContentsHash, @@ -14,7 +14,7 @@ use binaryninjacore_sys::{ BNReadSnapshotDataWithProgress, BNSetSnapshotName, BNSnapshot, BNSnapshotHasAncestor, BNSnapshotHasContents, BNSnapshotHasUndo, BNSnapshotStoreData, }; -use std::ffi::{c_char, c_void}; +use std::ffi::c_void; use std::fmt; use std::fmt::{Debug, Display, Formatter}; use std::ptr::NonNull; @@ -45,14 +45,14 @@ impl Snapshot { } /// Get the displayed snapshot name - pub fn name(&self) -> BnString { - unsafe { BnString::from_raw(BNGetSnapshotName(self.handle.as_ptr())) } + pub fn name(&self) -> String { + unsafe { BnString::into_string(BNGetSnapshotName(self.handle.as_ptr())) } } /// Set the displayed snapshot name - pub fn set_name(&self, value: S) { - let value_raw = value.into_bytes_with_nul(); - let value_ptr = value_raw.as_ref().as_ptr() as *const c_char; + pub fn set_name(&self, value: &str) { + let value_raw = value.to_cstr(); + let value_ptr = value_raw.as_ptr(); unsafe { BNSetSnapshotName(self.handle.as_ptr(), value_ptr) } } diff --git a/rust/src/database/undo.rs b/rust/src/database/undo.rs index 727a777ba5..a2001b7218 100644 --- a/rust/src/database/undo.rs +++ b/rust/src/database/undo.rs @@ -26,10 +26,10 @@ impl UndoEntry { Ref::new(Self { handle }) } - pub fn id(&self) -> BnString { + pub fn id(&self) -> String { let result = unsafe { BNUndoEntryGetId(self.handle.as_ptr()) }; assert!(!result.is_null()); - unsafe { BnString::from_raw(result) } + unsafe { BnString::into_string(result) } } pub fn actions(&self) -> Array { @@ -115,10 +115,10 @@ impl UndoAction { } /// Gets the [`UndoAction`] summary as text rather than [`InstructionTextToken`]'s. - pub fn summary_as_string(&self) -> BnString { + pub fn summary_as_string(&self) -> String { let result = unsafe { BNUndoActionGetSummaryText(self.handle.as_ptr()) }; assert!(!result.is_null()); - unsafe { BnString::from_raw(result) } + unsafe { BnString::into_string(result) } } } diff --git a/rust/src/debuginfo.rs b/rust/src/debuginfo.rs index 1353f8b177..a4d0b13970 100644 --- a/rust/src/debuginfo.rs +++ b/rust/src/debuginfo.rs @@ -83,7 +83,7 @@ use crate::{ binary_view::BinaryView, platform::Platform, rc::*, - string::{raw_to_string, BnStrCompatible, BnString}, + string::{raw_to_string, BnString, IntoCStr}, types::{NameAndType, Type}, }; @@ -115,9 +115,9 @@ impl DebugInfoParser { } /// Returns debug info parser of the given name, if it exists - pub fn from_name(name: S) -> Result, ()> { - let name = name.into_bytes_with_nul(); - let parser = unsafe { BNGetDebugInfoParserByName(name.as_ref().as_ptr() as *mut _) }; + pub fn from_name(name: &str) -> Result, ()> { + let name = name.to_cstr(); + let parser = unsafe { BNGetDebugInfoParserByName(name.as_ptr()) }; if parser.is_null() { Err(()) @@ -141,8 +141,8 @@ impl DebugInfoParser { } /// Returns the name of the current parser - pub fn name(&self) -> BnString { - unsafe { BnString::from_raw(BNGetDebugInfoParserName(self.handle)) } + pub fn name(&self) -> String { + unsafe { BnString::into_string(BNGetDebugInfoParserName(self.handle)) } } /// Returns whether this debug-info parser is valid for the provided binary view @@ -207,9 +207,8 @@ impl DebugInfoParser { } // Registers a DebugInfoParser. See `binaryninja::debuginfo::DebugInfoParser` for more details. - pub fn register(name: S, parser_callbacks: C) -> Ref + pub fn register(name: &str, parser_callbacks: C) -> Ref where - S: BnStrCompatible, C: CustomDebugInfoParser, { extern "C" fn cb_is_valid(ctxt: *mut c_void, view: *mut BNBinaryView) -> bool @@ -259,8 +258,8 @@ impl DebugInfoParser { }) } - let name = name.into_bytes_with_nul(); - let name_ptr = name.as_ref().as_ptr() as *mut _; + let name = name.to_cstr(); + let name_ptr = name.as_ptr(); let ctxt = Box::into_raw(Box::new(parser_callbacks)); unsafe { @@ -319,6 +318,7 @@ unsafe impl CoreArrayProviderInner for DebugInfoParser { /// /// Functions will not be created if an address is not provided, but will be able to be queried from debug info for later user analysis. pub struct DebugFunctionInfo { + // TODO: These need to be BnString if we want to support invalid UTF-8 short_name: Option, full_name: Option, raw_name: Option, @@ -417,17 +417,12 @@ impl DebugInfo { } /// 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(); + pub fn types_by_name(&self, parser_name: &str) -> Vec { + let parser_name = parser_name.to_cstr(); let mut count: usize = 0; - let debug_types_ptr = unsafe { - BNGetDebugTypes( - self.handle, - parser_name.as_ref().as_ptr() as *mut _, - &mut count, - ) - }; + let debug_types_ptr = + unsafe { BNGetDebugTypes(self.handle, parser_name.as_ptr(), &mut count) }; let result: Vec<_> = unsafe { std::slice::from_raw_parts_mut(debug_types_ptr, count) .iter() @@ -455,17 +450,12 @@ impl DebugInfo { } /// Returns all functions within the parser - pub fn functions_by_name(&self, parser_name: S) -> Vec { - let parser_name = parser_name.into_bytes_with_nul(); + pub fn functions_by_name(&self, parser_name: &str) -> Vec { + let parser_name = parser_name.to_cstr(); let mut count: usize = 0; - let functions_ptr = unsafe { - BNGetDebugFunctions( - self.handle, - parser_name.as_ref().as_ptr() as *mut _, - &mut count, - ) - }; + let functions_ptr = + unsafe { BNGetDebugFunctions(self.handle, parser_name.as_ptr(), &mut count) }; let result: Vec = unsafe { std::slice::from_raw_parts_mut(functions_ptr, count) @@ -495,20 +485,12 @@ impl DebugInfo { } /// Returns all data variables within the parser - pub fn data_variables_by_name( - &self, - parser_name: S, - ) -> Vec { - let parser_name = parser_name.into_bytes_with_nul(); + pub fn data_variables_by_name(&self, parser_name: &str) -> Vec { + let parser_name = parser_name.to_cstr(); let mut count: usize = 0; - let data_variables_ptr = unsafe { - BNGetDebugDataVariables( - self.handle, - parser_name.as_ref().as_ptr() as *mut _, - &mut count, - ) - }; + let data_variables_ptr = + unsafe { BNGetDebugDataVariables(self.handle, parser_name.as_ptr(), &mut count) }; let result: Vec = unsafe { std::slice::from_raw_parts_mut(data_variables_ptr, count) @@ -537,17 +519,12 @@ impl DebugInfo { 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(); + pub fn type_by_name(&self, parser_name: &str, name: &str) -> Option> { + let parser_name = parser_name.to_cstr(); + let name = name.to_cstr(); - let result = unsafe { - BNGetDebugTypeByName( - self.handle, - parser_name.as_ref().as_ptr() as *mut _, - name.as_ref().as_ptr() as *mut _, - ) - }; + let result = + unsafe { BNGetDebugTypeByName(self.handle, parser_name.as_ptr(), name.as_ptr()) }; if !result.is_null() { Some(unsafe { Type::ref_from_raw(result) }) } else { @@ -555,19 +532,19 @@ impl DebugInfo { } } - pub fn get_data_variable_by_name( + pub fn get_data_variable_by_name( &self, - parser_name: S, - name: S, + parser_name: &str, + name: &str, ) -> Option { - let parser_name = parser_name.into_bytes_with_nul(); - let name = name.into_bytes_with_nul(); + let parser_name = parser_name.to_cstr(); + let name = name.to_cstr(); let mut dv = BNDataVariableAndName::default(); unsafe { if BNGetDebugDataVariableByName( self.handle, - parser_name.as_ref().as_ptr() as *mut _, - name.as_ref().as_ptr() as *mut _, + parser_name.as_ptr(), + name.as_ptr(), &mut dv, ) { Some(NamedDataVariableWithType::from_owned_raw(dv)) @@ -577,20 +554,16 @@ impl DebugInfo { } } - pub fn get_data_variable_by_address( + pub fn get_data_variable_by_address( &self, - parser_name: S, + parser_name: &str, address: u64, ) -> Option { - let parser_name = parser_name.into_bytes_with_nul(); + let parser_name = parser_name.to_cstr(); let mut dv = BNDataVariableAndName::default(); unsafe { - if BNGetDebugDataVariableByAddress( - self.handle, - parser_name.as_ref().as_ptr() as *mut _, - address, - &mut dv, - ) { + if BNGetDebugDataVariableByAddress(self.handle, parser_name.as_ptr(), address, &mut dv) + { Some(NamedDataVariableWithType::from_owned_raw(dv)) } else { None @@ -599,12 +572,11 @@ impl DebugInfo { } /// 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 { + pub fn get_types_by_name(&self, name: &str) -> Vec { let mut count: usize = 0; - 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 name = name.to_cstr(); + let raw_names_and_types_ptr = + unsafe { BNGetDebugTypesByName(self.handle, name.as_ptr(), &mut count) }; let raw_names_and_types: &[BNNameAndType] = unsafe { std::slice::from_raw_parts(raw_names_and_types_ptr, count) }; @@ -619,16 +591,12 @@ impl DebugInfo { } // The tuple is (DebugInfoParserName, address, type) - pub fn get_data_variables_by_name( - &self, - name: S, - ) -> Vec<(String, u64, Ref)> { - let name = name.into_bytes_with_nul(); + pub fn get_data_variables_by_name(&self, name: &str) -> Vec<(String, u64, Ref)> { + let name = name.to_cstr(); let mut count: usize = 0; - let raw_variables_and_names = unsafe { - BNGetDebugDataVariablesByName(self.handle, name.as_ref().as_ptr() as *mut _, &mut count) - }; + let raw_variables_and_names = + unsafe { BNGetDebugDataVariablesByName(self.handle, name.as_ptr(), &mut count) }; let variables_and_names: &[*mut BNDataVariableAndName] = unsafe { std::slice::from_raw_parts(raw_variables_and_names as *mut _, count) }; @@ -674,94 +642,59 @@ impl DebugInfo { result } - pub fn remove_parser_info(&self, parser_name: S) -> bool { - let parser_name = parser_name.into_bytes_with_nul(); + pub fn remove_parser_info(&self, parser_name: &str) -> bool { + let parser_name = parser_name.to_cstr(); - unsafe { BNRemoveDebugParserInfo(self.handle, parser_name.as_ref().as_ptr() as *mut _) } + unsafe { BNRemoveDebugParserInfo(self.handle, parser_name.as_ptr()) } } - pub fn remove_parser_types(&self, parser_name: S) -> bool { - let parser_name = parser_name.into_bytes_with_nul(); + pub fn remove_parser_types(&self, parser_name: &str) -> bool { + let parser_name = parser_name.to_cstr(); - unsafe { BNRemoveDebugParserTypes(self.handle, parser_name.as_ref().as_ptr() as *mut _) } + unsafe { BNRemoveDebugParserTypes(self.handle, parser_name.as_ptr()) } } - pub fn remove_parser_functions(&self, parser_name: S) -> bool { - let parser_name = parser_name.into_bytes_with_nul(); + pub fn remove_parser_functions(&self, parser_name: &str) -> bool { + let parser_name = parser_name.to_cstr(); - unsafe { - BNRemoveDebugParserFunctions(self.handle, parser_name.as_ref().as_ptr() as *mut _) - } + unsafe { BNRemoveDebugParserFunctions(self.handle, parser_name.as_ptr()) } } - pub fn remove_parser_data_variables(&self, parser_name: S) -> bool { - let parser_name = parser_name.into_bytes_with_nul(); + pub fn remove_parser_data_variables(&self, parser_name: &str) -> bool { + let parser_name = parser_name.to_cstr(); - unsafe { - BNRemoveDebugParserDataVariables(self.handle, parser_name.as_ref().as_ptr() as *mut _) - } + unsafe { BNRemoveDebugParserDataVariables(self.handle, parser_name.as_ptr()) } } - pub fn remove_type_by_name(&self, parser_name: S, name: S) -> bool { - let parser_name = parser_name.into_bytes_with_nul(); - let name = name.into_bytes_with_nul(); + pub fn remove_type_by_name(&self, parser_name: &str, name: &str) -> bool { + let parser_name = parser_name.to_cstr(); + let name = name.to_cstr(); - unsafe { - BNRemoveDebugTypeByName( - self.handle, - parser_name.as_ref().as_ptr() as *mut _, - name.as_ref().as_ptr() as *mut _, - ) - } + unsafe { BNRemoveDebugTypeByName(self.handle, parser_name.as_ptr(), name.as_ptr()) } } - pub fn remove_function_by_index( - &self, - parser_name: S, - index: usize, - ) -> bool { - let parser_name = parser_name.into_bytes_with_nul(); + pub fn remove_function_by_index(&self, parser_name: &str, index: usize) -> bool { + let parser_name = parser_name.to_cstr(); - unsafe { - BNRemoveDebugFunctionByIndex( - self.handle, - parser_name.as_ref().as_ptr() as *mut _, - index, - ) - } + unsafe { BNRemoveDebugFunctionByIndex(self.handle, parser_name.as_ptr(), index) } } - pub fn remove_data_variable_by_address( - &self, - parser_name: S, - address: u64, - ) -> bool { - let parser_name = parser_name.into_bytes_with_nul(); + pub fn remove_data_variable_by_address(&self, parser_name: &str, address: u64) -> bool { + let parser_name = parser_name.to_cstr(); - unsafe { - BNRemoveDebugDataVariableByAddress( - self.handle, - parser_name.as_ref().as_ptr() as *mut _, - address, - ) - } + unsafe { BNRemoveDebugDataVariableByAddress(self.handle, parser_name.as_ptr(), address) } } /// Adds a type scoped under the current parser's name to the debug info - pub fn add_type( - &self, - name: S, - new_type: &Type, - components: &[&str], - ) -> bool { + pub fn add_type(&self, name: &str, new_type: &Type, components: &[&str]) -> 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(); + let name = name.to_cstr(); unsafe { BNAddDebugType( self.handle, - name.as_ref().as_ptr() as *mut _, + name.as_ptr(), new_type.handle, raw_components.as_ptr() as *mut _, components.len(), @@ -771,24 +704,15 @@ impl DebugInfo { /// Adds a function scoped under the current parser's name to the debug info pub fn add_function(&self, new_func: &DebugFunctionInfo) -> bool { - let short_name_bytes = new_func - .short_name - .as_ref() - .map(|name| name.into_bytes_with_nul()); + let short_name_bytes = new_func.short_name.as_ref().map(|name| name.to_cstr()); let short_name = short_name_bytes .as_ref() .map_or(std::ptr::null_mut() as *mut _, |name| name.as_ptr() as _); - let full_name_bytes = new_func - .full_name - .as_ref() - .map(|name| name.into_bytes_with_nul()); + let full_name_bytes = new_func.full_name.as_ref().map(|name| name.to_cstr()); let full_name = full_name_bytes .as_ref() .map_or(std::ptr::null_mut() as *mut _, |name| name.as_ptr() as _); - let raw_name_bytes = new_func - .raw_name - .as_ref() - .map(|name| name.into_bytes_with_nul()); + let raw_name_bytes = new_func.raw_name.as_ref().map(|name| name.to_cstr()); let raw_name = raw_name_bytes .as_ref() .map_or(std::ptr::null_mut() as *mut _, |name| name.as_ptr() as _); @@ -801,21 +725,13 @@ impl DebugInfo { unsafe { for component in &new_func.components { - components_array.push(BNAllocString( - component.clone().into_bytes_with_nul().as_ptr() as _, - )); + let component = component.to_cstr(); + components_array.push(BNAllocString(component.as_ptr())); } 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, - }); + // NOTE: must be manually freed after call to BNAddDebugFunction is over. + local_variables_array.push(NamedVariableWithType::into_raw(local_variable.clone())); } let result = BNAddDebugFunction( @@ -841,22 +757,22 @@ impl DebugInfo { ); for i in components_array { - BNFreeString(i); + BnString::free_raw(i); } for i in &local_variables_array { - BNFreeString(i.name); + NamedVariableWithType::free_raw(*i); } result } } /// Adds a data variable scoped under the current parser's name to the debug info - pub fn add_data_variable( + pub fn add_data_variable( &self, address: u64, t: &Type, - name: Option, + name: Option<&str>, components: &[&str], ) -> bool { let mut components_array: Vec<*const ::std::os::raw::c_char> = @@ -867,13 +783,13 @@ impl DebugInfo { match name { Some(name) => { - let name = name.into_bytes_with_nul(); + let name = name.to_cstr(); unsafe { BNAddDebugDataVariable( self.handle, address, t.handle, - name.as_ref().as_ptr() as *mut _, + name.as_ptr(), components.as_ptr() as _, components.len(), ) diff --git a/rust/src/demangle.rs b/rust/src/demangle.rs index e4d0be1d94..50878bdad6 100644 --- a/rust/src/demangle.rs +++ b/rust/src/demangle.rs @@ -19,27 +19,26 @@ use std::ffi::{c_char, c_void}; use crate::architecture::CoreArchitecture; use crate::binary_view::BinaryView; -use crate::string::{raw_to_string, BnStrCompatible, BnString}; +use crate::string::{raw_to_string, BnString, IntoCStr}; use crate::types::{QualifiedName, Type}; use crate::rc::*; pub type Result = std::result::Result; -pub fn demangle_generic( +pub fn demangle_generic( arch: &CoreArchitecture, - mangled_name: S, + mangled_name: &str, view: Option<&BinaryView>, simplify: bool, ) -> Option<(QualifiedName, Option>)> { - let mangled_name_bwn = mangled_name.into_bytes_with_nul(); - let mangled_name_ptr = mangled_name_bwn.as_ref(); + let mangled_name = mangled_name.to_cstr(); let mut out_type: *mut BNType = std::ptr::null_mut(); let mut out_name = BNQualifiedName::default(); let res = unsafe { BNDemangleGeneric( arch.handle, - mangled_name_ptr.as_ptr() as *const c_char, + mangled_name.as_ptr(), &mut out_type, &mut out_name, view.map(|v| v.handle).unwrap_or(std::ptr::null_mut()), @@ -58,14 +57,13 @@ pub fn demangle_generic( } } -pub fn demangle_llvm(mangled_name: S, simplify: bool) -> Option { - let mangled_name_bwn = mangled_name.into_bytes_with_nul(); - let mangled_name_ptr = mangled_name_bwn.as_ref(); +pub fn demangle_llvm(mangled_name: &str, simplify: bool) -> Option { + let mangled_name = mangled_name.to_cstr(); let mut out_name: *mut *mut std::os::raw::c_char = std::ptr::null_mut(); let mut out_size: usize = 0; let res = unsafe { BNDemangleLLVM( - mangled_name_ptr.as_ptr() as *const c_char, + mangled_name.as_ptr(), &mut out_name, &mut out_size, simplify, @@ -87,20 +85,19 @@ pub fn demangle_llvm(mangled_name: S, simplify: bool) -> Opt } } -pub fn demangle_gnu3( +pub fn demangle_gnu3( arch: &CoreArchitecture, - mangled_name: S, + mangled_name: &str, simplify: bool, ) -> Option<(QualifiedName, Option>)> { - let mangled_name_bwn = mangled_name.into_bytes_with_nul(); - let mangled_name_ptr = mangled_name_bwn.as_ref(); + let mangled_name = mangled_name.to_cstr(); let mut out_type: *mut BNType = std::ptr::null_mut(); let mut out_name: *mut *mut std::os::raw::c_char = std::ptr::null_mut(); let mut out_size: usize = 0; let res = unsafe { BNDemangleGNU3( arch.handle, - mangled_name_ptr.as_ptr() as *const c_char, + mangled_name.as_ptr(), &mut out_type, &mut out_name, &mut out_size, @@ -128,21 +125,19 @@ pub fn demangle_gnu3( } } -pub fn demangle_ms( +pub fn demangle_ms( arch: &CoreArchitecture, - mangled_name: S, + mangled_name: &str, simplify: bool, ) -> Option<(QualifiedName, Option>)> { - let mangled_name_bwn = mangled_name.into_bytes_with_nul(); - let mangled_name_ptr = mangled_name_bwn.as_ref(); - + let mangled_name = mangled_name.to_cstr(); let mut out_type: *mut BNType = std::ptr::null_mut(); let mut out_name: *mut *mut std::os::raw::c_char = std::ptr::null_mut(); let mut out_size: usize = 0; let res = unsafe { BNDemangleMS( arch.handle, - mangled_name_ptr.as_ptr() as *const c_char, + mangled_name.as_ptr(), &mut out_type, &mut out_name, &mut out_size, @@ -187,18 +182,18 @@ impl Demangler { unsafe { Array::::new(demanglers, count, ()) } } - pub fn is_mangled_string(&self, name: S) -> bool { - let bytes = name.into_bytes_with_nul(); + pub fn is_mangled_string(&self, name: &str) -> bool { + let bytes = name.to_cstr(); unsafe { BNIsDemanglerMangledName(self.handle, bytes.as_ref().as_ptr() as *const _) } } - pub fn demangle( + pub fn demangle( &self, arch: &CoreArchitecture, - name: S, + name: &str, view: Option<&BinaryView>, ) -> Option<(QualifiedName, Option>)> { - let name_bytes = name.into_bytes_with_nul(); + let name_bytes = name.to_cstr(); let mut out_type = std::ptr::null_mut(); let mut out_var_name = BNQualifiedName::default(); @@ -232,12 +227,12 @@ impl Demangler { } } - pub fn name(&self) -> BnString { - unsafe { BnString::from_raw(BNGetDemanglerName(self.handle)) } + pub fn name(&self) -> String { + unsafe { BnString::into_string(BNGetDemanglerName(self.handle)) } } - pub fn from_name(name: S) -> Option { - let name_bytes = name.into_bytes_with_nul(); + pub fn from_name(name: &str) -> Option { + let name_bytes = name.to_cstr(); let demangler = unsafe { BNGetDemanglerByName(name_bytes.as_ref().as_ptr() as *const _) }; if demangler.is_null() { None @@ -246,11 +241,7 @@ impl Demangler { } } - pub fn register(name: S, demangler: C) -> Self - where - S: BnStrCompatible, - C: CustomDemangler, - { + pub fn register(name: &str, demangler: C) -> Self { extern "C" fn cb_is_mangled_string(ctxt: *mut c_void, name: *const c_char) -> bool where C: CustomDemangler, @@ -308,8 +299,8 @@ impl Demangler { }) } - let name = name.into_bytes_with_nul(); - let name_ptr = name.as_ref().as_ptr() as *mut _; + let name = name.to_cstr(); + let name_ptr = name.as_ptr(); let ctxt = Box::into_raw(Box::new(demangler)); let callbacks = BNDemanglerCallbacks { diff --git a/rust/src/disassembly.rs b/rust/src/disassembly.rs index 9dfd160f4b..973fd57cb3 100644 --- a/rust/src/disassembly.rs +++ b/rust/src/disassembly.rs @@ -22,7 +22,7 @@ use crate::function::{Location, NativeBlock}; use crate::high_level_il as hlil; use crate::low_level_il as llil; use crate::medium_level_il as mlil; -use crate::string::BnStrCompatible; +use crate::string::IntoCStr; use crate::string::{raw_to_string, strings_to_string_list, BnString}; use crate::rc::*; @@ -302,9 +302,7 @@ impl InstructionTextToken { } pub(crate) fn free_raw(value: BNInstructionTextToken) { - if !value.text.is_null() { - unsafe { BNFreeString(value.text) }; - } + unsafe { BnString::free_raw(value.text) }; if !value.typeNames.is_null() { unsafe { BNFreeStringList(value.typeNames, value.namesCount) }; } @@ -1022,8 +1020,8 @@ impl DisassemblyTextRenderer { unsafe { Self::ref_from_raw(NonNull::new(result).unwrap()) } } - pub fn from_llil( - func: &LowLevelILFunction, + pub fn from_llil( + func: &LowLevelILFunction, settings: Option<&DisassemblySettings>, ) -> Ref { let settings_ptr = settings.map(|s| s.handle).unwrap_or(ptr::null_mut()); @@ -1058,14 +1056,11 @@ impl DisassemblyTextRenderer { unsafe { Function::ref_from_raw(result) } } - pub fn llil( - &self, - ) -> Ref> { - let arch = self.arch(); + pub fn llil(&self) -> Ref> { let result = unsafe { BNGetDisassemblyTextRendererLowLevelILFunction(self.handle.as_ptr()) }; assert!(!result.is_null()); - unsafe { LowLevelILFunction::ref_from_raw(arch.handle(), result) } + unsafe { LowLevelILFunction::ref_from_raw(result) } } pub fn mlil(&self) -> Ref { @@ -1247,18 +1242,18 @@ impl DisassemblyTextRenderer { unsafe { Array::new(tokens, count, ()) } } - pub fn wrap_comment( + pub fn wrap_comment( &self, cur_line: DisassemblyTextLine, - comment: S1, + comment: &str, has_auto_annotations: bool, - leading_spaces: S2, - indent_spaces: S3, + leading_spaces: &str, + indent_spaces: &str, ) -> Array { let cur_line_raw = DisassemblyTextLine::into_raw(cur_line); - let comment_raw = comment.into_bytes_with_nul(); - let leading_spaces_raw = leading_spaces.into_bytes_with_nul(); - let indent_spaces_raw = indent_spaces.into_bytes_with_nul(); + let comment_raw = comment.to_cstr(); + let leading_spaces_raw = leading_spaces.to_cstr(); + let indent_spaces_raw = indent_spaces.to_cstr(); let mut count = 0; let lines = unsafe { BNDisassemblyTextRendererWrapComment( diff --git a/rust/src/download_provider.rs b/rust/src/download_provider.rs index 39136c7d2d..9fd803a7de 100644 --- a/rust/src/download_provider.rs +++ b/rust/src/download_provider.rs @@ -1,6 +1,6 @@ use crate::rc::{Array, CoreArrayProvider, CoreArrayProviderInner, Guard, Ref, RefCountable}; use crate::settings::Settings; -use crate::string::{BnStrCompatible, BnString}; +use crate::string::{BnString, IntoCStr}; use binaryninjacore_sys::*; use std::collections::HashMap; use std::ffi::{c_void, CStr}; @@ -13,12 +13,9 @@ pub struct DownloadProvider { } impl DownloadProvider { - pub fn get(name: S) -> Option { - let result = unsafe { - BNGetDownloadProviderByName( - name.into_bytes_with_nul().as_ref().as_ptr() as *const c_char - ) - }; + pub fn get(name: &str) -> Option { + let name = name.to_cstr(); + let result = unsafe { BNGetDownloadProviderByName(name.as_ptr()) }; if result.is_null() { return None; } @@ -40,7 +37,7 @@ impl DownloadProvider { pub fn try_default() -> Result { let s = Settings::new(); let dp_name = s.get_string("network.downloadProviderName"); - Self::get(dp_name).ok_or(()) + Self::get(&dp_name).ok_or(()) } pub(crate) fn from_raw(handle: *mut BNDownloadProvider) -> DownloadProvider { @@ -105,9 +102,9 @@ impl DownloadInstance { Ref::new(Self::from_raw(handle)) } - fn get_error(&self) -> BnString { + fn get_error(&self) -> String { let err: *mut c_char = unsafe { BNGetErrorForDownloadInstance(self.handle) }; - unsafe { BnString::from_raw(err) } + unsafe { BnString::into_string(err) } } unsafe extern "C" fn o_write_callback(data: *mut u8, len: u64, ctxt: *mut c_void) -> u64 { @@ -134,11 +131,11 @@ impl DownloadInstance { } } - pub fn perform_request( + pub fn perform_request( &mut self, - url: S, + url: &str, callbacks: DownloadInstanceOutputCallbacks, - ) -> Result<(), BnString> { + ) -> Result<(), String> { let callbacks = Box::into_raw(Box::new(callbacks)); let mut cbs = BNDownloadInstanceOutputCallbacks { writeCallback: Some(Self::o_write_callback), @@ -147,10 +144,11 @@ impl DownloadInstance { progressContext: callbacks as *mut c_void, }; + let url_raw = url.to_cstr(); let result = unsafe { BNPerformDownloadRequest( self.handle, - url.into_bytes_with_nul().as_ref().as_ptr() as *const c_char, + url_raw.as_ptr(), &mut cbs as *mut BNDownloadInstanceOutputCallbacks, ) }; @@ -203,32 +201,29 @@ impl DownloadInstance { } } - pub fn perform_custom_request< - M: BnStrCompatible, - U: BnStrCompatible, - HK: BnStrCompatible, - HV: BnStrCompatible, - I: IntoIterator, - >( + pub fn perform_custom_request( &mut self, - method: M, - url: U, + method: &str, + url: &str, headers: I, callbacks: DownloadInstanceInputOutputCallbacks, - ) -> Result { + ) -> Result + where + I: IntoIterator, + { let mut header_keys = vec![]; let mut header_values = vec![]; for (key, value) in headers { - header_keys.push(key.into_bytes_with_nul()); - header_values.push(value.into_bytes_with_nul()); + header_keys.push(key.to_cstr()); + header_values.push(value.to_cstr()); } let mut header_key_ptrs = vec![]; let mut header_value_ptrs = vec![]; for (key, value) in header_keys.iter().zip(header_values.iter()) { - header_key_ptrs.push(key.as_ref().as_ptr() as *const c_char); - header_value_ptrs.push(value.as_ref().as_ptr() as *const c_char); + header_key_ptrs.push(key.as_ptr()); + header_value_ptrs.push(value.as_ptr()); } let callbacks = Box::into_raw(Box::new(callbacks)); @@ -243,11 +238,13 @@ impl DownloadInstance { let mut response: *mut BNDownloadInstanceResponse = null_mut(); + let method_raw = method.to_cstr(); + let url_raw = url.to_cstr(); let result = unsafe { BNPerformCustomRequest( self.handle, - method.into_bytes_with_nul().as_ref().as_ptr() as *const c_char, - url.into_bytes_with_nul().as_ref().as_ptr() as *const c_char, + method_raw.as_ptr(), + url_raw.as_ptr(), header_key_ptrs.len() as u64, header_key_ptrs.as_ptr(), header_value_ptrs.as_ptr(), diff --git a/rust/src/enterprise.rs b/rust/src/enterprise.rs index 7030b194aa..68ad069cb1 100644 --- a/rust/src/enterprise.rs +++ b/rust/src/enterprise.rs @@ -1,5 +1,5 @@ use crate::rc::Array; -use crate::string::{BnStrCompatible, BnString}; +use crate::string::{BnString, IntoCStr}; use std::ffi::c_void; use std::marker::PhantomData; use std::time::{Duration, SystemTime, UNIX_EPOCH}; @@ -19,13 +19,23 @@ pub enum EnterpriseCheckoutError { RefreshExpiredLicenseFailed(String), } +#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] +pub enum EnterpriseCheckoutStatus { + /// The UI is managing the enterprise checkout. + AlreadyManaged, + /// Checkout was successful, attached duration is the duration of the license, if any. + Success(Option), +} + /// Initialize the enterprise server connection to check out a floating license. /// Result value is if we actually checked out a license (i.e. Ok(false) means we already have a /// license checked out and will not need to release it later) -pub fn checkout_license(duration: Duration) -> Result { +pub fn checkout_license( + duration: Duration, +) -> Result { if crate::is_ui_enabled() { // We only need to check out a license if running headlessly. - return Ok(false); + return Ok(EnterpriseCheckoutStatus::AlreadyManaged); } // The disparate core functions we call here might already have mutexes to guard. @@ -56,7 +66,7 @@ pub fn checkout_license(duration: Duration) -> Result Result BnString { - unsafe { BnString::from_raw(binaryninjacore_sys::BNGetEnterpriseServerUsername()) } +pub fn server_username() -> String { + unsafe { BnString::into_string(binaryninjacore_sys::BNGetEnterpriseServerUsername()) } } // TODO: If "" string return None -pub fn server_url() -> BnString { - unsafe { BnString::from_raw(binaryninjacore_sys::BNGetEnterpriseServerUrl()) } +pub fn server_url() -> String { + unsafe { BnString::into_string(binaryninjacore_sys::BNGetEnterpriseServerUrl()) } } -pub fn set_server_url(url: S) -> Result<(), ()> { - let url = url.into_bytes_with_nul(); +pub fn set_server_url(url: &str) -> Result<(), ()> { + let url = url.to_cstr(); let result = unsafe { binaryninjacore_sys::BNSetEnterpriseServerUrl( url.as_ref().as_ptr() as *const std::os::raw::c_char @@ -121,28 +134,34 @@ pub fn set_server_url(url: S) -> Result<(), ()> { } } -pub fn server_name() -> BnString { - unsafe { BnString::from_raw(binaryninjacore_sys::BNGetEnterpriseServerName()) } +pub fn server_name() -> String { + unsafe { BnString::into_string(binaryninjacore_sys::BNGetEnterpriseServerName()) } } -pub fn server_id() -> BnString { - unsafe { BnString::from_raw(binaryninjacore_sys::BNGetEnterpriseServerId()) } +pub fn server_id() -> String { + unsafe { BnString::into_string(binaryninjacore_sys::BNGetEnterpriseServerId()) } } pub fn server_version() -> u64 { unsafe { binaryninjacore_sys::BNGetEnterpriseServerVersion() } } -pub fn server_build_id() -> BnString { - unsafe { BnString::from_raw(binaryninjacore_sys::BNGetEnterpriseServerBuildId()) } +pub fn server_build_id() -> String { + unsafe { BnString::into_string(binaryninjacore_sys::BNGetEnterpriseServerBuildId()) } } -pub fn server_token() -> BnString { - unsafe { BnString::from_raw(binaryninjacore_sys::BNGetEnterpriseServerToken()) } +pub fn server_token() -> String { + unsafe { BnString::into_string(binaryninjacore_sys::BNGetEnterpriseServerToken()) } } -pub fn license_duration() -> Duration { - Duration::from_secs(unsafe { binaryninjacore_sys::BNGetEnterpriseServerLicenseDuration() }) +pub fn license_duration() -> Option { + let duration = + Duration::from_secs(unsafe { binaryninjacore_sys::BNGetEnterpriseServerLicenseDuration() }); + match duration { + // If the core returns 0 there is no license duration. + Duration::ZERO => None, + _ => Some(duration), + } } pub fn license_expiration_time() -> SystemTime { @@ -164,13 +183,13 @@ pub fn is_server_license_still_activated() -> bool { unsafe { binaryninjacore_sys::BNIsEnterpriseServerLicenseStillActivated() } } -pub fn authenticate_server_with_credentials(username: U, password: P, remember: bool) -> bool -where - U: BnStrCompatible, - P: BnStrCompatible, -{ - let username = username.into_bytes_with_nul(); - let password = password.into_bytes_with_nul(); +pub fn authenticate_server_with_credentials( + username: &str, + password: &str, + remember: bool, +) -> bool { + let username = username.to_cstr(); + let password = password.to_cstr(); unsafe { binaryninjacore_sys::BNAuthenticateEnterpriseServerWithCredentials( username.as_ref().as_ptr() as *const std::os::raw::c_char, @@ -180,8 +199,8 @@ where } } -pub fn authenticate_server_with_method(method: S, remember: bool) -> bool { - let method = method.into_bytes_with_nul(); +pub fn authenticate_server_with_method(method: &str, remember: bool) -> bool { + let method = method.to_cstr(); unsafe { binaryninjacore_sys::BNAuthenticateEnterpriseServerWithMethod( method.as_ref().as_ptr() as *const std::os::raw::c_char, @@ -226,8 +245,8 @@ pub fn initialize_server() -> bool { unsafe { binaryninjacore_sys::BNInitializeEnterpriseServer() } } -pub fn server_last_error() -> BnString { - unsafe { BnString::from_raw(binaryninjacore_sys::BNGetEnterpriseServerLastError()) } +pub fn server_last_error() -> String { + unsafe { BnString::into_string(binaryninjacore_sys::BNGetEnterpriseServerLastError()) } } pub fn server_authentication_methods() -> (Array, Array) { diff --git a/rust/src/external_library.rs b/rust/src/external_library.rs index 2b3594b643..148fd030f6 100644 --- a/rust/src/external_library.rs +++ b/rust/src/external_library.rs @@ -1,9 +1,8 @@ use crate::project::file::ProjectFile; use crate::rc::{CoreArrayProvider, CoreArrayProviderInner, Guard, Ref, RefCountable}; -use crate::string::{BnStrCompatible, BnString}; +use crate::string::{BnString, IntoCStr}; use crate::symbol::Symbol; use binaryninjacore_sys::*; -use std::ffi::c_char; use std::fmt::Debug; use std::ptr::NonNull; @@ -24,10 +23,10 @@ impl ExternalLibrary { } /// Get the name of this external library - pub fn name(&self) -> BnString { + pub fn name(&self) -> String { let result = unsafe { BNExternalLibraryGetName(self.handle.as_ptr()) }; assert!(!result.is_null()); - unsafe { BnString::from_raw(result) } + unsafe { BnString::into_string(result) } } /// Get the file backing this external library @@ -167,11 +166,16 @@ impl ExternalLocation { /// Set the symbol pointed to by this ExternalLocation. /// ExternalLocations must have a valid target address and/or symbol set. - pub fn set_target_symbol(&self, symbol: Option) -> bool { - let symbol = symbol - .map(|x| x.into_bytes_with_nul().as_ref().as_ptr() as *const c_char) - .unwrap_or(std::ptr::null_mut()); - unsafe { BNExternalLocationSetTargetSymbol(self.handle.as_ptr(), symbol) } + pub fn set_target_symbol(&self, symbol: Option<&str>) -> bool { + match symbol { + Some(sym) => { + let raw_sym = sym.to_cstr(); + unsafe { BNExternalLocationSetTargetSymbol(self.handle.as_ptr(), raw_sym.as_ptr()) } + } + None => unsafe { + BNExternalLocationSetTargetSymbol(self.handle.as_ptr(), std::ptr::null()) + }, + } } } diff --git a/rust/src/file_accessor.rs b/rust/src/file_accessor.rs index 12d25ff0c3..2ba7938ffb 100644 --- a/rust/src/file_accessor.rs +++ b/rust/src/file_accessor.rs @@ -13,41 +13,36 @@ // limitations under the License. use binaryninjacore_sys::BNFileAccessor; -use std::io::{Read, Seek, SeekFrom, Write}; +use std::io::{ErrorKind, Read, Seek, SeekFrom, Write}; use std::marker::PhantomData; use std::slice; -pub struct FileAccessor<'a> { - pub(crate) api_object: BNFileAccessor, - _ref: PhantomData<&'a mut ()>, +pub trait Accessor: Read + Write + Seek + Sized {} + +impl Accessor for T {} + +pub struct FileAccessor { + pub(crate) raw: BNFileAccessor, + accessor: PhantomData, } -impl<'a> FileAccessor<'a> { - pub fn new(f: &'a mut F) -> Self - where - F: 'a + Read + Write + Seek + Sized, - { +impl FileAccessor { + pub fn new(accessor: A) -> Self { use std::os::raw::c_void; - extern "C" fn cb_get_length(ctxt: *mut c_void) -> u64 - where - F: Read + Write + Seek + Sized, - { - let f = unsafe { &mut *(ctxt as *mut F) }; + extern "C" fn cb_get_length(ctxt: *mut c_void) -> u64 { + let f = unsafe { &mut *(ctxt as *mut A) }; f.seek(SeekFrom::End(0)).unwrap_or(0) } - extern "C" fn cb_read( + extern "C" fn cb_read( ctxt: *mut c_void, dest: *mut c_void, offset: u64, len: usize, - ) -> usize - where - F: Read + Write + Seek + Sized, - { - let f = unsafe { &mut *(ctxt as *mut F) }; + ) -> usize { + let f = unsafe { &mut *(ctxt as *mut A) }; let dest = unsafe { slice::from_raw_parts_mut(dest as *mut u8, len) }; if f.seek(SeekFrom::Start(offset)).is_err() { @@ -58,16 +53,13 @@ impl<'a> FileAccessor<'a> { } } - extern "C" fn cb_write( + extern "C" fn cb_write( ctxt: *mut c_void, offset: u64, src: *const c_void, len: usize, - ) -> usize - where - F: Read + Write + Seek + Sized, - { - let f = unsafe { &mut *(ctxt as *mut F) }; + ) -> usize { + let f = unsafe { &mut *(ctxt as *mut A) }; let src = unsafe { slice::from_raw_parts(src as *const u8, len) }; if f.seek(SeekFrom::Start(offset)).is_err() { @@ -77,14 +69,52 @@ impl<'a> FileAccessor<'a> { } } + let boxed_accessor = Box::new(accessor); + let leaked_accessor = Box::leak(boxed_accessor); + Self { - api_object: BNFileAccessor { - context: f as *mut F as *mut _, - getLength: Some(cb_get_length::), - read: Some(cb_read::), - write: Some(cb_write::), + raw: BNFileAccessor { + context: leaked_accessor as *mut A as *mut _, + getLength: Some(cb_get_length::), + read: Some(cb_read::), + write: Some(cb_write::), }, - _ref: PhantomData, + accessor: PhantomData, + } + } + + pub fn read(&self, addr: u64, len: usize) -> Result, ErrorKind> { + let cb_read = self.raw.read.unwrap(); + let mut buf = vec![0; len]; + let read_len = unsafe { cb_read(self.raw.context, buf.as_mut_ptr() as *mut _, addr, len) }; + if read_len != len { + return Err(ErrorKind::UnexpectedEof); + } + Ok(buf) + } + + pub fn write(&self, addr: u64, data: &[u8]) -> usize { + let cb_write = self.raw.write.unwrap(); + unsafe { + cb_write( + self.raw.context, + addr, + data.as_ptr() as *const _, + data.len(), + ) + } + } + + pub fn length(&self) -> u64 { + let cb_get_length = self.raw.getLength.unwrap(); + unsafe { cb_get_length(self.raw.context) } + } +} + +impl Drop for FileAccessor { + fn drop(&mut self) { + unsafe { + let _ = Box::from_raw(self.raw.context as *mut A); } } } diff --git a/rust/src/file_metadata.rs b/rust/src/file_metadata.rs index f1923b407c..ed85977564 100644 --- a/rust/src/file_metadata.rs +++ b/rust/src/file_metadata.rs @@ -52,7 +52,7 @@ impl FileMetadata { Self::ref_from_raw(unsafe { BNCreateFileMetadata() }) } - pub fn with_filename(name: S) -> Ref { + pub fn with_filename(name: &str) -> Ref { let ret = FileMetadata::new(); ret.set_filename(name); ret @@ -68,18 +68,18 @@ impl FileMetadata { unsafe { BNFileMetadataGetSessionId(self.handle) } } - pub fn filename(&self) -> BnString { + pub fn filename(&self) -> String { unsafe { let raw = BNGetFilename(self.handle); - BnString::from_raw(raw) + BnString::into_string(raw) } } - pub fn set_filename(&self, name: S) { - let name = name.into_bytes_with_nul(); + pub fn set_filename(&self, name: &str) { + let name = name.to_cstr(); unsafe { - BNSetFilename(self.handle, name.as_ref().as_ptr() as *mut _); + BNSetFilename(self.handle, name.as_ptr()); } } @@ -107,12 +107,22 @@ impl FileMetadata { self.is_database_backed_for_view_type("") } - pub fn is_database_backed_for_view_type(&self, view_type: S) -> bool { - let view_type = view_type.into_bytes_with_nul(); + pub fn is_database_backed_for_view_type(&self, view_type: &str) -> bool { + let view_type = view_type.to_cstr(); unsafe { BNIsBackedByDatabase(self.handle, view_type.as_ref().as_ptr() as *const _) } } + /// Runs a failable function where the failure state will revert any undo actions that occurred + /// during the time of the function's execution. + /// + /// NOTE: This will commit or undo any actions that occurred on **any** thread as this state is not thread local. + /// + /// NOTE: This is **NOT** thread safe, if you are holding any locks that might be held by both the main thread + /// and the thread executing this function, you can deadlock. You should also never call this function + /// on multiple threads at a time. See the following issues: + /// - + /// - pub fn run_undoable_transaction Result, T, E>( &self, func: F, @@ -121,29 +131,50 @@ impl FileMetadata { let result = func(); match result { Ok(t) => { - self.commit_undo_actions(undo); + self.commit_undo_actions(&undo); Ok(t) } Err(e) => { - self.revert_undo_actions(undo); + self.revert_undo_actions(&undo); Err(e) } } } - pub fn begin_undo_actions(&self, anonymous_allowed: bool) -> BnString { - unsafe { BnString::from_raw(BNBeginUndoActions(self.handle, anonymous_allowed)) } - } - - pub fn commit_undo_actions(&self, id: S) { - let id = id.into_bytes_with_nul(); + /// Creates a new undo entry, any undo actions after this will be added to this entry. + /// + /// NOTE: This is **NOT** thread safe, if you are holding any locks that might be held by both the main thread + /// and the thread executing this function, you can deadlock. You should also never call this function + /// on multiple threads at a time. See the following issues: + /// - + /// - + pub fn begin_undo_actions(&self, anonymous_allowed: bool) -> String { + unsafe { BnString::into_string(BNBeginUndoActions(self.handle, anonymous_allowed)) } + } + + /// Commits the undo entry with the id to the undo buffer. + /// + /// NOTE: This is **NOT** thread safe, if you are holding any locks that might be held by both the main thread + /// and the thread executing this function, you can deadlock. You should also never call this function + /// on multiple threads at a time. See the following issues: + /// - + /// - + pub fn commit_undo_actions(&self, id: &str) { + let id = id.to_cstr(); unsafe { BNCommitUndoActions(self.handle, id.as_ref().as_ptr() as *const _); } } - pub fn revert_undo_actions(&self, id: S) { - let id = id.into_bytes_with_nul(); + /// Reverts the undo actions committed in the undo entry. + /// + /// NOTE: This is **NOT** thread safe, if you are holding any locks that might be held by both the main thread + /// and the thread executing this function, you can deadlock. You should also never call this function + /// on multiple threads at a time. See the following issues: + /// - + /// - + pub fn revert_undo_actions(&self, id: &str) { + let id = id.to_cstr(); unsafe { BNRevertUndoActions(self.handle, id.as_ref().as_ptr() as *const _); } @@ -161,16 +192,16 @@ impl FileMetadata { } } - pub fn current_view(&self) -> BnString { - unsafe { BnString::from_raw(BNGetCurrentView(self.handle)) } + pub fn current_view(&self) -> String { + unsafe { BnString::into_string(BNGetCurrentView(self.handle)) } } pub fn current_offset(&self) -> u64 { unsafe { BNGetCurrentOffset(self.handle) } } - pub fn navigate_to(&self, view: S, offset: u64) -> Result<(), ()> { - let view = view.into_bytes_with_nul(); + pub fn navigate_to(&self, view: &str, offset: u64) -> Result<(), ()> { + let view = view.to_cstr(); unsafe { if BNNavigate(self.handle, view.as_ref().as_ptr() as *const _, offset) { @@ -181,8 +212,8 @@ impl FileMetadata { } } - pub fn view_of_type(&self, view: S) -> Option> { - let view = view.into_bytes_with_nul(); + pub fn view_of_type(&self, view: &str) -> Option> { + let view = view.to_cstr(); unsafe { let raw_view_ptr = BNGetFileViewOfType(self.handle, view.as_ref().as_ptr() as *const _); @@ -215,7 +246,7 @@ impl FileMetadata { return false; }; - let file_path = file_path.as_ref().into_bytes_with_nul(); + let file_path = file_path.as_ref().to_cstr(); unsafe { BNCreateDatabase( raw_view.handle, @@ -226,7 +257,7 @@ impl FileMetadata { } // TODO: Pass settings? - pub fn create_database_with_progress( + pub fn create_database_with_progress( &self, file_path: impl AsRef, mut progress: P, @@ -235,7 +266,7 @@ impl FileMetadata { let Some(raw_view) = self.view_of_type("Raw") else { return false; }; - let file_path = file_path.as_ref().into_bytes_with_nul(); + let file_path = file_path.as_ref().to_cstr(); unsafe { BNCreateDatabaseWithProgress( raw_view.handle, @@ -256,14 +287,11 @@ impl FileMetadata { unsafe { BNSaveAutoSnapshot(raw_view.handle, ptr::null_mut() as *mut _) } } - pub fn open_database_for_configuration( - &self, - filename: S, - ) -> Result, ()> { - let filename = filename.into_bytes_with_nul(); + pub fn open_database_for_configuration(&self, file: &Path) -> Result, ()> { + let file = file.to_cstr(); unsafe { let bv = - BNOpenDatabaseForConfiguration(self.handle, filename.as_ref().as_ptr() as *const _); + BNOpenDatabaseForConfiguration(self.handle, file.as_ref().as_ptr() as *const _); if bv.is_null() { Err(()) @@ -273,11 +301,9 @@ impl FileMetadata { } } - pub fn open_database(&self, filename: S) -> Result, ()> { - let filename = filename.into_bytes_with_nul(); - let filename_ptr = filename.as_ref().as_ptr() as *mut _; - - let view = unsafe { BNOpenExistingDatabase(self.handle, filename_ptr) }; + pub fn open_database(&self, file: &Path) -> Result, ()> { + let file = file.to_cstr(); + let view = unsafe { BNOpenExistingDatabase(self.handle, file.as_ptr()) }; if view.is_null() { Err(()) @@ -286,18 +312,17 @@ impl FileMetadata { } } - pub fn open_database_with_progress( + pub fn open_database_with_progress( &self, - filename: S, + file: &Path, mut progress: P, ) -> Result, ()> { - let filename = filename.into_bytes_with_nul(); - let filename_ptr = filename.as_ref().as_ptr() as *mut _; + let file = file.to_cstr(); let view = unsafe { BNOpenExistingDatabaseWithProgress( self.handle, - filename_ptr, + file.as_ptr(), &mut progress as *mut P as *mut c_void, Some(P::cb_progress_callback), ) diff --git a/rust/src/flowgraph.rs b/rust/src/flowgraph.rs index c8a9e4a74d..2d1d986ca3 100644 --- a/rust/src/flowgraph.rs +++ b/rust/src/flowgraph.rs @@ -14,18 +14,24 @@ //! Interfaces for creating and displaying pretty CFGs in Binary Ninja. -use crate::architecture::CoreArchitecture; -use crate::disassembly::DisassemblyTextLine; -use crate::rc::*; use binaryninjacore_sys::*; -use crate::basic_block::{BasicBlock, BlockContext}; -use crate::function::HighlightColor; use crate::high_level_il::HighLevelILFunction; -use crate::low_level_il::RegularLowLevelILFunction; +use crate::low_level_il::LowLevelILRegularFunction; use crate::medium_level_il::MediumLevelILFunction; +use crate::rc::*; use crate::render_layer::CoreRenderLayer; +pub mod edge; +pub mod node; + +use crate::binary_view::BinaryView; +use crate::function::Function; +use crate::string::IntoCStr; +pub use edge::EdgeStyle; +pub use edge::FlowGraphEdge; +pub use node::FlowGraphNode; + pub type BranchType = BNBranchType; pub type EdgePenStyle = BNEdgePenStyle; pub type ThemeColor = BNThemeColor; @@ -49,44 +55,101 @@ impl FlowGraph { unsafe { FlowGraph::ref_from_raw(BNCreateFlowGraph()) } } + pub fn has_updates(&self) -> bool { + let query_mode = unsafe { BNFlowGraphUpdateQueryMode(self.handle) }; + match query_mode { + true => unsafe { BNFlowGraphHasUpdates(self.handle) }, + false => false, + } + } + + pub fn update(&self) -> Option> { + let new_graph = unsafe { BNUpdateFlowGraph(self.handle) }; + if new_graph.is_null() { + return None; + } + Some(unsafe { FlowGraph::ref_from_raw(new_graph) }) + } + + pub fn show(&self, title: &str) { + let raw_title = title.to_cstr(); + match self.view() { + None => unsafe { + BNShowGraphReport(std::ptr::null_mut(), raw_title.as_ptr(), self.handle); + }, + Some(view) => unsafe { + BNShowGraphReport(view.handle, raw_title.as_ptr(), self.handle); + }, + } + } + + /// Whether flow graph layout is complete. + pub fn is_layout_complete(&self) -> bool { + unsafe { BNIsFlowGraphLayoutComplete(self.handle) } + } + pub fn nodes(&self) -> Array { let mut count: usize = 0; let nodes_ptr = unsafe { BNGetFlowGraphNodes(self.handle, &mut count as *mut usize) }; unsafe { Array::new(nodes_ptr, count, ()) } } - pub fn low_level_il(&self) -> Result>, ()> { + pub fn function(&self) -> Option> { + unsafe { + let func_ptr = BNGetFunctionForFlowGraph(self.handle); + match func_ptr.is_null() { + false => Some(Function::ref_from_raw(func_ptr)), + true => None, + } + } + } + + pub fn set_function(&self, func: Option<&Function>) { + let func_ptr = func.map(|f| f.handle).unwrap_or(std::ptr::null_mut()); + unsafe { BNSetFunctionForFlowGraph(self.handle, func_ptr) } + } + + pub fn view(&self) -> Option> { + unsafe { + let view_ptr = BNGetViewForFlowGraph(self.handle); + match view_ptr.is_null() { + false => Some(BinaryView::ref_from_raw(view_ptr)), + true => None, + } + } + } + + pub fn set_view(&self, view: Option<&BinaryView>) { + let view_ptr = view.map(|v| v.handle).unwrap_or(std::ptr::null_mut()); + unsafe { BNSetViewForFlowGraph(self.handle, view_ptr) } + } + + pub fn low_level_il(&self) -> Option> { unsafe { let llil_ptr = BNGetFlowGraphLowLevelILFunction(self.handle); match llil_ptr.is_null() { - false => { - let func_ptr = BNGetLowLevelILOwnerFunction(llil_ptr); - let arch_ptr = BNGetFunctionArchitecture(func_ptr); - let arch = CoreArchitecture::from_raw(arch_ptr); - BNFreeFunction(func_ptr); - Ok(RegularLowLevelILFunction::ref_from_raw(arch, llil_ptr)) - } - true => Err(()), + false => Some(LowLevelILRegularFunction::ref_from_raw(llil_ptr)), + true => None, } } } - pub fn medium_level_il(&self) -> Result, ()> { + pub fn medium_level_il(&self) -> Option> { unsafe { let mlil_ptr = BNGetFlowGraphMediumLevelILFunction(self.handle); match mlil_ptr.is_null() { - false => Ok(MediumLevelILFunction::ref_from_raw(mlil_ptr)), - true => Err(()), + false => Some(MediumLevelILFunction::ref_from_raw(mlil_ptr)), + true => None, } } } - pub fn high_level_il(&self, full_ast: bool) -> Result, ()> { + pub fn high_level_il(&self, full_ast: bool) -> Option> { unsafe { let hlil_ptr = BNGetFlowGraphHighLevelILFunction(self.handle); match hlil_ptr.is_null() { - false => Ok(HighLevelILFunction::ref_from_raw(hlil_ptr, full_ast)), - true => Err(()), + false => Some(HighLevelILFunction::ref_from_raw(hlil_ptr, full_ast)), + true => None, } } } @@ -108,6 +171,25 @@ impl FlowGraph { unsafe { BNFlowGraphHasNodes(self.handle) } } + /// Returns the graph size in width, height form. + pub fn size(&self) -> (i32, i32) { + let width = unsafe { BNGetFlowGraphWidth(self.handle) }; + let height = unsafe { BNGetFlowGraphHeight(self.handle) }; + (width, height) + } + + /// Returns the graph margins between nodes. + pub fn node_margins(&self) -> (i32, i32) { + let horizontal = unsafe { BNGetHorizontalFlowGraphNodeMargin(self.handle) }; + let vertical = unsafe { BNGetVerticalFlowGraphNodeMargin(self.handle) }; + (horizontal, vertical) + } + + /// Sets the graph margins between nodes. + pub fn set_node_margins(&self, horizontal: i32, vertical: i32) { + unsafe { BNSetFlowGraphNodeMargins(self.handle, horizontal, vertical) }; + } + pub fn append(&self, node: &FlowGraphNode) -> usize { unsafe { BNAddFlowGraphNode(self.handle, node.handle) } } @@ -169,168 +251,3 @@ impl ToOwned for FlowGraph { unsafe { RefCountable::inc_ref(self) } } } - -#[derive(PartialEq, Eq, Hash)] -pub struct FlowGraphNode { - pub(crate) handle: *mut BNFlowGraphNode, -} - -impl FlowGraphNode { - pub(crate) unsafe fn from_raw(raw: *mut BNFlowGraphNode) -> Self { - Self { handle: raw } - } - - pub(crate) unsafe fn ref_from_raw(raw: *mut BNFlowGraphNode) -> Ref { - Ref::new(Self { handle: raw }) - } - - pub fn new(graph: &FlowGraph) -> Ref { - unsafe { FlowGraphNode::ref_from_raw(BNCreateFlowGraphNode(graph.handle)) } - } - - pub fn basic_block(&self, context: C) -> Option>> { - let block_ptr = unsafe { BNGetFlowGraphBasicBlock(self.handle) }; - if block_ptr.is_null() { - return None; - } - Some(unsafe { BasicBlock::ref_from_raw(block_ptr, context) }) - } - - pub fn set_basic_block(&self, block: Option<&BasicBlock>) { - match block { - Some(block) => unsafe { BNSetFlowGraphBasicBlock(self.handle, block.handle) }, - None => unsafe { BNSetFlowGraphBasicBlock(self.handle, std::ptr::null_mut()) }, - } - } - - pub fn lines(&self) -> Array { - let mut count = 0; - let result = unsafe { BNGetFlowGraphNodeLines(self.handle, &mut count) }; - assert!(!result.is_null()); - unsafe { Array::new(result, count, ()) } - } - - pub fn set_lines(&self, lines: impl IntoIterator) { - // NOTE: This will create allocations and increment tag refs, we must call DisassemblyTextLine::free_raw - 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 { - DisassemblyTextLine::free_raw(raw_line); - } - } - } - - /// Returns the graph position of the node in X, Y form. - pub fn position(&self) -> (i32, i32) { - let pos_x = unsafe { BNGetFlowGraphNodeX(self.handle) }; - let pos_y = unsafe { BNGetFlowGraphNodeY(self.handle) }; - (pos_x, pos_y) - } - - /// Sets the graph position of the node. - pub fn set_position(&self, x: i32, y: i32) { - unsafe { BNFlowGraphNodeSetX(self.handle, x) }; - unsafe { BNFlowGraphNodeSetX(self.handle, y) }; - } - - pub fn highlight_color(&self) -> HighlightColor { - let raw = unsafe { BNGetFlowGraphNodeHighlight(self.handle) }; - HighlightColor::from(raw) - } - - pub fn set_highlight_color(&self, highlight: HighlightColor) { - unsafe { BNSetFlowGraphNodeHighlight(self.handle, highlight.into()) }; - } - - // TODO: Add getters and setters for edges - - pub fn add_outgoing_edge( - &self, - type_: BranchType, - target: &FlowGraphNode, - edge_style: EdgeStyle, - ) { - unsafe { - BNAddFlowGraphNodeOutgoingEdge(self.handle, type_, target.handle, edge_style.into()) - } - } -} - -unsafe impl RefCountable for FlowGraphNode { - unsafe fn inc_ref(handle: &Self) -> Ref { - Ref::new(Self { - handle: BNNewFlowGraphNodeReference(handle.handle), - }) - } - - unsafe fn dec_ref(handle: &Self) { - BNFreeFlowGraphNode(handle.handle); - } -} - -impl ToOwned for FlowGraphNode { - type Owned = Ref; - - fn to_owned(&self) -> Self::Owned { - unsafe { RefCountable::inc_ref(self) } - } -} - -impl CoreArrayProvider for FlowGraphNode { - type Raw = *mut BNFlowGraphNode; - type Context = (); - type Wrapped<'a> = Guard<'a, FlowGraphNode>; -} - -unsafe impl CoreArrayProviderInner for FlowGraphNode { - unsafe fn free(raw: *mut Self::Raw, count: usize, _: &Self::Context) { - BNFreeFlowGraphNodeList(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) - } -} - -#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)] -pub struct EdgeStyle { - style: EdgePenStyle, - width: usize, - color: ThemeColor, -} - -impl EdgeStyle { - pub fn new(style: EdgePenStyle, width: usize, color: ThemeColor) -> Self { - Self { - style, - width, - color, - } - } -} - -impl Default for EdgeStyle { - fn default() -> Self { - Self::new(EdgePenStyle::SolidLine, 0, ThemeColor::AddressColor) - } -} - -impl From for EdgeStyle { - fn from(style: BNEdgeStyle) -> Self { - Self::new(style.style, style.width, style.color) - } -} - -impl From for BNEdgeStyle { - fn from(style: EdgeStyle) -> Self { - Self { - style: style.style, - width: style.width, - color: style.color, - } - } -} diff --git a/rust/src/flowgraph/edge.rs b/rust/src/flowgraph/edge.rs new file mode 100644 index 0000000000..7862f15652 --- /dev/null +++ b/rust/src/flowgraph/edge.rs @@ -0,0 +1,113 @@ +use binaryninjacore_sys::*; + +use crate::flowgraph::node::FlowGraphNode; +use crate::flowgraph::{BranchType, EdgePenStyle, ThemeColor}; +use crate::rc::{CoreArrayProvider, CoreArrayProviderInner, Ref}; + +#[derive(Clone, Debug, PartialEq)] +pub struct FlowGraphEdge { + pub branch_type: BranchType, + pub target: Ref, + pub points: Vec, + pub back_edge: bool, + pub style: EdgeStyle, +} + +impl FlowGraphEdge { + pub fn from_raw(value: &BNFlowGraphEdge) -> Self { + let raw_points = unsafe { std::slice::from_raw_parts(value.points, value.pointCount) }; + let points: Vec<_> = raw_points.iter().copied().map(Point::from).collect(); + Self { + branch_type: value.type_, + target: unsafe { FlowGraphNode::from_raw(value.target) }.to_owned(), + points, + back_edge: value.backEdge, + style: value.style.into(), + } + } +} + +impl CoreArrayProvider for FlowGraphEdge { + type Raw = BNFlowGraphEdge; + type Context = (); + type Wrapped<'a> = FlowGraphEdge; +} + +unsafe impl CoreArrayProviderInner for FlowGraphEdge { + unsafe fn free(raw: *mut Self::Raw, count: usize, _: &Self::Context) { + BNFreeFlowGraphNodeEdgeList(raw, count); + } + + unsafe fn wrap_raw<'a>(raw: &'a Self::Raw, _context: &'a Self::Context) -> Self::Wrapped<'a> { + Self::from_raw(raw) + } +} + +#[derive(Clone, Copy, Debug, PartialEq, Default)] +pub struct Point { + pub x: f32, + pub y: f32, +} + +impl Point { + pub fn new(x: f32, y: f32) -> Self { + Self { x, y } + } +} + +impl From for Point { + fn from(value: BNPoint) -> Self { + Self { + x: value.x, + y: value.y, + } + } +} + +impl From for BNPoint { + fn from(value: Point) -> Self { + Self { + x: value.x, + y: value.y, + } + } +} + +#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)] +pub struct EdgeStyle { + style: EdgePenStyle, + width: usize, + color: ThemeColor, +} + +impl EdgeStyle { + pub fn new(style: EdgePenStyle, width: usize, color: ThemeColor) -> Self { + Self { + style, + width, + color, + } + } +} + +impl Default for EdgeStyle { + fn default() -> Self { + Self::new(EdgePenStyle::SolidLine, 0, ThemeColor::AddressColor) + } +} + +impl From for EdgeStyle { + fn from(style: BNEdgeStyle) -> Self { + Self::new(style.style, style.width, style.color) + } +} + +impl From for BNEdgeStyle { + fn from(style: EdgeStyle) -> Self { + Self { + style: style.style, + width: style.width, + color: style.color, + } + } +} diff --git a/rust/src/flowgraph/node.rs b/rust/src/flowgraph/node.rs new file mode 100644 index 0000000000..5c2b9248da --- /dev/null +++ b/rust/src/flowgraph/node.rs @@ -0,0 +1,155 @@ +use crate::basic_block::{BasicBlock, BlockContext}; +use crate::disassembly::DisassemblyTextLine; +use crate::flowgraph::edge::{EdgeStyle, FlowGraphEdge}; +use crate::flowgraph::{BranchType, FlowGraph}; +use crate::function::HighlightColor; +use crate::rc::{Array, CoreArrayProvider, CoreArrayProviderInner, Guard, Ref, RefCountable}; +use binaryninjacore_sys::*; +use std::fmt::{Debug, Formatter}; + +#[derive(PartialEq, Eq, Hash)] +pub struct FlowGraphNode { + pub(crate) handle: *mut BNFlowGraphNode, +} + +impl FlowGraphNode { + pub(crate) unsafe fn from_raw(raw: *mut BNFlowGraphNode) -> Self { + Self { handle: raw } + } + + pub(crate) unsafe fn ref_from_raw(raw: *mut BNFlowGraphNode) -> Ref { + Ref::new(Self { handle: raw }) + } + + pub fn new(graph: &FlowGraph) -> Ref { + unsafe { FlowGraphNode::ref_from_raw(BNCreateFlowGraphNode(graph.handle)) } + } + + pub fn basic_block(&self, context: C) -> Option>> { + let block_ptr = unsafe { BNGetFlowGraphBasicBlock(self.handle) }; + if block_ptr.is_null() { + return None; + } + Some(unsafe { BasicBlock::ref_from_raw(block_ptr, context) }) + } + + pub fn set_basic_block(&self, block: Option<&BasicBlock>) { + match block { + Some(block) => unsafe { BNSetFlowGraphBasicBlock(self.handle, block.handle) }, + None => unsafe { BNSetFlowGraphBasicBlock(self.handle, std::ptr::null_mut()) }, + } + } + + pub fn lines(&self) -> Array { + let mut count = 0; + let result = unsafe { BNGetFlowGraphNodeLines(self.handle, &mut count) }; + assert!(!result.is_null()); + unsafe { Array::new(result, count, ()) } + } + + pub fn set_lines(&self, lines: impl IntoIterator) { + // NOTE: This will create allocations and increment tag refs, we must call DisassemblyTextLine::free_raw + 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 { + DisassemblyTextLine::free_raw(raw_line); + } + } + } + + /// Returns the graph position of the node in X, Y form. + pub fn position(&self) -> (i32, i32) { + let pos_x = unsafe { BNGetFlowGraphNodeX(self.handle) }; + let pos_y = unsafe { BNGetFlowGraphNodeY(self.handle) }; + (pos_x, pos_y) + } + + /// Sets the graph position of the node. + pub fn set_position(&self, x: i32, y: i32) { + unsafe { BNFlowGraphNodeSetX(self.handle, x) }; + unsafe { BNFlowGraphNodeSetY(self.handle, y) }; + } + + pub fn highlight_color(&self) -> HighlightColor { + let raw = unsafe { BNGetFlowGraphNodeHighlight(self.handle) }; + HighlightColor::from(raw) + } + + pub fn set_highlight_color(&self, highlight: HighlightColor) { + unsafe { BNSetFlowGraphNodeHighlight(self.handle, highlight.into()) }; + } + + pub fn incoming_edges(&self) -> Array { + let mut count = 0; + let result = unsafe { BNGetFlowGraphNodeIncomingEdges(self.handle, &mut count) }; + assert!(!result.is_null()); + unsafe { Array::new(result, count, ()) } + } + + pub fn outgoing_edges(&self) -> Array { + let mut count = 0; + let result = unsafe { BNGetFlowGraphNodeOutgoingEdges(self.handle, &mut count) }; + assert!(!result.is_null()); + unsafe { Array::new(result, count, ()) } + } + + /// Connects two flow graph nodes with an edge. + pub fn add_outgoing_edge( + &self, + type_: BranchType, + target: &FlowGraphNode, + edge_style: EdgeStyle, + ) { + unsafe { + BNAddFlowGraphNodeOutgoingEdge(self.handle, type_, target.handle, edge_style.into()) + } + } +} + +impl Debug for FlowGraphNode { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + f.debug_struct("FlowGraphNode") + .field("lines", &self.lines().to_vec()) + .finish() + } +} + +unsafe impl RefCountable for FlowGraphNode { + unsafe fn inc_ref(handle: &Self) -> Ref { + Ref::new(Self { + handle: BNNewFlowGraphNodeReference(handle.handle), + }) + } + + unsafe fn dec_ref(handle: &Self) { + BNFreeFlowGraphNode(handle.handle); + } +} + +impl ToOwned for FlowGraphNode { + type Owned = Ref; + + fn to_owned(&self) -> Self::Owned { + unsafe { RefCountable::inc_ref(self) } + } +} + +impl CoreArrayProvider for FlowGraphNode { + type Raw = *mut BNFlowGraphNode; + type Context = (); + type Wrapped<'a> = Guard<'a, FlowGraphNode>; +} + +unsafe impl CoreArrayProviderInner for FlowGraphNode { + unsafe fn free(raw: *mut Self::Raw, count: usize, _: &Self::Context) { + BNFreeFlowGraphNodeList(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) + } +} diff --git a/rust/src/function.rs b/rust/src/function.rs index 7990f109a9..60c8970c3d 100644 --- a/rust/src/function.rs +++ b/rust/src/function.rs @@ -40,17 +40,19 @@ pub use binaryninjacore_sys::BNHighlightStandardColor as HighlightStandardColor; use crate::architecture::RegisterId; use crate::confidence::Conf; use crate::high_level_il::HighLevelILFunction; -use crate::low_level_il::{LiftedILFunction, RegularLowLevelILFunction}; +use crate::language_representation::CoreLanguageRepresentationFunction; +use crate::low_level_il::LowLevelILRegularFunction; use crate::medium_level_il::MediumLevelILFunction; use crate::variable::{ IndirectBranchInfo, MergedVariable, NamedVariableWithType, RegisterValue, RegisterValueType, StackVariableReference, Variable, }; use crate::workflow::Workflow; +use std::ffi::CStr; use std::fmt::{Debug, Formatter}; use std::ptr::NonNull; use std::time::Duration; -use std::{ffi::c_char, hash::Hash, ops::Range}; +use std::{hash::Hash, ops::Range}; /// Used to describe a location within a [`Function`]. #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] @@ -259,7 +261,7 @@ impl FunctionViewType { } pub(crate) fn free_raw(value: BNFunctionViewType) { - let _ = unsafe { BnString::from_raw(value.name as *mut _) }; + unsafe { BnString::free_raw(value.name as *mut _) }; } } @@ -368,15 +370,15 @@ impl Function { } } - pub fn comment(&self) -> BnString { - unsafe { BnString::from_raw(BNGetFunctionComment(self.handle)) } + pub fn comment(&self) -> String { + unsafe { BnString::into_string(BNGetFunctionComment(self.handle)) } } - pub fn set_comment(&self, comment: S) { - let raw = comment.into_bytes_with_nul(); + pub fn set_comment(&self, comment: &str) { + let raw = comment.to_cstr(); unsafe { - BNSetFunctionComment(self.handle, raw.as_ref().as_ptr() as *mut _); + BNSetFunctionComment(self.handle, raw.as_ptr()); } } @@ -390,15 +392,15 @@ impl Function { unsafe { BNSetUserFunctionCanReturn(self.handle, &mut bool_with_confidence) } } - pub fn comment_at(&self, addr: u64) -> BnString { - unsafe { BnString::from_raw(BNGetCommentForAddress(self.handle, addr)) } + pub fn comment_at(&self, addr: u64) -> String { + unsafe { BnString::into_string(BNGetCommentForAddress(self.handle, addr)) } } - pub fn set_comment_at(&self, addr: u64, comment: S) { - let raw = comment.into_bytes_with_nul(); + pub fn set_comment_at(&self, addr: u64, comment: &str) { + let raw = comment.to_cstr(); unsafe { - BNSetCommentForAddress(self.handle, addr, raw.as_ref().as_ptr() as *mut _); + BNSetCommentForAddress(self.handle, addr, raw.as_ptr()); } } @@ -459,11 +461,11 @@ impl Function { unsafe { Array::new(lines, count, ()) } } - pub fn variable_name(&self, var: &Variable) -> BnString { + pub fn variable_name(&self, var: &Variable) -> String { unsafe { let raw_var = BNVariable::from(var); let raw_name = BNGetVariableName(self.handle, &raw_var); - BnString::from_raw(raw_name) + BnString::into_string(raw_name) } } @@ -476,6 +478,34 @@ impl Function { } } + /// Get the language representation of the function. + /// + /// * `language` - The language representation, ex. "Pseudo C". + pub fn language_representation( + &self, + language: &str, + ) -> Option> { + let lang_name = language.to_cstr(); + let repr = unsafe { BNGetFunctionLanguageRepresentation(self.handle, lang_name.as_ptr()) }; + NonNull::new(repr) + .map(|handle| unsafe { CoreLanguageRepresentationFunction::ref_from_raw(handle) }) + } + + /// Get the language representation of the function, if available. + /// + /// * `language` - The language representation, ex. "Pseudo C". + pub fn language_representation_if_available( + &self, + language: &str, + ) -> Option> { + let lang_name = language.to_cstr(); + let repr = unsafe { + BNGetFunctionLanguageRepresentationIfAvailable(self.handle, lang_name.as_ptr()) + }; + NonNull::new(repr) + .map(|handle| unsafe { CoreLanguageRepresentationFunction::ref_from_raw(handle) }) + } + pub fn high_level_il(&self, full_ast: bool) -> Result, ()> { unsafe { let hlil_ptr = BNGetFunctionHighLevelIL(self.handle); @@ -529,45 +559,38 @@ impl Function { } } - pub fn low_level_il(&self) -> Result>, ()> { + pub fn low_level_il(&self) -> Result, ()> { unsafe { let llil_ptr = BNGetFunctionLowLevelIL(self.handle); match llil_ptr.is_null() { - false => Ok(RegularLowLevelILFunction::ref_from_raw( - self.arch(), - llil_ptr, - )), + false => Ok(LowLevelILRegularFunction::ref_from_raw(llil_ptr)), true => Err(()), } } } - pub fn low_level_il_if_available( - &self, - ) -> Option>> { + pub fn low_level_il_if_available(&self) -> Option> { let llil_ptr = unsafe { BNGetFunctionLowLevelILIfAvailable(self.handle) }; match llil_ptr.is_null() { - false => { - Some(unsafe { RegularLowLevelILFunction::ref_from_raw(self.arch(), llil_ptr) }) - } + false => Some(unsafe { LowLevelILRegularFunction::ref_from_raw(llil_ptr) }), true => None, } } - pub fn lifted_il(&self) -> Result>, ()> { + pub fn lifted_il(&self) -> Result, ()> { unsafe { let llil_ptr = BNGetFunctionLiftedIL(self.handle); match llil_ptr.is_null() { - false => Ok(LiftedILFunction::ref_from_raw(self.arch(), llil_ptr)), + false => Ok(LowLevelILRegularFunction::ref_from_raw(llil_ptr)), true => Err(()), } } } - pub fn lifted_il_if_available(&self) -> Option>> { + pub fn lifted_il_if_available(&self) -> Option> { let llil_ptr = unsafe { BNGetFunctionLiftedILIfAvailable(self.handle) }; match llil_ptr.is_null() { - false => Some(unsafe { LiftedILFunction::ref_from_raw(self.arch(), llil_ptr) }), + false => Some(unsafe { LowLevelILRegularFunction::ref_from_raw(llil_ptr) }), true => None, } } @@ -1110,10 +1133,10 @@ impl Function { /// let crash = bv.create_tag_type("Crashes", "🎯"); /// fun.add_tag(&crash, "Nullpointer dereference", Some(0x1337), false, None); /// ``` - pub fn add_tag( + pub fn add_tag( &self, tag_type: &TagType, - data: S, + data: &str, addr: Option, user: bool, arch: Option, @@ -1714,12 +1737,12 @@ impl Function { operand: usize, display_type: IntegerDisplayType, arch: Option, - enum_display_typeid: Option, + enum_display_typeid: Option<&str>, ) { let arch = arch.unwrap_or_else(|| self.arch()); - let enum_display_typeid = enum_display_typeid.map(BnStrCompatible::into_bytes_with_nul); + let enum_display_typeid = enum_display_typeid.map(IntoCStr::to_cstr); let enum_display_typeid_ptr = enum_display_typeid - .map(|x| x.as_ref().as_ptr() as *const c_char) + .map(|x| x.as_ptr()) .unwrap_or(std::ptr::null()); unsafe { BNSetIntegerConstantDisplayType( @@ -1748,10 +1771,10 @@ impl Function { value: u64, operand: usize, arch: Option, - ) -> BnString { + ) -> String { let arch = arch.unwrap_or_else(|| self.arch()); unsafe { - BnString::from_raw(BNGetIntegerConstantDisplayTypeEnumerationType( + BnString::into_string(BNGetIntegerConstantDisplayTypeEnumerationType( self.handle, arch.handle, instr_addr, @@ -1773,7 +1796,7 @@ impl Function { value: u64, operand: usize, arch: Option, - ) -> (IntegerDisplayType, BnString) { + ) -> (IntegerDisplayType, String) { let arch = arch.unwrap_or_else(|| self.arch()); let name = self.int_enum_display_typeid(instr_addr, value, operand, Some(arch)); let display = self.int_display_type(instr_addr, value, operand, Some(arch)); @@ -1929,7 +1952,7 @@ impl Function { addr: u64, offset: i64, arch: Option, - ) -> Option<(Variable, BnString, Conf>)> { + ) -> Option { let arch = arch.unwrap_or_else(|| self.arch()); let mut found_value = BNVariableNameAndType::default(); let found = unsafe { @@ -1944,13 +1967,7 @@ impl Function { if !found { return None; } - 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_) }, - found_value.typeConfidence, - ); - Some((var, name, var_type)) + Some(NamedVariableWithType::from_owned_raw(found_value)) } pub fn stack_var_at_frame_offset_after_instruction( @@ -1958,7 +1975,7 @@ impl Function { addr: u64, offset: i64, arch: Option, - ) -> Option<(Variable, BnString, Conf>)> { + ) -> Option { let arch = arch.unwrap_or_else(|| self.arch()); let mut found_value = BNVariableNameAndType::default(); let found = unsafe { @@ -1973,13 +1990,7 @@ impl Function { if !found { return None; } - 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_) }, - found_value.typeConfidence, - ); - Some((var, name, var_type)) + Some(NamedVariableWithType::from_owned_raw(found_value)) } pub fn stack_variables_referenced_by( @@ -2144,7 +2155,7 @@ impl Function { /// 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::medium_level_il::MediumLevelILInstruction::get_split_var_for_definition] at the definition site. + /// [crate::medium_level_il::MediumLevelILInstruction::split_var_for_definition] at the definition site. /// /// This function is not meant to split variables that have been previously merged. Use /// [Function::unmerge_variables] to split previously merged variables. @@ -2167,7 +2178,7 @@ impl Function { /// 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::medium_level_il::MediumLevelILInstruction::get_split_var_for_definition] at the definition site. + /// [crate::medium_level_il::MediumLevelILInstruction::split_var_for_definition] at the definition site. /// /// * `var` - variable to unsplit pub fn unsplit_variable(&self, var: &Variable) { @@ -2203,17 +2214,17 @@ impl Function { /// /// * `name` - Name of the debug report pub fn request_debug_report(&self, name: &str) { - const DEBUG_REPORT_ALIAS: &[(&str, &str)] = &[ - ("stack", "stack_adjust_graph\x00"), - ("mlil", "mlil_translator\x00"), - ("hlil", "high_level_il\x00"), + const DEBUG_REPORT_ALIAS: &[(&str, &CStr)] = &[ + ("stack", c"stack_adjust_graph"), + ("mlil", c"mlil_translator"), + ("hlil", c"high_level_il"), ]; if let Some(alias_idx) = DEBUG_REPORT_ALIAS .iter() .position(|(alias, _value)| *alias == name) { - let name = DEBUG_REPORT_ALIAS[alias_idx].1.as_ptr() as *const c_char; + let name = DEBUG_REPORT_ALIAS[alias_idx].1.as_ptr(); unsafe { BNRequestFunctionDebugReport(self.handle, name) } } else { let name = std::ffi::CString::new(name.to_string()).unwrap(); @@ -2355,8 +2366,8 @@ impl Function { /// Returns a string representing the provenance. This portion of the API /// is under development. Currently the provenance information is /// undocumented, not persistent, and not saved to a database. - pub fn provenance(&self) -> BnString { - unsafe { BnString::from_raw(BNGetProvenanceString(self.handle)) } + pub fn provenance(&self) -> String { + unsafe { BnString::into_string(BNGetProvenanceString(self.handle)) } } /// Get registers that are used for the return value @@ -2539,23 +2550,39 @@ pub struct PerformanceInfo { pub seconds: Duration, } -impl From for PerformanceInfo { - fn from(value: BNPerformanceInfo) -> Self { +#[allow(unused)] +impl PerformanceInfo { + pub fn new(name: String, seconds: Duration) -> Self { Self { - name: unsafe { BnString::from_raw(value.name) }.to_string(), - seconds: Duration::from_secs_f64(value.seconds), + name: name.to_string(), + seconds, } } -} -impl From<&BNPerformanceInfo> for PerformanceInfo { - fn from(value: &BNPerformanceInfo) -> Self { + pub(crate) fn from_raw(value: &BNPerformanceInfo) -> Self { Self { - // TODO: Name will be freed by this. FIX! - name: unsafe { BnString::from_raw(value.name) }.to_string(), + name: raw_to_string(value.name as *mut _).unwrap(), seconds: Duration::from_secs_f64(value.seconds), } } + + pub(crate) fn from_owned_raw(value: BNPerformanceInfo) -> Self { + let owned = Self::from_raw(&value); + Self::free_raw(value); + owned + } + + pub(crate) fn into_raw(value: Self) -> BNPerformanceInfo { + let bn_name = BnString::new(value.name); + BNPerformanceInfo { + name: BnString::into_raw(bn_name), + seconds: value.seconds.as_secs_f64(), + } + } + + pub(crate) fn free_raw(value: BNPerformanceInfo) { + unsafe { BnString::free_raw(value.name) }; + } } impl CoreArrayProvider for PerformanceInfo { @@ -2570,8 +2597,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) + Self::from_raw(raw) } } @@ -2787,7 +2813,7 @@ impl Default for HighlightColor { #[derive(Clone, Debug, Hash, Eq, PartialEq)] pub struct Comment { pub addr: u64, - pub comment: BnString, + pub comment: String, } impl CoreArrayProvider for Comment { diff --git a/rust/src/function_recognizer.rs b/rust/src/function_recognizer.rs index 64e33806ba..0d85d827af 100644 --- a/rust/src/function_recognizer.rs +++ b/rust/src/function_recognizer.rs @@ -1,5 +1,5 @@ use crate::low_level_il::function::LowLevelILFunction; -use crate::low_level_il::RegularLowLevelILFunction; +use crate::low_level_il::LowLevelILRegularFunction; use crate::medium_level_il::MediumLevelILFunction; use crate::{architecture::CoreArchitecture, binary_view::BinaryView, function::Function}; use binaryninjacore_sys::*; @@ -10,7 +10,7 @@ pub trait FunctionRecognizer { &self, _bv: &BinaryView, _func: &Function, - _llil: &RegularLowLevelILFunction, + _llil: &LowLevelILRegularFunction, ) -> bool { false } @@ -49,7 +49,7 @@ where let context = unsafe { &*(ctxt as *mut FunctionRecognizerHandlerContext) }; let bv = unsafe { BinaryView::from_raw(bv).to_owned() }; let func = unsafe { Function::from_raw(func).to_owned() }; - let llil = unsafe { LowLevelILFunction::from_raw(func.arch(), llil).to_owned() }; + let llil = unsafe { LowLevelILFunction::from_raw(llil).to_owned() }; context.recognizer.recognize_low_level_il(&bv, &func, &llil) } diff --git a/rust/src/headless.rs b/rust/src/headless.rs index 3ea969473f..5910cd85e6 100644 --- a/rust/src/headless.rs +++ b/rust/src/headless.rs @@ -14,14 +14,15 @@ use crate::{ binary_view, bundled_plugin_directory, enterprise, is_license_validated, is_main_thread, - license_path, set_bundled_plugin_directory, set_license, string::IntoJson, + is_ui_enabled, license_path, set_bundled_plugin_directory, set_license, string::IntoJson, }; use std::io; use std::path::{Path, PathBuf}; +use std::sync::atomic::AtomicUsize; use std::sync::atomic::Ordering::SeqCst; -use std::sync::atomic::{AtomicBool, AtomicUsize}; use thiserror::Error; +use crate::enterprise::EnterpriseCheckoutStatus; use crate::main_thread::{MainThreadAction, MainThreadHandler}; use crate::progress::ProgressCallback; use crate::rc::Ref; @@ -32,12 +33,9 @@ use std::thread::JoinHandle; use std::time::Duration; static MAIN_THREAD_HANDLE: Mutex>> = Mutex::new(None); -/// Prevent two threads from calling init() at the same time -static INIT_LOCK: Mutex<()> = Mutex::new(()); -/// Used to prevent shutting down Binary Ninja if there are other [`Session`]'s. + +/// Used to prevent shutting down Binary Ninja if there is another active [`Session`]. static SESSION_COUNT: AtomicUsize = AtomicUsize::new(0); -/// If we checked out a floating license and should release it on shutdown -static NEED_LICENSE_RELEASE: AtomicBool = AtomicBool::new(false); #[derive(Error, Debug)] pub enum InitializationError { @@ -49,15 +47,15 @@ pub enum InitializationError { InvalidLicense, #[error("no license could located, please see `binaryninja::set_license` for details")] NoLicenseFound, - #[error("could not acquire initialization mutex")] - InitMutex, + #[error("initialization already managed by ui")] + AlreadyManaged, } /// 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`]. +/// The preferred method for core initialization is [`Session`], use that instead of this where possible. /// /// If you need to customize initialization, use [`init_with_opts`] instead. pub fn init() -> Result<(), InitializationError> { @@ -67,15 +65,12 @@ pub fn init() -> 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. ⚠️ +/// This function does _NOT_ release floating licenses; it is expected that you call [`enterprise::release_license`]. pub fn shutdown() { match crate::product().as_str() { "Binary Ninja Enterprise Client" | "Binary Ninja Ultimate" => { - if NEED_LICENSE_RELEASE.load(SeqCst) { - enterprise::release_license() - } + // By default, we do not release floating licenses. + enterprise::release_license(false) } _ => {} } @@ -91,9 +86,9 @@ pub fn is_shutdown_requested() -> bool { 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. + /// 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. + /// 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. /// @@ -105,11 +100,11 @@ pub struct InitializationOptions { pub bundled_plugin_directory: PathBuf, /// Whether to initialize user plugins. /// - /// Set this to false if your use might be impacted by a user installed plugin. + /// Set this to false if your use might be impacted by a user-installed plugin. pub user_plugins: bool, /// Whether to initialize repo plugins. /// - /// Set this to false if your use might be impacted by a repo installed plugin. + /// Set this to false if your use might be impacted by a repo-installed plugin. pub repo_plugins: bool, } @@ -129,9 +124,9 @@ impl InitializationOptions { self } - /// If you need to make sure that you do not check out a license set this to false. + /// 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. + /// This is really only useful if you have a headless license but are using an enterprise-enabled core. pub fn with_license_checkout(mut self, should_checkout: bool) -> Self { self.checkout_license = should_checkout; self @@ -151,13 +146,13 @@ impl InitializationOptions { self } - /// Set this to false if your use might be impacted by a user installed plugin. + /// Set this to false if your use might be impacted by a user-installed plugin. pub fn with_user_plugins(mut self, should_initialize: bool) -> Self { self.user_plugins = should_initialize; self } - /// Set this to false if your use might be impacted by a repo installed plugin. + /// Set this to false if your use might be impacted by a repo-installed plugin. pub fn with_repo_plugins(mut self, should_initialize: bool) -> Self { self.repo_plugins = should_initialize; self @@ -181,7 +176,11 @@ impl Default for InitializationOptions { /// 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 is_ui_enabled() { + return Err(InitializationError::AlreadyManaged); + } + + // 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() { let mut main_thread_handle = MAIN_THREAD_HANDLE.lock().unwrap(); if main_thread_handle.is_none() { @@ -192,7 +191,7 @@ pub fn init_with_opts(options: InitializationOptions) -> Result<(), Initializati let join_handle = std::thread::Builder::new() .name("HeadlessMainThread".to_string()) .spawn(move || { - // We must register the main thread within said thread. + // We must register the main thread within the thread. main_thread.register(); while let Ok(action) = receiver.recv() { action.execute(); @@ -204,19 +203,16 @@ pub fn init_with_opts(options: InitializationOptions) -> Result<(), Initializati } } - match crate::product().as_str() { - "Binary Ninja Enterprise Client" | "Binary Ninja Ultimate" => { - if options.checkout_license { - // We are allowed to check out a license, so do it! - if enterprise::checkout_license(options.floating_license_duration)? { - NEED_LICENSE_RELEASE.store(true, SeqCst); - } - } + if is_enterprise_product() && options.checkout_license { + // We are allowed to check out a license, so do it! + let checkout_status = enterprise::checkout_license(options.floating_license_duration)?; + if checkout_status == EnterpriseCheckoutStatus::AlreadyManaged { + // Should be impossible, but just in case. + return Err(InitializationError::AlreadyManaged); } - _ => {} } - if let Some(license) = options.license { + if let Some(license) = &options.license { // We were given a license override, use it! set_license(Some(license)); } @@ -232,7 +228,7 @@ pub fn init_with_opts(options: InitializationOptions) -> Result<(), Initializati } if !is_license_validated() { - // Unfortunately you must have a valid license to use Binary Ninja. + // Unfortunately, you must have a valid license to use Binary Ninja. Err(InitializationError::InvalidLicense) } else { Ok(()) @@ -258,20 +254,27 @@ impl MainThreadHandler for HeadlessMainThreadSender { } } +fn is_enterprise_product() -> bool { + matches!( + crate::product().as_str(), + "Binary Ninja Enterprise Client" | "Binary Ninja Ultimate" + ) +} + #[derive(Debug, PartialEq, Eq, Clone, Copy, Hash)] 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, - /// The license is retrieved using keychain credentials for `BN_ENTERPRISE_USERNAME`. + /// The license is retrieved using keychain credentials, this is only available for floating enterprise licenses. Keychain, } /// 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`]. +/// you should look into setting the `BN_LICENSE` environment variable or calling [`set_license`]. pub fn license_location() -> Option { match std::env::var("BN_LICENSE") { Ok(_) => Some(LicenseLocation::EnvironmentVariable), @@ -279,33 +282,26 @@ pub fn license_location() -> Option { // Check the license_path to see if a file is there. if license_path().exists() { Some(LicenseLocation::File) - } else { - // Check to see if we might be authorizing with enterprise - if crate::product().as_str() == "Binary Ninja Enterprise Client" - || crate::product().as_str() == "Binary Ninja Ultimate" - { - // If we can't initialize enterprise, we probably are missing enterprise.server.url - // and our license surely is not valid. - if !enterprise::is_server_initialized() && !enterprise::initialize_server() { - return None; - } - // If Enterprise thinks we are using a floating license, then report it will be in the keychain - if enterprise::is_server_floating_license() { - Some(LicenseLocation::Keychain) - } else { - None - } - } else { - None + } else if is_enterprise_product() { + // If we can't initialize enterprise, we probably are missing enterprise.server.url + // and our license surely is not valid. + if !enterprise::is_server_initialized() && !enterprise::initialize_server() { + return None; } + // If Enterprise thinks we are using a floating license, then report it will be in the keychain + enterprise::is_server_floating_license().then_some(LicenseLocation::Keychain) + } else { + // If we are not using an enterprise license, we can't check the keychain, nowhere else to check. + 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. +#[derive(Debug, Default, Clone, PartialEq, Eq, Hash)] pub struct Session { - index: usize, + license_duration: Option, } impl Session { @@ -313,10 +309,8 @@ impl Session { /// /// This is required so that we can keep track of the [`SESSION_COUNT`]. fn registered_session() -> Self { - let previous_count = SESSION_COUNT.fetch_add(1, SeqCst); - Self { - index: previous_count, - } + let _previous_count = SESSION_COUNT.fetch_add(1, SeqCst); + Self::default() } /// Before calling new you must make sure that the license is retrievable, otherwise the core won't be able to initialize. @@ -326,19 +320,7 @@ impl Session { pub fn new() -> Result { if license_location().is_some() { // We were able to locate a license, continue with initialization. - - // Grab the lock before initialization to prevent another thread from initializing - // and racing the call to BNInitPlugins. - let _lock = INIT_LOCK - .lock() - .map_err(|_| InitializationError::InitMutex)?; - let session = Self::registered_session(); - // Since this whole section is locked, we're guaranteed to be index 0 if we're first. - // Only the first thread hitting this should be allowed to call BNInitPlugins - if session.index == 0 { - init()?; - } - Ok(session) + Self::new_with_opts(InitializationOptions::default()) } else { // There was no license that could be automatically retrieved, you must call [Self::new_with_license]. Err(InitializationError::NoLicenseFound) @@ -348,19 +330,10 @@ impl Session { /// Initialize with options, the same rules apply as [`Session::new`], see [`InitializationOptions::default`] for the regular options passed. /// /// 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. + /// can discover by itself, therefore, it is expected that you know where your license is when calling this directly. pub fn new_with_opts(options: InitializationOptions) -> Result { - // Grab the lock before initialization to prevent another thread from initializing - // and racing the call to BNInitPlugins. - let _lock = INIT_LOCK - .lock() - .map_err(|_| InitializationError::InitMutex)?; let session = Self::registered_session(); - // Since this whole section is locked, we're guaranteed to be index 0 if we're first. - // Only the first thread hitting this should be allowed to call BNInitPlugins - if session.index == 0 { - init_with_opts(options)?; - } + init_with_opts(options)?; Ok(session) } @@ -457,7 +430,7 @@ impl Drop for Session { fn drop(&mut self) { let previous_count = SESSION_COUNT.fetch_sub(1, SeqCst); if previous_count == 1 { - // We were the last session, therefor we can safely shut down. + // We were the last session, therefore, we can safely shut down. shutdown(); } } diff --git a/rust/src/high_level_il.rs b/rust/src/high_level_il.rs index adb44a7eee..8d02720dc3 100644 --- a/rust/src/high_level_il.rs +++ b/rust/src/high_level_il.rs @@ -4,6 +4,7 @@ mod function; mod instruction; mod lift; pub mod operation; +pub mod token_emitter; pub use self::block::*; pub use self::function::*; diff --git a/rust/src/high_level_il/function.rs b/rust/src/high_level_il/function.rs index 0813ad0458..a65a244c84 100644 --- a/rust/src/high_level_il/function.rs +++ b/rust/src/high_level_il/function.rs @@ -15,12 +15,17 @@ pub struct HighLevelILFunction { } impl HighLevelILFunction { + pub(crate) unsafe fn from_raw(handle: *mut BNHighLevelILFunction, full_ast: bool) -> Self { + debug_assert!(!handle.is_null()); + Self { handle, full_ast } + } + pub(crate) unsafe fn ref_from_raw( handle: *mut BNHighLevelILFunction, full_ast: bool, ) -> Ref { debug_assert!(!handle.is_null()); - Self { handle, full_ast }.to_owned() + Ref::new(Self { handle, full_ast }) } pub fn instruction_from_index( diff --git a/rust/src/high_level_il/instruction.rs b/rust/src/high_level_il/instruction.rs index 00900de716..67225eea15 100644 --- a/rust/src/high_level_il/instruction.rs +++ b/rust/src/high_level_il/instruction.rs @@ -7,7 +7,6 @@ use super::{HighLevelILFunction, HighLevelILLiftedInstruction, HighLevelILLifted use crate::architecture::{CoreIntrinsic, IntrinsicId}; use crate::confidence::Conf; use crate::disassembly::DisassemblyTextLine; -use crate::operand_iter::OperandIter; use crate::rc::{Array, CoreArrayProvider, CoreArrayProviderInner, Ref}; use crate::types::Type; use crate::variable::{ConstantData, RegisterValue, SSAVariable, Variable}; @@ -552,6 +551,38 @@ impl HighLevelILInstruction { } } + fn get_operand_list(&self, operand_idx: usize) -> Vec { + let mut count = 0; + let raw_list_ptr = unsafe { + BNHighLevelILGetOperandList( + self.function.handle, + self.expr_index.0, + operand_idx, + &mut count, + ) + }; + assert!(!raw_list_ptr.is_null()); + let list = unsafe { std::slice::from_raw_parts(raw_list_ptr, count).to_vec() }; + unsafe { BNHighLevelILFreeOperandList(raw_list_ptr) }; + list + } + + fn get_ssa_var_list(&self, operand_idx: usize) -> Vec { + self.get_operand_list(operand_idx) + .chunks(2) + .map(|chunk| (Variable::from_identifier(chunk[0]), chunk[1] as usize)) + .map(|(var, version)| SSAVariable::new(var, version)) + .collect() + } + + fn get_expr_list(&self, operand_idx: usize) -> Vec { + self.get_operand_list(operand_idx) + .into_iter() + .map(|val| HighLevelInstructionIndex(val as usize)) + .filter_map(|idx| self.function.instruction_from_expr_index(idx)) + .collect() + } + pub fn lift(&self) -> HighLevelILLiftedInstruction { use HighLevelILInstructionKind::*; use HighLevelILLiftedInstructionKind as Lifted; @@ -630,7 +661,11 @@ impl HighLevelILInstruction { src: self.lift_operand(op.src), }), AssignUnpack(op) => Lifted::AssignUnpack(LiftedAssignUnpack { - dest: self.lift_instruction_list(op.first_dest, op.num_dests), + dest: self + .get_expr_list(0) + .iter() + .map(|expr| expr.lift()) + .collect(), src: self.lift_operand(op.src), }), AssignMemSsa(op) => Lifted::AssignMemSsa(LiftedAssignMemSsa { @@ -640,26 +675,42 @@ impl HighLevelILInstruction { src_memory: op.src_memory, }), AssignUnpackMemSsa(op) => Lifted::AssignUnpackMemSsa(LiftedAssignUnpackMemSsa { - dest: self.lift_instruction_list(op.first_dest, op.num_dests), + dest: self + .get_expr_list(0) + .iter() + .map(|expr| expr.lift()) + .collect(), dest_memory: op.dest_memory, src: self.lift_operand(op.src), src_memory: op.src_memory, }), - Block(op) => Lifted::Block(LiftedBlock { - body: self.lift_instruction_list(op.first_param, op.num_params), + Block(_op) => Lifted::Block(LiftedBlock { + body: self + .get_expr_list(0) + .iter() + .map(|expr| expr.lift()) + .collect(), }), Call(op) => Lifted::Call(self.lift_call(op)), Tailcall(op) => Lifted::Tailcall(self.lift_call(op)), CallSsa(op) => Lifted::CallSsa(LiftedCallSsa { dest: self.lift_operand(op.dest), - params: self.lift_instruction_list(op.first_param, op.num_params), + params: self + .get_expr_list(1) + .iter() + .map(|expr| expr.lift()) + .collect(), dest_memory: op.dest_memory, src_memory: op.src_memory, }), Case(op) => Lifted::Case(LiftedCase { - values: self.lift_instruction_list(op.first_value, op.num_values), + values: self + .get_expr_list(0) + .iter() + .map(|expr| expr.lift()) + .collect(), body: self.lift_operand(op.body), }), Const(op) => Lifted::Const(op), @@ -740,7 +791,11 @@ impl HighLevelILInstruction { IntrinsicId(op.intrinsic), ) .expect("Invalid intrinsic"), - params: self.lift_instruction_list(op.first_param, op.num_params), + params: self + .get_expr_list(1) + .iter() + .map(|expr| expr.lift()) + .collect(), }), IntrinsicSsa(op) => Lifted::IntrinsicSsa(LiftedIntrinsicSsa { intrinsic: CoreIntrinsic::new( @@ -748,7 +803,11 @@ impl HighLevelILInstruction { IntrinsicId(op.intrinsic), ) .expect("Invalid intrinsic"), - params: self.lift_instruction_list(op.first_param, op.num_params), + params: self + .get_expr_list(1) + .iter() + .map(|expr| expr.lift()) + .collect(), dest_memory: op.dest_memory, src_memory: op.src_memory, }), @@ -757,10 +816,14 @@ impl HighLevelILInstruction { }), MemPhi(op) => Lifted::MemPhi(LiftedMemPhi { dest: op.dest, - src: OperandIter::new(&*self.function, op.first_src, op.num_srcs).collect(), + src: self.get_operand_list(1), }), - Ret(op) => Lifted::Ret(LiftedRet { - src: self.lift_instruction_list(op.first_src, op.num_srcs), + Ret(_op) => Lifted::Ret(LiftedRet { + src: self + .get_expr_list(0) + .iter() + .map(|expr| expr.lift()) + .collect(), }), Split(op) => Lifted::Split(LiftedSplit { high: self.lift_operand(op.high), @@ -771,13 +834,25 @@ impl HighLevelILInstruction { Switch(op) => Lifted::Switch(LiftedSwitch { condition: self.lift_operand(op.condition), default: self.lift_operand(op.default), - cases: self.lift_instruction_list(op.first_case, op.num_cases), + cases: self + .get_expr_list(2) + .iter() + .map(|expr| expr.lift()) + .collect(), }), - Syscall(op) => Lifted::Syscall(LiftedSyscall { - params: self.lift_instruction_list(op.first_param, op.num_params), + Syscall(_op) => Lifted::Syscall(LiftedSyscall { + params: self + .get_expr_list(0) + .iter() + .map(|expr| expr.lift()) + .collect(), }), SyscallSsa(op) => Lifted::SyscallSsa(LiftedSyscallSsa { - params: self.lift_instruction_list(op.first_param, op.num_params), + params: self + .get_expr_list(0) + .iter() + .map(|expr| expr.lift()) + .collect(), dest_memory: op.dest_memory, src_memory: op.src_memory, }), @@ -794,9 +869,7 @@ impl HighLevelILInstruction { }), VarPhi(op) => Lifted::VarPhi(LiftedVarPhi { dest: op.dest, - src: OperandIter::new(&*self.function, op.first_src, op.num_srcs) - .ssa_vars() - .collect(), + src: self.get_ssa_var_list(2), }), VarSsa(op) => Lifted::VarSsa(op), @@ -907,8 +980,9 @@ impl HighLevelILInstruction { fn lift_call(&self, op: Call) -> LiftedCall { LiftedCall { dest: self.lift_operand(op.dest), - params: OperandIter::new(&*self.function, op.first_param, op.num_params) - .exprs() + params: self + .get_expr_list(1) + .iter() .map(|expr| expr.lift()) .collect(), } @@ -936,17 +1010,6 @@ impl HighLevelILInstruction { member_index: op.member_index, } } - - fn lift_instruction_list( - &self, - first_instruction: usize, - num_instructions: usize, - ) -> Vec { - OperandIter::new(&*self.function, first_instruction, num_instructions) - .exprs() - .map(|expr| expr.lift()) - .collect() - } } impl CoreArrayProvider for HighLevelILInstruction { diff --git a/rust/src/high_level_il/operation.rs b/rust/src/high_level_il/operation.rs index 340b85a75d..9c2182278d 100644 --- a/rust/src/high_level_il/operation.rs +++ b/rust/src/high_level_il/operation.rs @@ -6,7 +6,7 @@ use super::HighLevelILLiftedInstruction; use crate::architecture::CoreIntrinsic; use crate::function::Function; use crate::rc::Ref; -use crate::string::{BnStrCompatible, BnString}; +use crate::string::{BnString, IntoCStr}; use crate::variable::{ConstantData, SSAVariable, Variable}; #[derive(Clone, PartialEq, Eq)] @@ -16,12 +16,12 @@ pub struct GotoLabel { } impl GotoLabel { - pub fn name(&self) -> BnString { - unsafe { BnString::from_raw(BNGetGotoLabelName(self.function.handle, self.target)) } + pub fn name(&self) -> String { + unsafe { BnString::into_string(BNGetGotoLabelName(self.function.handle, self.target)) } } - fn set_name(&self, name: S) { - let raw = name.into_bytes_with_nul(); + fn set_name(&self, name: &str) { + let raw = name.to_cstr(); unsafe { BNSetUserGotoLabelName( self.function.handle, @@ -323,11 +323,11 @@ pub struct LiftedLabel { } impl LiftedLabel { - pub fn name(&self) -> BnString { + pub fn name(&self) -> String { self.target.name() } - pub fn set_name(&self, name: S) { + pub fn set_name(&self, name: &str) { self.target.set_name(name) } } diff --git a/rust/src/high_level_il/token_emitter.rs b/rust/src/high_level_il/token_emitter.rs new file mode 100644 index 0000000000..864ee50dd0 --- /dev/null +++ b/rust/src/high_level_il/token_emitter.rs @@ -0,0 +1,400 @@ +use std::ptr::NonNull; + +use binaryninjacore_sys::*; + +use crate::disassembly::{ + DisassemblySettings, DisassemblyTextLine, InstructionTextToken, InstructionTextTokenContext, + InstructionTextTokenType, +}; +use crate::high_level_il::HighLevelILFunction; +use crate::language_representation::{OperatorPrecedence, SymbolDisplayResult, SymbolDisplayType}; +use crate::rc::{Array, Ref, RefCountable}; +use crate::variable::Variable; + +pub type ScopeType = BNScopeType; +pub type TokenEmitterExpr = BNTokenEmitterExpr; +pub type BraceRequirement = BNBraceRequirement; + +#[derive(PartialEq, Eq, Hash)] +pub struct HighLevelILTokenEmitter { + handle: NonNull, +} + +impl HighLevelILTokenEmitter { + pub(crate) unsafe fn from_raw(handle: NonNull) -> Self { + Self { handle } + } + + /// Returns the list of [`InstructionTextToken`] on the current line. + pub fn current_tokens(&self) -> Array { + let mut count = 0; + let array = + unsafe { BNHighLevelILTokenEmitterGetCurrentTokens(self.handle.as_ptr(), &mut count) }; + unsafe { Array::new(array, count, ()) } + } + + /// Returns the list of [`DisassemblyTextLine`] in the output. + pub fn lines(&self) -> Array { + let mut count = 0; + let array = unsafe { BNHighLevelILTokenEmitterGetLines(self.handle.as_ptr(), &mut count) }; + unsafe { Array::new(array, count, ()) } + } + + pub fn prepend_collapse_blank_indicator(&self) { + unsafe { BNHighLevelILTokenPrependCollapseBlankIndicator(self.handle.as_ptr()) }; + } + + pub fn prepend_collapse_indicator(&self, context: InstructionTextTokenContext, hash: u64) { + unsafe { + BNHighLevelILTokenPrependCollapseIndicator(self.handle.as_ptr(), context.into(), hash) + }; + } + + pub fn has_collapsible_regions(&self) -> bool { + unsafe { BNHighLevelILTokenEmitterHasCollapsableRegions(self.handle.as_ptr()) } + } + + pub fn set_has_collapsible_regions(&self, state: bool) { + unsafe { BNHighLevelILTokenEmitterSetHasCollapsableRegions(self.handle.as_ptr(), state) }; + } + + pub fn append(&self, token: InstructionTextToken) { + let mut raw_token = InstructionTextToken::into_raw(token); + unsafe { BNHighLevelILTokenEmitterAppend(self.handle.as_ptr(), &mut raw_token) }; + InstructionTextToken::free_raw(raw_token); + } + + /// Starts a new line in the output. + pub fn init_line(&self) { + unsafe { BNHighLevelILTokenEmitterInitLine(self.handle.as_ptr()) }; + } + + // TODO: Difference from `init_line`? + /// Starts a new line in the output. + pub fn new_line(&self) { + unsafe { BNHighLevelILTokenEmitterNewLine(self.handle.as_ptr()) }; + } + + /// Increases the indentation level by one. + pub fn increase_indent(&self) { + unsafe { BNHighLevelILTokenEmitterIncreaseIndent(self.handle.as_ptr()) }; + } + + /// Decreases the indentation level by one. + pub fn decrease_indent(&self) { + unsafe { BNHighLevelILTokenEmitterDecreaseIndent(self.handle.as_ptr()) }; + } + + /// Indicates that visual separation of scopes is desirable at the current position. + /// + /// By default, this will insert a blank line, but this can be configured by the user. + pub fn scope_separator(&self) { + unsafe { BNHighLevelILTokenEmitterScopeSeparator(self.handle.as_ptr()) }; + } + + /// Begins a new scope. Insertion of newlines and braces will be handled using the current settings. + pub fn begin_scope(&self, ty: ScopeType) { + unsafe { BNHighLevelILTokenEmitterBeginScope(self.handle.as_ptr(), ty) }; + } + + /// Ends the current scope. + /// + /// The type `ty` should be equal to what was passed to [`HighLevelILTokenEmitter::begin_scope`]. + pub fn end_scope(&self, ty: ScopeType) { + unsafe { BNHighLevelILTokenEmitterEndScope(self.handle.as_ptr(), ty) }; + } + + /// Continues the previous scope with a new associated scope. This is most commonly used for else statements. + /// + /// If `force_same_line` is true, the continuation will always be placed on the same line as the previous scope. + pub fn scope_continuation(&self, force_same_line: bool) { + unsafe { + BNHighLevelILTokenEmitterScopeContinuation(self.handle.as_ptr(), force_same_line) + }; + } + + /// Finalizes the previous scope, indicating that there are no more associated scopes. + pub fn finalize_scope(&self) { + unsafe { BNHighLevelILTokenEmitterFinalizeScope(self.handle.as_ptr()) }; + } + + /// Forces there to be no indentation for the next line. + pub fn no_indent_for_this_line(&self) { + unsafe { BNHighLevelILTokenEmitterNoIndentForThisLine(self.handle.as_ptr()) }; + } + + /// Begins a region of tokens that always have zero confidence. + pub fn begin_force_zero_confidence(&self) { + unsafe { BNHighLevelILTokenEmitterBeginForceZeroConfidence(self.handle.as_ptr()) }; + } + + /// Ends a region of tokens that always have zero confidence. + pub fn end_force_zero_confidence(&self) { + unsafe { BNHighLevelILTokenEmitterEndForceZeroConfidence(self.handle.as_ptr()) }; + } + + /// Sets the current expression. Returning the [`CurrentTokenEmitterExpr`] which when dropped + /// will restore the previously active [`TokenEmitterExpr`]. + pub fn set_current_expr(&self, expr: TokenEmitterExpr) -> CurrentTokenEmitterExpr { + let previous_expr = + unsafe { BNHighLevelILTokenEmitterSetCurrentExpr(self.handle.as_ptr(), expr) }; + CurrentTokenEmitterExpr::new(self.to_owned(), expr, previous_expr) + } + + fn restore_current_expr(&self, expr: TokenEmitterExpr) { + unsafe { BNHighLevelILTokenEmitterRestoreCurrentExpr(self.handle.as_ptr(), expr) }; + } + + /// Finalizes the outputted lines. + pub fn finalize(&self) { + unsafe { BNHighLevelILTokenEmitterFinalize(self.handle.as_ptr()) }; + } + + /// Appends `(`. + pub fn append_open_paren(&self) { + unsafe { BNHighLevelILTokenEmitterAppendOpenParen(self.handle.as_ptr()) }; + } + + /// Appends `)`. + pub fn append_close_paren(&self) { + unsafe { BNHighLevelILTokenEmitterAppendCloseParen(self.handle.as_ptr()) }; + } + + /// Appends `[`. + pub fn append_open_bracket(&self) { + unsafe { BNHighLevelILTokenEmitterAppendOpenBracket(self.handle.as_ptr()) }; + } + + /// Appends `]`. + pub fn append_close_bracket(&self) { + unsafe { BNHighLevelILTokenEmitterAppendCloseBracket(self.handle.as_ptr()) }; + } + + /// Appends `{`. + pub fn append_open_brace(&self) { + unsafe { BNHighLevelILTokenEmitterAppendOpenBrace(self.handle.as_ptr()) }; + } + + /// Appends `}`. + pub fn append_close_brace(&self) { + unsafe { BNHighLevelILTokenEmitterAppendCloseBrace(self.handle.as_ptr()) }; + } + + /// Appends `;`. + pub fn append_semicolon(&self) { + unsafe { BNHighLevelILTokenEmitterAppendSemicolon(self.handle.as_ptr()) }; + } + + /// Sets the requirement for insertion of braces around scopes in the output. + pub fn set_brace_requirement(&self, required: BraceRequirement) { + unsafe { BNHighLevelILTokenEmitterSetBraceRequirement(self.handle.as_ptr(), required) }; + } + + /// Sets whether cases within switch statements should always have braces around them. + pub fn set_braces_around_switch_cases(&self, braces: bool) { + unsafe { + BNHighLevelILTokenEmitterSetBracesAroundSwitchCases(self.handle.as_ptr(), braces) + }; + } + + /// Sets whether braces should default to being on the same line as the statement that begins the scope. + /// + /// If the user has explicitly set a preference, this setting will be ignored and the user's preference will be used instead. + pub fn set_default_braces_on_same_line(&self, same_line: bool) { + unsafe { + BNHighLevelILTokenEmitterSetDefaultBracesOnSameLine(self.handle.as_ptr(), same_line) + }; + } + + /// Sets whether omitting braces around single-line scopes is allowed. + pub fn set_simple_scope_allowed(&self, allowed: bool) { + unsafe { BNHighLevelILTokenEmitterSetSimpleScopeAllowed(self.handle.as_ptr(), allowed) }; + } + + pub fn brace_requirement(&self) -> BraceRequirement { + unsafe { BNHighLevelILTokenEmitterGetBraceRequirement(self.handle.as_ptr()) } + } + + pub fn has_braces_around_switch_cases(&self) -> bool { + unsafe { BNHighLevelILTokenEmitterHasBracesAroundSwitchCases(self.handle.as_ptr()) } + } + + pub fn default_braces_on_same_line(&self) -> bool { + unsafe { BNHighLevelILTokenEmitterGetDefaultBracesOnSameLine(self.handle.as_ptr()) } + } + + pub fn is_simple_scope_allowed(&self) -> bool { + unsafe { BNHighLevelILTokenEmitterIsSimpleScopeAllowed(self.handle.as_ptr()) } + } + + /// Appends a size token for the given size in the High Level IL syntax. + pub fn append_size_token(&self, size: usize, ty: InstructionTextTokenType) { + unsafe { BNAddHighLevelILSizeToken(size, ty, self.handle.as_ptr()) } + } + + /// Appends a floating point size token for the given size in the High Level IL syntax. + pub fn append_float_size_token(&self, size: usize, ty: InstructionTextTokenType) { + unsafe { BNAddHighLevelILFloatSizeToken(size, ty, self.handle.as_ptr()) } + } + + /// Appends tokens for access to a variable. + pub fn append_var_text_token( + &self, + func: &HighLevelILFunction, + var: Variable, + expr_index: usize, + size: usize, + ) { + unsafe { + BNAddHighLevelILVarTextToken( + func.handle, + &BNVariable::from(var), + self.handle.as_ptr(), + expr_index, + size, + ) + } + } + + /// Appends tokens for a constant integer value. + pub fn append_integer_text_token( + &self, + func: &HighLevelILFunction, + expr_index: usize, + val: i64, + size: usize, + ) { + unsafe { + BNAddHighLevelILIntegerTextToken( + func.handle, + expr_index, + val, + size, + self.handle.as_ptr(), + ) + } + } + + /// Appends tokens for accessing an array by constant index. + pub fn append_array_index_token( + &self, + func: &HighLevelILFunction, + expr_index: usize, + val: i64, + size: usize, + address: Option, + ) { + unsafe { + BNAddHighLevelILArrayIndexToken( + func.handle, + expr_index, + val, + size, + self.handle.as_ptr(), + address.unwrap_or(0), + ) + } + } + + /// Appends tokens for displaying a constant pointer value. + /// + /// If `allow_short_string` is true, then a string will be shown even if it is "short". + pub fn append_pointer_text_token( + &self, + func: &HighLevelILFunction, + expr_index: usize, + val: i64, + settings: &DisassemblySettings, + symbol_display: SymbolDisplayType, + precedence: OperatorPrecedence, + allow_short_string: bool, + ) -> SymbolDisplayResult { + unsafe { + BNAddHighLevelILPointerTextToken( + func.handle, + expr_index, + val, + self.handle.as_ptr(), + settings.handle, + symbol_display, + precedence, + allow_short_string, + ) + } + } + + /// Appends tokens for a constant value. + pub fn append_constant_text_token( + &self, + func: &HighLevelILFunction, + expr_index: usize, + val: i64, + size: usize, + settings: &DisassemblySettings, + precedence: OperatorPrecedence, + ) { + unsafe { + BNAddHighLevelILConstantTextToken( + func.handle, + expr_index, + val, + size, + self.handle.as_ptr(), + settings.handle, + precedence, + ) + } + } +} + +unsafe impl Send for HighLevelILTokenEmitter {} +unsafe impl Sync for HighLevelILTokenEmitter {} + +unsafe impl RefCountable for HighLevelILTokenEmitter { + unsafe fn inc_ref(handle: &Self) -> Ref { + let handle = BNNewHighLevelILTokenEmitterReference(handle.handle.as_ptr()); + let handle = NonNull::new(handle).unwrap(); + Ref::new(HighLevelILTokenEmitter { handle }) + } + + unsafe fn dec_ref(handle: &Self) { + BNFreeHighLevelILTokenEmitter(handle.handle.as_ptr()) + } +} + +impl ToOwned for HighLevelILTokenEmitter { + type Owned = Ref; + + fn to_owned(&self) -> Self::Owned { + unsafe { RefCountable::inc_ref(self) } + } +} + +/// Manages the currently active [`TokenEmitterExpr`] for the given [`HighLevelILTokenEmitter`]. +/// +/// When this object is destroyed, the previously active [`TokenEmitterExpr`] will become active again. +pub struct CurrentTokenEmitterExpr { + pub emitter: Ref, + pub expr: TokenEmitterExpr, + pub previous_expr: TokenEmitterExpr, +} + +impl CurrentTokenEmitterExpr { + pub fn new( + emitter: Ref, + expr: TokenEmitterExpr, + previous_expr: TokenEmitterExpr, + ) -> Self { + Self { + emitter, + expr, + previous_expr, + } + } +} + +impl Drop for CurrentTokenEmitterExpr { + fn drop(&mut self) { + self.emitter.restore_current_expr(self.previous_expr); + } +} diff --git a/rust/src/interaction.rs b/rust/src/interaction.rs index 1546bc5ae2..1a3ef2aaf1 100644 --- a/rust/src/interaction.rs +++ b/rust/src/interaction.rs @@ -14,42 +14,40 @@ //! Interfaces for asking the user for information: forms, opening files, etc. +use std::ffi::{c_char, c_void}; +use std::path::PathBuf; + use binaryninjacore_sys::*; -use std::ffi::{c_char, c_void, CStr}; -use std::path::PathBuf; +use crate::string::{BnString, IntoCStr}; -use crate::binary_view::BinaryView; -use crate::rc::Ref; -use crate::string::{BnStrCompatible, BnString}; +pub mod form; +pub mod handler; +pub mod report; + +pub type MessageBoxButtonSet = BNMessageBoxButtonSet; +pub type MessageBoxIcon = BNMessageBoxIcon; +pub type MessageBoxButtonResult = BNMessageBoxButtonResult; pub fn get_text_line_input(prompt: &str, title: &str) -> Option { let mut value: *mut c_char = std::ptr::null_mut(); - let result = unsafe { - BNGetTextLineInput( - &mut value, - prompt.into_bytes_with_nul().as_ptr() as *mut _, - title.into_bytes_with_nul().as_ptr() as *mut _, - ) - }; + let prompt = prompt.to_cstr(); + let title = title.to_cstr(); + let result = unsafe { BNGetTextLineInput(&mut value, prompt.as_ptr(), title.as_ptr()) }; if !result { return None; } - Some(unsafe { BnString::from_raw(value).to_string() }) + Some(unsafe { BnString::into_string(value) }) } pub fn get_integer_input(prompt: &str, title: &str) -> Option { let mut value: i64 = 0; - let result = unsafe { - BNGetIntegerInput( - &mut value, - prompt.into_bytes_with_nul().as_ptr() as *mut _, - title.into_bytes_with_nul().as_ptr() as *mut _, - ) - }; + let prompt = prompt.to_cstr(); + let title = title.to_cstr(); + let result = unsafe { BNGetIntegerInput(&mut value, prompt.as_ptr(), title.as_ptr()) }; if !result { return None; @@ -61,11 +59,13 @@ pub fn get_integer_input(prompt: &str, title: &str) -> Option { pub fn get_address_input(prompt: &str, title: &str) -> Option { let mut value: u64 = 0; + let prompt = prompt.to_cstr(); + let title = title.to_cstr(); let result = unsafe { BNGetAddressInput( &mut value, - prompt.into_bytes_with_nul().as_ptr() as *mut _, - title.into_bytes_with_nul().as_ptr() as *mut _, + prompt.as_ptr(), + title.as_ptr(), std::ptr::null_mut(), 0, ) @@ -78,22 +78,60 @@ pub fn get_address_input(prompt: &str, title: &str) -> Option { Some(value) } -pub fn get_open_filename_input(prompt: &str, extension: &str) -> Option { - let mut value: *mut c_char = std::ptr::null_mut(); +pub fn get_choice_input(prompt: &str, title: &str, choices: &[&str]) -> Option { + let prompt = prompt.to_cstr(); + let title = title.to_cstr(); + let mut choices_inner: Vec = choices.iter().copied().map(BnString::new).collect(); + // SAFETY BnString and *const c_char are transparent + let choices: &mut [*const c_char] = unsafe { + core::mem::transmute::<&mut [BnString], &mut [*const c_char]>(&mut choices_inner[..]) + }; + let mut result = 0; + let succ = unsafe { + BNGetChoiceInput( + &mut result, + prompt.as_ptr(), + title.as_ptr(), + choices.as_mut_ptr(), + choices.len(), + ) + }; + succ.then_some(result) +} - let result = unsafe { - BNGetOpenFileNameInput( - &mut value, - prompt.into_bytes_with_nul().as_ptr() as *mut _, - extension.into_bytes_with_nul().as_ptr() as *mut _, +pub fn get_large_choice_input(prompt: &str, title: &str, choices: &[&str]) -> Option { + let prompt = prompt.to_cstr(); + let title = title.to_cstr(); + let mut choices_inner: Vec = choices.iter().copied().map(BnString::new).collect(); + // SAFETY BnString and *const c_char are transparent + let choices: &mut [*const c_char] = unsafe { + core::mem::transmute::<&mut [BnString], &mut [*const c_char]>(&mut choices_inner[..]) + }; + let mut result = 0; + let succ = unsafe { + BNGetLargeChoiceInput( + &mut result, + prompt.as_ptr(), + title.as_ptr(), + choices.as_mut_ptr(), + choices.len(), ) }; + succ.then_some(result) +} + +pub fn get_open_filename_input(prompt: &str, extension: &str) -> Option { + let mut value: *mut c_char = std::ptr::null_mut(); + + let prompt = prompt.to_cstr(); + let extension = extension.to_cstr(); + let result = unsafe { BNGetOpenFileNameInput(&mut value, prompt.as_ptr(), extension.as_ptr()) }; if !result { return None; } - let string = unsafe { BnString::from_raw(value) }; - Some(PathBuf::from(string.as_str())) + let path = unsafe { BnString::into_string(value) }; + Some(PathBuf::from(path)) } pub fn get_save_filename_input( @@ -103,444 +141,49 @@ pub fn get_save_filename_input( ) -> Option { let mut value: *mut c_char = std::ptr::null_mut(); + let prompt = prompt.to_cstr(); + let extension = extension.to_cstr(); + let default_name = default_name.to_cstr(); let result = unsafe { BNGetSaveFileNameInput( &mut value, - prompt.into_bytes_with_nul().as_ptr() as *mut _, - extension.into_bytes_with_nul().as_ptr() as *mut _, - default_name.into_bytes_with_nul().as_ptr() as *mut _, + prompt.as_ptr(), + extension.as_ptr(), + default_name.as_ptr(), ) }; if !result { return None; } - let string = unsafe { BnString::from_raw(value) }; - Some(PathBuf::from(string.as_str())) + let path = unsafe { BnString::into_string(value) }; + Some(PathBuf::from(path)) } pub fn get_directory_name_input(prompt: &str, default_name: &str) -> Option { let mut value: *mut c_char = std::ptr::null_mut(); - let result = unsafe { - BNGetDirectoryNameInput( - &mut value, - prompt.into_bytes_with_nul().as_ptr() as *mut _, - default_name.into_bytes_with_nul().as_ptr() as *mut _, - ) - }; + let prompt = prompt.to_cstr(); + let default_name = default_name.to_cstr(); + let result = + unsafe { BNGetDirectoryNameInput(&mut value, prompt.as_ptr(), default_name.as_ptr()) }; if !result { return None; } - let string = unsafe { BnString::from_raw(value) }; - Some(PathBuf::from(string.as_str())) + let path = unsafe { BnString::into_string(value) }; + Some(PathBuf::from(path)) } -pub type MessageBoxButtonSet = BNMessageBoxButtonSet; -pub type MessageBoxIcon = BNMessageBoxIcon; -pub type MessageBoxButtonResult = BNMessageBoxButtonResult; pub fn show_message_box( title: &str, text: &str, buttons: MessageBoxButtonSet, icon: MessageBoxIcon, ) -> MessageBoxButtonResult { - unsafe { - BNShowMessageBox( - title.into_bytes_with_nul().as_ptr() as *mut _, - text.into_bytes_with_nul().as_ptr() as *mut _, - buttons, - icon, - ) - } -} - -pub enum FormResponses { - None, - String(String), - Integer(i64), - Address(u64), - Index(usize), -} - -enum FormData { - Label { - _text: BnString, - }, - Text { - _prompt: BnString, - _default: Option, - }, - Choice { - _prompt: BnString, - _choices: Vec, - _raw: Vec<*const c_char>, - }, - File { - _prompt: BnString, - _ext: BnString, - _default: Option, - }, - FileSave { - _prompt: BnString, - _ext: BnString, - _default_name: BnString, - _default: Option, - }, -} - -pub struct FormInputBuilder { - fields: Vec, - data: Vec, -} - -impl FormInputBuilder { - pub fn new() -> Self { - Self { - fields: vec![], - data: vec![], - } - } - - /// Form Field: Text output - pub fn label_field(mut self, text: &str) -> Self { - let text = BnString::new(text); - - let mut result = unsafe { std::mem::zeroed::() }; - result.type_ = BNFormInputFieldType::LabelFormField; - result.hasDefault = false; - result.prompt = text.as_ref().as_ptr() as *const c_char; - self.fields.push(result); - - self.data.push(FormData::Label { _text: text }); - self - } - - /// Form Field: Vertical spacing - pub fn separator_field(mut self) -> Self { - let mut result = unsafe { std::mem::zeroed::() }; - result.type_ = BNFormInputFieldType::SeparatorFormField; - result.hasDefault = false; - self.fields.push(result); - self - } - - /// Form Field: Prompt for a string value - pub fn text_field(mut self, prompt: &str, default: Option<&str>) -> Self { - let prompt = BnString::new(prompt); - let default = default.map(BnString::new); - - let mut result = unsafe { std::mem::zeroed::() }; - result.type_ = BNFormInputFieldType::TextLineFormField; - result.prompt = prompt.as_ref().as_ptr() as *const c_char; - result.hasDefault = default.is_some(); - if let Some(ref default) = default { - result.stringDefault = default.as_ref().as_ptr() as *const c_char; - } - self.fields.push(result); - - self.data.push(FormData::Text { - _prompt: prompt, - _default: default, - }); - self - } - - /// Form Field: Prompt for multi-line string value - pub fn multiline_field(mut self, prompt: &str, default: Option<&str>) -> Self { - let prompt = BnString::new(prompt); - let default = default.map(BnString::new); - - let mut result = unsafe { std::mem::zeroed::() }; - result.type_ = BNFormInputFieldType::MultilineTextFormField; - result.prompt = prompt.as_ref().as_ptr() as *const c_char; - result.hasDefault = default.is_some(); - if let Some(ref default) = default { - result.stringDefault = default.as_ref().as_ptr() as *const c_char; - } - self.fields.push(result); - - self.data.push(FormData::Text { - _prompt: prompt, - _default: default, - }); - self - } - - /// Form Field: Prompt for an integer - pub fn integer_field(mut self, prompt: &str, default: Option) -> Self { - let prompt = BnString::new(prompt); - - let mut result = unsafe { std::mem::zeroed::() }; - result.type_ = BNFormInputFieldType::IntegerFormField; - result.prompt = prompt.as_ref().as_ptr() as *const c_char; - result.hasDefault = default.is_some(); - if let Some(default) = default { - result.intDefault = default; - } - self.fields.push(result); - - self.data.push(FormData::Label { _text: prompt }); - self - } - - /// Form Field: Prompt for an address - pub fn address_field( - mut self, - prompt: &str, - view: Option>, - current_address: Option, - default: Option, - ) -> Self { - let prompt = BnString::new(prompt); - - let mut result = unsafe { std::mem::zeroed::() }; - result.type_ = BNFormInputFieldType::AddressFormField; - result.prompt = prompt.as_ref().as_ptr() as *const c_char; - if let Some(view) = view { - // the view is being moved into result, there is no need to clone - // and drop is intentionally being avoided with `Ref::into_raw` - result.view = unsafe { Ref::into_raw(view) }.handle; - } - result.currentAddress = current_address.unwrap_or(0); - result.hasDefault = default.is_some(); - if let Some(default) = default { - result.addressDefault = default; - } - self.fields.push(result); - - self.data.push(FormData::Label { _text: prompt }); - self - } - - /// Form Field: Prompt for a choice from provided options - pub fn choice_field(mut self, prompt: &str, choices: &[&str], default: Option) -> Self { - let prompt = BnString::new(prompt); - let choices: Vec = choices.iter().map(|&s| BnString::new(s)).collect(); - - let mut result = unsafe { std::mem::zeroed::() }; - result.type_ = BNFormInputFieldType::ChoiceFormField; - result.prompt = prompt.as_ref().as_ptr() as *const c_char; - let mut raw_choices: Vec<*const c_char> = choices - .iter() - .map(|c| c.as_ref().as_ptr() as *const c_char) - .collect(); - result.choices = raw_choices.as_mut_ptr(); - result.count = choices.len(); - result.hasDefault = default.is_some(); - if let Some(default) = default { - result.indexDefault = default; - } - self.fields.push(result); - - self.data.push(FormData::Choice { - _prompt: prompt, - _choices: choices, - _raw: raw_choices, - }); - self - } - - /// Form Field: Prompt for file to open - pub fn open_file_field( - mut self, - prompt: &str, - ext: Option<&str>, - default: Option<&str>, - ) -> Self { - let prompt = BnString::new(prompt); - let ext = if let Some(ext) = ext { - BnString::new(ext) - } else { - BnString::new("") - }; - let default = default.map(BnString::new); - - let mut result = unsafe { std::mem::zeroed::() }; - result.type_ = BNFormInputFieldType::OpenFileNameFormField; - result.prompt = prompt.as_ref().as_ptr() as *const c_char; - result.ext = ext.as_ref().as_ptr() as *const c_char; - result.hasDefault = default.is_some(); - if let Some(ref default) = default { - result.stringDefault = default.as_ref().as_ptr() as *const c_char; - } - self.fields.push(result); - - self.data.push(FormData::File { - _prompt: prompt, - _ext: ext, - _default: default, - }); - self - } - - /// Form Field: Prompt for file to save to - pub fn save_file_field( - mut self, - prompt: &str, - ext: Option<&str>, - default_name: Option<&str>, - default: Option<&str>, - ) -> Self { - let prompt = BnString::new(prompt); - let ext = if let Some(ext) = ext { - BnString::new(ext) - } else { - BnString::new("") - }; - let default_name = if let Some(default_name) = default_name { - BnString::new(default_name) - } else { - BnString::new("") - }; - let default = default.map(BnString::new); - - let mut result = unsafe { std::mem::zeroed::() }; - result.type_ = BNFormInputFieldType::SaveFileNameFormField; - result.prompt = prompt.as_ref().as_ptr() as *const c_char; - result.ext = ext.as_ref().as_ptr() as *const c_char; - result.defaultName = default_name.as_ref().as_ptr() as *const c_char; - result.hasDefault = default.is_some(); - if let Some(ref default) = default { - result.stringDefault = default.as_ref().as_ptr() as *const c_char; - } - self.fields.push(result); - - self.data.push(FormData::FileSave { - _prompt: prompt, - _ext: ext, - _default_name: default_name, - _default: default, - }); - self - } - - /// Form Field: Prompt for directory name - pub fn directory_name_field( - mut self, - prompt: &str, - default_name: Option<&str>, - default: Option<&str>, - ) -> Self { - let prompt = BnString::new(prompt); - let default_name = if let Some(default_name) = default_name { - BnString::new(default_name) - } else { - BnString::new("") - }; - let default = default.map(BnString::new); - - let mut result = unsafe { std::mem::zeroed::() }; - result.type_ = BNFormInputFieldType::DirectoryNameFormField; - result.prompt = prompt.as_ref().as_ptr() as *const c_char; - result.defaultName = default_name.as_ref().as_ptr() as *const c_char; - result.hasDefault = default.is_some(); - if let Some(ref default) = default { - result.stringDefault = default.as_ref().as_ptr() as *const c_char; - } - self.fields.push(result); - - self.data.push(FormData::File { - _prompt: prompt, - _ext: default_name, - _default: default, - }); - self - } - - /// Prompts the user for a set of inputs specified in `fields` with given title. - /// The fields parameter is a list which can contain the following types: - /// - /// This API is flexible and works both in the UI via a pop-up dialog and on the command-line. - /// - /// ```no_run - /// # use binaryninja::interaction::FormInputBuilder; - /// # use binaryninja::interaction::FormResponses; - /// let responses = FormInputBuilder::new() - /// .text_field("First Name", None) - /// .text_field("Last Name", None) - /// .choice_field( - /// "Favorite Food", - /// &vec![ - /// "Pizza", - /// "Also Pizza", - /// "Also Pizza", - /// "Yummy Pizza", - /// "Wrong Answer", - /// ], - /// Some(0), - /// ) - /// .get_form_input("Form Title"); - /// - /// let food = match responses[2] { - /// FormResponses::Index(0) => "Pizza", - /// FormResponses::Index(1) => "Also Pizza", - /// FormResponses::Index(2) => "Also Pizza", - /// FormResponses::Index(3) => "Wrong Answer", - /// _ => panic!("This person doesn't like pizza?!?"), - /// }; - /// - /// let FormResponses::String(last_name) = &responses[0] else { - /// unreachable!() - /// }; - /// let FormResponses::String(first_name) = &responses[1] else { - /// unreachable!() - /// }; - /// - /// println!("{} {} likes {}", &first_name, &last_name, food); - /// ``` - pub fn get_form_input(&mut self, title: &str) -> Vec { - if unsafe { - BNGetFormInput( - self.fields.as_mut_ptr(), - self.fields.len(), - title.into_bytes_with_nul().as_ptr() as *const _, - ) - } { - let result = self - .fields - .iter() - .map(|form_field| match form_field.type_ { - BNFormInputFieldType::LabelFormField - | BNFormInputFieldType::SeparatorFormField => FormResponses::None, - - BNFormInputFieldType::TextLineFormField - | BNFormInputFieldType::MultilineTextFormField - | BNFormInputFieldType::OpenFileNameFormField - | BNFormInputFieldType::SaveFileNameFormField - | BNFormInputFieldType::DirectoryNameFormField => { - FormResponses::String(unsafe { - CStr::from_ptr(form_field.stringResult) - .to_str() - .unwrap() - .to_owned() - }) - } - - BNFormInputFieldType::IntegerFormField => { - FormResponses::Integer(form_field.intResult) - } - BNFormInputFieldType::AddressFormField => { - FormResponses::Address(form_field.addressResult) - } - BNFormInputFieldType::ChoiceFormField => { - FormResponses::Index(form_field.indexResult) - } - }) - .collect(); - unsafe { BNFreeFormInputResults(self.fields.as_mut_ptr(), self.fields.len()) }; - result - } else { - vec![] - } - } -} - -impl Default for FormInputBuilder { - fn default() -> Self { - Self::new() - } + let title = title.to_cstr(); + let text = text.to_cstr(); + unsafe { BNShowMessageBox(title.as_ptr(), text.as_ptr(), buttons, icon) } } struct TaskContext Result<(), ()>>)>(F); @@ -575,9 +218,10 @@ pub fn run_progress_dialog Result<(), ()>>)>( }) } + let title = title.to_cstr(); if unsafe { BNRunProgressDialog( - title.into_bytes_with_nul().as_ptr() as *mut _, + title.as_ptr(), can_cancel, Some(cb_task::), &mut ctxt as *mut _ as *mut c_void, diff --git a/rust/src/interaction/form.rs b/rust/src/interaction/form.rs new file mode 100644 index 0000000000..06799681d1 --- /dev/null +++ b/rust/src/interaction/form.rs @@ -0,0 +1,411 @@ +use std::ffi::c_char; + +use binaryninjacore_sys::*; + +use crate::binary_view::BinaryView; +use crate::rc::{Array, Ref}; +use crate::string::{raw_to_string, strings_to_string_list, BnString, IntoCStr}; + +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct Form { + pub title: String, + pub fields: Vec, +} + +impl Form { + pub fn new(title: impl Into) -> Self { + Self::new_with_fields(title, vec![]) + } + + pub fn new_with_fields(title: impl Into, fields: Vec) -> Self { + Self { + title: title.into(), + fields, + } + } + + pub fn add_field(&mut self, field: FormInputField) { + self.fields.push(field); + } + + /// Prompt the user (or interaction handler) for the form input. + /// + /// Updates the field's values and returns whether the form was accepted or not. + pub fn prompt(&mut self) -> bool { + let title = self.title.clone().to_cstr(); + let mut raw_fields = self + .fields + .iter() + .map(FormInputField::into_raw) + .collect::>(); + let success = + unsafe { BNGetFormInput(raw_fields.as_mut_ptr(), raw_fields.len(), title.as_ptr()) }; + // Update the fields with the new field values. + self.fields = raw_fields + .into_iter() + .map(FormInputField::from_owned_raw) + .collect(); + success + } + + pub fn get_field_with_name(&self, name: &str) -> Option<&FormInputField> { + self.fields + .iter() + .find(|field| field.try_prompt() == Some(name.to_string())) + } +} + +/// A field within a form. +#[derive(Clone, Debug, PartialEq, Eq)] +pub enum FormInputField { + Label { + prompt: String, + }, + Separator, + TextLine { + prompt: String, + default: Option, + value: Option, + }, + MultilineText { + prompt: String, + default: Option, + value: Option, + }, + Integer { + prompt: String, + default: Option, + value: i64, + }, + Address { + prompt: String, + view: Option>, + current_address: u64, + default: Option, + value: u64, + }, + Choice { + prompt: String, + choices: Vec, + default: Option, + value: usize, + }, + OpenFileName { + prompt: String, + /// File extension to filter on. + extension: Option, + default: Option, + value: Option, + }, + SaveFileName { + prompt: String, + /// File extension to filter on. + extension: Option, + /// Default file name to fill. + default_name: Option, + default: Option, + value: Option, + }, + DirectoryName { + prompt: String, + default_name: Option, + default: Option, + value: Option, + }, +} + +impl FormInputField { + pub fn from_raw(value: &BNFormInputField) -> Self { + let prompt = raw_to_string(value.prompt).unwrap_or_default(); + let view = match value.view.is_null() { + false => Some(unsafe { BinaryView::from_raw(value.view) }.to_owned()), + true => None, + }; + let name_default = value + .hasDefault + .then_some(raw_to_string(value.defaultName).unwrap_or_default()); + let string_default = value + .hasDefault + .then_some(raw_to_string(value.stringDefault).unwrap_or_default()); + let int_default = value.hasDefault.then_some(value.intDefault); + let address_default = value.hasDefault.then_some(value.addressDefault); + let index_default = value.hasDefault.then_some(value.indexDefault); + let extension = raw_to_string(value.ext); + let current_address = value.currentAddress; + let string_result = raw_to_string(value.stringResult); + let int_result = value.intResult; + let address_result = value.addressResult; + let index_result = value.indexResult; + match value.type_ { + BNFormInputFieldType::LabelFormField => Self::Label { prompt }, + BNFormInputFieldType::SeparatorFormField => Self::Separator, + BNFormInputFieldType::TextLineFormField => Self::TextLine { + prompt, + default: string_default, + value: string_result, + }, + BNFormInputFieldType::MultilineTextFormField => Self::MultilineText { + prompt, + default: string_default, + value: string_result, + }, + BNFormInputFieldType::IntegerFormField => Self::Integer { + prompt, + default: int_default, + value: int_result, + }, + BNFormInputFieldType::AddressFormField => Self::Address { + prompt, + view, + current_address, + default: address_default, + value: address_result, + }, + BNFormInputFieldType::ChoiceFormField => Self::Choice { + prompt, + choices: vec![], + default: index_default, + value: index_result, + }, + BNFormInputFieldType::OpenFileNameFormField => Self::OpenFileName { + prompt, + extension, + default: string_default, + value: string_result, + }, + BNFormInputFieldType::SaveFileNameFormField => Self::SaveFileName { + prompt, + extension, + default_name: name_default, + default: string_default, + value: string_result, + }, + BNFormInputFieldType::DirectoryNameFormField => Self::DirectoryName { + prompt, + default_name: name_default, + default: string_default, + value: string_result, + }, + } + } + + pub fn from_owned_raw(value: BNFormInputField) -> Self { + let owned = Self::from_raw(&value); + Self::free_raw(value); + owned + } + + pub fn into_raw(&self) -> BNFormInputField { + let bn_prompt = BnString::new(self.try_prompt().unwrap_or_default()); + let bn_extension = BnString::new(self.try_extension().unwrap_or_default()); + let bn_default_string = BnString::new(self.try_default_string().unwrap_or_default()); + let bn_default_name = BnString::new(self.try_default_name().unwrap_or_default()); + let bn_value_string = BnString::new(self.try_value_string().unwrap_or_default()); + // Expected to be freed by [`FormInputField::free_raw`]. + BNFormInputField { + type_: self.as_type(), + prompt: BnString::into_raw(bn_prompt), + view: match self.try_view() { + None => std::ptr::null_mut(), + Some(view) => unsafe { Ref::into_raw(view) }.handle, + }, + currentAddress: self.try_current_address().unwrap_or_default(), + choices: match self.try_choices() { + None => std::ptr::null_mut(), + Some(choices) => strings_to_string_list(choices.as_slice()) as *mut *const c_char, + }, + // NOTE: `count` is the length of the `choices` array. + count: self.try_choices().unwrap_or_default().len(), + ext: BnString::into_raw(bn_extension), + defaultName: BnString::into_raw(bn_default_name), + intResult: self.try_value_int().unwrap_or_default(), + addressResult: self.try_value_address().unwrap_or_default(), + stringResult: BnString::into_raw(bn_value_string), + indexResult: self.try_value_index().unwrap_or_default(), + hasDefault: self.try_has_default().unwrap_or_default(), + intDefault: self.try_default_int().unwrap_or_default(), + addressDefault: self.try_default_address().unwrap_or_default(), + stringDefault: BnString::into_raw(bn_default_string), + indexDefault: self.try_default_index().unwrap_or_default(), + } + } + + pub fn free_raw(value: BNFormInputField) { + unsafe { + BnString::free_raw(value.defaultName as *mut c_char); + BnString::free_raw(value.prompt as *mut c_char); + BnString::free_raw(value.ext as *mut c_char); + BnString::free_raw(value.stringDefault as *mut c_char); + BnString::free_raw(value.stringResult); + // TODO: Would like access to a `Array::free_raw` or something. + Array::::new(value.choices as *mut *mut c_char, value.count, ()); + // Free the view ref if provided. + if !value.view.is_null() { + BinaryView::ref_from_raw(value.view); + } + } + } + + pub fn as_type(&self) -> BNFormInputFieldType { + match self { + FormInputField::Label { .. } => BNFormInputFieldType::LabelFormField, + FormInputField::Separator => BNFormInputFieldType::SeparatorFormField, + FormInputField::TextLine { .. } => BNFormInputFieldType::TextLineFormField, + FormInputField::MultilineText { .. } => BNFormInputFieldType::MultilineTextFormField, + FormInputField::Integer { .. } => BNFormInputFieldType::IntegerFormField, + FormInputField::Address { .. } => BNFormInputFieldType::AddressFormField, + FormInputField::Choice { .. } => BNFormInputFieldType::ChoiceFormField, + FormInputField::OpenFileName { .. } => BNFormInputFieldType::OpenFileNameFormField, + FormInputField::SaveFileName { .. } => BNFormInputFieldType::SaveFileNameFormField, + FormInputField::DirectoryName { .. } => BNFormInputFieldType::DirectoryNameFormField, + } + } + + /// Mapping to the [`BNFormInputField::prompt`] field. + pub fn try_prompt(&self) -> Option { + match self { + FormInputField::Label { prompt, .. } => Some(prompt.clone()), + FormInputField::Separator => None, + FormInputField::TextLine { prompt, .. } => Some(prompt.clone()), + FormInputField::MultilineText { prompt, .. } => Some(prompt.clone()), + FormInputField::Integer { prompt, .. } => Some(prompt.clone()), + FormInputField::Address { prompt, .. } => Some(prompt.clone()), + FormInputField::Choice { prompt, .. } => Some(prompt.clone()), + FormInputField::OpenFileName { prompt, .. } => Some(prompt.clone()), + FormInputField::SaveFileName { prompt, .. } => Some(prompt.clone()), + FormInputField::DirectoryName { prompt, .. } => Some(prompt.clone()), + } + } + + /// Mapping to the [`BNFormInputField::view`] field. + pub fn try_view(&self) -> Option> { + match self { + FormInputField::Address { view, .. } => view.clone(), + _ => None, + } + } + + /// Mapping to the [`BNFormInputField::currentAddress`] field. + pub fn try_current_address(&self) -> Option { + match self { + FormInputField::Address { + current_address, .. + } => Some(*current_address), + _ => None, + } + } + + /// Mapping to the [`BNFormInputField::choices`] field. + pub fn try_choices(&self) -> Option> { + match self { + FormInputField::Choice { choices, .. } => Some(choices.clone()), + _ => None, + } + } + + /// Mapping to the [`BNFormInputField::ext`] field. + pub fn try_extension(&self) -> Option { + match self { + Self::SaveFileName { extension, .. } => extension.clone(), + Self::OpenFileName { extension, .. } => extension.clone(), + _ => None, + } + } + + /// Mapping to the [`BNFormInputField::hasDefault`] field. + pub fn try_has_default(&self) -> Option { + match self { + FormInputField::Label { .. } => None, + FormInputField::Separator => None, + FormInputField::TextLine { default, .. } => Some(default.is_some()), + FormInputField::MultilineText { default, .. } => Some(default.is_some()), + FormInputField::Integer { default, .. } => Some(default.is_some()), + FormInputField::Address { default, .. } => Some(default.is_some()), + FormInputField::Choice { default, .. } => Some(default.is_some()), + FormInputField::OpenFileName { default, .. } => Some(default.is_some()), + FormInputField::SaveFileName { default, .. } => Some(default.is_some()), + FormInputField::DirectoryName { default, .. } => Some(default.is_some()), + } + } + + /// Mapping to the [`BNFormInputField::defaultName`] field. + pub fn try_default_name(&self) -> Option { + match self { + Self::SaveFileName { default_name, .. } => default_name.clone(), + Self::DirectoryName { default_name, .. } => default_name.clone(), + _ => None, + } + } + + /// Mapping to the [`BNFormInputField::intDefault`] field. + pub fn try_default_int(&self) -> Option { + match self { + FormInputField::Integer { default, .. } => *default, + _ => None, + } + } + + /// Mapping to the [`BNFormInputField::addressDefault`] field. + pub fn try_default_address(&self) -> Option { + match self { + FormInputField::Address { default, .. } => *default, + _ => None, + } + } + + /// Mapping to the [`BNFormInputField::stringDefault`] field. + pub fn try_default_string(&self) -> Option { + match self { + FormInputField::TextLine { default, .. } => default.clone(), + FormInputField::MultilineText { default, .. } => default.clone(), + FormInputField::OpenFileName { default, .. } => default.clone(), + FormInputField::SaveFileName { default, .. } => default.clone(), + FormInputField::DirectoryName { default, .. } => default.clone(), + _ => None, + } + } + + /// Mapping to the [`BNFormInputField::indexDefault`] field. + pub fn try_default_index(&self) -> Option { + match self { + FormInputField::Choice { default, .. } => *default, + _ => None, + } + } + + /// Mapping to the [`BNFormInputField::intResult`] field. + pub fn try_value_int(&self) -> Option { + match self { + FormInputField::Integer { value, .. } => Some(*value), + _ => None, + } + } + + /// Mapping to the [`BNFormInputField::addressResult`] field. + pub fn try_value_address(&self) -> Option { + match self { + FormInputField::Address { value, .. } => Some(*value), + _ => None, + } + } + + /// Mapping to the [`BNFormInputField::stringResult`] field. + pub fn try_value_string(&self) -> Option { + match self { + FormInputField::TextLine { value, .. } => value.clone(), + FormInputField::MultilineText { value, .. } => value.clone(), + FormInputField::OpenFileName { value, .. } => value.clone(), + FormInputField::SaveFileName { value, .. } => value.clone(), + FormInputField::DirectoryName { value, .. } => value.clone(), + _ => None, + } + } + + /// Mapping to the [`BNFormInputField::indexResult`] field. + pub fn try_value_index(&self) -> Option { + match self { + FormInputField::Choice { value, .. } => Some(*value), + _ => None, + } + } +} diff --git a/rust/src/interaction/handler.rs b/rust/src/interaction/handler.rs new file mode 100644 index 0000000000..f854f612f5 --- /dev/null +++ b/rust/src/interaction/handler.rs @@ -0,0 +1,614 @@ +use std::ffi::{c_char, c_void, CStr}; +use std::ptr; + +use binaryninjacore_sys::*; + +use crate::binary_view::BinaryView; +use crate::flowgraph::FlowGraph; +use crate::interaction::form::{Form, FormInputField}; +use crate::interaction::report::{Report, ReportCollection}; +use crate::interaction::{MessageBoxButtonResult, MessageBoxButtonSet, MessageBoxIcon}; +use crate::string::{raw_to_string, BnString}; + +pub fn register_interaction_handler(custom: R) { + let leak_custom = Box::leak(Box::new(custom)); + let mut callbacks = BNInteractionHandlerCallbacks { + context: leak_custom as *mut R as *mut c_void, + showPlainTextReport: Some(cb_show_plain_text_report::), + showMarkdownReport: Some(cb_show_markdown_report::), + showHTMLReport: Some(cb_show_html_report::), + showGraphReport: Some(cb_show_graph_report::), + showReportCollection: Some(cb_show_report_collection::), + getTextLineInput: Some(cb_get_text_line_input::), + getIntegerInput: Some(cb_get_integer_input::), + getAddressInput: Some(cb_get_address_input::), + getChoiceInput: Some(cb_get_choice_input::), + getLargeChoiceInput: Some(cb_get_large_choice_input::), + getOpenFileNameInput: Some(cb_get_open_file_name_input::), + getSaveFileNameInput: Some(cb_get_save_file_name_input::), + getDirectoryNameInput: Some(cb_get_directory_name_input::), + getFormInput: Some(cb_get_form_input::), + showMessageBox: Some(cb_show_message_box::), + openUrl: Some(cb_open_url::), + runProgressDialog: Some(cb_run_progress_dialog::), + }; + unsafe { BNRegisterInteractionHandler(&mut callbacks) } +} + +pub trait InteractionHandler: Sync + Send + 'static { + fn show_message_box( + &mut self, + title: &str, + text: &str, + buttons: MessageBoxButtonSet, + icon: MessageBoxIcon, + ) -> MessageBoxButtonResult; + + fn open_url(&mut self, url: &str) -> bool; + + fn run_progress_dialog( + &mut self, + title: &str, + can_cancel: bool, + task: &InteractionHandlerTask, + ) -> bool; + + fn show_plain_text_report(&mut self, view: Option<&BinaryView>, title: &str, contents: &str); + + fn show_graph_report(&mut self, view: Option<&BinaryView>, title: &str, graph: &FlowGraph); + + fn show_markdown_report( + &mut self, + view: Option<&BinaryView>, + title: &str, + _contents: &str, + plain_text: &str, + ) { + self.show_plain_text_report(view, title, plain_text); + } + + fn show_html_report( + &mut self, + view: Option<&BinaryView>, + title: &str, + _contents: &str, + plain_text: &str, + ) { + self.show_plain_text_report(view, title, plain_text); + } + + fn show_report_collection(&mut self, _title: &str, reports: &ReportCollection) { + for report in reports { + match &report { + Report::PlainText(rpt) => self.show_plain_text_report( + report.view().as_deref(), + &report.title(), + &rpt.contents(), + ), + Report::Markdown(rm) => self.show_markdown_report( + report.view().as_deref(), + &report.title(), + &rm.contents(), + &rm.plaintext(), + ), + Report::Html(rh) => self.show_html_report( + report.view().as_deref(), + &report.title(), + &rh.contents(), + &rh.plaintext(), + ), + Report::FlowGraph(rfg) => self.show_graph_report( + report.view().as_deref(), + &report.title(), + &rfg.flow_graph(), + ), + } + } + } + + fn get_form_input(&mut self, form: &mut Form) -> bool; + + fn get_text_line_input(&mut self, prompt: &str, title: &str) -> Option { + let mut form = Form::new(title.to_owned()); + form.add_field(FormInputField::TextLine { + prompt: prompt.to_string(), + default: None, + value: None, + }); + if !self.get_form_input(&mut form) { + return None; + } + form.get_field_with_name(prompt) + .and_then(|f| f.try_value_string()) + } + + fn get_integer_input(&mut self, prompt: &str, title: &str) -> Option { + let mut form = Form::new(title.to_owned()); + form.add_field(FormInputField::Integer { + prompt: prompt.to_string(), + value: 0, + default: None, + }); + if !self.get_form_input(&mut form) { + return None; + } + form.get_field_with_name(prompt) + .and_then(|f| f.try_value_int()) + } + + fn get_address_input( + &mut self, + prompt: &str, + title: &str, + view: Option<&BinaryView>, + current_addr: u64, + ) -> Option { + let mut form = Form::new(title.to_owned()); + form.add_field(FormInputField::Address { + prompt: prompt.to_string(), + view: view.map(|v| v.to_owned()), + current_address: current_addr, + value: 0, + default: None, + }); + if !self.get_form_input(&mut form) { + return None; + } + form.get_field_with_name(prompt) + .and_then(|f| f.try_value_address()) + } + + fn get_choice_input( + &mut self, + prompt: &str, + title: &str, + choices: Vec, + ) -> Option { + let mut form = Form::new(title.to_owned()); + form.add_field(FormInputField::Choice { + prompt: prompt.to_string(), + choices, + default: None, + value: 0, + }); + if !self.get_form_input(&mut form) { + return None; + } + form.get_field_with_name(prompt) + .and_then(|f| f.try_value_index()) + } + + fn get_large_choice_input( + &mut self, + prompt: &str, + title: &str, + choices: Vec, + ) -> Option { + self.get_choice_input(prompt, title, choices) + } + + fn get_open_file_name_input( + &mut self, + prompt: &str, + extension: Option, + ) -> Option { + let mut form = Form::new(prompt.to_owned()); + form.add_field(FormInputField::OpenFileName { + prompt: prompt.to_string(), + default: None, + value: None, + extension, + }); + if !self.get_form_input(&mut form) { + return None; + } + form.get_field_with_name(prompt) + .and_then(|f| f.try_value_string()) + } + + fn get_save_file_name_input( + &mut self, + prompt: &str, + extension: Option, + default_name: Option, + ) -> Option { + let mut form = Form::new(prompt.to_owned()); + form.add_field(FormInputField::SaveFileName { + prompt: prompt.to_string(), + extension, + default: None, + value: None, + default_name, + }); + if !self.get_form_input(&mut form) { + return None; + } + form.get_field_with_name(prompt) + .and_then(|f| f.try_value_string()) + } + + fn get_directory_name_input( + &mut self, + prompt: &str, + default_name: Option, + ) -> Option { + let mut form = Form::new(prompt.to_owned()); + form.add_field(FormInputField::DirectoryName { + prompt: prompt.to_string(), + default_name, + default: None, + value: None, + }); + if !self.get_form_input(&mut form) { + return None; + } + form.get_field_with_name(prompt) + .and_then(|f| f.try_value_string()) + } +} + +pub struct InteractionHandlerTask { + ctxt: *mut c_void, + task: Option< + unsafe extern "C" fn( + taskCtxt: *mut c_void, + progress: Option< + unsafe extern "C" fn(progressCtxt: *mut c_void, cur: usize, max: usize) -> bool, + >, + progressCtxt: *mut c_void, + ), + >, +} + +impl InteractionHandlerTask { + pub fn task bool>(&mut self, progress: &mut P) { + let Some(task) = self.task else { + // Assuming a nullptr task mean nothing need to be done + return; + }; + + let progress_ctxt = progress as *mut P as *mut c_void; + ffi_wrap!("custom_interaction_run_progress_dialog", unsafe { + task( + self.ctxt, + Some(cb_custom_interaction_handler_task::

), + progress_ctxt, + ) + }) + } +} + +unsafe extern "C" fn cb_custom_interaction_handler_task bool>( + ctxt: *mut c_void, + cur: usize, + max: usize, +) -> bool { + let ctxt = ctxt as *mut P; + (*ctxt)(cur, max) +} + +unsafe extern "C" fn cb_show_plain_text_report( + ctxt: *mut c_void, + view: *mut BNBinaryView, + title: *const c_char, + contents: *const c_char, +) { + let ctxt = ctxt as *mut R; + let title = raw_to_string(title).unwrap(); + let contents = raw_to_string(contents).unwrap(); + let view = match !view.is_null() { + true => Some(BinaryView::from_raw(view)), + false => None, + }; + (*ctxt).show_plain_text_report(view.as_ref(), &title, &contents) +} + +unsafe extern "C" fn cb_show_markdown_report( + ctxt: *mut c_void, + view: *mut BNBinaryView, + title: *const c_char, + contents: *const c_char, + plaintext: *const c_char, +) { + let ctxt = ctxt as *mut R; + let title = raw_to_string(title).unwrap(); + let contents = raw_to_string(contents).unwrap(); + let plaintext = raw_to_string(plaintext).unwrap(); + let view = match !view.is_null() { + true => Some(BinaryView::from_raw(view)), + false => None, + }; + (*ctxt).show_markdown_report(view.as_ref(), &title, &contents, &plaintext) +} + +unsafe extern "C" fn cb_show_html_report( + ctxt: *mut c_void, + view: *mut BNBinaryView, + title: *const c_char, + contents: *const c_char, + plaintext: *const c_char, +) { + let ctxt = ctxt as *mut R; + let title = raw_to_string(title).unwrap(); + let contents = raw_to_string(contents).unwrap(); + let plaintext = raw_to_string(plaintext).unwrap(); + let view = match !view.is_null() { + true => Some(BinaryView::from_raw(view)), + false => None, + }; + (*ctxt).show_html_report(view.as_ref(), &title, &contents, &plaintext) +} + +unsafe extern "C" fn cb_show_graph_report( + ctxt: *mut c_void, + view: *mut BNBinaryView, + title: *const c_char, + graph: *mut BNFlowGraph, +) { + let ctxt = ctxt as *mut R; + let title = raw_to_string(title).unwrap(); + let view = match !view.is_null() { + true => Some(BinaryView::from_raw(view)), + false => None, + }; + (*ctxt).show_graph_report(view.as_ref(), &title, &FlowGraph::from_raw(graph)) +} + +unsafe extern "C" fn cb_show_report_collection( + ctxt: *mut c_void, + title: *const c_char, + report: *mut BNReportCollection, +) { + let ctxt = ctxt as *mut R; + let title = raw_to_string(title).unwrap(); + (*ctxt).show_report_collection( + &title, + &ReportCollection::from_raw(ptr::NonNull::new(report).unwrap()), + ) +} + +unsafe extern "C" fn cb_get_text_line_input( + ctxt: *mut c_void, + result_ffi: *mut *mut c_char, + prompt: *const c_char, + title: *const c_char, +) -> bool { + let ctxt = ctxt as *mut R; + let prompt = raw_to_string(prompt).unwrap(); + let title = raw_to_string(title).unwrap(); + let result = (*ctxt).get_text_line_input(&prompt, &title); + if let Some(result) = result { + unsafe { *result_ffi = BnString::into_raw(BnString::new(result)) }; + true + } else { + unsafe { *result_ffi = ptr::null_mut() }; + false + } +} + +unsafe extern "C" fn cb_get_integer_input( + ctxt: *mut c_void, + result_ffi: *mut i64, + prompt: *const c_char, + title: *const c_char, +) -> bool { + let ctxt = ctxt as *mut R; + let prompt = raw_to_string(prompt).unwrap(); + let title = raw_to_string(title).unwrap(); + let result = (*ctxt).get_integer_input(&prompt, &title); + if let Some(result) = result { + unsafe { *result_ffi = result }; + true + } else { + unsafe { *result_ffi = 0 }; + false + } +} + +unsafe extern "C" fn cb_get_address_input( + ctxt: *mut c_void, + result_ffi: *mut u64, + prompt: *const c_char, + title: *const c_char, + view: *mut BNBinaryView, + current_addr: u64, +) -> bool { + let ctxt = ctxt as *mut R; + let prompt = raw_to_string(prompt).unwrap(); + let title = raw_to_string(title).unwrap(); + let view = (!view.is_null()).then(|| BinaryView::from_raw(view)); + let result = (*ctxt).get_address_input(&prompt, &title, view.as_ref(), current_addr); + if let Some(result) = result { + unsafe { *result_ffi = result }; + true + } else { + unsafe { *result_ffi = 0 }; + false + } +} + +unsafe extern "C" fn cb_get_choice_input( + ctxt: *mut c_void, + result_ffi: *mut usize, + prompt: *const c_char, + title: *const c_char, + choices: *mut *const c_char, + count: usize, +) -> bool { + let ctxt = ctxt as *mut R; + let prompt = raw_to_string(prompt).unwrap(); + let title = raw_to_string(title).unwrap(); + let choices = unsafe { core::slice::from_raw_parts(choices, count) }; + // SAFETY: BnString and *const c_char are transparent + let choices = unsafe { core::mem::transmute::<&[*const c_char], &[BnString]>(choices) }; + let choices: Vec = choices + .iter() + .map(|x| x.to_string_lossy().to_string()) + .collect(); + let result = (*ctxt).get_choice_input(&prompt, &title, choices); + if let Some(result) = result { + unsafe { *result_ffi = result }; + true + } else { + unsafe { *result_ffi = 0 }; + false + } +} + +unsafe extern "C" fn cb_get_large_choice_input( + ctxt: *mut c_void, + result_ffi: *mut usize, + prompt: *const c_char, + title: *const c_char, + choices: *mut *const c_char, + count: usize, +) -> bool { + let ctxt = ctxt as *mut R; + let prompt = raw_to_string(prompt).unwrap(); + let title = raw_to_string(title).unwrap(); + let choices = unsafe { core::slice::from_raw_parts(choices, count) }; + // SAFETY: BnString and *const c_char are transparent + let choices = unsafe { core::mem::transmute::<&[*const c_char], &[BnString]>(choices) }; + let choices: Vec = choices + .iter() + .map(|x| x.to_string_lossy().to_string()) + .collect(); + let result = (*ctxt).get_large_choice_input(&prompt, &title, choices); + if let Some(result) = result { + unsafe { *result_ffi = result }; + true + } else { + unsafe { *result_ffi = 0 }; + false + } +} + +unsafe extern "C" fn cb_get_open_file_name_input( + ctxt: *mut c_void, + result_ffi: *mut *mut c_char, + prompt: *const c_char, + ext: *const c_char, +) -> bool { + let ctxt = ctxt as *mut R; + let prompt = raw_to_string(prompt).unwrap(); + let ext = (!ext.is_null()).then(|| unsafe { CStr::from_ptr(ext) }); + let result = + (*ctxt).get_open_file_name_input(&prompt, ext.map(|x| x.to_string_lossy().to_string())); + if let Some(result) = result { + unsafe { *result_ffi = BnString::into_raw(BnString::new(result)) }; + true + } else { + unsafe { *result_ffi = ptr::null_mut() }; + false + } +} + +unsafe extern "C" fn cb_get_save_file_name_input( + ctxt: *mut c_void, + result_ffi: *mut *mut c_char, + prompt: *const c_char, + ext: *const c_char, + default_name: *const c_char, +) -> bool { + let ctxt = ctxt as *mut R; + let prompt = raw_to_string(prompt).unwrap(); + let ext = raw_to_string(ext); + let default_name = raw_to_string(default_name); + let result = (*ctxt).get_save_file_name_input(&prompt, ext, default_name); + if let Some(result) = result { + unsafe { *result_ffi = BnString::into_raw(BnString::new(result)) }; + true + } else { + unsafe { *result_ffi = ptr::null_mut() }; + false + } +} + +unsafe extern "C" fn cb_get_directory_name_input( + ctxt: *mut c_void, + result_ffi: *mut *mut c_char, + prompt: *const c_char, + default_name: *const c_char, +) -> bool { + let ctxt = ctxt as *mut R; + let prompt = raw_to_string(prompt).unwrap(); + let default_name = raw_to_string(default_name); + let result = (*ctxt).get_directory_name_input(&prompt, default_name); + if let Some(result) = result { + unsafe { *result_ffi = BnString::into_raw(BnString::new(result)) }; + true + } else { + unsafe { *result_ffi = ptr::null_mut() }; + false + } +} + +unsafe extern "C" fn cb_get_form_input( + ctxt: *mut c_void, + fields: *mut BNFormInputField, + count: usize, + title: *const c_char, +) -> bool { + let ctxt = ctxt as *mut R; + let raw_fields = unsafe { core::slice::from_raw_parts_mut(fields, count) }; + let fields: Vec<_> = raw_fields + .iter_mut() + .map(|x| FormInputField::from_raw(x)) + .collect(); + let title = raw_to_string(title).unwrap(); + let mut form = Form::new_with_fields(title, fields); + let results = (*ctxt).get_form_input(&mut form); + // Update the fields with the new values. Freeing the old ones. + raw_fields + .iter_mut() + .enumerate() + .for_each(|(idx, raw_field)| { + FormInputField::free_raw(*raw_field); + *raw_field = form.fields[idx].into_raw(); + }); + results +} + +unsafe extern "C" fn cb_show_message_box( + ctxt: *mut c_void, + title: *const c_char, + text: *const c_char, + buttons: BNMessageBoxButtonSet, + icon: BNMessageBoxIcon, +) -> BNMessageBoxButtonResult { + let ctxt = ctxt as *mut R; + let title = raw_to_string(title).unwrap(); + let text = raw_to_string(text).unwrap(); + (*ctxt).show_message_box(&title, &text, buttons, icon) +} + +unsafe extern "C" fn cb_open_url( + ctxt: *mut c_void, + url: *const c_char, +) -> bool { + let ctxt = ctxt as *mut R; + let url = raw_to_string(url).unwrap(); + (*ctxt).open_url(&url) +} + +unsafe extern "C" fn cb_run_progress_dialog( + ctxt: *mut c_void, + title: *const c_char, + can_cancel: bool, + task: Option< + unsafe extern "C" fn( + *mut c_void, + Option bool>, + *mut c_void, + ), + >, + task_ctxt: *mut c_void, +) -> bool { + let ctxt = ctxt as *mut R; + let title = raw_to_string(title).unwrap(); + let task = InteractionHandlerTask { + ctxt: task_ctxt, + task, + }; + (*ctxt).run_progress_dialog(&title, can_cancel, &task) +} diff --git a/rust/src/interaction/report.rs b/rust/src/interaction/report.rs new file mode 100644 index 0000000000..415845db75 --- /dev/null +++ b/rust/src/interaction/report.rs @@ -0,0 +1,313 @@ +use std::ptr::NonNull; + +use binaryninjacore_sys::*; + +use crate::binary_view::BinaryView; +use crate::flowgraph::FlowGraph; +use crate::rc::{Ref, RefCountable}; +use crate::string::{BnString, IntoCStr}; + +pub type ReportType = BNReportType; + +#[repr(transparent)] +pub struct ReportCollection { + handle: NonNull, +} + +impl ReportCollection { + pub(crate) unsafe fn from_raw(handle: NonNull) -> Self { + Self { handle } + } + + pub(crate) unsafe fn ref_from_raw(handle: NonNull) -> Ref { + unsafe { Ref::new(Self { handle }) } + } + + pub fn new() -> Ref { + let raw = unsafe { BNCreateReportCollection() }; + unsafe { Self::ref_from_raw(NonNull::new(raw).unwrap()) } + } + + pub fn show(&self, title: &str) { + let title = title.to_cstr(); + unsafe { BNShowReportCollection(title.as_ptr(), self.handle.as_ptr()) } + } + + pub fn count(&self) -> usize { + unsafe { BNGetReportCollectionCount(self.handle.as_ptr()) } + } + + fn report_type(&self, i: usize) -> ReportType { + unsafe { BNGetReportType(self.handle.as_ptr(), i) } + } + + pub fn get(&self, i: usize) -> Report<'_> { + Report::new(self, i) + } + + fn view(&self, i: usize) -> Option> { + let raw = unsafe { BNGetReportView(self.handle.as_ptr(), i) }; + if raw.is_null() { + return None; + } + Some(unsafe { BinaryView::ref_from_raw(raw) }) + } + + fn title(&self, i: usize) -> String { + let raw = unsafe { BNGetReportTitle(self.handle.as_ptr(), i) }; + unsafe { BnString::into_string(raw) } + } + + fn contents(&self, i: usize) -> String { + let raw = unsafe { BNGetReportContents(self.handle.as_ptr(), i) }; + unsafe { BnString::into_string(raw) } + } + + fn plain_text(&self, i: usize) -> String { + let raw = unsafe { BNGetReportPlainText(self.handle.as_ptr(), i) }; + unsafe { BnString::into_string(raw) } + } + + fn flow_graph(&self, i: usize) -> Ref { + let raw = unsafe { BNGetReportFlowGraph(self.handle.as_ptr(), i) }; + unsafe { FlowGraph::ref_from_raw(raw) } + } + + pub fn add_text(&self, view: &BinaryView, title: &str, contents: &str) { + let title = title.to_cstr(); + let contents = contents.to_cstr(); + unsafe { + BNAddPlainTextReportToCollection( + self.handle.as_ptr(), + view.handle, + title.as_ptr(), + contents.as_ptr(), + ) + } + } + + pub fn add_markdown(&self, view: &BinaryView, title: &str, contents: &str, plaintext: &str) { + let title = title.to_cstr(); + let contents = contents.to_cstr(); + let plaintext = plaintext.to_cstr(); + unsafe { + BNAddMarkdownReportToCollection( + self.handle.as_ptr(), + view.handle, + title.as_ptr(), + contents.as_ptr(), + plaintext.as_ptr(), + ) + } + } + + pub fn add_html(&self, view: &BinaryView, title: &str, contents: &str, plaintext: &str) { + let title = title.to_cstr(); + let contents = contents.to_cstr(); + let plaintext = plaintext.to_cstr(); + unsafe { + BNAddHTMLReportToCollection( + self.handle.as_ptr(), + view.handle, + title.as_ptr(), + contents.as_ptr(), + plaintext.as_ptr(), + ) + } + } + + pub fn add_graph(&self, view: &BinaryView, title: &str, graph: &FlowGraph) { + let title = title.to_cstr(); + unsafe { + BNAddGraphReportToCollection( + self.handle.as_ptr(), + view.handle, + title.as_ptr(), + graph.handle, + ) + } + } + + fn update_report_flow_graph(&self, i: usize, graph: &FlowGraph) { + unsafe { BNUpdateReportFlowGraph(self.handle.as_ptr(), i, graph.handle) } + } + + pub fn iter(&self) -> ReportCollectionIter<'_> { + ReportCollectionIter::new(self) + } +} + +unsafe impl RefCountable for ReportCollection { + unsafe fn inc_ref(handle: &Self) -> Ref { + let raw = unsafe { BNNewReportCollectionReference(handle.handle.as_ptr()) }; + unsafe { Self::ref_from_raw(NonNull::new(raw).unwrap()) } + } + + unsafe fn dec_ref(handle: &Self) { + unsafe { BNFreeReportCollection(handle.handle.as_ptr()) } + } +} + +impl ToOwned for ReportCollection { + type Owned = Ref; + + fn to_owned(&self) -> Self::Owned { + unsafe { RefCountable::inc_ref(self) } + } +} + +impl<'a> IntoIterator for &'a ReportCollection { + type Item = Report<'a>; + type IntoIter = ReportCollectionIter<'a>; + + fn into_iter(self) -> Self::IntoIter { + self.iter() + } +} + +pub enum Report<'a> { + PlainText(ReportPlainText<'a>), + Markdown(ReportMarkdown<'a>), + Html(ReportHtml<'a>), + FlowGraph(ReportFlowGraph<'a>), +} + +impl<'a> Report<'a> { + fn new(collection: &'a ReportCollection, index: usize) -> Self { + let inner = ReportInner { collection, index }; + match inner.type_() { + ReportType::PlainTextReportType => Report::PlainText(ReportPlainText(inner)), + ReportType::MarkdownReportType => Report::Markdown(ReportMarkdown(inner)), + ReportType::HTMLReportType => Report::Html(ReportHtml(inner)), + ReportType::FlowGraphReportType => Report::FlowGraph(ReportFlowGraph(inner)), + } + } + + fn _inner(&self) -> &ReportInner<'a> { + match self { + Report::PlainText(ReportPlainText(x)) + | Report::Markdown(ReportMarkdown(x)) + | Report::Html(ReportHtml(x)) + | Report::FlowGraph(ReportFlowGraph(x)) => x, + } + } + + pub fn view(&self) -> Option> { + self._inner().view() + } + + pub fn title(&self) -> String { + self._inner().title() + } +} + +pub struct ReportPlainText<'a>(ReportInner<'a>); + +impl ReportPlainText<'_> { + pub fn contents(&self) -> String { + self.0.contents() + } +} + +pub struct ReportMarkdown<'a>(ReportInner<'a>); + +impl ReportMarkdown<'_> { + pub fn contents(&self) -> String { + self.0.contents() + } + + pub fn plaintext(&self) -> String { + self.0.plain_text() + } +} + +pub struct ReportHtml<'a>(ReportInner<'a>); + +impl ReportHtml<'_> { + pub fn contents(&self) -> String { + self.0.contents() + } + + pub fn plaintext(&self) -> String { + self.0.plain_text() + } +} + +pub struct ReportFlowGraph<'a>(ReportInner<'a>); + +impl ReportFlowGraph<'_> { + pub fn flow_graph(&self) -> Ref { + self.0.flow_graph() + } + + pub fn update_report_flow_graph(&self, graph: &FlowGraph) { + self.0.update_report_flow_graph(graph) + } +} + +struct ReportInner<'a> { + collection: &'a ReportCollection, + index: usize, +} + +impl ReportInner<'_> { + fn type_(&self) -> ReportType { + self.collection.report_type(self.index) + } + + fn view(&self) -> Option> { + self.collection.view(self.index) + } + + fn title(&self) -> String { + self.collection.title(self.index) + } + + fn contents(&self) -> String { + self.collection.contents(self.index) + } + + fn plain_text(&self) -> String { + self.collection.plain_text(self.index) + } + + fn flow_graph(&self) -> Ref { + self.collection.flow_graph(self.index) + } + + fn update_report_flow_graph(&self, graph: &FlowGraph) { + self.collection.update_report_flow_graph(self.index, graph) + } +} + +pub struct ReportCollectionIter<'a> { + report: &'a ReportCollection, + current_index: usize, + count: usize, +} + +impl<'a> ReportCollectionIter<'a> { + pub fn new(report: &'a ReportCollection) -> Self { + Self { + report, + current_index: 0, + count: report.count(), + } + } + + pub fn collection(&self) -> &ReportCollection { + self.report + } +} + +impl<'a> Iterator for ReportCollectionIter<'a> { + type Item = Report<'a>; + + fn next(&mut self) -> Option { + (self.current_index < self.count).then(|| { + let result = Report::new(self.report, self.current_index); + self.current_index += 1; + result + }) + } +} diff --git a/rust/src/language_representation.rs b/rust/src/language_representation.rs new file mode 100644 index 0000000000..536203fe71 --- /dev/null +++ b/rust/src/language_representation.rs @@ -0,0 +1,628 @@ +use std::ffi::{c_char, c_void}; +use std::mem::MaybeUninit; +use std::ptr::NonNull; + +use binaryninjacore_sys::*; + +use crate::architecture::{Architecture, CoreArchitecture}; +use crate::basic_block::{BasicBlock, BlockContext}; +use crate::binary_view::BinaryView; +use crate::disassembly::{DisassemblySettings, DisassemblyTextLine}; +use crate::function::{Function, HighlightColor}; +use crate::high_level_il::token_emitter::HighLevelILTokenEmitter; +use crate::high_level_il::{HighLevelILFunction, HighLevelInstructionIndex}; +use crate::line_formatter::CoreLineFormatter; +use crate::rc::{Array, CoreArrayProvider, CoreArrayProviderInner, Ref, RefCountable}; +use crate::string::{BnString, IntoCStr}; +use crate::type_parser::CoreTypeParser; +use crate::type_printer::CoreTypePrinter; + +pub type InstructionTextTokenContext = BNInstructionTextTokenContext; +pub type ScopeType = BNScopeType; +pub type BraceRequirement = BNBraceRequirement; +pub type SymbolDisplayType = BNSymbolDisplayType; +pub type OperatorPrecedence = BNOperatorPrecedence; +pub type SymbolDisplayResult = BNSymbolDisplayResult; + +pub fn register_language_representation_function_type< + C: LanguageRepresentationFunctionType, + F: FnOnce(CoreLanguageRepresentationFunctionType) -> C, +>( + creator: F, + name: &str, +) -> CoreLanguageRepresentationFunctionType { + let custom = Box::leak(Box::new(MaybeUninit::uninit())); + let mut callbacks = BNCustomLanguageRepresentationFunctionType { + context: custom as *mut MaybeUninit as *mut c_void, + create: Some(cb_create::), + isValid: Some(cb_is_valid::), + getTypePrinter: Some(cb_get_type_printer::), + getTypeParser: Some(cb_get_type_parser::), + getLineFormatter: Some(cb_get_line_formatter::), + getFunctionTypeTokens: Some(cb_get_function_type_tokens::), + freeLines: Some(cb_free_lines), + }; + let name = name.to_cstr(); + let core = + unsafe { BNRegisterLanguageRepresentationFunctionType(name.as_ptr(), &mut callbacks) }; + let core = + unsafe { CoreLanguageRepresentationFunctionType::from_raw(NonNull::new(core).unwrap()) }; + custom.write(creator(core)); + core +} + +pub trait LanguageRepresentationFunction: Send + Sync { + fn on_token_emitter_init(&self, tokens: &HighLevelILTokenEmitter); + + fn expr_text( + &self, + il: &HighLevelILFunction, + expr_index: HighLevelInstructionIndex, + tokens: &HighLevelILTokenEmitter, + settings: &DisassemblySettings, + as_full_ast: bool, + precedence: OperatorPrecedence, + statement: bool, + ); + + fn begin_lines( + &self, + il: &HighLevelILFunction, + expr_index: HighLevelInstructionIndex, + tokens: &HighLevelILTokenEmitter, + ); + + fn end_lines( + &self, + il: &HighLevelILFunction, + expr_index: HighLevelInstructionIndex, + tokens: &HighLevelILTokenEmitter, + ); + + fn comment_start_string(&self) -> &str; + + fn comment_end_string(&self) -> &str; + + fn annotation_start_string(&self) -> &str; + + fn annotation_end_string(&self) -> &str; +} + +pub trait LanguageRepresentationFunctionType: Send + Sync { + fn create( + &self, + arch: &CoreArchitecture, + owner: &Function, + high_level_il: &HighLevelILFunction, + ) -> Ref; + + fn is_valid(&self, view: &BinaryView) -> bool; + + fn type_printer(&self) -> Option { + None + } + + fn type_parser(&self) -> Option { + None + } + + fn line_formatter(&self) -> Option { + None + } + + fn function_type_tokens( + &self, + func: &Function, + settings: &DisassemblySettings, + ) -> Vec; +} + +// NOTE static, it never gets freed, so we can clone/copy it +#[repr(transparent)] +#[derive(Clone, Copy)] +pub struct CoreLanguageRepresentationFunctionType { + handle: NonNull, +} + +impl CoreLanguageRepresentationFunctionType { + pub(crate) unsafe fn from_raw(handle: NonNull) -> Self { + Self { handle } + } + + pub(crate) fn as_raw(&self) -> *mut BNLanguageRepresentationFunctionType { + self.handle.as_ptr() + } + + pub fn from_name(name: &str) -> Option { + let name = name.to_cstr(); + let result = unsafe { BNGetLanguageRepresentationFunctionTypeByName(name.as_ptr()) }; + NonNull::new(result).map(|handle| unsafe { Self::from_raw(handle) }) + } + + pub fn all() -> Array { + let mut count = 0; + let result = unsafe { BNGetLanguageRepresentationFunctionTypeList(&mut count) }; + unsafe { Array::new(result, count, ()) } + } + + pub fn tokens( + &self, + func: &Function, + settings: &DisassemblySettings, + ) -> Array { + let mut count = 0; + let result = unsafe { + BNGetLanguageRepresentationFunctionTypeFunctionTypeTokens( + self.handle.as_ptr(), + func.handle, + settings.handle, + &mut count, + ) + }; + unsafe { Array::new(result, count, ()) } + } + + pub fn name(&self) -> BnString { + unsafe { + BnString::from_raw(BNGetLanguageRepresentationFunctionTypeName( + self.handle.as_ptr(), + )) + } + } + + pub fn create(&self, func: &Function) -> Ref { + let repr_func = unsafe { + BNCreateLanguageRepresentationFunction( + self.handle.as_ptr(), + func.arch().handle, + func.handle, + match func.high_level_il(false) { + Ok(hlil) => hlil.handle, + Err(_) => std::ptr::null_mut(), + }, + ) + }; + + unsafe { + CoreLanguageRepresentationFunction::ref_from_raw(NonNull::new(repr_func).unwrap()) + } + } + + pub fn is_valid(&self, view: &BinaryView) -> bool { + unsafe { BNIsLanguageRepresentationFunctionTypeValid(self.handle.as_ptr(), view.handle) } + } + + pub fn printer(&self) -> CoreTypePrinter { + let type_printer = + unsafe { BNGetLanguageRepresentationFunctionTypePrinter(self.handle.as_ptr()) }; + unsafe { CoreTypePrinter::from_raw(NonNull::new(type_printer).unwrap()) } + } + + pub fn parser(&self) -> CoreTypeParser { + let type_parser = + unsafe { BNGetLanguageRepresentationFunctionTypeParser(self.handle.as_ptr()) }; + unsafe { CoreTypeParser::from_raw(NonNull::new(type_parser).unwrap()) } + } + + pub fn line_formatter(&self) -> CoreLineFormatter { + let formatter = + unsafe { BNGetLanguageRepresentationFunctionTypeLineFormatter(self.handle.as_ptr()) }; + CoreLineFormatter::from_raw(NonNull::new(formatter).unwrap()) + } +} + +impl CoreArrayProvider for CoreLanguageRepresentationFunctionType { + type Raw = *mut BNLanguageRepresentationFunctionType; + type Context = (); + type Wrapped<'a> = &'a CoreLanguageRepresentationFunctionType; +} + +unsafe impl CoreArrayProviderInner for CoreLanguageRepresentationFunctionType { + unsafe fn free(raw: *mut Self::Raw, _count: usize, _context: &Self::Context) { + BNFreeLanguageRepresentationFunctionTypeList(raw) + } + + unsafe fn wrap_raw<'a>(raw: &'a Self::Raw, _context: &'a Self::Context) -> Self::Wrapped<'a> { + // SAFETY: CoreLanguageRepresentationFunctionType and BNCoreLanguageRepresentationFunctionType + // transparent + std::mem::transmute::< + &*mut BNLanguageRepresentationFunctionType, + &CoreLanguageRepresentationFunctionType, + >(raw) + } +} + +pub struct CoreLanguageRepresentationFunction { + handle: NonNull, +} + +impl CoreLanguageRepresentationFunction { + pub(crate) unsafe fn ref_from_raw( + handle: NonNull, + ) -> Ref { + unsafe { Ref::new(Self { handle }) } + } + + pub fn new( + repr_type: &CoreLanguageRepresentationFunctionType, + repr_context: C, + arch: &A, + func: &Function, + high_level_il: &HighLevelILFunction, + ) -> Ref { + let core_arch: &CoreArchitecture = arch.as_ref(); + let context: &mut C = Box::leak(Box::new(repr_context)); + let mut callbacks = BNCustomLanguageRepresentationFunction { + context: context as *mut C as *mut c_void, + freeObject: Some(cb_free_object::), + externalRefTaken: Some(cb_external_ref_taken::), + externalRefReleased: Some(cb_external_ref_released::), + initTokenEmitter: Some(cb_init_token_emitter::), + getExprText: Some(cb_get_expr_text::), + beginLines: Some(cb_begin_lines::), + endLines: Some(cb_end_lines::), + getCommentStartString: Some(cb_get_comment_start_string::), + getCommentEndString: Some(cb_get_comment_end_string::), + getAnnotationStartString: Some(cb_get_annotation_start_string::), + getAnnotationEndString: Some(cb_get_annotation_end_string::), + }; + let handle = unsafe { + BNCreateCustomLanguageRepresentationFunction( + repr_type.as_raw(), + core_arch.handle, + func.handle, + high_level_il.handle, + &mut callbacks, + ) + }; + unsafe { Self::ref_from_raw(NonNull::new(handle).unwrap()) } + } + + pub fn expr_text( + &self, + il: &HighLevelILFunction, + expr_index: HighLevelInstructionIndex, + settings: &DisassemblySettings, + as_full_ast: bool, + precedence: OperatorPrecedence, + statement: bool, + ) -> Array { + let mut count = 0; + let result = unsafe { + BNGetLanguageRepresentationFunctionExprText( + self.handle.as_ptr(), + il.handle, + expr_index.0, + settings.handle, + as_full_ast, + precedence, + statement, + &mut count, + ) + }; + unsafe { Array::new(result, count, ()) } + } + + pub fn linear_lines( + &self, + il: &HighLevelILFunction, + expr_index: HighLevelInstructionIndex, + settings: &DisassemblySettings, + as_full_ast: bool, + ) -> Array { + let mut count = 0; + let result = unsafe { + BNGetLanguageRepresentationFunctionLinearLines( + self.handle.as_ptr(), + il.handle, + expr_index.0, + settings.handle, + as_full_ast, + &mut count, + ) + }; + unsafe { Array::new(result, count, ()) } + } + + pub fn block_lines( + &self, + block: &BasicBlock, + settings: &DisassemblySettings, + ) -> Array { + let mut count = 0; + let result = unsafe { + BNGetLanguageRepresentationFunctionBlockLines( + self.handle.as_ptr(), + block.handle, + settings.handle, + &mut count, + ) + }; + unsafe { Array::new(result, count, ()) } + } + + pub fn highlight(&self, block: &BasicBlock) -> HighlightColor { + let result = unsafe { + BNGetLanguageRepresentationFunctionHighlight(self.handle.as_ptr(), block.handle) + }; + result.into() + } + + pub fn get_type(&self) -> CoreLanguageRepresentationFunctionType { + let repr_type = unsafe { BNGetLanguageRepresentationType(self.handle.as_ptr()) }; + unsafe { + CoreLanguageRepresentationFunctionType::from_raw(NonNull::new(repr_type).unwrap()) + } + } + + pub fn arch(&self) -> CoreArchitecture { + let arch = unsafe { BNGetLanguageRepresentationArchitecture(self.handle.as_ptr()) }; + unsafe { CoreArchitecture::from_raw(arch) } + } + + pub fn owner_function(&self) -> Ref { + let func = unsafe { BNGetLanguageRepresentationOwnerFunction(self.handle.as_ptr()) }; + unsafe { Function::ref_from_raw(func) } + } + + pub fn hlil(&self) -> Ref { + let hlil = unsafe { BNGetLanguageRepresentationILFunction(self.handle.as_ptr()) }; + unsafe { HighLevelILFunction::ref_from_raw(hlil, false) } + } + + pub fn comment_start_string(&self) -> BnString { + unsafe { + BnString::from_raw(BNGetLanguageRepresentationFunctionCommentStartString( + self.handle.as_ptr(), + )) + } + } + + pub fn comment_end_string(&self) -> BnString { + unsafe { + BnString::from_raw(BNGetLanguageRepresentationFunctionCommentEndString( + self.handle.as_ptr(), + )) + } + } + + pub fn annotation_start_string(&self) -> BnString { + unsafe { + BnString::from_raw(BNGetLanguageRepresentationFunctionAnnotationStartString( + self.handle.as_ptr(), + )) + } + } + + pub fn annotation_end_string(&self) -> BnString { + unsafe { + BnString::from_raw(BNGetLanguageRepresentationFunctionAnnotationEndString( + self.handle.as_ptr(), + )) + } + } +} + +unsafe impl RefCountable for CoreLanguageRepresentationFunction { + unsafe fn inc_ref(handle: &Self) -> Ref { + Self::ref_from_raw( + NonNull::new(BNNewLanguageRepresentationFunctionReference( + handle.handle.as_ptr(), + )) + .unwrap(), + ) + } + + unsafe fn dec_ref(handle: &Self) { + BNFreeLanguageRepresentationFunction(handle.handle.as_ptr()) + } +} + +impl ToOwned for CoreLanguageRepresentationFunction { + type Owned = Ref; + + fn to_owned(&self) -> Self::Owned { + unsafe { ::inc_ref(self) } + } +} + +unsafe extern "C" fn cb_create( + ctxt: *mut c_void, + arch: *mut BNArchitecture, + owner: *mut BNFunction, + high_level_il: *mut BNHighLevelILFunction, +) -> *mut BNLanguageRepresentationFunction { + let ctxt = ctxt as *mut C; + let arch = CoreArchitecture::from_raw(arch); + let owner = Function::from_raw(owner); + let high_level_il = HighLevelILFunction { + full_ast: false, + handle: high_level_il, + }; + let result = (*ctxt).create(&arch, &owner, &high_level_il); + Ref::into_raw(result).handle.as_ptr() +} + +unsafe extern "C" fn cb_is_valid( + ctxt: *mut c_void, + view: *mut BNBinaryView, +) -> bool { + let ctxt = ctxt as *mut C; + let view = BinaryView::from_raw(view); + (*ctxt).is_valid(&view) +} + +unsafe extern "C" fn cb_get_type_printer( + ctxt: *mut c_void, +) -> *mut BNTypePrinter { + let ctxt = ctxt as *mut C; + match (*ctxt).type_printer() { + None => std::ptr::null_mut(), + Some(printer) => printer.handle.as_ptr(), + } +} + +unsafe extern "C" fn cb_get_type_parser( + ctxt: *mut c_void, +) -> *mut BNTypeParser { + let ctxt = ctxt as *mut C; + match (*ctxt).type_parser() { + None => std::ptr::null_mut(), + Some(parser) => parser.handle.as_ptr(), + } +} + +unsafe extern "C" fn cb_get_line_formatter( + ctxt: *mut c_void, +) -> *mut BNLineFormatter { + let ctxt = ctxt as *mut C; + match (*ctxt).line_formatter() { + None => std::ptr::null_mut(), + Some(formatter) => formatter.handle.as_ptr(), + } +} + +unsafe extern "C" fn cb_get_function_type_tokens( + ctxt: *mut c_void, + func: *mut BNFunction, + settings: *mut BNDisassemblySettings, + count: *mut usize, +) -> *mut BNDisassemblyTextLine { + let ctxt = ctxt as *mut C; + let func = Function::from_raw(func); + let settings = DisassemblySettings { handle: settings }; + let result = (*ctxt).function_type_tokens(&func, &settings); + *count = result.len(); + let result: Box<[BNDisassemblyTextLine]> = result + .into_iter() + .map(DisassemblyTextLine::into_raw) + .collect(); + // NOTE freed by function_type_free_lines_ffi + Box::leak(result).as_mut_ptr() +} + +unsafe extern "C" fn cb_free_lines( + _ctxt: *mut c_void, + lines: *mut BNDisassemblyTextLine, + count: usize, +) { + let lines: Box<[BNDisassemblyTextLine]> = + Box::from_raw(core::slice::from_raw_parts_mut(lines, count)); + for line in lines { + DisassemblyTextLine::free_raw(line); + } +} + +unsafe extern "C" fn cb_free_object(ctxt: *mut c_void) { + let ctxt = ctxt as *mut C; + drop(Box::from_raw(ctxt)) +} + +unsafe extern "C" fn cb_external_ref_taken(_ctxt: *mut c_void) { + // TODO Make an Arc? conflict with free? +} + +unsafe extern "C" fn cb_external_ref_released( + _ctxt: *mut c_void, +) { + // TODO Make an Arc? conflict with free? +} + +unsafe extern "C" fn cb_init_token_emitter( + ctxt: *mut c_void, + tokens: *mut BNHighLevelILTokenEmitter, +) { + let ctxt = ctxt as *mut C; + let tokens = HighLevelILTokenEmitter::from_raw(NonNull::new(tokens).unwrap()); + (*ctxt).on_token_emitter_init(&tokens) +} + +unsafe extern "C" fn cb_get_expr_text( + ctxt: *mut c_void, + il: *mut BNHighLevelILFunction, + expr_index: usize, + tokens: *mut BNHighLevelILTokenEmitter, + settings: *mut BNDisassemblySettings, + as_full_ast: bool, + precedence: BNOperatorPrecedence, + statement: bool, +) { + let ctxt = ctxt as *mut C; + let il = HighLevelILFunction { + full_ast: as_full_ast, + handle: il, + }; + let tokens = HighLevelILTokenEmitter::from_raw(NonNull::new(tokens).unwrap()); + let settings = DisassemblySettings { handle: settings }; + (*ctxt).expr_text( + &il, + expr_index.into(), + &tokens, + &settings, + as_full_ast, + precedence, + statement, + ); +} + +unsafe extern "C" fn cb_begin_lines( + ctxt: *mut c_void, + il: *mut BNHighLevelILFunction, + expr_index: usize, + tokens: *mut BNHighLevelILTokenEmitter, +) { + let ctxt = ctxt as *mut C; + let il = HighLevelILFunction { + full_ast: false, + handle: il, + }; + let tokens = HighLevelILTokenEmitter::from_raw(NonNull::new(tokens).unwrap()); + (*ctxt).begin_lines(&il, expr_index.into(), &tokens) +} + +unsafe extern "C" fn cb_end_lines( + ctxt: *mut c_void, + il: *mut BNHighLevelILFunction, + expr_index: usize, + tokens: *mut BNHighLevelILTokenEmitter, +) { + let ctxt = ctxt as *mut C; + let il = HighLevelILFunction { + full_ast: false, + handle: il, + }; + let tokens = HighLevelILTokenEmitter::from_raw(NonNull::new(tokens).unwrap()); + (*ctxt).end_lines(&il, expr_index.into(), &tokens) +} + +unsafe extern "C" fn cb_get_comment_start_string( + ctxt: *mut c_void, +) -> *mut c_char { + let ctxt = ctxt as *mut C; + let result = (*ctxt).comment_start_string(); + BnString::into_raw(BnString::new(result)) +} + +unsafe extern "C" fn cb_get_comment_end_string( + ctxt: *mut c_void, +) -> *mut c_char { + let ctxt = ctxt as *mut C; + let result = (*ctxt).comment_end_string(); + BnString::into_raw(BnString::new(result)) +} + +unsafe extern "C" fn cb_get_annotation_start_string( + ctxt: *mut c_void, +) -> *mut c_char { + let ctxt = ctxt as *mut C; + let result = (*ctxt).annotation_start_string(); + BnString::into_raw(BnString::new(result)) +} + +unsafe extern "C" fn cb_get_annotation_end_string( + ctxt: *mut c_void, +) -> *mut c_char { + let ctxt = ctxt as *mut C; + let result = (*ctxt).annotation_end_string(); + BnString::into_raw(BnString::new(result)) +} diff --git a/rust/src/lib.rs b/rust/src/lib.rs index a3df0cd350..05c0107727 100644 --- a/rust/src/lib.rs +++ b/rust/src/lib.rs @@ -26,15 +26,12 @@ #[macro_use] mod ffi; -mod operand_iter; pub mod architecture; pub mod background_task; pub mod base_detection; pub mod basic_block; -pub mod binary_reader; pub mod binary_view; -pub mod binary_writer; pub mod calling_convention; pub mod collaboration; pub mod command; @@ -57,6 +54,8 @@ pub mod function_recognizer; pub mod headless; pub mod high_level_il; pub mod interaction; +pub mod language_representation; +pub mod line_formatter; pub mod linear_view; pub mod logger; pub mod low_level_il; @@ -102,8 +101,8 @@ use std::cmp; use std::collections::HashMap; use std::ffi::{c_char, c_void, CStr}; use std::path::{Path, PathBuf}; -use string::BnStrCompatible; use string::BnString; +use string::IntoCStr; use string::IntoJson; use crate::progress::{NoProgressCallback, ProgressCallback}; @@ -128,7 +127,7 @@ pub fn load_with_progress( file_path: impl AsRef, mut progress: P, ) -> Option> { - let file_path = file_path.as_ref().into_bytes_with_nul(); + let file_path = file_path.as_ref().to_cstr(); let options = c""; let handle = unsafe { BNLoadFilename( @@ -193,13 +192,9 @@ where O: IntoJson, P: ProgressCallback, { - let file_path = file_path.as_ref().into_bytes_with_nul(); + let file_path = file_path.as_ref().to_cstr(); let options_or_default = if let Some(opt) = options { - opt.get_json_string() - .ok()? - .into_bytes_with_nul() - .as_ref() - .to_vec() + opt.get_json_string().ok()?.to_cstr().to_bytes().to_vec() } else { Metadata::new_of_type(MetadataType::KeyValueDataType) .get_json_string() @@ -247,11 +242,7 @@ where P: ProgressCallback, { let options_or_default = if let Some(opt) = options { - opt.get_json_string() - .ok()? - .into_bytes_with_nul() - .as_ref() - .to_vec() + opt.get_json_string().ok()?.to_cstr().to_bytes().to_vec() } else { Metadata::new_of_type(MetadataType::KeyValueDataType) .get_json_string() @@ -279,8 +270,8 @@ where 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()) + let install_dir_str = unsafe { BnString::into_string(install_dir_ptr) }; + PathBuf::from(install_dir_str) } pub fn bundled_plugin_directory() -> Result { @@ -288,19 +279,19 @@ pub fn bundled_plugin_directory() -> Result { if s.is_null() { return Err(()); } - Ok(PathBuf::from(unsafe { BnString::from_raw(s) }.to_string())) + Ok(PathBuf::from(unsafe { BnString::into_string(s) })) } 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) }; + let new_dir = new_dir.as_ref().to_cstr(); + unsafe { BNSetBundledPluginDirectory(new_dir.as_ptr()) }; } 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()) + let user_dir_str = unsafe { BnString::into_string(user_dir_ptr) }; + PathBuf::from(user_dir_str) } pub fn user_plugin_directory() -> Result { @@ -308,7 +299,8 @@ pub fn user_plugin_directory() -> Result { if s.is_null() { return Err(()); } - Ok(PathBuf::from(unsafe { BnString::from_raw(s) }.to_string())) + let user_plugin_dir_str = unsafe { BnString::into_string(s) }; + Ok(PathBuf::from(user_plugin_dir_str)) } pub fn repositories_directory() -> Result { @@ -316,14 +308,15 @@ pub fn repositories_directory() -> Result { if s.is_null() { return Err(()); } - Ok(PathBuf::from(unsafe { BnString::from_raw(s) }.to_string())) + let repo_dir_str = unsafe { BnString::into_string(s) }; + Ok(PathBuf::from(repo_dir_str)) } -pub fn settings_file_name() -> PathBuf { +pub fn settings_file_path() -> 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()) + let settings_file_path_str = unsafe { BnString::into_string(settings_file_name_ptr) }; + PathBuf::from(settings_file_path_str) } /// Write the installation directory of the currently running core instance to disk. @@ -334,33 +327,30 @@ pub fn save_last_run() { } 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) }; + let path_raw = path.as_ref().to_cstr(); + let s: *mut c_char = unsafe { BNGetPathRelativeToBundledPluginDirectory(path_raw.as_ptr()) }; if s.is_null() { return Err(()); } - Ok(PathBuf::from(unsafe { BnString::from_raw(s) }.to_string())) + Ok(PathBuf::from(unsafe { BnString::into_string(s) })) } 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) }; + let path_raw = path.as_ref().to_cstr(); + let s: *mut c_char = unsafe { BNGetPathRelativeToUserPluginDirectory(path_raw.as_ptr()) }; if s.is_null() { return Err(()); } - Ok(PathBuf::from(unsafe { BnString::from_raw(s) }.to_string())) + Ok(PathBuf::from(unsafe { BnString::into_string(s) })) } 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) }; + let path_raw = path.as_ref().to_cstr(); + let s: *mut c_char = unsafe { BNGetPathRelativeToUserDirectory(path_raw.as_ptr()) }; if s.is_null() { return Err(()); } - Ok(PathBuf::from(unsafe { BnString::from_raw(s) }.to_string())) + Ok(PathBuf::from(unsafe { BnString::into_string(s) })) } /// Returns if the running thread is the "main thread" @@ -433,8 +423,8 @@ pub trait ObjectDestructor: 'static + Sync + Sized { } } -pub fn version() -> BnString { - unsafe { BnString::from_raw(BNGetVersionString()) } +pub fn version() -> String { + unsafe { BnString::into_string(BNGetVersionString()) } } pub fn build_id() -> u32 { @@ -476,13 +466,20 @@ impl VersionInfo { } pub(crate) fn free_raw(value: BNVersionInfo) { - let _ = unsafe { BnString::from_raw(value.channel) }; + unsafe { BnString::free_raw(value.channel) }; } +} - pub fn from_string(string: S) -> Self { - let string = string.into_bytes_with_nul(); - let result = unsafe { BNParseVersionString(string.as_ref().as_ptr() as *const c_char) }; - Self::from_owned_raw(result) +impl TryFrom<&str> for VersionInfo { + type Error = (); + + fn try_from(value: &str) -> Result { + let string = value.to_cstr(); + let result = unsafe { BNParseVersionString(string.as_ptr()) }; + if result.build == 0 && result.channel.is_null() && result.major == 0 && result.minor == 0 { + return Err(()); + } + Ok(Self::from_owned_raw(result)) } } @@ -512,16 +509,16 @@ pub fn version_info() -> VersionInfo { VersionInfo::from_owned_raw(info_raw) } -pub fn serial_number() -> BnString { - unsafe { BnString::from_raw(BNGetSerialNumber()) } +pub fn serial_number() -> String { + unsafe { BnString::into_string(BNGetSerialNumber()) } } pub fn is_license_validated() -> bool { unsafe { BNIsLicenseValidated() } } -pub fn licensed_user_email() -> BnString { - unsafe { BnString::from_raw(BNGetLicensedUserEmail()) } +pub fn licensed_user_email() -> String { + unsafe { BnString::into_string(BNGetLicensedUserEmail()) } } pub fn license_path() -> PathBuf { @@ -538,21 +535,20 @@ pub fn license_count() -> i32 { /// 1. Check the BN_LICENSE environment variable /// 2. Check the Binary Ninja user directory for license.dat #[cfg(not(feature = "demo"))] -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) } +pub fn set_license(license: Option<&str>) { + let license = license.unwrap_or_default().to_cstr(); + unsafe { BNSetLicense(license.as_ptr()) } } #[cfg(feature = "demo")] -pub fn set_license(_license: Option) {} +pub fn set_license(_license: Option<&str>) {} -pub fn product() -> BnString { - unsafe { BnString::from_raw(BNGetProduct()) } +pub fn product() -> String { + unsafe { BnString::into_string(BNGetProduct()) } } -pub fn product_type() -> BnString { - unsafe { BnString::from_raw(BNGetProductType()) } +pub fn product_type() -> String { + unsafe { BnString::into_string(BNGetProductType()) } } pub fn license_expiration_time() -> std::time::SystemTime { @@ -564,10 +560,9 @@ pub fn is_ui_enabled() -> bool { unsafe { BNIsUIEnabled() } } -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) } +pub fn is_database(file: &Path) -> bool { + let filename = file.to_cstr(); + unsafe { BNIsDatabase(filename.as_ptr()) } } pub fn plugin_abi_version() -> u32 { @@ -594,16 +589,14 @@ pub fn plugin_ui_abi_minimum_version() -> u32 { BN_MINIMUM_UI_ABI_VERSION } -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_required_plugin_dependency(name: &str) { + let raw_name = name.to_cstr(); + unsafe { BNAddRequiredPluginDependency(raw_name.as_ptr()) }; } -pub fn add_optional_plugin_dependency(name: S) { - unsafe { - BNAddOptionalPluginDependency(name.into_bytes_with_nul().as_ref().as_ptr() as *const c_char) - }; +pub fn add_optional_plugin_dependency(name: &str) { + let raw_name = name.to_cstr(); + unsafe { BNAddOptionalPluginDependency(raw_name.as_ptr()) }; } // Provide ABI version automatically so that the core can verify binary compatibility diff --git a/rust/src/line_formatter.rs b/rust/src/line_formatter.rs new file mode 100644 index 0000000000..50f1900c31 --- /dev/null +++ b/rust/src/line_formatter.rs @@ -0,0 +1,167 @@ +use std::ffi::c_void; +use std::ptr::NonNull; + +use binaryninjacore_sys::*; + +use crate::disassembly::DisassemblyTextLine; +use crate::high_level_il::HighLevelILFunction; +use crate::rc::{Array, CoreArrayProvider, CoreArrayProviderInner, Ref}; +use crate::string::{raw_to_string, BnString, IntoCStr}; + +/// Register a [`LineFormatter`] with the API. +pub fn register_line_formatter(name: &str, formatter: C) -> CoreLineFormatter { + let custom = Box::leak(Box::new(formatter)); + let mut callbacks = BNCustomLineFormatter { + context: custom as *mut C as *mut c_void, + formatLines: Some(cb_format_lines::), + freeLines: Some(cb_free_lines), + }; + let name = name.to_cstr(); + let handle = unsafe { BNRegisterLineFormatter(name.as_ptr(), &mut callbacks) }; + CoreLineFormatter::from_raw(NonNull::new(handle).unwrap()) +} + +pub trait LineFormatter: Sized { + fn format_lines( + &self, + lines: &[DisassemblyTextLine], + settings: &LineFormatterSettings, + ) -> Vec; +} + +#[repr(transparent)] +pub struct CoreLineFormatter { + pub(crate) handle: NonNull, +} + +impl CoreLineFormatter { + pub fn from_raw(handle: NonNull) -> Self { + Self { handle } + } + + /// Get the default [`CoreLineFormatter`] if available, because the user might have disabled it. + pub fn default_if_available() -> Option { + Some(unsafe { Self::from_raw(NonNull::new(BNGetDefaultLineFormatter())?) }) + } + + pub fn all() -> Array { + let mut count = 0; + let result = unsafe { BNGetLineFormatterList(&mut count) }; + unsafe { Array::new(result, count, ()) } + } + + pub fn from_name(name: &str) -> Option { + let name_raw = name.to_cstr(); + let result = unsafe { BNGetLineFormatterByName(name_raw.as_ptr()) }; + NonNull::new(result).map(Self::from_raw) + } + + pub fn name(&self) -> BnString { + unsafe { BnString::from_raw(BNGetLineFormatterName(self.handle.as_ptr())) } + } +} + +impl CoreArrayProvider for CoreLineFormatter { + type Raw = *mut BNLineFormatter; + type Context = (); + type Wrapped<'a> = Self; +} + +unsafe impl CoreArrayProviderInner for CoreLineFormatter { + unsafe fn free(raw: *mut Self::Raw, _count: usize, _context: &Self::Context) { + BNFreeLineFormatterList(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(); + CoreLineFormatter::from_raw(handle) + } +} + +#[derive(Clone, Debug)] +pub struct LineFormatterSettings { + pub high_level_il: Option>, + pub desired_line_len: usize, + pub min_content_len: usize, + pub tab_width: usize, + pub lang_name: String, + pub comment_start: String, + pub comment_end: String, + pub annotation_start: String, + pub annotation_end: String, +} + +impl LineFormatterSettings { + pub(crate) fn from_raw(value: &BNLineFormatterSettings) -> Self { + Self { + high_level_il: match value.highLevelIL.is_null() { + false => Some( + unsafe { HighLevelILFunction::from_raw(value.highLevelIL, false) }.to_owned(), + ), + true => None, + }, + desired_line_len: value.desiredLineLength, + min_content_len: value.minimumContentLength, + tab_width: value.tabWidth, + lang_name: raw_to_string(value.languageName as *mut _).unwrap(), + comment_start: raw_to_string(value.commentStartString as *mut _).unwrap(), + comment_end: raw_to_string(value.commentEndString as *mut _).unwrap(), + annotation_start: raw_to_string(value.annotationStartString as *mut _).unwrap(), + annotation_end: raw_to_string(value.annotationEndString as *mut _).unwrap(), + } + } + + #[allow(unused)] + pub(crate) fn from_owned_raw(value: BNLineFormatterSettings) -> Self { + let owned = Self::from_raw(&value); + Self::free_raw(value); + owned + } + + #[allow(unused)] + pub(crate) fn free_raw(value: BNLineFormatterSettings) { + let _ = unsafe { HighLevelILFunction::ref_from_raw(value.highLevelIL, false) }; + let _ = unsafe { BnString::from_raw(value.languageName as *mut _) }; + let _ = unsafe { BnString::from_raw(value.commentStartString as *mut _) }; + let _ = unsafe { BnString::from_raw(value.commentEndString as *mut _) }; + let _ = unsafe { BnString::from_raw(value.annotationStartString as *mut _) }; + let _ = unsafe { BnString::from_raw(value.annotationEndString as *mut _) }; + } +} + +unsafe extern "C" fn cb_format_lines( + ctxt: *mut c_void, + in_lines: *mut BNDisassemblyTextLine, + in_count: usize, + raw_settings: *const BNLineFormatterSettings, + out_count: *mut usize, +) -> *mut BNDisassemblyTextLine { + // NOTE dropped by line_formatter_free_lines_ffi + let ctxt = ctxt as *mut C; + let lines_slice = core::slice::from_raw_parts(in_lines, in_count); + let lines: Vec<_> = lines_slice + .iter() + .map(DisassemblyTextLine::from_raw) + .collect(); + let settings = LineFormatterSettings::from_raw(&*raw_settings); + let result = (*ctxt).format_lines(&lines, &settings); + *out_count = result.len(); + let result: Box<[BNDisassemblyTextLine]> = result + .into_iter() + .map(DisassemblyTextLine::into_raw) + .collect(); + Box::leak(result).as_mut_ptr() +} + +unsafe extern "C" fn cb_free_lines( + _ctxt: *mut c_void, + raw_lines: *mut BNDisassemblyTextLine, + count: usize, +) { + let lines: Box<[BNDisassemblyTextLine]> = + Box::from_raw(core::slice::from_raw_parts_mut(raw_lines, count)); + for line in lines { + DisassemblyTextLine::free_raw(line); + } +} diff --git a/rust/src/linear_view.rs b/rust/src/linear_view.rs index ca35d525e7..db164278a6 100644 --- a/rust/src/linear_view.rs +++ b/rust/src/linear_view.rs @@ -267,7 +267,7 @@ impl LinearViewObjectIdentifier { } pub fn free_raw(value: BNLinearViewObjectIdentifier) { - let _ = unsafe { BnString::from_raw(value.name) }; + unsafe { BnString::free_raw(value.name) }; } } diff --git a/rust/src/logger.rs b/rust/src/logger.rs index c928287ff2..2735dd2f4b 100644 --- a/rust/src/logger.rs +++ b/rust/src/logger.rs @@ -35,10 +35,10 @@ use binaryninjacore_sys::{ }; use crate::rc::{Ref, RefCountable}; -use crate::string::BnString; +use crate::string::{raw_to_string, BnString, IntoCStr}; use log; use log::LevelFilter; -use std::ffi::{CStr, CString}; +use std::ffi::CString; use std::os::raw::{c_char, c_void}; use std::ptr::NonNull; @@ -66,8 +66,8 @@ impl Logger { } } - pub fn name(&self) -> BnString { - unsafe { BnString::from_raw(BNLoggerGetName(self.handle.as_ptr())) } + pub fn name(&self) -> String { + unsafe { BnString::into_string(BNLoggerGetName(self.handle.as_ptr())) } } pub fn session_id(&self) -> usize { @@ -138,15 +138,14 @@ impl log::Log for Ref { }; if let Ok(msg) = CString::new(format!("{}", record.args())) { - let percent_s = CString::new("%s").expect("'%s' has no null bytes"); - let logger_name = self.name(); + let logger_name = self.name().to_cstr(); unsafe { BNLog( self.session_id(), level, logger_name.as_ptr(), 0, - percent_s.as_ptr(), + c"%s".as_ptr(), msg.as_ptr(), ); } @@ -160,7 +159,7 @@ unsafe impl Send for Logger {} unsafe impl Sync for Logger {} pub trait LogListener: 'static + Sync { - fn log(&self, session: usize, level: Level, msg: &CStr, logger_name: &CStr, tid: usize); + fn log(&self, session: usize, level: Level, msg: &str, logger_name: &str, tid: usize); fn level(&self) -> Level; fn close(&self) {} } @@ -220,13 +219,9 @@ extern "C" fn cb_log( { ffi_wrap!("LogListener::log", unsafe { let listener = &*(ctxt as *const L); - listener.log( - session, - level, - CStr::from_ptr(msg), - CStr::from_ptr(logger_name), - tid, - ); + let msg_str = raw_to_string(msg).unwrap(); + let logger_name_str = raw_to_string(logger_name).unwrap(); + listener.log(session, level, &msg_str, &logger_name_str, tid); }) } diff --git a/rust/src/low_level_il.rs b/rust/src/low_level_il.rs index d6303c1342..010fa0bc00 100644 --- a/rust/src/low_level_il.rs +++ b/rust/src/low_level_il.rs @@ -12,6 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. +use std::borrow::Cow; use std::fmt; // TODO : provide some way to forbid emitting register reads for certain registers @@ -20,8 +21,8 @@ use std::fmt; // requirements on load/store memory address sizes? // can reg/set_reg be used with sizes that differ from what is in BNRegisterInfo? -use crate::architecture::Register as ArchReg; -use crate::architecture::{Architecture, RegisterId}; +use crate::architecture::{Architecture, Flag, RegisterId}; +use crate::architecture::{CoreRegister, Register as ArchReg}; use crate::function::Location; pub mod block; @@ -35,62 +36,173 @@ use self::expression::*; use self::function::*; use self::instruction::*; -pub type MutableLiftedILFunction = LowLevelILFunction>; -pub type LiftedILFunction = LowLevelILFunction>; -pub type MutableLiftedILExpr<'a, Arch, ReturnType> = - LowLevelILExpression<'a, Arch, Mutable, NonSSA, ReturnType>; -pub type RegularLowLevelILFunction = - LowLevelILFunction>; -pub type RegularLowLevelILInstruction<'a, Arch> = - LowLevelILInstruction<'a, Arch, Finalized, NonSSA>; -pub type RegularLowLevelILInstructionKind<'a, Arch> = - LowLevelILInstructionKind<'a, Arch, Finalized, NonSSA>; -pub type RegularLowLevelILExpression<'a, Arch, ReturnType> = - LowLevelILExpression<'a, Arch, Finalized, NonSSA, ReturnType>; -pub type RegularLowLevelILExpressionKind<'a, Arch> = - LowLevelILExpressionKind<'a, Arch, Finalized, NonSSA>; -pub type LowLevelILSSAFunction = LowLevelILFunction; +/// Regular low-level IL, if you are not modifying the functions IL or needing SSA, use this. +pub type LowLevelILRegularFunction = LowLevelILFunction; +pub type LowLevelILRegularInstruction<'a> = LowLevelILInstruction<'a, Finalized, NonSSA>; +pub type LowLevelILRegularInstructionKind<'a> = LowLevelILInstructionKind<'a, Finalized, NonSSA>; +pub type LowLevelILRegularExpression<'a, ReturnType> = + LowLevelILExpression<'a, Finalized, NonSSA, ReturnType>; +pub type LowLevelILRegularExpressionKind<'a> = LowLevelILExpressionKind<'a, Finalized, NonSSA>; + +/// Mutable low-level IL, used when lifting in architectures and modifying IL in workflow activities. +pub type LowLevelILMutableFunction = LowLevelILFunction; +pub type LowLevelILMutableExpression<'a, ReturnType> = + LowLevelILExpression<'a, Mutable, NonSSA, ReturnType>; + +/// SSA Variant of low-level IL, this can never be mutated directly. +pub type LowLevelILSSAFunction = LowLevelILFunction; + +#[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)] +pub struct LowLevelILTempRegister { + /// The temporary id for the register, this will **NOT** be the referenced id in the core. + /// + /// Do not attempt to pass this to the core. Use [`LowLevelILTempRegister::id`] instead. + temp_id: RegisterId, +} + +impl LowLevelILTempRegister { + pub fn new(temp_id: u32) -> Self { + Self { + temp_id: RegisterId(temp_id), + } + } + + pub fn from_id(id: RegisterId) -> Option { + match id.is_temporary() { + true => { + let temp_id = RegisterId(id.0 & 0x7fff_ffff); + Some(Self { temp_id }) + } + false => None, + } + } + + /// The temporary registers core id, with the temporary bit set. + pub fn id(&self) -> RegisterId { + RegisterId(self.temp_id.0 | 0x8000_0000) + } +} + +impl fmt::Debug for LowLevelILTempRegister { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "temp{}", self.temp_id) + } +} + +impl TryFrom for LowLevelILTempRegister { + type Error = (); + + fn try_from(value: RegisterId) -> Result { + Self::from_id(value).ok_or(()) + } +} + +impl From for LowLevelILTempRegister { + fn from(value: u32) -> Self { + Self::new(value) + } +} #[derive(Copy, Clone, PartialEq, Eq)] -pub enum LowLevelILRegister { - ArchReg(R), - // TODO: Might want to be changed to TempRegisterId. - // TODO: If we do that then we would need to get rid of `Register::id()` - Temp(u32), +pub enum LowLevelILRegisterKind { + Arch(R), + Temp(LowLevelILTempRegister), } -impl LowLevelILRegister { - fn id(&self) -> RegisterId { +impl LowLevelILRegisterKind { + pub fn from_raw(arch: &impl Architecture, val: RegisterId) -> Option { + match val.is_temporary() { + true => { + let temp_reg = LowLevelILTempRegister::from_id(val)?; + Some(LowLevelILRegisterKind::Temp(temp_reg)) + } + false => { + let arch_reg = arch.register_from_id(val)?; + Some(LowLevelILRegisterKind::Arch(arch_reg)) + } + } + } + + pub fn from_temp(temp: impl Into) -> Self { + LowLevelILRegisterKind::Temp(temp.into()) + } + + pub fn id(&self) -> RegisterId { + match *self { + LowLevelILRegisterKind::Arch(ref r) => r.id(), + LowLevelILRegisterKind::Temp(temp) => temp.id(), + } + } + + pub fn name(&self) -> Cow { match *self { - LowLevelILRegister::ArchReg(ref r) => r.id(), - LowLevelILRegister::Temp(id) => RegisterId(0x8000_0000 | id), + LowLevelILRegisterKind::Arch(ref r) => r.name(), + LowLevelILRegisterKind::Temp(temp) => Cow::Owned(format!("temp{}", temp.temp_id)), } } } -impl fmt::Debug for LowLevelILRegister { +impl fmt::Debug for LowLevelILRegisterKind { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match *self { - LowLevelILRegister::ArchReg(ref r) => write!(f, "{}", r.name().as_ref()), - LowLevelILRegister::Temp(id) => write!(f, "temp{}", id), + LowLevelILRegisterKind::Arch(ref r) => r.fmt(f), + LowLevelILRegisterKind::Temp(id) => id.fmt(f), } } } +impl From for LowLevelILRegisterKind { + fn from(reg: LowLevelILTempRegister) -> Self { + LowLevelILRegisterKind::Temp(reg) + } +} + #[derive(Copy, Clone, Debug)] -pub enum LowLevelILSSARegister { - Full(LowLevelILRegister, u32), // no such thing as partial access to a temp register, I think - Partial(R, u32, R), // partial accesses only possible for arch registers, I think +pub enum LowLevelILSSARegisterKind { + Full { + kind: LowLevelILRegisterKind, + version: u32, + }, + Partial { + full_reg: CoreRegister, + partial_reg: CoreRegister, + version: u32, + }, } -impl LowLevelILSSARegister { +impl LowLevelILSSARegisterKind { + pub fn new_full(kind: LowLevelILRegisterKind, version: u32) -> Self { + Self::Full { kind, version } + } + + pub fn new_partial(full_reg: CoreRegister, partial_reg: CoreRegister, version: u32) -> Self { + Self::Partial { + full_reg, + partial_reg, + version, + } + } + pub fn version(&self) -> u32 { match *self { - LowLevelILSSARegister::Full(_, ver) | LowLevelILSSARegister::Partial(_, ver, _) => ver, + LowLevelILSSARegisterKind::Full { version, .. } + | LowLevelILSSARegisterKind::Partial { version, .. } => version, } } } +#[derive(Copy, Clone, Debug)] +pub struct LowLevelILSSAFlag { + pub flag: F, + pub version: u32, +} + +impl LowLevelILSSAFlag { + pub fn new(flag: F, version: u32) -> Self { + Self { flag, version } + } +} + #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] pub enum VisitorAction { Descend, diff --git a/rust/src/low_level_il/block.rs b/rust/src/low_level_il/block.rs index ac0be449f5..fd3e7c81b3 100644 --- a/rust/src/low_level_il/block.rs +++ b/rust/src/low_level_il/block.rs @@ -15,38 +15,35 @@ use std::fmt::Debug; use std::ops::Range; -use crate::architecture::Architecture; use crate::basic_block::{BasicBlock, BlockContext}; use super::*; #[derive(Copy)] -pub struct LowLevelILBlock<'func, A, M, F> +pub struct LowLevelILBlock<'func, M, F> where - A: 'func + Architecture, M: FunctionMutability, F: FunctionForm, { - pub(crate) function: &'func LowLevelILFunction, + pub(crate) function: &'func LowLevelILFunction, } -impl<'func, A, M, F> BlockContext for LowLevelILBlock<'func, A, M, F> +impl<'func, M, F> BlockContext for LowLevelILBlock<'func, M, F> where - A: 'func + Architecture, M: FunctionMutability, F: FunctionForm, { - type Instruction = LowLevelILInstruction<'func, A, M, F>; + type Instruction = LowLevelILInstruction<'func, M, F>; type InstructionIndex = LowLevelInstructionIndex; - type Iter = LowLevelILBlockIter<'func, A, M, F>; + type Iter = LowLevelILBlockIter<'func, M, F>; - fn start(&self, block: &BasicBlock) -> LowLevelILInstruction<'func, A, M, F> { + fn start(&self, block: &BasicBlock) -> LowLevelILInstruction<'func, M, F> { self.function .instruction_from_index(block.start_index()) .unwrap() } - fn iter(&self, block: &BasicBlock) -> LowLevelILBlockIter<'func, A, M, F> { + fn iter(&self, block: &BasicBlock) -> LowLevelILBlockIter<'func, M, F> { LowLevelILBlockIter { function: self.function, range: (block.start_index().0)..(block.end_index().0), @@ -54,9 +51,8 @@ where } } -impl<'func, A, M, F> Debug for LowLevelILBlock<'func, A, M, F> +impl Debug for LowLevelILBlock<'_, M, F> where - A: 'func + Architecture + Debug, M: FunctionMutability, F: FunctionForm, { @@ -67,9 +63,8 @@ where } } -impl<'func, A, M, F> Clone for LowLevelILBlock<'func, A, M, F> +impl Clone for LowLevelILBlock<'_, M, F> where - A: 'func + Architecture, M: FunctionMutability, F: FunctionForm, { @@ -80,24 +75,22 @@ where } } -pub struct LowLevelILBlockIter<'func, A, M, F> +pub struct LowLevelILBlockIter<'func, M, F> where - A: 'func + Architecture, M: FunctionMutability, F: FunctionForm, { - function: &'func LowLevelILFunction, + function: &'func LowLevelILFunction, // TODO: Once step_trait is stable we can do Range range: Range, } -impl<'func, A, M, F> Iterator for LowLevelILBlockIter<'func, A, M, F> +impl<'func, M, F> Iterator for LowLevelILBlockIter<'func, M, F> where - A: 'func + Architecture, M: FunctionMutability, F: FunctionForm, { - type Item = LowLevelILInstruction<'func, A, M, F>; + type Item = LowLevelILInstruction<'func, M, F>; fn next(&mut self) -> Option { self.range diff --git a/rust/src/low_level_il/expression.rs b/rust/src/low_level_il/expression.rs index fd403c767e..8df8dfe0cf 100644 --- a/rust/src/low_level_il/expression.rs +++ b/rust/src/low_level_il/expression.rs @@ -19,9 +19,9 @@ use super::operation; use super::operation::Operation; use super::VisitorAction; use super::*; -use crate::architecture::Architecture; +use crate::architecture::CoreFlagWrite; use std::fmt; -use std::fmt::{Display, Formatter}; +use std::fmt::{Debug, Display, Formatter}; use std::marker::PhantomData; /// Used as a marker for an [`LowLevelILExpression`] that **can** produce a value. @@ -32,7 +32,7 @@ pub struct ValueExpr; #[derive(Copy, Clone, Debug)] pub struct VoidExpr; -pub trait ExpressionResultType: 'static {} +pub trait ExpressionResultType: 'static + Debug {} impl ExpressionResultType for ValueExpr {} impl ExpressionResultType for VoidExpr {} @@ -47,42 +47,39 @@ impl Display for LowLevelExpressionIndex { } // TODO: Probably want to rename this with a LowLevelIL prefix to avoid collisions when we add handlers for other ILs -pub trait ExpressionHandler<'func, A, M, F> +pub trait ExpressionHandler<'func, M, F> where - A: Architecture, M: FunctionMutability, F: FunctionForm, { - fn kind(&self) -> LowLevelILExpressionKind<'func, A, M, F>; + fn kind(&self) -> LowLevelILExpressionKind<'func, M, F>; fn visit_tree(&self, f: &mut T) -> VisitorAction where - T: FnMut(&LowLevelILExpression<'func, A, M, F, ValueExpr>) -> VisitorAction; + T: FnMut(&LowLevelILExpression<'func, M, F, ValueExpr>) -> VisitorAction; } -pub struct LowLevelILExpression<'func, A, M, F, R> +pub struct LowLevelILExpression<'func, M, F, R> where - A: 'func + Architecture, M: FunctionMutability, F: FunctionForm, R: ExpressionResultType, { - pub(crate) function: &'func LowLevelILFunction, + pub(crate) function: &'func LowLevelILFunction, pub index: LowLevelExpressionIndex, // tag the 'return' type of this expression pub(crate) _ty: PhantomData, } -impl<'func, A, M, F, R> LowLevelILExpression<'func, A, M, F, R> +impl<'func, M, F, R> LowLevelILExpression<'func, M, F, R> where - A: 'func + Architecture, M: FunctionMutability, F: FunctionForm, R: ExpressionResultType, { pub(crate) fn new( - function: &'func LowLevelILFunction, + function: &'func LowLevelILFunction, index: LowLevelExpressionIndex, ) -> Self { // TODO: Validate expression here? @@ -94,27 +91,24 @@ where } } -impl<'func, A, M, F, R> fmt::Debug for LowLevelILExpression<'func, A, M, F, R> +impl Debug for LowLevelILExpression<'_, M, F, R> where - A: 'func + Architecture, M: FunctionMutability, F: FunctionForm, R: ExpressionResultType, { fn fmt(&self, f: &mut Formatter) -> fmt::Result { - f.debug_struct("Expression") - .field("index", &self.index) - .finish() + let op = unsafe { BNGetLowLevelILByIndex(self.function.handle, self.index.0) }; + let kind = LowLevelILExpressionKind::from_raw(self.function, op, self.index); + kind.fmt(f) } } -impl<'func, A, M> ExpressionHandler<'func, A, M, SSA> - for LowLevelILExpression<'func, A, M, SSA, ValueExpr> +impl<'func, M> ExpressionHandler<'func, M, SSA> for LowLevelILExpression<'func, M, SSA, ValueExpr> where - A: 'func + Architecture, M: FunctionMutability, { - fn kind(&self) -> LowLevelILExpressionKind<'func, A, M, SSA> { + fn kind(&self) -> LowLevelILExpressionKind<'func, M, SSA> { #[allow(unused_imports)] use binaryninjacore_sys::BNLowLevelILOperation::*; let op = unsafe { BNGetLowLevelILByIndex(self.function.handle, self.index.0) }; @@ -122,13 +116,13 @@ where match op.operation { // Any invalid ops for SSA will be checked here. // SAFETY: We have checked for illegal operations. - _ => unsafe { LowLevelILExpressionKind::from_raw(self.function, op) }, + _ => LowLevelILExpressionKind::from_raw(self.function, op, self.index), } } fn visit_tree(&self, f: &mut T) -> VisitorAction where - T: FnMut(&LowLevelILExpression<'func, A, M, SSA, ValueExpr>) -> VisitorAction, + T: FnMut(&LowLevelILExpression<'func, M, SSA, ValueExpr>) -> VisitorAction, { // Visit the current expression. match f(self) { @@ -141,13 +135,12 @@ where } } -impl<'func, A, M> ExpressionHandler<'func, A, M, NonSSA> - for LowLevelILExpression<'func, A, M, NonSSA, ValueExpr> +impl<'func, M> ExpressionHandler<'func, M, NonSSA> + for LowLevelILExpression<'func, M, NonSSA, ValueExpr> where - A: 'func + Architecture, M: FunctionMutability, { - fn kind(&self) -> LowLevelILExpressionKind<'func, A, M, NonSSA> { + fn kind(&self) -> LowLevelILExpressionKind<'func, M, NonSSA> { #[allow(unused_imports)] use binaryninjacore_sys::BNLowLevelILOperation::*; let op = unsafe { BNGetLowLevelILByIndex(self.function.handle, self.index.0) }; @@ -155,15 +148,13 @@ where match op.operation { // Any invalid ops for Lifted IL will be checked here. // SAFETY: We have checked for illegal operations. - _ => unsafe { LowLevelILExpressionKind::from_raw(self.function, op) }, + _ => LowLevelILExpressionKind::from_raw(self.function, op, self.index), } } fn visit_tree(&self, f: &mut T) -> VisitorAction where - T: FnMut( - &LowLevelILExpression<'func, A, M, NonSSA, ValueExpr>, - ) -> VisitorAction, + T: FnMut(&LowLevelILExpression<'func, M, NonSSA, ValueExpr>) -> VisitorAction, { // Visit the current expression. match f(self) { @@ -176,271 +167,271 @@ where } } -impl<'func, A, M> ExpressionHandler<'func, A, M, NonSSA> - for LowLevelILExpression<'func, A, M, NonSSA, ValueExpr> +impl LowLevelILExpression<'_, Finalized, F, ValueExpr> where - A: 'func + Architecture, - M: FunctionMutability, -{ - fn kind(&self) -> LowLevelILExpressionKind<'func, A, M, NonSSA> { - use binaryninjacore_sys::BNLowLevelILOperation::*; - let op = unsafe { BNGetLowLevelILByIndex(self.function.handle, self.index.0) }; - match op.operation { - // Any invalid ops for Non-Lifted IL will be checked here. - LLIL_FLAG_COND => unreachable!("LLIL_FLAG_COND is only valid in Lifted IL"), - LLIL_FLAG_GROUP => unreachable!("LLIL_FLAG_GROUP is only valid in Lifted IL"), - // SAFETY: We have checked for illegal operations. - _ => unsafe { LowLevelILExpressionKind::from_raw(self.function, op) }, - } - } - - fn visit_tree(&self, f: &mut T) -> VisitorAction - where - T: FnMut( - &LowLevelILExpression<'func, A, M, NonSSA, ValueExpr>, - ) -> VisitorAction, - { - // Visit the current expression. - match f(self) { - VisitorAction::Descend => { - // Recursively visit sub expressions. - self.kind().visit_sub_expressions(|e| e.visit_tree(f)) - } - action => action, - } - } -} - -impl<'func, A, F> LowLevelILExpression<'func, A, Finalized, F, ValueExpr> -where - A: 'func + Architecture, F: FunctionForm, { // TODO possible values } #[derive(Debug)] -pub enum LowLevelILExpressionKind<'func, A, M, F> +pub enum LowLevelILExpressionKind<'func, M, F> where - A: 'func + Architecture, M: FunctionMutability, F: FunctionForm, { - Load(Operation<'func, A, M, F, operation::Load>), - Pop(Operation<'func, A, M, F, operation::Pop>), - Reg(Operation<'func, A, M, F, operation::Reg>), - RegSplit(Operation<'func, A, M, F, operation::RegSplit>), - Const(Operation<'func, A, M, F, operation::Const>), - ConstPtr(Operation<'func, A, M, F, operation::Const>), - Flag(Operation<'func, A, M, F, operation::Flag>), - FlagBit(Operation<'func, A, M, F, operation::FlagBit>), - ExternPtr(Operation<'func, A, M, F, operation::Extern>), - - RegStackPop(Operation<'func, A, M, F, operation::RegStackPop>), - - Add(Operation<'func, A, M, F, operation::BinaryOp>), - Adc(Operation<'func, A, M, F, operation::BinaryOpCarry>), - Sub(Operation<'func, A, M, F, operation::BinaryOp>), - Sbb(Operation<'func, A, M, F, operation::BinaryOpCarry>), - And(Operation<'func, A, M, F, operation::BinaryOp>), - Or(Operation<'func, A, M, F, operation::BinaryOp>), - Xor(Operation<'func, A, M, F, operation::BinaryOp>), - Lsl(Operation<'func, A, M, F, operation::BinaryOp>), - Lsr(Operation<'func, A, M, F, operation::BinaryOp>), - Asr(Operation<'func, A, M, F, operation::BinaryOp>), - Rol(Operation<'func, A, M, F, operation::BinaryOp>), - Rlc(Operation<'func, A, M, F, operation::BinaryOpCarry>), - Ror(Operation<'func, A, M, F, operation::BinaryOp>), - Rrc(Operation<'func, A, M, F, operation::BinaryOpCarry>), - Mul(Operation<'func, A, M, F, operation::BinaryOp>), - - MulsDp(Operation<'func, A, M, F, operation::BinaryOp>), - MuluDp(Operation<'func, A, M, F, operation::BinaryOp>), - - Divu(Operation<'func, A, M, F, operation::BinaryOp>), - Divs(Operation<'func, A, M, F, operation::BinaryOp>), - - DivuDp(Operation<'func, A, M, F, operation::DoublePrecDivOp>), - DivsDp(Operation<'func, A, M, F, operation::DoublePrecDivOp>), - - Modu(Operation<'func, A, M, F, operation::BinaryOp>), - Mods(Operation<'func, A, M, F, operation::BinaryOp>), - - ModuDp(Operation<'func, A, M, F, operation::DoublePrecDivOp>), - ModsDp(Operation<'func, A, M, F, operation::DoublePrecDivOp>), - - Neg(Operation<'func, A, M, F, operation::UnaryOp>), - Not(Operation<'func, A, M, F, operation::UnaryOp>), - Sx(Operation<'func, A, M, F, operation::UnaryOp>), - Zx(Operation<'func, A, M, F, operation::UnaryOp>), - LowPart(Operation<'func, A, M, F, operation::UnaryOp>), + Load(Operation<'func, M, F, operation::Load>), + LoadSsa(Operation<'func, M, F, operation::LoadSsa>), + Pop(Operation<'func, M, F, operation::Pop>), + Reg(Operation<'func, M, F, operation::Reg>), + RegSsa(Operation<'func, M, F, operation::RegSsa>), + RegPartialSsa(Operation<'func, M, F, operation::RegPartialSsa>), + RegSplit(Operation<'func, M, F, operation::RegSplit>), + RegSplitSsa(Operation<'func, M, F, operation::RegSplitSsa>), + Const(Operation<'func, M, F, operation::Const>), + ConstPtr(Operation<'func, M, F, operation::Const>), + Flag(Operation<'func, M, F, operation::Flag>), + FlagBit(Operation<'func, M, F, operation::FlagBit>), + ExternPtr(Operation<'func, M, F, operation::Extern>), + + RegStackPop(Operation<'func, M, F, operation::RegStackPop>), + + CallOutputSsa(Operation<'func, M, F, operation::CallOutputSsa>), + CallParamSsa(Operation<'func, M, F, operation::CallParamSsa>), + CallStackSsa(Operation<'func, M, F, operation::CallStackSsa>), + + Add(Operation<'func, M, F, operation::BinaryOp>), + Adc(Operation<'func, M, F, operation::BinaryOpCarry>), + Sub(Operation<'func, M, F, operation::BinaryOp>), + Sbb(Operation<'func, M, F, operation::BinaryOpCarry>), + And(Operation<'func, M, F, operation::BinaryOp>), + Or(Operation<'func, M, F, operation::BinaryOp>), + Xor(Operation<'func, M, F, operation::BinaryOp>), + Lsl(Operation<'func, M, F, operation::BinaryOp>), + Lsr(Operation<'func, M, F, operation::BinaryOp>), + Asr(Operation<'func, M, F, operation::BinaryOp>), + Rol(Operation<'func, M, F, operation::BinaryOp>), + Rlc(Operation<'func, M, F, operation::BinaryOpCarry>), + Ror(Operation<'func, M, F, operation::BinaryOp>), + Rrc(Operation<'func, M, F, operation::BinaryOpCarry>), + Mul(Operation<'func, M, F, operation::BinaryOp>), + + MulsDp(Operation<'func, M, F, operation::BinaryOp>), + MuluDp(Operation<'func, M, F, operation::BinaryOp>), + + Divu(Operation<'func, M, F, operation::BinaryOp>), + Divs(Operation<'func, M, F, operation::BinaryOp>), + + DivuDp(Operation<'func, M, F, operation::DoublePrecDivOp>), + DivsDp(Operation<'func, M, F, operation::DoublePrecDivOp>), + + Modu(Operation<'func, M, F, operation::BinaryOp>), + Mods(Operation<'func, M, F, operation::BinaryOp>), + + ModuDp(Operation<'func, M, F, operation::DoublePrecDivOp>), + ModsDp(Operation<'func, M, F, operation::DoublePrecDivOp>), + + Neg(Operation<'func, M, F, operation::UnaryOp>), + Not(Operation<'func, M, F, operation::UnaryOp>), + Sx(Operation<'func, M, F, operation::UnaryOp>), + Zx(Operation<'func, M, F, operation::UnaryOp>), + LowPart(Operation<'func, M, F, operation::UnaryOp>), // Valid only in Lifted IL - FlagCond(Operation<'func, A, M, NonSSA, operation::FlagCond>), + FlagCond(Operation<'func, M, F, operation::FlagCond>), // Valid only in Lifted IL - FlagGroup(Operation<'func, A, M, NonSSA, operation::FlagGroup>), - - CmpE(Operation<'func, A, M, F, operation::Condition>), - CmpNe(Operation<'func, A, M, F, operation::Condition>), - CmpSlt(Operation<'func, A, M, F, operation::Condition>), - CmpUlt(Operation<'func, A, M, F, operation::Condition>), - CmpSle(Operation<'func, A, M, F, operation::Condition>), - CmpUle(Operation<'func, A, M, F, operation::Condition>), - CmpSge(Operation<'func, A, M, F, operation::Condition>), - CmpUge(Operation<'func, A, M, F, operation::Condition>), - CmpSgt(Operation<'func, A, M, F, operation::Condition>), - CmpUgt(Operation<'func, A, M, F, operation::Condition>), - - //TestBit(Operation<'func, A, M, F, operation::TestBit>), // TODO - BoolToInt(Operation<'func, A, M, F, operation::UnaryOp>), - - Fadd(Operation<'func, A, M, F, operation::BinaryOp>), - Fsub(Operation<'func, A, M, F, operation::BinaryOp>), - Fmul(Operation<'func, A, M, F, operation::BinaryOp>), - Fdiv(Operation<'func, A, M, F, operation::BinaryOp>), - Fsqrt(Operation<'func, A, M, F, operation::UnaryOp>), - Fneg(Operation<'func, A, M, F, operation::UnaryOp>), - Fabs(Operation<'func, A, M, F, operation::UnaryOp>), - FloatToInt(Operation<'func, A, M, F, operation::UnaryOp>), - IntToFloat(Operation<'func, A, M, F, operation::UnaryOp>), - FloatConv(Operation<'func, A, M, F, operation::UnaryOp>), - RoundToInt(Operation<'func, A, M, F, operation::UnaryOp>), - Floor(Operation<'func, A, M, F, operation::UnaryOp>), - Ceil(Operation<'func, A, M, F, operation::UnaryOp>), - Ftrunc(Operation<'func, A, M, F, operation::UnaryOp>), - - FcmpE(Operation<'func, A, M, F, operation::Condition>), - FcmpNE(Operation<'func, A, M, F, operation::Condition>), - FcmpLT(Operation<'func, A, M, F, operation::Condition>), - FcmpLE(Operation<'func, A, M, F, operation::Condition>), - FcmpGE(Operation<'func, A, M, F, operation::Condition>), - FcmpGT(Operation<'func, A, M, F, operation::Condition>), - FcmpO(Operation<'func, A, M, F, operation::Condition>), - FcmpUO(Operation<'func, A, M, F, operation::Condition>), + FlagGroup(Operation<'func, M, F, operation::FlagGroup>), + + CmpE(Operation<'func, M, F, operation::Condition>), + CmpNe(Operation<'func, M, F, operation::Condition>), + CmpSlt(Operation<'func, M, F, operation::Condition>), + CmpUlt(Operation<'func, M, F, operation::Condition>), + CmpSle(Operation<'func, M, F, operation::Condition>), + CmpUle(Operation<'func, M, F, operation::Condition>), + CmpSge(Operation<'func, M, F, operation::Condition>), + CmpUge(Operation<'func, M, F, operation::Condition>), + CmpSgt(Operation<'func, M, F, operation::Condition>), + CmpUgt(Operation<'func, M, F, operation::Condition>), + + //TestBit(Operation<'func, M, F, operation::TestBit>), // TODO + BoolToInt(Operation<'func, M, F, operation::UnaryOp>), + + Fadd(Operation<'func, M, F, operation::BinaryOp>), + Fsub(Operation<'func, M, F, operation::BinaryOp>), + Fmul(Operation<'func, M, F, operation::BinaryOp>), + Fdiv(Operation<'func, M, F, operation::BinaryOp>), + Fsqrt(Operation<'func, M, F, operation::UnaryOp>), + Fneg(Operation<'func, M, F, operation::UnaryOp>), + Fabs(Operation<'func, M, F, operation::UnaryOp>), + FloatToInt(Operation<'func, M, F, operation::UnaryOp>), + IntToFloat(Operation<'func, M, F, operation::UnaryOp>), + FloatConv(Operation<'func, M, F, operation::UnaryOp>), + RoundToInt(Operation<'func, M, F, operation::UnaryOp>), + Floor(Operation<'func, M, F, operation::UnaryOp>), + Ceil(Operation<'func, M, F, operation::UnaryOp>), + Ftrunc(Operation<'func, M, F, operation::UnaryOp>), + + FcmpE(Operation<'func, M, F, operation::Condition>), + FcmpNE(Operation<'func, M, F, operation::Condition>), + FcmpLT(Operation<'func, M, F, operation::Condition>), + FcmpLE(Operation<'func, M, F, operation::Condition>), + FcmpGE(Operation<'func, M, F, operation::Condition>), + FcmpGT(Operation<'func, M, F, operation::Condition>), + FcmpO(Operation<'func, M, F, operation::Condition>), + FcmpUO(Operation<'func, M, F, operation::Condition>), // TODO ADD_OVERFLOW - Unimpl(Operation<'func, A, M, F, operation::NoArgs>), - UnimplMem(Operation<'func, A, M, F, operation::UnimplMem>), + Unimpl(Operation<'func, M, F, operation::NoArgs>), + UnimplMem(Operation<'func, M, F, operation::UnimplMem>), - Undef(Operation<'func, A, M, F, operation::NoArgs>), + Undef(Operation<'func, M, F, operation::NoArgs>), } -impl<'func, A, M, F> LowLevelILExpressionKind<'func, A, M, F> +impl<'func, M, F> LowLevelILExpressionKind<'func, M, F> where - A: 'func + Architecture, M: FunctionMutability, F: FunctionForm, { - // TODO: Document what "unchecked" means and how to consume this safely. - pub(crate) unsafe fn from_raw( - function: &'func LowLevelILFunction, + pub(crate) fn from_raw( + function: &'func LowLevelILFunction, op: BNLowLevelILInstruction, + index: LowLevelExpressionIndex, ) -> Self { use binaryninjacore_sys::BNLowLevelILOperation::*; match op.operation { - LLIL_LOAD | LLIL_LOAD_SSA => { - LowLevelILExpressionKind::Load(Operation::new(function, op)) + LLIL_LOAD => LowLevelILExpressionKind::Load(Operation::new(function, op, index)), + LLIL_LOAD_SSA => LowLevelILExpressionKind::LoadSsa(Operation::new(function, op, index)), + LLIL_POP => LowLevelILExpressionKind::Pop(Operation::new(function, op, index)), + LLIL_REG => LowLevelILExpressionKind::Reg(Operation::new(function, op, index)), + LLIL_REG_SSA => LowLevelILExpressionKind::RegSsa(Operation::new(function, op, index)), + LLIL_REG_SSA_PARTIAL => { + LowLevelILExpressionKind::RegPartialSsa(Operation::new(function, op, index)) } - LLIL_POP => LowLevelILExpressionKind::Pop(Operation::new(function, op)), - LLIL_REG | LLIL_REG_SSA | LLIL_REG_SSA_PARTIAL => { - LowLevelILExpressionKind::Reg(Operation::new(function, op)) + LLIL_REG_SPLIT => { + LowLevelILExpressionKind::RegSplit(Operation::new(function, op, index)) } - LLIL_REG_SPLIT | LLIL_REG_SPLIT_SSA => { - LowLevelILExpressionKind::RegSplit(Operation::new(function, op)) + LLIL_REG_SPLIT_SSA => { + LowLevelILExpressionKind::RegSplitSsa(Operation::new(function, op, index)) + } + LLIL_CONST => LowLevelILExpressionKind::Const(Operation::new(function, op, index)), + LLIL_CONST_PTR => { + LowLevelILExpressionKind::ConstPtr(Operation::new(function, op, index)) } - LLIL_CONST => LowLevelILExpressionKind::Const(Operation::new(function, op)), - LLIL_CONST_PTR => LowLevelILExpressionKind::ConstPtr(Operation::new(function, op)), LLIL_FLAG | LLIL_FLAG_SSA => { - LowLevelILExpressionKind::Flag(Operation::new(function, op)) + LowLevelILExpressionKind::Flag(Operation::new(function, op, index)) } LLIL_FLAG_BIT | LLIL_FLAG_BIT_SSA => { - LowLevelILExpressionKind::FlagBit(Operation::new(function, op)) + LowLevelILExpressionKind::FlagBit(Operation::new(function, op, index)) + } + LLIL_EXTERN_PTR => { + LowLevelILExpressionKind::ExternPtr(Operation::new(function, op, index)) } - LLIL_EXTERN_PTR => LowLevelILExpressionKind::ExternPtr(Operation::new(function, op)), LLIL_REG_STACK_POP => { - LowLevelILExpressionKind::RegStackPop(Operation::new(function, op)) + LowLevelILExpressionKind::RegStackPop(Operation::new(function, op, index)) } - LLIL_ADD => LowLevelILExpressionKind::Add(Operation::new(function, op)), - LLIL_ADC => LowLevelILExpressionKind::Adc(Operation::new(function, op)), - LLIL_SUB => LowLevelILExpressionKind::Sub(Operation::new(function, op)), - LLIL_SBB => LowLevelILExpressionKind::Sbb(Operation::new(function, op)), - LLIL_AND => LowLevelILExpressionKind::And(Operation::new(function, op)), - LLIL_OR => LowLevelILExpressionKind::Or(Operation::new(function, op)), - LLIL_XOR => LowLevelILExpressionKind::Xor(Operation::new(function, op)), - LLIL_LSL => LowLevelILExpressionKind::Lsl(Operation::new(function, op)), - LLIL_LSR => LowLevelILExpressionKind::Lsr(Operation::new(function, op)), - LLIL_ASR => LowLevelILExpressionKind::Asr(Operation::new(function, op)), - LLIL_ROL => LowLevelILExpressionKind::Rol(Operation::new(function, op)), - LLIL_RLC => LowLevelILExpressionKind::Rlc(Operation::new(function, op)), - LLIL_ROR => LowLevelILExpressionKind::Ror(Operation::new(function, op)), - LLIL_RRC => LowLevelILExpressionKind::Rrc(Operation::new(function, op)), - LLIL_MUL => LowLevelILExpressionKind::Mul(Operation::new(function, op)), - - LLIL_MULU_DP => LowLevelILExpressionKind::MuluDp(Operation::new(function, op)), - LLIL_MULS_DP => LowLevelILExpressionKind::MulsDp(Operation::new(function, op)), - - LLIL_DIVU => LowLevelILExpressionKind::Divu(Operation::new(function, op)), - LLIL_DIVS => LowLevelILExpressionKind::Divs(Operation::new(function, op)), - - LLIL_DIVU_DP => LowLevelILExpressionKind::DivuDp(Operation::new(function, op)), - LLIL_DIVS_DP => LowLevelILExpressionKind::DivsDp(Operation::new(function, op)), - - LLIL_MODU => LowLevelILExpressionKind::Modu(Operation::new(function, op)), - LLIL_MODS => LowLevelILExpressionKind::Mods(Operation::new(function, op)), - - LLIL_MODU_DP => LowLevelILExpressionKind::ModuDp(Operation::new(function, op)), - LLIL_MODS_DP => LowLevelILExpressionKind::ModsDp(Operation::new(function, op)), - - LLIL_NEG => LowLevelILExpressionKind::Neg(Operation::new(function, op)), - LLIL_NOT => LowLevelILExpressionKind::Not(Operation::new(function, op)), - - LLIL_SX => LowLevelILExpressionKind::Sx(Operation::new(function, op)), - LLIL_ZX => LowLevelILExpressionKind::Zx(Operation::new(function, op)), - LLIL_LOW_PART => LowLevelILExpressionKind::LowPart(Operation::new(function, op)), - - LLIL_CMP_E => LowLevelILExpressionKind::CmpE(Operation::new(function, op)), - LLIL_CMP_NE => LowLevelILExpressionKind::CmpNe(Operation::new(function, op)), - LLIL_CMP_SLT => LowLevelILExpressionKind::CmpSlt(Operation::new(function, op)), - LLIL_CMP_ULT => LowLevelILExpressionKind::CmpUlt(Operation::new(function, op)), - LLIL_CMP_SLE => LowLevelILExpressionKind::CmpSle(Operation::new(function, op)), - LLIL_CMP_ULE => LowLevelILExpressionKind::CmpUle(Operation::new(function, op)), - LLIL_CMP_SGE => LowLevelILExpressionKind::CmpSge(Operation::new(function, op)), - LLIL_CMP_UGE => LowLevelILExpressionKind::CmpUge(Operation::new(function, op)), - LLIL_CMP_SGT => LowLevelILExpressionKind::CmpSgt(Operation::new(function, op)), - LLIL_CMP_UGT => LowLevelILExpressionKind::CmpUgt(Operation::new(function, op)), - - LLIL_BOOL_TO_INT => LowLevelILExpressionKind::BoolToInt(Operation::new(function, op)), - - LLIL_FADD => LowLevelILExpressionKind::Fadd(Operation::new(function, op)), - LLIL_FSUB => LowLevelILExpressionKind::Fsub(Operation::new(function, op)), - LLIL_FMUL => LowLevelILExpressionKind::Fmul(Operation::new(function, op)), - LLIL_FDIV => LowLevelILExpressionKind::Fdiv(Operation::new(function, op)), - - LLIL_FSQRT => LowLevelILExpressionKind::Fsqrt(Operation::new(function, op)), - LLIL_FNEG => LowLevelILExpressionKind::Fneg(Operation::new(function, op)), - LLIL_FABS => LowLevelILExpressionKind::Fabs(Operation::new(function, op)), - LLIL_FLOAT_TO_INT => LowLevelILExpressionKind::FloatToInt(Operation::new(function, op)), - LLIL_INT_TO_FLOAT => LowLevelILExpressionKind::IntToFloat(Operation::new(function, op)), - LLIL_FLOAT_CONV => LowLevelILExpressionKind::FloatConv(Operation::new(function, op)), - LLIL_ROUND_TO_INT => LowLevelILExpressionKind::RoundToInt(Operation::new(function, op)), - LLIL_FLOOR => LowLevelILExpressionKind::Floor(Operation::new(function, op)), - LLIL_CEIL => LowLevelILExpressionKind::Ceil(Operation::new(function, op)), - LLIL_FTRUNC => LowLevelILExpressionKind::Ftrunc(Operation::new(function, op)), - - LLIL_FCMP_E => LowLevelILExpressionKind::FcmpE(Operation::new(function, op)), - LLIL_FCMP_NE => LowLevelILExpressionKind::FcmpNE(Operation::new(function, op)), - LLIL_FCMP_LT => LowLevelILExpressionKind::FcmpLT(Operation::new(function, op)), - LLIL_FCMP_LE => LowLevelILExpressionKind::FcmpLE(Operation::new(function, op)), - LLIL_FCMP_GT => LowLevelILExpressionKind::FcmpGT(Operation::new(function, op)), - LLIL_FCMP_GE => LowLevelILExpressionKind::FcmpGE(Operation::new(function, op)), - LLIL_FCMP_O => LowLevelILExpressionKind::FcmpO(Operation::new(function, op)), - LLIL_FCMP_UO => LowLevelILExpressionKind::FcmpUO(Operation::new(function, op)), - - LLIL_UNIMPL => LowLevelILExpressionKind::Unimpl(Operation::new(function, op)), - LLIL_UNIMPL_MEM => LowLevelILExpressionKind::UnimplMem(Operation::new(function, op)), + LLIL_CALL_OUTPUT_SSA => { + LowLevelILExpressionKind::CallOutputSsa(Operation::new(function, op, index)) + } + LLIL_CALL_PARAM => { + LowLevelILExpressionKind::CallParamSsa(Operation::new(function, op, index)) + } + LLIL_CALL_STACK_SSA => { + LowLevelILExpressionKind::CallStackSsa(Operation::new(function, op, index)) + } + + LLIL_ADD => LowLevelILExpressionKind::Add(Operation::new(function, op, index)), + LLIL_ADC => LowLevelILExpressionKind::Adc(Operation::new(function, op, index)), + LLIL_SUB => LowLevelILExpressionKind::Sub(Operation::new(function, op, index)), + LLIL_SBB => LowLevelILExpressionKind::Sbb(Operation::new(function, op, index)), + LLIL_AND => LowLevelILExpressionKind::And(Operation::new(function, op, index)), + LLIL_OR => LowLevelILExpressionKind::Or(Operation::new(function, op, index)), + LLIL_XOR => LowLevelILExpressionKind::Xor(Operation::new(function, op, index)), + LLIL_LSL => LowLevelILExpressionKind::Lsl(Operation::new(function, op, index)), + LLIL_LSR => LowLevelILExpressionKind::Lsr(Operation::new(function, op, index)), + LLIL_ASR => LowLevelILExpressionKind::Asr(Operation::new(function, op, index)), + LLIL_ROL => LowLevelILExpressionKind::Rol(Operation::new(function, op, index)), + LLIL_RLC => LowLevelILExpressionKind::Rlc(Operation::new(function, op, index)), + LLIL_ROR => LowLevelILExpressionKind::Ror(Operation::new(function, op, index)), + LLIL_RRC => LowLevelILExpressionKind::Rrc(Operation::new(function, op, index)), + LLIL_MUL => LowLevelILExpressionKind::Mul(Operation::new(function, op, index)), + + LLIL_MULU_DP => LowLevelILExpressionKind::MuluDp(Operation::new(function, op, index)), + LLIL_MULS_DP => LowLevelILExpressionKind::MulsDp(Operation::new(function, op, index)), + + LLIL_DIVU => LowLevelILExpressionKind::Divu(Operation::new(function, op, index)), + LLIL_DIVS => LowLevelILExpressionKind::Divs(Operation::new(function, op, index)), + + LLIL_DIVU_DP => LowLevelILExpressionKind::DivuDp(Operation::new(function, op, index)), + LLIL_DIVS_DP => LowLevelILExpressionKind::DivsDp(Operation::new(function, op, index)), + + LLIL_MODU => LowLevelILExpressionKind::Modu(Operation::new(function, op, index)), + LLIL_MODS => LowLevelILExpressionKind::Mods(Operation::new(function, op, index)), + + LLIL_MODU_DP => LowLevelILExpressionKind::ModuDp(Operation::new(function, op, index)), + LLIL_MODS_DP => LowLevelILExpressionKind::ModsDp(Operation::new(function, op, index)), + + LLIL_NEG => LowLevelILExpressionKind::Neg(Operation::new(function, op, index)), + LLIL_NOT => LowLevelILExpressionKind::Not(Operation::new(function, op, index)), + + LLIL_SX => LowLevelILExpressionKind::Sx(Operation::new(function, op, index)), + LLIL_ZX => LowLevelILExpressionKind::Zx(Operation::new(function, op, index)), + LLIL_LOW_PART => LowLevelILExpressionKind::LowPart(Operation::new(function, op, index)), + + LLIL_CMP_E => LowLevelILExpressionKind::CmpE(Operation::new(function, op, index)), + LLIL_CMP_NE => LowLevelILExpressionKind::CmpNe(Operation::new(function, op, index)), + LLIL_CMP_SLT => LowLevelILExpressionKind::CmpSlt(Operation::new(function, op, index)), + LLIL_CMP_ULT => LowLevelILExpressionKind::CmpUlt(Operation::new(function, op, index)), + LLIL_CMP_SLE => LowLevelILExpressionKind::CmpSle(Operation::new(function, op, index)), + LLIL_CMP_ULE => LowLevelILExpressionKind::CmpUle(Operation::new(function, op, index)), + LLIL_CMP_SGE => LowLevelILExpressionKind::CmpSge(Operation::new(function, op, index)), + LLIL_CMP_UGE => LowLevelILExpressionKind::CmpUge(Operation::new(function, op, index)), + LLIL_CMP_SGT => LowLevelILExpressionKind::CmpSgt(Operation::new(function, op, index)), + LLIL_CMP_UGT => LowLevelILExpressionKind::CmpUgt(Operation::new(function, op, index)), + + LLIL_BOOL_TO_INT => { + LowLevelILExpressionKind::BoolToInt(Operation::new(function, op, index)) + } + + LLIL_FADD => LowLevelILExpressionKind::Fadd(Operation::new(function, op, index)), + LLIL_FSUB => LowLevelILExpressionKind::Fsub(Operation::new(function, op, index)), + LLIL_FMUL => LowLevelILExpressionKind::Fmul(Operation::new(function, op, index)), + LLIL_FDIV => LowLevelILExpressionKind::Fdiv(Operation::new(function, op, index)), + + LLIL_FSQRT => LowLevelILExpressionKind::Fsqrt(Operation::new(function, op, index)), + LLIL_FNEG => LowLevelILExpressionKind::Fneg(Operation::new(function, op, index)), + LLIL_FABS => LowLevelILExpressionKind::Fabs(Operation::new(function, op, index)), + LLIL_FLOAT_TO_INT => { + LowLevelILExpressionKind::FloatToInt(Operation::new(function, op, index)) + } + LLIL_INT_TO_FLOAT => { + LowLevelILExpressionKind::IntToFloat(Operation::new(function, op, index)) + } + LLIL_FLOAT_CONV => { + LowLevelILExpressionKind::FloatConv(Operation::new(function, op, index)) + } + LLIL_ROUND_TO_INT => { + LowLevelILExpressionKind::RoundToInt(Operation::new(function, op, index)) + } + LLIL_FLOOR => LowLevelILExpressionKind::Floor(Operation::new(function, op, index)), + LLIL_CEIL => LowLevelILExpressionKind::Ceil(Operation::new(function, op, index)), + LLIL_FTRUNC => LowLevelILExpressionKind::Ftrunc(Operation::new(function, op, index)), + + LLIL_FCMP_E => LowLevelILExpressionKind::FcmpE(Operation::new(function, op, index)), + LLIL_FCMP_NE => LowLevelILExpressionKind::FcmpNE(Operation::new(function, op, index)), + LLIL_FCMP_LT => LowLevelILExpressionKind::FcmpLT(Operation::new(function, op, index)), + LLIL_FCMP_LE => LowLevelILExpressionKind::FcmpLE(Operation::new(function, op, index)), + LLIL_FCMP_GT => LowLevelILExpressionKind::FcmpGT(Operation::new(function, op, index)), + LLIL_FCMP_GE => LowLevelILExpressionKind::FcmpGE(Operation::new(function, op, index)), + LLIL_FCMP_O => LowLevelILExpressionKind::FcmpO(Operation::new(function, op, index)), + LLIL_FCMP_UO => LowLevelILExpressionKind::FcmpUO(Operation::new(function, op, index)), + + LLIL_UNIMPL => LowLevelILExpressionKind::Unimpl(Operation::new(function, op, index)), + LLIL_UNIMPL_MEM => { + LowLevelILExpressionKind::UnimplMem(Operation::new(function, op, index)) + } // TODO TEST_BIT ADD_OVERFLOW LLIL_REG_STACK_PUSH LLIL_REG_STACK_POP _ => { @@ -451,7 +442,7 @@ where op.address ); - LowLevelILExpressionKind::Undef(Operation::new(function, op)) + LowLevelILExpressionKind::Undef(Operation::new(function, op, index)) } } } @@ -472,7 +463,7 @@ where } _ => Some(self.raw_struct().size), - //TestBit(Operation<'func, A, M, F, operation::TestBit>), // TODO + //TestBit(Operation<'func, M, F, operation::TestBit>), // TODO } } @@ -492,7 +483,7 @@ where } } - pub fn as_cmp_op(&self) -> Option<&Operation<'func, A, M, F, operation::Condition>> { + pub fn as_cmp_op(&self) -> Option<&Operation<'func, M, F, operation::Condition>> { use self::LowLevelILExpressionKind::*; match *self { @@ -504,7 +495,7 @@ where } } - pub fn as_binary_op(&self) -> Option<&Operation<'func, A, M, F, operation::BinaryOp>> { + pub fn as_binary_op(&self) -> Option<&Operation<'func, M, F, operation::BinaryOp>> { use self::LowLevelILExpressionKind::*; match *self { @@ -516,9 +507,7 @@ where } } - pub fn as_binary_op_carry( - &self, - ) -> Option<&Operation<'func, A, M, F, operation::BinaryOpCarry>> { + pub fn as_binary_op_carry(&self) -> Option<&Operation<'func, M, F, operation::BinaryOpCarry>> { use self::LowLevelILExpressionKind::*; match *self { @@ -529,7 +518,7 @@ where pub fn as_double_prec_div_op( &self, - ) -> Option<&Operation<'func, A, M, F, operation::DoublePrecDivOp>> { + ) -> Option<&Operation<'func, M, F, operation::DoublePrecDivOp>> { use self::LowLevelILExpressionKind::*; match *self { @@ -538,7 +527,7 @@ where } } - pub fn as_unary_op(&self) -> Option<&Operation<'func, A, M, F, operation::UnaryOp>> { + pub fn as_unary_op(&self) -> Option<&Operation<'func, M, F, operation::UnaryOp>> { use self::LowLevelILExpressionKind::*; match *self { @@ -552,7 +541,7 @@ where pub fn visit_sub_expressions(&self, mut visitor: T) -> VisitorAction where - T: FnMut(LowLevelILExpression<'func, A, M, F, ValueExpr>) -> VisitorAction, + T: FnMut(LowLevelILExpression<'func, M, F, ValueExpr>) -> VisitorAction, { use LowLevelILExpressionKind::*; @@ -599,12 +588,21 @@ where visit!(op.mem_expr()); } Load(ref op) => { - visit!(op.source_mem_expr()); + visit!(op.source_expr()); } - // Do not have any sub expressions. - Pop(_) | Reg(_) | RegSplit(_) | Const(_) | ConstPtr(_) | Flag(_) | FlagBit(_) - | ExternPtr(_) | FlagCond(_) | FlagGroup(_) | Unimpl(_) | Undef(_) | RegStackPop(_) => { + LoadSsa(ref op) => { + visit!(op.source_expr()); + } + CallParamSsa(ref op) => { + for param_expr in op.param_exprs() { + visit!(param_expr); + } } + // Do not have any sub expressions. + Pop(_) | Reg(_) | RegSsa(_) | RegPartialSsa(_) | RegSplit(_) | RegSplitSsa(_) + | Const(_) | ConstPtr(_) | Flag(_) | FlagBit(_) | ExternPtr(_) | FlagCond(_) + | FlagGroup(_) | Unimpl(_) | Undef(_) | RegStackPop(_) | CallOutputSsa(_) + | CallStackSsa(_) => {} } VisitorAction::Sibling @@ -628,12 +626,20 @@ where Load(ref op) => &op.op, + LoadSsa(ref op) => &op.op, + Pop(ref op) => &op.op, Reg(ref op) => &op.op, + RegSsa(ref op) => &op.op, + + RegPartialSsa(ref op) => &op.op, + RegSplit(ref op) => &op.op, + RegSplitSsa(ref op) => &op.op, + Flag(ref op) => &op.op, FlagBit(ref op) => &op.op, @@ -644,6 +650,10 @@ where RegStackPop(ref op) => &op.op, + CallOutputSsa(ref op) => &op.op, + CallParamSsa(ref op) => &op.op, + CallStackSsa(ref op) => &op.op, + Adc(ref op) | Sbb(ref op) | Rlc(ref op) | Rrc(ref op) => &op.op, Add(ref op) | Sub(ref op) | And(ref op) | Or(ref op) | Xor(ref op) | Lsl(ref op) @@ -659,16 +669,13 @@ where | Floor(ref op) | Ceil(ref op) | Ftrunc(ref op) => &op.op, UnimplMem(ref op) => &op.op, - //TestBit(Operation<'func, A, M, F, operation::TestBit>), // TODO + //TestBit(Operation<'func, M, F, operation::TestBit>), // TODO } } } -impl<'func, A> LowLevelILExpressionKind<'func, A, Mutable, NonSSA> -where - A: 'func + Architecture, -{ - pub fn flag_write(&self) -> Option { +impl LowLevelILExpressionKind<'_, Mutable, NonSSA> { + pub fn flag_write(&self) -> Option { use self::LowLevelILExpressionKind::*; match *self { @@ -687,12 +694,20 @@ where Load(ref op) => op.flag_write(), + LoadSsa(ref op) => op.flag_write(), + Pop(ref op) => op.flag_write(), Reg(ref op) => op.flag_write(), + RegSsa(ref op) => op.flag_write(), + + RegPartialSsa(ref op) => op.flag_write(), + RegSplit(ref op) => op.flag_write(), + RegSplitSsa(ref op) => op.flag_write(), + Flag(ref op) => op.flag_write(), FlagBit(ref op) => op.flag_write(), @@ -703,6 +718,10 @@ where RegStackPop(ref op) => op.flag_write(), + CallOutputSsa(ref op) => op.flag_write(), + CallParamSsa(ref op) => op.flag_write(), + CallStackSsa(ref op) => op.flag_write(), + Adc(ref op) | Sbb(ref op) | Rlc(ref op) | Rrc(ref op) => op.flag_write(), Add(ref op) | Sub(ref op) | And(ref op) | Or(ref op) | Xor(ref op) | Lsl(ref op) @@ -720,7 +739,7 @@ where | Floor(ref op) | Ceil(ref op) | Ftrunc(ref op) => op.flag_write(), UnimplMem(ref op) => op.flag_write(), - //TestBit(Operation<'func, A, M, F, operation::TestBit>), // TODO + //TestBit(Operation<'func, M, F, operation::TestBit>), // TODO } } } diff --git a/rust/src/low_level_il/function.rs b/rust/src/low_level_il/function.rs index 85037632b3..495aae7905 100644 --- a/rust/src/low_level_il/function.rs +++ b/rust/src/low_level_il/function.rs @@ -12,16 +12,12 @@ // See the License for the specific language governing permissions and // limitations under the License. -use binaryninjacore_sys::BNFreeLowLevelILFunction; -use binaryninjacore_sys::BNGetLowLevelILOwnerFunction; -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; +use binaryninjacore_sys::*; + use crate::architecture::CoreArchitecture; use crate::basic_block::BasicBlock; use crate::function::Function; @@ -35,73 +31,69 @@ pub struct Mutable; #[derive(Copy, Clone, Debug)] pub struct Finalized; -pub trait FunctionMutability: 'static {} +pub trait FunctionMutability: 'static + Debug {} impl FunctionMutability for Mutable {} impl FunctionMutability for Finalized {} -#[derive(Copy, Clone, Debug)] -pub struct LiftedNonSSA; -#[derive(Copy, Clone, Debug)] -pub struct RegularNonSSA; - -pub trait NonSSAVariant: 'static {} -impl NonSSAVariant for LiftedNonSSA {} -impl NonSSAVariant for RegularNonSSA {} - #[derive(Copy, Clone, Debug)] pub struct SSA; #[derive(Copy, Clone, Debug)] -pub struct NonSSA(V); +pub struct NonSSA; -pub trait FunctionForm: 'static {} +pub trait FunctionForm: 'static + Debug {} impl FunctionForm for SSA {} -impl FunctionForm for NonSSA {} +impl FunctionForm for NonSSA {} -pub struct LowLevelILFunction { - pub(crate) arch_handle: A::Handle, +pub struct LowLevelILFunction { pub(crate) handle: *mut BNLowLevelILFunction, - _arch: PhantomData<*mut A>, + arch: Option, _mutability: PhantomData, _form: PhantomData, } -impl LowLevelILFunction +impl LowLevelILFunction where - A: Architecture, M: FunctionMutability, F: FunctionForm, { - pub(crate) unsafe fn from_raw( - arch_handle: A::Handle, + pub(crate) unsafe fn from_raw_with_arch( handle: *mut BNLowLevelILFunction, + arch: Option, ) -> Self { debug_assert!(!handle.is_null()); Self { - arch_handle, handle, - _arch: PhantomData, + arch, _mutability: PhantomData, _form: PhantomData, } } - pub(crate) unsafe fn ref_from_raw( - arch_handle: A::Handle, + pub(crate) unsafe fn from_raw(handle: *mut BNLowLevelILFunction) -> Self { + Self::from_raw_with_arch(handle, None) + } + + pub(crate) unsafe fn ref_from_raw_with_arch( handle: *mut BNLowLevelILFunction, + arch: Option, ) -> Ref { debug_assert!(!handle.is_null()); - Ref::new(Self::from_raw(arch_handle, handle)) + Ref::new(Self::from_raw_with_arch(handle, arch)) } - pub(crate) fn arch(&self) -> &A { - self.arch_handle.borrow() + pub(crate) unsafe fn ref_from_raw(handle: *mut BNLowLevelILFunction) -> Ref { + Self::ref_from_raw_with_arch(handle, None) } - pub fn instruction_at>( - &self, - loc: L, - ) -> Option> { + pub(crate) fn arch(&self) -> CoreArchitecture { + match self.arch { + None => self.function().arch(), + Some(arch) => arch, + } + } + + pub fn instruction_at>(&self, loc: L) -> Option> { Some(LowLevelILInstruction::new( self, self.instruction_index_at(loc)?, @@ -128,7 +120,7 @@ where pub fn instruction_from_index( &self, index: LowLevelInstructionIndex, - ) -> Option> { + ) -> Option> { if index.0 >= self.instruction_count() { None } else { @@ -156,17 +148,8 @@ where Function::ref_from_raw(func) } } -} -// LLIL basic blocks are not available until the function object -// is finalized, so ensure we can't try requesting basic blocks -// during lifting -impl LowLevelILFunction -where - A: Architecture, - F: FunctionForm, -{ - pub fn basic_blocks(&self) -> Array>> { + pub fn basic_blocks(&self) -> Array>> { use binaryninjacore_sys::BNGetLowLevelILBasicBlockList; unsafe { @@ -178,8 +161,19 @@ where } } +impl LowLevelILFunction { + /// Retrieve the SSA form of the function. + pub fn ssa_form(&self) -> Option>> { + let handle = unsafe { BNGetLowLevelILSSAForm(self.handle) }; + if handle.is_null() { + return None; + } + Some(unsafe { LowLevelILFunction::ref_from_raw(handle) }) + } +} + // Allow instantiating Lifted IL functions for querying Lifted IL from Architectures -impl LowLevelILFunction> { +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. @@ -196,19 +190,27 @@ impl LowLevelILFunction> { // BNCreateLowLevelILFunction should always return a valid object. assert!(!handle.is_null()); - unsafe { Self::ref_from_raw(arch, handle) } + unsafe { Self::ref_from_raw_with_arch(handle, Some(arch)) } } pub fn generate_ssa_form(&self) { use binaryninjacore_sys::BNGenerateLowLevelILSSAForm; - unsafe { BNGenerateLowLevelILSSAForm(self.handle) }; } } -impl ToOwned for LowLevelILFunction +impl Ref> { + pub fn finalized(self) -> Ref> { + unsafe { + BNFinalizeLowLevelILFunction(self.handle); + // Now that we have finalized return the function as is so the caller can reference the "finalized function". + LowLevelILFunction::from_raw(self.handle).to_owned() + } + } +} + +impl ToOwned for LowLevelILFunction where - A: Architecture, M: FunctionMutability, F: FunctionForm, { @@ -219,17 +221,15 @@ where } } -unsafe impl RefCountable for LowLevelILFunction +unsafe impl RefCountable for LowLevelILFunction where - A: Architecture, M: FunctionMutability, F: FunctionForm, { unsafe fn inc_ref(handle: &Self) -> Ref { Ref::new(Self { - arch_handle: handle.arch_handle.clone(), handle: BNNewLowLevelILFunctionReference(handle.handle), - _arch: PhantomData, + arch: handle.arch, _mutability: PhantomData, _form: PhantomData, }) @@ -240,9 +240,8 @@ where } } -impl Debug for LowLevelILFunction +impl Debug for LowLevelILFunction where - A: Architecture + Debug, M: FunctionMutability, F: FunctionForm, { @@ -255,26 +254,18 @@ 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 Eq for LowLevelILFunction {} -impl PartialEq - for LowLevelILFunction -{ +impl PartialEq for LowLevelILFunction { fn eq(&self, rhs: &Self) -> bool { self.function().eq(&rhs.function()) } } -impl Hash for LowLevelILFunction { +impl Hash for LowLevelILFunction { fn hash(&self, state: &mut H) { self.function().hash(state) } diff --git a/rust/src/low_level_il/instruction.rs b/rust/src/low_level_il/instruction.rs index 977dcff4f4..ef546412ed 100644 --- a/rust/src/low_level_il/instruction.rs +++ b/rust/src/low_level_il/instruction.rs @@ -16,7 +16,6 @@ use super::operation; use super::operation::Operation; use super::VisitorAction; use super::*; -use crate::architecture::Architecture; use binaryninjacore_sys::BNGetLowLevelILByIndex; use binaryninjacore_sys::BNGetLowLevelILIndexForInstruction; use binaryninjacore_sys::BNLowLevelILInstruction; @@ -51,44 +50,38 @@ impl Display for LowLevelInstructionIndex { } // TODO: Probably want to rename this with a LowLevelIL prefix to avoid collisions when we add handlers for other ILs -pub trait InstructionHandler<'func, A, M, F> +pub trait InstructionHandler<'func, M, F> where - A: Architecture, M: FunctionMutability, F: FunctionForm, { - fn kind(&self) -> LowLevelILInstructionKind<'func, A, M, F>; + fn kind(&self) -> LowLevelILInstructionKind<'func, M, F>; /// Visit the sub expressions of this instruction. /// /// NOTE: This does not visit the root expression, i.e. the instruction. fn visit_tree(&self, f: &mut T) -> VisitorAction where - T: FnMut(&LowLevelILExpression<'func, A, M, F, ValueExpr>) -> VisitorAction; + T: FnMut(&LowLevelILExpression<'func, M, F, ValueExpr>) -> VisitorAction; } -pub struct LowLevelILInstruction<'func, A, M, F> +pub struct LowLevelILInstruction<'func, M, F> where - A: 'func + Architecture, M: FunctionMutability, F: FunctionForm, { - pub(crate) function: &'func LowLevelILFunction, + pub(crate) function: &'func LowLevelILFunction, pub index: LowLevelInstructionIndex, } -impl<'func, A, M, F> LowLevelILInstruction<'func, A, M, F> +impl<'func, M, F> LowLevelILInstruction<'func, 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: LowLevelInstructionIndex, - ) -> Self { + pub fn new(function: &'func LowLevelILFunction, index: LowLevelInstructionIndex) -> Self { Self { function, index } } @@ -107,9 +100,8 @@ where } } -impl<'func, A, M, F> Debug for LowLevelILInstruction<'func, A, M, F> +impl Debug for LowLevelILInstruction<'_, M, F> where - A: 'func + Architecture, M: FunctionMutability, F: FunctionForm, { @@ -122,41 +114,11 @@ where } } -impl<'func, A, M> InstructionHandler<'func, A, M, SSA> for LowLevelILInstruction<'func, A, M, SSA> -where - A: 'func + Architecture, - M: FunctionMutability, -{ - fn kind(&self) -> LowLevelILInstructionKind<'func, A, M, SSA> { - #[allow(unused_imports)] - use binaryninjacore_sys::BNLowLevelILOperation::*; - let raw_op = self.into_raw(); - #[allow(clippy::match_single_binding)] - match raw_op.operation { - // Any invalid ops for Non-Lifted IL will be checked here. - // SAFETY: We have checked for illegal operations. - _ => unsafe { - LowLevelILInstructionKind::from_raw(self.function, self.expr_idx(), raw_op) - }, - } - } - - fn visit_tree(&self, f: &mut T) -> VisitorAction - where - T: FnMut(&LowLevelILExpression<'func, A, M, SSA, ValueExpr>) -> VisitorAction, - { - // Recursively visit sub expressions. - self.kind().visit_sub_expressions(|e| e.visit_tree(f)) - } -} - -impl<'func, A, M> InstructionHandler<'func, A, M, NonSSA> - for LowLevelILInstruction<'func, A, M, NonSSA> +impl<'func, M> InstructionHandler<'func, M, SSA> for LowLevelILInstruction<'func, M, SSA> where - A: 'func + Architecture, M: FunctionMutability, { - fn kind(&self) -> LowLevelILInstructionKind<'func, A, M, NonSSA> { + fn kind(&self) -> LowLevelILInstructionKind<'func, M, SSA> { #[allow(unused_imports)] use binaryninjacore_sys::BNLowLevelILOperation::*; let raw_op = self.into_raw(); @@ -172,22 +134,18 @@ where fn visit_tree(&self, f: &mut T) -> VisitorAction where - T: FnMut( - &LowLevelILExpression<'func, A, M, NonSSA, ValueExpr>, - ) -> VisitorAction, + T: FnMut(&LowLevelILExpression<'func, M, SSA, ValueExpr>) -> VisitorAction, { // Recursively visit sub expressions. self.kind().visit_sub_expressions(|e| e.visit_tree(f)) } } -impl<'func, A, M> InstructionHandler<'func, A, M, NonSSA> - for LowLevelILInstruction<'func, A, M, NonSSA> +impl<'func, M> InstructionHandler<'func, M, NonSSA> for LowLevelILInstruction<'func, M, NonSSA> where - A: 'func + Architecture, M: FunctionMutability, { - fn kind(&self) -> LowLevelILInstructionKind<'func, A, M, NonSSA> { + fn kind(&self) -> LowLevelILInstructionKind<'func, M, NonSSA> { #[allow(unused_imports)] use binaryninjacore_sys::BNLowLevelILOperation::*; let raw_op = self.into_raw(); @@ -203,9 +161,7 @@ where fn visit_tree(&self, f: &mut T) -> VisitorAction where - T: FnMut( - &LowLevelILExpression<'func, A, M, NonSSA, ValueExpr>, - ) -> VisitorAction, + T: FnMut(&LowLevelILExpression<'func, M, NonSSA, ValueExpr>) -> VisitorAction, { // Recursively visit sub expressions. self.kind().visit_sub_expressions(|e| e.visit_tree(f)) @@ -213,109 +169,161 @@ where } #[derive(Debug)] -pub enum LowLevelILInstructionKind<'func, A, M, F> +pub enum LowLevelILInstructionKind<'func, M, F> where - A: 'func + Architecture, M: FunctionMutability, F: FunctionForm, { - Nop(Operation<'func, A, M, F, operation::NoArgs>), - SetReg(Operation<'func, A, M, F, operation::SetReg>), - SetRegSplit(Operation<'func, A, M, F, operation::SetRegSplit>), - SetFlag(Operation<'func, A, M, F, operation::SetFlag>), - Store(Operation<'func, A, M, F, operation::Store>), + Nop(Operation<'func, M, F, operation::NoArgs>), + SetReg(Operation<'func, M, F, operation::SetReg>), + SetRegSsa(Operation<'func, M, F, operation::SetRegSsa>), + SetRegPartialSsa(Operation<'func, M, F, operation::SetRegPartialSsa>), + SetRegSplit(Operation<'func, M, F, operation::SetRegSplit>), + SetRegSplitSsa(Operation<'func, M, F, operation::SetRegSplitSsa>), + SetFlag(Operation<'func, M, F, operation::SetFlag>), + SetFlagSsa(Operation<'func, M, F, operation::SetFlagSsa>), + Store(Operation<'func, M, F, operation::Store>), + StoreSsa(Operation<'func, M, F, operation::StoreSsa>), // TODO needs a real op - Push(Operation<'func, A, M, F, operation::UnaryOp>), + Push(Operation<'func, M, F, operation::UnaryOp>), - RegStackPush(Operation<'func, A, M, F, operation::RegStackPush>), + RegStackPush(Operation<'func, M, F, operation::RegStackPush>), - Jump(Operation<'func, A, M, F, operation::Jump>), - JumpTo(Operation<'func, A, M, F, operation::JumpTo>), + Jump(Operation<'func, M, F, operation::Jump>), + JumpTo(Operation<'func, M, F, operation::JumpTo>), - Call(Operation<'func, A, M, F, operation::Call>), - TailCall(Operation<'func, A, M, F, operation::Call>), + Call(Operation<'func, M, F, operation::Call>), + CallSsa(Operation<'func, M, F, operation::CallSsa>), + TailCall(Operation<'func, M, F, operation::Call>), + TailCallSsa(Operation<'func, M, F, operation::CallSsa>), - Ret(Operation<'func, A, M, F, operation::Ret>), - NoRet(Operation<'func, A, M, F, operation::NoArgs>), + Ret(Operation<'func, M, F, operation::Ret>), + NoRet(Operation<'func, M, F, operation::NoArgs>), - If(Operation<'func, A, M, F, operation::If>), - Goto(Operation<'func, A, M, F, operation::Goto>), + If(Operation<'func, M, F, operation::If>), + Goto(Operation<'func, M, F, operation::Goto>), - Syscall(Operation<'func, A, M, F, operation::Syscall>), - Intrinsic(Operation<'func, A, M, F, operation::Intrinsic>), - Bp(Operation<'func, A, M, F, operation::NoArgs>), - Trap(Operation<'func, A, M, F, operation::Trap>), - Undef(Operation<'func, A, M, F, operation::NoArgs>), + Syscall(Operation<'func, M, F, operation::Syscall>), + SyscallSsa(Operation<'func, M, F, operation::SyscallSsa>), + Intrinsic(Operation<'func, M, F, operation::Intrinsic>), + Bp(Operation<'func, M, F, operation::NoArgs>), + Trap(Operation<'func, M, F, operation::Trap>), + Undef(Operation<'func, M, F, operation::NoArgs>), + Assert(Operation<'func, M, F, operation::Assert>), + AssertSsa(Operation<'func, M, F, operation::AssertSsa>), + ForceVersion(Operation<'func, M, F, operation::ForceVersion>), + ForceVersionSsa(Operation<'func, M, F, operation::ForceVersionSsa>), /// The instruction is an expression. - Value(LowLevelILExpression<'func, A, M, F, ValueExpr>), + Value(LowLevelILExpression<'func, M, F, ValueExpr>), } -impl<'func, A, M, F> LowLevelILInstructionKind<'func, A, M, F> +impl<'func, M, F> LowLevelILInstructionKind<'func, M, F> where - A: Architecture, M: FunctionMutability, F: FunctionForm, { pub(crate) unsafe fn from_raw( - function: &'func LowLevelILFunction, + function: &'func LowLevelILFunction, expr_index: LowLevelExpressionIndex, op: BNLowLevelILInstruction, ) -> Self { use binaryninjacore_sys::BNLowLevelILOperation::*; match op.operation { - LLIL_NOP => LowLevelILInstructionKind::Nop(Operation::new(function, op)), - LLIL_SET_REG | LLIL_SET_REG_SSA => { - LowLevelILInstructionKind::SetReg(Operation::new(function, op)) + LLIL_NOP => LowLevelILInstructionKind::Nop(Operation::new(function, op, expr_index)), + LLIL_SET_REG => { + LowLevelILInstructionKind::SetReg(Operation::new(function, op, expr_index)) + } + LLIL_SET_REG_SSA => { + LowLevelILInstructionKind::SetRegSsa(Operation::new(function, op, expr_index)) + } + LLIL_SET_REG_SSA_PARTIAL => LowLevelILInstructionKind::SetRegPartialSsa( + Operation::new(function, op, expr_index), + ), + LLIL_SET_REG_SPLIT => { + LowLevelILInstructionKind::SetRegSplit(Operation::new(function, op, expr_index)) } - LLIL_SET_REG_SPLIT | LLIL_SET_REG_SPLIT_SSA => { - LowLevelILInstructionKind::SetRegSplit(Operation::new(function, op)) + LLIL_SET_REG_SPLIT_SSA => { + LowLevelILInstructionKind::SetRegSplitSsa(Operation::new(function, op, expr_index)) } - LLIL_SET_FLAG | LLIL_SET_FLAG_SSA => { - LowLevelILInstructionKind::SetFlag(Operation::new(function, op)) + LLIL_SET_FLAG => { + LowLevelILInstructionKind::SetFlag(Operation::new(function, op, expr_index)) } - LLIL_STORE | LLIL_STORE_SSA => { - LowLevelILInstructionKind::Store(Operation::new(function, op)) + LLIL_SET_FLAG_SSA => { + LowLevelILInstructionKind::SetFlagSsa(Operation::new(function, op, expr_index)) } - LLIL_PUSH => LowLevelILInstructionKind::Push(Operation::new(function, op)), + LLIL_STORE => { + LowLevelILInstructionKind::Store(Operation::new(function, op, expr_index)) + } + LLIL_STORE_SSA => { + LowLevelILInstructionKind::StoreSsa(Operation::new(function, op, expr_index)) + } + LLIL_PUSH => LowLevelILInstructionKind::Push(Operation::new(function, op, expr_index)), LLIL_REG_STACK_PUSH => { - LowLevelILInstructionKind::RegStackPush(Operation::new(function, op)) + LowLevelILInstructionKind::RegStackPush(Operation::new(function, op, expr_index)) } - LLIL_JUMP => LowLevelILInstructionKind::Jump(Operation::new(function, op)), - LLIL_JUMP_TO => LowLevelILInstructionKind::JumpTo(Operation::new(function, op)), + LLIL_JUMP => LowLevelILInstructionKind::Jump(Operation::new(function, op, expr_index)), + LLIL_JUMP_TO => { + LowLevelILInstructionKind::JumpTo(Operation::new(function, op, expr_index)) + } - LLIL_CALL | LLIL_CALL_STACK_ADJUST | LLIL_CALL_SSA => { - LowLevelILInstructionKind::Call(Operation::new(function, op)) + LLIL_CALL | LLIL_CALL_STACK_ADJUST => { + LowLevelILInstructionKind::Call(Operation::new(function, op, expr_index)) + } + LLIL_CALL_SSA => { + LowLevelILInstructionKind::CallSsa(Operation::new(function, op, expr_index)) + } + LLIL_TAILCALL => { + LowLevelILInstructionKind::TailCall(Operation::new(function, op, expr_index)) } - LLIL_TAILCALL | LLIL_TAILCALL_SSA => { - LowLevelILInstructionKind::TailCall(Operation::new(function, op)) + LLIL_TAILCALL_SSA => { + LowLevelILInstructionKind::TailCallSsa(Operation::new(function, op, expr_index)) } - LLIL_RET => LowLevelILInstructionKind::Ret(Operation::new(function, op)), - LLIL_NORET => LowLevelILInstructionKind::NoRet(Operation::new(function, op)), + LLIL_RET => LowLevelILInstructionKind::Ret(Operation::new(function, op, expr_index)), + LLIL_NORET => { + LowLevelILInstructionKind::NoRet(Operation::new(function, op, expr_index)) + } - LLIL_IF => LowLevelILInstructionKind::If(Operation::new(function, op)), - LLIL_GOTO => LowLevelILInstructionKind::Goto(Operation::new(function, op)), + LLIL_IF => LowLevelILInstructionKind::If(Operation::new(function, op, expr_index)), + LLIL_GOTO => LowLevelILInstructionKind::Goto(Operation::new(function, op, expr_index)), - LLIL_SYSCALL | LLIL_SYSCALL_SSA => { - LowLevelILInstructionKind::Syscall(Operation::new(function, op)) + LLIL_SYSCALL => { + LowLevelILInstructionKind::Syscall(Operation::new(function, op, expr_index)) + } + LLIL_SYSCALL_SSA => { + LowLevelILInstructionKind::SyscallSsa(Operation::new(function, op, expr_index)) } LLIL_INTRINSIC | LLIL_INTRINSIC_SSA => { - LowLevelILInstructionKind::Intrinsic(Operation::new(function, op)) + LowLevelILInstructionKind::Intrinsic(Operation::new(function, op, expr_index)) + } + LLIL_BP => LowLevelILInstructionKind::Bp(Operation::new(function, op, expr_index)), + LLIL_TRAP => LowLevelILInstructionKind::Trap(Operation::new(function, op, expr_index)), + LLIL_UNDEF => { + LowLevelILInstructionKind::Undef(Operation::new(function, op, expr_index)) + } + LLIL_ASSERT => { + LowLevelILInstructionKind::Assert(Operation::new(function, op, expr_index)) + } + LLIL_ASSERT_SSA => { + LowLevelILInstructionKind::AssertSsa(Operation::new(function, op, expr_index)) + } + LLIL_FORCE_VER => { + LowLevelILInstructionKind::ForceVersion(Operation::new(function, op, expr_index)) + } + LLIL_FORCE_VER_SSA => { + LowLevelILInstructionKind::ForceVersionSsa(Operation::new(function, op, expr_index)) } - LLIL_BP => LowLevelILInstructionKind::Bp(Operation::new(function, op)), - LLIL_TRAP => LowLevelILInstructionKind::Trap(Operation::new(function, op)), - LLIL_UNDEF => LowLevelILInstructionKind::Undef(Operation::new(function, op)), _ => LowLevelILInstructionKind::Value(LowLevelILExpression::new(function, expr_index)), } } fn visit_sub_expressions(&self, mut visitor: T) -> VisitorAction where - T: FnMut(&LowLevelILExpression<'func, A, M, F, ValueExpr>) -> VisitorAction, + T: FnMut(&LowLevelILExpression<'func, M, F, ValueExpr>) -> VisitorAction, { use LowLevelILInstructionKind::*; @@ -329,17 +337,31 @@ where match self { SetReg(ref op) => visit!(&op.source_expr()), + SetRegSsa(ref op) => visit!(&op.source_expr()), + SetRegPartialSsa(ref op) => visit!(&op.source_expr()), SetRegSplit(ref op) => visit!(&op.source_expr()), + SetRegSplitSsa(ref op) => visit!(&op.source_expr()), SetFlag(ref op) => visit!(&op.source_expr()), + SetFlagSsa(ref op) => visit!(&op.source_expr()), Store(ref op) => { - visit!(&op.dest_mem_expr()); + visit!(&op.dest_expr()); + visit!(&op.source_expr()); + } + StoreSsa(ref op) => { + visit!(&op.dest_expr()); visit!(&op.source_expr()); } Push(ref op) => visit!(&op.operand()), RegStackPush(ref op) => visit!(&op.source_expr()), Jump(ref op) => visit!(&op.target()), JumpTo(ref op) => visit!(&op.target()), + SyscallSsa(ref op) => { + visit!(&op.output_expr()); + visit!(&op.param_expr()); + visit!(&op.stack_expr()); + } Call(ref op) | TailCall(ref op) => visit!(&op.target()), + CallSsa(ref op) | TailCallSsa(ref op) => visit!(&op.target()), Ret(ref op) => visit!(&op.target()), If(ref op) => visit!(&op.condition()), Intrinsic(ref _op) => { @@ -347,7 +369,8 @@ where } Value(e) => visit!(e), // Do not have any sub expressions. - Nop(_) | NoRet(_) | Goto(_) | Syscall(_) | Bp(_) | Trap(_) | Undef(_) => {} + Nop(_) | NoRet(_) | Goto(_) | Syscall(_) | Bp(_) | Trap(_) | Undef(_) | Assert(_) + | AssertSsa(_) | ForceVersion(_) | ForceVersionSsa(_) => {} } VisitorAction::Sibling diff --git a/rust/src/low_level_il/lifting.rs b/rust/src/low_level_il/lifting.rs index 7401c99b57..f2850776cf 100644 --- a/rust/src/low_level_il/lifting.rs +++ b/rust/src/low_level_il/lifting.rs @@ -18,35 +18,33 @@ use binaryninjacore_sys::{BNAddLowLevelILLabelForAddress, BNLowLevelILOperation} use binaryninjacore_sys::{BNLowLevelILLabel, BNRegisterOrConstant}; use super::*; -use crate::architecture::Register as ArchReg; use crate::architecture::{Architecture, FlagWriteId, RegisterId}; +use crate::architecture::{CoreRegister, Register as ArchReg}; use crate::architecture::{ Flag, FlagClass, FlagCondition, FlagGroup, FlagRole, FlagWrite, Intrinsic, }; use crate::function::Location; -pub trait LiftableLowLevelIL<'func, A: 'func + Architecture> { +pub trait LiftableLowLevelIL<'func> { type Result: ExpressionResultType; fn lift( - il: &'func MutableLiftedILFunction, + il: &'func LowLevelILMutableFunction, expr: Self, - ) -> MutableLiftedILExpr<'func, A, Self::Result>; + ) -> LowLevelILMutableExpression<'func, Self::Result>; } -pub trait LiftableLowLevelILWithSize<'func, A: 'func + Architecture>: - LiftableLowLevelIL<'func, A, Result = ValueExpr> -{ +pub trait LiftableLowLevelILWithSize<'func>: LiftableLowLevelIL<'func, Result = ValueExpr> { fn lift_with_size( - il: &'func MutableLiftedILFunction, + il: &'func LowLevelILMutableFunction, expr: Self, size: usize, - ) -> MutableLiftedILExpr<'func, A, ValueExpr>; + ) -> LowLevelILMutableExpression<'func, ValueExpr>; } #[derive(Copy, Clone)] pub enum LowLevelILRegisterOrConstant { - Register(usize, LowLevelILRegister), + Register(usize, LowLevelILRegisterKind), Constant(usize, u64), } @@ -267,14 +265,9 @@ impl LowLevelILFlagWriteOp { if operand.constant { LowLevelILRegisterOrConstant::Constant(size, operand.value) } else { - let il_reg = if 0x8000_0000 & operand.reg == 0 { - LowLevelILRegister::ArchReg( - arch.register_from_id(RegisterId(operand.reg)).unwrap(), - ) - } else { - LowLevelILRegister::Temp(operand.reg) - }; - + let raw_id = RegisterId(operand.reg); + let il_reg = + LowLevelILRegisterKind::from_raw(arch, raw_id).expect("Bad register ID"); LowLevelILRegisterOrConstant::Register(size, il_reg) } } @@ -466,8 +459,8 @@ pub fn get_default_flag_write_llil<'func, A>( arch: &A, role: FlagRole, op: LowLevelILFlagWriteOp, - il: &'func MutableLiftedILFunction, -) -> MutableLiftedILExpr<'func, A, ValueExpr> + il: &'func LowLevelILMutableFunction, +) -> LowLevelILMutableExpression<'func, ValueExpr> where A: 'func + Architecture, { @@ -494,8 +487,8 @@ pub fn get_default_flag_cond_llil<'func, A>( arch: &A, cond: FlagCondition, class: Option, - il: &'func MutableLiftedILFunction, -) -> MutableLiftedILExpr<'func, A, ValueExpr> + il: &'func LowLevelILMutableFunction, +) -> LowLevelILMutableExpression<'func, ValueExpr> where A: 'func + Architecture, { @@ -516,19 +509,19 @@ where macro_rules! prim_int_lifter { ($x:ty) => { - impl<'a, A: 'a + Architecture> LiftableLowLevelIL<'a, A> for $x { + impl<'a> LiftableLowLevelIL<'a> for $x { type Result = ValueExpr; - fn lift(il: &'a MutableLiftedILFunction, val: Self) - -> MutableLiftedILExpr<'a, A, Self::Result> + fn lift(il: &'a LowLevelILMutableFunction, val: Self) + -> LowLevelILMutableExpression<'a, Self::Result> { il.const_int(std::mem::size_of::(), val as i64 as u64) } } - impl<'a, A: 'a + Architecture> LiftableLowLevelILWithSize<'a, A> for $x { - fn lift_with_size(il: &'a MutableLiftedILFunction, val: Self, size: usize) - -> MutableLiftedILExpr<'a, A, ValueExpr> + impl<'a> LiftableLowLevelILWithSize<'a> for $x { + fn lift_with_size(il: &'a LowLevelILMutableFunction, val: Self, size: usize) + -> LowLevelILMutableExpression<'a, ValueExpr> { let raw = val as i64; @@ -561,57 +554,57 @@ prim_int_lifter!(u16); prim_int_lifter!(u32); prim_int_lifter!(u64); -impl<'a, R: ArchReg, A: 'a + Architecture> LiftableLowLevelIL<'a, A> for LowLevelILRegister +impl<'a, R> LiftableLowLevelIL<'a> for LowLevelILRegisterKind where - R: LiftableLowLevelIL<'a, A, Result = ValueExpr> + Into>, + R: LiftableLowLevelIL<'a, Result = ValueExpr> + Into> + ArchReg, { type Result = ValueExpr; fn lift( - il: &'a MutableLiftedILFunction, + il: &'a LowLevelILMutableFunction, reg: Self, - ) -> MutableLiftedILExpr<'a, A, Self::Result> { + ) -> LowLevelILMutableExpression<'a, Self::Result> { match reg { - LowLevelILRegister::ArchReg(r) => R::lift(il, r), - LowLevelILRegister::Temp(t) => il.reg( + LowLevelILRegisterKind::Arch(r) => R::lift(il, r), + LowLevelILRegisterKind::Temp(t) => il.reg( il.arch().default_integer_size(), - LowLevelILRegister::Temp(t), + LowLevelILRegisterKind::Temp::(t), ), } } } -impl<'a, R: ArchReg, A: 'a + Architecture> LiftableLowLevelILWithSize<'a, A> - for LowLevelILRegister +impl<'a, R> LiftableLowLevelILWithSize<'a> for LowLevelILRegisterKind where - R: LiftableLowLevelILWithSize<'a, A> + Into>, + R: LiftableLowLevelILWithSize<'a> + Into> + ArchReg, { fn lift_with_size( - il: &'a MutableLiftedILFunction, + il: &'a LowLevelILMutableFunction, reg: Self, size: usize, - ) -> MutableLiftedILExpr<'a, A, ValueExpr> { + ) -> LowLevelILMutableExpression<'a, ValueExpr> { match reg { - LowLevelILRegister::ArchReg(r) => R::lift_with_size(il, r, size), - LowLevelILRegister::Temp(t) => il.reg(size, LowLevelILRegister::Temp(t)), + LowLevelILRegisterKind::Arch(r) => R::lift_with_size(il, r, size), + LowLevelILRegisterKind::Temp(t) => il.reg(size, LowLevelILRegisterKind::::Temp(t)), } } } -impl<'a, R: ArchReg, A: 'a + Architecture> LiftableLowLevelIL<'a, A> - for LowLevelILRegisterOrConstant +impl<'a, R> LiftableLowLevelIL<'a> for LowLevelILRegisterOrConstant where - R: LiftableLowLevelILWithSize<'a, A, Result = ValueExpr> + Into>, + R: LiftableLowLevelILWithSize<'a, Result = ValueExpr> + + Into> + + ArchReg, { type Result = ValueExpr; fn lift( - il: &'a MutableLiftedILFunction, + il: &'a LowLevelILMutableFunction, reg: Self, - ) -> MutableLiftedILExpr<'a, A, Self::Result> { + ) -> LowLevelILMutableExpression<'a, Self::Result> { match reg { LowLevelILRegisterOrConstant::Register(size, r) => { - LowLevelILRegister::::lift_with_size(il, r, size) + LowLevelILRegisterKind::::lift_with_size(il, r, size) } LowLevelILRegisterOrConstant::Constant(size, value) => { u64::lift_with_size(il, value, size) @@ -620,20 +613,19 @@ where } } -impl<'a, R: ArchReg, A: 'a + Architecture> LiftableLowLevelILWithSize<'a, A> - for LowLevelILRegisterOrConstant +impl<'a, R> LiftableLowLevelILWithSize<'a> for LowLevelILRegisterOrConstant where - R: LiftableLowLevelILWithSize<'a, A> + Into>, + R: LiftableLowLevelILWithSize<'a> + Into> + ArchReg, { fn lift_with_size( - il: &'a MutableLiftedILFunction, + il: &'a LowLevelILMutableFunction, reg: Self, size: usize, - ) -> MutableLiftedILExpr<'a, A, ValueExpr> { + ) -> LowLevelILMutableExpression<'a, ValueExpr> { // TODO ensure requested size is compatible with size of this constant match reg { LowLevelILRegisterOrConstant::Register(_, r) => { - LowLevelILRegister::::lift_with_size(il, r, size) + LowLevelILRegisterKind::::lift_with_size(il, r, size) } LowLevelILRegisterOrConstant::Constant(_, value) => { u64::lift_with_size(il, value, size) @@ -642,31 +634,27 @@ where } } -impl<'a, A, R> LiftableLowLevelIL<'a, A> - for LowLevelILExpression<'a, A, Mutable, NonSSA, R> +impl<'a, R> LiftableLowLevelIL<'a> for LowLevelILExpression<'a, Mutable, NonSSA, R> where - A: 'a + Architecture, R: ExpressionResultType, { type Result = R; fn lift( - il: &'a MutableLiftedILFunction, + il: &'a LowLevelILMutableFunction, expr: Self, - ) -> MutableLiftedILExpr<'a, A, Self::Result> { + ) -> LowLevelILMutableExpression<'a, Self::Result> { debug_assert!(expr.function.handle == il.handle); expr } } -impl<'a, A: 'a + Architecture> LiftableLowLevelILWithSize<'a, A> - for LowLevelILExpression<'a, A, Mutable, NonSSA, ValueExpr> -{ +impl<'a> LiftableLowLevelILWithSize<'a> for LowLevelILExpression<'a, Mutable, NonSSA, ValueExpr> { fn lift_with_size( - il: &'a MutableLiftedILFunction, + il: &'a LowLevelILMutableFunction, expr: Self, _size: usize, - ) -> MutableLiftedILExpr<'a, A, Self::Result> { + ) -> LowLevelILMutableExpression<'a, Self::Result> { #[cfg(debug_assertions)] { use crate::low_level_il::ExpressionHandler; @@ -686,9 +674,8 @@ impl<'a, A: 'a + Architecture> LiftableLowLevelILWithSize<'a, A> } } -impl<'func, A, R> LowLevelILExpression<'func, A, Mutable, NonSSA, R> +impl LowLevelILExpression<'_, Mutable, NonSSA, R> where - A: 'func + Architecture, R: ExpressionResultType, { pub fn with_source_operand(self, op: u32) -> Self { @@ -702,12 +689,11 @@ where } } -pub struct ExpressionBuilder<'func, A, R> +pub struct ExpressionBuilder<'func, R> where - A: 'func + Architecture, R: ExpressionResultType, { - function: &'func LowLevelILFunction>, + function: &'func LowLevelILFunction, op: BNLowLevelILOperation, size: usize, flag_write: FlagWriteId, @@ -718,12 +704,11 @@ where _ty: PhantomData, } -impl<'a, A, R> ExpressionBuilder<'a, A, R> +impl<'a, R> ExpressionBuilder<'a, R> where - A: 'a + Architecture, R: ExpressionResultType, { - pub fn from_expr(expr: LowLevelILExpression<'a, A, Mutable, NonSSA, R>) -> Self { + pub fn from_expr(expr: LowLevelILExpression<'a, Mutable, NonSSA, R>) -> Self { use binaryninjacore_sys::BNGetLowLevelILByIndex; let instr = unsafe { BNGetLowLevelILByIndex(expr.function.handle, expr.index.0) }; @@ -741,13 +726,13 @@ where } } - pub fn with_flag_write(mut self, flag_write: A::FlagWrite) -> Self { + pub fn with_flag_write(mut self, flag_write: impl FlagWrite) -> Self { // TODO verify valid id self.flag_write = flag_write.id(); self } - pub fn build(self) -> LowLevelILExpression<'a, A, Mutable, NonSSA, R> { + pub fn build(self) -> LowLevelILExpression<'a, Mutable, NonSSA, R> { use binaryninjacore_sys::BNLowLevelILAddExpr; let expr_idx = unsafe { @@ -766,10 +751,7 @@ where LowLevelILExpression::new(self.function, LowLevelExpressionIndex(expr_idx)) } - pub fn with_source_operand( - self, - op: u32, - ) -> LowLevelILExpression<'a, A, Mutable, NonSSA, R> { + pub fn with_source_operand(self, op: u32) -> LowLevelILExpression<'a, Mutable, NonSSA, R> { self.build().with_source_operand(op) } @@ -779,32 +761,28 @@ where } } -impl<'a, A, R> LiftableLowLevelIL<'a, A> for ExpressionBuilder<'a, A, R> +impl<'a, R> LiftableLowLevelIL<'a> for ExpressionBuilder<'a, R> where - A: 'a + Architecture, R: ExpressionResultType, { type Result = R; fn lift( - il: &'a MutableLiftedILFunction, + il: &'a LowLevelILMutableFunction, expr: Self, - ) -> MutableLiftedILExpr<'a, A, Self::Result> { + ) -> LowLevelILMutableExpression<'a, Self::Result> { debug_assert!(expr.function.handle == il.handle); expr.build() } } -impl<'a, A> LiftableLowLevelILWithSize<'a, A> for ExpressionBuilder<'a, A, ValueExpr> -where - A: 'a + Architecture, -{ +impl<'a> LiftableLowLevelILWithSize<'a> for ExpressionBuilder<'a, ValueExpr> { fn lift_with_size( - il: &'a MutableLiftedILFunction, + il: &'a LowLevelILMutableFunction, expr: Self, _size: usize, - ) -> MutableLiftedILExpr<'a, A, ValueExpr> { + ) -> LowLevelILMutableExpression<'a, ValueExpr> { #[cfg(debug_assertions)] { use binaryninjacore_sys::BNLowLevelILOperation::{LLIL_UNIMPL, LLIL_UNIMPL_MEM}; @@ -825,7 +803,7 @@ where macro_rules! no_arg_lifter { ($name:ident, $op:ident, $result:ty) => { - pub fn $name(&self) -> LowLevelILExpression, $result> { + pub fn $name(&self) -> LowLevelILExpression { use binaryninjacore_sys::BNLowLevelILAddExpr; use binaryninjacore_sys::BNLowLevelILOperation::$op; @@ -838,7 +816,7 @@ macro_rules! no_arg_lifter { macro_rules! sized_no_arg_lifter { ($name:ident, $op:ident, $result:ty) => { - pub fn $name(&self, size: usize) -> ExpressionBuilder { + pub fn $name(&self, size: usize) -> ExpressionBuilder<$result> { use binaryninjacore_sys::BNLowLevelILOperation::$op; ExpressionBuilder { @@ -858,12 +836,9 @@ macro_rules! sized_no_arg_lifter { macro_rules! unsized_unary_op_lifter { ($name:ident, $op:ident, $result:ty) => { - pub fn $name<'a, E>( - &'a self, - expr: E, - ) -> LowLevelILExpression<'a, A, Mutable, NonSSA, $result> + pub fn $name<'a, E>(&'a self, expr: E) -> LowLevelILExpression<'a, Mutable, NonSSA, $result> where - E: LiftableLowLevelIL<'a, A, Result = ValueExpr>, + E: LiftableLowLevelIL<'a, Result = ValueExpr>, { use binaryninjacore_sys::BNLowLevelILAddExpr; use binaryninjacore_sys::BNLowLevelILOperation::$op; @@ -881,9 +856,9 @@ macro_rules! unsized_unary_op_lifter { macro_rules! sized_unary_op_lifter { ($name:ident, $op:ident, $result:ty) => { - pub fn $name<'a, E>(&'a self, size: usize, expr: E) -> ExpressionBuilder<'a, A, $result> + pub fn $name<'a, E>(&'a self, size: usize, expr: E) -> ExpressionBuilder<'a, $result> where - E: LiftableLowLevelILWithSize<'a, A>, + E: LiftableLowLevelILWithSize<'a>, { use binaryninjacore_sys::BNLowLevelILOperation::$op; @@ -906,9 +881,9 @@ macro_rules! sized_unary_op_lifter { macro_rules! size_changing_unary_op_lifter { ($name:ident, $op:ident, $result:ty) => { - pub fn $name<'a, E>(&'a self, size: usize, expr: E) -> ExpressionBuilder<'a, A, $result> + pub fn $name<'a, E>(&'a self, size: usize, expr: E) -> ExpressionBuilder<'a, $result> where - E: LiftableLowLevelILWithSize<'a, A>, + E: LiftableLowLevelILWithSize<'a>, { use binaryninjacore_sys::BNLowLevelILOperation::$op; @@ -936,10 +911,10 @@ macro_rules! binary_op_lifter { size: usize, left: L, right: R, - ) -> ExpressionBuilder<'a, A, ValueExpr> + ) -> ExpressionBuilder<'a, ValueExpr> where - L: LiftableLowLevelILWithSize<'a, A>, - R: LiftableLowLevelILWithSize<'a, A>, + L: LiftableLowLevelILWithSize<'a>, + R: LiftableLowLevelILWithSize<'a>, { use binaryninjacore_sys::BNLowLevelILOperation::$op; @@ -969,11 +944,11 @@ macro_rules! binary_op_carry_lifter { left: L, right: R, carry: C, - ) -> ExpressionBuilder<'a, A, ValueExpr> + ) -> ExpressionBuilder<'a, ValueExpr> where - L: LiftableLowLevelILWithSize<'a, A>, - R: LiftableLowLevelILWithSize<'a, A>, - C: LiftableLowLevelILWithSize<'a, A>, + L: LiftableLowLevelILWithSize<'a>, + R: LiftableLowLevelILWithSize<'a>, + C: LiftableLowLevelILWithSize<'a>, { use binaryninjacore_sys::BNLowLevelILOperation::$op; @@ -996,21 +971,18 @@ macro_rules! binary_op_carry_lifter { }; } -impl LowLevelILFunction> -where - A: Architecture, -{ - pub const NO_INPUTS: [ExpressionBuilder<'static, A, ValueExpr>; 0] = []; - pub const NO_OUTPUTS: [LowLevelILRegister; 0] = []; +impl LowLevelILMutableFunction { + pub const NO_INPUTS: [ExpressionBuilder<'static, ValueExpr>; 0] = []; + pub const NO_OUTPUTS: [LowLevelILRegisterKind; 0] = []; - pub fn expression<'a, E: LiftableLowLevelIL<'a, A>>( + pub fn expression<'a, E: LiftableLowLevelIL<'a>>( &'a self, expr: E, - ) -> LowLevelILExpression<'a, A, Mutable, NonSSA, E::Result> { + ) -> LowLevelILExpression<'a, Mutable, NonSSA, E::Result> { E::lift(self, expr) } - pub fn add_instruction<'a, E: LiftableLowLevelIL<'a, A>>(&'a self, expr: E) { + pub fn add_instruction<'a, E: LiftableLowLevelIL<'a>>(&'a self, expr: E) { let expr = self.expression(expr); unsafe { @@ -1019,7 +991,7 @@ where } } - pub unsafe fn replace_expression<'a, E: LiftableLowLevelIL<'a, A>>( + pub unsafe fn replace_expression<'a, E: LiftableLowLevelIL<'a>>( &'a self, replaced_expr_index: LowLevelExpressionIndex, replacement: E, @@ -1034,11 +1006,7 @@ where true } - pub fn const_int( - &self, - size: usize, - val: u64, - ) -> LowLevelILExpression, ValueExpr> { + pub fn const_int(&self, size: usize, val: u64) -> LowLevelILMutableExpression { use binaryninjacore_sys::BNLowLevelILAddExpr; use binaryninjacore_sys::BNLowLevelILOperation::LLIL_CONST; @@ -1048,11 +1016,7 @@ where LowLevelILExpression::new(self, LowLevelExpressionIndex(expr_idx)) } - pub fn const_ptr_sized( - &self, - size: usize, - val: u64, - ) -> LowLevelILExpression, ValueExpr> { + pub fn const_ptr_sized(&self, size: usize, val: u64) -> LowLevelILMutableExpression { use binaryninjacore_sys::BNLowLevelILAddExpr; use binaryninjacore_sys::BNLowLevelILOperation::LLIL_CONST_PTR; @@ -1062,17 +1026,11 @@ where LowLevelILExpression::new(self, LowLevelExpressionIndex(expr_idx)) } - pub fn const_ptr( - &self, - val: u64, - ) -> LowLevelILExpression, ValueExpr> { + pub fn const_ptr(&self, val: u64) -> LowLevelILMutableExpression { self.const_ptr_sized(self.arch().address_size(), val) } - pub fn trap( - &self, - val: u64, - ) -> LowLevelILExpression, VoidExpr> { + pub fn trap(&self, val: u64) -> LowLevelILExpression { use binaryninjacore_sys::BNLowLevelILAddExpr; use binaryninjacore_sys::BNLowLevelILOperation::LLIL_TRAP; @@ -1099,9 +1057,9 @@ where cond: C, true_label: &'b mut LowLevelILLabel, false_label: &'b mut LowLevelILLabel, - ) -> LowLevelILExpression<'a, A, Mutable, NonSSA, VoidExpr> + ) -> LowLevelILExpression<'a, Mutable, NonSSA, VoidExpr> where - C: LiftableLowLevelIL<'b, A, Result = ValueExpr>, + C: LiftableLowLevelIL<'b, Result = ValueExpr>, { use binaryninjacore_sys::BNLowLevelILIf; @@ -1139,7 +1097,7 @@ where pub fn goto<'a: 'b, 'b>( &'a self, label: &'b mut LowLevelILLabel, - ) -> LowLevelILExpression<'a, A, Mutable, NonSSA, VoidExpr> { + ) -> LowLevelILExpression<'a, Mutable, NonSSA, VoidExpr> { use binaryninjacore_sys::BNLowLevelILGoto; let mut raw_label = BNLowLevelILLabel::from(*label); @@ -1156,11 +1114,11 @@ where LowLevelILExpression::new(self, LowLevelExpressionIndex(expr_idx)) } - pub fn reg>>( + pub fn reg>>( &self, size: usize, - reg: R, - ) -> LowLevelILExpression, ValueExpr> { + reg: LR, + ) -> LowLevelILMutableExpression { use binaryninjacore_sys::BNLowLevelILAddExpr; use binaryninjacore_sys::BNLowLevelILOperation::LLIL_REG; @@ -1173,15 +1131,12 @@ where LowLevelILExpression::new(self, LowLevelExpressionIndex(expr_idx)) } - pub fn reg_split< - H: Into>, - L: Into>, - >( + pub fn reg_split>>( &self, size: usize, - hi_reg: H, - lo_reg: L, - ) -> LowLevelILExpression, ValueExpr> { + hi_reg: LR, + lo_reg: LR, + ) -> LowLevelILMutableExpression { use binaryninjacore_sys::BNLowLevelILAddExpr; use binaryninjacore_sys::BNLowLevelILOperation::LLIL_REG_SPLIT; @@ -1205,15 +1160,16 @@ where LowLevelILExpression::new(self, LowLevelExpressionIndex(expr_idx)) } - pub fn set_reg<'a, R, E>( + pub fn set_reg<'a, R, LR, E>( &'a self, size: usize, - dest_reg: R, + dest_reg: LR, expr: E, - ) -> ExpressionBuilder<'a, A, VoidExpr> + ) -> ExpressionBuilder<'a, VoidExpr> where - R: Into>, - E: LiftableLowLevelILWithSize<'a, A>, + R: ArchReg, + LR: Into>, + E: LiftableLowLevelILWithSize<'a>, { use binaryninjacore_sys::BNLowLevelILOperation::LLIL_SET_REG; @@ -1236,17 +1192,17 @@ where } } - pub fn set_reg_split<'a, H, L, E>( + pub fn set_reg_split<'a, R, LR, E>( &'a self, size: usize, - hi_reg: H, - lo_reg: L, + hi_reg: LR, + lo_reg: LR, expr: E, - ) -> ExpressionBuilder<'a, A, VoidExpr> + ) -> ExpressionBuilder<'a, VoidExpr> where - H: Into>, - L: Into>, - E: LiftableLowLevelILWithSize<'a, A>, + R: ArchReg, + LR: Into>, + E: LiftableLowLevelILWithSize<'a>, { use binaryninjacore_sys::BNLowLevelILOperation::LLIL_SET_REG_SPLIT; @@ -1260,7 +1216,6 @@ where function: self, op: LLIL_SET_REG_SPLIT, size, - // TODO: Make these optional? flag_write: FlagWriteId(0), op1: hi_reg.0 as u64, op2: lo_reg.0 as u64, @@ -1270,10 +1225,7 @@ where } } - pub fn flag( - &self, - flag: A::Flag, - ) -> LowLevelILExpression, ValueExpr> { + pub fn flag(&self, flag: impl Flag) -> LowLevelILMutableExpression { use binaryninjacore_sys::BNLowLevelILAddExpr; use binaryninjacore_sys::BNLowLevelILOperation::LLIL_FLAG; @@ -1285,10 +1237,7 @@ where LowLevelILExpression::new(self, LowLevelExpressionIndex(expr_idx)) } - pub fn flag_cond( - &self, - cond: FlagCondition, - ) -> LowLevelILExpression, ValueExpr> { + pub fn flag_cond(&self, cond: FlagCondition) -> LowLevelILMutableExpression { use binaryninjacore_sys::BNLowLevelILAddExpr; use binaryninjacore_sys::BNLowLevelILOperation::LLIL_FLAG_COND; @@ -1299,10 +1248,7 @@ where LowLevelILExpression::new(self, LowLevelExpressionIndex(expr_idx)) } - pub fn flag_group( - &self, - group: A::FlagGroup, - ) -> LowLevelILExpression, ValueExpr> { + pub fn flag_group(&self, group: impl FlagGroup) -> LowLevelILMutableExpression { use binaryninjacore_sys::BNLowLevelILAddExpr; use binaryninjacore_sys::BNLowLevelILOperation::LLIL_FLAG_GROUP; @@ -1325,11 +1271,11 @@ where pub fn set_flag<'a, E>( &'a self, - dest_flag: A::Flag, + dest_flag: impl Flag, expr: E, - ) -> ExpressionBuilder<'a, A, VoidExpr> + ) -> ExpressionBuilder<'a, VoidExpr> where - E: LiftableLowLevelILWithSize<'a, A>, + E: LiftableLowLevelILWithSize<'a>, { use binaryninjacore_sys::BNLowLevelILOperation::LLIL_SET_FLAG; @@ -1355,9 +1301,9 @@ where FlagBit(usize, Flag, u64), */ - pub fn load<'a, E>(&'a self, size: usize, source_mem: E) -> ExpressionBuilder<'a, A, ValueExpr> + pub fn load<'a, E>(&'a self, size: usize, source_mem: E) -> ExpressionBuilder<'a, ValueExpr> where - E: LiftableLowLevelIL<'a, A, Result = ValueExpr>, + E: LiftableLowLevelIL<'a, Result = ValueExpr>, { use binaryninjacore_sys::BNLowLevelILOperation::LLIL_LOAD; @@ -1381,10 +1327,10 @@ where size: usize, dest_mem: D, value: V, - ) -> ExpressionBuilder<'a, A, VoidExpr> + ) -> ExpressionBuilder<'a, VoidExpr> where - D: LiftableLowLevelIL<'a, A, Result = ValueExpr>, - V: LiftableLowLevelILWithSize<'a, A>, + D: LiftableLowLevelIL<'a, Result = ValueExpr>, + V: LiftableLowLevelILWithSize<'a>, { use binaryninjacore_sys::BNLowLevelILOperation::LLIL_STORE; @@ -1404,18 +1350,17 @@ where } } - pub fn intrinsic<'a, O, OL, I, P, PL>( + // TODO: Reposition arguments. + pub fn intrinsic<'a, R, O, P>( &'a self, - outputs: OL, - intrinsic: I, - inputs: PL, - ) -> ExpressionBuilder<'a, A, VoidExpr> + outputs: impl IntoIterator, + intrinsic: impl Intrinsic, + inputs: impl IntoIterator, + ) -> ExpressionBuilder<'a, VoidExpr> where - O: Into>, - OL: IntoIterator, - I: Into, - P: LiftableLowLevelIL<'a, A, Result = ValueExpr>, - PL: IntoIterator, + R: ArchReg, + O: Into>, + P: LiftableLowLevelIL<'a, Result = ValueExpr>, { use binaryninjacore_sys::BNLowLevelILOperation::{LLIL_CALL_PARAM, LLIL_INTRINSIC}; use binaryninjacore_sys::{BNLowLevelILAddExpr, BNLowLevelILAddOperandList}; @@ -1427,8 +1372,6 @@ where let output_expr_idx = unsafe { BNLowLevelILAddOperandList(self.handle, outputs.as_mut_ptr(), outputs.len()) }; - let intrinsic: A::Intrinsic = intrinsic.into(); - let mut inputs: Vec = inputs .into_iter() .map(|input| { diff --git a/rust/src/low_level_il/operation.rs b/rust/src/low_level_il/operation.rs index b1bc8ef43b..6e0dda9150 100644 --- a/rust/src/low_level_il/operation.rs +++ b/rust/src/low_level_il/operation.rs @@ -12,41 +12,49 @@ // See the License for the specific language governing permissions and // limitations under the License. -use binaryninjacore_sys::{BNGetLowLevelILByIndex, BNLowLevelILInstruction}; +use binaryninjacore_sys::{ + BNGetCachedLowLevelILPossibleValueSet, BNGetLowLevelILByIndex, BNLowLevelILFreeOperandList, + BNLowLevelILGetOperandList, BNLowLevelILInstruction, +}; use super::*; -use crate::architecture::{FlagGroupId, FlagId, FlagWriteId, IntrinsicId, RegisterStackId}; +use crate::architecture::{ + CoreFlag, CoreFlagGroup, CoreFlagWrite, CoreIntrinsic, CoreRegister, CoreRegisterStack, + FlagGroupId, FlagId, FlagWriteId, IntrinsicId, RegisterStackId, +}; +use crate::variable::PossibleValueSet; use std::collections::BTreeMap; use std::fmt::{Debug, Formatter}; use std::marker::PhantomData; use std::mem; -pub struct Operation<'func, A, M, F, O> +pub struct Operation<'func, M, F, O> where - A: Architecture, M: FunctionMutability, F: FunctionForm, O: OperationArguments, { - pub(crate) function: &'func LowLevelILFunction, + pub(crate) function: &'func LowLevelILFunction, pub(crate) op: BNLowLevelILInstruction, + pub(crate) expr_idx: LowLevelExpressionIndex, _args: PhantomData, } -impl<'func, A, M, F, O> Operation<'func, A, M, F, O> +impl<'func, M, F, O> Operation<'func, M, F, O> where - A: Architecture, M: FunctionMutability, F: FunctionForm, O: OperationArguments, { pub(crate) fn new( - function: &'func LowLevelILFunction, + function: &'func LowLevelILFunction, op: BNLowLevelILInstruction, + expr_idx: LowLevelExpressionIndex, ) -> Self { Self { function, op, + expr_idx, _args: PhantomData, } } @@ -54,15 +62,43 @@ where pub fn address(&self) -> u64 { self.op.address } + + fn get_operand_list(&self, operand_idx: usize) -> Vec { + let mut count = 0; + let raw_list_ptr = unsafe { + BNLowLevelILGetOperandList( + self.function.handle, + self.expr_idx.0, + operand_idx, + &mut count, + ) + }; + assert!(!raw_list_ptr.is_null()); + let list = unsafe { std::slice::from_raw_parts(raw_list_ptr, count).to_vec() }; + unsafe { BNLowLevelILFreeOperandList(raw_list_ptr) }; + list + } + + fn get_constraint(&self, operand_idx: usize) -> PossibleValueSet { + let raw_pvs = unsafe { + BNGetCachedLowLevelILPossibleValueSet( + self.function.handle, + self.op.operands[operand_idx] as usize, + ) + }; + PossibleValueSet::from_owned_raw(raw_pvs) + } } -impl Operation<'_, A, M, NonSSA, O> +impl Operation<'_, M, NonSSA, O> where - A: Architecture, M: FunctionMutability, O: OperationArguments, { - pub fn flag_write(&self) -> Option { + /// Get the [`CoreFlagWrite`] for the operation. + /// + /// NOTE: This is only expected to be present for lifted IL. + pub fn flag_write(&self) -> Option { match self.op.flags { 0 => None, id => self.function.arch().flag_write_from_id(FlagWriteId(id)), @@ -73,9 +109,8 @@ where // LLIL_NOP, LLIL_NORET, LLIL_BP, LLIL_UNDEF, LLIL_UNIMPL pub struct NoArgs; -impl Debug for Operation<'_, A, M, F, NoArgs> +impl Debug for Operation<'_, M, F, NoArgs> where - A: Architecture, M: FunctionMutability, F: FunctionForm, { @@ -87,9 +122,8 @@ where // LLIL_POP pub struct Pop; -impl Operation<'_, A, M, F, Pop> +impl Operation<'_, M, F, Pop> where - A: Architecture, M: FunctionMutability, F: FunctionForm, { @@ -98,9 +132,8 @@ where } } -impl Debug for Operation<'_, A, M, F, Pop> +impl Debug for Operation<'_, M, F, Pop> where - A: Architecture, M: FunctionMutability, F: FunctionForm, { @@ -112,12 +145,11 @@ where } } -// LLIL_SYSCALL, LLIL_SYSCALL_SSA +// LLIL_SYSCALL pub struct Syscall; -impl Debug for Operation<'_, A, M, F, Syscall> +impl Debug for Operation<'_, M, F, Syscall> where - A: Architecture, M: FunctionMutability, F: FunctionForm, { @@ -126,25 +158,76 @@ where } } +// LLIL_SYSCALL_SSA +pub struct SyscallSsa; + +impl<'func, M, F> Operation<'func, M, F, SyscallSsa> +where + M: FunctionMutability, + F: FunctionForm, +{ + /// Get the output expression of the call. + /// + /// NOTE: This is currently always [`CallOutputSsa`]. + pub fn output_expr(&self) -> LowLevelILExpression<'func, M, F, ValueExpr> { + LowLevelILExpression::new( + self.function, + LowLevelExpressionIndex(self.op.operands[0] as usize), + ) + } + + /// Get the parameter expression of the call. + /// + /// NOTE: This is currently always [`CallParamSsa`]. + pub fn param_expr(&self) -> LowLevelILExpression<'func, M, F, ValueExpr> { + LowLevelILExpression::new( + self.function, + LowLevelExpressionIndex(self.op.operands[2] as usize), + ) + } + + /// Get the stack expression of the call. + /// + /// NOTE: This is currently always [`CallStackSsa`]. + pub fn stack_expr(&self) -> LowLevelILExpression<'func, M, F, ValueExpr> { + LowLevelILExpression::new( + self.function, + LowLevelExpressionIndex(self.op.operands[1] as usize), + ) + } +} + +impl Debug for Operation<'_, M, F, SyscallSsa> +where + M: FunctionMutability, + F: FunctionForm, +{ + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + f.debug_struct("SyscallSsa") + .field("output_expr", &self.output_expr()) + .field("param_expr", &self.param_expr()) + .field("stack_expr", &self.stack_expr()) + .finish() + } +} + // LLIL_INTRINSIC, LLIL_INTRINSIC_SSA pub struct Intrinsic; -impl Operation<'_, A, M, F, Intrinsic> +impl Operation<'_, M, F, Intrinsic> where - A: Architecture, M: FunctionMutability, F: FunctionForm, { // TODO: Support register and expression lists - pub fn intrinsic(&self) -> Option { + pub fn intrinsic(&self) -> Option { let raw_id = self.op.operands[2] as u32; self.function.arch().intrinsic_from_id(IntrinsicId(raw_id)) } } -impl Debug for Operation<'_, A, M, F, Intrinsic> +impl Debug for Operation<'_, M, F, Intrinsic> where - A: Architecture, M: FunctionMutability, F: FunctionForm, { @@ -156,12 +239,11 @@ where } } -// LLIL_SET_REG, LLIL_SET_REG_SSA, LLIL_SET_REG_PARTIAL_SSA +// LLIL_SET_REG pub struct SetReg; -impl<'func, A, M, F> Operation<'func, A, M, F, SetReg> +impl<'func, M, F> Operation<'func, M, F, SetReg> where - A: Architecture, M: FunctionMutability, F: FunctionForm, { @@ -169,28 +251,12 @@ where self.op.size } - pub fn dest_reg(&self) -> LowLevelILRegister { - let raw_id = self.op.operands[0] as u32; - - if raw_id >= 0x8000_0000 { - LowLevelILRegister::Temp(raw_id & 0x7fff_ffff) - } else { - self.function - .arch() - .register_from_id(RegisterId(raw_id)) - .map(LowLevelILRegister::ArchReg) - .unwrap_or_else(|| { - log::error!( - "got garbage register from LLIL_SET_REG @ 0x{:x}", - self.op.address - ); - - LowLevelILRegister::Temp(0) - }) - } + pub fn dest_reg(&self) -> LowLevelILRegisterKind { + let raw_id = RegisterId(self.op.operands[0] as u32); + LowLevelILRegisterKind::from_raw(&self.function.arch(), raw_id).expect("Bad register ID") } - pub fn source_expr(&self) -> LowLevelILExpression<'func, A, M, F, ValueExpr> { + pub fn source_expr(&self) -> LowLevelILExpression<'func, M, F, ValueExpr> { LowLevelILExpression::new( self.function, LowLevelExpressionIndex(self.op.operands[1] as usize), @@ -198,9 +264,8 @@ where } } -impl Debug for Operation<'_, A, M, F, SetReg> +impl Debug for Operation<'_, M, F, SetReg> where - A: Architecture, M: FunctionMutability, F: FunctionForm, { @@ -214,12 +279,11 @@ where } } -// LLIL_SET_REG_SPLIT, LLIL_SET_REG_SPLIT_SSA -pub struct SetRegSplit; +// LLIL_SET_REG_SSA +pub struct SetRegSsa; -impl<'func, A, M, F> Operation<'func, A, M, F, SetRegSplit> +impl<'func, M, F> Operation<'func, M, F, SetRegSsa> where - A: Architecture, M: FunctionMutability, F: FunctionForm, { @@ -227,49 +291,106 @@ where self.op.size } - pub fn dest_reg_high(&self) -> LowLevelILRegister { - let raw_id = self.op.operands[0] as u32; + pub fn dest_reg(&self) -> LowLevelILSSARegisterKind { + let raw_id = RegisterId(self.op.operands[0] as u32); + let reg_kind = LowLevelILRegisterKind::from_raw(&self.function.arch(), raw_id) + .expect("Bad register ID"); + let version = self.op.operands[1] as u32; + LowLevelILSSARegisterKind::new_full(reg_kind, version) + } - if raw_id >= 0x8000_0000 { - LowLevelILRegister::Temp(raw_id & 0x7fff_ffff) - } else { - self.function - .arch() - .register_from_id(RegisterId(raw_id)) - .map(LowLevelILRegister::ArchReg) - .unwrap_or_else(|| { - log::error!( - "got garbage register from LLIL_SET_REG_SPLIT @ 0x{:x}", - self.op.address - ); - - LowLevelILRegister::Temp(0) - }) - } + pub fn source_expr(&self) -> LowLevelILExpression<'func, M, F, ValueExpr> { + LowLevelILExpression::new( + self.function, + LowLevelExpressionIndex(self.op.operands[2] as usize), + ) } +} - pub fn dest_reg_low(&self) -> LowLevelILRegister { - let raw_id = self.op.operands[1] as u32; +impl Debug for Operation<'_, M, F, SetRegSsa> +where + M: FunctionMutability, + F: FunctionForm, +{ + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + f.debug_struct("SetRegSsa") + .field("address", &self.address()) + .field("size", &self.size()) + .field("dest_reg", &self.dest_reg()) + .field("source_expr", &self.source_expr()) + .finish() + } +} - if raw_id >= 0x8000_0000 { - LowLevelILRegister::Temp(raw_id & 0x7fff_ffff) - } else { - self.function - .arch() - .register_from_id(RegisterId(raw_id)) - .map(LowLevelILRegister::ArchReg) - .unwrap_or_else(|| { - log::error!( - "got garbage register from LLIL_SET_REG_SPLIT @ 0x{:x}", - self.op.address - ); - - LowLevelILRegister::Temp(0) - }) - } +// LLIL_SET_REG_PARTIAL_SSA +pub struct SetRegPartialSsa; + +impl<'func, M, F> Operation<'func, M, F, SetRegPartialSsa> +where + M: FunctionMutability, + F: FunctionForm, +{ + pub fn size(&self) -> usize { + self.op.size } - pub fn source_expr(&self) -> LowLevelILExpression<'func, A, M, F, ValueExpr> { + pub fn dest_reg(&self) -> LowLevelILSSARegisterKind { + let full_raw_id = RegisterId(self.op.operands[0] as u32); + let version = self.op.operands[1] as u32; + let partial_raw_id = RegisterId(self.op.operands[2] as u32); + let full_reg = + CoreRegister::new(self.function.arch(), full_raw_id).expect("Bad register ID"); + let partial_reg = + CoreRegister::new(self.function.arch(), partial_raw_id).expect("Bad register ID"); + LowLevelILSSARegisterKind::new_partial(full_reg, partial_reg, version) + } + + pub fn source_expr(&self) -> LowLevelILExpression<'func, M, F, ValueExpr> { + LowLevelILExpression::new( + self.function, + LowLevelExpressionIndex(self.op.operands[3] as usize), + ) + } +} + +impl Debug for Operation<'_, M, F, SetRegPartialSsa> +where + M: FunctionMutability, + F: FunctionForm, +{ + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + f.debug_struct("SetRegPartialSsa") + .field("address", &self.address()) + .field("size", &self.size()) + .field("dest_reg", &self.dest_reg()) + .field("source_expr", &self.source_expr()) + .finish() + } +} + +// LLIL_SET_REG_SPLIT +pub struct SetRegSplit; + +impl<'func, M, F> Operation<'func, M, F, SetRegSplit> +where + M: FunctionMutability, + F: FunctionForm, +{ + pub fn size(&self) -> usize { + self.op.size + } + + pub fn dest_reg_high(&self) -> LowLevelILRegisterKind { + let raw_id = RegisterId(self.op.operands[0] as u32); + LowLevelILRegisterKind::from_raw(&self.function.arch(), raw_id).expect("Bad register ID") + } + + pub fn dest_reg_low(&self) -> LowLevelILRegisterKind { + let raw_id = RegisterId(self.op.operands[1] as u32); + LowLevelILRegisterKind::from_raw(&self.function.arch(), raw_id).expect("Bad register ID") + } + + pub fn source_expr(&self) -> LowLevelILExpression<'func, M, F, ValueExpr> { LowLevelILExpression::new( self.function, LowLevelExpressionIndex(self.op.operands[2] as usize), @@ -277,9 +398,8 @@ where } } -impl Debug for Operation<'_, A, M, F, SetRegSplit> +impl Debug for Operation<'_, M, F, SetRegSplit> where - A: Architecture, M: FunctionMutability, F: FunctionForm, { @@ -294,53 +414,339 @@ where } } -// LLIL_SET_FLAG, LLIL_SET_FLAG_SSA +// LLIL_SET_REG_SPLIT_SSA +pub struct SetRegSplitSsa; + +impl<'func, M, F> Operation<'func, M, F, SetRegSplitSsa> +where + M: FunctionMutability, + F: FunctionForm, +{ + pub fn size(&self) -> usize { + self.op.size + } + + /// Because of the fixed operand list size we use another expression for the dest high register. + /// + /// NOTE: This should always be an expression of [`RegSsa`]. + pub fn dest_expr_high(&self) -> LowLevelILExpression<'func, M, F, ValueExpr> { + LowLevelILExpression::new( + self.function, + LowLevelExpressionIndex(self.op.operands[0] as usize), + ) + } + + /// Because of the fixed operand list size we use another expression for the dest low register. + /// + /// NOTE: This should always be an expression of [`RegSsa`]. + pub fn dest_expr_low(&self) -> LowLevelILExpression<'func, M, F, ValueExpr> { + LowLevelILExpression::new( + self.function, + LowLevelExpressionIndex(self.op.operands[1] as usize), + ) + } + + pub fn source_expr(&self) -> LowLevelILExpression<'func, M, F, ValueExpr> { + LowLevelILExpression::new( + self.function, + LowLevelExpressionIndex(self.op.operands[2] as usize), + ) + } +} + +impl Debug for Operation<'_, M, F, SetRegSplitSsa> +where + M: FunctionMutability, + F: FunctionForm, +{ + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + f.debug_struct("SetRegSplitSsa") + .field("address", &self.address()) + .field("size", &self.size()) + .field("dest_expr_high", &self.dest_expr_high()) + .field("dest_expr_low", &self.dest_expr_low()) + .field("source_expr", &self.source_expr()) + .finish() + } +} + +// LLIL_SET_FLAG pub struct SetFlag; -impl<'func, A, M, F> Operation<'func, A, M, F, SetFlag> +impl<'func, M, F> Operation<'func, M, F, SetFlag> where - A: Architecture, M: FunctionMutability, F: FunctionForm, { - pub fn dest_flag(&self) -> A::Flag { - // TODO: Error handling? - // TODO: Test this. + pub fn dest_flag(&self) -> CoreFlag { self.function .arch() .flag_from_id(FlagId(self.op.operands[0] as u32)) - .unwrap() + .expect("Bad flag ID") + } + + pub fn source_expr(&self) -> LowLevelILExpression<'func, M, F, ValueExpr> { + LowLevelILExpression::new( + self.function, + LowLevelExpressionIndex(self.op.operands[1] as usize), + ) + } +} + +impl Debug for Operation<'_, M, F, SetFlag> +where + M: FunctionMutability, + F: FunctionForm, +{ + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + f.debug_struct("SetFlag") + .field("address", &self.address()) + .field("dest_flag", &self.dest_flag()) + .field("source_expr", &self.source_expr()) + .finish() + } +} + +// LLIL_SET_FLAG_SSA +pub struct SetFlagSsa; + +impl<'func, M, F> Operation<'func, M, F, SetFlagSsa> +where + M: FunctionMutability, + F: FunctionForm, +{ + pub fn dest_flag(&self) -> LowLevelILSSAFlag { + let flag = self + .function + .arch() + .flag_from_id(FlagId(self.op.operands[0] as u32)) + .expect("Bad flag ID"); + let version = self.op.operands[1] as u32; + LowLevelILSSAFlag::new(flag, version) + } + + pub fn source_expr(&self) -> LowLevelILExpression<'func, M, F, ValueExpr> { + LowLevelILExpression::new( + self.function, + LowLevelExpressionIndex(self.op.operands[2] as usize), + ) + } +} + +impl Debug for Operation<'_, M, F, SetFlagSsa> +where + M: FunctionMutability, + F: FunctionForm, +{ + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + f.debug_struct("SetFlagSsa") + .field("address", &self.address()) + .field("dest_flag", &self.dest_flag()) + .field("source_expr", &self.source_expr()) + .finish() + } +} +// LLIL_LOAD +pub struct Load; + +impl<'func, M, F> Operation<'func, M, F, Load> +where + M: FunctionMutability, + F: FunctionForm, +{ + pub fn size(&self) -> usize { + self.op.size + } + + pub fn source_expr(&self) -> LowLevelILExpression<'func, M, F, ValueExpr> { + LowLevelILExpression::new( + self.function, + LowLevelExpressionIndex(self.op.operands[0] as usize), + ) + } +} + +impl Debug for Operation<'_, M, F, Load> +where + M: FunctionMutability, + F: FunctionForm, +{ + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + f.debug_struct("Load") + .field("address", &self.address()) + .field("size", &self.size()) + .field("source_expr", &self.source_expr()) + .finish() + } +} + +// LLIL_LOAD_SSA +pub struct LoadSsa; + +impl<'func, M, F> Operation<'func, M, F, LoadSsa> +where + M: FunctionMutability, + F: FunctionForm, +{ + pub fn size(&self) -> usize { + self.op.size + } + + pub fn source_expr(&self) -> LowLevelILExpression<'func, M, F, ValueExpr> { + LowLevelILExpression::new( + self.function, + LowLevelExpressionIndex(self.op.operands[0] as usize), + ) + } + + pub fn source_memory_version(&self) -> u64 { + self.op.operands[1] + } +} + +impl Debug for Operation<'_, M, F, LoadSsa> +where + M: FunctionMutability, + F: FunctionForm, +{ + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + f.debug_struct("LoadSsa") + .field("address", &self.address()) + .field("size", &self.size()) + .field("source_expr", &self.source_expr()) + .finish() + } +} + +// LLIL_STORE +pub struct Store; + +impl<'func, M, F> Operation<'func, M, F, Store> +where + M: FunctionMutability, + F: FunctionForm, +{ + pub fn size(&self) -> usize { + self.op.size + } + + pub fn dest_expr(&self) -> LowLevelILExpression<'func, M, F, ValueExpr> { + LowLevelILExpression::new( + self.function, + LowLevelExpressionIndex(self.op.operands[0] as usize), + ) + } + + pub fn source_expr(&self) -> LowLevelILExpression<'func, M, F, ValueExpr> { + LowLevelILExpression::new( + self.function, + LowLevelExpressionIndex(self.op.operands[1] as usize), + ) + } +} + +impl Debug for Operation<'_, M, F, Store> +where + M: FunctionMutability, + F: FunctionForm, +{ + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + f.debug_struct("Store") + .field("address", &self.address()) + .field("size", &self.size()) + .field("dest_expr", &self.dest_expr()) + .field("source_expr", &self.source_expr()) + .finish() + } +} + +// LLIL_STORE_SSA +pub struct StoreSsa; + +impl<'func, M, F> Operation<'func, M, F, StoreSsa> +where + M: FunctionMutability, + F: FunctionForm, +{ + pub fn size(&self) -> usize { + self.op.size + } + + pub fn dest_expr(&self) -> LowLevelILExpression<'func, M, F, ValueExpr> { + LowLevelILExpression::new( + self.function, + LowLevelExpressionIndex(self.op.operands[0] as usize), + ) + } + + pub fn dest_memory_version(&self) -> u64 { + self.op.operands[1] + } + + pub fn source_expr(&self) -> LowLevelILExpression<'func, M, F, ValueExpr> { + LowLevelILExpression::new( + self.function, + LowLevelExpressionIndex(self.op.operands[3] as usize), + ) + } + + pub fn source_memory_version(&self) -> u64 { + self.op.operands[2] + } +} + +impl Debug for Operation<'_, M, F, StoreSsa> +where + M: FunctionMutability, + F: FunctionForm, +{ + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + f.debug_struct("StoreSsa") + .field("address", &self.address()) + .field("size", &self.size()) + .field("dest_expr", &self.dest_expr()) + .field("source_expr", &self.source_expr()) + .finish() + } +} + +// LLIL_REG +pub struct Reg; + +impl Operation<'_, M, F, Reg> +where + M: FunctionMutability, + F: FunctionForm, +{ + pub fn size(&self) -> usize { + self.op.size } - pub fn source_expr(&self) -> LowLevelILExpression<'func, A, M, F, ValueExpr> { - LowLevelILExpression::new( - self.function, - LowLevelExpressionIndex(self.op.operands[1] as usize), - ) + pub fn source_reg(&self) -> LowLevelILRegisterKind { + let raw_id = RegisterId(self.op.operands[0] as u32); + LowLevelILRegisterKind::from_raw(&self.function.arch(), raw_id).expect("Bad register ID") } } -impl Debug for Operation<'_, A, M, F, SetFlag> +impl Debug for Operation<'_, M, F, Reg> where - A: Architecture, M: FunctionMutability, F: FunctionForm, { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { - f.debug_struct("SetFlag") + f.debug_struct("Reg") .field("address", &self.address()) - .field("dest_flag", &self.dest_flag()) - .field("source_expr", &self.source_expr()) + .field("size", &self.size()) + .field("source_reg", &self.source_reg()) .finish() } } -// LLIL_LOAD, LLIL_LOAD_SSA -pub struct Load; +// LLIL_REG_SSA +pub struct RegSsa; -impl<'func, A, M, F> Operation<'func, A, M, F, Load> +impl Operation<'_, M, F, RegSsa> where - A: Architecture, M: FunctionMutability, F: FunctionForm, { @@ -348,35 +754,34 @@ where self.op.size } - pub fn source_mem_expr(&self) -> LowLevelILExpression<'func, A, M, F, ValueExpr> { - LowLevelILExpression::new( - self.function, - LowLevelExpressionIndex(self.op.operands[0] as usize), - ) + pub fn source_reg(&self) -> LowLevelILSSARegisterKind { + let raw_id = RegisterId(self.op.operands[0] as u32); + let reg_kind = LowLevelILRegisterKind::from_raw(&self.function.arch(), raw_id) + .expect("Bad register ID"); + let version = self.op.operands[1] as u32; + LowLevelILSSARegisterKind::new_full(reg_kind, version) } } -impl Debug for Operation<'_, A, M, F, Load> +impl Debug for Operation<'_, M, F, RegSsa> where - A: Architecture, M: FunctionMutability, F: FunctionForm, { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { - f.debug_struct("Load") + f.debug_struct("RegSsa") .field("address", &self.address()) .field("size", &self.size()) - .field("source_mem_expr", &self.source_mem_expr()) + .field("source_reg", &self.source_reg()) .finish() } } -// LLIL_STORE, LLIL_STORE_SSA -pub struct Store; +// LLIL_REG_SSA_PARTIAL +pub struct RegPartialSsa; -impl<'func, A, M, F> Operation<'func, A, M, F, Store> +impl Operation<'_, M, F, RegPartialSsa> where - A: Architecture, M: FunctionMutability, F: FunctionForm, { @@ -384,43 +789,37 @@ where self.op.size } - pub fn dest_mem_expr(&self) -> LowLevelILExpression<'func, A, M, F, ValueExpr> { - LowLevelILExpression::new( - self.function, - LowLevelExpressionIndex(self.op.operands[0] as usize), - ) - } - - pub fn source_expr(&self) -> LowLevelILExpression<'func, A, M, F, ValueExpr> { - LowLevelILExpression::new( - self.function, - LowLevelExpressionIndex(self.op.operands[1] as usize), - ) + pub fn source_reg(&self) -> LowLevelILSSARegisterKind { + let full_raw_id = RegisterId(self.op.operands[0] as u32); + let version = self.op.operands[1] as u32; + let partial_raw_id = RegisterId(self.op.operands[2] as u32); + let full_reg = + CoreRegister::new(self.function.arch(), full_raw_id).expect("Bad register ID"); + let partial_reg = + CoreRegister::new(self.function.arch(), partial_raw_id).expect("Bad register ID"); + LowLevelILSSARegisterKind::new_partial(full_reg, partial_reg, version) } } -impl Debug for Operation<'_, A, M, F, Store> +impl Debug for Operation<'_, M, F, RegPartialSsa> where - A: Architecture, M: FunctionMutability, F: FunctionForm, { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { - f.debug_struct("Store") + f.debug_struct("RegPartialSsa") .field("address", &self.address()) .field("size", &self.size()) - .field("dest_mem_expr", &self.dest_mem_expr()) - .field("source_expr", &self.source_expr()) + .field("source_reg", &self.source_reg()) .finish() } } -// LLIL_REG, LLIL_REG_SSA, LLIL_REG_SSA_PARTIAL -pub struct Reg; +// LLIL_REG_SPLIT +pub struct RegSplit; -impl Operation<'_, A, M, F, Reg> +impl Operation<'_, M, F, RegSplit> where - A: Architecture, M: FunctionMutability, F: FunctionForm, { @@ -428,49 +827,37 @@ where self.op.size } - pub fn source_reg(&self) -> LowLevelILRegister { - let raw_id = self.op.operands[0] as u32; + pub fn low_reg(&self) -> LowLevelILRegisterKind { + let raw_id = RegisterId(self.op.operands[0] as u32); + LowLevelILRegisterKind::from_raw(&self.function.arch(), raw_id).expect("Bad register ID") + } - if raw_id >= 0x8000_0000 { - LowLevelILRegister::Temp(raw_id & 0x7fff_ffff) - } else { - self.function - .arch() - .register_from_id(RegisterId(raw_id)) - .map(LowLevelILRegister::ArchReg) - .unwrap_or_else(|| { - log::error!( - "got garbage register from LLIL_REG @ 0x{:x}", - self.op.address - ); - - LowLevelILRegister::Temp(0) - }) - } + pub fn high_reg(&self) -> LowLevelILRegisterKind { + let raw_id = RegisterId(self.op.operands[1] as u32); + LowLevelILRegisterKind::from_raw(&self.function.arch(), raw_id).expect("Bad register ID") } } -impl Debug for Operation<'_, A, M, F, Reg> +impl Debug for Operation<'_, M, F, RegSplit> where - A: Architecture, M: FunctionMutability, F: FunctionForm, { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { - f.debug_struct("Reg") + f.debug_struct("RegSplit") .field("address", &self.address()) .field("size", &self.size()) - .field("source_reg", &self.source_reg()) + .field("low_reg", &self.low_reg()) + .field("high_reg", &self.high_reg()) .finish() } } -// LLIL_REG_SPLIT, LLIL_REG_SPLIT_SSA -pub struct RegSplit; +// LLIL_REG_SPLIT_SSA +pub struct RegSplitSsa; -impl Operation<'_, A, M, F, RegSplit> +impl Operation<'_, M, F, RegSplitSsa> where - A: Architecture, M: FunctionMutability, F: FunctionForm, { @@ -478,57 +865,30 @@ where self.op.size } - pub fn low_reg(&self) -> LowLevelILRegister { - let raw_id = self.op.operands[0] as u32; - - if raw_id >= 0x8000_0000 { - LowLevelILRegister::Temp(raw_id & 0x7fff_ffff) - } else { - self.function - .arch() - .register_from_id(RegisterId(raw_id)) - .map(LowLevelILRegister::ArchReg) - .unwrap_or_else(|| { - log::error!( - "got garbage register from LLIL_REG @ 0x{:x}", - self.op.address - ); - - LowLevelILRegister::Temp(0) - }) - } + pub fn low_reg(&self) -> LowLevelILSSARegisterKind { + let raw_id = RegisterId(self.op.operands[0] as u32); + let reg_kind = LowLevelILRegisterKind::from_raw(&self.function.arch(), raw_id) + .expect("Bad register ID"); + let version = self.op.operands[1] as u32; + LowLevelILSSARegisterKind::new_full(reg_kind, version) } - pub fn high_reg(&self) -> LowLevelILRegister { - let raw_id = self.op.operands[1] as u32; - - if raw_id >= 0x8000_0000 { - LowLevelILRegister::Temp(raw_id & 0x7fff_ffff) - } else { - self.function - .arch() - .register_from_id(RegisterId(raw_id)) - .map(LowLevelILRegister::ArchReg) - .unwrap_or_else(|| { - log::error!( - "got garbage register from LLIL_REG @ 0x{:x}", - self.op.address - ); - - LowLevelILRegister::Temp(0) - }) - } + pub fn high_reg(&self) -> LowLevelILSSARegisterKind { + let raw_id = RegisterId(self.op.operands[2] as u32); + let reg_kind = LowLevelILRegisterKind::from_raw(&self.function.arch(), raw_id) + .expect("Bad register ID"); + let version = self.op.operands[3] as u32; + LowLevelILSSARegisterKind::new_full(reg_kind, version) } } -impl Debug for Operation<'_, A, M, F, RegSplit> +impl Debug for Operation<'_, M, F, RegSplitSsa> where - A: Architecture, M: FunctionMutability, F: FunctionForm, { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { - f.debug_struct("RegSplit") + f.debug_struct("RegSplitSsa") .field("address", &self.address()) .field("size", &self.size()) .field("low_reg", &self.low_reg()) @@ -540,9 +900,8 @@ where // LLIL_REG_STACK_PUSH pub struct RegStackPush; -impl<'func, A, M, F> Operation<'func, A, M, F, RegStackPush> +impl<'func, M, F> Operation<'func, M, F, RegStackPush> where - A: Architecture, M: FunctionMutability, F: FunctionForm, { @@ -550,7 +909,7 @@ where self.op.size } - pub fn dest_reg_stack(&self) -> A::RegisterStack { + pub fn dest_reg_stack(&self) -> CoreRegisterStack { let raw_id = self.op.operands[0] as u32; self.function .arch() @@ -558,7 +917,7 @@ where .expect("Bad register stack ID") } - pub fn source_expr(&self) -> LowLevelILExpression<'func, A, M, F, ValueExpr> { + pub fn source_expr(&self) -> LowLevelILExpression<'func, M, F, ValueExpr> { LowLevelILExpression::new( self.function, LowLevelExpressionIndex(self.op.operands[1] as usize), @@ -566,9 +925,8 @@ where } } -impl Debug for Operation<'_, A, M, F, RegStackPush> +impl Debug for Operation<'_, M, F, RegStackPush> where - A: Architecture, M: FunctionMutability, F: FunctionForm, { @@ -585,9 +943,8 @@ where // LLIL_REG_STACK_POP pub struct RegStackPop; -impl Operation<'_, A, M, F, RegStackPop> +impl Operation<'_, M, F, RegStackPop> where - A: Architecture, M: FunctionMutability, F: FunctionForm, { @@ -595,7 +952,7 @@ where self.op.size } - pub fn source_reg_stack(&self) -> A::RegisterStack { + pub fn source_reg_stack(&self) -> CoreRegisterStack { let raw_id = self.op.operands[0] as u32; self.function .arch() @@ -604,9 +961,8 @@ where } } -impl Debug for Operation<'_, A, M, F, RegStackPop> +impl Debug for Operation<'_, M, F, RegStackPop> where - A: Architecture, M: FunctionMutability, F: FunctionForm, { @@ -622,9 +978,8 @@ where // LLIL_FLAG, LLIL_FLAG_SSA pub struct Flag; -impl Debug for Operation<'_, A, M, F, Flag> +impl Debug for Operation<'_, M, F, Flag> where - A: Architecture, M: FunctionMutability, F: FunctionForm, { @@ -636,9 +991,8 @@ where // LLIL_FLAG_BIT, LLIL_FLAG_BIT_SSA pub struct FlagBit; -impl Debug for Operation<'_, A, M, F, FlagBit> +impl Debug for Operation<'_, M, F, FlagBit> where - A: Architecture, M: FunctionMutability, F: FunctionForm, { @@ -650,13 +1004,12 @@ where // LLIL_JUMP pub struct Jump; -impl<'func, A, M, F> Operation<'func, A, M, F, Jump> +impl<'func, M, F> Operation<'func, M, F, Jump> where - A: Architecture, M: FunctionMutability, F: FunctionForm, { - pub fn target(&self) -> LowLevelILExpression<'func, A, M, F, ValueExpr> { + pub fn target(&self) -> LowLevelILExpression<'func, M, F, ValueExpr> { LowLevelILExpression::new( self.function, LowLevelExpressionIndex(self.op.operands[0] as usize), @@ -664,9 +1017,8 @@ where } } -impl Debug for Operation<'_, A, M, F, Jump> +impl Debug for Operation<'_, M, F, Jump> where - A: Architecture, M: FunctionMutability, F: FunctionForm, { @@ -680,20 +1032,18 @@ where // LLIL_JUMP_TO pub struct JumpTo; -struct TargetListIter<'func, A, M, F> +struct TargetListIter<'func, M, F> where - A: Architecture, M: FunctionMutability, F: FunctionForm, { - function: &'func LowLevelILFunction, + function: &'func LowLevelILFunction, cursor: BNLowLevelILInstruction, cursor_operand: usize, } -impl TargetListIter<'_, A, M, F> +impl TargetListIter<'_, M, F> where - A: Architecture, M: FunctionMutability, F: FunctionForm, { @@ -710,13 +1060,12 @@ where } } -impl<'func, A, M, F> Operation<'func, A, M, F, JumpTo> +impl<'func, M, F> Operation<'func, M, F, JumpTo> where - A: Architecture, M: FunctionMutability, F: FunctionForm, { - pub fn target(&self) -> LowLevelILExpression<'func, A, M, F, ValueExpr> { + pub fn target(&self) -> LowLevelILExpression<'func, M, F, ValueExpr> { LowLevelILExpression::new( self.function, LowLevelExpressionIndex(self.op.operands[0] as usize), @@ -744,9 +1093,8 @@ where } } -impl Debug for Operation<'_, A, M, F, JumpTo> +impl Debug for Operation<'_, M, F, JumpTo> where - A: Architecture, M: FunctionMutability, F: FunctionForm, { @@ -758,16 +1106,15 @@ where } } -// LLIL_CALL, LLIL_CALL_SSA +// LLIL_CALL, LLIL_CALL_STACK_ADJUST pub struct Call; -impl<'func, A, M, F> Operation<'func, A, M, F, Call> +impl<'func, M, F> Operation<'func, M, F, Call> where - A: Architecture, M: FunctionMutability, F: FunctionForm, { - pub fn target(&self) -> LowLevelILExpression<'func, A, M, F, ValueExpr> { + pub fn target(&self) -> LowLevelILExpression<'func, M, F, ValueExpr> { LowLevelILExpression::new( self.function, LowLevelExpressionIndex(self.op.operands[0] as usize), @@ -785,9 +1132,8 @@ where } } -impl Debug for Operation<'_, A, M, F, Call> +impl Debug for Operation<'_, M, F, Call> where - A: Architecture, M: FunctionMutability, F: FunctionForm, { @@ -799,16 +1145,174 @@ where } } +// LLIL_CALL_SSA +pub struct CallSsa; + +impl<'func, M, F> Operation<'func, M, F, CallSsa> +where + M: FunctionMutability, + F: FunctionForm, +{ + pub fn target(&self) -> LowLevelILExpression<'func, M, F, ValueExpr> { + LowLevelILExpression::new( + self.function, + LowLevelExpressionIndex(self.op.operands[1] as usize), + ) + } + + /// Get the output expression of the call. + /// + /// NOTE: This is currently always [`CallOutputSsa`]. + pub fn output_expr(&self) -> LowLevelILExpression<'func, M, F, ValueExpr> { + LowLevelILExpression::new( + self.function, + LowLevelExpressionIndex(self.op.operands[0] as usize), + ) + } + + /// Get the parameter expression of the call. + /// + /// NOTE: This is currently always [`CallParamSsa`]. + pub fn param_expr(&self) -> LowLevelILExpression<'func, M, F, ValueExpr> { + LowLevelILExpression::new( + self.function, + LowLevelExpressionIndex(self.op.operands[3] as usize), + ) + } + + /// Get the stack expression of the call. + /// + /// NOTE: This is currently always [`CallStackSsa`]. + pub fn stack_expr(&self) -> LowLevelILExpression<'func, M, F, ValueExpr> { + LowLevelILExpression::new( + self.function, + LowLevelExpressionIndex(self.op.operands[2] as usize), + ) + } +} + +impl Debug for Operation<'_, M, F, CallSsa> +where + M: FunctionMutability, + F: FunctionForm, +{ + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + f.debug_struct("CallSsa") + .field("target", &self.target()) + .field("output_expr", &self.output_expr()) + .field("param_expr", &self.param_expr()) + .field("stack_expr", &self.stack_expr()) + .finish() + } +} + +// LLIL_CALL_OUTPUT_SSA +pub struct CallOutputSsa; + +impl Operation<'_, M, F, CallOutputSsa> +where + M: FunctionMutability, + F: FunctionForm, +{ + pub fn dest_regs(&self) -> Vec> { + let operand_list = self.get_operand_list(1); + + // The operand list contains a list of ([0: reg, 1: version], ...). + let paired_ssa_reg = |paired: &[u64]| { + let raw_id = RegisterId(paired[0] as u32); + let version = paired[1] as u32; + let reg_kind = LowLevelILRegisterKind::from_raw(&self.function.arch(), raw_id) + .expect("Bad register ID"); + LowLevelILSSARegisterKind::new_full(reg_kind, version) + }; + + operand_list.chunks_exact(2).map(paired_ssa_reg).collect() + } + + pub fn dest_memory_version(&self) -> u64 { + self.op.operands[0] + } +} + +impl Debug for Operation<'_, M, F, CallOutputSsa> +where + M: FunctionMutability, + F: FunctionForm, +{ + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + f.debug_struct("CallOutputSsa") + .field("dest_regs", &self.dest_regs()) + .finish() + } +} + +// LLIL_CALL_PARAM_SSA +pub struct CallParamSsa; + +impl<'func, M, F> Operation<'func, M, F, CallParamSsa> +where + M: FunctionMutability, + F: FunctionForm, +{ + pub fn param_exprs(&self) -> Vec> { + self.get_operand_list(0) + .into_iter() + .map(|val| LowLevelExpressionIndex(val as usize)) + .map(|expr_idx| LowLevelILExpression::new(self.function, expr_idx)) + .collect() + } +} + +impl Debug for Operation<'_, M, F, CallParamSsa> +where + M: FunctionMutability, + F: FunctionForm, +{ + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + f.debug_struct("CallParamSsa") + .field("param_exprs", &self.param_exprs()) + .finish() + } +} + +// LLIL_CALL_STACK_SSA +pub struct CallStackSsa; + +impl Operation<'_, M, F, CallStackSsa> +where + M: FunctionMutability, + F: FunctionForm, +{ + pub fn source_reg(&self) -> LowLevelILSSARegisterKind { + let raw_id = RegisterId(self.op.operands[0] as u32); + let reg_kind = LowLevelILRegisterKind::from_raw(&self.function.arch(), raw_id) + .expect("Bad register ID"); + let version = self.op.operands[1] as u32; + LowLevelILSSARegisterKind::new_full(reg_kind, version) + } +} + +impl Debug for Operation<'_, M, F, CallStackSsa> +where + M: FunctionMutability, + F: FunctionForm, +{ + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + f.debug_struct("CallStackSsa") + .field("source_reg", &self.source_reg()) + .finish() + } +} + // LLIL_RET pub struct Ret; -impl<'func, A, M, F> Operation<'func, A, M, F, Ret> +impl<'func, M, F> Operation<'func, M, F, Ret> where - A: Architecture, M: FunctionMutability, F: FunctionForm, { - pub fn target(&self) -> LowLevelILExpression<'func, A, M, F, ValueExpr> { + pub fn target(&self) -> LowLevelILExpression<'func, M, F, ValueExpr> { LowLevelILExpression::new( self.function, LowLevelExpressionIndex(self.op.operands[0] as usize), @@ -816,9 +1320,8 @@ where } } -impl Debug for Operation<'_, A, M, F, Ret> +impl Debug for Operation<'_, M, F, Ret> where - A: Architecture, M: FunctionMutability, F: FunctionForm, { @@ -832,27 +1335,26 @@ where // LLIL_IF pub struct If; -impl<'func, A, M, F> Operation<'func, A, M, F, If> +impl<'func, M, F> Operation<'func, M, F, If> where - A: Architecture, M: FunctionMutability, F: FunctionForm, { - pub fn condition(&self) -> LowLevelILExpression<'func, A, M, F, ValueExpr> { + pub fn condition(&self) -> LowLevelILExpression<'func, M, F, ValueExpr> { LowLevelILExpression::new( self.function, LowLevelExpressionIndex(self.op.operands[0] as usize), ) } - pub fn true_target(&self) -> LowLevelILInstruction<'func, A, M, F> { + pub fn true_target(&self) -> LowLevelILInstruction<'func, M, F> { LowLevelILInstruction::new( self.function, LowLevelInstructionIndex(self.op.operands[1] as usize), ) } - pub fn false_target(&self) -> LowLevelILInstruction<'func, A, M, F> { + pub fn false_target(&self) -> LowLevelILInstruction<'func, M, F> { LowLevelILInstruction::new( self.function, LowLevelInstructionIndex(self.op.operands[2] as usize), @@ -860,9 +1362,8 @@ where } } -impl Debug for Operation<'_, A, M, F, If> +impl Debug for Operation<'_, M, F, If> where - A: Architecture, M: FunctionMutability, F: FunctionForm, { @@ -878,13 +1379,12 @@ where // LLIL_GOTO pub struct Goto; -impl<'func, A, M, F> Operation<'func, A, M, F, Goto> +impl<'func, M, F> Operation<'func, M, F, Goto> where - A: Architecture, M: FunctionMutability, F: FunctionForm, { - pub fn target(&self) -> LowLevelILInstruction<'func, A, M, F> { + pub fn target(&self) -> LowLevelILInstruction<'func, M, F> { LowLevelILInstruction::new( self.function, LowLevelInstructionIndex(self.op.operands[0] as usize), @@ -892,9 +1392,8 @@ where } } -impl Debug for Operation<'_, A, M, F, Goto> +impl Debug for Operation<'_, M, F, Goto> where - A: Architecture, M: FunctionMutability, F: FunctionForm, { @@ -909,9 +1408,8 @@ where // Valid only in Lifted IL pub struct FlagCond; -impl Debug for Operation<'_, A, M, F, FlagCond> +impl Debug for Operation<'_, M, F, FlagCond> where - A: Architecture, M: FunctionMutability, F: FunctionForm, { @@ -924,12 +1422,12 @@ where // Valid only in Lifted IL pub struct FlagGroup; -impl Operation<'_, A, M, NonSSA, FlagGroup> +impl Operation<'_, M, F, FlagGroup> where - A: Architecture, M: FunctionMutability, + F: FunctionForm, { - pub fn flag_group(&self) -> A::FlagGroup { + pub fn flag_group(&self) -> CoreFlagGroup { let id = self.op.operands[0] as u32; self.function .arch() @@ -938,10 +1436,10 @@ where } } -impl Debug for Operation<'_, A, M, NonSSA, FlagGroup> +impl Debug for Operation<'_, M, F, FlagGroup> where - A: Architecture, M: FunctionMutability, + F: FunctionForm, { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { f.debug_struct("FlagGroup") @@ -950,22 +1448,11 @@ where } } -impl Debug for Operation<'_, A, M, SSA, FlagGroup> -where - A: Architecture, - M: FunctionMutability, -{ - fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { - f.debug_struct("FlagGroup").finish() - } -} - // LLIL_TRAP pub struct Trap; -impl Operation<'_, A, M, F, Trap> +impl Operation<'_, M, F, Trap> where - A: Architecture, M: FunctionMutability, F: FunctionForm, { @@ -974,9 +1461,8 @@ where } } -impl Debug for Operation<'_, A, M, F, Trap> +impl Debug for Operation<'_, M, F, Trap> where - A: Architecture, M: FunctionMutability, F: FunctionForm, { @@ -990,9 +1476,8 @@ where // LLIL_REG_PHI pub struct RegPhi; -impl Debug for Operation<'_, A, M, F, RegPhi> +impl Debug for Operation<'_, M, F, RegPhi> where - A: Architecture, M: FunctionMutability, F: FunctionForm, { @@ -1004,9 +1489,8 @@ where // LLIL_FLAG_PHI pub struct FlagPhi; -impl Debug for Operation<'_, A, M, F, FlagPhi> +impl Debug for Operation<'_, M, F, FlagPhi> where - A: Architecture, M: FunctionMutability, F: FunctionForm, { @@ -1018,9 +1502,8 @@ where // LLIL_MEM_PHI pub struct MemPhi; -impl Debug for Operation<'_, A, M, F, MemPhi> +impl Debug for Operation<'_, M, F, MemPhi> where - A: Architecture, M: FunctionMutability, F: FunctionForm, { @@ -1032,9 +1515,8 @@ where // LLIL_CONST, LLIL_CONST_PTR pub struct Const; -impl Operation<'_, A, M, F, Const> +impl Operation<'_, M, F, Const> where - A: Architecture, M: FunctionMutability, F: FunctionForm, { @@ -1073,9 +1555,8 @@ where } } -impl Debug for Operation<'_, A, M, F, Const> +impl Debug for Operation<'_, M, F, Const> where - A: Architecture, M: FunctionMutability, F: FunctionForm, { @@ -1090,9 +1571,8 @@ where // LLIL_EXTERN_PTR pub struct Extern; -impl Operation<'_, A, M, F, Extern> +impl Operation<'_, M, F, Extern> where - A: Architecture, M: FunctionMutability, F: FunctionForm, { @@ -1131,9 +1611,8 @@ where } } -impl Debug for Operation<'_, A, M, F, Extern> +impl Debug for Operation<'_, M, F, Extern> where - A: Architecture, M: FunctionMutability, F: FunctionForm, { @@ -1152,9 +1631,8 @@ where // LLIL_MODS pub struct BinaryOp; -impl<'func, A, M, F> Operation<'func, A, M, F, BinaryOp> +impl<'func, M, F> Operation<'func, M, F, BinaryOp> where - A: Architecture, M: FunctionMutability, F: FunctionForm, { @@ -1162,14 +1640,14 @@ where self.op.size } - pub fn left(&self) -> LowLevelILExpression<'func, A, M, F, ValueExpr> { + pub fn left(&self) -> LowLevelILExpression<'func, M, F, ValueExpr> { LowLevelILExpression::new( self.function, LowLevelExpressionIndex(self.op.operands[0] as usize), ) } - pub fn right(&self) -> LowLevelILExpression<'func, A, M, F, ValueExpr> { + pub fn right(&self) -> LowLevelILExpression<'func, M, F, ValueExpr> { LowLevelILExpression::new( self.function, LowLevelExpressionIndex(self.op.operands[1] as usize), @@ -1177,9 +1655,8 @@ where } } -impl Debug for Operation<'_, A, M, F, BinaryOp> +impl Debug for Operation<'_, M, F, BinaryOp> where - A: Architecture, M: FunctionMutability, F: FunctionForm, { @@ -1195,9 +1672,8 @@ where // LLIL_ADC, LLIL_SBB, LLIL_RLC, LLIL_RRC pub struct BinaryOpCarry; -impl<'func, A, M, F> Operation<'func, A, M, F, BinaryOpCarry> +impl<'func, M, F> Operation<'func, M, F, BinaryOpCarry> where - A: Architecture, M: FunctionMutability, F: FunctionForm, { @@ -1205,21 +1681,21 @@ where self.op.size } - pub fn left(&self) -> LowLevelILExpression<'func, A, M, F, ValueExpr> { + pub fn left(&self) -> LowLevelILExpression<'func, M, F, ValueExpr> { LowLevelILExpression::new( self.function, LowLevelExpressionIndex(self.op.operands[0] as usize), ) } - pub fn right(&self) -> LowLevelILExpression<'func, A, M, F, ValueExpr> { + pub fn right(&self) -> LowLevelILExpression<'func, M, F, ValueExpr> { LowLevelILExpression::new( self.function, LowLevelExpressionIndex(self.op.operands[1] as usize), ) } - pub fn carry(&self) -> LowLevelILExpression<'func, A, M, F, ValueExpr> { + pub fn carry(&self) -> LowLevelILExpression<'func, M, F, ValueExpr> { LowLevelILExpression::new( self.function, LowLevelExpressionIndex(self.op.operands[2] as usize), @@ -1227,9 +1703,8 @@ where } } -impl Debug for Operation<'_, A, M, F, BinaryOpCarry> +impl Debug for Operation<'_, M, F, BinaryOpCarry> where - A: Architecture, M: FunctionMutability, F: FunctionForm, { @@ -1246,9 +1721,8 @@ where // LLIL_DIVS_DP, LLIL_DIVU_DP, LLIL_MODU_DP, LLIL_MODS_DP pub struct DoublePrecDivOp; -impl<'func, A, M, F> Operation<'func, A, M, F, DoublePrecDivOp> +impl<'func, M, F> Operation<'func, M, F, DoublePrecDivOp> where - A: Architecture, M: FunctionMutability, F: FunctionForm, { @@ -1256,14 +1730,14 @@ where self.op.size } - pub fn high(&self) -> LowLevelILExpression<'func, A, M, F, ValueExpr> { + pub fn high(&self) -> LowLevelILExpression<'func, M, F, ValueExpr> { LowLevelILExpression::new( self.function, LowLevelExpressionIndex(self.op.operands[0] as usize), ) } - pub fn low(&self) -> LowLevelILExpression<'func, A, M, F, ValueExpr> { + pub fn low(&self) -> LowLevelILExpression<'func, M, F, ValueExpr> { LowLevelILExpression::new( self.function, LowLevelExpressionIndex(self.op.operands[1] as usize), @@ -1271,7 +1745,7 @@ where } // TODO: I don't think this actually exists? - pub fn right(&self) -> LowLevelILExpression<'func, A, M, F, ValueExpr> { + pub fn right(&self) -> LowLevelILExpression<'func, M, F, ValueExpr> { LowLevelILExpression::new( self.function, LowLevelExpressionIndex(self.op.operands[2] as usize), @@ -1279,9 +1753,8 @@ where } } -impl Debug for Operation<'_, A, M, F, DoublePrecDivOp> +impl Debug for Operation<'_, M, F, DoublePrecDivOp> where - A: Architecture, M: FunctionMutability, F: FunctionForm, { @@ -1300,9 +1773,8 @@ where // LLIL_ZX, LLIL_LOW_PART, LLIL_BOOL_TO_INT, LLIL_UNIMPL_MEM pub struct UnaryOp; -impl<'func, A, M, F> Operation<'func, A, M, F, UnaryOp> +impl<'func, M, F> Operation<'func, M, F, UnaryOp> where - A: Architecture, M: FunctionMutability, F: FunctionForm, { @@ -1310,7 +1782,7 @@ where self.op.size } - pub fn operand(&self) -> LowLevelILExpression<'func, A, M, F, ValueExpr> { + pub fn operand(&self) -> LowLevelILExpression<'func, M, F, ValueExpr> { LowLevelILExpression::new( self.function, LowLevelExpressionIndex(self.op.operands[0] as usize), @@ -1318,9 +1790,8 @@ where } } -impl Debug for Operation<'_, A, M, F, UnaryOp> +impl Debug for Operation<'_, M, F, UnaryOp> where - A: Architecture, M: FunctionMutability, F: FunctionForm, { @@ -1335,9 +1806,8 @@ where // LLIL_CMP_X pub struct Condition; -impl<'func, A, M, F> Operation<'func, A, M, F, Condition> +impl<'func, M, F> Operation<'func, M, F, Condition> where - A: Architecture, M: FunctionMutability, F: FunctionForm, { @@ -1345,14 +1815,14 @@ where self.op.size } - pub fn left(&self) -> LowLevelILExpression<'func, A, M, F, ValueExpr> { + pub fn left(&self) -> LowLevelILExpression<'func, M, F, ValueExpr> { LowLevelILExpression::new( self.function, LowLevelExpressionIndex(self.op.operands[0] as usize), ) } - pub fn right(&self) -> LowLevelILExpression<'func, A, M, F, ValueExpr> { + pub fn right(&self) -> LowLevelILExpression<'func, M, F, ValueExpr> { LowLevelILExpression::new( self.function, LowLevelExpressionIndex(self.op.operands[1] as usize), @@ -1360,9 +1830,8 @@ where } } -impl Debug for Operation<'_, A, M, F, Condition> +impl Debug for Operation<'_, M, F, Condition> where - A: Architecture, M: FunctionMutability, F: FunctionForm, { @@ -1378,9 +1847,8 @@ where // LLIL_UNIMPL_MEM pub struct UnimplMem; -impl<'func, A, M, F> Operation<'func, A, M, F, UnimplMem> +impl<'func, M, F> Operation<'func, M, F, UnimplMem> where - A: Architecture, M: FunctionMutability, F: FunctionForm, { @@ -1388,7 +1856,7 @@ where self.op.size } - pub fn mem_expr(&self) -> LowLevelILExpression<'func, A, M, F, ValueExpr> { + pub fn mem_expr(&self) -> LowLevelILExpression<'func, M, F, ValueExpr> { LowLevelILExpression::new( self.function, LowLevelExpressionIndex(self.op.operands[0] as usize), @@ -1396,9 +1864,8 @@ where } } -impl Debug for Operation<'_, A, M, F, UnimplMem> +impl Debug for Operation<'_, M, F, UnimplMem> where - A: Architecture, M: FunctionMutability, F: FunctionForm, { @@ -1410,6 +1877,155 @@ where } } +// LLIL_ASSERT +pub struct Assert; + +impl Operation<'_, M, F, Assert> +where + M: FunctionMutability, + F: FunctionForm, +{ + pub fn size(&self) -> usize { + self.op.size + } + + pub fn source_reg(&self) -> LowLevelILRegisterKind { + let raw_id = RegisterId(self.op.operands[0] as u32); + LowLevelILRegisterKind::from_raw(&self.function.arch(), raw_id).expect("Bad register ID") + } + + pub fn constraint(&self) -> PossibleValueSet { + self.get_constraint(1) + } +} + +impl Debug for Operation<'_, M, F, Assert> +where + M: FunctionMutability, + F: FunctionForm, +{ + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + f.debug_struct("Assert") + .field("size", &self.size()) + .field("source_reg", &self.source_reg()) + .field("constraint", &self.constraint()) + .finish() + } +} + +// LLIL_ASSERT_SSA +pub struct AssertSsa; + +impl Operation<'_, M, F, AssertSsa> +where + M: FunctionMutability, + F: FunctionForm, +{ + pub fn size(&self) -> usize { + self.op.size + } + + pub fn source_reg(&self) -> LowLevelILSSARegisterKind { + let raw_id = RegisterId(self.op.operands[0] as u32); + let reg_kind = LowLevelILRegisterKind::from_raw(&self.function.arch(), raw_id) + .expect("Bad register ID"); + let version = self.op.operands[1] as u32; + LowLevelILSSARegisterKind::new_full(reg_kind, version) + } + + pub fn constraint(&self) -> PossibleValueSet { + self.get_constraint(2) + } +} + +impl Debug for Operation<'_, M, F, AssertSsa> +where + M: FunctionMutability, + F: FunctionForm, +{ + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + f.debug_struct("AssertSsa") + .field("size", &self.size()) + .field("source_reg", &self.source_reg()) + .field("constraint", &self.constraint()) + .finish() + } +} + +// LLIL_FORCE_VER +pub struct ForceVersion; + +impl Operation<'_, M, F, ForceVersion> +where + M: FunctionMutability, + F: FunctionForm, +{ + pub fn size(&self) -> usize { + self.op.size + } + + pub fn dest_reg(&self) -> LowLevelILRegisterKind { + let raw_id = RegisterId(self.op.operands[0] as u32); + LowLevelILRegisterKind::from_raw(&self.function.arch(), raw_id).expect("Bad register ID") + } +} + +impl Debug for Operation<'_, M, F, ForceVersion> +where + M: FunctionMutability, + F: FunctionForm, +{ + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + f.debug_struct("ForceVersion") + .field("size", &self.size()) + .field("dest_reg", &self.dest_reg()) + .finish() + } +} + +// LLIL_FORCE_VER_SSA +pub struct ForceVersionSsa; + +impl Operation<'_, M, F, ForceVersionSsa> +where + M: FunctionMutability, + F: FunctionForm, +{ + pub fn size(&self) -> usize { + self.op.size + } + + pub fn dest_reg(&self) -> LowLevelILSSARegisterKind { + let raw_id = RegisterId(self.op.operands[0] as u32); + let reg_kind = LowLevelILRegisterKind::from_raw(&self.function.arch(), raw_id) + .expect("Bad register ID"); + let version = self.op.operands[1] as u32; + LowLevelILSSARegisterKind::new_full(reg_kind, version) + } + + pub fn source_reg(&self) -> LowLevelILSSARegisterKind { + let raw_id = RegisterId(self.op.operands[2] as u32); + let reg_kind = LowLevelILRegisterKind::from_raw(&self.function.arch(), raw_id) + .expect("Bad register ID"); + let version = self.op.operands[3] as u32; + LowLevelILSSARegisterKind::new_full(reg_kind, version) + } +} + +impl Debug for Operation<'_, M, F, ForceVersionSsa> +where + M: FunctionMutability, + F: FunctionForm, +{ + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + f.debug_struct("ForceVersionSsa") + .field("size", &self.size()) + .field("dest_reg", &self.dest_reg()) + .field("source_reg", &self.source_reg()) + .finish() + } +} + // TODO TEST_BIT pub trait OperationArguments: 'static {} @@ -1417,14 +2033,24 @@ pub trait OperationArguments: 'static {} impl OperationArguments for NoArgs {} impl OperationArguments for Pop {} impl OperationArguments for Syscall {} +impl OperationArguments for SyscallSsa {} impl OperationArguments for Intrinsic {} impl OperationArguments for SetReg {} +impl OperationArguments for SetRegSsa {} +impl OperationArguments for SetRegPartialSsa {} impl OperationArguments for SetRegSplit {} +impl OperationArguments for SetRegSplitSsa {} impl OperationArguments for SetFlag {} +impl OperationArguments for SetFlagSsa {} impl OperationArguments for Load {} +impl OperationArguments for LoadSsa {} impl OperationArguments for Store {} +impl OperationArguments for StoreSsa {} impl OperationArguments for Reg {} +impl OperationArguments for RegSsa {} +impl OperationArguments for RegPartialSsa {} impl OperationArguments for RegSplit {} +impl OperationArguments for RegSplitSsa {} impl OperationArguments for RegStackPush {} impl OperationArguments for RegStackPop {} impl OperationArguments for Flag {} @@ -1432,6 +2058,10 @@ impl OperationArguments for FlagBit {} impl OperationArguments for Jump {} impl OperationArguments for JumpTo {} impl OperationArguments for Call {} +impl OperationArguments for CallSsa {} +impl OperationArguments for CallOutputSsa {} +impl OperationArguments for CallParamSsa {} +impl OperationArguments for CallStackSsa {} impl OperationArguments for Ret {} impl OperationArguments for If {} impl OperationArguments for Goto {} @@ -1449,3 +2079,7 @@ impl OperationArguments for DoublePrecDivOp {} impl OperationArguments for UnaryOp {} impl OperationArguments for Condition {} impl OperationArguments for UnimplMem {} +impl OperationArguments for Assert {} +impl OperationArguments for AssertSsa {} +impl OperationArguments for ForceVersion {} +impl OperationArguments for ForceVersionSsa {} diff --git a/rust/src/medium_level_il/function.rs b/rust/src/medium_level_il/function.rs index 0ac82ff182..bdbe315945 100644 --- a/rust/src/medium_level_il/function.rs +++ b/rust/src/medium_level_il/function.rs @@ -1,5 +1,4 @@ use binaryninjacore_sys::*; -use std::ffi::c_char; use std::fmt::{Debug, Formatter}; use std::hash::{Hash, Hasher}; @@ -11,7 +10,7 @@ use crate::disassembly::DisassemblySettings; use crate::flowgraph::FlowGraph; use crate::function::{Function, Location}; use crate::rc::{Array, CoreArrayProvider, CoreArrayProviderInner, Ref, RefCountable}; -use crate::string::BnStrCompatible; +use crate::string::IntoCStr; use crate::types::Type; use crate::variable::{PossibleValueSet, RegisterValue, SSAVariable, UserVariableValue, Variable}; @@ -122,20 +121,20 @@ impl MediumLevelILFunction { unsafe { Array::new(raw_instr_idxs, count, self.to_owned()) } } - pub fn create_user_stack_var<'a, S: BnStrCompatible, C: Into>>( + pub fn create_user_stack_var<'a, C: Into>>( self, offset: i64, var_type: C, - name: S, + name: &str, ) { let mut owned_raw_var_ty = Conf::<&Type>::into_raw(var_type.into()); - let name = name.into_bytes_with_nul(); + let name = name.to_cstr(); unsafe { BNCreateUserStackVariable( self.function().handle, offset, &mut owned_raw_var_ty, - name.as_ref().as_ptr() as *const c_char, + name.as_ptr(), ) } } @@ -144,16 +143,16 @@ impl MediumLevelILFunction { unsafe { BNDeleteUserStackVariable(self.function().handle, offset) } } - pub fn create_user_var<'a, S: BnStrCompatible, C: Into>>( + pub fn create_user_var<'a, C: Into>>( &self, var: &Variable, var_type: C, - name: S, + name: &str, ignore_disjoint_uses: bool, ) { let raw_var = BNVariable::from(var); let mut owned_raw_var_ty = Conf::<&Type>::into_raw(var_type.into()); - let name = name.into_bytes_with_nul(); + let name = name.to_cstr(); unsafe { BNCreateUserVariable( self.function().handle, @@ -274,21 +273,20 @@ impl MediumLevelILFunction { Ok(()) } - pub fn create_auto_stack_var<'a, T: Into>, S: BnStrCompatible>( + pub fn create_auto_stack_var<'a, T: Into>>( &self, offset: i64, var_type: T, - name: S, + name: &str, ) { 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(); + let name = name.to_cstr(); unsafe { BNCreateAutoStackVariable( self.function().handle, offset, &mut owned_raw_var_ty, - name_c_str.as_ptr() as *const c_char, + name.as_ptr(), ) } } @@ -297,23 +295,22 @@ impl MediumLevelILFunction { unsafe { BNDeleteAutoStackVariable(self.function().handle, offset) } } - pub fn create_auto_var<'a, S: BnStrCompatible, C: Into>>( + pub fn create_auto_var<'a, C: Into>>( &self, var: &Variable, var_type: C, - name: S, + name: &str, ignore_disjoint_uses: bool, ) { let raw_var = BNVariable::from(var); 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(); + let name = name.to_cstr(); unsafe { BNCreateAutoVariable( self.function().handle, &raw_var, &mut owned_raw_var_ty, - name_c_str.as_ptr() as *const c_char, + name.as_ptr(), ignore_disjoint_uses, ) } diff --git a/rust/src/medium_level_il/instruction.rs b/rust/src/medium_level_il/instruction.rs index 5e20147818..70d69b7f81 100644 --- a/rust/src/medium_level_il/instruction.rs +++ b/rust/src/medium_level_il/instruction.rs @@ -5,12 +5,12 @@ use crate::architecture::{CoreIntrinsic, FlagId, IntrinsicId, RegisterId}; use crate::basic_block::BasicBlock; use crate::confidence::Conf; use crate::disassembly::InstructionTextToken; -use crate::operand_iter::OperandIter; use crate::rc::{Array, CoreArrayProvider, CoreArrayProviderInner, Ref}; use crate::types::Type; use crate::variable::{ConstantData, PossibleValueSet, RegisterValue, SSAVariable, Variable}; use crate::{DataFlowQueryOption, ILBranchDependence}; use binaryninjacore_sys::*; +use std::collections::BTreeMap; use std::fmt; use std::fmt::{Debug, Display, Formatter}; @@ -411,6 +411,24 @@ impl MediumLevelILInstruction { num_params: op.operands[3] as usize, first_param: op.operands[4] as usize, }), + MLIL_CALL_OUTPUT => Op::CallOutput(CallOutput { + first_output: op.operands[0] as usize, + num_outputs: op.operands[1] as usize, + }), + MLIL_CALL_PARAM => Op::CallParam(CallParam { + first_param: op.operands[0] as usize, + num_params: op.operands[1] as usize, + }), + MLIL_CALL_OUTPUT_SSA => Op::CallOutputSsa(CallOutputSsa { + dest_memory: op.operands[0], + num_outputs: op.operands[1] as usize, + first_output: op.operands[2] as usize, + }), + MLIL_CALL_PARAM_SSA => Op::CallParamSsa(CallParamSsa { + src_memory: op.operands[0], + num_params: op.operands[1] as usize, + first_param: op.operands[2] as usize, + }), MLIL_TAILCALL => Op::Tailcall(Call { num_outputs: op.operands[0] as usize, first_output: op.operands[1] as usize, @@ -438,6 +456,20 @@ impl MediumLevelILInstruction { num_params: op.operands[3] as usize, first_param: op.operands[4] as usize, }), + MLIL_MEMORY_INTRINSIC_SSA => Op::MemoryIntrinsicSsa(MemoryIntrinsicSsa { + output: op.operands[0] as usize, + intrinsic: op.operands[1] as u32, + num_params: op.operands[2] as usize, + first_param: op.operands[3] as usize, + src_memory: op.operands[4], + }), + MLIL_MEMORY_INTRINSIC_OUTPUT_SSA => { + Op::MemoryIntrinsicOutputSsa(MemoryIntrinsicOutputSsa { + dest_memory: op.operands[0], + first_output: op.operands[1] as usize, + num_outputs: op.operands[2] as usize, + }) + } MLIL_CALL_SSA => Op::CallSsa(CallSsa { output: op.operands[0] as usize, dest: op.operands[1] as usize, @@ -602,16 +634,6 @@ impl MediumLevelILInstruction { MLIL_TRAP => Op::Trap(Trap { vector: op.operands[0], }), - // translated directly into a list for Expression or Variables - // TODO MLIL_MEMORY_INTRINSIC_SSA needs to be handled properly - MLIL_CALL_OUTPUT - | MLIL_CALL_PARAM - | MLIL_CALL_PARAM_SSA - | MLIL_CALL_OUTPUT_SSA - | MLIL_MEMORY_INTRINSIC_OUTPUT_SSA - | MLIL_MEMORY_INTRINSIC_SSA => { - unimplemented!() - } }; Self { @@ -623,6 +645,54 @@ impl MediumLevelILInstruction { } } + fn get_operand_list(&self, operand_idx: usize) -> Vec { + let mut count = 0; + let raw_list_ptr = unsafe { + BNMediumLevelILGetOperandList( + self.function.handle, + self.expr_index.0, + operand_idx, + &mut count, + ) + }; + assert!(!raw_list_ptr.is_null()); + let list = unsafe { std::slice::from_raw_parts(raw_list_ptr, count).to_vec() }; + unsafe { BNMediumLevelILFreeOperandList(raw_list_ptr) }; + list + } + + fn get_var_list(&self, operand_idx: usize) -> Vec { + self.get_operand_list(operand_idx) + .into_iter() + .map(Variable::from_identifier) + .collect() + } + + fn get_ssa_var_list(&self, operand_idx: usize) -> Vec { + self.get_operand_list(operand_idx) + .chunks(2) + .map(|chunk| (Variable::from_identifier(chunk[0]), chunk[1] as usize)) + .map(|(var, version)| SSAVariable::new(var, version)) + .collect() + } + + fn get_expr_list(&self, operand_idx: usize) -> Vec { + self.get_operand_list(operand_idx) + .into_iter() + .map(|val| MediumLevelInstructionIndex(val as usize)) + .filter_map(|idx| self.function.instruction_from_expr_index(idx)) + .collect() + } + + fn get_target_map(&self, operand_idx: usize) -> BTreeMap { + self.get_operand_list(operand_idx) + .chunks(2) + // TODO: This filter is kinda redundant. + .filter_map(|chunk| chunk.get(0..2)) + .map(|chunk| (chunk[0], MediumLevelInstructionIndex(chunk[1] as usize))) + .collect() + } + pub fn lift(&self) -> MediumLevelILLiftedInstruction { use MediumLevelILInstructionKind::*; use MediumLevelILLiftedInstructionKind as Lifted; @@ -633,6 +703,7 @@ impl MediumLevelILInstruction { Bp => Lifted::Bp, Undef => Lifted::Undef, Unimpl => Lifted::Unimpl, + NotYetImplemented => Lifted::NotYetImplemented, If(op) => Lifted::If(LiftedIf { condition: self.lift_operand(op.condition), dest_true: op.dest_true, @@ -691,12 +762,7 @@ impl MediumLevelILInstruction { }), JumpTo(op) => Lifted::JumpTo(LiftedJumpTo { dest: self.lift_operand(op.dest), - targets: OperandIter::new(&*self.function, op.first_operand, op.num_operands) - .pairs() - .map(|(addr, instr_idx)| { - (addr, MediumLevelInstructionIndex(instr_idx as usize)) - }) - .collect(), + targets: self.get_target_map(1), }), Goto(op) => Lifted::Goto(op), FreeVarSlot(op) => Lifted::FreeVarSlot(op), @@ -733,14 +799,12 @@ impl MediumLevelILInstruction { }), VarPhi(op) => Lifted::VarPhi(LiftedVarPhi { dest: op.dest, - src: OperandIter::new(&*self.function, op.first_operand, op.num_operands) - .ssa_vars() - .collect(), + src: self.get_ssa_var_list(2), }), MemPhi(op) => Lifted::MemPhi(LiftedMemPhi { dest_memory: op.dest_memory, - src_memory: OperandIter::new(&*self.function, op.first_operand, op.num_operands) - .collect(), + // TODO: Make a stronger type for this. + src_memory: self.get_operand_list(0), }), VarSplit(op) => Lifted::VarSplit(op), SetVarSplit(op) => Lifted::SetVarSplit(LiftedSetVarSplit { @@ -807,45 +871,77 @@ impl MediumLevelILInstruction { Rrc(op) => Lifted::Rrc(self.lift_binary_op_carry(op)), Call(op) => Lifted::Call(self.lift_call(op)), + CallOutput(_op) => Lifted::CallOutput(LiftedCallOutput { + output: self.get_var_list(0), + }), + CallParam(_op) => Lifted::CallParam(LiftedCallParam { + params: self.get_expr_list(0).iter().map(|i| i.lift()).collect(), + }), + CallOutputSsa(op) => Lifted::CallOutputSsa(LiftedCallOutputSsa { + dest_memory: op.dest_memory, + output: self.get_ssa_var_list(1), + }), + CallParamSsa(op) => Lifted::CallParamSsa(LiftedCallParamSsa { + src_memory: op.src_memory, + params: self.get_expr_list(1).iter().map(|i| i.lift()).collect(), + }), + Tailcall(op) => Lifted::Tailcall(self.lift_call(op)), Intrinsic(op) => Lifted::Intrinsic(LiftedIntrinsic { - output: OperandIter::new(&*self.function, op.first_output, op.num_outputs) - .vars() - .collect(), + output: self.get_var_list(0), intrinsic: CoreIntrinsic::new( self.function.function().arch(), IntrinsicId(op.intrinsic), ) .expect("Valid intrinsic"), - params: OperandIter::new(&*self.function, op.first_param, op.num_params) - .exprs() + params: self + .get_expr_list(3) + .iter() .map(|expr| expr.lift()) .collect(), }), - Syscall(op) => Lifted::Syscall(LiftedSyscallCall { - output: OperandIter::new(&*self.function, op.first_output, op.num_outputs) - .vars() - .collect(), - params: OperandIter::new(&*self.function, op.first_param, op.num_params) - .exprs() + Syscall(_op) => Lifted::Syscall(LiftedSyscallCall { + output: self.get_var_list(0), + params: self + .get_expr_list(2) + .iter() .map(|expr| expr.lift()) .collect(), }), IntrinsicSsa(op) => Lifted::IntrinsicSsa(LiftedIntrinsicSsa { - output: OperandIter::new(&*self.function, op.first_output, op.num_outputs) - .ssa_vars() + output: self.get_ssa_var_list(0), + intrinsic: CoreIntrinsic::new( + self.function.function().arch(), + IntrinsicId(op.intrinsic), + ) + .expect("Valid intrinsic"), + params: self + .get_expr_list(3) + .iter() + .map(|expr| expr.lift()) .collect(), + }), + MemoryIntrinsicSsa(op) => Lifted::MemoryIntrinsicSsa(LiftedMemoryIntrinsicSsa { + output: self.lift_operand(op.output), intrinsic: CoreIntrinsic::new( self.function.function().arch(), IntrinsicId(op.intrinsic), ) .expect("Valid intrinsic"), - params: OperandIter::new(&*self.function, op.first_param, op.num_params) - .exprs() + params: self + .get_expr_list(3) + .iter() .map(|expr| expr.lift()) .collect(), + src_memory: op.src_memory, }), + MemoryIntrinsicOutputSsa(op) => { + Lifted::MemoryIntrinsicOutputSsa(LiftedMemoryIntrinsicOutputSsa { + dest_memory: op.dest_memory, + output: self.get_ssa_var_list(1), + }) + } CallSsa(op) => Lifted::CallSsa(self.lift_call_ssa(op)), TailcallSsa(op) => Lifted::TailcallSsa(self.lift_call_ssa(op)), @@ -854,28 +950,46 @@ impl MediumLevelILInstruction { TailcallUntypedSsa(op) => Lifted::TailcallUntypedSsa(self.lift_call_untyped_ssa(op)), SyscallSsa(op) => Lifted::SyscallSsa(LiftedSyscallSsa { - output: get_call_output_ssa(&self.function, op.output).collect(), - params: OperandIter::new(&*self.function, op.first_param, op.num_params) - .exprs() + output: get_call_output_ssa(&MediumLevelILInstruction::new( + self.function.clone(), + MediumLevelInstructionIndex(op.output), + )), + params: self + .get_expr_list(1) + .iter() .map(|expr| expr.lift()) .collect(), src_memory: op.src_memory, }), SyscallUntypedSsa(op) => Lifted::SyscallUntypedSsa(LiftedSyscallUntypedSsa { - output: get_call_output_ssa(&self.function, op.output).collect(), - params: get_call_params_ssa(&self.function, op.params) - .map(|param| param.lift()) - .collect(), + output: get_call_output_ssa(&MediumLevelILInstruction::new( + self.function.clone(), + MediumLevelInstructionIndex(op.output), + )), + params: get_call_params_ssa(&MediumLevelILInstruction::new( + self.function.clone(), + MediumLevelInstructionIndex(op.params), + )) + .iter() + .map(|param| param.lift()) + .collect(), stack: self.lift_operand(op.stack), }), CallUntyped(op) => Lifted::CallUntyped(self.lift_call_untyped(op)), TailcallUntyped(op) => Lifted::TailcallUntyped(self.lift_call_untyped(op)), SyscallUntyped(op) => Lifted::SyscallUntyped(LiftedSyscallUntyped { - output: get_call_output(&self.function, op.output).collect(), - params: get_call_params(&self.function, op.params) - .map(|param| param.lift()) - .collect(), + output: get_call_output(&MediumLevelILInstruction::new( + self.function.clone(), + MediumLevelInstructionIndex(op.output), + )), + params: get_call_params(&MediumLevelILInstruction::new( + self.function.clone(), + MediumLevelInstructionIndex(op.params), + )) + .iter() + .map(|param| param.lift()) + .collect(), stack: self.lift_operand(op.stack), }), @@ -911,21 +1025,24 @@ impl MediumLevelILInstruction { src: self.lift_operand(op.src), src_memory: op.src_memory, }), - Ret(op) => Lifted::Ret(LiftedRet { - src: OperandIter::new(&*self.function, op.first_operand, op.num_operands) - .exprs() + Ret(_op) => Lifted::Ret(LiftedRet { + src: self + .get_expr_list(0) + .iter() .map(|expr| expr.lift()) .collect(), }), - SeparateParamList(op) => Lifted::SeparateParamList(LiftedSeparateParamList { - params: OperandIter::new(&*self.function, op.first_param, op.num_params) - .exprs() + SeparateParamList(_op) => Lifted::SeparateParamList(LiftedSeparateParamList { + params: self + .get_expr_list(0) + .iter() .map(|expr| expr.lift()) .collect(), }), - SharedParamSlot(op) => Lifted::SharedParamSlot(LiftedSharedParamSlot { - params: OperandIter::new(&*self.function, op.first_param, op.num_params) - .exprs() + SharedParamSlot(_op) => Lifted::SharedParamSlot(LiftedSharedParamSlot { + params: self + .get_expr_list(0) + .iter() .map(|expr| expr.lift()) .collect(), }), @@ -1377,12 +1494,6 @@ impl MediumLevelILInstruction { Variable::new(var.ty, index, var.storage) } - /// alias for [MediumLevelILInstruction::split_var_for_definition] - #[inline] - pub fn get_split_var_for_definition(&self, var: &Variable) -> Variable { - self.split_var_for_definition(var) - } - fn lift_operand(&self, expr_idx: usize) -> Box { // TODO: UGH, if your gonna call it expr_idx, call the instruction and expression!!!!! // TODO: We dont even need to say instruction in the type! @@ -1420,12 +1531,11 @@ impl MediumLevelILInstruction { fn lift_call(&self, op: Call) -> LiftedCall { LiftedCall { - output: OperandIter::new(&*self.function, op.first_output, op.num_outputs) - .vars() - .collect(), + output: self.get_var_list(0), dest: self.lift_operand(op.dest), - params: OperandIter::new(&*self.function, op.first_param, op.num_params) - .exprs() + params: self + .get_expr_list(3) + .iter() .map(|expr| expr.lift()) .collect(), } @@ -1433,21 +1543,32 @@ impl MediumLevelILInstruction { fn lift_call_untyped(&self, op: CallUntyped) -> LiftedCallUntyped { LiftedCallUntyped { - output: get_call_output(&self.function, op.output).collect(), + output: get_call_output(&MediumLevelILInstruction::new( + self.function.clone(), + MediumLevelInstructionIndex(op.output), + )), dest: self.lift_operand(op.dest), - params: get_call_params(&self.function, op.params) - .map(|expr| expr.lift()) - .collect(), + params: get_call_params(&MediumLevelILInstruction::new( + self.function.clone(), + MediumLevelInstructionIndex(op.params), + )) + .iter() + .map(|expr| expr.lift()) + .collect(), stack: self.lift_operand(op.stack), } } fn lift_call_ssa(&self, op: CallSsa) -> LiftedCallSsa { LiftedCallSsa { - output: get_call_output_ssa(&self.function, op.output).collect(), + output: get_call_output_ssa(&MediumLevelILInstruction::new( + self.function.clone(), + MediumLevelInstructionIndex(op.output), + )), dest: self.lift_operand(op.dest), - params: OperandIter::new(&*self.function, op.first_param, op.num_params) - .exprs() + params: self + .get_expr_list(2) + .iter() .map(|expr| expr.lift()) .collect(), src_memory: op.src_memory, @@ -1456,11 +1577,18 @@ impl MediumLevelILInstruction { fn lift_call_untyped_ssa(&self, op: CallUntypedSsa) -> LiftedCallUntypedSsa { LiftedCallUntypedSsa { - output: get_call_output_ssa(&self.function, op.output).collect(), + output: get_call_output_ssa(&MediumLevelILInstruction::new( + self.function.clone(), + MediumLevelInstructionIndex(op.output), + )), dest: self.lift_operand(op.dest), - params: get_call_params_ssa(&self.function, op.params) - .map(|param| param.lift()) - .collect(), + params: get_call_params_ssa(&MediumLevelILInstruction::new( + self.function.clone(), + MediumLevelInstructionIndex(op.params), + )) + .iter() + .map(|param| param.lift()) + .collect(), stack: self.lift_operand(op.stack), } } @@ -1583,10 +1711,16 @@ pub enum MediumLevelILInstructionKind { Rlc(BinaryOpCarry), Rrc(BinaryOpCarry), Call(Call), + CallOutput(CallOutput), + CallParam(CallParam), + CallOutputSsa(CallOutputSsa), + CallParamSsa(CallParamSsa), Tailcall(Call), Syscall(Syscall), Intrinsic(Intrinsic), IntrinsicSsa(IntrinsicSsa), + MemoryIntrinsicSsa(MemoryIntrinsicSsa), + MemoryIntrinsicOutputSsa(MemoryIntrinsicOutputSsa), CallSsa(CallSsa), TailcallSsa(CallSsa), CallUntypedSsa(CallUntypedSsa), @@ -1629,6 +1763,9 @@ pub enum MediumLevelILInstructionKind { VarSsaField(VarSsaField), VarAliasedField(VarSsaField), Trap(Trap), + // A placeholder for instructions that the Rust bindings do not yet support. + // Distinct from `Unimpl` as that is a valid instruction. + NotYetImplemented, } fn get_float(value: u64, size: usize) -> f64 { @@ -1640,10 +1777,6 @@ fn get_float(value: u64, size: usize) -> f64 { } } -fn get_raw_operation(function: &MediumLevelILFunction, idx: usize) -> BNMediumLevelILInstruction { - unsafe { BNGetMediumLevelILByIndex(function.handle, idx) } -} - fn get_var(id: u64) -> Variable { Variable::from_identifier(id) } @@ -1652,37 +1785,32 @@ fn get_var_ssa(id: u64, version: usize) -> SSAVariable { SSAVariable::new(get_var(id), version) } -fn get_call_output(function: &MediumLevelILFunction, idx: usize) -> impl Iterator { - let op = get_raw_operation(function, idx); - assert_eq!(op.operation, BNMediumLevelILOperation::MLIL_CALL_OUTPUT); - OperandIter::new(function, op.operands[1] as usize, op.operands[0] as usize).vars() +fn get_call_output(instr: &MediumLevelILInstruction) -> Vec { + match instr.kind { + MediumLevelILInstructionKind::CallOutput(_op) => instr.get_var_list(0), + _ => vec![], + } } -fn get_call_params( - function: &MediumLevelILFunction, - idx: usize, -) -> impl Iterator { - let op = get_raw_operation(function, idx); - assert_eq!(op.operation, BNMediumLevelILOperation::MLIL_CALL_PARAM); - OperandIter::new(function, op.operands[1] as usize, op.operands[0] as usize).exprs() +fn get_call_params(instr: &MediumLevelILInstruction) -> Vec { + match instr.kind { + MediumLevelILInstructionKind::CallParam(_op) => instr.get_expr_list(0), + _ => vec![], + } } -fn get_call_output_ssa( - function: &MediumLevelILFunction, - idx: usize, -) -> impl Iterator { - let op = get_raw_operation(function, idx); - assert_eq!(op.operation, BNMediumLevelILOperation::MLIL_CALL_OUTPUT_SSA); - OperandIter::new(function, op.operands[2] as usize, op.operands[1] as usize).ssa_vars() +fn get_call_output_ssa(instr: &MediumLevelILInstruction) -> Vec { + match instr.kind { + MediumLevelILInstructionKind::CallOutputSsa(_op) => instr.get_ssa_var_list(1), + _ => vec![], + } } -fn get_call_params_ssa( - function: &MediumLevelILFunction, - idx: usize, -) -> impl Iterator { - let op = get_raw_operation(function, idx); - assert_eq!(op.operation, BNMediumLevelILOperation::MLIL_CALL_PARAM_SSA); - OperandIter::new(function, op.operands[2] as usize, op.operands[1] as usize).exprs() +fn get_call_params_ssa(instr: &MediumLevelILInstruction) -> Vec { + match instr.kind { + MediumLevelILInstructionKind::CallParamSsa(_op) => instr.get_expr_list(1), + _ => vec![], + } } /// Conditional branching instruction and an expected conditional result diff --git a/rust/src/medium_level_il/lift.rs b/rust/src/medium_level_il/lift.rs index 3f16148477..399bda78e1 100644 --- a/rust/src/medium_level_il/lift.rs +++ b/rust/src/medium_level_il/lift.rs @@ -129,10 +129,16 @@ pub enum MediumLevelILLiftedInstructionKind { Rlc(LiftedBinaryOpCarry), Rrc(LiftedBinaryOpCarry), Call(LiftedCall), + CallOutput(LiftedCallOutput), + CallParam(LiftedCallParam), + CallOutputSsa(LiftedCallOutputSsa), + CallParamSsa(LiftedCallParamSsa), Tailcall(LiftedCall), Intrinsic(LiftedIntrinsic), Syscall(LiftedSyscallCall), IntrinsicSsa(LiftedIntrinsicSsa), + MemoryIntrinsicSsa(LiftedMemoryIntrinsicSsa), + MemoryIntrinsicOutputSsa(LiftedMemoryIntrinsicOutputSsa), CallSsa(LiftedCallSsa), TailcallSsa(LiftedCallSsa), CallUntypedSsa(LiftedCallUntypedSsa), @@ -175,6 +181,9 @@ pub enum MediumLevelILLiftedInstructionKind { VarSsaField(VarSsaField), VarAliasedField(VarSsaField), Trap(Trap), + // A placeholder for instructions that the Rust bindings do not yet support. + // Distinct from `Unimpl` as that is a valid instruction. + NotYetImplemented, } impl MediumLevelILLiftedInstruction { @@ -186,6 +195,7 @@ impl MediumLevelILLiftedInstruction { Bp => "Bp", Undef => "Undef", Unimpl => "Unimpl", + NotYetImplemented => "NotYetImplemented", If(_) => "If", FloatConst(_) => "FloatConst", Const(_) => "Const", @@ -265,10 +275,16 @@ impl MediumLevelILLiftedInstruction { Rlc(_) => "Rlc", Rrc(_) => "Rrc", Call(_) => "Call", + CallOutput(_) => "CallOutput", + CallParam(_) => "CallParam", + CallOutputSsa(_) => "CallOutputSsa", + CallParamSsa(_) => "CallParamSsa", Tailcall(_) => "Tailcall", Syscall(_) => "Syscall", Intrinsic(_) => "Intrinsic", IntrinsicSsa(_) => "IntrinsicSsa", + MemoryIntrinsicSsa(_) => "MemoryIntrinsicSsa", + MemoryIntrinsicOutputSsa(_) => "MemoryIntrinsicOutputSsa", CallSsa(_) => "CallSsa", TailcallSsa(_) => "TailcallSsa", CallUntypedSsa(_) => "CallUntypedSsa", @@ -318,7 +334,7 @@ impl MediumLevelILLiftedInstruction { use MediumLevelILLiftedInstructionKind::*; use MediumLevelILLiftedOperand as Operand; match &self.kind { - Nop | Noret | Bp | Undef | Unimpl => vec![], + Nop | Noret | Bp | Undef | Unimpl | NotYetImplemented => vec![], If(op) => vec![ ("condition", Operand::Expr(*op.condition.clone())), ("dest_true", Operand::InstructionIndex(op.dest_true)), @@ -437,6 +453,16 @@ impl MediumLevelILLiftedInstruction { ("dest", Operand::Expr(*op.dest.clone())), ("params", Operand::ExprList(op.params.clone())), ], + CallOutput(op) => vec![("output", Operand::VarList(op.output.clone()))], + CallParam(op) => vec![("params", Operand::ExprList(op.params.clone()))], + CallOutputSsa(op) => vec![ + ("output", Operand::VarSsaList(op.output.clone())), + ("dest_memory", Operand::Int(op.dest_memory)), + ], + CallParamSsa(op) => vec![ + ("params", Operand::ExprList(op.params.clone())), + ("src_memory", Operand::Int(op.src_memory)), + ], Syscall(op) => vec![ ("output", Operand::VarList(op.output.clone())), ("params", Operand::ExprList(op.params.clone())), @@ -451,6 +477,16 @@ impl MediumLevelILLiftedInstruction { ("intrinsic", Operand::Intrinsic(op.intrinsic)), ("params", Operand::ExprList(op.params.clone())), ], + MemoryIntrinsicSsa(op) => vec![ + ("output", Operand::Expr(*op.output.clone())), + ("intrinsic", Operand::Intrinsic(op.intrinsic)), + ("params", Operand::ExprList(op.params.clone())), + ("src_memory", Operand::Int(op.src_memory)), + ], + MemoryIntrinsicOutputSsa(op) => vec![ + ("dest_memory", Operand::Int(op.dest_memory)), + ("output", Operand::VarSsaList(op.output.clone())), + ], CallSsa(op) | TailcallSsa(op) => vec![ ("output", Operand::VarSsaList(op.output.clone())), ("dest", Operand::Expr(*op.dest.clone())), diff --git a/rust/src/medium_level_il/operation.rs b/rust/src/medium_level_il/operation.rs index e11f59c8be..e8b70cc6b2 100644 --- a/rust/src/medium_level_il/operation.rs +++ b/rust/src/medium_level_il/operation.rs @@ -329,6 +329,54 @@ pub struct LiftedCall { pub params: Vec, } +// CALL_OUTPUT +#[derive(Debug, Copy, Clone)] +pub struct CallOutput { + pub first_output: usize, + pub num_outputs: usize, +} +#[derive(Clone, Debug, PartialEq)] +pub struct LiftedCallOutput { + pub output: Vec, +} + +// CALL_PARAM_SSA +#[derive(Debug, Copy, Clone)] +pub struct CallParam { + pub first_param: usize, + pub num_params: usize, +} +#[derive(Clone, Debug, PartialEq)] +pub struct LiftedCallParam { + pub params: Vec, +} + +// CALL_OUTPUT_SSA +#[derive(Debug, Copy, Clone)] +pub struct CallOutputSsa { + pub dest_memory: u64, + pub first_output: usize, + pub num_outputs: usize, +} +#[derive(Clone, Debug, PartialEq)] +pub struct LiftedCallOutputSsa { + pub dest_memory: u64, + pub output: Vec, +} + +// CALL_PARAM_SSA +#[derive(Debug, Copy, Clone)] +pub struct CallParamSsa { + pub src_memory: u64, + pub first_param: usize, + pub num_params: usize, +} +#[derive(Clone, Debug, PartialEq)] +pub struct LiftedCallParamSsa { + pub src_memory: u64, + pub params: Vec, +} + // SYSCALL #[derive(Debug, Copy, Clone)] pub struct Syscall { @@ -375,6 +423,36 @@ pub struct LiftedIntrinsicSsa { pub params: Vec, } +// MEMORY_INTRINSIC_SSA +#[derive(Debug, Copy, Clone)] +pub struct MemoryIntrinsicSsa { + pub output: usize, + pub intrinsic: u32, + pub first_param: usize, + pub num_params: usize, + pub src_memory: u64, +} +#[derive(Clone, Debug, PartialEq)] +pub struct LiftedMemoryIntrinsicSsa { + pub output: Box, + pub intrinsic: CoreIntrinsic, + pub params: Vec, + pub src_memory: u64, +} + +// MEMORY_INTRINSIC_OUTPUT_SSA +#[derive(Debug, Copy, Clone)] +pub struct MemoryIntrinsicOutputSsa { + pub dest_memory: u64, + pub first_output: usize, + pub num_outputs: usize, +} +#[derive(Clone, Debug, PartialEq)] +pub struct LiftedMemoryIntrinsicOutputSsa { + pub dest_memory: u64, + pub output: Vec, +} + // CALL_SSA, TAILCALL_SSA #[derive(Debug, Copy, Clone)] pub struct CallSsa { diff --git a/rust/src/metadata.rs b/rust/src/metadata.rs index fc935cd506..b6a88eef4f 100644 --- a/rust/src/metadata.rs +++ b/rust/src/metadata.rs @@ -1,7 +1,8 @@ use crate::rc::{Array, CoreArrayProvider, CoreArrayProviderInner, Guard, Ref, RefCountable}; -use crate::string::{BnStrCompatible, BnString, IntoJson}; +use crate::string::{raw_to_string, BnString, IntoCStr, IntoJson}; use binaryninjacore_sys::*; use std::collections::HashMap; +use std::fmt::{Debug, Display, Formatter}; use std::os::raw::c_char; use std::slice; @@ -30,129 +31,129 @@ impl Metadata { unsafe { BNMetadataGetType(self.handle) } } - pub fn get_boolean(&self) -> Result { + pub fn get_boolean(&self) -> Option { match self.get_type() { - MetadataType::BooleanDataType => Ok(unsafe { BNMetadataGetBoolean(self.handle) }), - _ => Err(()), + MetadataType::BooleanDataType => Some(unsafe { BNMetadataGetBoolean(self.handle) }), + _ => None, } } - pub fn get_unsigned_integer(&self) -> Result { + pub fn get_unsigned_integer(&self) -> Option { match self.get_type() { MetadataType::UnsignedIntegerDataType => { - Ok(unsafe { BNMetadataGetUnsignedInteger(self.handle) }) + Some(unsafe { BNMetadataGetUnsignedInteger(self.handle) }) } - _ => Err(()), + _ => None, } } - pub fn get_signed_integer(&self) -> Result { + pub fn get_signed_integer(&self) -> Option { match self.get_type() { MetadataType::SignedIntegerDataType => { - Ok(unsafe { BNMetadataGetSignedInteger(self.handle) }) + Some(unsafe { BNMetadataGetSignedInteger(self.handle) }) } - _ => Err(()), + _ => None, } } - pub fn get_double(&self) -> Result { + pub fn get_double(&self) -> Option { match self.get_type() { - MetadataType::DoubleDataType => Ok(unsafe { BNMetadataGetDouble(self.handle) }), - _ => Err(()), + MetadataType::DoubleDataType => Some(unsafe { BNMetadataGetDouble(self.handle) }), + _ => None, } } - pub fn get_string(&self) -> Result { + pub fn get_string(&self) -> Option { match self.get_type() { MetadataType::StringDataType => { let ptr: *mut c_char = unsafe { BNMetadataGetString(self.handle) }; if ptr.is_null() { - return Err(()); + return None; } - Ok(unsafe { BnString::from_raw(ptr) }) + Some(unsafe { BnString::from_raw(ptr) }) } - _ => Err(()), + _ => None, } } - pub fn get_boolean_list(&self) -> Result, ()> { + pub fn get_boolean_list(&self) -> Option> { match self.get_type() { MetadataType::ArrayDataType => { let mut size: usize = 0; let ptr: *mut bool = unsafe { BNMetadataGetBooleanList(self.handle, &mut size) }; if ptr.is_null() { - return Err(()); + return None; } let list = unsafe { slice::from_raw_parts(ptr, size) }; let vec = Vec::from(list); unsafe { BNFreeMetadataBooleanList(ptr, size) }; - Ok(vec) + Some(vec) } - _ => Err(()), + _ => None, } } - pub fn get_unsigned_integer_list(&self) -> Result, ()> { + pub fn get_unsigned_integer_list(&self) -> Option> { match self.get_type() { MetadataType::ArrayDataType => { let mut size: usize = 0; let ptr: *mut u64 = unsafe { BNMetadataGetUnsignedIntegerList(self.handle, &mut size) }; if ptr.is_null() { - return Err(()); + return None; } let list = unsafe { slice::from_raw_parts(ptr, size) }; let vec = Vec::from(list); unsafe { BNFreeMetadataUnsignedIntegerList(ptr, size) }; - Ok(vec) + Some(vec) } - _ => Err(()), + _ => None, } } - pub fn get_signed_integer_list(&self) -> Result, ()> { + pub fn get_signed_integer_list(&self) -> Option> { match self.get_type() { MetadataType::ArrayDataType => { let mut size: usize = 0; let ptr: *mut i64 = unsafe { BNMetadataGetSignedIntegerList(self.handle, &mut size) }; if ptr.is_null() { - return Err(()); + return None; } let list = unsafe { slice::from_raw_parts(ptr, size) }; let vec = Vec::from(list); unsafe { BNFreeMetadataSignedIntegerList(ptr, size) }; - Ok(vec) + Some(vec) } - _ => Err(()), + _ => None, } } - pub fn get_double_list(&self) -> Result, ()> { + pub fn get_double_list(&self) -> Option> { match self.get_type() { MetadataType::ArrayDataType => { let mut size: usize = 0; let ptr: *mut f64 = unsafe { BNMetadataGetDoubleList(self.handle, &mut size) }; if ptr.is_null() { - return Err(()); + return None; } let list = unsafe { slice::from_raw_parts(ptr, size) }; let vec = Vec::from(list); unsafe { BNFreeMetadataDoubleList(ptr, size) }; - Ok(vec) + Some(vec) } - _ => Err(()), + _ => None, } } - pub fn get_string_list(&self) -> Result, ()> { + pub fn get_string_list(&self) -> Option> { match self.get_type() { MetadataType::ArrayDataType => { let mut size: usize = 0; let ptr: *mut *mut c_char = unsafe { BNMetadataGetStringList(self.handle, &mut size) }; if ptr.is_null() { - return Err(()); + return None; } let list = unsafe { slice::from_raw_parts(ptr, size) }; let vec = list @@ -160,66 +161,66 @@ impl Metadata { .map(|ptr| unsafe { BnString::from_raw(*ptr) }) .collect::>(); unsafe { BNFreeMetadataStringList(ptr, size) }; - Ok(vec) + Some(vec) } - _ => Err(()), + _ => None, } } - pub fn get_json_string(&self) -> Result { + pub fn get_json_string(&self) -> Option { match self.get_type() { MetadataType::StringDataType => { let ptr: *mut c_char = unsafe { BNMetadataGetJsonString(self.handle) }; if ptr.is_null() { - return Err(()); + return None; } - Ok(unsafe { BnString::from_raw(ptr) }) + Some(unsafe { BnString::from_raw(ptr) }) } - _ => Err(()), + _ => None, } } - pub fn get_raw(&self) -> Result, ()> { + pub fn get_raw(&self) -> Option> { match self.get_type() { MetadataType::RawDataType => { let mut size: usize = 0; let ptr: *mut u8 = unsafe { BNMetadataGetRaw(self.handle, &mut size) }; if ptr.is_null() { - return Err(()); + return None; } let list = unsafe { slice::from_raw_parts(ptr, size) }; let vec = Vec::from(list); unsafe { BNFreeMetadataRaw(ptr) }; - Ok(vec) + Some(vec) } - _ => Err(()), + _ => None, } } - pub fn get_array(&self) -> Result, ()> { + pub fn get_array(&self) -> Option> { match self.get_type() { MetadataType::ArrayDataType => { let mut size: usize = 0; let ptr: *mut *mut BNMetadata = unsafe { BNMetadataGetArray(self.handle, &mut size) }; if ptr.is_null() { - return Err(()); + return None; } - Ok(unsafe { Array::new(ptr, size, ()) }) + Some(unsafe { Array::new(ptr, size, ()) }) } - _ => Err(()), + _ => None, } } - pub fn get_value_store(&self) -> Result>, ()> { + pub fn get_value_store(&self) -> Option>> { match self.get_type() { MetadataType::KeyValueDataType => { let ptr: *mut BNMetadataValueStore = unsafe { BNMetadataGetValueStore(self.handle) }; if ptr.is_null() { - return Err(()); + return None; } let size = unsafe { (*ptr).size }; @@ -230,21 +231,16 @@ impl Metadata { let mut map = HashMap::new(); for i in 0..size { - let key = unsafe { BnString::from_raw(keys[i]) }; - - let value = unsafe { - Ref::::new(Self { - handle: BNNewMetadataReference(values[i]), - }) - }; + let key = raw_to_string(keys[i]).unwrap(); + let value = unsafe { Ref::::new(Self { handle: values[i] }) }; map.insert(key, value); } unsafe { BNFreeMetadataValueStore(ptr) }; - Ok(map) + Some(map) } - _ => Err(()), + _ => None, } } @@ -267,16 +263,12 @@ impl Metadata { Ok(Some(unsafe { Self::ref_from_raw(ptr) })) } - pub fn get(&self, key: S) -> Result>, ()> { + pub fn get(&self, key: &str) -> Result>, ()> { if self.get_type() != MetadataType::KeyValueDataType { return Err(()); } - let ptr: *mut BNMetadata = unsafe { - BNMetadataGetForKey( - self.handle, - key.into_bytes_with_nul().as_ref().as_ptr() as *const c_char, - ) - }; + let raw_key = key.to_cstr(); + let ptr: *mut BNMetadata = unsafe { BNMetadataGetForKey(self.handle, raw_key.as_ptr()) }; if ptr.is_null() { return Ok(None); } @@ -291,18 +283,12 @@ impl Metadata { Ok(()) } - pub fn insert(&self, key: S, value: &Metadata) -> Result<(), ()> { + pub fn insert(&self, key: &str, value: &Metadata) -> Result<(), ()> { if self.get_type() != MetadataType::KeyValueDataType { return Err(()); } - - unsafe { - BNMetadataSetValueForKey( - self.handle, - key.into_bytes_with_nul().as_ref().as_ptr() as *const c_char, - value.handle, - ) - }; + let raw_key = key.to_cstr(); + unsafe { BNMetadataSetValueForKey(self.handle, raw_key.as_ptr(), value.handle) }; Ok(()) } @@ -315,21 +301,94 @@ impl Metadata { Ok(()) } - pub fn remove_key(&self, key: S) -> Result<(), ()> { + pub fn remove_key(&self, key: &str) -> Result<(), ()> { if self.get_type() != MetadataType::KeyValueDataType { return Err(()); } - unsafe { - BNMetadataRemoveKey( - self.handle, - key.into_bytes_with_nul().as_ref().as_ptr() as *const c_char, - ) - }; + let raw_key = key.to_cstr(); + unsafe { BNMetadataRemoveKey(self.handle, raw_key.as_ptr()) }; Ok(()) } } +impl Debug for Metadata { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + f.debug_struct("Metadata") + .field("type", &self.get_type()) + .field("len", &self.len()) + // Display will give you the metadata value as a string. + .field("value", &self.to_string()) + .finish() + } +} + +impl Display for Metadata { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + // Display will give you the metadata value as a string. + match self.get_type() { + MetadataType::BooleanDataType => match self.get_boolean() { + Some(val) => write!(f, "{}", val), + None => write!(f, "null"), + }, + MetadataType::UnsignedIntegerDataType => match self.get_unsigned_integer() { + Some(val) => write!(f, "{}", val), + None => write!(f, "null"), + }, + MetadataType::SignedIntegerDataType => match self.get_signed_integer() { + Some(val) => write!(f, "{}", val), + None => write!(f, "null"), + }, + MetadataType::DoubleDataType => match self.get_double() { + Some(val) => write!(f, "{}", val), + None => write!(f, "null"), + }, + MetadataType::StringDataType => match self.get_string() { + Some(val) => write!(f, "{}", val.to_string_lossy()), + None => write!(f, "null"), + }, + MetadataType::ArrayDataType => { + match self.get_array() { + Some(array) => { + // TODO: This is extremely ugly + write!(f, "[")?; + for (i, val) in array.iter().enumerate() { + if i > 0 { + write!(f, ", ")?; + } + write!(f, "{}", *val)?; + } + write!(f, "]")?; + Ok(()) + } + None => write!(f, "null"), + } + } + MetadataType::InvalidDataType => { + write!(f, "null") + } + MetadataType::KeyValueDataType => match self.get_value_store() { + Some(map) => { + write!(f, "{{")?; + for (i, (key, val)) in map.iter().enumerate() { + if i > 0 { + write!(f, ", ")?; + } + write!(f, "{}: {}", key, val)?; + } + write!(f, "}}")?; + Ok(()) + } + None => write!(f, "null"), + }, + MetadataType::RawDataType => match self.get_raw() { + Some(val) => write!(f, "{:x?}", val), + None => write!(f, "null"), + }, + } + } +} + unsafe impl Sync for Metadata {} unsafe impl Send for Metadata {} @@ -396,21 +455,15 @@ impl From for Ref { impl From for Ref { fn from(value: String) -> Self { - unsafe { - Metadata::ref_from_raw(BNCreateMetadataStringData( - value.into_bytes_with_nul().as_ptr() as *const c_char, - )) - } + let raw_value = value.to_cstr(); + unsafe { Metadata::ref_from_raw(BNCreateMetadataStringData(raw_value.as_ptr())) } } } impl From<&str> for Ref { fn from(value: &str) -> Self { - unsafe { - Metadata::ref_from_raw(BNCreateMetadataStringData( - value.into_bytes_with_nul().as_ptr() as *const c_char, - )) - } + let raw_value = value.to_cstr(); + unsafe { Metadata::ref_from_raw(BNCreateMetadataStringData(raw_value.as_ptr())) } } } @@ -444,16 +497,13 @@ impl From<&Array> for Ref { } } -impl From>> for Ref { - fn from(value: HashMap>) -> Self { +impl>> From> for Ref { + fn from(value: HashMap) -> Self { let data: Vec<(S::Result, Ref)> = value .into_iter() - .map(|(k, v)| (k.into_bytes_with_nul(), v)) - .collect(); - let mut keys: Vec<*const c_char> = data - .iter() - .map(|(k, _)| k.as_ref().as_ptr() as *const c_char) + .map(|(k, v)| (k.to_cstr(), v.into())) .collect(); + let mut keys: Vec<*const c_char> = data.iter().map(|(k, _)| k.as_ptr()).collect(); let mut values: Vec<*mut BNMetadata> = data.iter().map(|(_, v)| v.handle).collect(); unsafe { @@ -468,18 +518,13 @@ impl From>> for Ref { impl From<&[(S, T)]> for Ref where - S: BnStrCompatible + Copy, + S: IntoCStr + Copy, for<'a> &'a T: Into>, { fn from(value: &[(S, T)]) -> Self { - let data: Vec<(S::Result, Ref)> = value - .iter() - .map(|(k, v)| (k.into_bytes_with_nul(), v.into())) - .collect(); - let mut keys: Vec<*const c_char> = data - .iter() - .map(|(k, _)| k.as_ref().as_ptr() as *const c_char) - .collect(); + let data: Vec<(S::Result, Ref)> = + value.iter().map(|(k, v)| (k.to_cstr(), v.into())).collect(); + let mut keys: Vec<*const c_char> = data.iter().map(|(k, _)| k.as_ptr()).collect(); let mut values: Vec<*mut BNMetadata> = data.iter().map(|(_, v)| v.handle).collect(); unsafe { @@ -494,7 +539,7 @@ where impl From<[(S, T); N]> for Ref where - S: BnStrCompatible + Copy, + S: IntoCStr + Copy, for<'a> &'a T: Into>, { fn from(value: [(S, T); N]) -> Self { @@ -548,15 +593,15 @@ impl From<&Vec> for Ref { } } -impl From> for Ref { +impl From> for Ref { fn from(value: Vec) -> Self { let mut refs = vec![]; for v in value { - refs.push(v.into_bytes_with_nul()); + refs.push(v.to_cstr()); } let mut pointers = vec![]; for r in &refs { - pointers.push(r.as_ref().as_ptr() as *const c_char); + pointers.push(r.as_ptr()); } unsafe { Metadata::ref_from_raw(BNCreateMetadataStringListData( @@ -579,7 +624,7 @@ impl TryFrom<&Metadata> for bool { type Error = (); fn try_from(value: &Metadata) -> Result { - value.get_boolean() + value.get_boolean().ok_or(()) } } @@ -587,7 +632,7 @@ impl TryFrom<&Metadata> for u64 { type Error = (); fn try_from(value: &Metadata) -> Result { - value.get_unsigned_integer() + value.get_unsigned_integer().ok_or(()) } } @@ -595,7 +640,7 @@ impl TryFrom<&Metadata> for i64 { type Error = (); fn try_from(value: &Metadata) -> Result { - value.get_signed_integer() + value.get_signed_integer().ok_or(()) } } @@ -603,7 +648,7 @@ impl TryFrom<&Metadata> for f64 { type Error = (); fn try_from(value: &Metadata) -> Result { - value.get_double() + value.get_double().ok_or(()) } } @@ -611,7 +656,7 @@ impl TryFrom<&Metadata> for BnString { type Error = (); fn try_from(value: &Metadata) -> Result { - value.get_string() + value.get_string().ok_or(()) } } @@ -619,7 +664,10 @@ impl TryFrom<&Metadata> for String { type Error = (); fn try_from(value: &Metadata) -> Result { - value.get_string().map(|s| s.to_string()) + value + .get_string() + .map(|s| s.to_string_lossy().to_string()) + .ok_or(()) } } @@ -627,7 +675,7 @@ impl TryFrom<&Metadata> for Vec { type Error = (); fn try_from(value: &Metadata) -> Result { - value.get_boolean_list() + value.get_boolean_list().ok_or(()) } } @@ -635,7 +683,7 @@ impl TryFrom<&Metadata> for Vec { type Error = (); fn try_from(value: &Metadata) -> Result { - value.get_unsigned_integer_list() + value.get_unsigned_integer_list().ok_or(()) } } @@ -643,7 +691,7 @@ impl TryFrom<&Metadata> for Vec { type Error = (); fn try_from(value: &Metadata) -> Result { - value.get_signed_integer_list() + value.get_signed_integer_list().ok_or(()) } } @@ -651,7 +699,7 @@ impl TryFrom<&Metadata> for Vec { type Error = (); fn try_from(value: &Metadata) -> Result { - value.get_double_list() + value.get_double_list().ok_or(()) } } @@ -659,7 +707,7 @@ impl TryFrom<&Metadata> for Vec { type Error = (); fn try_from(value: &Metadata) -> Result { - value.get_string_list() + value.get_string_list().ok_or(()) } } @@ -669,7 +717,12 @@ impl TryFrom<&Metadata> for Vec { fn try_from(value: &Metadata) -> Result { value .get_string_list() - .map(|v| v.into_iter().map(|s| s.to_string()).collect()) + .map(|v| { + v.into_iter() + .map(|s| s.to_string_lossy().to_string()) + .collect() + }) + .ok_or(()) } } @@ -677,7 +730,7 @@ impl TryFrom<&Metadata> for Vec { type Error = (); fn try_from(value: &Metadata) -> Result { - value.get_raw() + value.get_raw().ok_or(()) } } @@ -685,15 +738,7 @@ impl TryFrom<&Metadata> for Array { type Error = (); fn try_from(value: &Metadata) -> Result { - value.get_array() - } -} - -impl TryFrom<&Metadata> for HashMap> { - type Error = (); - - fn try_from(value: &Metadata) -> Result { - value.get_value_store() + value.get_array().ok_or(()) } } @@ -701,22 +746,20 @@ impl TryFrom<&Metadata> for HashMap> { type Error = (); fn try_from(value: &Metadata) -> Result { - value - .get_value_store() - .map(|m| m.into_iter().map(|(k, v)| (k.to_string(), v)).collect()) + value.get_value_store().ok_or(()) } } impl IntoJson for &Metadata { type Output = BnString; fn get_json_string(self) -> Result { - Metadata::get_json_string(self) + Metadata::get_json_string(self).ok_or(()) } } impl IntoJson for Ref { type Output = BnString; fn get_json_string(self) -> Result { - Metadata::get_json_string(&self) + Metadata::get_json_string(&self).ok_or(()) } } diff --git a/rust/src/operand_iter.rs b/rust/src/operand_iter.rs deleted file mode 100644 index fcbf5fad8a..0000000000 --- a/rust/src/operand_iter.rs +++ /dev/null @@ -1,237 +0,0 @@ -use binaryninjacore_sys::BNGetHighLevelILByIndex; -use binaryninjacore_sys::BNGetMediumLevelILByIndex; -use binaryninjacore_sys::BNHighLevelILOperation; -use binaryninjacore_sys::BNMediumLevelILOperation; - -use crate::high_level_il::{ - HighLevelILFunction, HighLevelILInstruction, HighLevelInstructionIndex, -}; -use crate::medium_level_il::{ - MediumLevelILFunction, MediumLevelILInstruction, MediumLevelInstructionIndex, -}; -use crate::rc::{Ref, RefCountable}; -use crate::variable::{SSAVariable, Variable}; - -// TODO: This code needs to go away IMO, we have the facilities to do this for each IL already! - -pub trait ILFunction { - type Instruction; - type InstructionIndex: From; - - // TODO Actually this is il expression from index! - fn il_instruction_from_index(&self, instr_index: Self::InstructionIndex) -> Self::Instruction; - fn operands_from_index(&self, instr_index: Self::InstructionIndex) -> [u64; 5]; -} - -impl ILFunction for MediumLevelILFunction { - type Instruction = MediumLevelILInstruction; - type InstructionIndex = MediumLevelInstructionIndex; - - fn il_instruction_from_index(&self, instr_index: Self::InstructionIndex) -> Self::Instruction { - self.instruction_from_expr_index(instr_index).unwrap() - } - - fn operands_from_index(&self, instr_index: Self::InstructionIndex) -> [u64; 5] { - // TODO: WTF?!?!?! - let node = unsafe { BNGetMediumLevelILByIndex(self.handle, instr_index.0) }; - assert_eq!(node.operation, BNMediumLevelILOperation::MLIL_UNDEF); - node.operands - } -} - -impl ILFunction for HighLevelILFunction { - type Instruction = HighLevelILInstruction; - type InstructionIndex = HighLevelInstructionIndex; - - fn il_instruction_from_index(&self, instr_index: Self::InstructionIndex) -> Self::Instruction { - self.instruction_from_expr_index(instr_index).unwrap() - } - - fn operands_from_index(&self, instr_index: Self::InstructionIndex) -> [u64; 5] { - let node = unsafe { BNGetHighLevelILByIndex(self.handle, instr_index.0, self.full_ast) }; - assert_eq!(node.operation, BNHighLevelILOperation::HLIL_UNDEF); - node.operands - } -} - -pub struct OperandIter { - function: Ref, - remaining: usize, - next_iter_idx: Option, - current_iter: OperandIterInner, -} - -impl OperandIter { - pub(crate) fn new(function: &F, idx: usize, number: usize) -> Self { - // Zero-length lists immediately finish iteration - let next_iter_idx = if number > 0 { Some(idx) } else { None }; - Self { - function: function.to_owned(), - remaining: number, - next_iter_idx, - current_iter: OperandIterInner::empty(), - } - } - - pub fn pairs(self) -> OperandPairIter { - assert_eq!(self.len() % 2, 0); - OperandPairIter(self) - } - - pub fn exprs(self) -> OperandExprIter { - OperandExprIter(self) - } - - pub fn vars(self) -> OperandVarIter { - OperandVarIter(self) - } - - pub fn ssa_vars(self) -> OperandSSAVarIter { - OperandSSAVarIter(self.pairs()) - } -} - -impl Iterator for OperandIter { - type Item = u64; - - fn next(&mut self) -> Option { - if let Some(item) = self.current_iter.next() { - self.remaining -= 1; - Some(item) - } else { - // Will short-circuit and return `None` once iter is exhausted - let iter_idx = self.next_iter_idx?; - let iter_idx = F::InstructionIndex::from(iter_idx as u64); - let operands = self.function.operands_from_index(iter_idx); - - let next = if self.remaining > 4 { - self.next_iter_idx = Some(operands[4] as usize); - &operands[..4] - } else { - self.next_iter_idx = None; - &operands[..self.remaining] - }; - - self.current_iter = OperandIterInner::from_slice(next); - self.next() - } - } -} - -impl ExactSizeIterator for OperandIter { - fn len(&self) -> usize { - self.remaining + self.current_iter.len() - } -} - -struct OperandIterInner { - arr: [u64; 4], - idx: usize, -} - -impl OperandIterInner { - fn from_slice(slice: &[u64]) -> Self { - assert!(slice.len() <= 4); - let idx = 4 - slice.len(); - let mut arr = [0; 4]; - arr[idx..].copy_from_slice(slice); - Self { arr, idx } - } - - fn empty() -> Self { - Self { - arr: [0; 4], - idx: 4, - } - } -} - -impl Iterator for OperandIterInner { - type Item = u64; - - fn next(&mut self) -> Option { - if self.idx < 4 { - let val = self.arr[self.idx]; - self.idx += 1; - Some(val) - } else { - None - } - } -} - -impl ExactSizeIterator for OperandIterInner { - fn len(&self) -> usize { - 4 - self.idx - } -} - -pub struct OperandPairIter(OperandIter); - -impl Iterator for OperandPairIter { - type Item = (u64, u64); - - fn next(&mut self) -> Option { - let first = self.0.next()?; - let second = self.0.next()?; - Some((first, second)) - } -} -impl ExactSizeIterator for OperandPairIter { - fn len(&self) -> usize { - self.0.len() / 2 - } -} - -pub struct OperandExprIter(OperandIter); - -impl Iterator for OperandExprIter { - type Item = F::Instruction; - - fn next(&mut self) -> Option { - self.0 - .next() - .map(F::InstructionIndex::from) - .map(|idx| self.0.function.il_instruction_from_index(idx)) - } -} - -impl ExactSizeIterator for OperandExprIter { - fn len(&self) -> usize { - self.0.len() - } -} - -pub struct OperandVarIter(OperandIter); - -impl Iterator for OperandVarIter { - type Item = Variable; - - fn next(&mut self) -> Option { - self.0.next().map(Variable::from_identifier) - } -} -impl ExactSizeIterator for OperandVarIter { - fn len(&self) -> usize { - self.0.len() - } -} - -pub struct OperandSSAVarIter(OperandPairIter); - -impl Iterator for OperandSSAVarIter { - type Item = SSAVariable; - - fn next(&mut self) -> Option { - self.0.next().map(|(id, version)| { - let var = Variable::from_identifier(id); - SSAVariable::new(var, version as _) - }) - } -} - -impl ExactSizeIterator for OperandSSAVarIter { - fn len(&self) -> usize { - self.0.len() - } -} diff --git a/rust/src/platform.rs b/rust/src/platform.rs index 72f6143041..1bbdef4e93 100644 --- a/rust/src/platform.rs +++ b/rust/src/platform.rs @@ -82,10 +82,10 @@ impl Platform { Ref::new(Self { handle }) } - pub fn by_name(name: S) -> Option> { - let raw_name = name.into_bytes_with_nul(); + pub fn by_name(name: &str) -> Option> { + let raw_name = name.to_cstr(); unsafe { - let res = BNGetPlatformByName(raw_name.as_ref().as_ptr() as *mut _); + let res = BNGetPlatformByName(raw_name.as_ptr()); if res.is_null() { None @@ -113,30 +113,24 @@ impl Platform { } } - pub fn list_by_os(name: S) -> Array { - let raw_name = name.into_bytes_with_nul(); + pub fn list_by_os(name: &str) -> Array { + let raw_name = name.to_cstr(); unsafe { let mut count = 0; - let handles = BNGetPlatformListByOS(raw_name.as_ref().as_ptr() as *mut _, &mut count); + let handles = BNGetPlatformListByOS(raw_name.as_ptr(), &mut count); Array::new(handles, count, ()) } } - pub fn list_by_os_and_arch( - name: S, - arch: &CoreArchitecture, - ) -> Array { - let raw_name = name.into_bytes_with_nul(); + pub fn list_by_os_and_arch(name: &str, arch: &CoreArchitecture) -> Array { + let raw_name = name.to_cstr(); unsafe { let mut count = 0; - let handles = BNGetPlatformListByOSAndArchitecture( - raw_name.as_ref().as_ptr() as *mut _, - arch.handle, - &mut count, - ); + let handles = + BNGetPlatformListByOSAndArchitecture(raw_name.as_ptr(), arch.handle, &mut count); Array::new(handles, count, ()) } @@ -151,19 +145,19 @@ impl Platform { } } - pub fn new(arch: &A, name: S) -> Ref { - let name = name.into_bytes_with_nul(); + pub fn new(arch: &A, name: &str) -> Ref { + let name = name.to_cstr(); unsafe { - let handle = BNCreatePlatform(arch.as_ref().handle, name.as_ref().as_ptr() as *mut _); + let handle = BNCreatePlatform(arch.as_ref().handle, name.as_ptr()); assert!(!handle.is_null()); Ref::new(Self { handle }) } } - pub fn name(&self) -> BnString { + pub fn name(&self) -> String { unsafe { let raw_name = BNGetPlatformName(self.handle); - BnString::from_raw(raw_name) + BnString::into_string(raw_name) } } @@ -179,25 +173,34 @@ impl Platform { unsafe { TypeContainer::from_raw(type_container_ptr.unwrap()) } } - pub fn get_type_libraries_by_name(&self, name: T) -> Array { + /// Get all the type libraries that have been registered with the name. + /// + /// NOTE: This is a list because libraries can have `alternate_names`, use [`Platform::get_type_library_by_name`] + /// if you want to get _the_ type library with that name, skipping alternate names. + pub fn get_type_libraries_by_name(&self, name: &str) -> Array { let mut count = 0; - let name = name.into_bytes_with_nul(); - let result = unsafe { - BNGetPlatformTypeLibrariesByName( - self.handle, - name.as_ref().as_ptr() as *mut _, - &mut count, - ) - }; + let name = name.to_cstr(); + let result = + unsafe { BNGetPlatformTypeLibrariesByName(self.handle, name.as_ptr(), &mut count) }; assert!(!result.is_null()); unsafe { Array::new(result, count, ()) } } - pub fn register_os(&self, os: S) { - let os = os.into_bytes_with_nul(); + /// Get the type library with the given name. + /// + /// NOTE: This finds the first type library that has the given name, skipping alternate names. + pub fn get_type_library_by_name(&self, name: &str) -> Option> { + let libraries = self.get_type_libraries_by_name(name); + libraries + .iter() + .find(|lib| lib.name() == name) + .map(|lib| lib.to_owned()) + } + pub fn register_os(&self, os: &str) { + let os = os.to_cstr(); unsafe { - BNRegisterPlatform(os.as_ref().as_ptr() as *mut _, self.handle); + BNRegisterPlatform(os.as_ptr(), self.handle); } } @@ -301,7 +304,7 @@ impl Platform { assert!(!error_string.is_null()); Err(TypeParserError::new( TypeParserErrorSeverity::FatalSeverity, - unsafe { BnString::from_raw(error_string) }.to_string(), + unsafe { BnString::into_string(error_string) }, file_name.to_string(), 0, 0, @@ -345,7 +348,7 @@ impl Platform { assert!(!error_string.is_null()); Err(TypeParserError::new( TypeParserErrorSeverity::FatalSeverity, - unsafe { BnString::from_raw(error_string) }.to_string(), + unsafe { BnString::into_string(error_string) }, filename.to_string(), 0, 0, @@ -386,7 +389,7 @@ impl Platform { assert!(!error_string.is_null()); Err(TypeParserError::new( TypeParserErrorSeverity::FatalSeverity, - unsafe { BnString::from_raw(error_string) }.to_string(), + unsafe { BnString::into_string(error_string) }, filename.to_string(), 0, 0, diff --git a/rust/src/project.rs b/rust/src/project.rs index fad1e5ef2f..8b5f594a33 100644 --- a/rust/src/project.rs +++ b/rust/src/project.rs @@ -1,7 +1,7 @@ pub mod file; pub mod folder; -use std::ffi::{c_char, c_void}; +use std::ffi::c_void; use std::fmt::Debug; use std::ptr::{null_mut, NonNull}; use std::time::{Duration, SystemTime, UNIX_EPOCH}; @@ -13,7 +13,7 @@ use crate::progress::{NoProgressCallback, ProgressCallback}; use crate::project::file::ProjectFile; use crate::project::folder::ProjectFolder; use crate::rc::{Array, CoreArrayProvider, CoreArrayProviderInner, Guard, Ref, RefCountable}; -use crate::string::{BnStrCompatible, BnString}; +use crate::string::{BnString, IntoCStr}; pub struct Project { pub(crate) handle: NonNull, @@ -35,28 +35,25 @@ impl Project { unsafe { Array::new(result, count, ()) } } + // TODO: Path here is actually local path? /// Create a new project /// /// * `path` - Path to the project directory (.bnpr) /// * `name` - Name of the new project - pub fn create(path: P, name: S) -> Option> { - let path_raw = path.into_bytes_with_nul(); - let name_raw = name.into_bytes_with_nul(); - let handle = unsafe { - BNCreateProject( - path_raw.as_ref().as_ptr() as *const c_char, - name_raw.as_ref().as_ptr() as *const c_char, - ) - }; + pub fn create(path: &str, name: &str) -> Option> { + let path_raw = path.to_cstr(); + let name_raw = name.to_cstr(); + let handle = unsafe { BNCreateProject(path_raw.as_ptr(), name_raw.as_ptr()) }; NonNull::new(handle).map(|h| unsafe { Self::ref_from_raw(h) }) } + // TODO: Path here is actually local path? /// Open an existing project /// /// * `path` - Path to the project directory (.bnpr) or project metadata file (.bnpm) - pub fn open_project(path: P) -> Option> { - let path_raw = path.into_bytes_with_nul(); - let handle = unsafe { BNOpenProject(path_raw.as_ref().as_ptr() as *const c_char) }; + pub fn open_project(path: &str) -> Option> { + let path_raw = path.to_cstr(); + let handle = unsafe { BNOpenProject(path_raw.as_ptr()) }; NonNull::new(handle).map(|h| unsafe { Self::ref_from_raw(h) }) } @@ -84,53 +81,41 @@ impl Project { } /// Get the unique id of this project - pub fn id(&self) -> BnString { - unsafe { BnString::from_raw(BNProjectGetId(self.handle.as_ptr())) } + pub fn id(&self) -> String { + unsafe { BnString::into_string(BNProjectGetId(self.handle.as_ptr())) } } /// Get the path of the project - pub fn path(&self) -> BnString { - unsafe { BnString::from_raw(BNProjectGetPath(self.handle.as_ptr())) } + pub fn path(&self) -> String { + unsafe { BnString::into_string(BNProjectGetPath(self.handle.as_ptr())) } } /// Get the name of the project - pub fn name(&self) -> BnString { - unsafe { BnString::from_raw(BNProjectGetName(self.handle.as_ptr())) } + pub fn name(&self) -> String { + unsafe { BnString::into_string(BNProjectGetName(self.handle.as_ptr())) } } /// Set the name of the project - pub fn set_name(&self, value: S) { - let value = value.into_bytes_with_nul(); - unsafe { - BNProjectSetName( - self.handle.as_ptr(), - value.as_ref().as_ptr() as *const c_char, - ) - } + pub fn set_name(&self, value: &str) { + let value = value.to_cstr(); + unsafe { BNProjectSetName(self.handle.as_ptr(), value.as_ptr()) } } /// Get the description of the project - pub fn description(&self) -> BnString { - unsafe { BnString::from_raw(BNProjectGetDescription(self.handle.as_ptr())) } + pub fn description(&self) -> String { + unsafe { BnString::into_string(BNProjectGetDescription(self.handle.as_ptr())) } } /// Set the description of the project - pub fn set_description(&self, value: S) { - let value = value.into_bytes_with_nul(); - unsafe { - BNProjectSetDescription( - self.handle.as_ptr(), - value.as_ref().as_ptr() as *const c_char, - ) - } + pub fn set_description(&self, value: &str) { + let value = value.to_cstr(); + unsafe { BNProjectSetDescription(self.handle.as_ptr(), value.as_ptr()) } } /// Retrieves metadata stored under a key from the project - pub fn query_metadata(&self, key: S) -> Ref { - let key = key.into_bytes_with_nul(); - let result = unsafe { - BNProjectQueryMetadata(self.handle.as_ptr(), key.as_ref().as_ptr() as *const c_char) - }; + pub fn query_metadata(&self, key: &str) -> Ref { + let key = key.to_cstr(); + let result = unsafe { BNProjectQueryMetadata(self.handle.as_ptr(), key.as_ptr()) }; unsafe { Metadata::ref_from_raw(result) } } @@ -138,26 +123,15 @@ impl Project { /// /// * `key` - Key under which to store the Metadata object /// * `value` - Object to store - pub fn store_metadata(&self, key: S, value: &Metadata) -> bool { - let key_raw = key.into_bytes_with_nul(); - unsafe { - BNProjectStoreMetadata( - self.handle.as_ptr(), - key_raw.as_ref().as_ptr() as *const c_char, - value.handle, - ) - } + pub fn store_metadata(&self, key: &str, value: &Metadata) -> bool { + let key_raw = key.to_cstr(); + unsafe { BNProjectStoreMetadata(self.handle.as_ptr(), key_raw.as_ptr(), value.handle) } } /// Removes the metadata associated with this `key` from the project - pub fn remove_metadata(&self, key: S) { - let key_raw = key.into_bytes_with_nul(); - unsafe { - BNProjectRemoveMetadata( - self.handle.as_ptr(), - key_raw.as_ref().as_ptr() as *const c_char, - ) - } + pub fn remove_metadata(&self, key: &str) { + let key_raw = key.to_cstr(); + unsafe { BNProjectRemoveMetadata(self.handle.as_ptr(), key_raw.as_ptr()) } } pub fn push_folder(&self, file: &ProjectFolder) { @@ -169,16 +143,12 @@ impl Project { /// * `path` - Path to folder on disk /// * `parent` - Parent folder in the project that will contain the new contents /// * `description` - Description for created root folder - pub fn create_folder_from_path( + pub fn create_folder_from_path( &self, - path: P, + path: &str, parent: Option<&ProjectFolder>, - description: D, - ) -> Result, ()> - where - P: BnStrCompatible, - D: BnStrCompatible, - { + description: &str, + ) -> Result, ()> { self.create_folder_from_path_with_progress(path, parent, description, NoProgressCallback) } @@ -188,28 +158,26 @@ impl Project { /// * `parent` - Parent folder in the project that will contain the new contents /// * `description` - Description for created root folder /// * `progress` - [`ProgressCallback`] that will be called as the [`ProjectFolder`] is being created - pub fn create_folder_from_path_with_progress( + pub fn create_folder_from_path_with_progress( &self, - path: P, + path: &str, parent: Option<&ProjectFolder>, - description: D, + description: &str, mut progress: PC, ) -> Result, ()> where - P: BnStrCompatible, - D: BnStrCompatible, PC: ProgressCallback, { - let path_raw = path.into_bytes_with_nul(); - let description_raw = description.into_bytes_with_nul(); + let path_raw = path.to_cstr(); + let description_raw = description.to_cstr(); let parent_ptr = parent.map(|p| p.handle.as_ptr()).unwrap_or(null_mut()); unsafe { let result = BNProjectCreateFolderFromPath( self.handle.as_ptr(), - path_raw.as_ref().as_ptr() as *const c_char, + path_raw.as_ptr(), parent_ptr, - description_raw.as_ref().as_ptr() as *const c_char, + description_raw.as_ptr(), &mut progress as *mut PC as *mut c_void, Some(PC::cb_progress_callback), ); @@ -222,25 +190,21 @@ impl Project { /// * `parent` - Parent folder in the project that will contain the new folder /// * `name` - Name for the created folder /// * `description` - Description for created folder - pub fn create_folder( + pub fn create_folder( &self, parent: Option<&ProjectFolder>, - name: N, - description: D, - ) -> Result, ()> - where - N: BnStrCompatible, - D: BnStrCompatible, - { - let name_raw = name.into_bytes_with_nul(); - let description_raw = description.into_bytes_with_nul(); + name: &str, + description: &str, + ) -> Result, ()> { + let name_raw = name.to_cstr(); + let description_raw = description.to_cstr(); let parent_ptr = parent.map(|p| p.handle.as_ptr()).unwrap_or(null_mut()); unsafe { let result = BNProjectCreateFolder( self.handle.as_ptr(), parent_ptr, - name_raw.as_ref().as_ptr() as *const c_char, - description_raw.as_ref().as_ptr() as *const c_char, + name_raw.as_ptr(), + description_raw.as_ptr(), ); Ok(ProjectFolder::ref_from_raw(NonNull::new(result).ok_or(())?)) } @@ -252,29 +216,24 @@ impl Project { /// * `name` - Name for the created folder /// * `description` - Description for created folder /// * `id` - id unique ID - pub unsafe fn create_folder_unsafe( + pub unsafe fn create_folder_unsafe( &self, parent: Option<&ProjectFolder>, - name: N, - description: D, - id: I, - ) -> Result, ()> - where - N: BnStrCompatible, - D: BnStrCompatible, - I: BnStrCompatible, - { - let name_raw = name.into_bytes_with_nul(); - let description_raw = description.into_bytes_with_nul(); + name: &str, + description: &str, + id: &str, + ) -> Result, ()> { + let name_raw = name.to_cstr(); + let description_raw = description.to_cstr(); let parent_ptr = parent.map(|p| p.handle.as_ptr()).unwrap_or(null_mut()); - let id_raw = id.into_bytes_with_nul(); + let id_raw = id.to_cstr(); unsafe { let result = BNProjectCreateFolderUnsafe( self.handle.as_ptr(), parent_ptr, - name_raw.as_ref().as_ptr() as *const c_char, - description_raw.as_ref().as_ptr() as *const c_char, - id_raw.as_ref().as_ptr() as *const c_char, + name_raw.as_ptr(), + description_raw.as_ptr(), + id_raw.as_ptr(), ); Ok(ProjectFolder::ref_from_raw(NonNull::new(result).ok_or(())?)) } @@ -292,10 +251,9 @@ impl Project { } /// Retrieve a folder in the project by unique folder `id` - pub fn folder_by_id(&self, id: S) -> Option> { - let id_raw = id.into_bytes_with_nul(); - let id_ptr = id_raw.as_ref().as_ptr() as *const c_char; - let result = unsafe { BNProjectGetFolderById(self.handle.as_ptr(), id_ptr) }; + pub fn folder_by_id(&self, id: &str) -> Option> { + let raw_id = id.to_cstr(); + let result = unsafe { BNProjectGetFolderById(self.handle.as_ptr(), raw_id.as_ptr()) }; let handle = NonNull::new(result)?; Some(unsafe { ProjectFolder::ref_from_raw(handle) }) } @@ -311,17 +269,17 @@ impl Project { /// /// * `folder` - [`ProjectFolder`] to delete recursively /// * `progress` - [`ProgressCallback`] that will be called as objects get deleted - pub fn delete_folder_with_progress( + pub fn delete_folder_with_progress( &self, folder: &ProjectFolder, - mut progress: P, + mut progress: PC, ) -> Result<(), ()> { let result = unsafe { BNProjectDeleteFolder( self.handle.as_ptr(), folder.handle.as_ptr(), - &mut progress as *mut P as *mut c_void, - Some(P::cb_progress_callback), + &mut progress as *mut PC as *mut c_void, + Some(PC::cb_progress_callback), ) }; @@ -342,18 +300,13 @@ impl Project { /// * `folder` - Folder to place the created file in /// * `name` - Name to assign to the created file /// * `description` - Description to assign to the created file - pub fn create_file_from_path( + pub fn create_file_from_path( &self, - path: P, + path: &str, folder: Option<&ProjectFolder>, - name: N, - description: D, - ) -> Result, ()> - where - P: BnStrCompatible, - N: BnStrCompatible, - D: BnStrCompatible, - { + name: &str, + description: &str, + ) -> Result, ()> { self.create_file_from_path_with_progress( path, folder, @@ -370,32 +323,29 @@ impl Project { /// * `name` - Name to assign to the created file /// * `description` - Description to assign to the created file /// * `progress` - [`ProgressCallback`] that will be called as the [`ProjectFile`] is being added - pub fn create_file_from_path_with_progress( + pub fn create_file_from_path_with_progress( &self, - path: P, + path: &str, folder: Option<&ProjectFolder>, - name: N, - description: D, + name: &str, + description: &str, mut progress: PC, ) -> Result, ()> where - P: BnStrCompatible, - N: BnStrCompatible, - D: BnStrCompatible, PC: ProgressCallback, { - let path_raw = path.into_bytes_with_nul(); - let name_raw = name.into_bytes_with_nul(); - let description_raw = description.into_bytes_with_nul(); + let path_raw = path.to_cstr(); + let name_raw = name.to_cstr(); + let description_raw = description.to_cstr(); let folder_ptr = folder.map(|p| p.handle.as_ptr()).unwrap_or(null_mut()); unsafe { let result = BNProjectCreateFileFromPath( self.handle.as_ptr(), - path_raw.as_ref().as_ptr() as *const c_char, + path_raw.as_ptr(), folder_ptr, - name_raw.as_ref().as_ptr() as *const c_char, - description_raw.as_ref().as_ptr() as *const c_char, + name_raw.as_ptr(), + description_raw.as_ptr(), &mut progress as *mut PC as *mut c_void, Some(PC::cb_progress_callback), ); @@ -411,21 +361,15 @@ impl Project { /// * `description` - Description to assign to the created file /// * `id` - id unique ID /// * `creation_time` - Creation time of the file - pub unsafe fn create_file_from_path_unsafe( + pub unsafe fn create_file_from_path_unsafe( &self, - path: P, + path: &str, folder: Option<&ProjectFolder>, - name: N, - description: D, - id: I, + name: &str, + description: &str, + id: &str, creation_time: SystemTime, - ) -> Result, ()> - where - P: BnStrCompatible, - N: BnStrCompatible, - D: BnStrCompatible, - I: BnStrCompatible, - { + ) -> Result, ()> { self.create_file_from_path_unsafe_with_progress( path, folder, @@ -447,37 +391,33 @@ impl Project { /// * `creation_time` - Creation time of the file /// * `progress` - [`ProgressCallback`] that will be called as the [`ProjectFile`] is being created #[allow(clippy::too_many_arguments)] - pub unsafe fn create_file_from_path_unsafe_with_progress( + pub unsafe fn create_file_from_path_unsafe_with_progress( &self, - path: P, + path: &str, folder: Option<&ProjectFolder>, - name: N, - description: D, - id: I, + name: &str, + description: &str, + id: &str, creation_time: SystemTime, mut progress: PC, ) -> Result, ()> where - P: BnStrCompatible, - N: BnStrCompatible, - D: BnStrCompatible, - I: BnStrCompatible, PC: ProgressCallback, { - let path_raw = path.into_bytes_with_nul(); - let name_raw = name.into_bytes_with_nul(); - let description_raw = description.into_bytes_with_nul(); - let id_raw = id.into_bytes_with_nul(); + let path_raw = path.to_cstr(); + let name_raw = name.to_cstr(); + let description_raw = description.to_cstr(); + let id_raw = id.to_cstr(); let folder_ptr = folder.map(|p| p.handle.as_ptr()).unwrap_or(null_mut()); unsafe { let result = BNProjectCreateFileFromPathUnsafe( self.handle.as_ptr(), - path_raw.as_ref().as_ptr() as *const c_char, + path_raw.as_ptr(), folder_ptr, - name_raw.as_ref().as_ptr() as *const c_char, - description_raw.as_ref().as_ptr() as *const c_char, - id_raw.as_ref().as_ptr() as *const c_char, + name_raw.as_ptr(), + description_raw.as_ptr(), + id_raw.as_ptr(), systime_to_bntime(creation_time).unwrap(), &mut progress as *mut PC as *mut c_void, Some(PC::cb_progress_callback), @@ -492,17 +432,13 @@ impl Project { /// * `folder` - Folder to place the created file in /// * `name` - Name to assign to the created file /// * `description` - Description to assign to the created file - pub fn create_file( + pub fn create_file( &self, contents: &[u8], folder: Option<&ProjectFolder>, - name: N, - description: D, - ) -> Result, ()> - where - N: BnStrCompatible, - D: BnStrCompatible, - { + name: &str, + description: &str, + ) -> Result, ()> { self.create_file_with_progress(contents, folder, name, description, NoProgressCallback) } @@ -513,21 +449,19 @@ impl Project { /// * `name` - Name to assign to the created file /// * `description` - Description to assign to the created file /// * `progress` - [`ProgressCallback`] that will be called as the [`ProjectFile`] is being created - pub fn create_file_with_progress( + pub fn create_file_with_progress( &self, contents: &[u8], folder: Option<&ProjectFolder>, - name: N, - description: D, - mut progress: P, + name: &str, + description: &str, + mut progress: PC, ) -> Result, ()> where - N: BnStrCompatible, - D: BnStrCompatible, - P: ProgressCallback, + PC: ProgressCallback, { - let name_raw = name.into_bytes_with_nul(); - let description_raw = description.into_bytes_with_nul(); + let name_raw = name.to_cstr(); + let description_raw = description.to_cstr(); let folder_ptr = folder.map(|p| p.handle.as_ptr()).unwrap_or(null_mut()); unsafe { @@ -536,10 +470,10 @@ impl Project { contents.as_ptr(), contents.len(), folder_ptr, - name_raw.as_ref().as_ptr() as *const c_char, - description_raw.as_ref().as_ptr() as *const c_char, - &mut progress as *mut P as *mut c_void, - Some(P::cb_progress_callback), + name_raw.as_ptr(), + description_raw.as_ptr(), + &mut progress as *mut PC as *mut c_void, + Some(PC::cb_progress_callback), ); Ok(ProjectFile::ref_from_raw(NonNull::new(result).ok_or(())?)) } @@ -553,20 +487,15 @@ impl Project { /// * `description` - Description to assign to the created file /// * `id` - id unique ID /// * `creation_time` - Creation time of the file - pub unsafe fn create_file_unsafe( + pub unsafe fn create_file_unsafe( &self, contents: &[u8], folder: Option<&ProjectFolder>, - name: N, - description: D, - id: I, + name: &str, + description: &str, + id: &str, creation_time: SystemTime, - ) -> Result, ()> - where - N: BnStrCompatible, - D: BnStrCompatible, - I: BnStrCompatible, - { + ) -> Result, ()> { self.create_file_unsafe_with_progress( contents, folder, @@ -588,25 +517,22 @@ impl Project { /// * `creation_time` - Creation time of the file /// * `progress` - [`ProgressCallback`] that will be called as the [`ProjectFile`] is being created #[allow(clippy::too_many_arguments)] - pub unsafe fn create_file_unsafe_with_progress( + pub unsafe fn create_file_unsafe_with_progress( &self, contents: &[u8], folder: Option<&ProjectFolder>, - name: N, - description: D, - id: I, + name: &str, + description: &str, + id: &str, creation_time: SystemTime, - mut progress: P, + mut progress: PC, ) -> Result, ()> where - N: BnStrCompatible, - D: BnStrCompatible, - I: BnStrCompatible, - P: ProgressCallback, + PC: ProgressCallback, { - let name_raw = name.into_bytes_with_nul(); - let description_raw = description.into_bytes_with_nul(); - let id_raw = id.into_bytes_with_nul(); + let name_raw = name.to_cstr(); + let description_raw = description.to_cstr(); + let id_raw = id.to_cstr(); let folder_ptr = folder.map(|p| p.handle.as_ptr()).unwrap_or(null_mut()); unsafe { @@ -615,12 +541,12 @@ impl Project { contents.as_ptr(), contents.len(), folder_ptr, - name_raw.as_ref().as_ptr() as *const c_char, - description_raw.as_ref().as_ptr() as *const c_char, - id_raw.as_ref().as_ptr() as *const c_char, + name_raw.as_ptr(), + description_raw.as_ptr(), + id_raw.as_ptr(), systime_to_bntime(creation_time).unwrap(), - &mut progress as *mut P as *mut c_void, - Some(P::cb_progress_callback), + &mut progress as *mut PC as *mut c_void, + Some(PC::cb_progress_callback), ); Ok(ProjectFile::ref_from_raw(NonNull::new(result).ok_or(())?)) } @@ -635,21 +561,18 @@ impl Project { } /// Retrieve a file in the project by unique `id` - pub fn file_by_id(&self, id: S) -> Option> { - let id_raw = id.into_bytes_with_nul(); - let id_ptr = id_raw.as_ref().as_ptr() as *const c_char; - - let result = unsafe { BNProjectGetFileById(self.handle.as_ptr(), id_ptr) }; + pub fn file_by_id(&self, id: &str) -> Option> { + let raw_id = id.to_cstr(); + let result = unsafe { BNProjectGetFileById(self.handle.as_ptr(), raw_id.as_ptr()) }; let handle = NonNull::new(result)?; Some(unsafe { ProjectFile::ref_from_raw(handle) }) } /// Retrieve a file in the project by the `path` on disk - pub fn file_by_path(&self, path: S) -> Option> { - let path_raw = path.into_bytes_with_nul(); - let path_ptr = path_raw.as_ref().as_ptr() as *const c_char; - - let result = unsafe { BNProjectGetFileByPathOnDisk(self.handle.as_ptr(), path_ptr) }; + pub fn file_by_path(&self, path: &str) -> Option> { + let path_raw = path.to_cstr(); + let result = + unsafe { BNProjectGetFileByPathOnDisk(self.handle.as_ptr(), path_raw.as_ptr()) }; let handle = NonNull::new(result)?; Some(unsafe { ProjectFile::ref_from_raw(handle) }) } diff --git a/rust/src/project/file.rs b/rust/src/project/file.rs index 5071dd50b8..3b3e48f7af 100644 --- a/rust/src/project/file.rs +++ b/rust/src/project/file.rs @@ -1,6 +1,6 @@ use crate::project::{systime_from_bntime, Project, ProjectFolder}; use crate::rc::{CoreArrayProvider, CoreArrayProviderInner, Guard, Ref, RefCountable}; -use crate::string::{BnStrCompatible, BnString}; +use crate::string::{BnString, IntoCStr}; use binaryninjacore_sys::{ BNFreeProjectFile, BNFreeProjectFileList, BNNewProjectFileReference, BNProjectFile, BNProjectFileExistsOnDisk, BNProjectFileExport, BNProjectFileGetCreationTimestamp, @@ -8,8 +8,8 @@ use binaryninjacore_sys::{ BNProjectFileGetPathOnDisk, BNProjectFileGetProject, BNProjectFileSetDescription, BNProjectFileSetFolder, BNProjectFileSetName, }; -use std::ffi::c_char; use std::fmt::Debug; +use std::path::Path; use std::ptr::{null_mut, NonNull}; use std::time::SystemTime; @@ -37,8 +37,8 @@ impl ProjectFile { } /// Get the path on disk to this file's contents - pub fn path_on_disk(&self) -> BnString { - unsafe { BnString::from_raw(BNProjectFileGetPathOnDisk(self.handle.as_ptr())) } + pub fn path_on_disk(&self) -> String { + unsafe { BnString::into_string(BNProjectFileGetPathOnDisk(self.handle.as_ptr())) } } /// Check if this file's contents exist on disk @@ -47,40 +47,30 @@ impl ProjectFile { } /// Get the unique id of this file - pub fn id(&self) -> BnString { - unsafe { BnString::from_raw(BNProjectFileGetId(self.handle.as_ptr())) } + pub fn id(&self) -> String { + unsafe { BnString::into_string(BNProjectFileGetId(self.handle.as_ptr())) } } /// Get the name of this file - pub fn name(&self) -> BnString { - unsafe { BnString::from_raw(BNProjectFileGetName(self.handle.as_ptr())) } + pub fn name(&self) -> String { + unsafe { BnString::into_string(BNProjectFileGetName(self.handle.as_ptr())) } } /// Set the name of this file - pub fn set_name(&self, value: S) -> bool { - let value_raw = value.into_bytes_with_nul(); - unsafe { - BNProjectFileSetName( - self.handle.as_ptr(), - value_raw.as_ref().as_ptr() as *const c_char, - ) - } + pub fn set_name(&self, value: &str) -> bool { + let value_raw = value.to_cstr(); + unsafe { BNProjectFileSetName(self.handle.as_ptr(), value_raw.as_ptr()) } } /// Get the description of this file - pub fn description(&self) -> BnString { - unsafe { BnString::from_raw(BNProjectFileGetDescription(self.handle.as_ptr())) } + pub fn description(&self) -> String { + unsafe { BnString::into_string(BNProjectFileGetDescription(self.handle.as_ptr())) } } /// Set the description of this file - pub fn set_description(&self, value: S) -> bool { - let value_raw = value.into_bytes_with_nul(); - unsafe { - BNProjectFileSetDescription( - self.handle.as_ptr(), - value_raw.as_ref().as_ptr() as *const c_char, - ) - } + pub fn set_description(&self, value: &str) -> bool { + let value_raw = value.to_cstr(); + unsafe { BNProjectFileSetDescription(self.handle.as_ptr(), value_raw.as_ptr()) } } /// Get the file creation time @@ -103,15 +93,10 @@ impl ProjectFile { /// Export this file to disk, `true' if the export succeeded /// - /// * `dest` - Destination path for the exported contents - pub fn export(&self, dest: S) -> bool { - let dest_raw = dest.into_bytes_with_nul(); - unsafe { - BNProjectFileExport( - self.handle.as_ptr(), - dest_raw.as_ref().as_ptr() as *const c_char, - ) - } + /// * `dest` - Destination file path for the exported contents, passing a directory will append the file name. + pub fn export(&self, dest: &Path) -> bool { + let dest_raw = dest.to_cstr(); + unsafe { BNProjectFileExport(self.handle.as_ptr(), dest_raw.as_ptr()) } } } diff --git a/rust/src/project/folder.rs b/rust/src/project/folder.rs index 90b40051b6..d5d95e7641 100644 --- a/rust/src/project/folder.rs +++ b/rust/src/project/folder.rs @@ -1,15 +1,16 @@ use crate::progress::{NoProgressCallback, ProgressCallback}; use crate::project::Project; use crate::rc::{CoreArrayProvider, CoreArrayProviderInner, Guard, Ref, RefCountable}; -use crate::string::{BnStrCompatible, BnString}; +use crate::string::{BnString, IntoCStr}; use binaryninjacore_sys::{ BNFreeProjectFolder, BNFreeProjectFolderList, BNNewProjectFolderReference, BNProjectFolder, BNProjectFolderExport, BNProjectFolderGetDescription, BNProjectFolderGetId, BNProjectFolderGetName, BNProjectFolderGetParent, BNProjectFolderGetProject, BNProjectFolderSetDescription, BNProjectFolderSetName, BNProjectFolderSetParent, }; -use std::ffi::{c_char, c_void}; +use std::ffi::c_void; use std::fmt::Debug; +use std::path::Path; use std::ptr::{null_mut, NonNull}; #[repr(transparent)] @@ -36,40 +37,30 @@ impl ProjectFolder { } /// Get the unique id of this folder - pub fn id(&self) -> BnString { - unsafe { BnString::from_raw(BNProjectFolderGetId(self.handle.as_ptr())) } + pub fn id(&self) -> String { + unsafe { BnString::into_string(BNProjectFolderGetId(self.handle.as_ptr())) } } /// Get the name of this folder - pub fn name(&self) -> BnString { - unsafe { BnString::from_raw(BNProjectFolderGetName(self.handle.as_ptr())) } + pub fn name(&self) -> String { + unsafe { BnString::into_string(BNProjectFolderGetName(self.handle.as_ptr())) } } /// Set the name of this folder - pub fn set_name(&self, value: S) -> bool { - let value_raw = value.into_bytes_with_nul(); - unsafe { - BNProjectFolderSetName( - self.handle.as_ptr(), - value_raw.as_ref().as_ptr() as *const c_char, - ) - } + pub fn set_name(&self, value: &str) -> bool { + let value_raw = value.to_cstr(); + unsafe { BNProjectFolderSetName(self.handle.as_ptr(), value_raw.as_ptr()) } } /// Get the description of this folder - pub fn description(&self) -> BnString { - unsafe { BnString::from_raw(BNProjectFolderGetDescription(self.handle.as_ptr())) } + pub fn description(&self) -> String { + unsafe { BnString::into_string(BNProjectFolderGetDescription(self.handle.as_ptr())) } } /// Set the description of this folder - pub fn set_description(&self, value: S) -> bool { - let value_raw = value.into_bytes_with_nul(); - unsafe { - BNProjectFolderSetDescription( - self.handle.as_ptr(), - value_raw.as_ref().as_ptr() as *const c_char, - ) - } + pub fn set_description(&self, value: &str) -> bool { + let value_raw = value.to_cstr(); + unsafe { BNProjectFolderSetDescription(self.handle.as_ptr(), value_raw.as_ptr()) } } /// Get the folder that contains this folder @@ -84,36 +75,30 @@ impl ProjectFolder { unsafe { BNProjectFolderSetParent(self.handle.as_ptr(), folder_handle) } } - // TODO: Take Path? /// Recursively export this folder to disk, returns `true' if the export succeeded /// /// * `dest` - Destination path for the exported contents - pub fn export(&self, dest: S) -> bool { + pub fn export(&self, dest: &Path) -> bool { self.export_with_progress(dest, NoProgressCallback) } - // TODO: Take Path? /// Recursively export this folder to disk, returns `true' if the export succeeded /// /// * `dest` - Destination path for the exported contents /// * `progress` - [`ProgressCallback`] that will be called as contents are exporting - pub fn export_with_progress(&self, dest: S, mut progress: P) -> bool + pub fn export_with_progress

(&self, dest: &Path, mut progress: P) -> bool where - S: BnStrCompatible, P: ProgressCallback, { - let dest_raw = dest.into_bytes_with_nul(); - - let success = unsafe { + let dest_raw = dest.to_cstr(); + unsafe { BNProjectFolderExport( self.handle.as_ptr(), - dest_raw.as_ref().as_ptr() as *const c_char, + dest_raw.as_ptr(), &mut progress as *mut P as *mut c_void, Some(P::cb_progress_callback), ) - }; - - success + } } } diff --git a/rust/src/relocation.rs b/rust/src/relocation.rs index 8fa9a86b08..a4f9667ece 100644 --- a/rust/src/relocation.rs +++ b/rust/src/relocation.rs @@ -1,6 +1,6 @@ -use crate::low_level_il::RegularLowLevelILFunction; +use crate::low_level_il::LowLevelILRegularFunction; use crate::rc::Guard; -use crate::string::BnStrCompatible; +use crate::string::IntoCStr; use crate::{ architecture::CoreArchitecture, binary_view::BinaryView, @@ -83,7 +83,7 @@ impl From for usize { } // TODO: How to handle related relocation linked lists? -#[derive(Clone)] +#[derive(Clone, Debug)] pub struct RelocationInfo { pub type_: RelocationType, pub pc_relative: bool, @@ -265,7 +265,7 @@ pub trait RelocationHandler: 'static + Sized + AsRef { _data: &[u8], _addr: u64, // TODO: Are we sure this is not a liftedilfunction? - _il: &RegularLowLevelILFunction, + _il: &LowLevelILRegularFunction, _reloc: &Relocation, ) -> RelocationOperand { RelocationOperand::AutocoerceExternPtr @@ -363,7 +363,7 @@ impl RelocationHandler for CoreRelocationHandler { &self, data: &[u8], addr: u64, - il: &RegularLowLevelILFunction, + il: &LowLevelILRegularFunction, reloc: &Relocation, ) -> RelocationOperand { unsafe { @@ -402,9 +402,8 @@ unsafe impl RefCountable for CoreRelocationHandler { } } -pub(crate) fn register_relocation_handler(arch: &CoreArchitecture, name: S, func: F) +pub(crate) fn register_relocation_handler(arch: &CoreArchitecture, name: &str, func: F) where - S: BnStrCompatible, R: 'static + RelocationHandler> + Send + Sync + Sized, F: FnOnce(CustomRelocationHandlerHandle, CoreRelocationHandler) -> R, { @@ -496,14 +495,14 @@ where return RelocationOperand::Invalid.into(); } let arch = unsafe { CoreArchitecture::from_raw(arch) }; - let il = unsafe { RegularLowLevelILFunction::from_raw(arch, il) }; + let il = unsafe { LowLevelILRegularFunction::from_raw_with_arch(il, Some(arch)) }; custom_handler .get_operand_for_external_relocation(data, addr, &il, &reloc) .into() } - let name = name.into_bytes_with_nul(); + let name = name.to_cstr(); let raw = Box::leak(Box::new( MaybeUninit::>::zeroed(), diff --git a/rust/src/relocs.rs b/rust/src/relocs.rs deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/rust/src/render_layer.rs b/rust/src/render_layer.rs index 80ecc63949..181294d4ac 100644 --- a/rust/src/render_layer.rs +++ b/rust/src/render_layer.rs @@ -6,9 +6,9 @@ use crate::flowgraph::FlowGraph; use crate::function::{Function, NativeBlock}; use crate::linear_view::{LinearDisassemblyLine, LinearDisassemblyLineType, LinearViewObject}; use crate::rc::{Array, CoreArrayProvider, CoreArrayProviderInner}; -use crate::string::BnStrCompatible; +use crate::string::IntoCStr; use binaryninjacore_sys::*; -use std::ffi::{c_char, c_void}; +use std::ffi::c_void; use std::ptr::NonNull; /// The state in which the [`RenderLayer`] will be registered with. @@ -61,8 +61,8 @@ impl Default for RenderLayerDefaultState { } /// Register a [`RenderLayer`] with the API. -pub fn register_render_layer( - name: S, +pub fn register_render_layer( + name: &str, render_layer: T, default_state: RenderLayerDefaultState, ) -> (&'static mut T, CoreRenderLayer) { @@ -73,13 +73,9 @@ pub fn register_render_layer( applyToLinearViewObject: Some(cb_apply_to_linear_view_object::), freeLines: Some(cb_free_lines), }; - let result = unsafe { - BNRegisterRenderLayer( - name.into_bytes_with_nul().as_ref().as_ptr() as *const _, - &mut callback, - default_state.into(), - ) - }; + let name = name.to_cstr(); + let result = + unsafe { BNRegisterRenderLayer(name.as_ptr(), &mut callback, default_state.into()) }; let core = CoreRenderLayer::from_raw(NonNull::new(result).unwrap()); (render_layer, core) } @@ -297,15 +293,15 @@ impl CoreRenderLayer { Self { handle } } - pub fn render_layers() -> Array { + pub fn all() -> Array { let mut count = 0; let result = unsafe { BNGetRenderLayerList(&mut count) }; unsafe { Array::new(result, count, ()) } } - pub fn render_layer_by_name(name: S) -> Option { - let name_raw = name.into_bytes_with_nul(); - let result = unsafe { BNGetRenderLayerByName(name_raw.as_ref().as_ptr() as *const c_char) }; + pub fn from_name(name: &str) -> Option { + let name_raw = name.to_cstr(); + let result = unsafe { BNGetRenderLayerByName(name_raw.as_ptr()) }; NonNull::new(result).map(Self::from_raw) } diff --git a/rust/src/repository.rs b/rust/src/repository.rs index 135b236783..a29be323d7 100644 --- a/rust/src/repository.rs +++ b/rust/src/repository.rs @@ -3,13 +3,14 @@ mod plugin; use std::ffi::c_char; use std::fmt::Debug; +use std::path::{Path, PathBuf}; use std::ptr::NonNull; use binaryninjacore_sys::*; use crate::rc::{Array, CoreArrayProvider, CoreArrayProviderInner, Guard, Ref, RefCountable}; use crate::repository::plugin::RepositoryPlugin; -use crate::string::{BnStrCompatible, BnString}; +use crate::string::{BnString, IntoCStr}; pub use manager::RepositoryManager; @@ -31,17 +32,18 @@ impl Repository { } /// String URL of the git repository where the plugin repository's are stored - pub fn url(&self) -> BnString { + pub fn url(&self) -> String { let result = unsafe { BNRepositoryGetUrl(self.handle.as_ptr()) }; assert!(!result.is_null()); - unsafe { BnString::from_raw(result as *mut c_char) } + unsafe { BnString::into_string(result as *mut c_char) } } /// String local path to store the given plugin repository - pub fn path(&self) -> BnString { + pub fn path(&self) -> PathBuf { let result = unsafe { BNRepositoryGetRepoPath(self.handle.as_ptr()) }; assert!(!result.is_null()); - unsafe { BnString::from_raw(result as *mut c_char) } + let result_str = unsafe { BnString::into_string(result as *mut c_char) }; + PathBuf::from(result_str) } /// List of RepoPlugin objects contained within this repository @@ -52,23 +54,18 @@ impl Repository { unsafe { Array::new(result, count, ()) } } - pub fn plugin_by_path(&self, path: S) -> Option> { - let path = path.into_bytes_with_nul(); - let result = unsafe { - BNRepositoryGetPluginByPath( - self.handle.as_ptr(), - path.as_ref().as_ptr() as *const c_char, - ) - }; + pub fn plugin_by_path(&self, path: &Path) -> Option> { + let path = path.to_cstr(); + let result = unsafe { BNRepositoryGetPluginByPath(self.handle.as_ptr(), path.as_ptr()) }; NonNull::new(result).map(|h| unsafe { RepositoryPlugin::ref_from_raw(h) }) } - // TODO: Make this a PathBuf? /// String full path the repository - pub fn full_path(&self) -> BnString { + pub fn full_path(&self) -> PathBuf { let result = unsafe { BNRepositoryGetPluginsPath(self.handle.as_ptr()) }; assert!(!result.is_null()); - unsafe { BnString::from_raw(result as *mut c_char) } + let result_str = unsafe { BnString::into_string(result as *mut c_char) }; + PathBuf::from(result_str) } } diff --git a/rust/src/repository/manager.rs b/rust/src/repository/manager.rs index 598891625d..cf0118adce 100644 --- a/rust/src/repository/manager.rs +++ b/rust/src/repository/manager.rs @@ -1,14 +1,14 @@ use crate::rc::{Array, Ref, RefCountable}; use crate::repository::Repository; -use crate::string::BnStrCompatible; +use crate::string::IntoCStr; use binaryninjacore_sys::{ BNCreateRepositoryManager, BNFreeRepositoryManager, BNGetRepositoryManager, BNNewRepositoryManagerReference, BNRepositoryGetRepositoryByPath, BNRepositoryManager, BNRepositoryManagerAddRepository, BNRepositoryManagerCheckForUpdates, BNRepositoryManagerGetDefaultRepository, BNRepositoryManagerGetRepositories, }; -use std::ffi::c_char; use std::fmt::Debug; +use std::path::Path; use std::ptr::NonNull; /// Keeps track of all the repositories and keeps the `enabled_plugins.json` @@ -29,10 +29,9 @@ impl RepositoryManager { Ref::new(Self { handle }) } - pub fn new(plugins_path: S) -> Ref { - let plugins_path = plugins_path.into_bytes_with_nul(); - let result = - unsafe { BNCreateRepositoryManager(plugins_path.as_ref().as_ptr() as *const c_char) }; + pub fn new(plugins_path: &str) -> Ref { + let plugins_path = plugins_path.to_cstr(); + let result = unsafe { BNCreateRepositoryManager(plugins_path.as_ptr()) }; unsafe { Self::ref_from_raw(NonNull::new(result).unwrap()) } } @@ -61,30 +60,18 @@ impl RepositoryManager { /// * `repository_path` - path to where the repository will be stored on disk locally /// /// Returns true if the repository was successfully added, false otherwise. - pub fn add_repository( - &self, - url: U, - repository_path: P, - ) -> bool { - let url = url.into_bytes_with_nul(); - let repo_path = repository_path.into_bytes_with_nul(); + pub fn add_repository(&self, url: &str, repository_path: &Path) -> bool { + let url = url.to_cstr(); + let repo_path = repository_path.to_cstr(); unsafe { - BNRepositoryManagerAddRepository( - self.handle.as_ptr(), - url.as_ref().as_ptr() as *const c_char, - repo_path.as_ref().as_ptr() as *const c_char, - ) + BNRepositoryManagerAddRepository(self.handle.as_ptr(), url.as_ptr(), repo_path.as_ptr()) } } - pub fn repository_by_path(&self, path: P) -> Option { - let path = path.into_bytes_with_nul(); - let result = unsafe { - BNRepositoryGetRepositoryByPath( - self.handle.as_ptr(), - path.as_ref().as_ptr() as *const c_char, - ) - }; + pub fn repository_by_path(&self, path: &Path) -> Option { + let path = path.to_cstr(); + let result = + unsafe { BNRepositoryGetRepositoryByPath(self.handle.as_ptr(), path.as_ptr()) }; NonNull::new(result).map(|raw| unsafe { Repository::from_raw(raw) }) } diff --git a/rust/src/repository/plugin.rs b/rust/src/repository/plugin.rs index af3886f2e9..e0ab96792d 100644 --- a/rust/src/repository/plugin.rs +++ b/rust/src/repository/plugin.rs @@ -5,6 +5,7 @@ use crate::VersionInfo; use binaryninjacore_sys::*; use std::ffi::c_char; use std::fmt::Debug; +use std::path::PathBuf; use std::ptr::NonNull; use std::time::{Duration, SystemTime, UNIX_EPOCH}; @@ -31,31 +32,31 @@ impl RepositoryPlugin { } /// String of the plugin author - pub fn author(&self) -> BnString { + pub fn author(&self) -> String { let result = unsafe { BNPluginGetAuthor(self.handle.as_ptr()) }; assert!(!result.is_null()); - unsafe { BnString::from_raw(result as *mut c_char) } + unsafe { BnString::into_string(result as *mut c_char) } } /// String short description of the plugin - pub fn description(&self) -> BnString { + pub fn description(&self) -> String { let result = unsafe { BNPluginGetDescription(self.handle.as_ptr()) }; assert!(!result.is_null()); - unsafe { BnString::from_raw(result as *mut c_char) } + unsafe { BnString::into_string(result as *mut c_char) } } /// String complete license text for the given plugin - pub fn license_text(&self) -> BnString { + pub fn license_text(&self) -> String { let result = unsafe { BNPluginGetLicenseText(self.handle.as_ptr()) }; assert!(!result.is_null()); - unsafe { BnString::from_raw(result as *mut c_char) } + unsafe { BnString::into_string(result as *mut c_char) } } /// String long description of the plugin - pub fn long_description(&self) -> BnString { + pub fn long_description(&self) -> String { let result = unsafe { BNPluginGetLongdescription(self.handle.as_ptr()) }; assert!(!result.is_null()); - unsafe { BnString::from_raw(result as *mut c_char) } + unsafe { BnString::into_string(result as *mut c_char) } } /// Minimum version info the plugin was tested on @@ -71,65 +72,67 @@ impl RepositoryPlugin { } /// String plugin name - pub fn name(&self) -> BnString { + pub fn name(&self) -> String { let result = unsafe { BNPluginGetName(self.handle.as_ptr()) }; assert!(!result.is_null()); - unsafe { BnString::from_raw(result as *mut c_char) } + unsafe { BnString::into_string(result as *mut c_char) } } /// String URL of the plugin's git repository - pub fn project_url(&self) -> BnString { + pub fn project_url(&self) -> String { let result = unsafe { BNPluginGetProjectUrl(self.handle.as_ptr()) }; assert!(!result.is_null()); - unsafe { BnString::from_raw(result as *mut c_char) } + unsafe { BnString::into_string(result as *mut c_char) } } /// String URL of the plugin's git repository - pub fn package_url(&self) -> BnString { + pub fn package_url(&self) -> String { let result = unsafe { BNPluginGetPackageUrl(self.handle.as_ptr()) }; assert!(!result.is_null()); - unsafe { BnString::from_raw(result as *mut c_char) } + unsafe { BnString::into_string(result as *mut c_char) } } /// String URL of the plugin author's url - pub fn author_url(&self) -> BnString { + pub fn author_url(&self) -> String { let result = unsafe { BNPluginGetAuthorUrl(self.handle.as_ptr()) }; assert!(!result.is_null()); - unsafe { BnString::from_raw(result as *mut c_char) } + unsafe { BnString::into_string(result as *mut c_char) } } /// String version of the plugin - pub fn version(&self) -> BnString { + pub fn version(&self) -> String { let result = unsafe { BNPluginGetVersion(self.handle.as_ptr()) }; assert!(!result.is_null()); - unsafe { BnString::from_raw(result as *mut c_char) } + unsafe { BnString::into_string(result as *mut c_char) } } /// String of the commit of this plugin git repository - pub fn commit(&self) -> BnString { + pub fn commit(&self) -> String { let result = unsafe { BNPluginGetCommit(self.handle.as_ptr()) }; assert!(!result.is_null()); - unsafe { BnString::from_raw(result as *mut c_char) } + unsafe { BnString::into_string(result as *mut c_char) } } /// Relative path from the base of the repository to the actual plugin - pub fn path(&self) -> BnString { + pub fn path(&self) -> PathBuf { let result = unsafe { BNPluginGetPath(self.handle.as_ptr()) }; assert!(!result.is_null()); - unsafe { BnString::from_raw(result as *mut c_char) } + let result_str = unsafe { BnString::into_string(result as *mut c_char) }; + PathBuf::from(result_str) } /// Optional sub-directory the plugin code lives in as a relative path from the plugin root - pub fn subdir(&self) -> BnString { + pub fn subdir(&self) -> PathBuf { let result = unsafe { BNPluginGetSubdir(self.handle.as_ptr()) }; assert!(!result.is_null()); - unsafe { BnString::from_raw(result as *mut c_char) } + let result_str = unsafe { BnString::into_string(result as *mut c_char) }; + PathBuf::from(result_str) } /// Dependencies required for installing this plugin - pub fn dependencies(&self) -> BnString { + pub fn dependencies(&self) -> String { let result = unsafe { BNPluginGetDependencies(self.handle.as_ptr()) }; assert!(!result.is_null()); - unsafe { BnString::from_raw(result as *mut c_char) } + unsafe { BnString::into_string(result as *mut c_char) } } /// true if the plugin is installed, false otherwise @@ -190,10 +193,10 @@ impl RepositoryPlugin { unsafe { Array::new(result, count, ()) } } - pub fn repository(&self) -> BnString { + pub fn repository(&self) -> String { let result = unsafe { BNPluginGetRepository(self.handle.as_ptr()) }; assert!(!result.is_null()); - unsafe { BnString::from_raw(result as *mut c_char) } + unsafe { BnString::into_string(result as *mut c_char) } } /// Boolean status indicating that the plugin is being deleted @@ -237,10 +240,10 @@ impl RepositoryPlugin { } /// Gets a json object of the project data field - pub fn project_data(&self) -> BnString { + pub fn project_data(&self) -> String { let result = unsafe { BNPluginGetProjectData(self.handle.as_ptr()) }; assert!(!result.is_null()); - unsafe { BnString::from_raw(result) } + unsafe { BnString::into_string(result) } } /// Returns a datetime object representing the plugins last update diff --git a/rust/src/secrets_provider.rs b/rust/src/secrets_provider.rs index e61cd46652..a310b08e65 100644 --- a/rust/src/secrets_provider.rs +++ b/rust/src/secrets_provider.rs @@ -4,7 +4,7 @@ use std::fmt::Debug; use std::ptr::NonNull; use crate::rc::{Array, CoreArrayProvider, CoreArrayProviderInner}; -use crate::string::{BnStrCompatible, BnString}; +use crate::string::{BnString, IntoCStr}; pub trait SecretsProvider { fn has_data(&mut self, key: &str) -> bool; @@ -27,7 +27,7 @@ impl CoreSecretsProvider { /// Register a new provider pub fn new(name: &str, callback: C) -> Self { // SAFETY: once create SecretsProvider is never dropped - let name = name.into_bytes_with_nul(); + let name = name.to_cstr(); let callback = Box::leak(Box::new(callback)); let mut callbacks = BNSecretsProviderCallbacks { context: callback as *mut C as *mut c_void, @@ -36,8 +36,7 @@ impl CoreSecretsProvider { storeData: Some(cb_store_data::), deleteData: Some(cb_delete_data::), }; - let result = - unsafe { BNRegisterSecretsProvider(name.as_ptr() as *const c_char, &mut callbacks) }; + let result = unsafe { BNRegisterSecretsProvider(name.as_ptr(), &mut callbacks) }; unsafe { Self::from_raw(NonNull::new(result).unwrap()) } } @@ -50,57 +49,42 @@ impl CoreSecretsProvider { } /// Retrieve a provider by name - pub fn by_name(name: S) -> Option { - let name = name.into_bytes_with_nul(); - let result = unsafe { BNGetSecretsProviderByName(name.as_ref().as_ptr() as *const c_char) }; + pub fn by_name(name: &str) -> Option { + let name = name.to_cstr(); + let result = unsafe { BNGetSecretsProviderByName(name.as_ptr()) }; NonNull::new(result).map(|h| unsafe { Self::from_raw(h) }) } - pub fn name(&self) -> BnString { + pub fn name(&self) -> String { let result = unsafe { BNGetSecretsProviderName(self.handle.as_ptr()) }; assert!(!result.is_null()); - unsafe { BnString::from_raw(result) } + unsafe { BnString::into_string(result) } } /// Check if data for a specific key exists, but do not retrieve it - pub fn has_data(&self, key: S) -> bool { - let key = key.into_bytes_with_nul(); - unsafe { - BNSecretsProviderHasData(self.handle.as_ptr(), key.as_ref().as_ptr() as *const c_char) - } + pub fn has_data(&self, key: &str) -> bool { + let key = key.to_cstr(); + unsafe { BNSecretsProviderHasData(self.handle.as_ptr(), key.as_ptr()) } } /// Retrieve data for the given key, if it exists - pub fn get_data(&self, key: S) -> BnString { - let key = key.into_bytes_with_nul(); - let result = unsafe { - BNGetSecretsProviderData(self.handle.as_ptr(), key.as_ref().as_ptr() as *const c_char) - }; - unsafe { BnString::from_raw(result) } + pub fn get_data(&self, key: &str) -> String { + let key = key.to_cstr(); + let result = unsafe { BNGetSecretsProviderData(self.handle.as_ptr(), key.as_ptr()) }; + unsafe { BnString::into_string(result) } } /// Store data with the given key - pub fn store_data(&self, key: K, value: V) -> bool { - let key = key.into_bytes_with_nul(); - let value = value.into_bytes_with_nul(); - unsafe { - BNStoreSecretsProviderData( - self.handle.as_ptr(), - key.as_ref().as_ptr() as *const c_char, - value.as_ref().as_ptr() as *const c_char, - ) - } + pub fn store_data(&self, key: &str, value: &str) -> bool { + let key = key.to_cstr(); + let value = value.to_cstr(); + unsafe { BNStoreSecretsProviderData(self.handle.as_ptr(), key.as_ptr(), value.as_ptr()) } } /// Delete stored data with the given key - pub fn delete_data(&self, key: S) -> bool { - let key = key.into_bytes_with_nul(); - unsafe { - BNDeleteSecretsProviderData( - self.handle.as_ptr(), - key.as_ref().as_ptr() as *const c_char, - ) - } + pub fn delete_data(&self, key: &str) -> bool { + let key = key.to_cstr(); + unsafe { BNDeleteSecretsProviderData(self.handle.as_ptr(), key.as_ptr()) } } } diff --git a/rust/src/section.rs b/rust/src/section.rs index c88ef9b0e6..75b3e836c6 100644 --- a/rust/src/section.rs +++ b/rust/src/section.rs @@ -14,8 +14,8 @@ //! Sections are [crate::segment::Segment]s that are loaded into memory at run time -use std::ffi::c_char; use std::fmt; +use std::hash::{Hash, Hasher}; use std::ops::Range; use binaryninjacore_sys::*; @@ -62,7 +62,6 @@ impl From for BNSectionSemantics { } } -#[derive(PartialEq, Eq, Hash)] pub struct Section { handle: *mut BNSection, } @@ -94,8 +93,8 @@ impl Section { unsafe { BnString::from_raw(BNSectionGetName(self.handle)) } } - pub fn section_type(&self) -> BnString { - unsafe { BnString::from_raw(BNSectionGetType(self.handle)) } + pub fn section_type(&self) -> String { + unsafe { BnString::into_string(BNSectionGetType(self.handle)) } } pub fn start(&self) -> u64 { @@ -162,6 +161,30 @@ impl fmt::Debug for Section { } } +impl PartialEq for Section { + fn eq(&self, other: &Self) -> bool { + // TODO: Do we want to make this complete match like this? + self.name() == other.name() + && self.address_range() == other.address_range() + && self.semantics() == other.semantics() + && self.linked_section() == other.linked_section() + && self.info_section() == other.info_section() + && self.info_data() == other.info_data() + && self.align() == other.align() + && self.entry_size() == other.entry_size() + && self.auto_defined() == other.auto_defined() + } +} + +impl Eq for Section {} + +impl Hash for Section { + fn hash(&self, state: &mut H) { + self.name().hash(state); + self.address_range().hash(state); + } +} + impl ToOwned for Section { type Owned = Ref; @@ -270,10 +293,10 @@ impl SectionBuilder { } pub(crate) fn create(self, view: &BinaryView) { - let name = self.name.into_bytes_with_nul(); - let ty = self.ty.into_bytes_with_nul(); - let linked_section = self.linked_section.into_bytes_with_nul(); - let info_section = self.info_section.into_bytes_with_nul(); + let name = self.name.to_cstr(); + let ty = self.ty.to_cstr(); + let linked_section = self.linked_section.to_cstr(); + let info_section = self.info_section.to_cstr(); let start = self.range.start; let len = self.range.end.wrapping_sub(start); @@ -282,32 +305,55 @@ impl SectionBuilder { if self.is_auto { BNAddAutoSection( view.handle, - name.as_ptr() as *const c_char, + name.as_ptr(), start, len, self.semantics.into(), - ty.as_ptr() as *const c_char, + ty.as_ptr(), self.align, self.entry_size, - linked_section.as_ptr() as *const c_char, - info_section.as_ptr() as *const c_char, + linked_section.as_ptr(), + info_section.as_ptr(), self.info_data, ); } else { BNAddUserSection( view.handle, - name.as_ptr() as *const c_char, + name.as_ptr(), start, len, self.semantics.into(), - ty.as_ptr() as *const c_char, + ty.as_ptr(), self.align, self.entry_size, - linked_section.as_ptr() as *const c_char, - info_section.as_ptr() as *const c_char, + linked_section.as_ptr(), + info_section.as_ptr(), self.info_data, ); } } } } + +impl> From for SectionBuilder { + fn from(value: T) -> Self { + let value = value.as_ref(); + let name = value.name().to_string_lossy().to_string(); + let ty = value.section_type().to_string(); + let linked_section = value.linked_section().to_string_lossy().to_string(); + let info_section = value.info_section().to_string_lossy().to_string(); + + Self { + is_auto: value.auto_defined(), + name, + range: value.address_range(), + semantics: value.semantics(), + ty, + align: value.align(), + entry_size: value.entry_size() as u64, + linked_section, + info_section, + info_data: value.info_data(), + } + } +} diff --git a/rust/src/settings.rs b/rust/src/settings.rs index 8c64f314e8..3882ce4d20 100644 --- a/rust/src/settings.rs +++ b/rust/src/settings.rs @@ -20,7 +20,7 @@ use std::fmt::Debug; use crate::binary_view::BinaryView; use crate::rc::*; -use crate::string::{BnStrCompatible, BnString}; +use crate::string::{BnString, IntoCStr}; use crate::function::Function; @@ -35,7 +35,7 @@ pub struct Settings { } impl Settings { - pub(crate) unsafe fn from_raw(handle: *mut BNSettings) -> Ref { + pub(crate) unsafe fn ref_from_raw(handle: *mut BNSettings) -> Ref { debug_assert!(!handle.is_null()); Ref::new(Self { handle }) } @@ -44,48 +44,33 @@ impl Settings { Self::new_with_id(GLOBAL_INSTANCE_ID) } - pub fn new_with_id(instance_id: S) -> Ref { - let instance_id = instance_id.into_bytes_with_nul(); - unsafe { - let handle = BNCreateSettings(instance_id.as_ref().as_ptr() as *mut _); - debug_assert!(!handle.is_null()); - Ref::new(Self { handle }) - } + pub fn new_with_id(instance_id: &str) -> Ref { + let instance_id = instance_id.to_cstr(); + unsafe { Self::ref_from_raw(BNCreateSettings(instance_id.as_ptr())) } } - 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 _) }; + pub fn set_resource_id(&self, resource_id: &str) { + let resource_id = resource_id.to_cstr(); + unsafe { BNSettingsSetResourceId(self.handle, resource_id.as_ptr()) }; } - pub fn serialize_schema(&self) -> BnString { - unsafe { BnString::from_raw(BNSettingsSerializeSchema(self.handle)) } + pub fn serialize_schema(&self) -> String { + unsafe { BnString::into_string(BNSettingsSerializeSchema(self.handle)) } } - pub fn deserialize_schema(&self, schema: S) -> bool { + pub fn deserialize_schema(&self, schema: &str) -> bool { self.deserialize_schema_with_scope(schema, SettingsScope::SettingsAutoScope) } - pub fn deserialize_schema_with_scope( - &self, - schema: S, - scope: SettingsScope, - ) -> bool { - let schema = schema.into_bytes_with_nul(); - unsafe { - BNSettingsDeserializeSchema( - self.handle, - schema.as_ref().as_ptr() as *mut _, - scope, - true, - ) - } + pub fn deserialize_schema_with_scope(&self, schema: &str, scope: SettingsScope) -> bool { + let schema = schema.to_cstr(); + unsafe { BNSettingsDeserializeSchema(self.handle, schema.as_ptr(), scope, true) } } - pub fn contains(&self, key: S) -> bool { - let key = key.into_bytes_with_nul(); + pub fn contains(&self, key: &str) -> bool { + let key = key.to_cstr(); - unsafe { BNSettingsContains(self.handle, key.as_ref().as_ptr() as *mut _) } + unsafe { BNSettingsContains(self.handle, key.as_ptr()) } } pub fn keys(&self) -> Array { @@ -95,18 +80,12 @@ impl Settings { unsafe { Array::new(result as *mut *mut c_char, count, ()) } } - // TODO Update the settings API to take an optional BinaryView or Function. Separate functions or...? - - pub fn get_bool(&self, key: S) -> bool { + pub fn get_bool(&self, key: &str) -> bool { self.get_bool_with_opts(key, &mut QueryOptions::default()) } - pub fn get_bool_with_opts( - &self, - key: S, - options: &mut QueryOptions, - ) -> bool { - let key = key.into_bytes_with_nul(); + pub fn get_bool_with_opts(&self, key: &str, options: &mut QueryOptions) -> bool { + let key = key.to_cstr(); let view_ptr = match options.view.as_ref() { Some(view) => view.handle, _ => std::ptr::null_mut(), @@ -118,7 +97,7 @@ impl Settings { unsafe { BNSettingsGetBool( self.handle, - key.as_ref().as_ptr() as *mut _, + key.as_ptr(), view_ptr, func_ptr, &mut options.scope, @@ -126,16 +105,12 @@ impl Settings { } } - pub fn get_double(&self, key: S) -> f64 { + pub fn get_double(&self, key: &str) -> f64 { self.get_double_with_opts(key, &mut QueryOptions::default()) } - pub fn get_double_with_opts( - &self, - key: S, - options: &mut QueryOptions, - ) -> f64 { - let key = key.into_bytes_with_nul(); + pub fn get_double_with_opts(&self, key: &str, options: &mut QueryOptions) -> f64 { + let key = key.to_cstr(); let view_ptr = match options.view.as_ref() { Some(view) => view.handle, _ => std::ptr::null_mut(), @@ -147,7 +122,7 @@ impl Settings { unsafe { BNSettingsGetDouble( self.handle, - key.as_ref().as_ptr() as *mut _, + key.as_ptr(), view_ptr, func_ptr, &mut options.scope, @@ -155,16 +130,12 @@ impl Settings { } } - pub fn get_integer(&self, key: S) -> u64 { + pub fn get_integer(&self, key: &str) -> u64 { self.get_integer_with_opts(key, &mut QueryOptions::default()) } - pub fn get_integer_with_opts( - &self, - key: S, - options: &mut QueryOptions, - ) -> u64 { - let key = key.into_bytes_with_nul(); + pub fn get_integer_with_opts(&self, key: &str, options: &mut QueryOptions) -> u64 { + let key = key.to_cstr(); let view_ptr = match options.view.as_ref() { Some(view) => view.handle, _ => std::ptr::null_mut(), @@ -176,7 +147,7 @@ impl Settings { unsafe { BNSettingsGetUInt64( self.handle, - key.as_ref().as_ptr() as *mut _, + key.as_ptr(), view_ptr, func_ptr, &mut options.scope, @@ -184,16 +155,12 @@ impl Settings { } } - pub fn get_string(&self, key: S) -> BnString { + pub fn get_string(&self, key: &str) -> String { self.get_string_with_opts(key, &mut QueryOptions::default()) } - pub fn get_string_with_opts( - &self, - key: S, - options: &mut QueryOptions, - ) -> BnString { - let key = key.into_bytes_with_nul(); + pub fn get_string_with_opts(&self, key: &str, options: &mut QueryOptions) -> String { + let key = key.to_cstr(); let view_ptr = match options.view.as_ref() { Some(view) => view.handle, _ => std::ptr::null_mut(), @@ -203,9 +170,9 @@ impl Settings { _ => std::ptr::null_mut(), }; unsafe { - BnString::from_raw(BNSettingsGetString( + BnString::into_string(BNSettingsGetString( self.handle, - key.as_ref().as_ptr() as *mut _, + key.as_ptr(), view_ptr, func_ptr, &mut options.scope, @@ -213,16 +180,16 @@ impl Settings { } } - pub fn get_string_list(&self, key: S) -> Array { + pub fn get_string_list(&self, key: &str) -> Array { self.get_string_list_with_opts(key, &mut QueryOptions::default()) } - pub fn get_string_list_with_opts( + pub fn get_string_list_with_opts( &self, - key: S, + key: &str, options: &mut QueryOptions, ) -> Array { - let key = key.into_bytes_with_nul(); + let key = key.to_cstr(); let view_ptr = match options.view.as_ref() { Some(view) => view.handle, _ => std::ptr::null_mut(), @@ -236,7 +203,7 @@ impl Settings { Array::new( BNSettingsGetStringList( self.handle, - key.as_ref().as_ptr() as *mut _, + key.as_ptr(), view_ptr, func_ptr, &mut options.scope, @@ -248,16 +215,12 @@ impl Settings { } } - pub fn get_json(&self, key: S) -> BnString { + pub fn get_json(&self, key: &str) -> String { self.get_json_with_opts(key, &mut QueryOptions::default()) } - pub fn get_json_with_opts( - &self, - key: S, - options: &mut QueryOptions, - ) -> BnString { - let key = key.into_bytes_with_nul(); + pub fn get_json_with_opts(&self, key: &str, options: &mut QueryOptions) -> String { + let key = key.to_cstr(); let view_ptr = match options.view.as_ref() { Some(view) => view.handle, _ => std::ptr::null_mut(), @@ -267,9 +230,9 @@ impl Settings { _ => std::ptr::null_mut(), }; unsafe { - BnString::from_raw(BNSettingsGetJson( + BnString::into_string(BNSettingsGetJson( self.handle, - key.as_ref().as_ptr() as *mut _, + key.as_ptr(), view_ptr, func_ptr, &mut options.scope, @@ -277,17 +240,12 @@ impl Settings { } } - pub fn set_bool(&self, key: S, value: bool) { + pub fn set_bool(&self, key: &str, value: bool) { self.set_bool_with_opts(key, value, &QueryOptions::default()) } - pub fn set_bool_with_opts( - &self, - key: S, - value: bool, - options: &QueryOptions, - ) { - let key = key.into_bytes_with_nul(); + pub fn set_bool_with_opts(&self, key: &str, value: bool, options: &QueryOptions) { + let key = key.to_cstr(); let view_ptr = match options.view.as_ref() { Some(view) => view.handle, _ => std::ptr::null_mut(), @@ -302,22 +260,17 @@ impl Settings { view_ptr, func_ptr, options.scope, - key.as_ref().as_ptr() as *mut _, + key.as_ptr(), value, ); } } - pub fn set_double(&self, key: S, value: f64) { + pub fn set_double(&self, key: &str, value: f64) { self.set_double_with_opts(key, value, &QueryOptions::default()) } - pub fn set_double_with_opts( - &self, - key: S, - value: f64, - options: &QueryOptions, - ) { - let key = key.into_bytes_with_nul(); + pub fn set_double_with_opts(&self, key: &str, value: f64, options: &QueryOptions) { + let key = key.to_cstr(); let view_ptr = match options.view.as_ref() { Some(view) => view.handle, _ => std::ptr::null_mut(), @@ -332,23 +285,18 @@ impl Settings { view_ptr, func_ptr, options.scope, - key.as_ref().as_ptr() as *mut _, + key.as_ptr(), value, ); } } - pub fn set_integer(&self, key: S, value: u64) { + pub fn set_integer(&self, key: &str, value: u64) { self.set_integer_with_opts(key, value, &QueryOptions::default()) } - pub fn set_integer_with_opts( - &self, - key: S, - value: u64, - options: &QueryOptions, - ) { - let key = key.into_bytes_with_nul(); + pub fn set_integer_with_opts(&self, key: &str, value: u64, options: &QueryOptions) { + let key = key.to_cstr(); let view_ptr = match options.view.as_ref() { Some(view) => view.handle, _ => std::ptr::null_mut(), @@ -363,24 +311,19 @@ impl Settings { view_ptr, func_ptr, options.scope, - key.as_ref().as_ptr() as *mut _, + key.as_ptr(), value, ); } } - pub fn set_string(&self, key: S1, value: S2) { + pub fn set_string(&self, key: &str, value: &str) { self.set_string_with_opts(key, value, &QueryOptions::default()) } - pub fn set_string_with_opts( - &self, - key: S1, - value: S2, - options: &QueryOptions, - ) { - let key = key.into_bytes_with_nul(); - let value = value.into_bytes_with_nul(); + pub fn set_string_with_opts(&self, key: &str, value: &str, options: &QueryOptions) { + let key = key.to_cstr(); + let value = value.to_cstr(); let view_ptr = match options.view.as_ref() { Some(view) => view.handle, _ => std::ptr::null_mut(), @@ -395,36 +338,25 @@ impl Settings { view_ptr, func_ptr, options.scope, - key.as_ref().as_ptr() as *mut _, - value.as_ref().as_ptr() as *mut _, + key.as_ptr(), + value.as_ptr(), ); } } - pub fn set_string_list>( - &self, - key: S1, - value: I, - ) -> bool { + pub fn set_string_list>(&self, key: &str, value: I) -> bool { self.set_string_list_with_opts(key, value, &QueryOptions::default()) } - pub fn set_string_list_with_opts< - S1: BnStrCompatible, - S2: BnStrCompatible, - I: Iterator, - >( + pub fn set_string_list_with_opts>( &self, - key: S1, + key: &str, value: I, options: &QueryOptions, ) -> bool { - let key = key.into_bytes_with_nul(); - let raw_list: Vec<_> = value.map(|s| s.into_bytes_with_nul()).collect(); - let mut raw_list_ptr: Vec<_> = raw_list - .iter() - .map(|s| s.as_ref().as_ptr() as *const c_char) - .collect(); + let key = key.to_cstr(); + let raw_list: Vec<_> = value.into_iter().map(|s| s.to_cstr()).collect(); + let mut raw_list_ptr: Vec<_> = raw_list.iter().map(|s| s.as_ptr()).collect(); let view_ptr = match options.view.as_ref() { Some(view) => view.handle, @@ -440,25 +372,20 @@ impl Settings { view_ptr, func_ptr, options.scope, - key.as_ref().as_ptr() as *mut _, + key.as_ptr(), raw_list_ptr.as_mut_ptr(), raw_list_ptr.len(), ) } } - pub fn set_json(&self, key: S1, value: S2) -> bool { + pub fn set_json(&self, key: &str, value: &str) -> bool { self.set_json_with_opts(key, value, &QueryOptions::default()) } - pub fn set_json_with_opts( - &self, - key: S1, - value: S2, - options: &QueryOptions, - ) -> bool { - let key = key.into_bytes_with_nul(); - let value = value.into_bytes_with_nul(); + pub fn set_json_with_opts(&self, key: &str, value: &str, options: &QueryOptions) -> bool { + let key = key.to_cstr(); + let value = value.to_cstr(); let view_ptr = match options.view.as_ref() { Some(view) => view.handle, _ => std::ptr::null_mut(), @@ -473,38 +400,34 @@ impl Settings { view_ptr, func_ptr, options.scope, - key.as_ref().as_ptr() as *mut _, - value.as_ref().as_ptr() as *mut _, + key.as_ptr(), + value.as_ptr(), ) } } - pub fn get_property_string(&self, key: S, property: S) -> BnString { - let key = key.into_bytes_with_nul(); - let property = property.into_bytes_with_nul(); + pub fn get_property_string(&self, key: &str, property: &str) -> String { + let key = key.to_cstr(); + let property = property.to_cstr(); unsafe { - BnString::from_raw(BNSettingsQueryPropertyString( + BnString::into_string(BNSettingsQueryPropertyString( self.handle, - key.as_ref().as_ptr() as *mut _, - property.as_ref().as_ptr() as *mut _, + key.as_ptr(), + property.as_ptr(), )) } } - pub fn get_property_string_list( - &self, - key: S, - property: S, - ) -> Array { - let key = key.into_bytes_with_nul(); - let property = property.into_bytes_with_nul(); + pub fn get_property_string_list(&self, key: &str, property: &str) -> Array { + let key = key.to_cstr(); + let property = property.to_cstr(); let mut size: usize = 0; unsafe { Array::new( BNSettingsQueryPropertyStringList( self.handle, - key.as_ref().as_ptr() as *mut _, - property.as_ref().as_ptr() as *mut _, + key.as_ptr(), + property.as_ptr(), &mut size, ) as *mut *mut c_char, size, @@ -513,116 +436,78 @@ impl Settings { } } - pub fn update_bool_property(&self, key: S, property: S, value: bool) { - let key = key.into_bytes_with_nul(); - let property = property.into_bytes_with_nul(); + pub fn update_bool_property(&self, key: &str, property: &str, value: bool) { + let key = key.to_cstr(); + let property = property.to_cstr(); unsafe { - BNSettingsUpdateBoolProperty( - self.handle, - key.as_ref().as_ptr() as *mut _, - property.as_ref().as_ptr() as *mut _, - value, - ); + BNSettingsUpdateBoolProperty(self.handle, key.as_ptr(), property.as_ptr(), value); } } - pub fn update_integer_property(&self, key: S, property: S, value: u64) { - let key = key.into_bytes_with_nul(); - let property = property.into_bytes_with_nul(); + pub fn update_integer_property(&self, key: &str, property: &str, value: u64) { + let key = key.to_cstr(); + let property = property.to_cstr(); unsafe { - BNSettingsUpdateUInt64Property( - self.handle, - key.as_ref().as_ptr() as *mut _, - property.as_ref().as_ptr() as *mut _, - value, - ); + BNSettingsUpdateUInt64Property(self.handle, key.as_ptr(), property.as_ptr(), value); } } - pub fn update_double_property(&self, key: S, property: S, value: f64) { - let key = key.into_bytes_with_nul(); - let property = property.into_bytes_with_nul(); + pub fn update_double_property(&self, key: &str, property: &str, value: f64) { + let key = key.to_cstr(); + let property = property.to_cstr(); unsafe { - BNSettingsUpdateDoubleProperty( - self.handle, - key.as_ref().as_ptr() as *mut _, - property.as_ref().as_ptr() as *mut _, - value, - ); + BNSettingsUpdateDoubleProperty(self.handle, key.as_ptr(), property.as_ptr(), value); } } - pub fn update_string_property(&self, key: S, property: S, value: S) { - let key = key.into_bytes_with_nul(); - let property = property.into_bytes_with_nul(); - let value = value.into_bytes_with_nul(); + pub fn update_string_property(&self, key: &str, property: &str, value: &str) { + let key = key.to_cstr(); + let property = property.to_cstr(); + let value = value.to_cstr(); unsafe { BNSettingsUpdateStringProperty( self.handle, - key.as_ref().as_ptr() as *mut _, - property.as_ref().as_ptr() as *mut _, - value.as_ref().as_ptr() as *mut _, + key.as_ptr(), + property.as_ptr(), + value.as_ptr(), ); } } - pub fn update_string_list_property>( + pub fn update_string_list_property>( &self, - key: S, - property: S, + key: &str, + property: &str, value: I, ) { - let key = key.into_bytes_with_nul(); - let property = property.into_bytes_with_nul(); - let raw_list: Vec<_> = value.map(|s| s.into_bytes_with_nul()).collect(); - let mut raw_list_ptr: Vec<_> = raw_list - .iter() - .map(|s| s.as_ref().as_ptr() as *const c_char) - .collect(); + let key = key.to_cstr(); + let property = property.to_cstr(); + let raw_list: Vec<_> = value.into_iter().map(|s| s.to_cstr()).collect(); + let mut raw_list_ptr: Vec<_> = raw_list.iter().map(|s| s.as_ptr()).collect(); unsafe { BNSettingsUpdateStringListProperty( self.handle, - key.as_ref().as_ptr() as *mut _, - property.as_ref().as_ptr() as *mut _, + key.as_ptr(), + property.as_ptr(), raw_list_ptr.as_mut_ptr(), raw_list_ptr.len(), ); } } - pub fn register_group( - &self, - group: S1, - title: S2, - ) -> bool { - let group = group.into_bytes_with_nul(); - let title = title.into_bytes_with_nul(); + pub fn register_group(&self, group: &str, title: &str) -> bool { + let group = group.to_cstr(); + let title = title.to_cstr(); - unsafe { - BNSettingsRegisterGroup( - self.handle, - group.as_ref().as_ptr() as *mut _, - title.as_ref().as_ptr() as *mut _, - ) - } + unsafe { BNSettingsRegisterGroup(self.handle, group.as_ptr(), title.as_ptr()) } } - pub fn register_setting_json( - &self, - group: S1, - properties: S2, - ) -> bool { - let group = group.into_bytes_with_nul(); - let properties = properties.into_bytes_with_nul(); + pub fn register_setting_json(&self, group: &str, properties: &str) -> bool { + let group = group.to_cstr(); + let properties = properties.to_cstr(); - unsafe { - BNSettingsRegisterSetting( - self.handle, - group.as_ref().as_ptr() as *mut _, - properties.as_ref().as_ptr() as *mut _, - ) - } + unsafe { BNSettingsRegisterSetting(self.handle, group.as_ptr(), properties.as_ptr()) } } // TODO: register_setting but type-safely turn it into json diff --git a/rust/src/string.rs b/rust/src/string.rs index f92248516a..0a1191d5df 100644 --- a/rust/src/string.rs +++ b/rust/src/string.rs @@ -14,8 +14,7 @@ //! String wrappers for core-owned strings and strings being passed to the core -use crate::rc::*; -use crate::types::QualifiedName; +use binaryninjacore_sys::*; use std::borrow::Cow; use std::ffi::{c_char, CStr, CString}; use std::fmt; @@ -24,6 +23,10 @@ use std::mem; use std::ops::Deref; use std::path::{Path, PathBuf}; +use crate::rc::*; +use crate::type_archive::TypeArchiveSnapshotId; +use crate::types::QualifiedName; + // TODO: Remove or refactor this. pub(crate) fn raw_to_string(ptr: *const c_char) -> Option { if ptr.is_null() { @@ -33,47 +36,62 @@ pub(crate) fn raw_to_string(ptr: *const c_char) -> Option { } } -// TODO: Make this pass in an iterator over something more generic... -pub(crate) fn strings_to_string_list(strings: &[String]) -> *mut *mut c_char { +pub(crate) fn strings_to_string_list(strings: I) -> *mut *mut c_char +where + I: IntoIterator, + // TODO make `S: BnStrCompatible,` + S: AsRef, +{ use binaryninjacore_sys::BNAllocStringList; let bn_str_list = strings - .iter() - .map(|s| BnString::new(s.as_str())) + .into_iter() + .map(|s| BnString::new(s.as_ref())) .collect::>(); let mut raw_str_list = bn_str_list.iter().map(|s| s.as_ptr()).collect::>(); unsafe { BNAllocStringList(raw_str_list.as_mut_ptr(), raw_str_list.len()) } } -/// Is the equivalent of `core::ffi::CString` but using the alloc and free from `binaryninjacore-sys`. +/// A nul-terminated C string allocated by the core. +/// +/// Received from a variety of core function calls, and must be used when giving strings to the +/// core from many core-invoked callbacks, or otherwise passing ownership of the string to the core. +/// +/// These are strings we're responsible for freeing, such as strings allocated by the core and +/// given to us through the API and then forgotten about by the core. +/// +/// When passing to the core, make sure to use [`BnString::to_cstr`] and [`CStr::as_ptr`]. +/// +/// When giving ownership to the core, make sure to prevent dropping by calling [`BnString::into_raw`]. #[repr(transparent)] pub struct BnString { raw: *mut c_char, } -/// A nul-terminated C string allocated by the core. -/// -/// Received from a variety of core function calls, and -/// must be used when giving strings to the core from many -/// core-invoked callbacks. -/// -/// These are strings we're responsible for freeing, such as -/// strings allocated by the core and given to us through the API -/// and then forgotten about by the core. impl BnString { - pub fn new(s: S) -> Self { - use binaryninjacore_sys::BNAllocString; - let raw = s.into_bytes_with_nul(); - unsafe { - let ptr = raw.as_ref().as_ptr() as *mut _; - Self::from_raw(BNAllocString(ptr)) - } + pub fn new(s: impl IntoCStr) -> Self { + let raw = s.to_cstr(); + unsafe { Self::from_raw(BNAllocString(raw.as_ptr())) } + } + + /// Take an owned core string and convert it to [`String`]. + /// + /// This expects the passed raw string to be owned, as in, freed by us. + pub unsafe fn into_string(raw: *mut c_char) -> String { + Self::from_raw(raw).to_string_lossy().to_string() } - /// Construct a BnString from an owned const char* allocated by BNAllocString + /// Construct a BnString from an owned const char* allocated by [`BNAllocString`]. pub(crate) unsafe fn from_raw(raw: *mut c_char) -> Self { Self { raw } } + /// Free a raw string allocated by [`BNAllocString`]. + pub(crate) unsafe fn free_raw(raw: *mut c_char) { + if !raw.is_null() { + BNFreeString(raw); + } + } + /// Consumes the `BnString`, returning a raw pointer to the string. /// /// After calling this function, the caller is responsible for the @@ -87,40 +105,16 @@ impl BnString { mem::forget(value); res } - - pub fn as_str(&self) -> &str { - unsafe { CStr::from_ptr(self.raw).to_str().unwrap() } - } - - pub fn as_bytes(&self) -> &[u8] { - self.as_str().as_bytes() - } - - pub fn as_bytes_with_null(&self) -> &[u8] { - self.deref().to_bytes() - } - - pub fn len(&self) -> usize { - self.as_str().len() - } - - pub fn is_empty(&self) -> bool { - self.as_str().is_empty() - } } impl Drop for BnString { fn drop(&mut self) { - use binaryninjacore_sys::BNFreeString; - unsafe { - BNFreeString(self.raw); - } + unsafe { BnString::free_raw(self.raw) }; } } impl Clone for BnString { fn clone(&self) -> Self { - use binaryninjacore_sys::BNAllocString; unsafe { Self { raw: BNAllocString(self.raw), @@ -137,6 +131,18 @@ impl Deref for BnString { } } +impl From for BnString { + fn from(s: String) -> Self { + Self::new(s) + } +} + +impl From<&str> for BnString { + fn from(s: &str) -> Self { + Self::new(s) + } +} + impl AsRef<[u8]> for BnString { fn as_ref(&self) -> &[u8] { self.to_bytes_with_nul() @@ -157,12 +163,6 @@ impl PartialEq for BnString { impl Eq for BnString {} -impl fmt::Display for BnString { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "{}", self.to_string_lossy()) - } -} - impl fmt::Debug for BnString { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { self.to_string_lossy().fmt(f) @@ -177,7 +177,6 @@ impl CoreArrayProvider for BnString { unsafe impl CoreArrayProviderInner for BnString { unsafe fn free(raw: *mut Self::Raw, count: usize, _context: &Self::Context) { - use binaryninjacore_sys::BNFreeStringList; BNFreeStringList(raw, count); } @@ -186,110 +185,124 @@ unsafe impl CoreArrayProviderInner for BnString { } } -pub unsafe trait BnStrCompatible { - type Result: AsRef<[u8]>; +pub trait IntoCStr { + type Result: Deref; - fn into_bytes_with_nul(self) -> Self::Result; + fn to_cstr(self) -> Self::Result; } -unsafe impl<'a> BnStrCompatible for &'a CStr { - type Result = &'a [u8]; +impl IntoCStr for &CStr { + type Result = Self; - fn into_bytes_with_nul(self) -> Self::Result { - self.to_bytes_with_nul() + fn to_cstr(self) -> Self::Result { + self + } +} + +impl IntoCStr for BnString { + type Result = Self; + + fn to_cstr(self) -> Self::Result { + self + } +} + +impl IntoCStr for &BnString { + type Result = BnString; + + fn to_cstr(self) -> Self::Result { + self.clone() } } -unsafe impl BnStrCompatible for BnString { +impl IntoCStr for CString { type Result = Self; - fn into_bytes_with_nul(self) -> Self::Result { + fn to_cstr(self) -> Self::Result { self } } -unsafe impl BnStrCompatible for CString { - type Result = Vec; +impl IntoCStr for &str { + type Result = CString; - fn into_bytes_with_nul(self) -> Self::Result { - self.into_bytes_with_nul() + fn to_cstr(self) -> Self::Result { + CString::new(self).expect("can't pass strings with internal nul bytes to core!") } } -unsafe impl BnStrCompatible for &str { - type Result = Vec; +impl IntoCStr for String { + type Result = CString; - fn into_bytes_with_nul(self) -> Self::Result { - let ret = CString::new(self).expect("can't pass strings with internal nul bytes to core!"); - ret.into_bytes_with_nul() + fn to_cstr(self) -> Self::Result { + CString::new(self).expect("can't pass strings with internal nul bytes to core!") } } -unsafe impl BnStrCompatible for String { - type Result = Vec; +impl IntoCStr for &String { + type Result = CString; - fn into_bytes_with_nul(self) -> Self::Result { - self.as_str().into_bytes_with_nul() + fn to_cstr(self) -> Self::Result { + self.clone().to_cstr() } } -unsafe impl BnStrCompatible for &String { - type Result = Vec; +impl<'a> IntoCStr for &'a Cow<'a, str> { + type Result = CString; - fn into_bytes_with_nul(self) -> Self::Result { - self.as_str().into_bytes_with_nul() + fn to_cstr(self) -> Self::Result { + self.to_string().to_cstr() } } -unsafe impl<'a> BnStrCompatible for &'a Cow<'a, str> { - type Result = Vec; +impl IntoCStr for Cow<'_, str> { + type Result = CString; - fn into_bytes_with_nul(self) -> Self::Result { - self.to_string().into_bytes_with_nul() + fn to_cstr(self) -> Self::Result { + self.to_string().to_cstr() } } -unsafe impl BnStrCompatible for Cow<'_, str> { - type Result = Vec; +impl IntoCStr for &QualifiedName { + type Result = CString; - fn into_bytes_with_nul(self) -> Self::Result { - self.to_string().into_bytes_with_nul() + fn to_cstr(self) -> Self::Result { + self.to_string().to_cstr() } } -unsafe impl BnStrCompatible for &QualifiedName { - type Result = Vec; +impl IntoCStr for PathBuf { + type Result = CString; - fn into_bytes_with_nul(self) -> Self::Result { - self.to_string().into_bytes_with_nul() + fn to_cstr(self) -> Self::Result { + self.as_path().to_cstr() } } -unsafe impl BnStrCompatible for PathBuf { - type Result = Vec; +impl IntoCStr for &Path { + type Result = CString; - fn into_bytes_with_nul(self) -> Self::Result { - self.as_path().into_bytes_with_nul() + fn to_cstr(self) -> Self::Result { + CString::new(self.as_os_str().as_encoded_bytes()) + .expect("can't pass paths with internal nul bytes to core!") } } -unsafe impl BnStrCompatible for &Path { - type Result = Vec; +impl IntoCStr for TypeArchiveSnapshotId { + type Result = CString; - fn into_bytes_with_nul(self) -> Self::Result { - let ret = CString::new(self.as_os_str().as_encoded_bytes()) - .expect("can't pass paths with internal nul bytes to core!"); - ret.into_bytes_with_nul() + fn to_cstr(self) -> Self::Result { + self.to_string().to_cstr() } } pub trait IntoJson { - type Output: BnStrCompatible; + type Output: IntoCStr; fn get_json_string(self) -> Result; } -impl IntoJson for S { +impl IntoJson for S { type Output = S; fn get_json_string(self) -> Result { diff --git a/rust/src/symbol.rs b/rust/src/symbol.rs index 2ff92db537..b06a0f2fdc 100644 --- a/rust/src/symbol.rs +++ b/rust/src/symbol.rs @@ -153,9 +153,9 @@ impl SymbolBuilder { } pub fn create(self) -> Ref { - let raw_name = self.raw_name.into_bytes_with_nul(); - let short_name = self.short_name.map(|s| s.into_bytes_with_nul()); - let full_name = self.full_name.map(|s| s.into_bytes_with_nul()); + let raw_name = self.raw_name.to_cstr(); + let short_name = self.short_name.map(|s| s.to_cstr()); + let full_name = self.full_name.map(|s| s.to_cstr()); // Lifetimes, man let raw_name = raw_name.as_ptr() as _; diff --git a/rust/src/tags.rs b/rust/src/tags.rs index 2db3a58cbc..a8c4d5d4b5 100644 --- a/rust/src/tags.rs +++ b/rust/src/tags.rs @@ -42,27 +42,27 @@ impl Tag { Ref::new(Self { handle }) } - pub fn new(t: &TagType, data: S) -> Ref { - let data = data.into_bytes_with_nul(); - unsafe { Self::ref_from_raw(BNCreateTag(t.handle, data.as_ref().as_ptr() as *mut _)) } + pub fn new(t: &TagType, data: &str) -> Ref { + let data = data.to_cstr(); + unsafe { Self::ref_from_raw(BNCreateTag(t.handle, data.as_ptr())) } } - pub fn id(&self) -> BnString { - unsafe { BnString::from_raw(BNTagGetId(self.handle)) } + pub fn id(&self) -> String { + unsafe { BnString::into_string(BNTagGetId(self.handle)) } } - pub fn data(&self) -> BnString { - unsafe { BnString::from_raw(BNTagGetData(self.handle)) } + pub fn data(&self) -> String { + unsafe { BnString::into_string(BNTagGetData(self.handle)) } } pub fn ty(&self) -> Ref { unsafe { TagType::ref_from_raw(BNTagGetType(self.handle)) } } - pub fn set_data(&self, data: S) { - let data = data.into_bytes_with_nul(); + pub fn set_data(&self, data: &str) { + let data = data.to_cstr(); unsafe { - BNTagSetData(self.handle, data.as_ref().as_ptr() as *mut _); + BNTagSetData(self.handle, data.as_ptr()); } } } @@ -134,40 +134,36 @@ impl TagType { Ref::new(Self { handle }) } - pub fn create( - view: &BinaryView, - name: N, - icon: I, - ) -> Ref { + pub fn create(view: &BinaryView, name: &str, icon: &str) -> Ref { let tag_type = unsafe { Self::ref_from_raw(BNCreateTagType(view.handle)) }; tag_type.set_name(name); tag_type.set_icon(icon); tag_type } - pub fn id(&self) -> BnString { - unsafe { BnString::from_raw(BNTagTypeGetId(self.handle)) } + pub fn id(&self) -> String { + unsafe { BnString::into_string(BNTagTypeGetId(self.handle)) } } - pub fn icon(&self) -> BnString { - unsafe { BnString::from_raw(BNTagTypeGetIcon(self.handle)) } + pub fn icon(&self) -> String { + unsafe { BnString::into_string(BNTagTypeGetIcon(self.handle)) } } - pub fn set_icon(&self, icon: S) { - let icon = icon.into_bytes_with_nul(); + pub fn set_icon(&self, icon: &str) { + let icon = icon.to_cstr(); unsafe { - BNTagTypeSetIcon(self.handle, icon.as_ref().as_ptr() as *mut _); + BNTagTypeSetIcon(self.handle, icon.as_ptr()); } } - pub fn name(&self) -> BnString { - unsafe { BnString::from_raw(BNTagTypeGetName(self.handle)) } + pub fn name(&self) -> String { + unsafe { BnString::into_string(BNTagTypeGetName(self.handle)) } } - pub fn set_name(&self, name: S) { - let name = name.into_bytes_with_nul(); + pub fn set_name(&self, name: &str) { + let name = name.to_cstr(); unsafe { - BNTagTypeSetName(self.handle, name.as_ref().as_ptr() as *mut _); + BNTagTypeSetName(self.handle, name.as_ptr()); } } @@ -183,10 +179,10 @@ impl TagType { unsafe { BNTagTypeGetType(self.handle) } } - pub fn set_type(&self, t: S) { - let t = t.into_bytes_with_nul(); + pub fn set_type(&self, t: &str) { + let t = t.to_cstr(); unsafe { - BNTagTypeSetName(self.handle, t.as_ref().as_ptr() as *mut _); + BNTagTypeSetName(self.handle, t.as_ptr()); } } diff --git a/rust/src/template_simplifier.rs b/rust/src/template_simplifier.rs index dd815f69a8..81f8deb403 100644 --- a/rust/src/template_simplifier.rs +++ b/rust/src/template_simplifier.rs @@ -1,20 +1,15 @@ use crate::{ - string::{BnStrCompatible, BnString}, + string::{BnString, IntoCStr}, types::QualifiedName, }; use binaryninjacore_sys::{BNRustSimplifyStrToFQN, BNRustSimplifyStrToStr}; -pub fn simplify_str_to_str(input: S) -> BnString { - let name = input.into_bytes_with_nul(); - unsafe { BnString::from_raw(BNRustSimplifyStrToStr(name.as_ref().as_ptr() as *mut _)) } +pub fn simplify_str_to_str(input: S) -> BnString { + let name = input.to_cstr(); + unsafe { BnString::from_raw(BNRustSimplifyStrToStr(name.as_ptr())) } } -pub fn simplify_str_to_fqn(input: S, simplify: bool) -> QualifiedName { - let name = input.into_bytes_with_nul(); - unsafe { - QualifiedName::from_owned_raw(BNRustSimplifyStrToFQN( - name.as_ref().as_ptr() as *mut _, - simplify, - )) - } +pub fn simplify_str_to_fqn(input: S, simplify: bool) -> QualifiedName { + let name = input.to_cstr(); + unsafe { QualifiedName::from_owned_raw(BNRustSimplifyStrToFQN(name.as_ptr(), simplify)) } } diff --git a/rust/src/type_archive.rs b/rust/src/type_archive.rs index f10cda504b..5f7ffb84ad 100644 --- a/rust/src/type_archive.rs +++ b/rust/src/type_archive.rs @@ -10,7 +10,7 @@ use crate::data_buffer::DataBuffer; use crate::metadata::Metadata; use crate::platform::Platform; use crate::rc::{Array, CoreArrayProvider, CoreArrayProviderInner, Guard, Ref, RefCountable}; -use crate::string::{raw_to_string, BnStrCompatible, BnString}; +use crate::string::{raw_to_string, BnString, IntoCStr}; use crate::type_container::TypeContainer; use crate::types::{QualifiedName, QualifiedNameAndType, QualifiedNameTypeAndId, Type}; @@ -65,8 +65,8 @@ impl TypeArchive { /// Open the Type Archive at the given path, if it exists. pub fn open(path: impl AsRef) -> Option> { - let raw_path = path.as_ref().into_bytes_with_nul(); - let handle = unsafe { BNOpenTypeArchive(raw_path.as_ptr() as *const c_char) }; + let raw_path = path.as_ref().to_cstr(); + let handle = unsafe { BNOpenTypeArchive(raw_path.as_ptr()) }; NonNull::new(handle).map(|handle| unsafe { TypeArchive::ref_from_raw(handle) }) } @@ -74,55 +74,46 @@ impl TypeArchive { /// /// If the file has already been created and is not a valid type archive this will return `None`. pub fn create(path: impl AsRef, platform: &Platform) -> Option> { - let raw_path = path.as_ref().into_bytes_with_nul(); - let handle = - unsafe { BNCreateTypeArchive(raw_path.as_ptr() as *const c_char, platform.handle) }; + let raw_path = path.as_ref().to_cstr(); + let handle = unsafe { BNCreateTypeArchive(raw_path.as_ptr(), platform.handle) }; NonNull::new(handle).map(|handle| unsafe { TypeArchive::ref_from_raw(handle) }) } /// Create a Type Archive at the given path and id, returning None if it could not be created. /// /// If the file has already been created and is not a valid type archive this will return `None`. - pub fn create_with_id( + pub fn create_with_id( path: impl AsRef, - id: I, + id: &str, platform: &Platform, ) -> Option> { - let raw_path = path.as_ref().into_bytes_with_nul(); - let id = id.into_bytes_with_nul(); - let handle = unsafe { - BNCreateTypeArchiveWithId( - raw_path.as_ptr() as *const c_char, - platform.handle, - id.as_ref().as_ptr() as *const c_char, - ) - }; + let raw_path = path.as_ref().to_cstr(); + let id = id.to_cstr(); + let handle = + unsafe { BNCreateTypeArchiveWithId(raw_path.as_ptr(), platform.handle, id.as_ptr()) }; NonNull::new(handle).map(|handle| unsafe { TypeArchive::ref_from_raw(handle) }) } /// Get a reference to the Type Archive with the known id, if one exists. - pub fn lookup_by_id(id: S) -> Option> { - let id = id.into_bytes_with_nul(); - let handle = unsafe { BNLookupTypeArchiveById(id.as_ref().as_ptr() as *const c_char) }; + pub fn lookup_by_id(id: &str) -> Option> { + let id = id.to_cstr(); + let handle = unsafe { BNLookupTypeArchiveById(id.as_ptr()) }; NonNull::new(handle).map(|handle| unsafe { TypeArchive::ref_from_raw(handle) }) } /// Get the path to the Type Archive's file pub fn path(&self) -> Option { let result = unsafe { BNGetTypeArchivePath(self.handle.as_ptr()) }; - match result.is_null() { - false => { - let bn_res = unsafe { BnString::from_raw(result) }; - Some(PathBuf::from(bn_res.to_string())) - } - true => None, - } + assert!(!result.is_null()); + let path_str = unsafe { BnString::into_string(result) }; + Some(PathBuf::from(path_str)) } /// Get the guid for a Type Archive - pub fn id(&self) -> Option { + pub fn id(&self) -> BnString { let result = unsafe { BNGetTypeArchiveId(self.handle.as_ptr()) }; - (!result.is_null()).then(|| unsafe { BnString::from_raw(result) }) + assert!(!result.is_null()); + unsafe { BnString::from_raw(result) } } /// Get the associated Platform for a Type Archive @@ -136,14 +127,14 @@ impl TypeArchive { pub fn current_snapshot_id(&self) -> TypeArchiveSnapshotId { let result = unsafe { BNGetTypeArchiveCurrentSnapshotId(self.handle.as_ptr()) }; assert!(!result.is_null()); - TypeArchiveSnapshotId(unsafe { BnString::from_raw(result) }.to_string()) + let id = unsafe { BnString::into_string(result) }; + TypeArchiveSnapshotId(id) } /// Revert the type archive's current snapshot to the given snapshot pub fn set_current_snapshot_id(&self, id: &TypeArchiveSnapshotId) { - unsafe { - BNSetTypeArchiveCurrentSnapshot(self.handle.as_ptr(), id.0.as_ptr() as *const c_char) - } + let snapshot = id.clone().to_cstr(); + unsafe { BNSetTypeArchiveCurrentSnapshot(self.handle.as_ptr(), snapshot.as_ptr()) } } /// Get a list of every snapshot's id @@ -155,33 +146,27 @@ impl TypeArchive { } /// Get the ids of the parents to the given snapshot - pub fn get_snapshot_parent_ids( + pub fn get_snapshot_parent_ids( &self, snapshot: &TypeArchiveSnapshotId, ) -> Option> { let mut count = 0; + let snapshot = snapshot.clone().to_cstr(); let result = unsafe { - BNGetTypeArchiveSnapshotParentIds( - self.handle.as_ptr(), - snapshot.0.as_ptr() as *const c_char, - &mut count, - ) + BNGetTypeArchiveSnapshotParentIds(self.handle.as_ptr(), snapshot.as_ptr(), &mut count) }; (!result.is_null()).then(|| unsafe { Array::new(result, count, ()) }) } /// Get the ids of the children to the given snapshot - pub fn get_snapshot_child_ids( + pub fn get_snapshot_child_ids( &self, snapshot: &TypeArchiveSnapshotId, ) -> Option> { let mut count = 0; + let snapshot = snapshot.clone().to_cstr(); let result = unsafe { - BNGetTypeArchiveSnapshotChildIds( - self.handle.as_ptr(), - snapshot.0.as_ptr() as *const c_char, - &mut count, - ) + BNGetTypeArchiveSnapshotChildIds(self.handle.as_ptr(), snapshot.as_ptr(), &mut count) }; (!result.is_null()).then(|| unsafe { Array::new(result, count, ()) }) } @@ -224,7 +209,7 @@ impl TypeArchive { /// * `new_name` - New type name pub fn rename_type(&self, old_name: QualifiedName, new_name: QualifiedName) -> bool { if let Some(id) = self.get_type_id(old_name) { - self.rename_type_by_id(id, new_name) + self.rename_type_by_id(&id, new_name) } else { false } @@ -234,16 +219,11 @@ impl TypeArchive { /// /// * `id` - Old id of type in archive /// * `new_name` - New type name - pub fn rename_type_by_id(&self, id: S, new_name: QualifiedName) -> bool { - let id = id.into_bytes_with_nul(); + pub fn rename_type_by_id(&self, id: &str, new_name: QualifiedName) -> bool { + let id = id.to_cstr(); let raw_name = QualifiedName::into_raw(new_name); - let result = unsafe { - BNRenameTypeArchiveType( - self.handle.as_ptr(), - id.as_ref().as_ptr() as *const c_char, - &raw_name, - ) - }; + let result = + unsafe { BNRenameTypeArchiveType(self.handle.as_ptr(), id.as_ptr(), &raw_name) }; QualifiedName::free_raw(raw_name); result } @@ -251,25 +231,22 @@ impl TypeArchive { /// Delete an existing type in the type archive. pub fn delete_type(&self, name: QualifiedName) -> bool { if let Some(type_id) = self.get_type_id(name) { - self.delete_type_by_id(type_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) -> bool { - let id = id.into_bytes_with_nul(); - let result = unsafe { - BNDeleteTypeArchiveType(self.handle.as_ptr(), id.as_ref().as_ptr() as *const c_char) - }; - result + pub fn delete_type_by_id(&self, id: &str) -> bool { + let id = id.to_cstr(); + unsafe { BNDeleteTypeArchiveType(self.handle.as_ptr(), id.as_ptr()) } } /// Retrieve a stored type in the archive /// /// * `name` - Type name - pub fn get_type_by_name(&self, name: QualifiedName) -> Option> { + pub fn get_type_by_name(&self, name: QualifiedName) -> Option> { self.get_type_by_name_from_snapshot(name, &TypeArchiveSnapshotId::unset()) } @@ -283,12 +260,9 @@ impl TypeArchive { snapshot: &TypeArchiveSnapshotId, ) -> Option> { let raw_name = QualifiedName::into_raw(name); + let snapshot = snapshot.clone().to_cstr(); let result = unsafe { - BNGetTypeArchiveTypeByName( - self.handle.as_ptr(), - &raw_name, - snapshot.0.as_ptr() as *const c_char, - ) + BNGetTypeArchiveTypeByName(self.handle.as_ptr(), &raw_name, snapshot.as_ptr()) }; QualifiedName::free_raw(raw_name); (!result.is_null()).then(|| unsafe { Type::ref_from_raw(result) }) @@ -297,7 +271,7 @@ impl TypeArchive { /// Retrieve a stored type in the archive by id /// /// * `id` - Type id - pub fn get_type_by_id(&self, id: I) -> Option> { + pub fn get_type_by_id(&self, id: &str) -> Option> { self.get_type_by_id_from_snapshot(id, &TypeArchiveSnapshotId::unset()) } @@ -305,18 +279,15 @@ impl TypeArchive { /// /// * `id` - Type id /// * `snapshot` - Snapshot id to search for types - pub fn get_type_by_id_from_snapshot( + pub fn get_type_by_id_from_snapshot( &self, - id: I, + id: &str, snapshot: &TypeArchiveSnapshotId, ) -> Option> { - let id = id.into_bytes_with_nul(); + let id = id.to_cstr(); + let snapshot = snapshot.clone().to_cstr(); let result = unsafe { - BNGetTypeArchiveTypeById( - self.handle.as_ptr(), - id.as_ref().as_ptr() as *const c_char, - snapshot.0.as_ptr() as *const c_char, - ) + BNGetTypeArchiveTypeById(self.handle.as_ptr(), id.as_ptr(), snapshot.as_ptr()) }; (!result.is_null()).then(|| unsafe { Type::ref_from_raw(result) }) } @@ -324,7 +295,7 @@ impl TypeArchive { /// Retrieve a type's name by its id /// /// * `id` - Type id - pub fn get_type_name_by_id(&self, id: I) -> QualifiedName { + pub fn get_type_name_by_id(&self, id: &str) -> QualifiedName { self.get_type_name_by_id_from_snapshot(id, &TypeArchiveSnapshotId::unset()) } @@ -332,18 +303,15 @@ impl TypeArchive { /// /// * `id` - Type id /// * `snapshot` - Snapshot id to search for types - pub fn get_type_name_by_id_from_snapshot( + pub fn get_type_name_by_id_from_snapshot( &self, - id: I, + id: &str, snapshot: &TypeArchiveSnapshotId, ) -> QualifiedName { - let id = id.into_bytes_with_nul(); + let id = id.to_cstr(); + let snapshot = snapshot.clone().to_cstr(); let result = unsafe { - BNGetTypeArchiveTypeName( - self.handle.as_ptr(), - id.as_ref().as_ptr() as *const c_char, - snapshot.0.as_ptr() as *const c_char, - ) + BNGetTypeArchiveTypeName(self.handle.as_ptr(), id.as_ptr(), snapshot.as_ptr()) }; QualifiedName::from_owned_raw(result) } @@ -351,7 +319,7 @@ impl TypeArchive { /// Retrieve a type's id by its name /// /// * `name` - Type name - pub fn get_type_id(&self, name: QualifiedName) -> Option { + pub fn get_type_id(&self, name: QualifiedName) -> Option { self.get_type_id_from_snapshot(name, &TypeArchiveSnapshotId::unset()) } @@ -363,17 +331,13 @@ impl TypeArchive { &self, name: QualifiedName, snapshot: &TypeArchiveSnapshotId, - ) -> Option { + ) -> Option { let raw_name = QualifiedName::into_raw(name); - let result = unsafe { - BNGetTypeArchiveTypeId( - self.handle.as_ptr(), - &raw_name, - snapshot.0.as_ptr() as *const c_char, - ) - }; + let snapshot = snapshot.clone().to_cstr(); + let result = + unsafe { BNGetTypeArchiveTypeId(self.handle.as_ptr(), &raw_name, snapshot.as_ptr()) }; QualifiedName::free_raw(raw_name); - (!result.is_null()).then(|| unsafe { BnString::from_raw(result) }) + (!result.is_null()).then(|| unsafe { BnString::into_string(result) }) } /// Retrieve all stored types in the archive at a snapshot @@ -389,13 +353,9 @@ impl TypeArchive { snapshot: &TypeArchiveSnapshotId, ) -> Array { let mut count = 0; - let result = unsafe { - BNGetTypeArchiveTypes( - self.handle.as_ptr(), - snapshot.0.as_ptr() as *const c_char, - &mut count, - ) - }; + let snapshot = snapshot.clone().to_cstr(); + let result = + unsafe { BNGetTypeArchiveTypes(self.handle.as_ptr(), snapshot.as_ptr(), &mut count) }; assert!(!result.is_null()); unsafe { Array::new(result, count, ()) } } @@ -410,13 +370,9 @@ impl TypeArchive { /// * `snapshot` - Snapshot id to search for types pub fn get_type_ids_from_snapshot(&self, snapshot: &TypeArchiveSnapshotId) -> Array { let mut count = 0; - let result = unsafe { - BNGetTypeArchiveTypeIds( - self.handle.as_ptr(), - snapshot.0.as_ptr() as *const c_char, - &mut count, - ) - }; + let snapshot = snapshot.clone().to_cstr(); + let result = + unsafe { BNGetTypeArchiveTypeIds(self.handle.as_ptr(), snapshot.as_ptr(), &mut count) }; assert!(!result.is_null()); unsafe { Array::new(result, count, ()) } } @@ -434,12 +390,9 @@ impl TypeArchive { snapshot: &TypeArchiveSnapshotId, ) -> Array { let mut count = 0; + let snapshot = snapshot.clone().to_cstr(); let result = unsafe { - BNGetTypeArchiveTypeNames( - self.handle.as_ptr(), - snapshot.0.as_ptr() as *const c_char, - &mut count, - ) + BNGetTypeArchiveTypeNames(self.handle.as_ptr(), snapshot.as_ptr(), &mut count) }; assert!(!result.is_null()); unsafe { Array::new(result, count, ()) } @@ -458,12 +411,13 @@ impl TypeArchive { snapshot: &TypeArchiveSnapshotId, ) -> (Array, Array) { let mut count = 0; + let snapshot = snapshot.clone().to_cstr(); let mut names = std::ptr::null_mut(); let mut ids = std::ptr::null_mut(); let result = unsafe { BNGetTypeArchiveTypeNamesAndIds( self.handle.as_ptr(), - snapshot.0.as_ptr() as *const c_char, + snapshot.as_ptr(), &mut names, &mut ids, &mut count, @@ -478,7 +432,7 @@ impl TypeArchive { /// Get all types a given type references directly /// /// * `id` - Source type id - pub fn get_outgoing_direct_references(&self, id: I) -> Array { + pub fn get_outgoing_direct_references(&self, id: &str) -> Array { self.get_outgoing_direct_references_from_snapshot(id, &TypeArchiveSnapshotId::unset()) } @@ -486,18 +440,19 @@ impl TypeArchive { /// /// * `id` - Source type id /// * `snapshot` - Snapshot id to search for types - pub fn get_outgoing_direct_references_from_snapshot( + pub fn get_outgoing_direct_references_from_snapshot( &self, - id: I, + id: &str, snapshot: &TypeArchiveSnapshotId, ) -> Array { - let id = id.into_bytes_with_nul(); + let id = id.to_cstr(); + let snapshot = snapshot.clone().to_cstr(); let mut count = 0; let result = unsafe { BNGetTypeArchiveOutgoingDirectTypeReferences( self.handle.as_ptr(), - id.as_ref().as_ptr() as *const c_char, - snapshot.0.as_ptr() as *const c_char, + id.as_ptr(), + snapshot.as_ptr(), &mut count, ) }; @@ -508,7 +463,7 @@ impl TypeArchive { /// Get all types a given type references, and any types that the referenced types reference /// /// * `id` - Source type id - pub fn get_outgoing_recursive_references(&self, id: I) -> Array { + pub fn get_outgoing_recursive_references(&self, id: &str) -> Array { self.get_outgoing_recursive_references_from_snapshot(id, &TypeArchiveSnapshotId::unset()) } @@ -516,18 +471,19 @@ impl TypeArchive { /// /// * `id` - Source type id /// * `snapshot` - Snapshot id to search for types - pub fn get_outgoing_recursive_references_from_snapshot( + pub fn get_outgoing_recursive_references_from_snapshot( &self, - id: I, + id: &str, snapshot: &TypeArchiveSnapshotId, ) -> Array { - let id = id.into_bytes_with_nul(); + let id = id.to_cstr(); + let snapshot = snapshot.clone().to_cstr(); let mut count = 0; let result = unsafe { BNGetTypeArchiveOutgoingRecursiveTypeReferences( self.handle.as_ptr(), - id.as_ref().as_ptr() as *const c_char, - snapshot.0.as_ptr() as *const c_char, + id.as_ptr(), + snapshot.as_ptr(), &mut count, ) }; @@ -538,7 +494,7 @@ impl TypeArchive { /// Get all types that reference a given type /// /// * `id` - Target type id - pub fn get_incoming_direct_references(&self, id: I) -> Array { + pub fn get_incoming_direct_references(&self, id: &str) -> Array { self.get_incoming_direct_references_with_snapshot(id, &TypeArchiveSnapshotId::unset()) } @@ -546,18 +502,19 @@ impl TypeArchive { /// /// * `id` - Target type id /// * `snapshot` - Snapshot id to search for types - pub fn get_incoming_direct_references_with_snapshot( + pub fn get_incoming_direct_references_with_snapshot( &self, - id: I, + id: &str, snapshot: &TypeArchiveSnapshotId, ) -> Array { - let id = id.into_bytes_with_nul(); + let id = id.to_cstr(); + let snapshot = snapshot.clone().to_cstr(); let mut count = 0; let result = unsafe { BNGetTypeArchiveIncomingDirectTypeReferences( self.handle.as_ptr(), - id.as_ref().as_ptr() as *const c_char, - snapshot.0.as_ptr() as *const c_char, + id.as_ptr(), + snapshot.as_ptr(), &mut count, ) }; @@ -568,7 +525,7 @@ impl TypeArchive { /// Get all types that reference a given type, and all types that reference them, recursively /// /// * `id` - Target type id - pub fn get_incoming_recursive_references(&self, id: I) -> Array { + pub fn get_incoming_recursive_references(&self, id: &str) -> Array { self.get_incoming_recursive_references_with_snapshot(id, &TypeArchiveSnapshotId::unset()) } @@ -576,18 +533,19 @@ impl TypeArchive { /// /// * `id` - Target type id /// * `snapshot` - Snapshot id to search for types, or empty string to search the latest snapshot - pub fn get_incoming_recursive_references_with_snapshot( + pub fn get_incoming_recursive_references_with_snapshot( &self, - id: I, + id: &str, snapshot: &TypeArchiveSnapshotId, ) -> Array { - let id = id.into_bytes_with_nul(); + let id = id.to_cstr(); + let snapshot = snapshot.clone().to_cstr(); let mut count = 0; let result = unsafe { BNGetTypeArchiveIncomingRecursiveTypeReferences( self.handle.as_ptr(), - id.as_ref().as_ptr() as *const c_char, - snapshot.0.as_ptr() as *const c_char, + id.as_ptr(), + snapshot.as_ptr(), &mut count, ) }; @@ -596,11 +554,9 @@ impl TypeArchive { } /// Look up a metadata entry in the archive - pub fn query_metadata(&self, key: S) -> Option> { - let key = key.into_bytes_with_nul(); - let result = unsafe { - BNTypeArchiveQueryMetadata(self.handle.as_ptr(), key.as_ref().as_ptr() as *const c_char) - }; + pub fn query_metadata(&self, key: &str) -> Option> { + let key = key.to_cstr(); + let result = unsafe { BNTypeArchiveQueryMetadata(self.handle.as_ptr(), key.as_ptr()) }; (!result.is_null()).then(|| unsafe { Metadata::ref_from_raw(result) }) } @@ -608,40 +564,24 @@ impl TypeArchive { /// /// * `key` - key value to associate the Metadata object with /// * `md` - object to store. - pub fn store_metadata(&self, key: S, md: &Metadata) { - let key = key.into_bytes_with_nul(); - let result = unsafe { - BNTypeArchiveStoreMetadata( - self.handle.as_ptr(), - key.as_ref().as_ptr() as *const c_char, - md.handle, - ) - }; + pub fn store_metadata(&self, key: &str, md: &Metadata) { + let key = key.to_cstr(); + let result = + unsafe { BNTypeArchiveStoreMetadata(self.handle.as_ptr(), key.as_ptr(), md.handle) }; assert!(result); } /// Delete a given metadata entry in the archive from the `key` - pub fn remove_metadata(&self, key: S) -> bool { - let key = key.into_bytes_with_nul(); - unsafe { - BNTypeArchiveRemoveMetadata( - self.handle.as_ptr(), - key.as_ref().as_ptr() as *const c_char, - ) - } + pub fn remove_metadata(&self, key: &str) -> bool { + let key = key.to_cstr(); + unsafe { BNTypeArchiveRemoveMetadata(self.handle.as_ptr(), key.as_ptr()) } } /// Turn a given `snapshot` id into a data stream - pub fn serialize_snapshot( - &self, - snapshot: &TypeArchiveSnapshotId, - ) -> DataBuffer { - let result = unsafe { - BNTypeArchiveSerializeSnapshot( - self.handle.as_ptr(), - snapshot.0.as_ptr() as *const c_char, - ) - }; + pub fn serialize_snapshot(&self, snapshot: &TypeArchiveSnapshotId) -> DataBuffer { + let snapshot = snapshot.clone().to_cstr(); + let result = + unsafe { BNTypeArchiveSerializeSnapshot(self.handle.as_ptr(), snapshot.as_ptr()) }; assert!(!result.is_null()); DataBuffer::from_raw(result) } @@ -651,7 +591,8 @@ impl TypeArchive { let result = unsafe { BNTypeArchiveDeserializeSnapshot(self.handle.as_ptr(), data.as_raw()) }; assert!(!result.is_null()); - TypeArchiveSnapshotId(unsafe { BnString::from_raw(result) }.to_string()) + let id = unsafe { BnString::into_string(result) }; + TypeArchiveSnapshotId(id) } /// Register a notification listener @@ -705,11 +646,10 @@ impl TypeArchive { unsafe { BNCloseTypeArchive(self.handle.as_ptr()) } } - // TODO: Make this AsRef? /// Determine if `file` is a Type Archive - pub fn is_type_archive(file: P) -> bool { - let file = file.into_bytes_with_nul(); - unsafe { BNIsTypeArchive(file.as_ref().as_ptr() as *const c_char) } + pub fn is_type_archive(file: &Path) -> bool { + let file = file.to_cstr(); + unsafe { BNIsTypeArchive(file.as_ptr()) } } ///// Get the TypeContainer interface for this Type Archive, presenting types @@ -726,13 +666,12 @@ impl TypeArchive { /// * `parents` - Parent snapshot ids /// /// Returns Created snapshot id - pub fn new_snapshot_transaction( + pub fn new_snapshot_transaction( &self, mut function: F, parents: &[TypeArchiveSnapshotId], ) -> TypeArchiveSnapshotId where - P: BnStrCompatible, F: FnMut(&TypeArchiveSnapshotId) -> bool, { unsafe extern "C" fn cb_callback bool>( @@ -744,21 +683,20 @@ impl TypeArchive { fun(&TypeArchiveSnapshotId(id_str)) } - // SAFETY TypeArchiveSnapshotId and `*const c_char` are transparent - let parents_raw = parents.as_ptr() as *const *const c_char; - + let parents_cstr: Vec<_> = parents.iter().map(|p| p.clone().to_cstr()).collect(); + let parents_raw: Vec<_> = parents_cstr.iter().map(|p| p.as_ptr()).collect(); let result = unsafe { BNTypeArchiveNewSnapshotTransaction( self.handle.as_ptr(), Some(cb_callback::), &mut function as *mut F as *mut c_void, - parents_raw, + parents_raw.as_ptr(), parents.len(), ) }; assert!(!result.is_null()); - let id_str = unsafe { BnString::from_raw(result) }; - TypeArchiveSnapshotId(id_str.to_string()) + let id_str = unsafe { BnString::into_string(result) }; + TypeArchiveSnapshotId(id_str) } /// Merge two snapshots in the archive to produce a new snapshot @@ -771,20 +709,15 @@ impl TypeArchive { /// /// Returns Snapshot id, if merge was successful, otherwise the List of /// conflicting type ids - pub fn merge_snapshots( + pub fn merge_snapshots( &self, - base_snapshot: B, - first_snapshot: F, - second_snapshot: S, + base_snapshot: &str, + first_snapshot: &str, + second_snapshot: &str, merge_conflicts: M, ) -> Result> where - B: BnStrCompatible, - F: BnStrCompatible, - S: BnStrCompatible, - M: IntoIterator, - MI: BnStrCompatible, - MK: BnStrCompatible, + M: IntoIterator, { self.merge_snapshots_with_progress( base_snapshot, @@ -805,26 +738,21 @@ impl TypeArchive { /// /// Returns Snapshot id, if merge was successful, otherwise the List of /// conflicting type ids - pub fn merge_snapshots_with_progress( + pub fn merge_snapshots_with_progress( &self, - base_snapshot: B, - first_snapshot: F, - second_snapshot: S, + base_snapshot: &str, + first_snapshot: &str, + second_snapshot: &str, merge_conflicts: M, - mut progress: P, + mut progress: PC, ) -> Result> where - B: BnStrCompatible, - F: BnStrCompatible, - S: BnStrCompatible, - M: IntoIterator, - MI: BnStrCompatible, - MK: BnStrCompatible, - P: ProgressCallback, + M: IntoIterator, + PC: ProgressCallback, { - let base_snapshot = base_snapshot.into_bytes_with_nul(); - let first_snapshot = first_snapshot.into_bytes_with_nul(); - let second_snapshot = second_snapshot.into_bytes_with_nul(); + let base_snapshot = base_snapshot.to_cstr(); + let first_snapshot = first_snapshot.to_cstr(); + let second_snapshot = second_snapshot.to_cstr(); let (merge_keys, merge_values): (Vec, Vec) = merge_conflicts .into_iter() .map(|(k, v)| (BnString::new(k), BnString::new(v))) @@ -841,17 +769,17 @@ impl TypeArchive { let success = unsafe { BNTypeArchiveMergeSnapshots( self.handle.as_ptr(), - base_snapshot.as_ref().as_ptr() as *const c_char, - first_snapshot.as_ref().as_ptr() as *const c_char, - second_snapshot.as_ref().as_ptr() as *const c_char, + base_snapshot.as_ptr(), + first_snapshot.as_ptr(), + second_snapshot.as_ptr(), merge_keys_raw, merge_values_raw, merge_keys.len(), &mut conflicts_errors, &mut conflicts_errors_count, &mut result, - Some(P::cb_progress_callback), - &mut progress as *mut P as *mut c_void, + Some(PC::cb_progress_callback), + &mut progress as *mut PC as *mut c_void, ) }; @@ -894,7 +822,7 @@ impl Eq for TypeArchive {} impl Hash for TypeArchive { fn hash(&self, state: &mut H) { - (self.handle.as_ptr() as usize).hash(state); + self.id().hash(state); } } @@ -1140,42 +1068,37 @@ impl TypeArchiveMergeConflict { NonNull::new(value).map(|handle| unsafe { TypeArchive::ref_from_raw(handle) }) } - pub fn type_id(&self) -> BnString { + pub fn type_id(&self) -> String { let value = unsafe { BNTypeArchiveMergeConflictGetTypeId(self.handle.as_ptr()) }; assert!(!value.is_null()); - unsafe { BnString::from_raw(value) } + unsafe { BnString::into_string(value) } } pub fn base_snapshot_id(&self) -> TypeArchiveSnapshotId { let value = unsafe { BNTypeArchiveMergeConflictGetBaseSnapshotId(self.handle.as_ptr()) }; assert!(!value.is_null()); - let id = unsafe { BnString::from_raw(value) }.to_string(); + let id = unsafe { BnString::into_string(value) }; TypeArchiveSnapshotId(id) } pub fn first_snapshot_id(&self) -> TypeArchiveSnapshotId { let value = unsafe { BNTypeArchiveMergeConflictGetFirstSnapshotId(self.handle.as_ptr()) }; assert!(!value.is_null()); - let id = unsafe { BnString::from_raw(value) }.to_string(); + let id = unsafe { BnString::into_string(value) }; TypeArchiveSnapshotId(id) } pub fn second_snapshot_id(&self) -> TypeArchiveSnapshotId { let value = unsafe { BNTypeArchiveMergeConflictGetSecondSnapshotId(self.handle.as_ptr()) }; assert!(!value.is_null()); - let id = unsafe { BnString::from_raw(value) }.to_string(); + let id = unsafe { BnString::into_string(value) }; TypeArchiveSnapshotId(id) } - // TODO: This needs documentation! - pub fn success(&self, value: S) -> bool { - let value = value.into_bytes_with_nul(); - unsafe { - BNTypeArchiveMergeConflictSuccess( - self.handle.as_ptr(), - value.as_ref().as_ptr() as *const c_char, - ) - } + /// Call this when you've resolved the conflict to save the result. + pub fn success(&self, result: &str) -> bool { + let result = result.to_cstr(); + unsafe { BNTypeArchiveMergeConflictSuccess(self.handle.as_ptr(), result.as_ptr()) } } } diff --git a/rust/src/type_container.rs b/rust/src/type_container.rs index 947c6912a0..d68fc9c078 100644 --- a/rust/src/type_container.rs +++ b/rust/src/type_container.rs @@ -11,7 +11,7 @@ use crate::platform::Platform; use crate::progress::{NoProgressCallback, ProgressCallback}; use crate::rc::{Array, Ref}; -use crate::string::{raw_to_string, BnStrCompatible, BnString}; +use crate::string::{raw_to_string, BnString, IntoCStr}; use crate::type_parser::{TypeParserError, TypeParserResult}; use crate::types::{QualifiedName, QualifiedNameAndType, Type}; use binaryninjacore_sys::*; @@ -44,17 +44,17 @@ impl TypeContainer { /// 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 { + pub fn id(&self) -> String { let result = unsafe { BNTypeContainerGetId(self.handle.as_ptr()) }; assert!(!result.is_null()); - unsafe { BnString::from_raw(result) } + unsafe { BnString::into_string(result) } } /// Get a user-friendly name for the Type Container. - pub fn name(&self) -> BnString { + pub fn name(&self) -> String { let result = unsafe { BNTypeContainerGetName(self.handle.as_ptr()) }; assert!(!result.is_null()); - unsafe { BnString::from_raw(result) } + unsafe { BnString::into_string(result) } } /// Get the type of underlying model the Type Container is accessing. @@ -137,20 +137,11 @@ impl TypeContainer { /// (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(); + pub fn rename_type>(&self, name: T, type_id: &str) -> bool { + let type_id = type_id.to_cstr(); 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, - ) - }; + let success = + unsafe { BNTypeContainerRenameType(self.handle.as_ptr(), type_id.as_ptr(), &raw_name) }; QualifiedName::free_raw(raw_name); success } @@ -159,40 +150,31 @@ impl TypeContainer { /// 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, - ) - } + pub fn delete_type(&self, type_id: &str) -> bool { + let type_id = type_id.to_cstr(); + unsafe { BNTypeContainerDeleteType(self.handle.as_ptr(), type_id.as_ptr()) } } /// 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 { + pub fn type_id>(&self, name: T) -> Option { let mut result = std::ptr::null_mut(); 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) }) + success.then(|| unsafe { BnString::into_string(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(); + pub fn type_name(&self, type_id: &str) -> Option { + let type_id = type_id.to_cstr(); 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, - ) + BNTypeContainerGetTypeName(self.handle.as_ptr(), type_id.as_ptr(), &mut result) }; success.then(|| QualifiedName::from_owned_raw(result)) } @@ -200,15 +182,11 @@ impl TypeContainer { /// 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(); + pub fn type_by_id(&self, type_id: &str) -> Option> { + let type_id = type_id.to_cstr(); 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, - ) + BNTypeContainerGetTypeById(self.handle.as_ptr(), type_id.as_ptr(), &mut result) }; success.then(|| unsafe { Type::ref_from_raw(result) }) } @@ -305,19 +283,19 @@ impl TypeContainer { /// /// * `source` - Source code to parse /// * `import_dependencies` - If Type Library / Type Archive types should be imported during parsing - pub fn parse_type_string( + pub fn parse_type_string( &self, - source: S, + source: &str, import_dependencies: bool, ) -> Result> { - let source = source.into_bytes_with_nul(); + let source = source.to_cstr(); 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, + source.as_ptr(), import_dependencies, &mut result, &mut errors, @@ -341,56 +319,43 @@ impl TypeContainer { /// * `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( + pub fn parse_types_from_source( &self, - source: S, - filename: F, + source: &str, + filename: &str, options: O, - include_directories: D, - auto_type_source: A, + include_directories: I, + auto_type_source: &str, import_dependencies: bool, ) -> Result> where - S: BnStrCompatible, - F: BnStrCompatible, - O: IntoIterator, - O::Item: BnStrCompatible, - D: IntoIterator, - D::Item: BnStrCompatible, - A: BnStrCompatible, + O: IntoIterator, + I: IntoIterator, { - 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 source = source.to_cstr(); + let filename = filename.to_cstr(); + let options: Vec<_> = options.into_iter().map(|o| o.to_cstr()).collect(); + let options_raw: Vec<*const c_char> = options.iter().map(|o| o.as_ptr()).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) + .map(|d| d.to_cstr()) .collect(); - let auto_type_source = auto_type_source.into_bytes_with_nul(); + let include_directories_raw: Vec<*const c_char> = + include_directories.iter().map(|d| d.as_ptr()).collect(); + let auto_type_source = auto_type_source.to_cstr(); 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, + source.as_ptr(), + filename.as_ptr(), 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, + auto_type_source.as_ptr(), import_dependencies, &mut raw_result, &mut errors, diff --git a/rust/src/type_library.rs b/rust/src/type_library.rs index ee978513e9..89f5e48f43 100644 --- a/rust/src/type_library.rs +++ b/rust/src/type_library.rs @@ -1,29 +1,30 @@ use binaryninjacore_sys::*; +use std::fmt::{Debug, Formatter}; -use core::{ffi, mem, ptr}; - +use crate::rc::{Guard, RefCountable}; use crate::{ architecture::CoreArchitecture, metadata::Metadata, platform::Platform, rc::{Array, CoreArrayProvider, CoreArrayProviderInner, Ref}, - string::{BnStrCompatible, BnString}, + string::{BnString, IntoCStr}, types::{QualifiedName, QualifiedNameAndType, Type}, }; +use std::path::Path; +use std::ptr::NonNull; #[repr(transparent)] pub struct TypeLibrary { - handle: ptr::NonNull, + handle: NonNull, } impl TypeLibrary { - 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 BNTypeLibrary) -> &Self { - assert!(!handle.is_null()); - mem::transmute(handle) + pub(crate) unsafe fn ref_from_raw(handle: NonNull) -> Ref { + Ref::new(Self { handle }) } #[allow(clippy::mut_from_ref)] @@ -31,22 +32,15 @@ impl TypeLibrary { &mut *self.handle.as_ptr() } - pub fn new_reference(&self) -> Self { - unsafe { - Self::from_raw(ptr::NonNull::new(BNNewTypeLibraryReference(self.as_raw())).unwrap()) - } - } - - pub fn new_duplicated(&self) -> Self { - unsafe { Self::from_raw(ptr::NonNull::new(BNDuplicateTypeLibrary(self.as_raw())).unwrap()) } + pub fn new_duplicated(&self) -> Ref { + unsafe { Self::ref_from_raw(NonNull::new(BNDuplicateTypeLibrary(self.as_raw())).unwrap()) } } /// Creates an empty type library object with a random GUID and the provided name. - pub fn new(arch: CoreArchitecture, name: S) -> TypeLibrary { - let name = name.into_bytes_with_nul(); - let new_lib = - unsafe { BNNewTypeLibrary(arch.handle, name.as_ref().as_ptr() as *const ffi::c_char) }; - unsafe { TypeLibrary::from_raw(ptr::NonNull::new(new_lib).unwrap()) } + pub fn new(arch: CoreArchitecture, name: &str) -> Ref { + let name = name.to_cstr(); + let new_lib = unsafe { BNNewTypeLibrary(arch.handle, name.as_ptr()) }; + unsafe { TypeLibrary::ref_from_raw(NonNull::new(new_lib).unwrap()) } } pub fn all(arch: CoreArchitecture) -> Array { @@ -57,50 +51,45 @@ impl TypeLibrary { } /// Decompresses a type library file to a file on disk. - pub fn decompress_to_file(path: P, output: O) -> bool { - let path = path.into_bytes_with_nul(); - let output = output.into_bytes_with_nul(); - unsafe { - BNTypeLibraryDecompressToFile( - path.as_ref().as_ptr() as *const ffi::c_char, - output.as_ref().as_ptr() as *const ffi::c_char, - ) - } + pub fn decompress_to_file(path: &Path, output_path: &Path) -> bool { + let path = path.to_cstr(); + let output = output_path.to_cstr(); + unsafe { BNTypeLibraryDecompressToFile(path.as_ptr(), output.as_ptr()) } } /// Loads a finalized type library instance from file - pub fn load_from_file(path: S) -> Option { - let path = path.into_bytes_with_nul(); - let handle = - unsafe { BNLoadTypeLibraryFromFile(path.as_ref().as_ptr() as *const ffi::c_char) }; - ptr::NonNull::new(handle).map(|h| unsafe { TypeLibrary::from_raw(h) }) + pub fn load_from_file(path: &Path) -> Option> { + let path = path.to_cstr(); + let handle = unsafe { BNLoadTypeLibraryFromFile(path.as_ptr()) }; + NonNull::new(handle).map(|h| unsafe { TypeLibrary::ref_from_raw(h) }) } /// Saves a finalized type library instance to file - pub fn write_to_file(&self, path: S) -> bool { - let path = path.into_bytes_with_nul(); - unsafe { - BNWriteTypeLibraryToFile(self.as_raw(), path.as_ref().as_ptr() as *const ffi::c_char) - } + pub fn write_to_file(&self, path: &Path) -> bool { + let path = path.to_cstr(); + unsafe { BNWriteTypeLibraryToFile(self.as_raw(), path.as_ptr()) } } /// Looks up the first type library found with a matching name. Keep in mind that names are not /// necessarily unique. - pub fn from_name(arch: CoreArchitecture, name: S) -> Option { - let name = name.into_bytes_with_nul(); - let handle = unsafe { - BNLookupTypeLibraryByName(arch.handle, name.as_ref().as_ptr() as *const ffi::c_char) - }; - ptr::NonNull::new(handle).map(|h| unsafe { TypeLibrary::from_raw(h) }) + /// + /// NOTE: If the type library architecture's associated platform has not been initialized, this will + /// return `None`. To make sure that the platform has been initialized, one should instead get the type + /// libraries through [`Platform::get_type_libraries_by_name`]. + pub fn from_name(arch: CoreArchitecture, name: &str) -> Option> { + let name = name.to_cstr(); + let handle = unsafe { BNLookupTypeLibraryByName(arch.handle, name.as_ptr()) }; + NonNull::new(handle).map(|h| unsafe { TypeLibrary::ref_from_raw(h) }) } - /// Attempts to grab a type library associated with the provided Architecture and GUID pair - pub fn from_guid(arch: CoreArchitecture, guid: S) -> Option { - let guid = guid.into_bytes_with_nul(); - let handle = unsafe { - BNLookupTypeLibraryByGuid(arch.handle, guid.as_ref().as_ptr() as *const ffi::c_char) - }; - ptr::NonNull::new(handle).map(|h| unsafe { TypeLibrary::from_raw(h) }) + /// Attempts to grab a type library associated with the provided Architecture and GUID pair. + /// + /// NOTE: If the associated platform for the architecture has not been initialized, + /// this will return `None`. Avoid calling this outside of a view context. + pub fn from_guid(arch: CoreArchitecture, guid: &str) -> Option> { + let guid = guid.to_cstr(); + let handle = unsafe { BNLookupTypeLibraryByGuid(arch.handle, guid.as_ptr()) }; + NonNull::new(handle).map(|h| unsafe { TypeLibrary::ref_from_raw(h) }) } /// The Architecture this type library is associated with @@ -111,17 +100,16 @@ impl TypeLibrary { } /// The primary name associated with this type library - pub fn name(&self) -> Option { + pub fn name(&self) -> String { let result = unsafe { BNGetTypeLibraryName(self.as_raw()) }; - (!result.is_null()).then(|| unsafe { BnString::from_raw(result) }) + assert!(!result.is_null()); + unsafe { BnString::into_string(result) } } /// Sets the name of a type library instance that has not been finalized - pub fn set_name(&self, value: S) { - let value = value.into_bytes_with_nul(); - unsafe { - BNSetTypeLibraryName(self.as_raw(), value.as_ref().as_ptr() as *const ffi::c_char) - } + pub fn set_name(&self, value: &str) { + let value = value.to_cstr(); + unsafe { BNSetTypeLibraryName(self.as_raw(), value.as_ptr()) } } /// The `dependency_name` of a library is the name used to record dependencies across @@ -129,34 +117,29 @@ impl TypeLibrary { /// dependencies on it recorded as "libc_generic", allowing a type library to be used across /// multiple platforms where each has a specific libc that also provides the name "libc_generic" /// as an `alternate_name`. - pub fn dependency_name(&self) -> Option { + pub fn dependency_name(&self) -> String { let result = unsafe { BNGetTypeLibraryDependencyName(self.as_raw()) }; - (!result.is_null()).then(|| unsafe { BnString::from_raw(result) }) + assert!(!result.is_null()); + unsafe { BnString::into_string(result) } } /// Sets the dependency name of a type library instance that has not been finalized - pub fn set_dependency_name(&self, value: S) { - let value = value.into_bytes_with_nul(); - unsafe { - BNSetTypeLibraryDependencyName( - self.as_raw(), - value.as_ref().as_ptr() as *const ffi::c_char, - ) - } + pub fn set_dependency_name(&self, value: &str) { + let value = value.to_cstr(); + unsafe { BNSetTypeLibraryDependencyName(self.as_raw(), value.as_ptr()) } } /// Returns the GUID associated with the type library - pub fn guid(&self) -> Option { + pub fn guid(&self) -> String { let result = unsafe { BNGetTypeLibraryGuid(self.as_raw()) }; - (!result.is_null()).then(|| unsafe { BnString::from_raw(result) }) + assert!(!result.is_null()); + unsafe { BnString::into_string(result) } } /// Sets the GUID of a type library instance that has not been finalized - pub fn set_guid(&self, value: S) { - let value = value.into_bytes_with_nul(); - unsafe { - BNSetTypeLibraryGuid(self.as_raw(), value.as_ref().as_ptr() as *const ffi::c_char) - } + pub fn set_guid(&self, value: &str) { + let value = value.to_cstr(); + unsafe { BNSetTypeLibraryGuid(self.as_raw(), value.as_ptr()) } } /// A list of extra names that will be considered a match by [Platform::get_type_libraries_by_name] @@ -168,14 +151,9 @@ impl TypeLibrary { } /// Adds an extra name to this type library used during library lookups and dependency resolution - pub fn add_alternate_name(&self, value: S) { - let value = value.into_bytes_with_nul(); - unsafe { - BNAddTypeLibraryAlternateName( - self.as_raw(), - value.as_ref().as_ptr() as *const ffi::c_char, - ) - } + pub fn add_alternate_name(&self, value: &str) { + let value = value.to_cstr(); + unsafe { BNAddTypeLibraryAlternateName(self.as_raw(), value.as_ptr()) } } /// Returns a list of all platform names that this type library will register with during platform @@ -212,12 +190,10 @@ impl TypeLibrary { } /// Retrieves a metadata associated with the given key stored in the type library - pub fn query_metadata(&self, key: S) -> Option { - let key = key.into_bytes_with_nul(); - let result = unsafe { - BNTypeLibraryQueryMetadata(self.as_raw(), key.as_ref().as_ptr() as *const ffi::c_char) - }; - (!result.is_null()).then(|| unsafe { Metadata::from_raw(result) }) + pub fn query_metadata(&self, key: &str) -> Option> { + let key = key.to_cstr(); + let result = unsafe { BNTypeLibraryQueryMetadata(self.as_raw(), key.as_ptr()) }; + (!result.is_null()).then(|| unsafe { Metadata::ref_from_raw(result) }) } /// Stores an object for the given key in the current type library. Objects stored using @@ -231,30 +207,22 @@ impl TypeLibrary { /// /// * `key` - key value to associate the Metadata object with /// * `md` - object to store. - pub fn store_metadata(&self, key: S, md: &Metadata) { - let key = key.into_bytes_with_nul(); - unsafe { - BNTypeLibraryStoreMetadata( - self.as_raw(), - key.as_ref().as_ptr() as *const ffi::c_char, - md.handle, - ) - } + pub fn store_metadata(&self, key: &str, md: &Metadata) { + let key = key.to_cstr(); + unsafe { BNTypeLibraryStoreMetadata(self.as_raw(), key.as_ptr(), md.handle) } } /// Removes the metadata associated with key from the current type library. - pub fn remove_metadata(&self, key: S) { - let key = key.into_bytes_with_nul(); - unsafe { - BNTypeLibraryRemoveMetadata(self.as_raw(), key.as_ref().as_ptr() as *const ffi::c_char) - } + pub fn remove_metadata(&self, key: &str) { + let key = key.to_cstr(); + unsafe { BNTypeLibraryRemoveMetadata(self.as_raw(), key.as_ptr()) } } /// Retrieves the metadata associated with the current type library. - pub fn metadata(&self) -> Metadata { + pub fn metadata(&self) -> Ref { let md_handle = unsafe { BNTypeLibraryGetMetadata(self.as_raw()) }; assert!(!md_handle.is_null()); - unsafe { Metadata::from_raw(md_handle) } + unsafe { Metadata::ref_from_raw(md_handle) } } // TODO: implement TypeContainer @@ -262,7 +230,7 @@ impl TypeLibrary { // /// The Type Container's Platform will be the first platform associated with the Type Library. // pub fn type_container(&self) -> TypeContainer { // let result = unsafe{ BNGetTypeLibraryTypeContainer(self.as_raw())}; - // unsafe{TypeContainer::from_raw(ptr::NonNull::new(result).unwrap())} + // unsafe{TypeContainer::from_raw(NonNull::new(result).unwrap())} // } /// Directly inserts a named object into the type library's object store. @@ -299,16 +267,10 @@ impl TypeLibrary { /// Use this api with extreme caution. /// /// - pub fn add_type_source(&self, name: QualifiedName, source: S) { - let source = source.into_bytes_with_nul(); + pub fn add_type_source(&self, name: QualifiedName, source: &str) { + let source = source.to_cstr(); let mut raw_name = QualifiedName::into_raw(name); - unsafe { - BNAddTypeLibraryNamedTypeSource( - self.as_raw(), - &mut raw_name, - source.as_ref().as_ptr() as *const ffi::c_char, - ) - } + unsafe { BNAddTypeLibraryNamedTypeSource(self.as_raw(), &mut raw_name, source.as_ptr()) } QualifiedName::free_raw(raw_name); } @@ -349,16 +311,47 @@ impl TypeLibrary { } } -impl Drop for TypeLibrary { - fn drop(&mut self) { - unsafe { BNFreeTypeLibrary(self.as_raw()) } +impl Debug for TypeLibrary { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + f.debug_struct("TypeLibrary") + .field("name", &self.name()) + .field("dependency_name", &self.dependency_name()) + .field("arch", &self.arch()) + .field("guid", &self.guid()) + .field("alternate_names", &self.alternate_names().to_vec()) + .field("platform_names", &self.platform_names().to_vec()) + .field("metadata", &self.metadata()) + // These two are too verbose. + // .field("named_objects", &self.named_objects().to_vec()) + // .field("named_types", &self.named_types().to_vec()) + .finish() + } +} + +unsafe impl RefCountable for TypeLibrary { + unsafe fn inc_ref(handle: &Self) -> Ref { + Ref::new(Self { + handle: NonNull::new(BNNewTypeLibraryReference(handle.handle.as_ptr())).unwrap(), + }) + } + + unsafe fn dec_ref(handle: &Self) { + BNFreeTypeLibrary(handle.handle.as_ptr()); + } +} + +impl ToOwned for TypeLibrary { + type Owned = Ref; + + fn to_owned(&self) -> Self::Owned { + unsafe { RefCountable::inc_ref(self) } } } impl CoreArrayProvider for TypeLibrary { type Raw = *mut BNTypeLibrary; type Context = (); - type Wrapped<'a> = &'a Self; + type Wrapped<'a> = Guard<'a, Self>; } unsafe impl CoreArrayProviderInner for TypeLibrary { @@ -366,7 +359,7 @@ unsafe impl CoreArrayProviderInner for TypeLibrary { BNFreeTypeLibraryList(raw, count) } - unsafe fn wrap_raw<'a>(raw: &'a Self::Raw, _context: &'a Self::Context) -> Self::Wrapped<'a> { - Self::ref_from_raw(raw) + unsafe fn wrap_raw<'a>(raw: &'a Self::Raw, context: &'a Self::Context) -> Self::Wrapped<'a> { + Guard::new(Self::from_raw(NonNull::new(*raw).unwrap()), context) } } diff --git a/rust/src/type_parser.rs b/rust/src/type_parser.rs index 656f0c9d46..13985c4434 100644 --- a/rust/src/type_parser.rs +++ b/rust/src/type_parser.rs @@ -6,7 +6,7 @@ use std::ptr::NonNull; use crate::platform::Platform; use crate::rc::{Array, CoreArrayProvider, CoreArrayProviderInner, Ref}; -use crate::string::{raw_to_string, BnStrCompatible, BnString}; +use crate::string::{raw_to_string, BnString, IntoCStr}; use crate::type_container::TypeContainer; use crate::types::{QualifiedName, QualifiedNameAndType, Type}; @@ -14,8 +14,8 @@ pub type TypeParserErrorSeverity = BNTypeParserErrorSeverity; pub type TypeParserOption = BNTypeParserOption; /// Register a custom parser with the API -pub fn register_type_parser( - name: S, +pub fn register_type_parser( + name: &str, parser: T, ) -> (&'static mut T, CoreTypeParser) { let parser = Box::leak(Box::new(parser)); @@ -29,19 +29,15 @@ pub fn register_type_parser( 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 name = name.to_cstr(); + let result = unsafe { BNRegisterTypeParser(name.as_ptr(), &mut callback) }; let core = unsafe { CoreTypeParser::from_raw(NonNull::new(result).unwrap()) }; (parser, core) } #[repr(transparent)] pub struct CoreTypeParser { - handle: NonNull, + pub(crate) handle: NonNull, } impl CoreTypeParser { @@ -55,34 +51,29 @@ impl CoreTypeParser { unsafe { Array::new(result, count, ()) } } - 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 c_char) }; + pub fn parser_by_name(name: &str) -> Option { + let name_raw = name.to_cstr(); + let result = unsafe { BNGetTypeParserByName(name_raw.as_ptr()) }; NonNull::new(result).map(|x| unsafe { Self::from_raw(x) }) } - pub fn name(&self) -> BnString { + pub fn name(&self) -> String { let result = unsafe { BNGetTypeParserName(self.handle.as_ptr()) }; assert!(!result.is_null()); - unsafe { BnString::from_raw(result) } + unsafe { BnString::into_string(result) } } } 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 value_ptr = std::ptr::null_mut(); let result = unsafe { - BNGetTypeParserOptionText( - self.handle.as_ptr(), - option, - value_cstr.as_ptr(), - &mut output, - ) + BNGetTypeParserOptionText(self.handle.as_ptr(), option, value_ptr, &mut output) }; result.then(|| { assert!(!output.is_null()); - value_cstr.to_string() + unsafe { BnString::into_string(value_ptr) } }) } @@ -118,8 +109,8 @@ impl TypeParser for CoreTypeParser { }; if success { assert!(!result.is_null()); - let bn_result = unsafe { BnString::from_raw(result) }; - Ok(bn_result.to_string()) + let bn_result = unsafe { BnString::into_string(result) }; + Ok(bn_result) } else { let errors: Array = unsafe { Array::new(errors, error_count, ()) }; Err(errors.to_vec()) @@ -323,8 +314,8 @@ impl TypeParserError { } pub(crate) fn free_raw(value: BNTypeParserError) { - let _ = unsafe { BnString::from_raw(value.message) }; - let _ = unsafe { BnString::from_raw(value.fileName) }; + unsafe { BnString::free_raw(value.message) }; + unsafe { BnString::free_raw(value.fileName) }; } pub fn new( @@ -677,7 +668,7 @@ unsafe extern "C" fn cb_parse_type_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); + BnString::free_raw(string); } unsafe extern "C" fn cb_free_result(_ctxt: *mut c_void, result: *mut BNTypeParserResult) { diff --git a/rust/src/type_printer.rs b/rust/src/type_printer.rs index 12c7164580..a7495f1c16 100644 --- a/rust/src/type_printer.rs +++ b/rust/src/type_printer.rs @@ -4,7 +4,7 @@ use crate::binary_view::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::string::{raw_to_string, BnString, IntoCStr}; use crate::type_container::TypeContainer; use crate::types::{NamedTypeReference, QualifiedName, QualifiedNameAndType, Type}; use binaryninjacore_sys::*; @@ -15,8 +15,8 @@ pub type TokenEscapingType = BNTokenEscapingType; pub type TypeDefinitionLineType = BNTypeDefinitionLineType; /// Register a custom parser with the API -pub fn register_type_printer( - name: S, +pub fn register_type_printer( + name: &str, parser: T, ) -> (&'static mut T, CoreTypePrinter) { let parser = Box::leak(Box::new(parser)); @@ -34,19 +34,15 @@ pub fn register_type_printer( 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 raw_name = name.to_cstr(); + let result = unsafe { BNRegisterTypePrinter(raw_name.as_ptr(), &mut callback) }; let core = unsafe { CoreTypePrinter::from_raw(NonNull::new(result).unwrap()) }; (parser, core) } #[repr(transparent)] pub struct CoreTypePrinter { - handle: NonNull, + pub(crate) handle: NonNull, } impl CoreTypePrinter { @@ -61,16 +57,16 @@ impl CoreTypePrinter { 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) }; + pub fn printer_by_name(name: &str) -> Option { + let name_raw = name.to_cstr(); + let result = unsafe { BNGetTypePrinterByName(name_raw.as_ptr()) }; NonNull::new(result).map(|x| unsafe { Self::from_raw(x) }) } - pub fn name(&self) -> BnString { + pub fn name(&self) -> String { let result = unsafe { BNGetTypePrinterName(self.handle.as_ptr()) }; assert!(!result.is_null()); - unsafe { BnString::from_raw(result) } + unsafe { BnString::into_string(result) } } pub fn get_type_tokens>( @@ -367,7 +363,7 @@ impl Default for CoreTypePrinter { // TODO: Remove this entirely, there is no "default", its view specific lets not make this some defined behavior. let default_settings = crate::settings::Settings::new(); let name = default_settings.get_string("analysis.types.printerName"); - Self::printer_by_name(name).unwrap() + Self::printer_by_name(&name).unwrap() } } @@ -951,7 +947,7 @@ unsafe extern "C" fn cb_print_all_types( 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); + BnString::free_raw(string); } unsafe extern "C" fn cb_free_tokens( diff --git a/rust/src/types.rs b/rust/src/types.rs index 9d1353bd96..5a39eb275e 100644 --- a/rust/src/types.rs +++ b/rust/src/types.rs @@ -24,7 +24,7 @@ use crate::{ binary_view::{BinaryView, BinaryViewExt}, calling_convention::CoreCallingConvention, rc::*, - string::{BnStrCompatible, BnString}, + string::{BnString, IntoCStr}, }; use crate::confidence::{Conf, MAX_CONFIDENCE, MIN_CONFIDENCE}; @@ -258,10 +258,10 @@ impl TypeBuilder { } } - pub fn named_int(width: usize, is_signed: bool, alt_name: S) -> Self { + pub fn named_int(width: usize, is_signed: bool, alt_name: &str) -> Self { 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 + let alt_name = alt_name.to_cstr(); // 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 unsafe { Self::from_raw(BNCreateIntegerTypeBuilder( @@ -273,24 +273,12 @@ impl TypeBuilder { } pub fn float(width: usize) -> Self { - unsafe { - Self::from_raw(BNCreateFloatTypeBuilder( - width, - BnString::new("").as_ptr() as *mut _, - )) - } + unsafe { Self::from_raw(BNCreateFloatTypeBuilder(width, c"".as_ptr())) } } - pub fn named_float(width: usize, alt_name: S) -> Self { - // let alt_name = BnString::new(alt_name); - let alt_name = alt_name.into_bytes_with_nul(); // See same line in `named_int` above - - unsafe { - Self::from_raw(BNCreateFloatTypeBuilder( - width, - alt_name.as_ref().as_ptr() as _, - )) - } + pub fn named_float(width: usize, alt_name: &str) -> Self { + let alt_name = alt_name.to_cstr(); + unsafe { Self::from_raw(BNCreateFloatTypeBuilder(width, alt_name.as_ptr())) } } pub fn array<'a, T: Into>>(ty: T, count: u64) -> Self { @@ -430,7 +418,7 @@ impl TypeBuilder { impl Display for TypeBuilder { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { write!(f, "{}", unsafe { - BnString::from_raw(BNGetTypeBuilderString(self.handle, std::ptr::null_mut())) + BnString::into_string(BNGetTypeBuilderString(self.handle, std::ptr::null_mut())) }) } } @@ -630,53 +618,34 @@ impl Type { } pub fn wide_char(width: usize) -> Ref { - unsafe { - Self::ref_from_raw(BNCreateWideCharType( - width, - BnString::new("").as_ptr() as *mut _, - )) - } + unsafe { Self::ref_from_raw(BNCreateWideCharType(width, c"".as_ptr())) } } pub fn int(width: usize, is_signed: bool) -> Ref { let mut is_signed = Conf::new(is_signed, MAX_CONFIDENCE).into(); - unsafe { - Self::ref_from_raw(BNCreateIntegerType( - width, - &mut is_signed, - BnString::new("").as_ptr() as *mut _, - )) - } + unsafe { Self::ref_from_raw(BNCreateIntegerType(width, &mut is_signed, c"".as_ptr())) } } - pub fn named_int(width: usize, is_signed: bool, alt_name: S) -> Ref { + pub fn named_int(width: usize, is_signed: bool, alt_name: &str) -> Ref { 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 + let alt_name = alt_name.to_cstr(); unsafe { Self::ref_from_raw(BNCreateIntegerType( width, &mut is_signed, - alt_name.as_ref().as_ptr() as _, + alt_name.as_ptr(), )) } } pub fn float(width: usize) -> Ref { - unsafe { - Self::ref_from_raw(BNCreateFloatType( - width, - BnString::new("").as_ptr() as *mut _, - )) - } + unsafe { Self::ref_from_raw(BNCreateFloatType(width, c"".as_ptr())) } } - pub fn named_float(width: usize, alt_name: S) -> Ref { - // let alt_name = BnString::new(alt_name); - let alt_name = alt_name.into_bytes_with_nul(); // See same line in `named_int` above - - unsafe { Self::ref_from_raw(BNCreateFloatType(width, alt_name.as_ref().as_ptr() as _)) } + pub fn named_float(width: usize, alt_name: &str) -> Ref { + let alt_name = alt_name.to_cstr(); + unsafe { Self::ref_from_raw(BNCreateFloatType(width, alt_name.as_ptr())) } } pub fn array<'a, T: Into>>(ty: T, count: u64) -> Ref { @@ -932,9 +901,10 @@ impl Type { } } - pub fn generate_auto_demangled_type_id>(name: T) -> BnString { + pub fn generate_auto_demangled_type_id>(name: T) -> String { let mut raw_name = QualifiedName::into_raw(name.into()); - let type_id = unsafe { BnString::from_raw(BNGenerateAutoDemangledTypeId(&mut raw_name)) }; + let type_id = + unsafe { BnString::into_string(BNGenerateAutoDemangledTypeId(&mut raw_name)) }; QualifiedName::free_raw(raw_name); type_id } @@ -943,7 +913,7 @@ impl Type { impl Display for Type { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { write!(f, "{}", unsafe { - BnString::from_raw(BNGetTypeString( + BnString::into_string(BNGetTypeString( self.handle, std::ptr::null_mut(), BNTokenEscapingType::NoTokenEscapingType, @@ -1109,7 +1079,7 @@ impl FunctionParameter { } pub(crate) fn free_raw(value: BNFunctionParameter) { - let _ = unsafe { BnString::from_raw(value.name) }; + unsafe { BnString::free_raw(value.name) }; let _ = unsafe { Type::ref_from_raw(value.type_) }; } @@ -1184,7 +1154,7 @@ impl EnumerationMember { } pub(crate) fn free_raw(value: BNEnumerationMember) { - let _ = unsafe { BnString::from_raw(value.name) }; + unsafe { BnString::free_raw(value.name) }; } pub fn new(name: String, value: u64, default: bool) -> Self { @@ -1216,24 +1186,24 @@ impl EnumerationBuilder { unsafe { Enumeration::ref_from_raw(BNFinalizeEnumerationBuilder(self.handle)) } } - pub fn append(&mut self, name: S) -> &mut Self { - let name = name.into_bytes_with_nul(); + pub fn append(&mut self, name: &str) -> &mut Self { + let name = name.to_cstr(); unsafe { BNAddEnumerationBuilderMember(self.handle, name.as_ref().as_ptr() as _); } self } - pub fn insert(&mut self, name: S, value: u64) -> &mut Self { - let name = name.into_bytes_with_nul(); + pub fn insert(&mut self, name: &str, value: u64) -> &mut Self { + let name = name.to_cstr(); unsafe { BNAddEnumerationBuilderMemberWithValue(self.handle, name.as_ref().as_ptr() as _, value); } self } - pub fn replace(&mut self, id: usize, name: S, value: u64) -> &mut Self { - let name = name.into_bytes_with_nul(); + pub fn replace(&mut self, id: usize, name: &str, value: u64) -> &mut Self { + let name = name.to_cstr(); unsafe { BNReplaceEnumerationBuilderMember(self.handle, id, name.as_ref().as_ptr() as _, value); } @@ -1475,14 +1445,14 @@ impl StructureBuilder { self } - pub fn append<'a, S: BnStrCompatible, T: Into>>( + pub fn append<'a, T: Into>>( &mut self, ty: T, - name: S, + name: &str, access: MemberAccess, scope: MemberScope, ) -> &mut Self { - let name = name.into_bytes_with_nul(); + let name = name.to_cstr(); let owned_raw_ty = Conf::<&Type>::into_raw(ty.into()); unsafe { BNAddStructureBuilderMember( @@ -1503,7 +1473,7 @@ impl StructureBuilder { ) -> &mut Self { self.insert( &member.ty, - member.name, + &member.name, member.offset, overwrite_existing, member.access, @@ -1512,16 +1482,16 @@ impl StructureBuilder { self } - pub fn insert<'a, S: BnStrCompatible, T: Into>>( + pub fn insert<'a, T: Into>>( &mut self, ty: T, - name: S, + name: &str, offset: u64, overwrite_existing: bool, access: MemberAccess, scope: MemberScope, ) -> &mut Self { - let name = name.into_bytes_with_nul(); + let name = name.to_cstr(); let owned_raw_ty = Conf::<&Type>::into_raw(ty.into()); unsafe { BNAddStructureBuilderMemberAtOffset( @@ -1537,14 +1507,14 @@ impl StructureBuilder { self } - pub fn replace<'a, S: BnStrCompatible, T: Into>>( + pub fn replace<'a, T: Into>>( &mut self, index: usize, ty: T, - name: S, + name: &str, overwrite_existing: bool, ) -> &mut Self { - let name = name.into_bytes_with_nul(); + let name = name.to_cstr(); let owned_raw_ty = Conf::<&Type>::into_raw(ty.into()); unsafe { BNReplaceStructureBuilderMember( @@ -1723,7 +1693,7 @@ impl StructureMember { pub(crate) fn free_raw(value: BNStructureMember) { let _ = unsafe { Type::ref_from_raw(value.type_) }; - let _ = unsafe { BnString::from_raw(value.name) }; + unsafe { BnString::free_raw(value.name) }; } pub fn new( @@ -1869,12 +1839,12 @@ 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, S: BnStrCompatible>( + pub fn new_with_id>( type_class: NamedTypeReferenceClass, - type_id: S, + type_id: &str, name: T, ) -> Ref { - let type_id = type_id.into_bytes_with_nul(); + let type_id = type_id.to_cstr(); let mut raw_name = QualifiedName::into_raw(name.into()); let result = unsafe { Self::ref_from_raw(BNCreateNamedType( @@ -1892,16 +1862,16 @@ impl NamedTypeReference { QualifiedName::from_owned_raw(raw_name) } - pub fn id(&self) -> BnString { - unsafe { BnString::from_raw(BNGetTypeReferenceId(self.handle)) } + pub fn id(&self) -> String { + unsafe { BnString::into_string(BNGetTypeReferenceId(self.handle)) } } pub fn class(&self) -> NamedTypeReferenceClass { unsafe { BNGetTypeReferenceClass(self.handle) } } - fn target_helper(&self, bv: &BinaryView, visited: &mut HashSet) -> Option> { - let ty = bv.type_by_id(self.id())?; + fn target_helper(&self, bv: &BinaryView, visited: &mut HashSet) -> Option> { + let ty = bv.type_by_id(&self.id())?; match ty.type_class() { TypeClass::NamedTypeReferenceClass => { // Recurse into the NTR type until we get the target type. @@ -1987,7 +1957,7 @@ impl QualifiedName { } pub(crate) fn free_raw(value: BNQualifiedName) { - unsafe { BNFreeString(value.join) }; + unsafe { BnString::free_raw(value.join) }; unsafe { BNFreeStringList(value.name, value.nameCount) }; } @@ -2334,7 +2304,7 @@ impl NameAndType { } pub(crate) fn free_raw(value: BNNameAndType) { - let _ = unsafe { BnString::from_raw(value.name) }; + unsafe { BnString::free_raw(value.name) }; let _ = unsafe { Type::ref_from_raw(value.type_) }; } diff --git a/rust/src/update.rs b/rust/src/update.rs index 2a22c9c9f5..48d841fb99 100644 --- a/rust/src/update.rs +++ b/rust/src/update.rs @@ -1,10 +1,10 @@ #![allow(dead_code)] -use std::ffi::{c_char, c_void}; +use std::ffi::c_void; use std::time::{Duration, SystemTime, UNIX_EPOCH}; use crate::progress::{NoProgressCallback, ProgressCallback}; use crate::rc::{Array, CoreArrayProvider, CoreArrayProviderInner}; -use crate::string::{raw_to_string, BnString}; +use crate::string::{raw_to_string, BnString, IntoCStr}; use binaryninjacore_sys::*; pub type UpdateResult = BNUpdateResult; @@ -75,9 +75,9 @@ impl UpdateChannel { } pub(crate) fn free_raw(value: BNUpdateChannel) { - let _ = unsafe { BnString::from_raw(value.name) }; - let _ = unsafe { BnString::from_raw(value.description) }; - let _ = unsafe { BnString::from_raw(value.latestVersion) }; + unsafe { BnString::free_raw(value.name) }; + unsafe { BnString::free_raw(value.description) }; + unsafe { BnString::free_raw(value.latestVersion) }; } pub fn all() -> Result, BnString> { @@ -96,9 +96,8 @@ impl UpdateChannel { pub fn versions(&self) -> Result, BnString> { let mut count = 0; let mut errors = std::ptr::null_mut(); - let result = unsafe { - BNGetUpdateChannelVersions(self.name.as_ptr() as *const c_char, &mut count, &mut errors) - }; + let name = self.name.clone().to_cstr(); + let result = unsafe { BNGetUpdateChannelVersions(name.as_ptr(), &mut count, &mut errors) }; if !errors.is_null() { Err(unsafe { BnString::from_raw(errors) }) } else { @@ -122,9 +121,10 @@ impl UpdateChannel { /// Whether updates are available pub fn updates_available(&self) -> Result { let mut errors = std::ptr::null_mut(); + let name = self.name.clone().to_cstr(); let result = unsafe { BNAreUpdatesAvailable( - self.name.as_ptr() as *const c_char, + name.as_ptr(), std::ptr::null_mut(), std::ptr::null_mut(), &mut errors, @@ -147,9 +147,10 @@ impl UpdateChannel { ) -> Result { let mut errors = std::ptr::null_mut(); + let name = self.name.clone().to_cstr(); let result = unsafe { BNUpdateToLatestVersion( - self.name.as_ptr() as *const c_char, + name.as_ptr(), &mut errors, Some(P::cb_progress_callback), &mut progress as *mut P as *mut c_void, @@ -174,10 +175,12 @@ impl UpdateChannel { ) -> Result { let mut errors = std::ptr::null_mut(); + let name = self.name.clone().to_cstr(); + let version = version.version.clone().to_cstr(); let result = unsafe { BNUpdateToVersion( - self.name.as_ptr() as *const c_char, - version.version.as_ptr() as *const c_char, + name.as_ptr(), + version.as_ptr(), &mut errors, Some(P::cb_progress_callback), &mut progress as *mut P as *mut c_void, @@ -242,8 +245,8 @@ impl UpdateVersion { } pub(crate) fn free_raw(value: BNUpdateVersion) { - let _ = unsafe { BnString::from_raw(value.version) }; - let _ = unsafe { BnString::from_raw(value.notes) }; + unsafe { BnString::free_raw(value.version) }; + unsafe { BnString::free_raw(value.notes) }; } } diff --git a/rust/src/variable.rs b/rust/src/variable.rs index f6b0f52241..c0cab7d51f 100644 --- a/rust/src/variable.rs +++ b/rust/src/variable.rs @@ -186,7 +186,7 @@ impl NamedVariableWithType { pub(crate) fn free_raw(value: BNVariableNameAndType) { let _ = unsafe { Type::ref_from_raw(value.type_) }; - let _ = unsafe { BnString::from_raw(value.name) }; + unsafe { BnString::free_raw(value.name) }; } pub fn new(variable: Variable, ty: Conf>, name: String, auto_defined: bool) -> Self { @@ -307,7 +307,7 @@ impl StackVariableReference { pub(crate) fn free_raw(value: BNStackVariableReference) { let _ = unsafe { Type::ref_from_raw(value.type_) }; - let _ = unsafe { BnString::from_raw(value.name) }; + unsafe { BnString::free_raw(value.name) }; } } diff --git a/rust/src/websocket.rs b/rust/src/websocket.rs index cc1b5f7740..4acf10a6b5 100644 --- a/rust/src/websocket.rs +++ b/rust/src/websocket.rs @@ -1,7 +1,7 @@ //! Interface for registering new websocket providers //! //! WARNING: Do _not_ use this for anything other than provider registration. If you need to open a -//! websocket connection use a real websocket library. +//! websocket connection, use a real websocket library. mod client; mod provider; diff --git a/rust/src/websocket/client.rs b/rust/src/websocket/client.rs index 36c7bedd20..fc56dc0ba9 100644 --- a/rust/src/websocket/client.rs +++ b/rust/src/websocket/client.rs @@ -1,5 +1,5 @@ use crate::rc::{Ref, RefCountable}; -use crate::string::{BnStrCompatible, BnString}; +use crate::string::{BnString, IntoCStr}; use binaryninjacore_sys::*; use std::ffi::{c_char, c_void, CStr}; use std::ptr::NonNull; @@ -18,11 +18,9 @@ pub trait WebsocketClient: Sync + Send { /// Called to construct this client object with the given core object. fn from_core(core: Ref) -> Self; - fn connect(&self, host: &str, headers: I) -> bool + fn connect(&self, host: &str, headers: I) -> bool where - I: IntoIterator, - K: BnStrCompatible, - V: BnStrCompatible; + I: IntoIterator; fn write(&self, data: &[u8]) -> bool; @@ -69,31 +67,18 @@ impl CoreWebsocketClient { /// * `host` - Full url with scheme, domain, optionally port, and path /// * `headers` - HTTP header keys and values /// * `callback` - Callbacks for various websocket events - pub fn initialize_connection( - &self, - host: &str, - headers: I, - callbacks: &mut C, - ) -> bool + pub fn initialize_connection(&self, host: &str, headers: I, callbacks: &mut C) -> bool where - I: IntoIterator, - K: BnStrCompatible, - V: BnStrCompatible, + I: IntoIterator, C: WebsocketClientCallback, { - let url = host.into_bytes_with_nul(); - let (header_keys, header_values): (Vec, Vec) = headers + let url = host.to_cstr(); + let (header_keys, header_values): (Vec<_>, Vec<_>) = headers .into_iter() - .map(|(k, v)| (k.into_bytes_with_nul(), v.into_bytes_with_nul())) + .map(|(k, v)| (k.to_cstr(), v.to_cstr())) .unzip(); - let header_keys: Vec<*const c_char> = header_keys - .iter() - .map(|k| k.as_ref().as_ptr() as *const c_char) - .collect(); - let header_values: Vec<*const c_char> = header_values - .iter() - .map(|v| v.as_ref().as_ptr() as *const c_char) - .collect(); + let header_keys: Vec<*const c_char> = header_keys.iter().map(|k| k.as_ptr()).collect(); + let header_values: Vec<*const c_char> = header_values.iter().map(|v| v.as_ptr()).collect(); // SAFETY: This context will only be live for the duration of BNConnectWebsocketClient // SAFETY: Any subsequent call to BNConnectWebsocketClient will write over the context. let mut output_callbacks = BNWebsocketClientOutputCallbacks { @@ -106,7 +91,7 @@ impl CoreWebsocketClient { unsafe { BNConnectWebsocketClient( self.handle.as_ptr(), - url.as_ptr() as *const c_char, + url.as_ptr(), header_keys.len().try_into().unwrap(), header_keys.as_ptr(), header_values.as_ptr(), @@ -129,10 +114,8 @@ impl CoreWebsocketClient { /// Call the error callback function pub fn notify_error(&self, msg: &str) { - let error = msg.into_bytes_with_nul(); - unsafe { - BNNotifyWebsocketClientError(self.handle.as_ptr(), error.as_ptr() as *const c_char) - } + let error = msg.to_cstr(); + unsafe { BNNotifyWebsocketClientError(self.handle.as_ptr(), error.as_ptr()) } } /// Call the read callback function, forward the callback returned value @@ -195,8 +178,10 @@ pub(crate) unsafe extern "C" fn cb_connect( let header_count = usize::try_from(header_count).unwrap(); let header_keys = core::slice::from_raw_parts(header_keys as *const BnString, header_count); let header_values = core::slice::from_raw_parts(header_values as *const BnString, header_count); - let header_keys_str = header_keys.iter().map(|s| s.to_string_lossy()); - let header_values_str = header_values.iter().map(|s| s.to_string_lossy()); + let header_keys_str = header_keys.iter().map(|s| s.to_string_lossy().to_string()); + let header_values_str = header_values + .iter() + .map(|s| s.to_string_lossy().to_string()); let header = header_keys_str.zip(header_values_str); ctxt.connect(&host.to_string_lossy(), header) } diff --git a/rust/src/websocket/provider.rs b/rust/src/websocket/provider.rs index 0e28afe45c..914a3f165c 100644 --- a/rust/src/websocket/provider.rs +++ b/rust/src/websocket/provider.rs @@ -1,9 +1,9 @@ use crate::rc::{Array, CoreArrayProvider, CoreArrayProviderInner, Ref}; -use crate::string::{BnStrCompatible, BnString}; +use crate::string::{BnString, IntoCStr}; use crate::websocket::client; use crate::websocket::client::{CoreWebsocketClient, WebsocketClient}; use binaryninjacore_sys::*; -use std::ffi::{c_char, c_void}; +use std::ffi::c_void; use std::mem::MaybeUninit; use std::ptr::NonNull; @@ -11,13 +11,13 @@ pub fn register_websocket_provider(name: &str) -> &'static mut W where W: WebsocketProvider, { - let name = name.into_bytes_with_nul(); + let name = name.to_cstr(); let provider_uninit = MaybeUninit::uninit(); // SAFETY: Websocket provider is never freed let leaked_provider = Box::leak(Box::new(provider_uninit)); let result = unsafe { BNRegisterWebsocketProvider( - name.as_ptr() as *const c_char, + name.as_ptr(), &mut BNWebsocketProviderCallbacks { context: leaked_provider as *mut _ as *mut c_void, createClient: Some(cb_create_client::), @@ -80,17 +80,16 @@ impl CoreWebsocketProvider { unsafe { Array::new(result, count, ()) } } - pub fn by_name(name: S) -> Option { - let name = name.into_bytes_with_nul(); - let result = - unsafe { BNGetWebsocketProviderByName(name.as_ref().as_ptr() as *const c_char) }; + pub fn by_name(name: &str) -> Option { + let name = name.to_cstr(); + let result = unsafe { BNGetWebsocketProviderByName(name.as_ptr()) }; NonNull::new(result).map(|h| unsafe { Self::from_raw(h) }) } - pub fn name(&self) -> BnString { + pub fn name(&self) -> String { let result = unsafe { BNGetWebsocketProviderName(self.handle.as_ptr()) }; assert!(!result.is_null()); - unsafe { BnString::from_raw(result) } + unsafe { BnString::into_string(result) } } } diff --git a/rust/src/worker_thread.rs b/rust/src/worker_thread.rs index 349456e5f3..113a7d0b01 100644 --- a/rust/src/worker_thread.rs +++ b/rust/src/worker_thread.rs @@ -1,6 +1,6 @@ -use crate::string::BnStrCompatible; +use crate::string::IntoCStr; use binaryninjacore_sys::*; -use std::ffi::{c_char, c_void}; +use std::ffi::c_void; pub struct WorkerThreadActionExecutor { func: Box, @@ -17,41 +17,41 @@ impl WorkerThreadActionExecutor { } } -pub fn execute_on_worker_thread(name: S, f: F) { +pub fn execute_on_worker_thread(name: &str, f: F) { let boxed_executor = Box::new(WorkerThreadActionExecutor { func: Box::new(f) }); let raw_executor = Box::into_raw(boxed_executor); - let name = name.into_bytes_with_nul(); + let name = name.to_cstr(); unsafe { BNWorkerEnqueueNamed( raw_executor as *mut c_void, Some(WorkerThreadActionExecutor::cb_execute), - name.as_ref().as_ptr() as *const c_char, + name.as_ptr(), ) } } -pub fn execute_on_worker_thread_priority(name: S, f: F) { +pub fn execute_on_worker_thread_priority(name: &str, f: F) { let boxed_executor = Box::new(WorkerThreadActionExecutor { func: Box::new(f) }); let raw_executor = Box::into_raw(boxed_executor); - let name = name.into_bytes_with_nul(); + let name = name.to_cstr(); unsafe { BNWorkerPriorityEnqueueNamed( raw_executor as *mut c_void, Some(WorkerThreadActionExecutor::cb_execute), - name.as_ref().as_ptr() as *const c_char, + name.as_ptr(), ) } } -pub fn execute_on_worker_thread_interactive(name: S, f: F) { +pub fn execute_on_worker_thread_interactive(name: &str, f: F) { let boxed_executor = Box::new(WorkerThreadActionExecutor { func: Box::new(f) }); let raw_executor = Box::into_raw(boxed_executor); - let name = name.into_bytes_with_nul(); + let name = name.to_cstr(); unsafe { BNWorkerInteractiveEnqueueNamed( raw_executor as *mut c_void, Some(WorkerThreadActionExecutor::cb_execute), - name.as_ref().as_ptr() as *const c_char, + name.as_ptr(), ) } } diff --git a/rust/src/workflow.rs b/rust/src/workflow.rs index b9c67a5df9..3dcb104251 100644 --- a/rust/src/workflow.rs +++ b/rust/src/workflow.rs @@ -1,18 +1,16 @@ use binaryninjacore_sys::*; -use std::ffi::{c_char, c_void}; -use std::ptr::NonNull; -use crate::architecture::CoreArchitecture; use crate::basic_block::BasicBlock; use crate::binary_view::BinaryView; use crate::flowgraph::FlowGraph; use crate::function::{Function, NativeBlock}; use crate::high_level_il::HighLevelILFunction; -use crate::low_level_il::function::{LowLevelILFunction, Mutable, NonSSA, NonSSAVariant}; -use crate::low_level_il::MutableLiftedILFunction; +use crate::low_level_il::{LowLevelILMutableFunction, LowLevelILRegularFunction}; use crate::medium_level_il::MediumLevelILFunction; use crate::rc::{Array, CoreArrayProvider, CoreArrayProviderInner, Guard, Ref, RefCountable}; -use crate::string::{BnStrCompatible, BnString}; +use crate::string::{BnString, IntoCStr}; +use std::ffi::{c_char, c_void}; +use std::ptr::NonNull; #[repr(transparent)] /// The AnalysisContext struct is used to represent the current state of @@ -46,47 +44,32 @@ impl AnalysisContext { unsafe { Function::ref_from_raw(result) } } - /// [`LowLevelILFunction`] used to represent Low Level IL - pub unsafe fn lifted_il_function( - &self, - ) -> Option>> { + /// [`LowLevelILMutableFunction`] used to represent Lifted Level IL + pub unsafe fn lifted_il_function(&self) -> Option> { let func = self.function(); let result = unsafe { BNGetFunctionLiftedIL(func.handle) }; - let arch = self.function().arch(); unsafe { - Some(LowLevelILFunction::ref_from_raw( - arch, + Some(LowLevelILMutableFunction::ref_from_raw( NonNull::new(result)?.as_ptr(), )) } } - pub fn set_lifted_il_function(&self, value: &MutableLiftedILFunction) { + pub fn set_lifted_il_function(&self, value: &LowLevelILRegularFunction) { unsafe { BNSetLiftedILFunction(self.handle.as_ptr(), value.handle) } } - // TODO: This returns LiftedNonSSA because the lifting code was written before we could patch the IL - // TODO: At some point we need to take the lifting code and make it available to regular IL. - /// [`LowLevelILFunction`] used to represent Low Level IL - pub unsafe fn llil_function( - &self, - ) -> Option>>> { + /// [`LowLevelILMutableFunction`] used to represent Low Level IL + pub unsafe fn llil_function(&self) -> Option> { let result = unsafe { BNAnalysisContextGetLowLevelILFunction(self.handle.as_ptr()) }; - let arch = self.function().arch(); unsafe { - Some(LowLevelILFunction::ref_from_raw( - arch, + Some(LowLevelILMutableFunction::ref_from_raw( NonNull::new(result)?.as_ptr(), )) } } - // TODO: This returns LiftedNonSSA because the lifting code was written before we could patch the IL - // TODO: At some point we need to take the lifting code and make it available to regular IL. - pub fn set_llil_function( - &self, - value: &LowLevelILFunction>, - ) { + pub fn set_llil_function(&self, value: &LowLevelILRegularFunction) { unsafe { BNSetLowLevelILFunction(self.handle.as_ptr(), value.handle) } } @@ -115,14 +98,9 @@ impl AnalysisContext { } } - pub fn inform(&self, request: S) -> bool { - let request = request.into_bytes_with_nul(); - unsafe { - BNAnalysisContextInform( - self.handle.as_ptr(), - request.as_ref().as_ptr() as *const c_char, - ) - } + pub fn inform(&self, request: &str) -> bool { + let request = request.to_cstr(); + unsafe { BNAnalysisContextInform(self.handle.as_ptr(), request.as_ptr()) } } pub fn set_basic_blocks(&self, blocks: I) @@ -173,22 +151,16 @@ impl Activity { Ref::new(Self { handle }) } - pub fn new(config: S) -> Ref { + pub fn new(config: &str) -> Ref { unsafe extern "C" fn cb_action_nop(_: *mut c_void, _: *mut BNAnalysisContext) {} - let config = config.into_bytes_with_nul(); - let result = unsafe { - BNCreateActivity( - config.as_ref().as_ptr() as *const c_char, - std::ptr::null_mut(), - Some(cb_action_nop), - ) - }; + let config = config.to_cstr(); + let result = + unsafe { BNCreateActivity(config.as_ptr(), std::ptr::null_mut(), Some(cb_action_nop)) }; unsafe { Activity::ref_from_raw(NonNull::new(result).unwrap()) } } - pub fn new_with_action(config: S, mut action: F) -> Ref + pub fn new_with_action(config: &str, mut action: F) -> Ref where - S: BnStrCompatible, F: FnMut(&AnalysisContext), { unsafe extern "C" fn cb_action( @@ -200,10 +172,10 @@ impl Activity { ctxt(&AnalysisContext::from_raw(analysis)) } } - let config = config.into_bytes_with_nul(); + let config = config.to_cstr(); let result = unsafe { BNCreateActivity( - config.as_ref().as_ptr() as *const c_char, + config.as_ptr(), &mut action as *mut F as *mut c_void, Some(cb_action::), ) @@ -211,10 +183,10 @@ impl Activity { unsafe { Activity::ref_from_raw(NonNull::new(result).unwrap()) } } - pub fn name(&self) -> BnString { + pub fn name(&self) -> String { let result = unsafe { BNActivityGetName(self.handle.as_ptr()) }; assert!(!result.is_null()); - unsafe { BnString::from_raw(result) } + unsafe { BnString::into_string(result) } } } @@ -257,9 +229,9 @@ impl Workflow { /// Create a new unregistered [Workflow] with no activities. /// /// To get a copy of an existing registered [Workflow] use [Workflow::clone_to]. - pub fn new(name: S) -> Ref { - let name = name.into_bytes_with_nul(); - let result = unsafe { BNCreateWorkflow(name.as_ref().as_ptr() as *const c_char) }; + pub fn new(name: &str) -> Ref { + let name = name.to_cstr(); + let result = unsafe { BNCreateWorkflow(name.as_ptr()) }; unsafe { Workflow::ref_from_raw(NonNull::new(result).unwrap()) } } @@ -267,7 +239,7 @@ impl Workflow { /// /// * `name` - the name for the new [Workflow] #[must_use] - pub fn clone_to(&self, name: S) -> Ref { + pub fn clone_to(&self, name: &str) -> Ref { self.clone_to_with_root(name, "") } @@ -276,29 +248,24 @@ impl Workflow { /// * `name` - the name for the new [Workflow] /// * `root_activity` - perform the clone operation with this activity as the root #[must_use] - pub fn clone_to_with_root( - &self, - name: S, - root_activity: A, - ) -> Ref { - let raw_name = name.into_bytes_with_nul(); - let activity = root_activity.into_bytes_with_nul(); + pub fn clone_to_with_root(&self, name: &str, root_activity: &str) -> Ref { + let raw_name = name.to_cstr(); + let activity = root_activity.to_cstr(); unsafe { Self::ref_from_raw( NonNull::new(BNWorkflowClone( self.handle.as_ptr(), - raw_name.as_ref().as_ptr() as *const c_char, - activity.as_ref().as_ptr() as *const c_char, + raw_name.as_ptr(), + activity.as_ptr(), )) .unwrap(), ) } } - pub fn instance(name: S) -> Ref { - let result = unsafe { - BNWorkflowInstance(name.into_bytes_with_nul().as_ref().as_ptr() as *const c_char) - }; + pub fn instance(name: &str) -> Ref { + let name = name.to_cstr(); + let result = unsafe { BNWorkflowInstance(name.as_ptr()) }; unsafe { Workflow::ref_from_raw(NonNull::new(result).unwrap()) } } @@ -310,10 +277,10 @@ impl Workflow { unsafe { Array::new(result, count, ()) } } - pub fn name(&self) -> BnString { + pub fn name(&self) -> String { let result = unsafe { BNGetWorkflowName(self.handle.as_ptr()) }; assert!(!result.is_null()); - unsafe { BnString::from_raw(result) } + unsafe { BnString::into_string(result) } } /// Register this [Workflow], making it immutable and available for use. @@ -324,14 +291,9 @@ impl Workflow { /// Register this [Workflow], making it immutable and available for use. /// /// * `configuration` - a JSON representation of the workflow configuration - pub fn register_with_config(&self, config: S) -> Result<(), ()> { - let config = config.into_bytes_with_nul(); - if unsafe { - BNRegisterWorkflow( - self.handle.as_ptr(), - config.as_ref().as_ptr() as *const c_char, - ) - } { + pub fn register_with_config(&self, config: &str) -> Result<(), ()> { + let config = config.to_cstr(); + if unsafe { BNRegisterWorkflow(self.handle.as_ptr(), config.as_ptr()) } { Ok(()) } else { Err(()) @@ -356,16 +318,11 @@ impl Workflow { ) -> Result, ()> where I: IntoIterator, - I::Item: BnStrCompatible, + I::Item: IntoCStr, { - let subactivities_raw: Vec<_> = subactivities - .into_iter() - .map(|x| x.into_bytes_with_nul()) - .collect(); - let mut subactivities_ptr: Vec<*const _> = subactivities_raw - .iter() - .map(|x| x.as_ref().as_ptr() as *const c_char) - .collect(); + let subactivities_raw: Vec<_> = subactivities.into_iter().map(|x| x.to_cstr()).collect(); + let mut subactivities_ptr: Vec<*const _> = + subactivities_raw.iter().map(|x| x.as_ptr()).collect(); let result = unsafe { BNWorkflowRegisterActivity( self.handle.as_ptr(), @@ -379,17 +336,13 @@ impl Workflow { } /// Determine if an Activity exists in this [Workflow]. - pub fn contains(&self, activity: A) -> bool { - unsafe { - BNWorkflowContains( - self.handle.as_ptr(), - activity.into_bytes_with_nul().as_ref().as_ptr() as *const c_char, - ) - } + pub fn contains(&self, activity: &str) -> bool { + let activity = activity.to_cstr(); + unsafe { BNWorkflowContains(self.handle.as_ptr(), activity.as_ptr()) } } /// Retrieve the configuration as an adjacency list in JSON for the [Workflow]. - pub fn configuration(&self) -> BnString { + pub fn configuration(&self) -> String { self.configuration_with_activity("") } @@ -397,15 +350,11 @@ impl Workflow { /// [Workflow], just for the given `activity`. /// /// `activity` - return the configuration for the `activity` - pub fn configuration_with_activity(&self, activity: A) -> BnString { - let result = unsafe { - BNWorkflowGetConfiguration( - self.handle.as_ptr(), - activity.into_bytes_with_nul().as_ref().as_ptr() as *const c_char, - ) - }; + pub fn configuration_with_activity(&self, activity: &str) -> String { + let activity = activity.to_cstr(); + let result = unsafe { BNWorkflowGetConfiguration(self.handle.as_ptr(), activity.as_ptr()) }; assert!(!result.is_null()); - unsafe { BnString::from_raw(result) } + unsafe { BnString::into_string(result) } } /// Whether this [Workflow] is registered or not. A [Workflow] becomes immutable once registered. @@ -418,14 +367,9 @@ impl Workflow { } /// Retrieve the Activity object for the specified `name`. - pub fn activity(&self, name: A) -> Option> { - let name = name.into_bytes_with_nul(); - let result = unsafe { - BNWorkflowGetActivity( - self.handle.as_ptr(), - name.as_ref().as_ptr() as *const c_char, - ) - }; + pub fn activity(&self, name: &str) -> Option> { + let name = name.to_cstr(); + let result = unsafe { BNWorkflowGetActivity(self.handle.as_ptr(), name.as_ptr()) }; NonNull::new(result).map(|a| unsafe { Activity::ref_from_raw(a) }) } @@ -433,14 +377,11 @@ impl Workflow { /// specified just for the given `activity`. /// /// * `activity` - if specified, return the roots for the `activity` - pub fn activity_roots(&self, activity: A) -> Array { + pub fn activity_roots(&self, activity: &str) -> Array { + let activity = activity.to_cstr(); let mut count = 0; let result = unsafe { - BNWorkflowGetActivityRoots( - self.handle.as_ptr(), - activity.into_bytes_with_nul().as_ref().as_ptr() as *const c_char, - &mut count, - ) + BNWorkflowGetActivityRoots(self.handle.as_ptr(), activity.as_ptr(), &mut count) }; assert!(!result.is_null()); unsafe { Array::new(result as *mut *mut c_char, count, ()) } @@ -450,16 +391,13 @@ impl Workflow { /// /// * `activity` - if specified, return the direct children and optionally the descendants of the `activity` (includes `activity`) /// * `immediate` - whether to include only direct children of `activity` or all descendants - pub fn subactivities( - &self, - activity: A, - immediate: bool, - ) -> Array { + pub fn subactivities(&self, activity: &str, immediate: bool) -> Array { + let activity = activity.to_cstr(); let mut count = 0; let result = unsafe { BNWorkflowGetSubactivities( self.handle.as_ptr(), - activity.into_bytes_with_nul().as_ref().as_ptr() as *const c_char, + activity.as_ptr(), immediate, &mut count, ) @@ -472,24 +410,18 @@ impl Workflow { /// /// * `activity` - the Activity node to assign children /// * `activities` - the list of Activities to assign - pub fn assign_subactivities(&self, activity: A, activities: I) -> bool + pub fn assign_subactivities(&self, activity: &str, activities: I) -> bool where - A: BnStrCompatible, I: IntoIterator, - I::Item: BnStrCompatible, + I::Item: IntoCStr, { - let input_list: Vec<_> = activities - .into_iter() - .map(|a| a.into_bytes_with_nul()) - .collect(); - let mut input_list_ptr: Vec<*const _> = input_list - .iter() - .map(|x| x.as_ref().as_ptr() as *const c_char) - .collect(); + let activity = activity.to_cstr(); + let input_list: Vec<_> = activities.into_iter().map(|a| a.to_cstr()).collect(); + let mut input_list_ptr: Vec<*const _> = input_list.iter().map(|x| x.as_ptr()).collect(); unsafe { BNWorkflowAssignSubactivities( self.handle.as_ptr(), - activity.into_bytes_with_nul().as_ref().as_ptr() as *const c_char, + activity.as_ptr(), input_list_ptr.as_mut_ptr(), input_list.len(), ) @@ -505,24 +437,18 @@ impl Workflow { /// /// * `activity` - the Activity node for which to insert `activities` before /// * `activities` - the list of Activities to insert - pub fn insert(&self, activity: A, activities: I) -> bool + pub fn insert(&self, activity: &str, activities: I) -> bool where - A: BnStrCompatible, I: IntoIterator, - I::Item: BnStrCompatible, + I::Item: IntoCStr, { - let input_list: Vec<_> = activities - .into_iter() - .map(|a| a.into_bytes_with_nul()) - .collect(); - let mut input_list_ptr: Vec<*const _> = input_list - .iter() - .map(|x| x.as_ref().as_ptr() as *const c_char) - .collect(); + let activity = activity.to_cstr(); + let input_list: Vec<_> = activities.into_iter().map(|a| a.to_cstr()).collect(); + let mut input_list_ptr: Vec<*const _> = input_list.iter().map(|x| x.as_ptr()).collect(); unsafe { BNWorkflowInsert( self.handle.as_ptr(), - activity.into_bytes_with_nul().as_ref().as_ptr() as *const c_char, + activity.as_ptr(), input_list_ptr.as_mut_ptr(), input_list.len(), ) @@ -533,24 +459,18 @@ impl Workflow { /// /// * `activity` - the Activity node for which to insert `activities` after /// * `activities` - the list of Activities to insert - pub fn insert_after(&self, activity: A, activities: I) -> bool + pub fn insert_after(&self, activity: &str, activities: I) -> bool where - A: BnStrCompatible, I: IntoIterator, - I::Item: BnStrCompatible, + I::Item: IntoCStr, { - let input_list: Vec<_> = activities - .into_iter() - .map(|a| a.into_bytes_with_nul()) - .collect(); - let mut input_list_ptr: Vec<*const _> = input_list - .iter() - .map(|x| x.as_ref().as_ptr() as *const c_char) - .collect(); + let activity = activity.to_cstr(); + let input_list: Vec<_> = activities.into_iter().map(|a| a.to_cstr()).collect(); + let mut input_list_ptr: Vec<*const _> = input_list.iter().map(|x| x.as_ptr()).collect(); unsafe { BNWorkflowInsertAfter( self.handle.as_ptr(), - activity.into_bytes_with_nul().as_ref().as_ptr() as *const c_char, + activity.as_ptr(), input_list_ptr.as_mut_ptr(), input_list.len(), ) @@ -558,29 +478,23 @@ impl Workflow { } /// Remove the specified `activity` - pub fn remove(&self, activity: A) -> bool { - unsafe { - BNWorkflowRemove( - self.handle.as_ptr(), - activity.into_bytes_with_nul().as_ref().as_ptr() as *const c_char, - ) - } + pub fn remove(&self, activity: &str) -> bool { + let activity = activity.to_cstr(); + unsafe { BNWorkflowRemove(self.handle.as_ptr(), activity.as_ptr()) } } /// Replace the specified `activity`. /// /// * `activity` - the Activity to replace /// * `new_activity` - the replacement Activity - pub fn replace( - &self, - activity: A, - new_activity: N, - ) -> bool { + pub fn replace(&self, activity: &str, new_activity: &str) -> bool { + let activity = activity.to_cstr(); + let new_activity = new_activity.to_cstr(); unsafe { BNWorkflowReplace( self.handle.as_ptr(), - activity.into_bytes_with_nul().as_ref().as_ptr() as *const c_char, - new_activity.into_bytes_with_nul().as_ref().as_ptr() as *const c_char, + activity.as_ptr(), + new_activity.as_ptr(), ) } } @@ -589,20 +503,11 @@ impl Workflow { /// /// * `activity` - if specified, generate the Flowgraph using `activity` as the root /// * `sequential` - whether to generate a **Composite** or **Sequential** style graph - pub fn graph( - &self, - activity: A, - sequential: Option, - ) -> Option> { + pub fn graph(&self, activity: &str, sequential: Option) -> Option> { let sequential = sequential.unwrap_or(false); - let activity_name = activity.into_bytes_with_nul(); - let graph = unsafe { - BNWorkflowGetGraph( - self.handle.as_ptr(), - activity_name.as_ref().as_ptr() as *const c_char, - sequential, - ) - }; + let activity = activity.to_cstr(); + let graph = + unsafe { BNWorkflowGetGraph(self.handle.as_ptr(), activity.as_ptr(), sequential) }; if graph.is_null() { return None; } diff --git a/rust/tests/binary_reader.rs b/rust/tests/binary_reader.rs index 12be9706fc..0adac464e0 100644 --- a/rust/tests/binary_reader.rs +++ b/rust/tests/binary_reader.rs @@ -1,5 +1,4 @@ -use binaryninja::binary_reader::BinaryReader; -use binaryninja::binary_view::{BinaryViewBase, BinaryViewExt}; +use binaryninja::binary_view::{BinaryReader, BinaryViewBase, BinaryViewExt}; use binaryninja::headless::Session; use std::io::{Read, Seek, SeekFrom}; use std::path::PathBuf; diff --git a/rust/tests/binary_view.rs b/rust/tests/binary_view.rs index e48d5f5299..a240725369 100644 --- a/rust/tests/binary_view.rs +++ b/rust/tests/binary_view.rs @@ -29,7 +29,7 @@ fn test_binary_saving() { let modified_contents = view.read_vec(contents_addr, 4); assert_eq!(modified_contents, [0xff, 0xff, 0xff, 0xff]); - // HACK: To prevent us from deadlocking in save_to_path we wait for all main thread actions to finish. + // HACK: To prevent us from deadlocking in save_to_path, we wait for all main thread actions to finish. execute_on_main_thread_and_wait(|| {}); // Save the modified file @@ -56,7 +56,7 @@ fn test_binary_saving_database() { SymbolBuilder::new(SymbolType::Function, "test", entry_function.start()).create(); view.define_user_symbol(&new_entry_func_symbol); // Verify that we modified the binary - assert_eq!(entry_function.symbol().raw_name().as_str(), "test"); + assert_eq!(entry_function.symbol().raw_name().to_string_lossy(), "test"); // Save the modified database. assert!(view.file().create_database(out_dir.join("atox.obj.bndb"))); // Verify that the file exists and is modified. @@ -65,5 +65,8 @@ fn test_binary_saving_database() { let new_entry_function = new_view .entry_point_function() .expect("Failed to get entry point function"); - assert_eq!(new_entry_function.symbol().raw_name().as_str(), "test"); + assert_eq!( + new_entry_function.symbol().raw_name().to_string_lossy(), + "test" + ); } diff --git a/rust/tests/binary_writer.rs b/rust/tests/binary_writer.rs index 1ec12dd3e4..b74b28374c 100644 --- a/rust/tests/binary_writer.rs +++ b/rust/tests/binary_writer.rs @@ -1,6 +1,4 @@ -use binaryninja::binary_reader::BinaryReader; -use binaryninja::binary_view::{BinaryViewBase, BinaryViewExt}; -use binaryninja::binary_writer::BinaryWriter; +use binaryninja::binary_view::{BinaryReader, BinaryViewBase, BinaryViewExt, BinaryWriter}; use binaryninja::headless::Session; use std::io::{Read, Seek, SeekFrom, Write}; use std::path::PathBuf; diff --git a/rust/tests/collaboration.rs b/rust/tests/collaboration.rs index 210492e463..85f8702e2a 100644 --- a/rust/tests/collaboration.rs +++ b/rust/tests/collaboration.rs @@ -20,13 +20,6 @@ fn temp_project_scope(remote: &Remote, project_name: &str // TODO: have connected by the time this errors out. Maybe? let _ = remote.connect(); } - - if let Ok(home_dir) = env::var("HOME").or_else(|_| env::var("USERPROFILE")) { - eprintln!("Current user directory: {}", home_dir); - } else { - eprintln!("Unable to determine the current user directory."); - } - let project = remote .create_project(project_name, "Test project for test purposes") .expect("Failed to create project"); @@ -109,7 +102,7 @@ fn test_project_creation() { .expect("Failed to delete file"); assert!( !project - .get_file_by_id(created_file_id) + .get_file_by_id(&created_file_id) .is_ok_and(|f| f.is_some()), "File was not deleted" ); @@ -136,7 +129,7 @@ fn test_project_creation() { let created_folder_file_id = created_folder_file.id(); // Verify the file exists in the folder. let check_folder_file = project - .get_file_by_id(created_folder_file_id) + .get_file_by_id(&created_folder_file_id) .expect("Failed to get folder file by id") .unwrap(); assert_eq!(check_folder_file.name().as_str(), "test_folder_file"); @@ -155,7 +148,7 @@ fn test_project_creation() { .expect("Failed to delete folder"); assert!( !project - .get_folder_by_id(created_folder_id) + .get_folder_by_id(&created_folder_id) .is_ok_and(|f| f.is_some()), "Folder was not deleted" ); @@ -190,7 +183,7 @@ fn test_project_sync() { SymbolBuilder::new(SymbolType::Function, "test", entry_function.start()).create(); view.define_user_symbol(&new_entry_func_symbol); // Verify that we modified the binary - assert_eq!(entry_function.symbol().raw_name().as_str(), "test"); + assert_eq!(entry_function.symbol().raw_name(), "test".into()); // Make new snapshot. assert!(view.file().save_auto_snapshot()); // We should have two snapshots. @@ -210,20 +203,20 @@ fn test_project_sync() { drop(view); // Verify that the remote file exists. project - .get_file_by_id(remote_file.id()) + .get_file_by_id(&remote_file.id()) .expect("Failed to get remote file by id"); // Download the remote database with our changes. let downloaded_file = remote_file - .download_database(out_dir.join("downloaded_atox.obj.bndb")) + .download_database(&out_dir.join("downloaded_atox.obj.bndb")) .expect("Failed to download database"); let downloaded_view = downloaded_file - .view_of_type(view_type) + .view_of_type(&view_type) .expect("Failed to open downloaded view"); // Verify the changes in the entry function. let entry_function = downloaded_view .entry_point_function() .expect("Failed to get entry point function"); - assert_eq!(entry_function.symbol().raw_name().as_str(), "test"); + assert_eq!(entry_function.symbol().raw_name(), "test".into()); project .delete_file(&remote_file) .expect("Failed to delete file"); diff --git a/rust/tests/data_buffer.rs b/rust/tests/data_buffer.rs index 8480ae71c5..1e095d1952 100644 --- a/rust/tests/data_buffer.rs +++ b/rust/tests/data_buffer.rs @@ -13,7 +13,7 @@ fn get_slice() { #[test] fn set_len_write() { let mut data = DataBuffer::default(); - assert_eq!(data.get_data(), &[]); + assert!(data.get_data().is_empty()); unsafe { data.set_len(DUMMY_DATA_0.len()) }; assert_eq!(data.len(), DUMMY_DATA_0.len()); let mut contents = DUMMY_DATA_0.to_vec(); @@ -29,7 +29,7 @@ fn set_len_write() { assert_eq!(data.get_data(), &DUMMY_DATA_0[..13]); data.clear(); - assert_eq!(data.get_data(), &[]); + assert!(data.get_data().is_empty()); } #[test] diff --git a/rust/tests/debug_info.rs b/rust/tests/debug_info.rs index 8b83363b45..407b41d5a5 100644 --- a/rust/tests/debug_info.rs +++ b/rust/tests/debug_info.rs @@ -69,7 +69,7 @@ fn test_debug_info() { let func = view .function_at(&view.default_platform().unwrap(), 0x3b440) .expect("Debug info test function exists"); - assert_eq!(func.symbol().raw_name().to_string(), "test_func"); + assert_eq!(func.symbol().raw_name().to_string_lossy(), "test_func"); view.file().close(); } diff --git a/rust/tests/file_accessor.rs b/rust/tests/file_accessor.rs new file mode 100644 index 0000000000..ce79d277fc --- /dev/null +++ b/rust/tests/file_accessor.rs @@ -0,0 +1,22 @@ +use binaryninja::file_accessor::FileAccessor; +use std::io::Cursor; + +#[test] +fn test_file_accessor() { + let mut mock_data = Cursor::new(vec![0u8; 100]); + let accessor = FileAccessor::new(&mut mock_data); + assert_eq!( + accessor.length(), + 100, + "File accessor length does not match" + ); + assert_eq!( + accessor.write(0x10, &[0xff]), + 1, + "Failed to write to file accessor" + ); + let read_value = accessor + .read(0x10, 1) + .expect("Failed to read from file accessor"); + assert_eq!(read_value, &[0xff], "Read value does not match"); +} diff --git a/rust/tests/initialization.rs b/rust/tests/initialization.rs index 9847a73402..00063215d6 100644 --- a/rust/tests/initialization.rs +++ b/rust/tests/initialization.rs @@ -11,8 +11,8 @@ use binaryninja::set_license; #[test] fn test_license_validation() { - // Release floating license if we already have one, otherwise the failure will succeed. - release_license(); + // Release the floating license if we already have one, otherwise the failure will succeed. + release_license(true); // Make sure we properly report invalid license. let options = InitializationOptions::default() .with_license_checkout(false) @@ -23,7 +23,7 @@ fn test_license_validation() { Err(e) => panic!("Unexpected error: {:?}", e), } // Reset the license so that it actually can validate license. - set_license::(None); + set_license(None); // Actually make sure we can initialize. init().expect("Failed to initialize, make sure you have a license before trying to run tests!"); // Open an empty binary and make sure it succeeds. diff --git a/rust/tests/interaction.rs b/rust/tests/interaction.rs new file mode 100644 index 0000000000..5e9606b099 --- /dev/null +++ b/rust/tests/interaction.rs @@ -0,0 +1,174 @@ +use std::path::PathBuf; + +use binaryninja::binary_view::BinaryView; +use binaryninja::flowgraph::FlowGraph; +use binaryninja::headless::Session; +use binaryninja::interaction::form::{Form, FormInputField}; +use binaryninja::interaction::handler::{ + register_interaction_handler, InteractionHandler, InteractionHandlerTask, +}; +use binaryninja::interaction::report::{Report, ReportCollection}; +use binaryninja::interaction::{MessageBoxButtonResult, MessageBoxButtonSet, MessageBoxIcon}; + +struct MyInteractionHandler; + +impl InteractionHandler for MyInteractionHandler { + fn show_message_box( + &mut self, + _title: &str, + _text: &str, + _buttons: MessageBoxButtonSet, + _icon: MessageBoxIcon, + ) -> MessageBoxButtonResult { + todo!() + } + + fn open_url(&mut self, _url: &str) -> bool { + todo!() + } + + fn run_progress_dialog( + &mut self, + _title: &str, + _can_cancel: bool, + _task: &InteractionHandlerTask, + ) -> bool { + todo!() + } + + fn show_plain_text_report( + &mut self, + _view: Option<&BinaryView>, + _title: &str, + _contents: &str, + ) { + todo!() + } + + fn show_graph_report(&mut self, _view: Option<&BinaryView>, _title: &str, _graph: &FlowGraph) { + todo!() + } + + fn show_report_collection(&mut self, title: &str, reports: &ReportCollection) { + assert_eq!(title, "show_report_collection_title"); + for (i, report) in reports.iter().enumerate() { + assert_eq!(report.title(), format!("title_report_{i}")); + match (i, report) { + (0, Report::PlainText(x)) => { + assert_eq!(x.contents().as_str(), "contents"); + } + (1, Report::Markdown(x)) => { + assert_eq!(x.contents().as_str(), "# contents"); + assert_eq!(x.plaintext().as_str(), "markdown_plain_text"); + } + (2, Report::Html(x)) => { + assert_eq!(x.contents().as_str(), "contents"); + assert_eq!(x.plaintext().as_str(), "html_plain_text"); + } + (3, Report::FlowGraph(x)) => { + assert_eq!(x.flow_graph().get_node_count(), 0); + } + _ => unreachable!(), + } + } + } + + fn get_form_input(&mut self, form: &mut Form) -> bool { + if form.fields.len() != 1 { + return false; + } + + match &mut form.fields[0] { + FormInputField::Integer { ref mut value, .. } => { + *value = 1337; + true + } + FormInputField::Address { + ref mut value, + default, + .. + } => { + *value = default.unwrap_or(0) + 0x10; + true + } + FormInputField::DirectoryName { + ref mut value, + default, + default_name, + .. + } => { + let new_value = format!( + "example{}{}", + default.clone().unwrap_or_default(), + default_name.clone().unwrap_or_default() + ); + *value = Some(new_value); + true + } + _ => false, + } + } +} + +#[test] +fn test_get_integer() { + register_interaction_handler(MyInteractionHandler {}); + let output = binaryninja::interaction::get_integer_input("get_int", "get_int_prompt"); + assert_eq!(output, Some(1337)); +} + +#[test] +fn test_get_directory() { + register_interaction_handler(MyInteractionHandler {}); + let output = binaryninja::interaction::get_directory_name_input("get_dir", ""); + assert_eq!( + output.as_ref().map(|x| x.to_str().unwrap()), + Some("example") + ); +} + +#[test] +fn test_get_directory_default() { + register_interaction_handler(MyInteractionHandler {}); + + let mut my_form = Form::new("get_dir_default"); + my_form.add_field(FormInputField::DirectoryName { + prompt: "get_dir_default".to_string(), + default_name: Some("_default_name".to_string()), + default: Some("_default".to_string()), + value: None, + }); + + assert_eq!(my_form.prompt(), true); + assert_eq!( + my_form.fields[0].try_value_string(), + Some("example_default_default_name".to_string()) + ) +} + +#[test] +fn test_get_address() { + register_interaction_handler(MyInteractionHandler {}); + let output = binaryninja::interaction::get_address_input("address", "Address Prompt"); + assert_eq!(output, Some(0x10)); +} + +#[test] +fn test_show_report_collection() { + let _session = Session::new().expect("Failed to initialize session"); + let out_dir = env!("OUT_DIR").parse::().unwrap(); + let view = binaryninja::load(out_dir.join("atox.obj")).expect("Failed to create view"); + + register_interaction_handler(MyInteractionHandler {}); + let collection = ReportCollection::new(); + collection.add_text(&view, "title_report_0", "contents"); + collection.add_markdown(&view, "title_report_1", "# contents", "markdown_plain_text"); + collection.add_html( + &view, + "title_report_2", + "contents", + "html_plain_text", + ); + collection.add_graph(&view, "title_report_3", &FlowGraph::new()); + collection.show("show_report_collection_title"); +} diff --git a/rust/tests/language_representation.rs b/rust/tests/language_representation.rs new file mode 100644 index 0000000000..c3d6096678 --- /dev/null +++ b/rust/tests/language_representation.rs @@ -0,0 +1,186 @@ +use std::path::PathBuf; + +use binaryninja::architecture::CoreArchitecture; +use binaryninja::binary_view::{BinaryView, BinaryViewExt}; +use binaryninja::disassembly::{ + DisassemblySettings, DisassemblyTextLine, InstructionTextToken, InstructionTextTokenKind, +}; +use binaryninja::function::Function; +use binaryninja::headless::Session; +use binaryninja::high_level_il::token_emitter::HighLevelILTokenEmitter; +use binaryninja::high_level_il::{HighLevelILFunction, HighLevelInstructionIndex}; +use binaryninja::language_representation::{ + register_language_representation_function_type, CoreLanguageRepresentationFunction, + CoreLanguageRepresentationFunctionType, LanguageRepresentationFunction, + LanguageRepresentationFunctionType, OperatorPrecedence, +}; +use binaryninja::rc::Ref; + +struct MyLangReprType { + core: CoreLanguageRepresentationFunctionType, +} + +impl LanguageRepresentationFunctionType for MyLangReprType { + fn create( + &self, + arch: &CoreArchitecture, + func: &Function, + high_level_il: &HighLevelILFunction, + ) -> Ref { + CoreLanguageRepresentationFunction::new( + &self.core, + MyLangRepr {}, + arch, + func, + high_level_il, + ) + } + + fn is_valid(&self, _view: &BinaryView) -> bool { + true + } + + fn function_type_tokens( + &self, + _func: &Function, + _settings: &DisassemblySettings, + ) -> Vec { + todo!() + } +} + +unsafe impl Send for MyLangReprType {} +unsafe impl Sync for MyLangReprType {} + +struct MyLangRepr; + +impl LanguageRepresentationFunction for MyLangRepr { + fn on_token_emitter_init(&self, _tokens: &HighLevelILTokenEmitter) {} + + fn expr_text( + &self, + il: &HighLevelILFunction, + expr_index: HighLevelInstructionIndex, + tokens: &HighLevelILTokenEmitter, + _settings: &DisassemblySettings, + _as_full_ast: bool, + _precedence: OperatorPrecedence, + _statement: bool, + ) { + let instr = il.instruction_from_expr_index(expr_index).unwrap(); + let instr = instr.lift(); + use binaryninja::high_level_il::HighLevelILLiftedInstructionKind::*; + match &instr.kind { + Block(block) => { + tokens.append(InstructionTextToken::new( + format!("block {}\n", block.body.len()), + InstructionTextTokenKind::Text, + )); + for block_inst in &block.body { + self.expr_text( + il, + block_inst.expr_index, + tokens, + _settings, + _as_full_ast, + _precedence, + _statement, + ); + } + } + Unimpl | Unreachable | Undef => panic!(), + _kind => { + tokens.append(InstructionTextToken::new( + format!("other instr 0x{:x}\n", instr.address), + InstructionTextTokenKind::Text, + )); + } + } + } + + fn begin_lines( + &self, + _il: &HighLevelILFunction, + _expr_index: HighLevelInstructionIndex, + _tokens: &HighLevelILTokenEmitter, + ) { + } + + fn end_lines( + &self, + _il: &HighLevelILFunction, + _expr_index: HighLevelInstructionIndex, + _tokens: &HighLevelILTokenEmitter, + ) { + } + + fn comment_start_string(&self) -> &str { + "/* " + } + + fn comment_end_string(&self) -> &str { + " */" + } + + fn annotation_start_string(&self) -> &str { + "{" + } + + fn annotation_end_string(&self) -> &str { + "}" + } +} + +#[test] +fn test_custom_language_representation() { + const LANG_REPR_NAME: &str = "test_lang_repr"; + let _session = Session::new().expect("Failed to initialize session"); + let out_dir = env!("OUT_DIR").parse::().unwrap(); + + let my_repr = register_language_representation_function_type( + |core| MyLangReprType { core }, + LANG_REPR_NAME, + ); + let view = binaryninja::load(out_dir.join("atox.obj")).expect("Failed to create view"); + let func = view + .function_at(&view.default_platform().unwrap(), 0x36760) + .unwrap(); + let _repr = my_repr.create(&func); + let il = func.high_level_il(false).unwrap(); + + let settings = DisassemblySettings::new(); + let root_idx = il.root_instruction_index(); + let result = _repr.linear_lines(&il, root_idx, &settings, false); + let output: String = result.iter().map(|dis| dis.to_string()).collect(); + assert_eq!( + format!("{output}"), + "block 26 +other instr 0x36775 +other instr 0x3679e +other instr 0x3679e +other instr 0x367ba +other instr 0x367e6 +other instr 0x3682f +other instr 0x3682f +other instr 0x36834 +other instr 0x3683e +other instr 0x3684e +other instr 0x36867 +other instr 0x36881 +other instr 0x36881 +other instr 0x36881 +other instr 0x36896 +other instr 0x368a0 +other instr 0x368bb +other instr 0x368d2 +other instr 0x3694a +other instr 0x36960 +other instr 0x369e1 +other instr 0x369ec +other instr 0x36a2e +other instr 0x36ab5 +other instr 0x36abd +other instr 0x36ac2 +" + ); +} diff --git a/rust/tests/line_formatter.rs b/rust/tests/line_formatter.rs new file mode 100644 index 0000000000..eeb5f0e109 --- /dev/null +++ b/rust/tests/line_formatter.rs @@ -0,0 +1,25 @@ +use binaryninja::disassembly::DisassemblyTextLine; +use binaryninja::headless::Session; +use binaryninja::line_formatter::{register_line_formatter, LineFormatter, LineFormatterSettings}; +use std::path::PathBuf; + +struct MyLineFormatter; + +impl LineFormatter for MyLineFormatter { + fn format_lines( + &self, + lines: &[DisassemblyTextLine], + _settings: &LineFormatterSettings, + ) -> Vec { + lines.to_vec() + } +} + +#[test] +fn test_custom_line_formatter() { + let _session = Session::new().expect("Failed to initialize session"); + let out_dir = env!("OUT_DIR").parse::().unwrap(); + let line_formatter = register_line_formatter("my_line_formatter", MyLineFormatter {}); + assert_eq!(line_formatter.name(), "my_line_formatter".into()); + // TODO: Finish this test. +} diff --git a/rust/tests/low_level_il.rs b/rust/tests/low_level_il.rs index b9d7d203a5..db8cc54974 100644 --- a/rust/tests/low_level_il.rs +++ b/rust/tests/low_level_il.rs @@ -7,7 +7,7 @@ use binaryninja::low_level_il::expression::{ use binaryninja::low_level_il::instruction::{ InstructionHandler, LowLevelILInstructionKind, LowLevelInstructionIndex, }; -use binaryninja::low_level_il::{LowLevelILRegister, VisitorAction}; +use binaryninja::low_level_il::{LowLevelILRegisterKind, LowLevelILSSARegisterKind, VisitorAction}; use std::path::PathBuf; #[test] @@ -34,7 +34,7 @@ fn test_llil_info() { LowLevelILInstructionKind::SetReg(op) => { assert_eq!(op.size(), 4); match op.dest_reg() { - LowLevelILRegister::ArchReg(reg) => assert_eq!(reg.name(), "edi"), + LowLevelILRegisterKind::Arch(reg) => assert_eq!(reg.name(), "edi"), _ => panic!("Expected Register::ArchReg"), } assert_eq!(op.source_expr().index, LowLevelExpressionIndex(0)); @@ -55,7 +55,7 @@ fn test_llil_info() { LowLevelILExpressionKind::Reg(op) => { assert_eq!(op.size(), 4); match op.source_reg() { - LowLevelILRegister::ArchReg(reg) => assert_eq!(reg.name(), "ebp"), + LowLevelILRegisterKind::Arch(reg) => assert_eq!(reg.name(), "ebp"), _ => panic!("Expected Register::ArchReg"), } } @@ -73,7 +73,7 @@ fn test_llil_info() { LowLevelILInstructionKind::SetReg(op) => { assert_eq!(op.size(), 4); match op.dest_reg() { - LowLevelILRegister::ArchReg(reg) => assert_eq!(reg.name(), "ebp"), + LowLevelILRegisterKind::Arch(reg) => assert_eq!(reg.name(), "ebp"), _ => panic!("Expected Register::ArchReg"), } assert_eq!(op.source_expr().index, LowLevelExpressionIndex(4)); @@ -89,7 +89,7 @@ fn test_llil_info() { LowLevelILInstructionKind::SetReg(op) => { assert_eq!(op.size(), 4); match op.dest_reg() { - LowLevelILRegister::ArchReg(reg) => assert_eq!(reg.name(), "eax"), + LowLevelILRegisterKind::Arch(reg) => assert_eq!(reg.name(), "eax"), _ => panic!("Expected Register::ArchReg"), } assert_eq!(op.source_expr().index, LowLevelExpressionIndex(9)); @@ -128,7 +128,7 @@ fn test_llil_info() { LowLevelILInstructionKind::SetReg(op) => { assert_eq!(op.size(), 4); match op.dest_reg() { - LowLevelILRegister::ArchReg(reg) => assert_eq!(reg.name(), "esp"), + LowLevelILRegisterKind::Arch(reg) => assert_eq!(reg.name(), "esp"), _ => panic!("Expected Register::ArchReg"), } assert_eq!(op.source_expr().index, LowLevelExpressionIndex(17)); @@ -144,7 +144,7 @@ fn test_llil_info() { LowLevelILInstructionKind::SetReg(op) => { assert_eq!(op.size(), 4); match op.dest_reg() { - LowLevelILRegister::ArchReg(reg) => assert_eq!(reg.name(), "ebp"), + LowLevelILRegisterKind::Arch(reg) => assert_eq!(reg.name(), "ebp"), _ => panic!("Expected Register::ArchReg"), } assert_eq!(op.source_expr().index, LowLevelExpressionIndex(19)); @@ -225,3 +225,86 @@ fn test_llil_visitor() { }; } } + +#[test] +fn test_llil_ssa() { + let _session = Session::new().expect("Failed to initialize session"); + let out_dir = env!("OUT_DIR").parse::().unwrap(); + let view = binaryninja::load(out_dir.join("atox.obj")).expect("Failed to create view"); + let image_base = view.original_image_base(); + let platform = view.default_platform().unwrap(); + + // Sample function: __crt_strtox::c_string_character_source::validate + let sample_function = view.function_at(&platform, image_base + 0x2bd80).unwrap(); + let llil_function = sample_function.low_level_il().unwrap(); + let llil_ssa_function = llil_function.ssa_form().expect("Valid SSA form"); + + let llil_ssa_basic_blocks = llil_ssa_function.basic_blocks(); + let mut llil_ssa_basic_block_iter = llil_ssa_basic_blocks.iter(); + let first_basic_block = llil_ssa_basic_block_iter.next().unwrap(); + let mut llil_instr_iter = first_basic_block.iter(); + + // 0 @ 0002bd80 (LLIL_SET_REG_SSA.d edi#1 = (LLIL_REG_SSA.d edi#0)) + let ssa_instr_0 = llil_instr_iter.next().unwrap(); + assert_eq!(ssa_instr_0.index, LowLevelInstructionIndex(0)); + assert_eq!(ssa_instr_0.address(), image_base + 0x0002bd80); + println!("{:?}", ssa_instr_0); + println!("{:?}", ssa_instr_0.kind()); + match ssa_instr_0.kind() { + LowLevelILInstructionKind::SetRegSsa(op) => { + assert_eq!(op.size(), 4); + match op.dest_reg() { + LowLevelILSSARegisterKind::Full { kind, version } => { + assert_eq!(kind.name(), "edi"); + assert_eq!(version, 1); + } + _ => panic!("Expected LowLevelILSSARegisterKind::Full"), + } + assert_eq!(op.source_expr().index, LowLevelExpressionIndex(0)); + } + _ => panic!("Expected SetRegSsa"), + } + + // 1 @ 0002bd82 (LLIL_STORE_SSA.d [(LLIL_SUB.d (LLIL_REG_SSA.d esp#0) - (LLIL_CONST.d 4)) {__saved_ebp}].d = (LLIL_REG_SSA.d ebp#0) @ mem#0 -> mem#1) + let ssa_instr_1 = llil_instr_iter.next().unwrap(); + assert_eq!(ssa_instr_1.index, LowLevelInstructionIndex(1)); + assert_eq!(ssa_instr_1.address(), image_base + 0x0002bd82); + println!("{:?}", ssa_instr_1); + println!("{:?}", ssa_instr_1.kind()); + match ssa_instr_1.kind() { + LowLevelILInstructionKind::StoreSsa(op) => { + assert_eq!(op.size(), 4); + let source_expr = op.source_expr(); + let source_memory_version = op.source_memory_version(); + assert_eq!(source_memory_version, 0); + assert_eq!(source_expr.index, LowLevelExpressionIndex(5)); + let dest_expr = op.dest_expr(); + let dest_memory_version = op.dest_memory_version(); + assert_eq!(dest_memory_version, 1); + assert_eq!(dest_expr.index, LowLevelExpressionIndex(4)); + } + _ => panic!("Expected StoreSsa"), + } + + // 34 @ 0002bdc7 (LLIL_CALL_SSA eax#8, edx#5, ecx#6, mem#23 = call((LLIL_EXTERN_PTR.d __CrtDbgReportW), stack = esp#18 @ mem#22)) + let ssa_instr_34 = llil_ssa_function + .instruction_from_index(LowLevelInstructionIndex(34)) + .expect("Valid instruction"); + assert_eq!(ssa_instr_34.index, LowLevelInstructionIndex(34)); + assert_eq!(ssa_instr_34.address(), image_base + 0x0002bdc7); + println!("{:?}", ssa_instr_34); + println!("{:?}", ssa_instr_34.kind()); + match ssa_instr_34.kind() { + LowLevelILInstructionKind::CallSsa(op) => match op.target().kind() { + LowLevelILExpressionKind::ExternPtr(extern_ptr) => { + assert_eq!(extern_ptr.size(), 4); + let extern_sym = view + .symbol_by_address(extern_ptr.value()) + .expect("Valid symbol"); + assert_eq!(extern_sym.short_name(), "__CrtDbgReportW".into()) + } + _ => panic!("Expected ExternPtr"), + }, + _ => panic!("Expected CallSsa"), + } +} diff --git a/rust/tests/medium_level_il.rs b/rust/tests/medium_level_il.rs index 544076ccae..b8c1c108bf 100644 --- a/rust/tests/medium_level_il.rs +++ b/rust/tests/medium_level_il.rs @@ -1,6 +1,8 @@ use binaryninja::binary_view::BinaryViewExt; use binaryninja::headless::Session; -use binaryninja::medium_level_il::{MediumLevelILInstructionKind, MediumLevelInstructionIndex}; +use binaryninja::medium_level_il::{ + MediumLevelILInstructionKind, MediumLevelILLiftedInstructionKind, MediumLevelInstructionIndex, +}; use std::path::PathBuf; #[test] @@ -68,6 +70,14 @@ fn test_mlil_info() { } _ => panic!("Expected Call"), } + match instr_3.lift().kind { + MediumLevelILLiftedInstructionKind::Call(lifted_call) => { + assert_eq!(lifted_call.dest.index, MediumLevelInstructionIndex(7)); + assert_eq!(lifted_call.output.len(), 1); + assert_eq!(lifted_call.params.len(), 1); + } + _ => panic!("Expected Call"), + } // 4 @ 00025f22 (MLIL_RET return (MLIL_VAR.d eax_1)) let instr_4 = mlil_instr_iter.next().unwrap(); assert_eq!(instr_4.expr_index, MediumLevelInstructionIndex(13)); diff --git a/rust/tests/metadata.rs b/rust/tests/metadata.rs new file mode 100644 index 0000000000..6b67c5e05a --- /dev/null +++ b/rust/tests/metadata.rs @@ -0,0 +1,50 @@ +use binaryninja::metadata::{Metadata, MetadataType}; +use binaryninja::rc::Ref; +use std::collections::HashMap; + +#[test] +fn basic_metadata() { + let metadata = Metadata::new_of_type(MetadataType::UnsignedIntegerDataType); + assert_eq!(metadata.get_type(), MetadataType::UnsignedIntegerDataType); + assert_eq!(metadata.get_unsigned_integer(), Some(0)); + + let metadata_0: Ref = 1u64.into(); + assert_eq!(metadata_0.get_type(), MetadataType::UnsignedIntegerDataType); + assert_eq!(metadata_0.get_unsigned_integer(), Some(1)); + + let metadata_1: Ref = true.into(); + assert_eq!(metadata_1.get_type(), MetadataType::BooleanDataType); + assert_eq!(metadata_1.get_boolean(), Some(true)); + + let metadata_2: Ref = 0.55f64.into(); + assert_eq!(metadata_2.get_type(), MetadataType::DoubleDataType); + assert_eq!(metadata_2.get_double(), Some(0.55f64)); + + let metadata_3: Ref = From::from(&vec![1i64, 2i64]); + assert_eq!(metadata_3.get_type(), MetadataType::ArrayDataType); + assert_eq!(metadata_3.get_signed_integer_list(), Some(vec![1i64, 2i64])); + + let metadata_4: Ref = From::from(&vec![1.55f64, 2.55f64]); + assert_eq!(metadata_4.get_type(), MetadataType::ArrayDataType); + assert_eq!(metadata_4.get_double_list(), Some(vec![1.55f64, 2.55f64])); +} + +#[test] +fn object_metadata() { + let metadata = Metadata::new_of_type(MetadataType::UnsignedIntegerDataType); + assert_eq!(metadata.get_type(), MetadataType::UnsignedIntegerDataType); + assert_eq!(metadata.get_unsigned_integer(), Some(0)); + + let mut map = HashMap::new(); + map.insert("key", 1u64); + + let metadata_0: Ref = From::from(map); + assert_eq!(metadata_0.get_type(), MetadataType::KeyValueDataType); + + let value_store = metadata_0 + .get_value_store() + .expect("Expected a value store"); + let key_value = value_store.get("key").expect("Expected a key to exist"); + assert_eq!(key_value.get_type(), MetadataType::UnsignedIntegerDataType); + assert_eq!(key_value.get_unsigned_integer(), Some(1)); +} diff --git a/rust/tests/project.rs b/rust/tests/project.rs index f7d49badfa..0b03e64767 100644 --- a/rust/tests/project.rs +++ b/rust/tests/project.rs @@ -152,7 +152,7 @@ fn modify_project() { .unwrap(); assert_eq!(project.folders().unwrap().len(), 5); - let last_folder = project.folder_by_id(folder_5.id()).unwrap(); + let last_folder = project.folder_by_id(&folder_5.id()).unwrap(); project.delete_folder(&last_folder).unwrap(); assert_eq!(project.folders().unwrap().len(), 4); drop(folder_5); @@ -245,8 +245,8 @@ fn modify_project() { .unwrap(); assert_eq!(project.files().len(), 10); - let file_a = project.file_by_id(file_8.id()).unwrap(); - let file_b = project.file_by_path(file_7.path_on_disk()).unwrap(); + let file_a = project.file_by_id(&file_8.id()).unwrap(); + let file_b = project.file_by_path(&file_7.path_on_disk()).unwrap(); project.delete_file(&file_a); project.delete_file(&file_b); assert_eq!(project.files().len(), 8); diff --git a/rust/tests/render_layer.rs b/rust/tests/render_layer.rs index 05ea17f6bd..6f6ebc9818 100644 --- a/rust/tests/render_layer.rs +++ b/rust/tests/render_layer.rs @@ -13,7 +13,7 @@ fn test_render_layer_register() { struct EmptyRenderLayer; impl RenderLayer for EmptyRenderLayer {} register_render_layer("Test Render Layer", EmptyRenderLayer, Default::default()); - CoreRenderLayer::render_layer_by_name("Test Render Layer").expect("Failed to get render layer"); + CoreRenderLayer::from_name("Test Render Layer").expect("Failed to get render layer"); } #[test] diff --git a/rust/tests/repository.rs b/rust/tests/repository.rs index b1efee11e9..ac861b2cde 100644 --- a/rust/tests/repository.rs +++ b/rust/tests/repository.rs @@ -8,7 +8,7 @@ fn test_list() { let repositories = manager.repositories(); for repository in &repositories { let repo_path = repository.path(); - let repository_by_path = manager.repository_by_path(repo_path).unwrap(); + let repository_by_path = manager.repository_by_path(&repo_path).unwrap(); assert_eq!(repository.url(), repository_by_path.url()); } @@ -19,7 +19,7 @@ fn test_list() { let plugins = repository.plugins(); for plugin in &plugins { let plugin_path = plugin.path(); - let plugin_by_path = repository.plugin_by_path(plugin_path).unwrap(); + let plugin_by_path = repository.plugin_by_path(&plugin_path).unwrap(); assert_eq!(plugin.package_url(), plugin_by_path.package_url()); } } diff --git a/rust/tests/section.rs b/rust/tests/section.rs new file mode 100644 index 0000000000..ea3d8e06f1 --- /dev/null +++ b/rust/tests/section.rs @@ -0,0 +1,56 @@ +use binaryninja::binary_view::BinaryViewExt; +use binaryninja::headless::Session; +use binaryninja::section::{SectionBuilder, Semantics}; +use std::path::PathBuf; + +#[test] +fn test_binary_section() { + let _session = Session::new().expect("Failed to initialize session"); + let out_dir = env!("OUT_DIR").parse::().unwrap(); + let view = binaryninja::load(out_dir.join("atox.obj")).expect("Failed to create view"); + // This binary has a lot of sections! + assert_eq!(view.sections().len(), 139); + + // Make sure we are not grabbing garbage. + assert_eq!(view.section_by_name("test"), None); + + // Just test a bunch of properties of a section. + let section = view.section_by_name(".rdata").unwrap(); + assert_eq!(section.name(), ".rdata".into()); + let image_base = view.original_image_base(); + let section_start = image_base + 0x25efc; + let section_end = image_base + 0x25f04; + assert_eq!(section.start(), section_start); + assert_eq!(section.end(), image_base + 0x25f04); + assert_eq!(section.len(), 0x8); + assert_eq!(section.address_range(), section_start..section_end); + assert_eq!(section.auto_defined(), true); + assert_eq!(section.semantics(), Semantics::ReadOnlyData); + + let sections_at = view.sections_at(image_base + 0x25efc); + assert_eq!(sections_at.len(), 1); + let same_section = sections_at.to_vec()[0].to_owned(); + assert_eq!(section, same_section); +} + +#[test] +fn test_add_remove_section() { + let _session = Session::new().expect("Failed to initialize session"); + let out_dir = env!("OUT_DIR").parse::().unwrap(); + let view = binaryninja::load(out_dir.join("atox.obj")).expect("Failed to create view"); + + // Remove the rdata section! + let old_section = view + .section_by_name(".rdata") + .expect("Failed to find rdata section"); + view.remove_auto_section(".rdata"); + assert!(view.section_by_name(".rdata").is_none()); + + // Add a new section and compare with the old section + let new_section_builder = SectionBuilder::from(&old_section); + view.add_section(new_section_builder); + let new_section = view + .section_by_name(".rdata") + .expect("Failed to find new section"); + assert_eq!(old_section, new_section); +} diff --git a/rust/tests/string.rs b/rust/tests/string.rs new file mode 100644 index 0000000000..4f33394059 --- /dev/null +++ b/rust/tests/string.rs @@ -0,0 +1,51 @@ +use binaryninja::string::{BnString, IntoCStr}; +use std::ffi::{CStr, CString}; + +#[test] +fn test_bnstring() { + // Test a basic ASCII string + let str_0 = BnString::new("test"); + assert_eq!(str_0.to_string_lossy(), "test"); + assert_eq!(str_0.to_bytes_with_nul(), b"test\0"); + + // Test non-UTF8 bytes + let invalid_utf8 = CStr::from_bytes_with_nul(&[0x48, 0x65, 0x6C, 0x6C, 0x6F, 0xFF, 0x00]) + .expect("Failed to create string"); + let str_2 = BnString::new(invalid_utf8); + assert_eq!(str_2.to_string_lossy(), "Hello�"); + assert_eq!( + str_2.to_bytes_with_nul(), + &[0x48, 0x65, 0x6C, 0x6C, 0x6F, 0xFF, 0x00] + ); + + // Test empty string + let str_3 = BnString::new(""); + assert_eq!(str_3.to_string_lossy(), ""); + assert_eq!(str_3.to_bytes_with_nul(), b"\0"); + + // Test string with Unicode + let str_4 = BnString::new("Hello 世界"); + assert_eq!(str_4.to_string_lossy(), "Hello 世界"); + assert_eq!( + str_4.to_bytes_with_nul(), + b"Hello \xE4\xB8\x96\xE7\x95\x8C\0" + ); +} + +#[test] +fn test_cstr() { + let str_0 = BnString::new("test"); + let cstr_0: BnString = str_0.to_cstr(); + assert_eq!(cstr_0.to_str().unwrap(), "test"); + assert_eq!(cstr_0.to_bytes_with_nul(), b"test\0"); + + let str_1 = String::from("test"); + let cstr_1: CString = str_1.to_cstr(); + assert_eq!(cstr_1.to_str().unwrap(), "test"); + assert_eq!(cstr_1.to_bytes_with_nul(), b"test\0"); + + let str_2 = "test"; + let cstr_2: CString = str_2.to_cstr(); + assert_eq!(cstr_2.to_str().unwrap(), "test"); + assert_eq!(cstr_2.to_bytes_with_nul(), b"test\0"); +} diff --git a/rust/tests/type_container.rs b/rust/tests/type_container.rs index 4a0175411e..96f3bc4a0e 100644 --- a/rust/tests/type_container.rs +++ b/rust/tests/type_container.rs @@ -49,7 +49,7 @@ fn test_add_delete_type() { .type_id("mytype") .expect("mytype not found"); assert!( - view_type_container.delete_type(my_type_id), + view_type_container.delete_type(&my_type_id), "Type was deleted!" ); // There should be no type ids if the type was actually deleted diff --git a/rust/tests/type_library.rs b/rust/tests/type_library.rs new file mode 100644 index 0000000000..83937386e0 --- /dev/null +++ b/rust/tests/type_library.rs @@ -0,0 +1,101 @@ +use binaryninja::binary_view::BinaryViewExt; +use binaryninja::headless::Session; +use binaryninja::platform::Platform; +use binaryninja::type_library::TypeLibrary; +use binaryninja::types::{Type, TypeClass}; +use std::path::PathBuf; + +#[test] +fn test_type_library() { + let _session = Session::new().expect("Failed to initialize session"); + let platform = Platform::by_name("windows-x86").expect("windows-x86 exists"); + let library = platform + .get_type_library_by_name("crypt32.dll") + .expect("crypt32.dll exists"); + + println!("{:#?}", library); + assert_eq!(library.name(), "crypt32.dll"); + assert_eq!(library.dependency_name(), "crypt32.dll"); + assert!(library.alternate_names().is_empty()); + assert_eq!(library.platform_names().to_vec(), vec!["windows-x86"]); + + // Check some types. + let type_0 = library + .get_named_type("SIP_ADD_NEWPROVIDER".into()) + .unwrap(); + println!("{:#?}", type_0); + assert_eq!(type_0.width(), 48); + assert_eq!(type_0.type_class(), TypeClass::StructureTypeClass); +} + +#[test] +fn test_applying_type_library() { + let _session = Session::new().expect("Failed to initialize session"); + let platform = Platform::by_name("windows-x86").expect("windows-x86 exists"); + let library = platform + .get_type_library_by_name("crypt32.dll") + .expect("crypt32.dll exists"); + + let out_dir = env!("OUT_DIR").parse::().unwrap(); + let view = binaryninja::load(out_dir.join("atox.obj")).expect("Failed to create view"); + view.add_type_library(&library); + + let view_library = view + .type_library_by_name("crypt32.dll") + .expect("crypt32.dll exists"); + assert_eq!(view_library.name(), "crypt32.dll"); + + // Type library types don't exist in the view until they are imported. + // Adding the type library to the view will let you import types from it without necessarily knowing "where" they came from. + let found_lib_type = view + .import_type_library("SIP_ADD_NEWPROVIDER", None) + .expect("SIP_ADD_NEWPROVIDER exists"); + assert_eq!(found_lib_type.width(), 48); + // Per docs type is returned as a NamedTypeReferenceClass. + assert_eq!( + found_lib_type.type_class(), + TypeClass::NamedTypeReferenceClass + ); + + // Check that the type is actually in the view now. + view.type_by_name("SIP_ADD_NEWPROVIDER") + .expect("SIP_ADD_NEWPROVIDER exists"); +} + +#[test] +fn test_create_type_library() { + let _session = Session::new().expect("Failed to initialize session"); + let platform = Platform::by_name("windows-x86").expect("windows-x86 exists"); + let arch = platform.arch(); + + // Create the new type library. + let my_library = TypeLibrary::new(arch, "test_type_lib"); + my_library.add_alternate_name("alternate_test"); + my_library.add_platform(&platform); + my_library.add_named_type("test_type".into(), &Type::int(7, true)); + + // Write the library to a file. + let temp_dir = tempfile::tempdir().expect("Failed to create temp dir"); + let my_library_path = temp_dir.path().join("test_type_lib.bntl"); + assert!(my_library.write_to_file(&my_library_path)); + + // Verify the contents of the created file. + let loaded_library = + TypeLibrary::load_from_file(&my_library_path).expect("Failed to load type library"); + assert_eq!(loaded_library.name(), "test_type_lib"); + assert_eq!( + loaded_library.alternate_names().to_vec(), + vec!["alternate_test"] + ); + assert_eq!( + loaded_library.platform_names().to_vec(), + vec!["windows-x86"] + ); + assert_eq!( + loaded_library + .get_named_type("test_type".into()) + .unwrap() + .width(), + 7 + ); +} diff --git a/rust/tests/type_printer.rs b/rust/tests/type_printer.rs new file mode 100644 index 0000000000..4ff67232da --- /dev/null +++ b/rust/tests/type_printer.rs @@ -0,0 +1,169 @@ +use binaryninja::binary_view::BinaryView; +use binaryninja::disassembly::InstructionTextToken; +use binaryninja::headless::Session; +use binaryninja::platform::Platform; +use binaryninja::rc::Ref; +use binaryninja::type_container::TypeContainer; +use binaryninja::type_printer::{ + register_type_printer, CoreTypePrinter, TokenEscapingType, TypeDefinitionLine, TypePrinter, +}; +use binaryninja::types::{ + MemberAccess, MemberScope, QualifiedName, Structure, StructureMember, Type, +}; +use std::path::PathBuf; + +#[test] +fn test_type_printer() { + let _session = Session::new().expect("Failed to initialize session"); + let out_dir = env!("OUT_DIR").parse::().unwrap(); + let view = binaryninja::load(out_dir.join("atox.obj")).expect("Failed to create view"); + + 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)], + &view, + 4, + TokenEscapingType::NoTokenEscapingType, + ) + .expect("Failed to print types"); + + // TODO: Assert this + /* + // "my_struct" + struct my_struct + { + uint32_t my_field; + }; + */ + + println!("{:#?}", printed_types); +} + +struct MyTypePrinter; + +impl TypePrinter for MyTypePrinter { + fn get_type_tokens>( + &self, + _type_: Ref, + _platform: Option>, + _name: T, + _base_confidence: u8, + _escaping: TokenEscapingType, + ) -> Option> { + todo!() + } + + fn get_type_tokens_before_name( + &self, + _type_: Ref, + _platform: Option>, + _base_confidence: u8, + _parent_type: Option>, + _escaping: TokenEscapingType, + ) -> Option> { + todo!() + } + + fn get_type_tokens_after_name( + &self, + _type_: Ref, + _platform: Option>, + _base_confidence: u8, + _parent_type: Option>, + _escaping: TokenEscapingType, + ) -> Option> { + todo!() + } + + fn get_type_string>( + &self, + _type_: Ref, + _platform: Option>, + _name: T, + _escaping: TokenEscapingType, + ) -> Option { + todo!() + } + + fn get_type_string_before_name( + &self, + _type_: Ref, + _platform: Option>, + _escaping: TokenEscapingType, + ) -> Option { + todo!() + } + + fn get_type_string_after_name( + &self, + _type_: Ref, + _platform: Option>, + _escaping: TokenEscapingType, + ) -> Option { + todo!() + } + + fn get_type_lines>( + &self, + _type_: Ref, + _types: &TypeContainer, + _name: T, + _padding_cols: isize, + _collapsed: bool, + _escaping: TokenEscapingType, + ) -> Option> { + todo!() + } + + fn print_all_types( + &self, + names: Vec, + types: Vec>, + _data: Ref, + padding_cols: isize, + escaping: TokenEscapingType, + ) -> Option { + let printed = format!("{:?}, {:?}, {}, {:?}", names, types, padding_cols, escaping); + Some(printed) + } +} + +#[test] +fn test_custom_type_printer() { + let _session = Session::new().expect("Failed to initialize session"); + let out_dir = env!("OUT_DIR").parse::().unwrap(); + let view = binaryninja::load(out_dir.join("atox.obj")).expect("Failed to create view"); + + let type_printer = MyTypePrinter; + register_type_printer("my_type_printer", type_printer); + + let core_type_printer = CoreTypePrinter::printer_by_name("my_type_printer") + .expect("Failed to get core type printer"); + let printed_types = core_type_printer + .print_all_types( + vec![("test", Type::int(4, false))], + &view, + 0, + TokenEscapingType::NoTokenEscapingType, + ) + .expect("Failed to print types"); + + // TODO: Assert this + println!("{:#?}", printed_types); +} diff --git a/rust/tests/types.rs b/rust/tests/types.rs index 4ed4caf7d6..1768277795 100644 --- a/rust/tests/types.rs +++ b/rust/tests/types.rs @@ -65,7 +65,7 @@ fn add_type_to_view() { empty_view.define_auto_type("test", "me", &test_type); assert!(empty_view.type_by_name("test").is_some()); empty_view.undefine_auto_type( - empty_view + &empty_view .type_id_by_name("test") .expect("Failed to get type id"), ); diff --git a/rust/tests/websocket.rs b/rust/tests/websocket.rs index 97a4ae2bac..2c25a81095 100644 --- a/rust/tests/websocket.rs +++ b/rust/tests/websocket.rs @@ -1,6 +1,5 @@ use binaryninja::headless::Session; use binaryninja::rc::Ref; -use binaryninja::string::BnStrCompatible; use binaryninja::websocket::{ register_websocket_provider, CoreWebsocketClient, CoreWebsocketProvider, WebsocketClient, WebsocketClientCallback, WebsocketProvider, @@ -31,11 +30,9 @@ impl WebsocketClient for MyWebsocketClient { Self { core } } - fn connect(&self, host: &str, _headers: I) -> bool + fn connect(&self, host: &str, _headers: I) -> bool where - I: IntoIterator, - K: BnStrCompatible, - V: BnStrCompatible, + I: IntoIterator, { assert_eq!(host, "url"); true @@ -89,7 +86,11 @@ fn reg_websocket_provider() { let provider = register_websocket_provider::("RustWebsocketProvider"); let client = provider.create_client().unwrap(); let mut callback = MyClientCallbacks::default(); - let success = client.initialize_connection("url", [("header", "value")], &mut callback); + let success = client.initialize_connection( + "url", + [("header".to_string(), "value".to_string())], + &mut callback, + ); assert!(success, "Failed to initialize connection!"); } @@ -100,7 +101,11 @@ fn listen_websocket_provider() { let client = provider.create_client().unwrap(); let mut callback = MyClientCallbacks::default(); - client.initialize_connection("url", [("header", "value")], &mut callback); + client.initialize_connection( + "url", + [("header".to_string(), "value".to_string())], + &mut callback, + ); assert!(client.write("test1".as_bytes())); assert!(client.write("test2".as_bytes()));