use binaryninjacore_sys::*;
use crate::{
binaryview::BinaryView,
platform::Platform,
rc::*,
string::{raw_to_string, BnStrCompatible, BnString},
types::{DataVariableAndName, NameAndType, NamedTypedVariable, Type},
};
use std::{hash::Hash, os::raw::c_void, ptr, slice};
struct ProgressContext(Option<Box<dyn Fn(usize, usize) -> Result<(), ()>>>);
#[derive(PartialEq, Eq, Hash)]
pub struct DebugInfoParser {
pub(crate) handle: *mut BNDebugInfoParser,
}
impl DebugInfoParser {
pub(crate) unsafe fn from_raw(handle: *mut BNDebugInfoParser) -> Ref<Self> {
debug_assert!(!handle.is_null());
Ref::new(Self { handle })
}
pub fn from_name<S: BnStrCompatible>(name: S) -> Result<Ref<Self>, ()> {
let name = name.into_bytes_with_nul();
let parser = unsafe { BNGetDebugInfoParserByName(name.as_ref().as_ptr() as *mut _) };
if parser.is_null() {
Err(())
} else {
unsafe { Ok(Self::from_raw(parser)) }
}
}
pub fn list() -> Array<DebugInfoParser> {
let mut count = 0;
let raw_parsers = unsafe { BNGetDebugInfoParsers(&mut count as *mut _) };
unsafe { Array::new(raw_parsers, count, ()) }
}
pub fn parsers_for_view(bv: &BinaryView) -> Array<DebugInfoParser> {
let mut count = 0;
let raw_parsers = unsafe { BNGetDebugInfoParsersForView(bv.handle, &mut count as *mut _) };
unsafe { Array::new(raw_parsers, count, ()) }
}
pub fn name(&self) -> BnString {
unsafe { BnString::from_raw(BNGetDebugInfoParserName(self.handle)) }
}
pub fn is_valid_for_view(&self, view: &BinaryView) -> bool {
unsafe { BNIsDebugInfoParserValidForView(self.handle, view.handle) }
}
extern "C" fn cb_progress(ctxt: *mut c_void, cur: usize, max: usize) -> bool {
ffi_wrap!("DebugInfoParser::cb_progress", unsafe {
let progress = ctxt as *mut ProgressContext;
match &(*progress).0 {
Some(func) => (func)(cur, max).is_ok(),
None => true,
}
})
}
pub fn parse_debug_info(
&self,
view: &BinaryView,
debug_file: &BinaryView,
existing_debug_info: Option<&DebugInfo>,
progress: Option<Box<dyn Fn(usize, usize) -> Result<(), ()>>>,
) -> Option<Ref<DebugInfo>> {
let mut progress_raw = ProgressContext(progress);
let info: *mut BNDebugInfo = match existing_debug_info {
Some(debug_info) => unsafe {
BNParseDebugInfo(
self.handle,
view.handle,
debug_file.handle,
debug_info.handle,
Some(Self::cb_progress),
&mut progress_raw as *mut _ as *mut c_void,
)
},
None => unsafe {
BNParseDebugInfo(
self.handle,
view.handle,
debug_file.handle,
ptr::null_mut(),
Some(Self::cb_progress),
&mut progress_raw as *mut _ as *mut c_void,
)
},
};
if info.is_null() {
return None;
}
Some(unsafe { DebugInfo::from_raw(info) })
}
pub fn register<S, C>(name: S, parser_callbacks: C) -> Ref<Self>
where
S: BnStrCompatible,
C: CustomDebugInfoParser,
{
extern "C" fn cb_is_valid<C>(ctxt: *mut c_void, view: *mut BNBinaryView) -> bool
where
C: CustomDebugInfoParser,
{
ffi_wrap!("CustomDebugInfoParser::is_valid", unsafe {
let cmd = &*(ctxt as *const C);
let view = BinaryView::from_raw(view);
cmd.is_valid(&view)
})
}
extern "C" fn cb_parse_info<C>(
ctxt: *mut c_void,
debug_info: *mut BNDebugInfo,
view: *mut BNBinaryView,
debug_file: *mut BNBinaryView,
progress: Option<unsafe extern "C" fn(*mut c_void, usize, usize) -> bool>,
progress_ctxt: *mut c_void,
) -> bool
where
C: CustomDebugInfoParser,
{
ffi_wrap!("CustomDebugInfoParser::parse_info", unsafe {
let cmd = &*(ctxt as *const C);
let view = BinaryView::from_raw(view);
let debug_file = BinaryView::from_raw(debug_file);
let mut debug_info = DebugInfo::from_raw(debug_info);
cmd.parse_info(
&mut debug_info,
&view,
&debug_file,
Box::new(move |cur: usize, max: usize| match progress {
Some(func) => {
if func(progress_ctxt, cur, max) {
Ok(())
} else {
Err(())
}
}
_ => Ok(()),
}),
)
})
}
let name = name.into_bytes_with_nul();
let name_ptr = name.as_ref().as_ptr() as *mut _;
let ctxt = Box::into_raw(Box::new(parser_callbacks));
unsafe {
DebugInfoParser::from_raw(BNRegisterDebugInfoParser(
name_ptr,
Some(cb_is_valid::<C>),
Some(cb_parse_info::<C>),
ctxt as *mut _,
))
}
}
}
unsafe impl RefCountable for DebugInfoParser {
unsafe fn inc_ref(handle: &Self) -> Ref<Self> {
Ref::new(Self {
handle: BNNewDebugInfoParserReference(handle.handle),
})
}
unsafe fn dec_ref(handle: &Self) {
BNFreeDebugInfoParserReference(handle.handle);
}
}
impl ToOwned for DebugInfoParser {
type Owned = Ref<Self>;
fn to_owned(&self) -> Self::Owned {
unsafe { RefCountable::inc_ref(self) }
}
}
impl CoreArrayProvider for DebugInfoParser {
type Raw = *mut BNDebugInfoParser;
type Context = ();
type Wrapped<'a> = Guard<'a, DebugInfoParser>;
}
unsafe impl CoreArrayProviderInner for DebugInfoParser {
unsafe fn free(raw: *mut Self::Raw, count: usize, _: &Self::Context) {
BNFreeDebugInfoParserList(raw, count);
}
unsafe fn wrap_raw<'a>(raw: &'a Self::Raw, context: &'a Self::Context) -> Self::Wrapped<'a> {
Guard::new(Self { handle: *raw }, context)
}
}
pub struct DebugFunctionInfo {
short_name: Option<String>,
full_name: Option<String>,
raw_name: Option<String>,
type_: Option<Ref<Type>>,
address: u64,
platform: Option<Ref<Platform>>,
components: Vec<String>,
local_variables: Vec<NamedTypedVariable>,
}
impl From<&BNDebugFunctionInfo> for DebugFunctionInfo {
fn from(raw: &BNDebugFunctionInfo) -> Self {
let components = unsafe { slice::from_raw_parts(raw.components, raw.componentN) }
.iter()
.map(|component| raw_to_string(*component as *const _).unwrap())
.collect();
let local_variables: Vec<NamedTypedVariable> = unsafe { slice::from_raw_parts(raw.localVariables, raw.localVariableN) }
.iter()
.map(|local_variable| {
unsafe {
NamedTypedVariable::from_raw(local_variable)
}
})
.collect();
Self {
short_name: raw_to_string(raw.shortName),
full_name: raw_to_string(raw.fullName),
raw_name: raw_to_string(raw.rawName),
type_: if raw.type_.is_null() {
None
} else {
Some(unsafe { Type::ref_from_raw(raw.type_) })
},
address: raw.address,
platform: if raw.platform.is_null() {
None
} else {
Some(unsafe { Platform::ref_from_raw(raw.platform) })
},
components,
local_variables,
}
}
}
impl DebugFunctionInfo {
#[allow(clippy::too_many_arguments)]
pub fn new(
short_name: Option<String>,
full_name: Option<String>,
raw_name: Option<String>,
type_: Option<Ref<Type>>,
address: Option<u64>,
platform: Option<Ref<Platform>>,
components: Vec<String>,
local_variables: Vec<NamedTypedVariable>,
) -> Self {
Self {
short_name,
full_name,
raw_name,
type_,
address: address.unwrap_or(0),
platform,
components,
local_variables,
}
}
}
#[derive(PartialEq, Eq, Hash)]
pub struct DebugInfo {
pub(crate) handle: *mut BNDebugInfo,
}
impl DebugInfo {
pub(crate) unsafe fn from_raw(handle: *mut BNDebugInfo) -> Ref<Self> {
debug_assert!(!handle.is_null());
Ref::new(Self { handle })
}
pub fn types_by_name<S: BnStrCompatible>(&self, parser_name: S) -> Vec<Ref<NameAndType>> {
let parser_name = parser_name.into_bytes_with_nul();
let mut count: usize = 0;
let debug_types_ptr = unsafe {
BNGetDebugTypes(
self.handle,
parser_name.as_ref().as_ptr() as *mut _,
&mut count,
)
};
let result: Vec<Ref<NameAndType>> = unsafe {
slice::from_raw_parts_mut(debug_types_ptr, count)
.iter()
.map(|x| NameAndType::from_raw(x).to_owned())
.collect()
};
unsafe { BNFreeDebugTypes(debug_types_ptr, count) };
result
}
pub fn types(&self) -> Vec<Ref<NameAndType>> {
let mut count: usize = 0;
let debug_types_ptr = unsafe { BNGetDebugTypes(self.handle, ptr::null_mut(), &mut count) };
let result: Vec<Ref<NameAndType>> = unsafe {
slice::from_raw_parts_mut(debug_types_ptr, count)
.iter()
.map(|x| NameAndType::from_raw(x).to_owned())
.collect()
};
unsafe { BNFreeDebugTypes(debug_types_ptr, count) };
result
}
pub fn functions_by_name<S: BnStrCompatible>(
&self,
parser_name: S
) -> Vec<DebugFunctionInfo> {
let parser_name = parser_name.into_bytes_with_nul();
let mut count: usize = 0;
let functions_ptr = unsafe {
BNGetDebugFunctions(
self.handle,
parser_name.as_ref().as_ptr() as *mut _,
&mut count,
)
};
let result: Vec<DebugFunctionInfo> = unsafe {
slice::from_raw_parts_mut(functions_ptr, count)
.iter()
.map(DebugFunctionInfo::from)
.collect()
};
unsafe { BNFreeDebugFunctions(functions_ptr, count) };
result
}
pub fn functions(&self) -> Vec<DebugFunctionInfo> {
let mut count: usize = 0;
let functions_ptr =
unsafe { BNGetDebugFunctions(self.handle, ptr::null_mut(), &mut count) };
let result: Vec<DebugFunctionInfo> = unsafe {
slice::from_raw_parts_mut(functions_ptr, count)
.iter()
.map(DebugFunctionInfo::from)
.collect()
};
unsafe { BNFreeDebugFunctions(functions_ptr, count) };
result
}
pub fn data_variables_by_name<S: BnStrCompatible>(
&self,
parser_name: S,
) -> Vec<DataVariableAndName<String>> {
let parser_name = parser_name.into_bytes_with_nul();
let mut count: usize = 0;
let data_variables_ptr = unsafe {
BNGetDebugDataVariables(
self.handle,
parser_name.as_ref().as_ptr() as *mut _,
&mut count,
)
};
let result: Vec<DataVariableAndName<String>> = unsafe {
slice::from_raw_parts_mut(data_variables_ptr, count)
.iter()
.map(DataVariableAndName::<String>::from_raw)
.collect()
};
unsafe { BNFreeDataVariablesAndName(data_variables_ptr, count) };
result
}
pub fn data_variables(&self) -> Vec<DataVariableAndName<String>> {
let mut count: usize = 0;
let data_variables_ptr =
unsafe { BNGetDebugDataVariables(self.handle, ptr::null_mut(), &mut count) };
let result: Vec<DataVariableAndName<String>> = unsafe {
slice::from_raw_parts_mut(data_variables_ptr, count)
.iter()
.map(DataVariableAndName::<String>::from_raw)
.collect()
};
unsafe { BNFreeDataVariablesAndName(data_variables_ptr, count) };
result
}
pub fn type_by_name<S: BnStrCompatible>(&self, parser_name: S, name: S) -> Option<Ref<Type>> {
let parser_name = parser_name.into_bytes_with_nul();
let name = name.into_bytes_with_nul();
let result = unsafe {
BNGetDebugTypeByName(
self.handle,
parser_name.as_ref().as_ptr() as *mut _,
name.as_ref().as_ptr() as *mut _,
)
};
if !result.is_null() {
Some(unsafe { Type::ref_from_raw(result) })
} else {
None
}
}
pub fn get_data_variable_by_name<S: BnStrCompatible>(
&self,
parser_name: S,
name: S,
) -> Option<(u64, Ref<Type>)> {
let parser_name = parser_name.into_bytes_with_nul();
let name = name.into_bytes_with_nul();
let result = unsafe {
BNGetDebugDataVariableByName(
self.handle,
parser_name.as_ref().as_ptr() as *mut _,
name.as_ref().as_ptr() as *mut _,
)
};
if !result.is_null() {
unsafe { BNFreeString((*result).name) };
Some(unsafe { ((*result).address, Type::ref_from_raw((*result).type_)) })
} else {
None
}
}
pub fn get_data_variable_by_address<S: BnStrCompatible>(
&self,
parser_name: S,
address: u64,
) -> Option<(String, Ref<Type>)> {
let parser_name = parser_name.into_bytes_with_nul();
let name_and_var = unsafe {
BNGetDebugDataVariableByAddress(
self.handle,
parser_name.as_ref().as_ptr() as *mut _,
address,
)
};
if !name_and_var.is_null() {
let result = unsafe {
(
raw_to_string((*name_and_var).name).unwrap(),
Type::ref_from_raw((*name_and_var).type_),
)
};
unsafe { BNFreeString((*name_and_var).name) };
Some(result)
} else {
None
}
}
pub fn get_types_by_name<S: BnStrCompatible>(&self, name: S) -> Vec<(String, Ref<Type>)> {
let name = name.into_bytes_with_nul();
let mut count: usize = 0;
let raw_names_and_types = unsafe {
BNGetDebugTypesByName(self.handle, name.as_ref().as_ptr() as *mut _, &mut count)
};
let names_and_types: &[*mut BNNameAndType] =
unsafe { slice::from_raw_parts(raw_names_and_types as *mut _, count) };
let result = names_and_types
.iter()
.take(count)
.map(|&name_and_type| unsafe {
(
raw_to_string((*name_and_type).name).unwrap(),
Type::ref_from_raw(BNNewTypeReference((*name_and_type).type_)),
)
})
.collect();
unsafe { BNFreeNameAndTypeList(raw_names_and_types, count) };
result
}
pub fn get_data_variables_by_name<S: BnStrCompatible>(
&self,
name: S,
) -> Vec<(String, u64, Ref<Type>)> {
let name = name.into_bytes_with_nul();
let mut count: usize = 0;
let raw_variables_and_names = unsafe {
BNGetDebugDataVariablesByName(self.handle, name.as_ref().as_ptr() as *mut _, &mut count)
};
let variables_and_names: &[*mut BNDataVariableAndName] =
unsafe { slice::from_raw_parts(raw_variables_and_names as *mut _, count) };
let result = variables_and_names
.iter()
.take(count)
.map(|&variable_and_name| unsafe {
(
raw_to_string((*variable_and_name).name).unwrap(),
(*variable_and_name).address,
Type::ref_from_raw(BNNewTypeReference((*variable_and_name).type_)),
)
})
.collect();
unsafe { BNFreeDataVariablesAndName(raw_variables_and_names, count) };
result
}
pub fn get_data_variables_by_address(&self, address: u64) -> Vec<(String, String, Ref<Type>)> {
let mut count: usize = 0;
let raw_variables_and_names =
unsafe { BNGetDebugDataVariablesByAddress(self.handle, address, &mut count) };
let variables_and_names: &[*mut BNDataVariableAndNameAndDebugParser] =
unsafe { slice::from_raw_parts(raw_variables_and_names as *mut _, count) };
let result = variables_and_names
.iter()
.take(count)
.map(|&variable_and_name| unsafe {
(
raw_to_string((*variable_and_name).parser).unwrap(),
raw_to_string((*variable_and_name).name).unwrap(),
Type::ref_from_raw(BNNewTypeReference((*variable_and_name).type_)),
)
})
.collect();
unsafe { BNFreeDataVariableAndNameAndDebugParserList(raw_variables_and_names, count) };
result
}
pub fn remove_parser_info<S: BnStrCompatible>(&self, parser_name: S) -> bool {
let parser_name = parser_name.into_bytes_with_nul();
unsafe { BNRemoveDebugParserInfo(self.handle, parser_name.as_ref().as_ptr() as *mut _) }
}
pub fn remove_parser_types<S: BnStrCompatible>(&self, parser_name: S) -> bool {
let parser_name = parser_name.into_bytes_with_nul();
unsafe { BNRemoveDebugParserTypes(self.handle, parser_name.as_ref().as_ptr() as *mut _) }
}
pub fn remove_parser_functions<S: BnStrCompatible>(&self, parser_name: S) -> bool {
let parser_name = parser_name.into_bytes_with_nul();
unsafe {
BNRemoveDebugParserFunctions(self.handle, parser_name.as_ref().as_ptr() as *mut _)
}
}
pub fn remove_parser_data_variables<S: BnStrCompatible>(&self, parser_name: S) -> bool {
let parser_name = parser_name.into_bytes_with_nul();
unsafe {
BNRemoveDebugParserDataVariables(self.handle, parser_name.as_ref().as_ptr() as *mut _)
}
}
pub fn remove_type_by_name<S: BnStrCompatible>(&self, parser_name: S, name: S) -> bool {
let parser_name = parser_name.into_bytes_with_nul();
let name = name.into_bytes_with_nul();
unsafe {
BNRemoveDebugTypeByName(
self.handle,
parser_name.as_ref().as_ptr() as *mut _,
name.as_ref().as_ptr() as *mut _,
)
}
}
pub fn remove_function_by_index<S: BnStrCompatible>(
&self,
parser_name: S,
index: usize,
) -> bool {
let parser_name = parser_name.into_bytes_with_nul();
unsafe {
BNRemoveDebugFunctionByIndex(
self.handle,
parser_name.as_ref().as_ptr() as *mut _,
index,
)
}
}
pub fn remove_data_variable_by_address<S: BnStrCompatible>(
&self,
parser_name: S,
address: u64,
) -> bool {
let parser_name = parser_name.into_bytes_with_nul();
unsafe {
BNRemoveDebugDataVariableByAddress(
self.handle,
parser_name.as_ref().as_ptr() as *mut _,
address,
)
}
}
pub fn add_type<S: BnStrCompatible>(
&self,
name: S,
new_type: &Type,
components: &[&str],
) -> bool {
let mut components_array: Vec<*const ::std::os::raw::c_char> =
Vec::with_capacity(components.len());
for component in components {
components_array.push(component.as_ptr() as _);
}
let name = name.into_bytes_with_nul();
unsafe {
BNAddDebugType(
self.handle,
name.as_ref().as_ptr() as *mut _,
new_type.handle,
components_array.as_ptr() as _,
components.len(),
)
}
}
pub fn add_function(&self, new_func: DebugFunctionInfo) -> bool {
let short_name_bytes = new_func.short_name.map(|name| name.into_bytes_with_nul());
let short_name = short_name_bytes
.as_ref()
.map_or(ptr::null_mut() as *mut _, |name| name.as_ptr() as _);
let full_name_bytes = new_func.full_name.map(|name| name.into_bytes_with_nul());
let full_name = full_name_bytes
.as_ref()
.map_or(ptr::null_mut() as *mut _, |name| name.as_ptr() as _);
let raw_name_bytes = new_func.raw_name.map(|name| name.into_bytes_with_nul());
let raw_name = raw_name_bytes
.as_ref()
.map_or(ptr::null_mut() as *mut _, |name| name.as_ptr() as _);
let mut components_array: Vec<*mut ::std::os::raw::c_char> =
Vec::with_capacity(new_func.components.len());
let mut local_variables_array: Vec<BNVariableNameAndType> =
Vec::with_capacity(new_func.local_variables.len());
unsafe {
for component in &new_func.components {
components_array.push(BNAllocString(component.clone().into_bytes_with_nul().as_ptr() as _));
}
for local_variable in &new_func.local_variables {
local_variables_array.push(
BNVariableNameAndType {
var: local_variable.var.raw(),
autoDefined: local_variable.auto_defined,
typeConfidence: local_variable.ty.confidence,
name: BNAllocString(local_variable.name.clone().into_bytes_with_nul().as_ptr() as _),
type_: local_variable.ty.contents.handle,
}
);
}
let result = BNAddDebugFunction(
self.handle,
&mut BNDebugFunctionInfo {
shortName: short_name,
fullName: full_name,
rawName: raw_name,
address: new_func.address,
type_: match new_func.type_ {
Some(type_) => type_.handle,
_ => ptr::null_mut(),
},
platform: match new_func.platform {
Some(platform) => platform.handle,
_ => ptr::null_mut(),
},
components: components_array.as_ptr() as _,
componentN: new_func.components.len(),
localVariables: local_variables_array.as_ptr() as _,
localVariableN: local_variables_array.len(),
},
);
for i in components_array {
BNFreeString(i);
}
for i in &local_variables_array {
BNFreeString(i.name);
}
result
}
}
pub fn add_data_variable<S: BnStrCompatible>(
&self,
address: u64,
t: &Type,
name: Option<S>,
components: &[&str],
) -> bool {
let mut components_array: Vec<*const ::std::os::raw::c_char> =
Vec::with_capacity(components.len());
for component in components {
components_array.push(component.as_ptr() as _);
}
match name {
Some(name) => {
let name = name.into_bytes_with_nul();
unsafe {
BNAddDebugDataVariable(
self.handle,
address,
t.handle,
name.as_ref().as_ptr() as *mut _,
components.as_ptr() as _,
components.len(),
)
}
}
None => unsafe {
BNAddDebugDataVariable(
self.handle,
address,
t.handle,
ptr::null_mut(),
components.as_ptr() as _,
components.len(),
)
},
}
}
pub fn add_data_variable_info<S: BnStrCompatible>(&self, var: DataVariableAndName<S>) -> bool {
let name = var.name.into_bytes_with_nul();
unsafe {
BNAddDebugDataVariableInfo(
self.handle,
&BNDataVariableAndName {
address: var.address,
type_: var.t.contents.handle,
name: name.as_ref().as_ptr() as *mut _,
autoDiscovered: var.auto_discovered,
typeConfidence: var.t.confidence,
},
)
}
}
}
unsafe impl RefCountable for DebugInfo {
unsafe fn inc_ref(handle: &Self) -> Ref<Self> {
Ref::new(Self {
handle: BNNewDebugInfoReference(handle.handle),
})
}
unsafe fn dec_ref(handle: &Self) {
BNFreeDebugInfoReference(handle.handle);
}
}
impl ToOwned for DebugInfo {
type Owned = Ref<Self>;
fn to_owned(&self) -> Self::Owned {
unsafe { RefCountable::inc_ref(self) }
}
}
pub trait CustomDebugInfoParser: 'static + Sync {
fn is_valid(&self, view: &BinaryView) -> bool;
fn parse_info(
&self,
debug_info: &mut DebugInfo,
view: &BinaryView,
debug_file: &BinaryView,
progress: Box<dyn Fn(usize, usize) -> Result<(), ()>>,
) -> bool;
}