diff options
Diffstat (limited to 'rust/src/demangle.rs')
| -rw-r--r-- | rust/src/demangle.rs | 323 |
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>>)>; } |
