use binaryninjacore_sys::*;
use std::ffi::c_void;
use crate::progress::{NoProgressCallback, ProgressCallback};
use crate::variable::{NamedDataVariableWithType, NamedVariableWithType};
use crate::{
binary_view::BinaryView,
platform::Platform,
rc::*,
string::{raw_to_string, BnString, IntoCStr},
types::{NameAndType, Type},
};
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;
}
#[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(name: &str) -> Result<Ref<Self>, ()> {
let name = name.to_cstr();
let parser = unsafe { BNGetDebugInfoParserByName(name.as_ptr()) };
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) -> String {
unsafe { BnString::into_string(BNGetDebugInfoParserName(self.handle)) }
}
pub fn is_valid_for_view(&self, view: &BinaryView) -> bool {
unsafe { BNIsDebugInfoParserValidForView(self.handle, view.handle) }
}
pub fn parse_debug_info(
&self,
view: &BinaryView,
debug_file: &BinaryView,
existing_debug_info: Option<&DebugInfo>,
) -> Option<Ref<DebugInfo>> {
self.parse_debug_info_with_progress(
view,
debug_file,
existing_debug_info,
NoProgressCallback,
)
}
pub fn parse_debug_info_with_progress<P: ProgressCallback>(
&self,
view: &BinaryView,
debug_file: &BinaryView,
existing_debug_info: Option<&DebugInfo>,
mut progress: P,
) -> Option<Ref<DebugInfo>> {
let info: *mut BNDebugInfo = match existing_debug_info {
Some(debug_info) => unsafe {
BNParseDebugInfo(
self.handle,
view.handle,
debug_file.handle,
debug_info.handle,
Some(P::cb_progress_callback),
&mut progress as *mut P as *mut c_void,
)
},
None => unsafe {
BNParseDebugInfo(
self.handle,
view.handle,
debug_file.handle,
std::ptr::null_mut(),
Some(P::cb_progress_callback),
&mut progress as *mut P as *mut c_void,
)
},
};
if info.is_null() {
return None;
}
Some(unsafe { DebugInfo::ref_from_raw(info) })
}
pub fn register<C>(name: &str, parser_callbacks: C) -> Ref<Self>
where
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::ref_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::ref_from_raw(view);
let debug_file = BinaryView::ref_from_raw(debug_file);
let mut debug_info = DebugInfo::ref_from_raw(debug_info);
cmd.parse_info(
&mut debug_info,
&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.to_cstr();
let name_ptr = name.as_ptr();
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<NamedVariableWithType>,
}
impl DebugFunctionInfo {
pub(crate) fn from_raw(value: &BNDebugFunctionInfo) -> Self {
let raw_components =
unsafe { std::slice::from_raw_parts(value.components, value.componentN) };
let components = raw_components
.iter()
.filter_map(|&c| raw_to_string(c))
.collect();
let raw_local_variables =
unsafe { std::slice::from_raw_parts(value.localVariables, value.localVariableN) };
let local_variables = raw_local_variables
.iter()
.map(NamedVariableWithType::from_raw)
.collect();
Self {
short_name: raw_to_string(value.shortName),
full_name: raw_to_string(value.fullName),
raw_name: raw_to_string(value.rawName),
type_: if value.type_.is_null() {
None
} else {
Some(unsafe { Type::from_raw(value.type_) }.to_owned())
},
address: value.address,
platform: if value.platform.is_null() {
None
} else {
Some(unsafe { Platform::from_raw(value.platform) }.to_owned())
},
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<NamedVariableWithType>,
) -> 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 ref_from_raw(handle: *mut BNDebugInfo) -> Ref<Self> {
debug_assert!(!handle.is_null());
Ref::new(Self { handle })
}
pub fn types_by_name(&self, parser_name: &str) -> Vec<NameAndType> {
let parser_name = parser_name.to_cstr();
let mut count: usize = 0;
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()
.map(NameAndType::from_raw)
.collect()
};
unsafe { BNFreeDebugTypes(debug_types_ptr, count) };
result
}
pub fn types(&self) -> Vec<NameAndType> {
let mut count: usize = 0;
let debug_types_ptr =
unsafe { BNGetDebugTypes(self.handle, std::ptr::null_mut(), &mut count) };
let result: Vec<_> = unsafe {
std::slice::from_raw_parts_mut(debug_types_ptr, count)
.iter()
.map(NameAndType::from_raw)
.collect()
};
unsafe { BNFreeDebugTypes(debug_types_ptr, count) };
result
}
pub fn functions_by_name(&self, parser_name: &str) -> Vec<DebugFunctionInfo> {
let parser_name = parser_name.to_cstr();
let mut count: usize = 0;
let functions_ptr =
unsafe { BNGetDebugFunctions(self.handle, parser_name.as_ptr(), &mut count) };
let result: Vec<DebugFunctionInfo> = unsafe {
std::slice::from_raw_parts_mut(functions_ptr, count)
.iter()
.map(DebugFunctionInfo::from_raw)
.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, std::ptr::null_mut(), &mut count) };
let result: Vec<DebugFunctionInfo> = unsafe {
std::slice::from_raw_parts_mut(functions_ptr, count)
.iter()
.map(DebugFunctionInfo::from_raw)
.collect()
};
unsafe { BNFreeDebugFunctions(functions_ptr, count) };
result
}
pub fn data_variables_by_name(&self, parser_name: &str) -> Vec<NamedDataVariableWithType> {
let parser_name = parser_name.to_cstr();
let mut count: usize = 0;
let data_variables_ptr =
unsafe { BNGetDebugDataVariables(self.handle, parser_name.as_ptr(), &mut count) };
let result: Vec<NamedDataVariableWithType> = unsafe {
std::slice::from_raw_parts_mut(data_variables_ptr, count)
.iter()
.map(NamedDataVariableWithType::from_raw)
.collect()
};
unsafe { BNFreeDataVariablesAndName(data_variables_ptr, count) };
result
}
pub fn data_variables(&self) -> Vec<NamedDataVariableWithType> {
let mut count: usize = 0;
let data_variables_ptr =
unsafe { BNGetDebugDataVariables(self.handle, std::ptr::null_mut(), &mut count) };
let result: Vec<NamedDataVariableWithType> = unsafe {
std::slice::from_raw_parts_mut(data_variables_ptr, count)
.iter()
.map(NamedDataVariableWithType::from_raw)
.collect()
};
unsafe { BNFreeDataVariablesAndName(data_variables_ptr, count) };
result
}
pub fn type_by_name(&self, parser_name: &str, name: &str) -> Option<Ref<Type>> {
let parser_name = parser_name.to_cstr();
let name = name.to_cstr();
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 {
None
}
}
pub fn get_data_variable_by_name(
&self,
parser_name: &str,
name: &str,
) -> Option<NamedDataVariableWithType> {
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_ptr(),
name.as_ptr(),
&mut dv,
) {
Some(NamedDataVariableWithType::from_owned_raw(dv))
} else {
None
}
}
}
pub fn get_data_variable_by_address(
&self,
parser_name: &str,
address: u64,
) -> Option<NamedDataVariableWithType> {
let parser_name = parser_name.to_cstr();
let mut dv = BNDataVariableAndName::default();
unsafe {
if BNGetDebugDataVariableByAddress(self.handle, parser_name.as_ptr(), address, &mut dv)
{
Some(NamedDataVariableWithType::from_owned_raw(dv))
} else {
None
}
}
}
pub fn get_types_by_name(&self, name: &str) -> Vec<NameAndType> {
let mut count: usize = 0;
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) };
let names_and_types = raw_names_and_types
.iter()
.map(NameAndType::from_raw)
.collect();
unsafe { BNFreeNameAndTypeList(raw_names_and_types_ptr, count) };
names_and_types
}
pub fn get_data_variables_by_name(&self, name: &str) -> Vec<(String, u64, Ref<Type>)> {
let name = name.to_cstr();
let mut count: usize = 0;
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) };
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::from_raw((*variable_and_name).type_).to_owned(),
)
})
.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 { std::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::from_raw((*variable_and_name).type_).to_owned(),
)
})
.collect();
unsafe { BNFreeDataVariableAndNameAndDebugParserList(raw_variables_and_names, count) };
result
}
pub fn remove_parser_info(&self, parser_name: &str) -> bool {
let parser_name = parser_name.to_cstr();
unsafe { BNRemoveDebugParserInfo(self.handle, parser_name.as_ptr()) }
}
pub fn remove_parser_types(&self, parser_name: &str) -> bool {
let parser_name = parser_name.to_cstr();
unsafe { BNRemoveDebugParserTypes(self.handle, parser_name.as_ptr()) }
}
pub fn remove_parser_functions(&self, parser_name: &str) -> bool {
let parser_name = parser_name.to_cstr();
unsafe { BNRemoveDebugParserFunctions(self.handle, parser_name.as_ptr()) }
}
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_ptr()) }
}
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_ptr(), name.as_ptr()) }
}
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_ptr(), index) }
}
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_ptr(), address) }
}
pub fn add_type(&self, name: &str, new_type: &Type, components: &[&str]) -> bool {
let raw_components: Vec<_> = components.iter().map(|&c| c.as_ptr()).collect();
let name = name.to_cstr();
unsafe {
BNAddDebugType(
self.handle,
name.as_ptr(),
new_type.handle,
raw_components.as_ptr() as *mut _,
components.len(),
)
}
}
pub fn add_function(&self, new_func: &DebugFunctionInfo) -> bool {
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.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.to_cstr());
let raw_name = raw_name_bytes
.as_ref()
.map_or(std::ptr::null_mut() as *mut _, |name| name.as_ptr() as _);
let mut components_array: Vec<*mut ::std::os::raw::c_char> =
Vec::with_capacity(new_func.components.len());
let mut local_variables_array: Vec<BNVariableNameAndType> =
Vec::with_capacity(new_func.local_variables.len());
unsafe {
for component in &new_func.components {
let component = component.to_cstr();
components_array.push(BNAllocString(component.as_ptr()));
}
for local_variable in &new_func.local_variables {
local_variables_array.push(NamedVariableWithType::into_raw(local_variable.clone()));
}
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,
_ => std::ptr::null_mut(),
},
platform: match &new_func.platform {
Some(platform) => platform.handle,
_ => std::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 {
BnString::free_raw(i);
}
for i in &local_variables_array {
NamedVariableWithType::free_raw(*i);
}
result
}
}
pub fn add_data_variable(
&self,
address: u64,
t: &Type,
name: Option<&str>,
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.to_cstr();
unsafe {
BNAddDebugDataVariable(
self.handle,
address,
t.handle,
name.as_ptr(),
components.as_ptr() as _,
components.len(),
)
}
}
None => unsafe {
BNAddDebugDataVariable(
self.handle,
address,
t.handle,
std::ptr::null_mut(),
components.as_ptr() as _,
components.len(),
)
},
}
}
pub fn add_data_variable_info(&self, var: NamedDataVariableWithType) -> bool {
let raw_data_var = NamedDataVariableWithType::into_raw(var);
let success = unsafe { BNAddDebugDataVariableInfo(self.handle, &raw_data_var) };
NamedDataVariableWithType::free_raw(raw_data_var);
success
}
}
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) }
}
}