summaryrefslogtreecommitdiff
path: root/rust/src/demangle.rs
diff options
context:
space:
mode:
Diffstat (limited to 'rust/src/demangle.rs')
-rw-r--r--rust/src/demangle.rs323
1 files changed, 127 insertions, 196 deletions
diff --git a/rust/src/demangle.rs b/rust/src/demangle.rs
index 8fbc4316..6e303fac 100644
--- a/rust/src/demangle.rs
+++ b/rust/src/demangle.rs
@@ -15,84 +15,53 @@
//! Interfaces for demangling and simplifying mangled names in binaries.
use binaryninjacore_sys::*;
-use std::os::raw::c_char;
-use std::{ffi::CStr, result};
-use std::ffi::c_void;
+use std::ffi::{c_char, c_void};
use crate::architecture::CoreArchitecture;
-use crate::binaryview::BinaryView;
-use crate::string::{BnStrCompatible, BnString, raw_to_string};
+use crate::binary_view::BinaryView;
+use crate::string::{raw_to_string, BnStrCompatible, BnString};
use crate::types::{QualifiedName, Type};
use crate::rc::*;
-pub type Result<R> = result::Result<R, ()>;
+pub type Result<R> = std::result::Result<R, ()>;
pub fn demangle_generic<S: BnStrCompatible>(
arch: &CoreArchitecture,
mangled_name: S,
view: Option<&BinaryView>,
simplify: bool,
-) -> Result<(Option<Ref<Type>>, Vec<String>)> {
+) -> Option<(QualifiedName, Option<Ref<Type>>)> {
let mangled_name_bwn = mangled_name.into_bytes_with_nul();
let mangled_name_ptr = mangled_name_bwn.as_ref();
let mut out_type: *mut BNType = std::ptr::null_mut();
- let mut out_name = BNQualifiedName {
- name: std::ptr::null_mut(),
- join: std::ptr::null_mut(),
- nameCount: 0,
- };
- let view_ptr = match view {
- Some(v) => v.handle,
- None => std::ptr::null_mut(),
- };
+ let mut out_name = BNQualifiedName::default();
let res = unsafe {
BNDemangleGeneric(
- arch.0,
+ arch.handle,
mangled_name_ptr.as_ptr() as *const c_char,
&mut out_type,
&mut out_name,
- view_ptr,
+ view.map(|v| v.handle).unwrap_or(std::ptr::null_mut()),
simplify,
)
};
- if !res {
- let cstr = match CStr::from_bytes_with_nul(mangled_name_ptr) {
- Ok(cstr) => cstr,
- Err(_) => {
- log::error!("demangle_generic: failed to parse mangled name");
- return Err(());
- }
+ if res {
+ let out_type = match out_type.is_null() {
+ true => None,
+ false => Some(unsafe { Type::ref_from_raw(out_type) }),
};
- return Ok((None, vec![cstr.to_string_lossy().into_owned()]));
+ Some((QualifiedName::from_owned_raw(out_name), out_type))
+ } else {
+ None
}
-
- let out_type = match out_type.is_null() {
- true => {
- log::debug!("demangle_generic: out_type is NULL");
- None
- }
- false => Some(unsafe { Type::ref_from_raw(out_type) }),
- };
-
- Ok((
- out_type,
- QualifiedName(out_name)
- .strings()
- .iter()
- .map(|str| str.to_string())
- .collect::<Vec<_>>()
- ))
}
-pub fn demangle_llvm<S: BnStrCompatible>(
- mangled_name: S,
- simplify: bool,
-) -> Result<Vec<String>> {
+pub fn demangle_llvm<S: BnStrCompatible>(mangled_name: S, simplify: bool) -> Option<QualifiedName> {
let mangled_name_bwn = mangled_name.into_bytes_with_nul();
let mangled_name_ptr = mangled_name_bwn.as_ref();
- let mut out_name: *mut *mut std::os::raw::c_char = unsafe { std::mem::zeroed() };
+ 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(
@@ -103,37 +72,26 @@ pub fn demangle_llvm<S: BnStrCompatible>(
)
};
- if !res || out_size == 0 {
- let cstr = match CStr::from_bytes_with_nul(mangled_name_ptr) {
- Ok(cstr) => cstr,
- Err(_) => {
- log::error!("demangle_llvm: failed to parse mangled name");
- return Err(());
- }
- };
- return Ok(vec![cstr.to_string_lossy().into_owned()]);
- }
+ match res {
+ true => {
+ assert!(!out_name.is_null());
+ let names: Vec<_> = unsafe { ArrayGuard::<BnString>::new(out_name, out_size, ()) }
+ .iter()
+ .map(str::to_string)
+ .collect();
+ unsafe { BNFreeDemangledName(&mut out_name, out_size) };
- if out_name.is_null() {
- log::error!("demangle_llvm: out_name is NULL");
- return Err(());
+ Some(names.into())
+ }
+ false => None,
}
-
- let names = unsafe { ArrayGuard::<BnString>::new(out_name, out_size, ()) }
- .iter()
- .map(str::to_string)
- .collect();
-
- unsafe { BNFreeDemangledName(&mut out_name, out_size) };
-
- Ok(names)
}
pub fn demangle_gnu3<S: BnStrCompatible>(
arch: &CoreArchitecture,
mangled_name: S,
simplify: bool,
-) -> Result<(Option<Ref<Type>>, Vec<String>)> {
+) -> Option<(QualifiedName, Option<Ref<Type>>)> {
let mangled_name_bwn = mangled_name.into_bytes_with_nul();
let mangled_name_ptr = mangled_name_bwn.as_ref();
let mut out_type: *mut BNType = std::ptr::null_mut();
@@ -141,7 +99,7 @@ pub fn demangle_gnu3<S: BnStrCompatible>(
let mut out_size: usize = 0;
let res = unsafe {
BNDemangleGNU3(
- arch.0,
+ arch.handle,
mangled_name_ptr.as_ptr() as *const c_char,
&mut out_type,
&mut out_name,
@@ -150,45 +108,31 @@ pub fn demangle_gnu3<S: BnStrCompatible>(
)
};
- if !res || out_size == 0 {
- let cstr = match CStr::from_bytes_with_nul(mangled_name_ptr) {
- Ok(cstr) => cstr,
- Err(_) => {
- log::error!("demangle_gnu3: failed to parse mangled name");
- return Err(());
- }
- };
- return Ok((None, vec![cstr.to_string_lossy().into_owned()]));
- }
-
- let out_type = match out_type.is_null() {
+ match res {
true => {
- log::debug!("demangle_gnu3: out_type is NULL");
- None
- }
- false => Some(unsafe { Type::ref_from_raw(out_type) }),
- };
-
- if out_name.is_null() {
- log::error!("demangle_gnu3: out_name is NULL");
- return Err(());
- }
-
- let names = unsafe { ArrayGuard::<BnString>::new(out_name, out_size, ()) }
- .iter()
- .map(str::to_string)
- .collect();
+ assert!(!out_name.is_null());
+ let names: Vec<_> = unsafe { ArrayGuard::<BnString>::new(out_name, out_size, ()) }
+ .iter()
+ .map(str::to_string)
+ .collect();
+ unsafe { BNFreeDemangledName(&mut out_name, out_size) };
- unsafe { BNFreeDemangledName(&mut out_name, out_size) };
+ let out_type = match out_type.is_null() {
+ true => None,
+ false => Some(unsafe { Type::ref_from_raw(out_type) }),
+ };
- Ok((out_type, names))
+ Some((names.into(), out_type))
+ }
+ false => None,
+ }
}
pub fn demangle_ms<S: BnStrCompatible>(
arch: &CoreArchitecture,
mangled_name: S,
simplify: bool,
-) -> Result<(Option<Ref<Type>>, Vec<String>)> {
+) -> Option<(QualifiedName, Option<Ref<Type>>)> {
let mangled_name_bwn = mangled_name.into_bytes_with_nul();
let mangled_name_ptr = mangled_name_bwn.as_ref();
@@ -197,7 +141,7 @@ pub fn demangle_ms<S: BnStrCompatible>(
let mut out_size: usize = 0;
let res = unsafe {
BNDemangleMS(
- arch.0,
+ arch.handle,
mangled_name_ptr.as_ptr() as *const c_char,
&mut out_type,
&mut out_name,
@@ -206,38 +150,24 @@ pub fn demangle_ms<S: BnStrCompatible>(
)
};
- if !res || out_size == 0 {
- let cstr = match CStr::from_bytes_with_nul(mangled_name_ptr) {
- Ok(cstr) => cstr,
- Err(_) => {
- log::error!("demangle_ms: failed to parse mangled name");
- return Err(());
- }
- };
- return Ok((None, vec![cstr.to_string_lossy().into_owned()]));
- }
-
- let out_type = match out_type.is_null() {
+ match res {
true => {
- log::debug!("demangle_ms: out_type is NULL");
- None
- }
- false => Some(unsafe { Type::ref_from_raw(out_type) }),
- };
-
- if out_name.is_null() {
- log::error!("demangle_ms: out_name is NULL");
- return Err(());
- }
+ assert!(!out_name.is_null());
+ let names: Vec<_> = unsafe { ArrayGuard::<BnString>::new(out_name, out_size, ()) }
+ .iter()
+ .map(str::to_string)
+ .collect();
+ unsafe { BNFreeDemangledName(&mut out_name, out_size) };
- let names = unsafe { ArrayGuard::<BnString>::new(out_name, out_size, ()) }
- .iter()
- .map(|name| name.to_string())
- .collect();
-
- unsafe { BNFreeDemangledName(&mut out_name, out_size) };
+ let out_type = match out_type.is_null() {
+ true => None,
+ false => Some(unsafe { Type::ref_from_raw(out_type) }),
+ };
- Ok((out_type, names))
+ Some((names.into(), out_type))
+ }
+ false => None,
+ }
}
#[derive(PartialEq, Eq, Hash)]
@@ -248,10 +178,15 @@ pub struct Demangler {
impl Demangler {
pub(crate) unsafe fn from_raw(handle: *mut BNDemangler) -> Self {
debug_assert!(!handle.is_null());
-
Self { handle }
}
+ pub fn list() -> Array<Self> {
+ let mut count: usize = 0;
+ let demanglers = unsafe { BNGetDemanglerList(&mut count) };
+ unsafe { Array::<Demangler>::new(demanglers, count, ()) }
+ }
+
pub fn is_mangled_string<S: BnStrCompatible>(&self, name: S) -> bool {
let bytes = name.into_bytes_with_nul();
unsafe { BNIsDemanglerMangledName(self.handle, bytes.as_ref().as_ptr() as *const _) }
@@ -262,34 +197,39 @@ impl Demangler {
arch: &CoreArchitecture,
name: S,
view: Option<&BinaryView>,
- ) -> Result<(Option<Ref<Type>>, QualifiedName)> {
+ ) -> Option<(QualifiedName, Option<Ref<Type>>)> {
let name_bytes = name.into_bytes_with_nul();
let mut out_type = std::ptr::null_mut();
- let mut out_var_name = BNQualifiedName {
- name: std::ptr::null_mut(),
- join: std::ptr::null_mut(),
- nameCount: 0,
- };
+ let mut out_var_name = BNQualifiedName::default();
let view_ptr = match view {
Some(v) => v.handle,
- None => std::ptr::null_mut()
+ None => std::ptr::null_mut(),
};
- if !unsafe { BNDemanglerDemangle(self.handle, arch.0, name_bytes.as_ref().as_ptr() as *const _, &mut out_type, &mut out_var_name, view_ptr) } {
- return Err(());
- }
+ let res = unsafe {
+ BNDemanglerDemangle(
+ self.handle,
+ arch.handle,
+ name_bytes.as_ref().as_ptr() as *const _,
+ &mut out_type,
+ &mut out_var_name,
+ view_ptr,
+ )
+ };
- let var_type =
- if out_type.is_null() {
- None
- } else {
- Some(unsafe { Type::ref_from_raw(out_type) })
- };
- let var_name = QualifiedName(out_var_name);
+ match res {
+ true => {
+ let var_type = match out_type.is_null() {
+ true => None,
+ false => Some(unsafe { Type::ref_from_raw(out_type) }),
+ };
- Ok((var_type, var_name))
+ Some((QualifiedName::from_owned_raw(out_var_name), var_type))
+ }
+ false => None,
+ }
}
pub fn name(&self) -> BnString {
@@ -306,13 +246,7 @@ impl Demangler {
}
}
- pub fn list() -> Array<Self> {
- let mut count: usize = 0;
- let demanglers = unsafe { BNGetDemanglerList(&mut count) };
- unsafe { Array::<Demangler>::new(demanglers, count, ()) }
- }
-
- pub fn register<S, C>(name: S, callbacks: C) -> Self
+ pub fn register<S, C>(name: S, demangler: C) -> Self
where
S: BnStrCompatible,
C: CustomDemangler,
@@ -323,71 +257,66 @@ impl Demangler {
{
ffi_wrap!("CustomDemangler::cb_is_mangled_string", unsafe {
let cmd = &*(ctxt as *const C);
- let name =
- if let Some(n) = raw_to_string(name) {
- n
- } else {
- return false;
- };
+ let Some(name) = raw_to_string(name) else {
+ return false;
+ };
cmd.is_mangled_string(&name)
})
}
- extern "C" fn cb_demangle<C>(ctxt: *mut c_void, arch: *mut BNArchitecture, name: *const c_char, out_type: *mut *mut BNType, out_var_name: *mut BNQualifiedName, view: *mut BNBinaryView) -> bool
+ extern "C" fn cb_demangle<C>(
+ ctxt: *mut c_void,
+ arch: *mut BNArchitecture,
+ name: *const c_char,
+ out_type: *mut *mut BNType,
+ out_var_name: *mut BNQualifiedName,
+ view: *mut BNBinaryView,
+ ) -> bool
where
C: CustomDemangler,
{
ffi_wrap!("CustomDemangler::cb_demangle", unsafe {
let cmd = &*(ctxt as *const C);
let arch = CoreArchitecture::from_raw(arch);
- let name =
- if let Some(n) = raw_to_string(name) {
- n
- } else {
- return false;
- };
- let view = if view.is_null() {
- None
- } else {
- Some(BinaryView::from_raw(BNNewViewReference(view)))
+ let Some(name) = raw_to_string(name) else {
+ return false;
+ };
+ let view = match view.is_null() {
+ false => Some(BinaryView::from_raw(view).to_owned()),
+ true => None,
};
match cmd.demangle(&arch, &name, view) {
- Ok((type_, name)) => {
- *out_type = match type_ {
- Some(t) => RefCountable::inc_ref(t.as_ref()).handle,
- None => std::ptr::null_mut()
+ Some((name, ty)) => {
+ // NOTE: Leaked to the caller, who must pick the ref up.
+ *out_type = match ty {
+ Some(t) => Ref::into_raw(t).handle,
+ None => std::ptr::null_mut(),
};
- // TODO: Need to have a better way for api-owned QNames
- (*out_var_name).nameCount = name.0.nameCount;
- (*out_var_name).join = BNAllocString(name.0.join);
- (*out_var_name).name = BNAllocStringList(name.0.name as *mut *const _, name.0.nameCount);
+ // NOTE: Leaked to be freed with `cb_free_var_name`.
+ *out_var_name = QualifiedName::into_raw(name);
true
- },
- Err(_) => {
- false
}
+ None => false,
}
})
}
- extern "C" fn cb_free_var_name<C>(_ctxt: *mut c_void, name: *mut BNQualifiedName)
- where
- C: CustomDemangler,
- {
+
+ extern "C" fn cb_free_var_name(_ctxt: *mut c_void, name: *mut BNQualifiedName) {
ffi_wrap!("CustomDemangler::cb_free_var_name", unsafe {
- BNFreeString((*name).join);
- BNFreeStringList((*name).name, (*name).nameCount);
+ // TODO: What is the point of this free callback?
+ QualifiedName::free_raw(*name)
})
}
let name = name.into_bytes_with_nul();
let name_ptr = name.as_ref().as_ptr() as *mut _;
- let ctxt = Box::into_raw(Box::new(callbacks));
+ let ctxt = Box::into_raw(Box::new(demangler));
let callbacks = BNDemanglerCallbacks {
context: ctxt as *mut c_void,
isMangledString: Some(cb_is_mangled_string::<C>),
demangle: Some(cb_demangle::<C>),
- freeVarName: Some(cb_free_var_name::<C>),
+ freeVarName: Some(cb_free_var_name),
};
unsafe {
@@ -399,7 +328,9 @@ impl Demangler {
}
pub fn promote(demangler: &Demangler) {
- unsafe { BNPromoteDemangler(demangler.handle); }
+ unsafe {
+ BNPromoteDemangler(demangler.handle);
+ }
}
}
@@ -431,5 +362,5 @@ pub trait CustomDemangler: 'static + Sync {
arch: &CoreArchitecture,
name: &str,
view: Option<Ref<BinaryView>>,
- ) -> Result<(Option<Ref<Type>>, QualifiedName)>;
+ ) -> Option<(QualifiedName, Option<Ref<Type>>)>;
}