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.rs258
1 files changed, 256 insertions, 2 deletions
diff --git a/rust/src/demangle.rs b/rust/src/demangle.rs
index 1b940ff9..8fbc4316 100644
--- a/rust/src/demangle.rs
+++ b/rust/src/demangle.rs
@@ -17,15 +17,75 @@
use binaryninjacore_sys::*;
use std::os::raw::c_char;
use std::{ffi::CStr, result};
+use std::ffi::c_void;
use crate::architecture::CoreArchitecture;
-use crate::string::{BnStrCompatible, BnString};
-use crate::types::Type;
+use crate::binaryview::BinaryView;
+use crate::string::{BnStrCompatible, BnString, raw_to_string};
+use crate::types::{QualifiedName, Type};
use crate::rc::*;
pub type Result<R> = 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>)> {
+ 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 res = unsafe {
+ BNDemangleGeneric(
+ arch.0,
+ mangled_name_ptr.as_ptr() as *const c_char,
+ &mut out_type,
+ &mut out_name,
+ view_ptr,
+ 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(());
+ }
+ };
+ return Ok((None, vec![cstr.to_string_lossy().into_owned()]));
+ }
+
+ 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,
@@ -179,3 +239,197 @@ pub fn demangle_ms<S: BnStrCompatible>(
Ok((out_type, names))
}
+
+#[derive(PartialEq, Eq, Hash)]
+pub struct Demangler {
+ pub(crate) handle: *mut BNDemangler,
+}
+
+impl Demangler {
+ pub(crate) unsafe fn from_raw(handle: *mut BNDemangler) -> Self {
+ debug_assert!(!handle.is_null());
+
+ Self { handle }
+ }
+
+ 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 _) }
+ }
+
+ pub fn demangle<S: BnStrCompatible>(
+ &self,
+ arch: &CoreArchitecture,
+ name: S,
+ view: Option<&BinaryView>,
+ ) -> Result<(Option<Ref<Type>>, QualifiedName)> {
+ 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 view_ptr = match view {
+ Some(v) => v.handle,
+ 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 var_type =
+ if out_type.is_null() {
+ None
+ } else {
+ Some(unsafe { Type::ref_from_raw(out_type) })
+ };
+ let var_name = QualifiedName(out_var_name);
+
+ Ok((var_type, var_name))
+ }
+
+ pub fn name(&self) -> BnString {
+ unsafe { BnString::from_raw(BNGetDemanglerName(self.handle)) }
+ }
+
+ pub fn from_name<S: BnStrCompatible>(name: S) -> Option<Self> {
+ let name_bytes = name.into_bytes_with_nul();
+ let demangler = unsafe { BNGetDemanglerByName(name_bytes.as_ref().as_ptr() as *const _) };
+ if demangler.is_null() {
+ None
+ } else {
+ Some(unsafe { Demangler::from_raw(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
+ where
+ S: BnStrCompatible,
+ C: CustomDemangler,
+ {
+ extern "C" fn cb_is_mangled_string<C>(ctxt: *mut c_void, name: *const c_char) -> bool
+ where
+ C: CustomDemangler,
+ {
+ 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;
+ };
+ 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
+ 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)))
+ };
+
+ 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()
+ };
+ // 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);
+ true
+ },
+ Err(_) => {
+ false
+ }
+ }
+ })
+ }
+ extern "C" fn cb_free_var_name<C>(_ctxt: *mut c_void, name: *mut BNQualifiedName)
+ where
+ C: CustomDemangler,
+ {
+ ffi_wrap!("CustomDemangler::cb_free_var_name", unsafe {
+ BNFreeString((*name).join);
+ BNFreeStringList((*name).name, (*name).nameCount);
+ })
+ }
+
+ 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 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>),
+ };
+
+ unsafe {
+ Demangler::from_raw(BNRegisterDemangler(
+ name_ptr,
+ Box::leak(Box::new(callbacks)),
+ ))
+ }
+ }
+
+ pub fn promote(demangler: &Demangler) {
+ unsafe { BNPromoteDemangler(demangler.handle); }
+ }
+}
+
+unsafe impl Sync for Demangler {}
+
+unsafe impl Send for Demangler {}
+
+impl CoreArrayProvider for Demangler {
+ type Raw = *mut BNDemangler;
+ type Context = ();
+ type Wrapped<'a> = Demangler;
+}
+
+unsafe impl CoreArrayProviderInner for Demangler {
+ unsafe fn free(raw: *mut Self::Raw, _count: usize, _context: &Self::Context) {
+ BNFreeDemanglerList(raw);
+ }
+
+ unsafe fn wrap_raw<'a>(raw: &'a Self::Raw, _context: &'a Self::Context) -> Self::Wrapped<'a> {
+ Demangler::from_raw(*raw)
+ }
+}
+
+pub trait CustomDemangler: 'static + Sync {
+ fn is_mangled_string(&self, name: &str) -> bool;
+
+ fn demangle(
+ &self,
+ arch: &CoreArchitecture,
+ name: &str,
+ view: Option<Ref<BinaryView>>,
+ ) -> Result<(Option<Ref<Type>>, QualifiedName)>;
+}