diff --git a/plugins/minidump/src/command.rs b/plugins/minidump/src/command.rs index d33a8d0bb..e69dbdc47 100644 --- a/plugins/minidump/src/command.rs +++ b/plugins/minidump/src/command.rs @@ -7,7 +7,7 @@ use binaryninja::binaryview::{BinaryView, BinaryViewBase, BinaryViewExt}; pub fn print_memory_information(bv: &BinaryView) { debug!("Printing memory information"); - if let Ok(minidump_bv) = bv.parent_view() { + if let Some(minidump_bv) = bv.parent_view() { if let Ok(read_buffer) = minidump_bv.read_buffer(0, minidump_bv.len()) { if let Ok(minidump_obj) = Minidump::read(read_buffer.get_data()) { if let Ok(memory_info_list) = minidump_obj.get_stream::() { diff --git a/plugins/minidump/src/view.rs b/plugins/minidump/src/view.rs index ddb18c775..baa8a53d2 100644 --- a/plugins/minidump/src/view.rs +++ b/plugins/minidump/src/view.rs @@ -116,7 +116,7 @@ impl MinidumpBinaryView { } fn init(&self) -> BinaryViewResult<()> { - let parent_view = self.parent_view()?; + let parent_view = self.parent_view().ok_or(())?; let read_buffer = parent_view.read_buffer(0, parent_view.len())?; if let Ok(minidump_obj) = Minidump::read(read_buffer.get_data()) { diff --git a/rust/Cargo.lock b/rust/Cargo.lock index c6207e7ec..cb38ed466 100644 --- a/rust/Cargo.lock +++ b/rust/Cargo.lock @@ -25,6 +25,7 @@ dependencies = [ "log", "rayon", "rstest", + "tempfile", ] [[package]] @@ -125,29 +126,20 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" [[package]] -name = "futures" -version = "0.3.31" +name = "errno" +version = "0.3.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "65bc07b1a8bc7c85c5f2e110c476c7389b4554ba72af57d8445ea63a576b0876" +checksum = "33d852cb9b869c2a9b3df2f71a3074817f01e1844f839a144f5fcef059a4eb5d" dependencies = [ - "futures-channel", - "futures-core", - "futures-executor", - "futures-io", - "futures-sink", - "futures-task", - "futures-util", + "libc", + "windows-sys", ] [[package]] -name = "futures-channel" -version = "0.3.31" +name = "fastrand" +version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2dff15bf788c671c1934e366d07e30c1814a8ef514e1af724a602e8a2fbe1b10" -dependencies = [ - "futures-core", - "futures-sink", -] +checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be" [[package]] name = "futures-core" @@ -155,23 +147,6 @@ version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e" -[[package]] -name = "futures-executor" -version = "0.3.31" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e28d1d997f585e54aebc3f97d39e72338912123a67330d723fdbb564d646c9f" -dependencies = [ - "futures-core", - "futures-task", - "futures-util", -] - -[[package]] -name = "futures-io" -version = "0.3.31" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e5c1b78ca4aae1ac06c48a526a655760685149f0d465d21f37abfe57ce075c6" - [[package]] name = "futures-macro" version = "0.3.31" @@ -183,12 +158,6 @@ dependencies = [ "syn", ] -[[package]] -name = "futures-sink" -version = "0.3.31" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e575fab7d1e0dcb8d0c7bcf9a63ee213816ab51902e6d244a95819acacf1d4f7" - [[package]] name = "futures-task" version = "0.3.31" @@ -207,18 +176,25 @@ version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81" dependencies = [ - "futures-channel", "futures-core", - "futures-io", "futures-macro", - "futures-sink", "futures-task", - "memchr", "pin-project-lite", "pin-utils", "slab", ] +[[package]] +name = "getrandom" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" +dependencies = [ + "cfg-if", + "libc", + "wasi", +] + [[package]] name = "glob" version = "0.3.1" @@ -252,9 +228,9 @@ dependencies = [ [[package]] name = "libc" -version = "0.2.153" +version = "0.2.169" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c198f91728a82281a64e1f4f9eeb25d82cb32a5de251c6bd1b5154d63a8e7bd" +checksum = "b5aba8db14291edd000dfcc4d620c7ebfb122c613afb886ca8803fa4e128a20a" [[package]] name = "libloading" @@ -266,6 +242,12 @@ dependencies = [ "windows-targets", ] +[[package]] +name = "linux-raw-sys" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89" + [[package]] name = "log" version = "0.4.22" @@ -294,6 +276,12 @@ dependencies = [ "minimal-lexical", ] +[[package]] +name = "once_cell" +version = "1.20.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1261fe7e33c73b354eab43b1273a57c8f967d0391e80353e51f764ac02cf6775" + [[package]] name = "pin-project-lite" version = "0.2.15" @@ -400,21 +388,21 @@ checksum = "ba39f3699c378cd8970968dcbff9c43159ea4cfbd88d43c00b22f2ef10a435d2" [[package]] name = "rstest" -version = "0.23.0" +version = "0.24.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0a2c585be59b6b5dd66a9d2084aa1d8bd52fbdb806eafdeffb52791147862035" +checksum = "03e905296805ab93e13c1ec3a03f4b6c4f35e9498a3d5fa96dc626d22c03cd89" dependencies = [ - "futures", "futures-timer", + "futures-util", "rstest_macros", "rustc_version", ] [[package]] name = "rstest_macros" -version = "0.23.0" +version = "0.24.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "825ea780781b15345a146be27eaefb05085e337e869bff01b4306a4fd4a9ad5a" +checksum = "ef0053bbffce09062bee4bcc499b0fbe7a57b879f1efe088d6d8d4c7adcdef9b" dependencies = [ "cfg-if", "glob", @@ -443,6 +431,19 @@ dependencies = [ "semver", ] +[[package]] +name = "rustix" +version = "0.38.42" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f93dc38ecbab2eb790ff964bb77fa94faf256fd3e73285fd7ba0903b76bedb85" +dependencies = [ + "bitflags", + "errno", + "libc", + "linux-raw-sys", + "windows-sys", +] + [[package]] name = "semver" version = "1.0.24" @@ -475,6 +476,20 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "tempfile" +version = "3.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a8a559c81686f576e8cd0290cd2a24a2a9ad80c98b3478856500fcbd7acd704" +dependencies = [ + "cfg-if", + "fastrand", + "getrandom", + "once_cell", + "rustix", + "windows-sys", +] + [[package]] name = "toml_datetime" version = "0.6.8" @@ -498,15 +513,31 @@ version = "1.0.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "adb9e6ca4f869e1180728b7950e35922a7fc6397f7b641499e8f3ef06e50dc83" +[[package]] +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" + +[[package]] +name = "windows-sys" +version = "0.59.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" +dependencies = [ + "windows-targets", +] + [[package]] name = "windows-targets" -version = "0.52.4" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7dd37b7e5ab9018759f893a1952c9420d060016fc19a472b4bb20d1bdd694d1b" +checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" dependencies = [ "windows_aarch64_gnullvm", "windows_aarch64_msvc", "windows_i686_gnu", + "windows_i686_gnullvm", "windows_i686_msvc", "windows_x86_64_gnu", "windows_x86_64_gnullvm", @@ -515,45 +546,51 @@ dependencies = [ [[package]] name = "windows_aarch64_gnullvm" -version = "0.52.4" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bcf46cf4c365c6f2d1cc93ce535f2c8b244591df96ceee75d8e83deb70a9cac9" +checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" [[package]] name = "windows_aarch64_msvc" -version = "0.52.4" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da9f259dd3bcf6990b55bffd094c4f7235817ba4ceebde8e6d11cd0c5633b675" +checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" [[package]] name = "windows_i686_gnu" -version = "0.52.4" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b474d8268f99e0995f25b9f095bc7434632601028cf86590aea5c8a5cb7801d3" +checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" [[package]] name = "windows_i686_msvc" -version = "0.52.4" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1515e9a29e5bed743cb4415a9ecf5dfca648ce85ee42e15873c3cd8610ff8e02" +checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" [[package]] name = "windows_x86_64_gnu" -version = "0.52.4" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5eee091590e89cc02ad514ffe3ead9eb6b660aedca2183455434b93546371a03" +checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" [[package]] name = "windows_x86_64_gnullvm" -version = "0.52.4" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77ca79f2451b49fa9e2af39f0747fe999fcda4f5e241b2898624dca97a1f2177" +checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" [[package]] name = "windows_x86_64_msvc" -version = "0.52.4" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32b752e52a2da0ddfbdbcc6fceadfeede4c939ed16d13e648833a61dfb611ed8" +checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" [[package]] name = "winnow" diff --git a/rust/Cargo.toml b/rust/Cargo.toml index 8d1776948..46c557615 100644 --- a/rust/Cargo.toml +++ b/rust/Cargo.toml @@ -16,4 +16,5 @@ rayon = { version = "1.8", optional = true } binaryninjacore-sys = { path = "binaryninjacore-sys" } [dev-dependencies] -rstest = "0.23.0" \ No newline at end of file +rstest = "0.24.0" +tempfile = "3" \ No newline at end of file diff --git a/rust/examples/demangler.rs b/rust/examples/demangler.rs index f3d8a9f12..9a1af4f99 100644 --- a/rust/examples/demangler.rs +++ b/rust/examples/demangler.rs @@ -16,11 +16,11 @@ impl CustomDemangler for TestDemangler { _arch: &CoreArchitecture, name: &str, _view: Option>, - ) -> Result<(Option>, QualifiedName), ()> { + ) -> Option<(QualifiedName, Option>)> { match name { - "test_name" => Ok((Some(Type::bool()), QualifiedName::from(vec!["test_name"]))), - "test_name2" => Ok((None, QualifiedName::from(vec!["test_name2", "aaa"]))), - _ => Err(()), + "test_name" => Some((QualifiedName::from(vec!["test_name"]), Some(Type::bool()))), + "test_name2" => Some((QualifiedName::from(vec!["test_name2", "aaa"]), None)), + _ => None, } } } diff --git a/rust/src/architecture.rs b/rust/src/architecture.rs index ed0a20b68..64c7b5088 100644 --- a/rust/src/architecture.rs +++ b/rust/src/architecture.rs @@ -2588,7 +2588,7 @@ where return expr.index.0; } } else { - warn!( + log::warn!( "unable to unpack flag write op: {:?} with {} operands", op, operands.len() diff --git a/rust/src/binaryview.rs b/rust/src/binaryview.rs index 1855c77f2..0d9570f3a 100644 --- a/rust/src/binaryview.rs +++ b/rust/src/binaryview.rs @@ -46,7 +46,9 @@ use crate::settings::Settings; use crate::symbol::{Symbol, SymbolType}; use crate::tags::{Tag, TagType}; use crate::typelibrary::TypeLibrary; -use crate::types::{NamedTypeReference, QualifiedName, QualifiedNameAndType, Type}; +use crate::types::{ + NamedTypeReference, QualifiedName, QualifiedNameAndType, QualifiedNameTypeAndId, Type, +}; use crate::Endianness; use std::collections::HashMap; use std::ffi::{c_char, c_void}; @@ -586,87 +588,75 @@ pub trait BinaryViewExt: BinaryViewBase { } } - fn define_auto_type( + fn define_auto_type, S: BnStrCompatible>( &self, - name: S, + name: T, source: S, type_obj: &Type, ) -> QualifiedName { - let mut qualified_name = QualifiedName::from(name); + let mut raw_name = BNQualifiedName::from(name.into()); let source_str = source.into_bytes_with_nul(); let name_handle = unsafe { - let id_str = BNGenerateAutoTypeId( - source_str.as_ref().as_ptr() as *const _, - &mut qualified_name.0, - ); - BNDefineAnalysisType( - self.as_ref().handle, - id_str, - &mut qualified_name.0, - type_obj.handle, - ) + let id_str = + BNGenerateAutoTypeId(source_str.as_ref().as_ptr() as *const _, &mut raw_name); + BNDefineAnalysisType(self.as_ref().handle, id_str, &mut raw_name, type_obj.handle) }; - QualifiedName(name_handle) + QualifiedName::from(name_handle) } - fn define_auto_type_with_id( + fn define_auto_type_with_id, S: BnStrCompatible>( &self, - name: S, + name: T, id: S, type_obj: &Type, ) -> QualifiedName { - let mut qualified_name = QualifiedName::from(name); + let mut raw_name = BNQualifiedName::from(name.into()); let id_str = id.into_bytes_with_nul(); - let name_handle = unsafe { + let result_raw_name = unsafe { BNDefineAnalysisType( self.as_ref().handle, id_str.as_ref().as_ptr() as *const _, - &mut qualified_name.0, + &mut raw_name, type_obj.handle, ) }; - QualifiedName(name_handle) + QualifiedName::from(result_raw_name) } - fn define_user_type(&self, name: S, type_obj: &Type) { - let mut qualified_name = QualifiedName::from(name); - unsafe { - BNDefineUserAnalysisType(self.as_ref().handle, &mut qualified_name.0, type_obj.handle) - } + fn define_user_type>(&self, name: T, type_obj: &Type) { + let mut raw_name = BNQualifiedName::from(name.into()); + unsafe { BNDefineUserAnalysisType(self.as_ref().handle, &mut raw_name, type_obj.handle) } + } + + fn define_auto_types(&self, names_sources_and_types: T) -> HashMap + where + T: Iterator, + I: Into, + { + self.define_auto_types_with_progress(names_sources_and_types, None) } - fn define_auto_types( + fn define_auto_types_with_progress( &self, - names_sources_and_types: Vec<(S, S, &Type)>, + names_sources_and_types: T, progress: Option Result<()>>>, - ) -> HashMap { - let mut names = vec![]; - let mut ids = vec![]; - let mut types = vec![]; - let mut api_types = - Vec::::with_capacity(names_sources_and_types.len()); - for (name, source, type_obj) in names_sources_and_types.into_iter() { - names.push(QualifiedName::from(name)); - ids.push(source.into_bytes_with_nul()); - types.push(type_obj); - } - - for ((name, source), type_obj) in names.iter().zip(ids.iter()).zip(types.iter()) { - api_types.push(BNQualifiedNameTypeAndId { - name: name.0, - id: source.as_ref().as_ptr() as *mut _, - type_: type_obj.handle, - }); - } - + ) -> HashMap + where + T: Iterator, + I: Into, + { + let mut types: Vec = names_sources_and_types + .map(Into::into) + .map(Into::into) + .collect(); let mut progress_raw = ProgressContext(progress); let mut result_ids: *mut *mut c_char = std::ptr::null_mut(); let mut result_names: *mut BNQualifiedName = std::ptr::null_mut(); let result_count = unsafe { BNDefineAnalysisTypes( self.as_ref().handle, - api_types.as_mut_ptr(), - api_types.len(), + types.as_mut_ptr(), + types.len(), Some(cb_progress), &mut progress_raw as *mut _ as *mut c_void, &mut result_ids as *mut _, @@ -674,44 +664,39 @@ pub trait BinaryViewExt: BinaryViewBase { ) }; - let mut result = HashMap::with_capacity(result_count); - let id_array = unsafe { Array::::new(result_ids, result_count, ()) }; let name_array = unsafe { Array::::new(result_names, result_count, ()) }; + id_array + .into_iter() + .zip(name_array.into_iter()) + .map(|(id, name)| (id.to_owned(), name)) + .collect() + } - for (id, name) in id_array.iter().zip(name_array.iter()) { - result.insert(id.to_owned(), name.clone()); - } - - result + fn define_user_types(&self, names_and_types: T) + where + T: Iterator, + I: Into, + { + self.define_user_types_with_progress(names_and_types, None); } - fn define_user_types( + fn define_user_types_with_progress( &self, - names_and_types: Vec<(S, &Type)>, + names_and_types: T, progress: Option Result<()>>>, - ) { - let mut names = vec![]; - let mut types = vec![]; - let mut api_types = Vec::::with_capacity(names_and_types.len()); - for (name, type_obj) in names_and_types.into_iter() { - names.push(QualifiedName::from(name)); - types.push(type_obj); - } - - for (name, type_obj) in names.iter().zip(types.iter()) { - api_types.push(BNQualifiedNameAndType { - name: name.0, - type_: type_obj.handle, - }); - } - + ) where + T: Iterator, + I: Into, + { + let mut types: Vec = + names_and_types.map(Into::into).map(Into::into).collect(); let mut progress_raw = ProgressContext(progress); unsafe { BNDefineUserAnalysisTypes( self.as_ref().handle, - api_types.as_mut_ptr(), - api_types.len(), + types.as_mut_ptr(), + types.len(), Some(cb_progress), &mut progress_raw as *mut _ as *mut c_void, ) @@ -725,9 +710,9 @@ pub trait BinaryViewExt: BinaryViewBase { } } - fn undefine_user_type(&self, name: S) { - let mut qualified_name = QualifiedName::from(name); - unsafe { BNUndefineUserAnalysisType(self.as_ref().handle, &mut qualified_name.0) } + fn undefine_user_type>(&self, name: T) { + let mut raw_name = BNQualifiedName::from(name.into()); + unsafe { BNUndefineUserAnalysisType(self.as_ref().handle, &mut raw_name) } } fn types(&self) -> Array { @@ -746,10 +731,10 @@ pub trait BinaryViewExt: BinaryViewBase { } } - fn get_type_by_name(&self, name: S) -> Option> { + fn get_type_by_name>(&self, name: T) -> Option> { + let mut raw_name = BNQualifiedName::from(name.into()); unsafe { - let mut qualified_name = QualifiedName::from(name); - let type_handle = BNGetAnalysisTypeByName(self.as_ref().handle, &mut qualified_name.0); + let type_handle = BNGetAnalysisTypeByName(self.as_ref().handle, &mut raw_name); if type_handle.is_null() { return None; } @@ -784,49 +769,46 @@ pub trait BinaryViewExt: BinaryViewBase { let id_str = id.into_bytes_with_nul(); let name_handle = BNGetAnalysisTypeNameById(self.as_ref().handle, id_str.as_ref().as_ptr() as *mut _); - let name = QualifiedName(name_handle); - if name.strings().is_empty() { - return None; + let name = QualifiedName::from(name_handle); + // The core will return an empty qualified name if no type name was found. + match name.items.is_empty() { + true => None, + false => Some(name), } - Some(name) } } - fn get_type_id(&self, name: S) -> Option { + fn get_type_id>(&self, name: T) -> Option { + let mut raw_name = BNQualifiedName::from(name.into()); unsafe { - let mut qualified_name = QualifiedName::from(name); - let id_cstr = BNGetAnalysisTypeId(self.as_ref().handle, &mut qualified_name.0); + let id_cstr = BNGetAnalysisTypeId(self.as_ref().handle, &mut raw_name); let id = BnString::from_raw(id_cstr); - if id.is_empty() { - return None; + match id.is_empty() { + true => None, + false => Some(id), } - Some(id) } } - fn is_type_auto_defined(&self, name: S) -> bool { - unsafe { - let mut qualified_name = QualifiedName::from(name); - BNIsAnalysisTypeAutoDefined(self.as_ref().handle, &mut qualified_name.0) - } + fn is_type_auto_defined>(&self, name: T) -> bool { + let mut raw_name = BNQualifiedName::from(name.into()); + unsafe { BNIsAnalysisTypeAutoDefined(self.as_ref().handle, &mut raw_name) } } fn segments(&self) -> Array { unsafe { let mut count = 0; - let segs = BNGetSegments(self.as_ref().handle, &mut count); - - Array::new(segs, count, ()) + let raw_segments = BNGetSegments(self.as_ref().handle, &mut count); + Array::new(raw_segments, count, ()) } } - fn segment_at(&self, addr: u64) -> Option { + fn segment_at(&self, addr: u64) -> Option> { unsafe { let raw_seg = BNGetSegmentAt(self.as_ref().handle, addr); - if !raw_seg.is_null() { - Some(Segment::from_raw(raw_seg)) - } else { - None + match raw_seg.is_null() { + false => Some(Segment::ref_from_raw(raw_seg)), + true => None, } } } @@ -878,20 +860,18 @@ pub trait BinaryViewExt: BinaryViewBase { } fn remove_auto_section(&self, name: S) { - let name = name.into_bytes_with_nul(); - let name_ptr = name.as_ref().as_ptr() as *mut _; - + let raw_name = name.into_bytes_with_nul(); + let raw_name_ptr = raw_name.as_ref().as_ptr() as *mut _; unsafe { - BNRemoveAutoSection(self.as_ref().handle, name_ptr); + BNRemoveAutoSection(self.as_ref().handle, raw_name_ptr); } } fn remove_user_section(&self, name: S) { - let name = name.into_bytes_with_nul(); - let name_ptr = name.as_ref().as_ptr() as *mut _; - + let raw_name = name.into_bytes_with_nul(); + let raw_name_ptr = raw_name.as_ref().as_ptr() as *mut _; unsafe { - BNRemoveUserSection(self.as_ref().handle, name_ptr); + BNRemoveUserSection(self.as_ref().handle, raw_name_ptr); } } @@ -911,7 +891,6 @@ pub trait BinaryViewExt: BinaryViewBase { unsafe { let mut count = 0; let sections = BNGetSections(self.as_ref().handle, &mut count); - Array::new(sections, count, ()) } } @@ -920,7 +899,6 @@ pub trait BinaryViewExt: BinaryViewBase { unsafe { let mut count = 0; let sections = BNGetSectionsAt(self.as_ref().handle, addr, &mut count); - Array::new(sections, count, ()) } } @@ -1396,38 +1374,23 @@ pub trait BinaryViewExt: BinaryViewBase { } /// Retrieves a list of [CodeReference]s for locations in code that use a given named type. - /// - /// TODO: It might be cleaner if this used an already allocated type from the core and - /// used its name instead of this slightly-gross [QualifiedName] hack. Since the returned - /// object doesn't have any [QualifiedName], I'm assuming the core does not alias - /// the [QualifiedName] we pass to it and it is safe to destroy it on [Drop], as in this function. - fn get_code_refs_for_type(&self, name: B) -> Array { + fn get_code_refs_for_type>(&self, name: T) -> Array { + let mut raw_name = BNQualifiedName::from(name.into()); unsafe { let mut count = 0; - let q_name = &mut QualifiedName::from(name).0; - let handle = BNGetCodeReferencesForType( - self.as_ref().handle, - q_name as *mut BNQualifiedName, - &mut count, - ); + let handle = + BNGetCodeReferencesForType(self.as_ref().handle, &mut raw_name, &mut count); Array::new(handle, count, ()) } } + /// Retrieves a list of [DataReference]s instances of a given named type in data. - /// - /// TODO: It might be cleaner if this used an already allocated type from the core and - /// used its name instead of this slightly-gross [QualifiedName] hack. Since the returned - /// object doesn't have any [QualifiedName], I'm assuming the core does not alias - /// the [QualifiedName] we pass to it and it is safe to destroy it on [Drop], as in this function. - fn get_data_refs_for_type(&self, name: B) -> Array { + fn get_data_refs_for_type>(&self, name: T) -> Array { + let mut raw_name = BNQualifiedName::from(name.into()); unsafe { let mut count = 0; - let q_name = &mut QualifiedName::from(name).0; - let handle = BNGetDataReferencesForType( - self.as_ref().handle, - q_name as *mut BNQualifiedName, - &mut count, - ); + let handle = + BNGetDataReferencesForType(self.as_ref().handle, &mut raw_name, &mut count); Array::new(handle, count, ()) } } @@ -1535,20 +1498,21 @@ pub trait BinaryViewExt: BinaryViewBase { /// * `name` - Name of the object in the type library /// * `addr` - address of symbol at import site /// * `platform` - Platform of symbol at import site - fn record_imported_object_library( + fn record_imported_object_library>( &self, lib: &TypeLibrary, - name: &QualifiedName, + name: T, addr: u64, platform: &Platform, ) { + let mut raw_name = BNQualifiedName::from(name.into()); unsafe { BNBinaryViewRecordImportedObjectLibrary( self.as_ref().handle, platform.handle, addr, lib.as_raw(), - &name.0 as *const _ as *mut _, + &mut raw_name, ) } } @@ -1563,21 +1527,18 @@ pub trait BinaryViewExt: BinaryViewBase { /// Note that the name actually inserted into the view may not match the name as it exists in the type library in /// the event of a name conflict. To aid in this, the [Type] object returned is a `NamedTypeReference` to /// the deconflicted name used. - fn import_type_library( + fn import_type_library>( &self, - name: &QualifiedName, + name: T, mut lib: Option, ) -> Option> { let mut lib_ref = lib .as_mut() .map(|l| unsafe { l.as_raw() } as *mut _) .unwrap_or(std::ptr::null_mut()); + let mut raw_name = BNQualifiedName::from(name.into()); let result = unsafe { - BNBinaryViewImportTypeLibraryType( - self.as_ref().handle, - &mut lib_ref, - &name.0 as *const _ as *mut _, - ) + BNBinaryViewImportTypeLibraryType(self.as_ref().handle, &mut lib_ref, &mut raw_name) }; (!result.is_null()).then(|| unsafe { Type::ref_from_raw(result) }) } @@ -1591,21 +1552,18 @@ pub trait BinaryViewExt: BinaryViewBase { /// /// .. note:: If you are implementing a custom BinaryView and use this method to import object types, /// you should then call [BinaryViewExt::record_imported_object_library] with the details of where the object is located. - fn import_type_object( + fn import_type_object>( &self, - name: &QualifiedName, + name: T, mut lib: Option, ) -> Option> { let mut lib_ref = lib .as_mut() .map(|l| unsafe { l.as_raw() } as *mut _) .unwrap_or(std::ptr::null_mut()); + let mut raw_name = BNQualifiedName::from(name.into()); let result = unsafe { - BNBinaryViewImportTypeLibraryObject( - self.as_ref().handle, - &mut lib_ref, - &name.0 as *const _ as *mut _, - ) + BNBinaryViewImportTypeLibraryObject(self.as_ref().handle, &mut lib_ref, &mut raw_name) }; (!result.is_null()).then(|| unsafe { Type::ref_from_raw(result) }) } @@ -1631,12 +1589,18 @@ pub trait BinaryViewExt: BinaryViewBase { /// /// As other referenced types are encountered, they are either copied into the destination type library or /// else the type library that provided the referenced type is added as a dependency for the destination library. - fn export_type_to_library(&self, lib: &TypeLibrary, name: &QualifiedName, type_obj: &Type) { + fn export_type_to_library>( + &self, + lib: &TypeLibrary, + name: T, + type_obj: &Type, + ) { + let mut raw_name = BNQualifiedName::from(name.into()); unsafe { BNBinaryViewExportTypeToTypeLibrary( self.as_ref().handle, lib.as_raw(), - &name.0 as *const _ as *mut _, + &mut raw_name, type_obj.handle, ) } @@ -1646,12 +1610,18 @@ pub trait BinaryViewExt: BinaryViewBase { /// /// As other referenced types are encountered, they are either copied into the destination type library or /// else the type library that provided the referenced type is added as a dependency for the destination library. - fn export_object_to_library(&self, lib: &TypeLibrary, name: &QualifiedName, type_obj: &Type) { + fn export_object_to_library>( + &self, + lib: &TypeLibrary, + name: T, + type_obj: &Type, + ) { + let mut raw_name = BNQualifiedName::from(name.into()); unsafe { BNBinaryViewExportObjectToTypeLibrary( self.as_ref().handle, lib.as_raw(), - &name.0 as *const _ as *mut _, + &mut raw_name, type_obj.handle, ) } @@ -1668,7 +1638,7 @@ pub trait BinaryViewExt: BinaryViewBase { platform: &Platform, ) -> Option<(TypeLibrary, QualifiedName)> { let mut result_lib = std::ptr::null_mut(); - let mut result_name = Default::default(); + let mut result_name = BNQualifiedName::default(); let success = unsafe { BNBinaryViewLookupImportedObjectLibrary( self.as_ref().handle, @@ -1682,23 +1652,24 @@ pub trait BinaryViewExt: BinaryViewBase { return None; } let lib = unsafe { TypeLibrary::from_raw(NonNull::new(result_lib)?) }; - let name = QualifiedName(result_name); + let name = QualifiedName::from(result_name); Some((lib, name)) } /// Gives you details of from which type library and name a given type in the analysis was imported. /// /// * `name` - Name of type in analysis - fn lookup_imported_type_library( + fn lookup_imported_type_library>( &self, - name: &QualifiedNameAndType, + name: T, ) -> Option<(TypeLibrary, QualifiedName)> { + let raw_name = BNQualifiedName::from(name.into()); let mut result_lib = std::ptr::null_mut(); - let mut result_name = Default::default(); + let mut result_name = BNQualifiedName::default(); let success = unsafe { BNBinaryViewLookupImportedTypeLibrary( self.as_ref().handle, - &name.0 as *const _ as *mut _, + &raw_name, &mut result_lib, &mut result_name, ) @@ -1707,9 +1678,26 @@ pub trait BinaryViewExt: BinaryViewBase { return None; } let lib = unsafe { TypeLibrary::from_raw(NonNull::new(result_lib)?) }; - let name = QualifiedName(result_name); + let name = QualifiedName::from(result_name); Some((lib, name)) } + // + // fn type_archives(&self) -> Array { + // let mut ids: *mut *mut c_char = std::ptr::null_mut(); + // let mut paths: *mut *mut c_char = std::ptr::null_mut(); + // let count = unsafe { BNBinaryViewGetTypeArchives(self.as_ref().handle, &mut ids, &mut paths) }; + // let path_list = unsafe { Array::::new(paths, count, ()) }; + // let ids_list = unsafe { std::slice::from_raw_parts(ids, count).to_vec() }; + // let archives = ids_list.iter().filter_map(|id| { + // let archive_raw = unsafe { BNBinaryViewGetTypeArchive(self.as_ref().handle, *id) }; + // match archive_raw.is_null() { + // true => None, + // false => Some(archive_raw) + // } + // }).collect(); + // unsafe { BNFreeStringList(ids, count) }; + // Array::new(archives) + // } } impl BinaryViewExt for T {} diff --git a/rust/src/custombinaryview.rs b/rust/src/custombinaryview.rs index 7160b78ed..cfef7886b 100644 --- a/rust/src/custombinaryview.rs +++ b/rust/src/custombinaryview.rs @@ -98,7 +98,7 @@ where Ref::into_raw(bv.handle).handle } Err(_) => { - error!("CustomBinaryViewType::create_custom_view returned Err"); + log::error!("CustomBinaryViewType::create_custom_view returned Err"); ptr::null_mut() } } @@ -126,7 +126,7 @@ where Ref::into_raw(bv.handle).handle } Err(_) => { - error!("CustomBinaryViewType::parse returned Err"); + log::error!("CustomBinaryViewType::parse returned Err"); ptr::null_mut() } } @@ -242,7 +242,7 @@ pub trait BinaryViewTypeExt: BinaryViewTypeBase { let handle = unsafe { BNCreateBinaryViewOfType(self.as_ref().handle, data.handle) }; if handle.is_null() { - error!( + log::error!( "failed to create BinaryView of BinaryViewType '{}'", self.name() ); @@ -256,7 +256,7 @@ pub trait BinaryViewTypeExt: BinaryViewTypeBase { let handle = unsafe { BNParseBinaryViewOfType(self.as_ref().handle, data.handle) }; if handle.is_null() { - error!( + log::error!( "failed to parse BinaryView of BinaryViewType '{}'", self.name() ); @@ -450,7 +450,7 @@ impl<'a, T: CustomBinaryViewType> CustomViewBuilder<'a, T> { // even if we deal with it gracefully in cb_free_object, // BNCreateBinaryViewOfType is still going to crash, so we're just // going to try and stop this from happening in the first place. - error!( + log::error!( "attempt to create duplicate view of type '{}' (existing: {:?})", view_name.as_str(), bv.handle @@ -511,12 +511,12 @@ impl<'a, T: CustomBinaryViewType> CustomViewBuilder<'a, T> { true } Err(_) => { - error!("CustomBinaryView::init failed; custom view returned Err"); + log::error!("CustomBinaryView::init failed; custom view returned Err"); false } }, Err(_) => { - error!("CustomBinaryView::new failed; custom view returned Err"); + log::error!("CustomBinaryView::new failed; custom view returned Err"); false } } @@ -546,7 +546,7 @@ impl<'a, T: CustomBinaryViewType> CustomViewBuilder<'a, T> { // // if we're here, it's too late to do anything about it, though we can at least not // run the destructor on the custom view since that memory is unitialized. - error!( + log::error!( "BinaryViewBase::freeObject called on partially initialized object! crash imminent!" ); } else if matches!( @@ -565,7 +565,7 @@ impl<'a, T: CustomBinaryViewType> CustomViewBuilder<'a, T> { // // we can't do anything to prevent this, but we can at least have the crash // not be our fault. - error!("BinaryViewBase::freeObject called on leaked/never initialized custom view!"); + log::error!("BinaryViewBase::freeObject called on leaked/never initialized custom view!"); } }) } diff --git a/rust/src/demangle.rs b/rust/src/demangle.rs index cd3279ffd..128df6af1 100644 --- a/rust/src/demangle.rs +++ b/rust/src/demangle.rs @@ -15,7 +15,7 @@ //! Interfaces for demangling and simplifying mangled names in binaries. use binaryninjacore_sys::*; -use std::ffi::{c_char, c_void, CStr}; +use std::ffi::{c_char, c_void}; use crate::architecture::CoreArchitecture; use crate::binaryview::BinaryView; @@ -31,56 +31,34 @@ pub fn demangle_generic( mangled_name: S, view: Option<&BinaryView>, simplify: bool, -) -> Result<(Option>, Vec)> { +) -> Option<(QualifiedName, Option>)> { 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::default(); - let view_ptr = match view { - Some(v) => v.handle, - None => std::ptr::null_mut(), - }; let res = unsafe { BNDemangleGeneric( 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((out_name.into(), 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::>(), - )) } -pub fn demangle_llvm(mangled_name: S, simplify: bool) -> Result> { +pub fn demangle_llvm(mangled_name: S, simplify: bool) -> Option> { 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 = std::ptr::null_mut(); @@ -94,38 +72,26 @@ pub fn demangle_llvm(mangled_name: S, simplify: bool) -> Res ) }; - 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()]); - } - - if out_name.is_null() { - log::error!("demangle_llvm: out_name is NULL"); - return Err(()); + match res { + true => { + assert!(!out_name.is_null()); + let names = unsafe { ArrayGuard::::new(out_name, out_size, ()) } + .iter() + .map(str::to_string) + .collect(); + unsafe { BNFreeDemangledName(&mut out_name, out_size) }; + + Some(names) + } + false => None, } - - let names = unsafe { ArrayGuard::::new(out_name, out_size, ()) } - .iter() - .map(str::to_string) - .collect(); - - // TODO: This wont get freed on early returns. - unsafe { BNFreeDemangledName(&mut out_name, out_size) }; - - Ok(names) } pub fn demangle_gnu3( arch: &CoreArchitecture, mangled_name: S, simplify: bool, -) -> Result<(Option>, Vec)> { +) -> Option<(Vec, Option>)> { 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(); @@ -142,45 +108,31 @@ pub fn demangle_gnu3( ) }; - 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 + assert!(!out_name.is_null()); + let names = unsafe { ArrayGuard::::new(out_name, out_size, ()) } + .iter() + .map(str::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) }), + }; + + Some((names, out_type)) } - false => Some(unsafe { Type::ref_from_raw(out_type) }), - }; - - if out_name.is_null() { - log::error!("demangle_gnu3: out_name is NULL"); - return Err(()); + false => None, } - - let names = unsafe { ArrayGuard::::new(out_name, out_size, ()) } - .iter() - .map(str::to_string) - .collect(); - - unsafe { BNFreeDemangledName(&mut out_name, out_size) }; - - Ok((out_type, names)) } pub fn demangle_ms( arch: &CoreArchitecture, mangled_name: S, simplify: bool, -) -> Result<(Option>, Vec)> { +) -> Option<(Vec, Option>)> { let mangled_name_bwn = mangled_name.into_bytes_with_nul(); let mangled_name_ptr = mangled_name_bwn.as_ref(); @@ -198,38 +150,24 @@ pub fn demangle_ms( ) }; - 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 + assert!(!out_name.is_null()); + let names = unsafe { ArrayGuard::::new(out_name, out_size, ()) } + .iter() + .map(str::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) }), + }; + + Some((names, out_type)) } - false => Some(unsafe { Type::ref_from_raw(out_type) }), - }; - - if out_name.is_null() { - log::error!("demangle_ms: out_name is NULL"); - return Err(()); + false => None, } - - let names = unsafe { ArrayGuard::::new(out_name, out_size, ()) } - .iter() - .map(|name| name.to_string()) - .collect(); - - unsafe { BNFreeDemangledName(&mut out_name, out_size) }; - - Ok((out_type, names)) } #[derive(PartialEq, Eq, Hash)] @@ -240,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 { + let mut count: usize = 0; + let demanglers = unsafe { BNGetDemanglerList(&mut count) }; + unsafe { Array::::new(demanglers, count, ()) } + } + pub fn is_mangled_string(&self, name: S) -> bool { let bytes = name.into_bytes_with_nul(); unsafe { BNIsDemanglerMangledName(self.handle, bytes.as_ref().as_ptr() as *const _) } @@ -254,22 +197,18 @@ impl Demangler { arch: &CoreArchitecture, name: S, view: Option<&BinaryView>, - ) -> Result<(Option>, QualifiedName)> { + ) -> Option<(QualifiedName, Option>)> { 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(), }; - if !unsafe { + let res = unsafe { BNDemanglerDemangle( self.handle, arch.handle, @@ -278,18 +217,19 @@ impl Demangler { &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)) + match res { + true => { + let var_type = match out_type.is_null() { + true => None, + false => Some(unsafe { Type::ref_from_raw(out_type) }), + }; + + Some((out_var_name.into(), var_type)) + } + false => None, + } } pub fn name(&self) -> BnString { @@ -306,13 +246,7 @@ impl Demangler { } } - pub fn list() -> Array { - let mut count: usize = 0; - let demanglers = unsafe { BNGetDemanglerList(&mut count) }; - unsafe { Array::::new(demanglers, count, ()) } - } - - pub fn register(name: S, callbacks: C) -> Self + pub fn register(name: S, demangler: C) -> Self where S: BnStrCompatible, C: CustomDemangler, @@ -357,35 +291,33 @@ impl Demangler { }; match cmd.demangle(&arch, &name, view) { - Ok((type_, name)) => { - *out_type = match type_ { + Some((name, ty)) => { + *out_type = match ty { 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); + *out_var_name = name.into(); true } - Err(_) => false, + None => false, } }) } + extern "C" fn cb_free_var_name(_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); + // TODO: What is the point of this free callback? + // TODO: The core can just call BNFreeQualifiedName. + BNFreeQualifiedName(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, @@ -437,5 +369,5 @@ pub trait CustomDemangler: 'static + Sync { arch: &CoreArchitecture, name: &str, view: Option>, - ) -> Result<(Option>, QualifiedName)>; + ) -> Option<(QualifiedName, Option>)>; } diff --git a/rust/src/ffi.rs b/rust/src/ffi.rs index eb5848280..c8a0ac542 100644 --- a/rust/src/ffi.rs +++ b/rust/src/ffi.rs @@ -18,7 +18,7 @@ macro_rules! ffi_wrap { use std::process; panic::catch_unwind(|| $b).unwrap_or_else(|_| { - error!("ffi callback caught panic: {}", $n); + log::error!("ffi callback caught panic: {}", $n); process::abort() }) }}; diff --git a/rust/src/fileaccessor.rs b/rust/src/fileaccessor.rs index cf94e902b..3f3247101 100644 --- a/rust/src/fileaccessor.rs +++ b/rust/src/fileaccessor.rs @@ -51,7 +51,7 @@ impl<'a> FileAccessor<'a> { let dest = unsafe { slice::from_raw_parts_mut(dest as *mut u8, len) }; if f.seek(SeekFrom::Start(offset)).is_err() { - debug!("Failed to seek to offset {:x}", offset); + log::debug!("Failed to seek to offset {:x}", offset); 0 } else { f.read(dest).unwrap_or(0) diff --git a/rust/src/function.rs b/rust/src/function.rs index 8a0b6ed86..62b39a694 100644 --- a/rust/src/function.rs +++ b/rust/src/function.rs @@ -1193,17 +1193,17 @@ impl Function { /// ```no_run /// # use binaryninja::function::Function; /// # let fun: Function = todo!(); - /// fun.add_user_type_ref(0x1337, &"A".into(), None); + /// fun.add_user_type_ref(0x1337, "A", None); /// ``` - pub fn add_user_type_ref( + pub fn add_user_type_ref>( &self, from_addr: u64, - name: &QualifiedName, + name: T, arch: Option, ) { let arch = arch.unwrap_or_else(|| self.arch()); - let name_ptr = &name.0 as *const BNQualifiedName as *mut _; - unsafe { BNAddUserTypeReference(self.handle, arch.handle, from_addr, name_ptr) } + let mut raw_name = BNQualifiedName::from(name.into()); + unsafe { BNAddUserTypeReference(self.handle, arch.handle, from_addr, &mut raw_name) } } /// Removes a user-defined type cross-reference. @@ -1218,17 +1218,17 @@ impl Function { /// ```no_run /// # use binaryninja::function::Function; /// # let fun: Function = todo!(); - /// fun.remove_user_type_ref(0x1337, &"A".into(), None); + /// fun.remove_user_type_ref(0x1337, "A", None); /// ``` - pub fn remove_user_type_ref( + pub fn remove_user_type_ref>( &self, from_addr: u64, - name: &QualifiedName, + name: T, arch: Option, ) { let arch = arch.unwrap_or_else(|| self.arch()); - let name_ptr = &name.0 as *const BNQualifiedName as *mut _; - unsafe { BNRemoveUserTypeReference(self.handle, arch.handle, from_addr, name_ptr) } + let mut raw_name = BNQualifiedName::from(name.into()); + unsafe { BNRemoveUserTypeReference(self.handle, arch.handle, from_addr, &mut raw_name) } } /// Places a user-defined type field cross-reference from the @@ -1246,21 +1246,28 @@ impl Function { /// ```no_run /// # use binaryninja::function::Function; /// # let fun: Function = todo!(); - /// fun.add_user_type_field_ref(0x1337, &"A".into(), 0x8, None, None); + /// fun.add_user_type_field_ref(0x1337, "A", 0x8, None, None); /// ``` - pub fn add_user_type_field_ref( + pub fn add_user_type_field_ref>( &self, from_addr: u64, - name: &QualifiedName, + name: T, offset: u64, arch: Option, size: Option, ) { let size = size.unwrap_or(0); let arch = arch.unwrap_or_else(|| self.arch()); - let name_ptr = &name.0 as *const _ as *mut _; + let mut raw_name = BNQualifiedName::from(name.into()); unsafe { - BNAddUserTypeFieldReference(self.handle, arch.handle, from_addr, name_ptr, offset, size) + BNAddUserTypeFieldReference( + self.handle, + arch.handle, + from_addr, + &mut raw_name, + offset, + size, + ) } } @@ -1278,25 +1285,25 @@ impl Function { /// ```no_run /// # use binaryninja::function::Function; /// # let fun: Function = todo!(); - /// fun.remove_user_type_field_ref(0x1337, &"A".into(), 0x8, None, None); + /// fun.remove_user_type_field_ref(0x1337, "A", 0x8, None, None); /// ``` - pub fn remove_user_type_field_ref( + pub fn remove_user_type_field_ref>( &self, from_addr: u64, - name: &QualifiedName, + name: T, offset: u64, arch: Option, size: Option, ) { let size = size.unwrap_or(0); let arch = arch.unwrap_or_else(|| self.arch()); - let name_ptr = &name.0 as *const _ as *mut _; + let mut raw_name = BNQualifiedName::from(name.into()); unsafe { BNRemoveUserTypeFieldReference( self.handle, arch.handle, from_addr, - name_ptr, + &mut raw_name, offset, size, ) diff --git a/rust/src/headless.rs b/rust/src/headless.rs index e324a85d2..a72fd669c 100644 --- a/rust/src/headless.rs +++ b/rust/src/headless.rs @@ -13,75 +13,68 @@ // limitations under the License. use crate::{ - binaryview, rc, - string::{BnStrCompatible, IntoJson}, + binaryview, bundled_plugin_directory, is_main_thread, rc, set_bundled_plugin_directory, + string::IntoJson, }; -use std::env; -use std::path::PathBuf; +use crate::mainthread::{MainThreadAction, MainThreadHandler}; +use crate::rc::Ref; +use binaryninjacore_sys::{BNInitPlugins, BNInitRepoPlugins}; +use std::sync::mpsc::Sender; use std::time::Duration; -#[cfg(not(target_os = "windows"))] -fn binja_path() -> PathBuf { - use std::ffi::{CStr, OsStr}; - use std::mem; - use std::os::raw; - use std::os::unix::ffi::OsStrExt; - - #[repr(C)] - struct DlInfo { - dli_fname: *const raw::c_char, - dli_fbase: *mut raw::c_void, - dli_sname: *const raw::c_char, - dli_saddr: *mut raw::c_void, - } - - if let Ok(p) = env::var("BINJA_DIR") { - return PathBuf::from(p); - } - - extern "C" { - fn dladdr(addr: *mut raw::c_void, info: *mut DlInfo) -> raw::c_int; - } - - unsafe { - let mut info: DlInfo = mem::zeroed(); - - if dladdr(BNSetBundledPluginDirectory as *mut _, &mut info) == 0 { - panic!("Failed to find libbinaryninjacore path!"); - } - - if info.dli_fname.is_null() { - panic!("Failed to find libbinaryninjacore path!"); - } - - let path = CStr::from_ptr(info.dli_fname); - let path = OsStr::from_bytes(path.to_bytes()); - let mut path = PathBuf::from(path); - while path.is_symlink() { - path = path - .read_link() - .expect("Failed to find libbinaryninjacore path!"); - } +#[derive(Debug)] +pub struct HeadlessMainThreadSender { + sender: Sender>, +} - path.pop(); - path +impl HeadlessMainThreadSender { + pub fn new(sender: Sender>) -> Self { + Self { sender } } } -#[cfg(target_os = "windows")] -fn binja_path() -> PathBuf { - PathBuf::from(env::var("PROGRAMFILES").unwrap()).join("Vector35\\BinaryNinja\\") +impl MainThreadHandler for HeadlessMainThreadSender { + fn add_action(&self, action: Ref) { + self.sender + .send(action) + .expect("Failed to send action to main thread"); + } } -use binaryninjacore_sys::{BNInitPlugins, BNInitRepoPlugins, BNSetBundledPluginDirectory}; - /// Loads plugins, core architecture, platform, etc. /// /// ⚠️ Important! Must be called at the beginning of scripts. Plugins do not need to call this. ⚠️ /// /// You can instead call this through [`Session`]. +/// +/// If you are using a custom [`MainThreadHandler`] than use [`init_without_main_thread`] instead. pub fn init() { + // If we are the main thread that means there is no main thread. + if is_main_thread() { + let (sender, receiver) = std::sync::mpsc::channel(); + let main_thread = HeadlessMainThreadSender::new(sender); + + // This thread will act as our main thread. + std::thread::Builder::new() + .name("HeadlessMainThread".to_string()) + .spawn(move || { + // We must register the main thread within said thread. + main_thread.register(); + while let Ok(action) = receiver.recv() { + action.execute(); + } + }) + .expect("Failed to spawn main thread"); + } + + init_without_main_thread(); +} + +/// This initializes the core without registering a main thread handler. +/// +/// Call this if you have previously registered a [`MainThreadHandler`]. +pub fn init_without_main_thread() { match crate::product().as_str() { "Binary Ninja Enterprise Client" | "Binary Ninja Ultimate" => { crate::enterprise::checkout_license(Duration::from_secs(900)) @@ -90,11 +83,11 @@ pub fn init() { _ => {} } - unsafe { - let path = binja_path().join("plugins").into_os_string(); - let path = path.into_string().unwrap(); + let bundled_plugin_dir = + bundled_plugin_directory().expect("Failed to get bundled plugin directory"); + set_bundled_plugin_directory(bundled_plugin_dir); - BNSetBundledPluginDirectory(path.as_str().into_bytes_with_nul().as_ptr() as *mut _); + unsafe { BNInitPlugins(true); BNInitRepoPlugins(); } diff --git a/rust/src/lib.rs b/rust/src/lib.rs index 09698b6c9..0fcf83fc0 100644 --- a/rust/src/lib.rs +++ b/rust/src/lib.rs @@ -31,6 +31,7 @@ //! //! --- //! # Warning +//! //! //! //! > ⚠️ **These bindings are in a very early beta, only have partial support for the core APIs and are still actively under development. Compatibility _will_ break and conventions _will_ change! They are being used for core Binary Ninja features however, so we expect much of what is already there to be reliable enough to build on, just don't be surprised if your plugins/scripts need to hit a moving target.** @@ -72,7 +73,9 @@ //! The most up-to-date version of the suggested [`build.rs` is here]. //! //! ### `main.rs` +//! //! Standalone binaries need to initialize Binary Ninja before they can work. You can do this through [`headless::Session`], or [`headless::init()`] at start and [`headless::shutdown()`] at shutdown. +//! //! ```no_run //! // This loads all the core architecture, platform, etc plugins //! // Standalone executables need to call this, but plugins do not @@ -85,6 +88,7 @@ //! ``` //! //! ### `Cargo.toml` +//! //! ```toml //! [dependencies] //! binaryninja = { git = "https://github.com/Vector35/binaryninja-api.git", branch = "dev"} @@ -102,8 +106,6 @@ //! [examples]: https://github.com/Vector35/binaryninja-api/tree/dev/rust/examples //! -#[macro_use] -extern crate log; #[doc(hidden)] pub extern crate binaryninjacore_sys; #[cfg(feature = "rayon")] @@ -153,6 +155,7 @@ pub mod interaction; pub mod linearview; pub mod llil; pub mod logger; +pub mod mainthread; pub mod metadata; pub mod mlil; pub mod platform; @@ -180,27 +183,13 @@ use binaryninjacore_sys::*; use binaryview::BinaryView; use metadata::Metadata; use metadata::MetadataType; +use rc::Ref; use std::collections::HashMap; use std::ffi::{c_char, c_void, CStr}; use std::path::PathBuf; use string::BnStrCompatible; +use string::BnString; use string::IntoJson; -// Commented out to suppress unused warnings -// const BN_MAX_INSTRUCTION_LENGTH: u64 = 256; -// const BN_DEFAULT_INSTRUCTION_LENGTH: u64 = 16; -// const BN_DEFAULT_OPCODE_DISPLAY: u64 = 8; -// const BN_MAX_INSTRUCTION_BRANCHES: u64 = 3; -// const BN_MAX_STORED_DATA_LENGTH: u64 = 0x3fffffff; -// const BN_NULL_ID: i64 = -1; -// const BN_INVALID_REGISTER: usize = 0xffffffff; -// const BN_AUTOCOERCE_EXTERN_PTR: u64 = 0xfffffffd; -// const BN_NOCOERCE_EXTERN_PTR: u64 = 0xfffffffe; -// const BN_INVALID_OPERAND: u64 = 0xffffffff; -// const BN_MAX_STRING_LENGTH: u64 = 128; -// const BN_MAX_VARIABLE_OFFSET: u64 = 0x7fffffffff; -// const BN_MAX_VARIABLE_INDEX: u64 = 0xfffff; -// const BN_MINIMUM_CONFIDENCE: u8 = 1; -// const BN_HEURISTIC_CONFIDENCE: u8 = 192; pub use binaryninjacore_sys::BNBranchType as BranchType; pub use binaryninjacore_sys::BNDataFlowQueryOption as DataFlowQueryOption; @@ -227,7 +216,7 @@ unsafe extern "C" fn cb_progress_nop(_ctxt: *mut c_void, _arg1: usize, _arg2: us } /// The main way to open and load files into Binary Ninja. Make sure you've properly initialized the core before calling this function. See [`crate::headless::init()`] -pub fn load(filename: S) -> Option> +pub fn load(filename: S) -> Option> where S: BnStrCompatible, { @@ -251,7 +240,7 @@ where } } -pub fn load_with_progress(filename: S, mut progress: F) -> Option> +pub fn load_with_progress(filename: S, mut progress: F) -> Option> where S: BnStrCompatible, F: FnMut(usize, usize) -> bool, @@ -299,7 +288,7 @@ pub fn load_with_options( filename: S, update_analysis_and_wait: bool, options: Option, -) -> Option> +) -> Option> where S: BnStrCompatible, O: IntoJson, @@ -342,7 +331,7 @@ pub fn load_with_options_and_progress( update_analysis_and_wait: bool, options: Option, progress: Option, -) -> Option> +) -> Option> where S: BnStrCompatible, O: IntoJson, @@ -390,7 +379,7 @@ pub fn load_view( bv: &BinaryView, update_analysis_and_wait: bool, options: Option, -) -> Option> +) -> Option> where O: IntoJson, { @@ -430,7 +419,7 @@ pub fn load_view_with_progress( update_analysis_and_wait: bool, options: Option, progress: Option, -) -> Option> +) -> Option> where O: IntoJson, F: FnMut(usize, usize) -> bool, @@ -476,9 +465,7 @@ pub fn install_directory() -> Result { if s.is_null() { return Err(()); } - Ok(PathBuf::from( - unsafe { string::BnString::from_raw(s) }.to_string(), - )) + Ok(PathBuf::from(unsafe { BnString::from_raw(s) }.to_string())) } pub fn bundled_plugin_directory() -> Result { @@ -486,12 +473,10 @@ pub fn bundled_plugin_directory() -> Result { if s.is_null() { return Err(()); } - Ok(PathBuf::from( - unsafe { string::BnString::from_raw(s) }.to_string(), - )) + Ok(PathBuf::from(unsafe { BnString::from_raw(s) }.to_string())) } -pub fn set_bundled_plugin_directory(new_dir: S) { +pub fn set_bundled_plugin_directory(new_dir: S) { unsafe { BNSetBundledPluginDirectory(new_dir.into_bytes_with_nul().as_ref().as_ptr() as *const c_char) }; @@ -502,9 +487,7 @@ pub fn user_directory() -> Result { if s.is_null() { return Err(()); } - Ok(PathBuf::from( - unsafe { string::BnString::from_raw(s) }.to_string(), - )) + Ok(PathBuf::from(unsafe { BnString::from_raw(s) }.to_string())) } pub fn user_plugin_directory() -> Result { @@ -512,9 +495,7 @@ pub fn user_plugin_directory() -> Result { if s.is_null() { return Err(()); } - Ok(PathBuf::from( - unsafe { string::BnString::from_raw(s) }.to_string(), - )) + Ok(PathBuf::from(unsafe { BnString::from_raw(s) }.to_string())) } pub fn repositories_directory() -> Result { @@ -522,9 +503,7 @@ pub fn repositories_directory() -> Result { if s.is_null() { return Err(()); } - Ok(PathBuf::from( - unsafe { string::BnString::from_raw(s) }.to_string(), - )) + Ok(PathBuf::from(unsafe { BnString::from_raw(s) }.to_string())) } pub fn settings_file_name() -> Result { @@ -532,11 +511,12 @@ pub fn settings_file_name() -> Result { if s.is_null() { return Err(()); } - Ok(PathBuf::from( - unsafe { string::BnString::from_raw(s) }.to_string(), - )) + Ok(PathBuf::from(unsafe { BnString::from_raw(s) }.to_string())) } +/// Write the installation directory of the currently running core instance to disk. +/// +/// This is used to select the most recent installation for running scripts. pub fn save_last_run() { unsafe { BNSaveLastRun() }; } @@ -552,9 +532,7 @@ pub fn path_relative_to_bundled_plugin_directory( if s.is_null() { return Err(()); } - Ok(PathBuf::from( - unsafe { string::BnString::from_raw(s) }.to_string(), - )) + Ok(PathBuf::from(unsafe { BnString::from_raw(s) }.to_string())) } pub fn path_relative_to_user_plugin_directory( @@ -568,9 +546,7 @@ pub fn path_relative_to_user_plugin_directory( if s.is_null() { return Err(()); } - Ok(PathBuf::from( - unsafe { string::BnString::from_raw(s) }.to_string(), - )) + Ok(PathBuf::from(unsafe { BnString::from_raw(s) }.to_string())) } pub fn path_relative_to_user_directory(path: S) -> Result { @@ -582,9 +558,14 @@ pub fn path_relative_to_user_directory(path: S) -> R if s.is_null() { return Err(()); } - Ok(PathBuf::from( - unsafe { string::BnString::from_raw(s) }.to_string(), - )) + Ok(PathBuf::from(unsafe { BnString::from_raw(s) }.to_string())) +} + +/// Returns if the running thread is the "main thread" +/// +/// If there is no registered main thread than this will return the current thread. +pub fn is_main_thread() -> bool { + unsafe { binaryninjacore_sys::BNIsMainThread() } } pub fn memory_info() -> HashMap { @@ -650,8 +631,8 @@ pub trait ObjectDestructor: 'static + Sync + Sized { } } -pub fn version() -> string::BnString { - unsafe { string::BnString::from_raw(BNGetVersionString()) } +pub fn version() -> BnString { + unsafe { BnString::from_raw(BNGetVersionString()) } } pub fn build_id() -> u32 { @@ -663,7 +644,7 @@ pub struct VersionInfo { pub major: u32, pub minor: u32, pub build: u32, - pub channel: string::BnString, + pub channel: String, } pub fn version_info() -> VersionInfo { @@ -672,20 +653,20 @@ pub fn version_info() -> VersionInfo { major: info_raw.major, minor: info_raw.minor, build: info_raw.build, - channel: unsafe { string::BnString::from_raw(info_raw.channel) }, + channel: unsafe { BnString::from_raw(info_raw.channel).to_string() }, } } -pub fn serial_number() -> string::BnString { - unsafe { string::BnString::from_raw(BNGetSerialNumber()) } +pub fn serial_number() -> BnString { + unsafe { BnString::from_raw(BNGetSerialNumber()) } } pub fn is_license_validated() -> bool { unsafe { BNIsLicenseValidated() } } -pub fn licensed_user_email() -> string::BnString { - unsafe { string::BnString::from_raw(BNGetLicensedUserEmail()) } +pub fn licensed_user_email() -> BnString { + unsafe { BnString::from_raw(BNGetLicensedUserEmail()) } } pub fn license_count() -> i32 { @@ -698,12 +679,12 @@ pub fn set_license(license: S) { unsafe { BNSetLicense(license_slice.as_ptr() as *const c_char) } } -pub fn product() -> string::BnString { - unsafe { string::BnString::from_raw(BNGetProduct()) } +pub fn product() -> BnString { + unsafe { BnString::from_raw(BNGetProduct()) } } -pub fn product_type() -> string::BnString { - unsafe { string::BnString::from_raw(BNGetProductType()) } +pub fn product_type() -> BnString { + unsafe { BnString::from_raw(BNGetProductType()) } } pub fn license_expiration_time() -> std::time::SystemTime { diff --git a/rust/src/llil/expression.rs b/rust/src/llil/expression.rs index b6968dbf4..7f24641bf 100644 --- a/rust/src/llil/expression.rs +++ b/rust/src/llil/expression.rs @@ -194,9 +194,10 @@ where _ => { #[cfg(debug_assertions)] { - error!( + log::error!( "Got unexpected operation {:?} in value expr at 0x{:x}", - op.operation, op.address + op.operation, + op.address ); } diff --git a/rust/src/llil/lifting.rs b/rust/src/llil/lifting.rs index 2861a5a81..261f8880d 100644 --- a/rust/src/llil/lifting.rs +++ b/rust/src/llil/lifting.rs @@ -440,7 +440,7 @@ macro_rules! prim_int_lifter { }; if !is_safe { - error!("il @ {:x} attempted to lift constant 0x{:x} as {} byte expr (won't fit!)", + log::error!("il @ {:x} attempted to lift constant 0x{:x} as {} byte expr (won't fit!)", il.current_address(), val, size); } } @@ -556,7 +556,7 @@ impl<'a, A: 'a + Architecture> LiftableWithSize<'a, A> { if let Some(expr_size) = expr.info().size() { if expr_size != _size { - warn!( + log::warn!( "il @ {:x} attempted to lift {} byte expression as {} bytes", il.current_address(), expr_size, @@ -699,7 +699,7 @@ where use binaryninjacore_sys::BNLowLevelILOperation::{LLIL_UNIMPL, LLIL_UNIMPL_MEM}; if expr.size != _size && ![LLIL_UNIMPL, LLIL_UNIMPL_MEM].contains(&expr.op) { - warn!( + log::warn!( "il @ {:x} attempted to lift {} byte expression builder as {} bytes", il.current_address(), expr.size, diff --git a/rust/src/llil/operation.rs b/rust/src/llil/operation.rs index b0fd31435..be84a54cf 100644 --- a/rust/src/llil/operation.rs +++ b/rust/src/llil/operation.rs @@ -129,7 +129,7 @@ where .register_from_id(RegisterId(raw_id)) .map(Register::ArchReg) .unwrap_or_else(|| { - error!( + log::error!( "got garbage register from LLIL_SET_REG @ 0x{:x}", self.op.address ); @@ -168,7 +168,7 @@ where .register_from_id(RegisterId(raw_id)) .map(Register::ArchReg) .unwrap_or_else(|| { - error!( + log::error!( "got garbage register from LLIL_SET_REG_SPLIT @ 0x{:x}", self.op.address ); @@ -189,7 +189,7 @@ where .register_from_id(RegisterId(raw_id)) .map(Register::ArchReg) .unwrap_or_else(|| { - error!( + log::error!( "got garbage register from LLIL_SET_REG_SPLIT @ 0x{:x}", self.op.address ); @@ -282,7 +282,7 @@ where .register_from_id(RegisterId(raw_id)) .map(Register::ArchReg) .unwrap_or_else(|| { - error!( + log::error!( "got garbage register from LLIL_REG @ 0x{:x}", self.op.address ); @@ -317,7 +317,7 @@ where .register_from_id(RegisterId(raw_id)) .map(Register::ArchReg) .unwrap_or_else(|| { - error!( + log::error!( "got garbage register from LLIL_REG @ 0x{:x}", self.op.address ); @@ -338,7 +338,7 @@ where .register_from_id(RegisterId(raw_id)) .map(Register::ArchReg) .unwrap_or_else(|| { - error!( + log::error!( "got garbage register from LLIL_REG @ 0x{:x}", self.op.address ); @@ -583,9 +583,11 @@ where }; if !is_safe { - error!( + log::error!( "il expr @ {:x} contains constant 0x{:x} as {} byte value (doesn't fit!)", - self.op.address, self.op.operands[0], self.op.size + self.op.address, + self.op.operands[0], + self.op.size ); } } @@ -625,9 +627,11 @@ where }; if !is_safe { - error!( + log::error!( "il expr @ {:x} contains extern 0x{:x} as {} byte value (doesn't fit!)", - self.op.address, self.op.operands[0], self.op.size + self.op.address, + self.op.operands[0], + self.op.size ); } } diff --git a/rust/src/mainthread.rs b/rust/src/mainthread.rs new file mode 100644 index 000000000..f7e1466c1 --- /dev/null +++ b/rust/src/mainthread.rs @@ -0,0 +1,129 @@ +use crate::rc::{Ref, RefCountable}; +use binaryninjacore_sys::{ + BNExecuteMainThreadAction, BNExecuteOnMainThread, BNExecuteOnMainThreadAndWait, + BNFreeMainThreadAction, BNIsMainThreadActionDone, BNMainThreadAction, BNMainThreadCallbacks, + BNNewMainThreadActionReference, BNRegisterMainThread, BNWaitForMainThreadAction, +}; +use std::ffi::c_void; + +pub struct MainThreadActionExecutor { + func: Box, +} + +impl MainThreadActionExecutor { + unsafe extern "C" fn cb_execute(ctx: *mut c_void) { + let f: Box = Box::from_raw(ctx as *mut MainThreadActionExecutor); + f.execute(); + } + + pub fn execute(&self) { + (self.func)(); + } +} + +/// Execute passed function on the main thread. Returns `None` if already running on the main thread. +/// +/// When not running in headless this will block the UI. +pub fn execute_on_main_thread(f: F) -> Option> { + let boxed_executor = Box::new(MainThreadActionExecutor { func: Box::new(f) }); + let raw_executor = Box::into_raw(boxed_executor); + let raw_action = unsafe { + BNExecuteOnMainThread( + raw_executor as *mut c_void, + Some(MainThreadActionExecutor::cb_execute), + ) + }; + match raw_action.is_null() { + false => Some(MainThreadAction::ref_from_raw(raw_action)), + true => None, + } +} + +/// Execute passed function on the main thread and wait until the function is finished. +/// +/// When not running in headless this will block the UI. +pub fn execute_on_main_thread_and_wait(f: F) { + let boxed_executor = Box::new(MainThreadActionExecutor { func: Box::new(f) }); + let raw_executor = Box::into_raw(boxed_executor); + unsafe { + BNExecuteOnMainThreadAndWait( + raw_executor as *mut c_void, + Some(MainThreadActionExecutor::cb_execute), + ) + }; +} + +/// The trait required for receiving main thread actions +pub trait MainThreadHandler: Sized { + fn add_action(&self, _view: Ref); + + unsafe extern "C" fn cb_add_action(ctxt: *mut c_void, action: *mut BNMainThreadAction) { + ffi_wrap!("MainThread::add_action", { + let main_thread = &*(ctxt as *mut Self); + let action = MainThreadAction::ref_from_raw(action); + main_thread.add_action(action); + }) + } + + /// Register the main thread handler. Leaking [`Self`] in the process. + /// + /// NOTE: This MUST be called from **within** the main thread. + fn register(self) { + // NOTE: We leak self here. + let raw = Box::into_raw(Box::new(self)); + let mut callbacks = BNMainThreadCallbacks { + context: raw as *mut c_void, + addAction: Some(Self::cb_add_action), + }; + unsafe { BNRegisterMainThread(&mut callbacks) }; + } +} + +pub struct MainThreadAction { + pub handle: *mut BNMainThreadAction, +} + +impl MainThreadAction { + pub fn from_raw(handle: *mut BNMainThreadAction) -> Self { + assert!(!handle.is_null()); + Self { handle } + } + + pub fn ref_from_raw(handle: *mut BNMainThreadAction) -> Ref { + unsafe { Ref::new(Self::from_raw(handle)) } + } + + pub fn execute(&self) { + unsafe { BNExecuteMainThreadAction(self.handle) } + } + + pub fn is_done(&self) -> bool { + unsafe { BNIsMainThreadActionDone(self.handle) } + } + + pub fn wait(&self) { + unsafe { BNWaitForMainThreadAction(self.handle) } + } +} + +impl ToOwned for MainThreadAction { + type Owned = Ref; + + fn to_owned(&self) -> Self::Owned { + unsafe { RefCountable::inc_ref(self) } + } +} + +unsafe impl RefCountable for MainThreadAction { + unsafe fn inc_ref(action: &Self) -> Ref { + Ref::new(Self { + handle: BNNewMainThreadActionReference(action.handle), + }) + } + + unsafe fn dec_ref(action: &Self) { + BNFreeMainThreadAction(action.handle); + } +} + +unsafe impl Send for MainThreadAction {} diff --git a/rust/src/platform.rs b/rust/src/platform.rs index 84723c1e7..9fc27d54b 100644 --- a/rust/src/platform.rs +++ b/rust/src/platform.rs @@ -168,10 +168,15 @@ impl Platform { unsafe { CoreArchitecture::from_raw(BNGetPlatformArchitecture(self.handle)) } } - pub fn get_type_libraries_by_name(&self, name: &QualifiedName) -> Array { + pub fn get_type_libraries_by_name(&self, name: T) -> Array { let mut count = 0; + let name = name.into_bytes_with_nul(); let result = unsafe { - BNGetPlatformTypeLibrariesByName(self.handle, &name.0 as *const _ as *mut _, &mut count) + BNGetPlatformTypeLibrariesByName( + self.handle, + name.as_ref().as_ptr() as *mut _, + &mut count, + ) }; assert!(!result.is_null()); unsafe { Array::new(result, count, ()) } @@ -271,9 +276,9 @@ pub trait TypeParser { #[derive(Clone, Default)] pub struct TypeParserResult { - pub types: HashMap>, - pub variables: HashMap>, - pub functions: HashMap>, + pub types: HashMap>, + pub variables: HashMap>, + pub functions: HashMap>, } impl TypeParser for Platform { @@ -284,17 +289,8 @@ impl TypeParser for Platform { include_directories: &[P], auto_type_source: S, ) -> Result { - let mut result = BNTypeParserResult { - functionCount: 0, - typeCount: 0, - variableCount: 0, - functions: ptr::null_mut(), - types: ptr::null_mut(), - variables: ptr::null_mut(), - }; - + let mut result = BNTypeParserResult::default(); let mut type_parser_result = TypeParserResult::default(); - let mut error_string: *mut raw::c_char = ptr::null_mut(); let src = source.into_bytes_with_nul(); @@ -331,24 +327,24 @@ impl TypeParser for Platform { } for i in slice::from_raw_parts(result.types, result.typeCount) { - let name = QualifiedName(i.name); + let name = QualifiedName::from(i.name); type_parser_result .types - .insert(name.string(), Type::ref_from_raw(i.type_)); + .insert(name, Type::ref_from_raw(i.type_)); } for i in slice::from_raw_parts(result.functions, result.functionCount) { - let name = QualifiedName(i.name); + let name = QualifiedName::from(i.name); type_parser_result .functions - .insert(name.string(), Type::ref_from_raw(i.type_)); + .insert(name, Type::ref_from_raw(i.type_)); } for i in slice::from_raw_parts(result.variables, result.variableCount) { - let name = QualifiedName(i.name); + let name = QualifiedName::from(i.name); type_parser_result .variables - .insert(name.string(), Type::ref_from_raw(i.type_)); + .insert(name, Type::ref_from_raw(i.type_)); } } diff --git a/rust/src/segment.rs b/rust/src/segment.rs index d27c5957a..0347490af 100644 --- a/rust/src/segment.rs +++ b/rust/src/segment.rs @@ -111,8 +111,14 @@ pub struct Segment { } impl Segment { - pub(crate) unsafe fn from_raw(raw: *mut BNSegment) -> Self { - Self { handle: raw } + pub(crate) unsafe fn from_raw(handle: *mut BNSegment) -> Self { + assert!(!handle.is_null()); + Self { handle } + } + + pub(crate) unsafe fn ref_from_raw(handle: *mut BNSegment) -> Ref { + assert!(!handle.is_null()); + Ref::new(Self { handle }) } /// You need to create a segment builder, customize that segment, then add it to a binary view: diff --git a/rust/src/string.rs b/rust/src/string.rs index 2ede15ef0..25e0eee3b 100644 --- a/rust/src/string.rs +++ b/rust/src/string.rs @@ -14,15 +14,15 @@ //! String wrappers for core-owned strings and strings being passed to the core +use crate::rc::*; +use crate::types::QualifiedName; use std::borrow::Cow; use std::ffi::{c_char, CStr, CString}; use std::fmt; use std::hash::{Hash, Hasher}; use std::mem; use std::ops::Deref; - -use crate::rc::*; -use crate::types::QualifiedName; +use std::path::{Path, PathBuf}; // TODO: Remove or refactor this. pub(crate) fn raw_to_string(ptr: *const c_char) -> Option { @@ -33,6 +33,17 @@ pub(crate) fn raw_to_string(ptr: *const c_char) -> Option { } } +// TODO: Make this pass in an iterator over something more generic... +pub(crate) fn strings_to_string_list(strings: &[String]) -> *mut *mut c_char { + use binaryninjacore_sys::BNAllocStringList; + let bn_str_list = strings + .iter() + .map(|s| BnString::new(s.as_str())) + .collect::>(); + let mut raw_str_list = bn_str_list.iter().map(|s| s.as_ptr()).collect::>(); + unsafe { BNAllocStringList(raw_str_list.as_mut_ptr(), raw_str_list.len()) } +} + /// Is the equivalent of `core::ffi::CString` but using the alloc and free from `binaryninjacore-sys`. #[repr(transparent)] pub struct BnString { @@ -250,7 +261,23 @@ unsafe impl BnStrCompatible for &QualifiedName { type Result = Vec; fn into_bytes_with_nul(self) -> Self::Result { - self.string().into_bytes_with_nul() + self.to_string().into_bytes_with_nul() + } +} + +unsafe impl BnStrCompatible for PathBuf { + type Result = Vec; + + fn into_bytes_with_nul(self) -> Self::Result { + self.as_path().into_bytes_with_nul() + } +} + +unsafe impl BnStrCompatible for &Path { + type Result = Vec; + + fn into_bytes_with_nul(self) -> Self::Result { + self.to_string_lossy().into_bytes_with_nul() } } diff --git a/rust/src/templatesimplifier.rs b/rust/src/templatesimplifier.rs index 5959167da..2958161bb 100644 --- a/rust/src/templatesimplifier.rs +++ b/rust/src/templatesimplifier.rs @@ -12,7 +12,7 @@ pub fn simplify_str_to_str(input: S) -> BnString { pub fn simplify_str_to_fqn(input: S, simplify: bool) -> QualifiedName { let name = input.into_bytes_with_nul(); unsafe { - QualifiedName(BNRustSimplifyStrToFQN( + QualifiedName::from(BNRustSimplifyStrToFQN( name.as_ref().as_ptr() as *mut _, simplify, )) diff --git a/rust/src/typearchive.rs b/rust/src/typearchive.rs index 7767a172c..4a0640fb4 100644 --- a/rust/src/typearchive.rs +++ b/rust/src/typearchive.rs @@ -1,6 +1,8 @@ -use core::{ffi, mem, ptr}; - use binaryninjacore_sys::*; +use std::ffi::{c_char, c_void, CStr}; +use std::mem::ManuallyDrop; +use std::path::{Path, PathBuf}; +use std::ptr::NonNull; use crate::databuffer::DataBuffer; use crate::metadata::Metadata; @@ -14,7 +16,7 @@ use crate::types::{QualifiedName, QualifiedNameAndType, QualifiedNameTypeAndId, /// a history of previous versions of types is stored in snapshots in the archive. #[repr(transparent)] pub struct TypeArchive { - handle: ptr::NonNull, + handle: NonNull, } impl Drop for TypeArchive { @@ -25,9 +27,7 @@ impl Drop for TypeArchive { impl Clone for TypeArchive { fn clone(&self) -> Self { - unsafe { - Self::from_raw(ptr::NonNull::new(BNNewTypeArchiveReference(self.as_raw())).unwrap()) - } + unsafe { Self::from_raw(NonNull::new(BNNewTypeArchiveReference(self.as_raw())).unwrap()) } } } @@ -46,19 +46,20 @@ impl core::hash::Hash for TypeArchive { impl core::fmt::Debug for TypeArchive { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - let path = self.path().map(|x| x.to_string()); - f.debug_struct("TypeArchive").field("path", &path).finish() + f.debug_struct("TypeArchive") + .field("path", &self.path()) + .finish() } } impl TypeArchive { - pub(crate) unsafe fn from_raw(handle: ptr::NonNull) -> Self { + pub(crate) unsafe fn from_raw(handle: NonNull) -> Self { Self { handle } } pub(crate) unsafe fn ref_from_raw(handle: &*mut BNTypeArchive) -> &Self { assert!(!handle.is_null()); - mem::transmute(handle) + std::mem::transmute(handle) } #[allow(clippy::mut_from_ref)] @@ -67,53 +68,59 @@ impl TypeArchive { } /// Open the Type Archive at the given path, if it exists. - pub fn open(path: S) -> Option { - let path = path.into_bytes_with_nul(); - let handle = unsafe { BNOpenTypeArchive(path.as_ref().as_ptr() as *const ffi::c_char) }; - ptr::NonNull::new(handle).map(|handle| unsafe { TypeArchive::from_raw(handle) }) + pub fn open(path: impl AsRef) -> Option { + let raw_path = path.as_ref().into_bytes_with_nul(); + let handle = unsafe { BNOpenTypeArchive(raw_path.as_ptr() as *const c_char) }; + NonNull::new(handle).map(|handle| unsafe { TypeArchive::from_raw(handle) }) } - /// Create a Type Archive at the given path, returning None if it could not be created. - pub fn create(path: S, platform: &Platform) -> Option { - let path = path.into_bytes_with_nul(); - let handle = unsafe { - BNCreateTypeArchive( - path.as_ref().as_ptr() as *const ffi::c_char, - platform.handle, - ) - }; - ptr::NonNull::new(handle).map(|handle| unsafe { TypeArchive::from_raw(handle) }) + /// Create a Type Archive at the given path, returning `None` if it could not be created. + /// + /// If the file has already been created and is not a valid type archive this will return `None`. + pub fn create(path: impl AsRef, platform: &Platform) -> Option { + let raw_path = path.as_ref().into_bytes_with_nul(); + let handle = + unsafe { BNCreateTypeArchive(raw_path.as_ptr() as *const c_char, platform.handle) }; + NonNull::new(handle).map(|handle| unsafe { TypeArchive::from_raw(handle) }) } /// Create a Type Archive at the given path and id, returning None if it could not be created. - pub fn create_with_id( - path: P, + /// + /// If the file has already been created and is not a valid type archive this will return `None`. + pub fn create_with_id( + path: impl AsRef, id: I, platform: &Platform, ) -> Option { - let path = path.into_bytes_with_nul(); + let raw_path = path.as_ref().into_bytes_with_nul(); let id = id.into_bytes_with_nul(); let handle = unsafe { BNCreateTypeArchiveWithId( - path.as_ref().as_ptr() as *const ffi::c_char, + raw_path.as_ptr() as *const c_char, platform.handle, - id.as_ref().as_ptr() as *const ffi::c_char, + id.as_ref().as_ptr() as *const c_char, ) }; - ptr::NonNull::new(handle).map(|handle| unsafe { TypeArchive::from_raw(handle) }) + NonNull::new(handle).map(|handle| unsafe { TypeArchive::from_raw(handle) }) } /// Get a reference to the Type Archive with the known id, if one exists. pub fn lookup_by_id(id: S) -> Option { let id = id.into_bytes_with_nul(); - let handle = unsafe { BNLookupTypeArchiveById(id.as_ref().as_ptr() as *const ffi::c_char) }; - ptr::NonNull::new(handle).map(|handle| unsafe { TypeArchive::from_raw(handle) }) + let handle = unsafe { BNLookupTypeArchiveById(id.as_ref().as_ptr() as *const c_char) }; + NonNull::new(handle).map(|handle| unsafe { TypeArchive::from_raw(handle) }) } /// Get the path to the Type Archive's file - pub fn path(&self) -> Option { + pub fn path(&self) -> Option { let result = unsafe { BNGetTypeArchivePath(self.as_raw()) }; - (!result.is_null()).then(|| unsafe { BnString::from_raw(result) }) + match result.is_null() { + false => { + let bn_res = unsafe { BnString::from_raw(result) }; + Some(PathBuf::from(bn_res.to_string())) + } + true => None, + } } /// Get the guid for a Type Archive @@ -140,10 +147,7 @@ impl TypeArchive { pub fn set_current_snapshot_id(&self, id: S) { let id = id.into_bytes_with_nul(); unsafe { - BNSetTypeArchiveCurrentSnapshot( - self.as_raw(), - id.as_ref().as_ptr() as *const ffi::c_char, - ) + BNSetTypeArchiveCurrentSnapshot(self.as_raw(), id.as_ref().as_ptr() as *const c_char) } } @@ -165,7 +169,7 @@ impl TypeArchive { let result = unsafe { BNGetTypeArchiveSnapshotParentIds( self.as_raw(), - snapshot.as_ref().as_ptr() as *const ffi::c_char, + snapshot.as_ref().as_ptr() as *const c_char, &mut count, ) }; @@ -182,7 +186,7 @@ impl TypeArchive { let result = unsafe { BNGetTypeArchiveSnapshotChildIds( self.as_raw(), - snapshot.as_ref().as_ptr() as *const ffi::c_char, + snapshot.as_ref().as_ptr() as *const c_char, &mut count, ) }; @@ -206,7 +210,7 @@ impl TypeArchive { /// * `new_types` - Names and definitions of new types pub fn add_types(&self, new_types: &[QualifiedNameAndType]) { // SAFETY BNQualifiedNameAndType and QualifiedNameAndType are transparent - let new_types_raw: &[BNQualifiedNameAndType] = unsafe { mem::transmute(new_types) }; + let new_types_raw: &[BNQualifiedNameAndType] = unsafe { std::mem::transmute(new_types) }; let result = unsafe { BNAddTypeArchiveTypes(self.as_raw(), new_types_raw.as_ptr(), new_types.len()) }; @@ -221,7 +225,7 @@ impl TypeArchive { let id = self .get_type_id(old_name, self.current_snapshot_id()) .unwrap(); - self.rename_type_by_id(id, new_name.name()) + self.rename_type_by_id(id, &new_name.name) } /// Change the name of an existing type in the type archive. @@ -230,11 +234,12 @@ impl TypeArchive { /// * `new_name` - New type name pub fn rename_type_by_id(&self, id: S, new_name: &QualifiedName) { let id = id.into_bytes_with_nul(); + let raw_name = BNQualifiedName::from(new_name); let result = unsafe { BNRenameTypeArchiveType( self.as_raw(), - id.as_ref().as_ptr() as *const ffi::c_char, - &new_name.0, + id.as_ref().as_ptr() as *const c_char, + &raw_name, ) }; assert!(result); @@ -244,7 +249,7 @@ impl TypeArchive { pub fn delete_type(&self, name: &QualifiedName) { let id = self.get_type_id(name, self.current_snapshot_id()); let Some(id) = id else { - panic!("Unknown type {}", name.string()) + panic!("Unknown type {}", name) }; self.delete_type_by_id(id); } @@ -253,7 +258,7 @@ impl TypeArchive { pub fn delete_type_by_id(&self, id: S) { let id = id.into_bytes_with_nul(); let result = unsafe { - BNDeleteTypeArchiveType(self.as_raw(), id.as_ref().as_ptr() as *const ffi::c_char) + BNDeleteTypeArchiveType(self.as_raw(), id.as_ref().as_ptr() as *const c_char) }; assert!(result); } @@ -268,11 +273,12 @@ impl TypeArchive { snapshot: S, ) -> Option> { let snapshot = snapshot.into_bytes_with_nul(); + let raw_name = BNQualifiedName::from(name); let result = unsafe { BNGetTypeArchiveTypeByName( self.as_raw(), - &name.0, - snapshot.as_ref().as_ptr() as *const ffi::c_char, + &raw_name, + snapshot.as_ref().as_ptr() as *const c_char, ) }; (!result.is_null()).then(|| unsafe { Type::ref_from_raw(result) }) @@ -292,8 +298,8 @@ impl TypeArchive { let result = unsafe { BNGetTypeArchiveTypeById( self.as_raw(), - id.as_ref().as_ptr() as *const ffi::c_char, - snapshot.as_ref().as_ptr() as *const ffi::c_char, + id.as_ref().as_ptr() as *const c_char, + snapshot.as_ref().as_ptr() as *const c_char, ) }; (!result.is_null()).then(|| unsafe { Type::ref_from_raw(result) }) @@ -313,11 +319,11 @@ impl TypeArchive { let result = unsafe { BNGetTypeArchiveTypeName( self.as_raw(), - id.as_ref().as_ptr() as *const ffi::c_char, - snapshot.as_ref().as_ptr() as *const ffi::c_char, + id.as_ref().as_ptr() as *const c_char, + snapshot.as_ref().as_ptr() as *const c_char, ) }; - QualifiedName(result) + QualifiedName::from(result) } /// Retrieve a type's id by its name @@ -330,11 +336,12 @@ impl TypeArchive { snapshot: S, ) -> Option { let snapshot = snapshot.into_bytes_with_nul(); + let raw_name = BNQualifiedName::from(name); let result = unsafe { BNGetTypeArchiveTypeId( self.as_raw(), - &name.0, - snapshot.as_ref().as_ptr() as *const ffi::c_char, + &raw_name, + snapshot.as_ref().as_ptr() as *const c_char, ) }; (!result.is_null()).then(|| unsafe { BnString::from_raw(result) }) @@ -352,7 +359,7 @@ impl TypeArchive { let result = unsafe { BNGetTypeArchiveTypes( self.as_raw(), - snapshot.as_ref().as_ptr() as *const ffi::c_char, + snapshot.as_ref().as_ptr() as *const c_char, &mut count, ) }; @@ -369,7 +376,7 @@ impl TypeArchive { let result = unsafe { BNGetTypeArchiveTypeIds( self.as_raw(), - snapshot.as_ref().as_ptr() as *const ffi::c_char, + snapshot.as_ref().as_ptr() as *const c_char, &mut count, ) }; @@ -386,7 +393,7 @@ impl TypeArchive { let result = unsafe { BNGetTypeArchiveTypeNames( self.as_raw(), - snapshot.as_ref().as_ptr() as *const ffi::c_char, + snapshot.as_ref().as_ptr() as *const c_char, &mut count, ) }; @@ -403,12 +410,12 @@ impl TypeArchive { ) -> (Array, Array) { let snapshot = snapshot.into_bytes_with_nul(); let mut count = 0; - let mut names = ptr::null_mut(); - let mut ids = ptr::null_mut(); + let mut names = std::ptr::null_mut(); + let mut ids = std::ptr::null_mut(); let result = unsafe { BNGetTypeArchiveTypeNamesAndIds( self.as_raw(), - snapshot.as_ref().as_ptr() as *const ffi::c_char, + snapshot.as_ref().as_ptr() as *const c_char, &mut names, &mut ids, &mut count, @@ -435,8 +442,8 @@ impl TypeArchive { let result = unsafe { BNGetTypeArchiveOutgoingDirectTypeReferences( self.as_raw(), - id.as_ref().as_ptr() as *const ffi::c_char, - snapshot.as_ref().as_ptr() as *const ffi::c_char, + id.as_ref().as_ptr() as *const c_char, + snapshot.as_ref().as_ptr() as *const c_char, &mut count, ) }; @@ -459,8 +466,8 @@ impl TypeArchive { let result = unsafe { BNGetTypeArchiveOutgoingRecursiveTypeReferences( self.as_raw(), - id.as_ref().as_ptr() as *const ffi::c_char, - snapshot.as_ref().as_ptr() as *const ffi::c_char, + id.as_ref().as_ptr() as *const c_char, + snapshot.as_ref().as_ptr() as *const c_char, &mut count, ) }; @@ -483,8 +490,8 @@ impl TypeArchive { let result = unsafe { BNGetTypeArchiveIncomingDirectTypeReferences( self.as_raw(), - id.as_ref().as_ptr() as *const ffi::c_char, - snapshot.as_ref().as_ptr() as *const ffi::c_char, + id.as_ref().as_ptr() as *const c_char, + snapshot.as_ref().as_ptr() as *const c_char, &mut count, ) }; @@ -507,8 +514,8 @@ impl TypeArchive { let result = unsafe { BNGetTypeArchiveIncomingRecursiveTypeReferences( self.as_raw(), - id.as_ref().as_ptr() as *const ffi::c_char, - snapshot.as_ref().as_ptr() as *const ffi::c_char, + id.as_ref().as_ptr() as *const c_char, + snapshot.as_ref().as_ptr() as *const c_char, &mut count, ) }; @@ -520,7 +527,7 @@ impl TypeArchive { pub fn query_metadata(&self, key: S) -> Option> { let key = key.into_bytes_with_nul(); let result = unsafe { - BNTypeArchiveQueryMetadata(self.as_raw(), key.as_ref().as_ptr() as *const ffi::c_char) + BNTypeArchiveQueryMetadata(self.as_raw(), key.as_ref().as_ptr() as *const c_char) }; (!result.is_null()).then(|| unsafe { Metadata::ref_from_raw(result) }) } @@ -534,7 +541,7 @@ impl TypeArchive { let result = unsafe { BNTypeArchiveStoreMetadata( self.as_raw(), - key.as_ref().as_ptr() as *const ffi::c_char, + key.as_ref().as_ptr() as *const c_char, md.handle, ) }; @@ -545,7 +552,7 @@ impl TypeArchive { pub fn remove_metadata(&self, key: S) -> bool { let key = key.into_bytes_with_nul(); unsafe { - BNTypeArchiveRemoveMetadata(self.as_raw(), key.as_ref().as_ptr() as *const ffi::c_char) + BNTypeArchiveRemoveMetadata(self.as_raw(), key.as_ref().as_ptr() as *const c_char) } } @@ -555,7 +562,7 @@ impl TypeArchive { let result = unsafe { BNTypeArchiveSerializeSnapshot( self.as_raw(), - snapshot.as_ref().as_ptr() as *const ffi::c_char, + snapshot.as_ref().as_ptr() as *const c_char, ) }; assert!(!result.is_null()); @@ -577,7 +584,7 @@ impl TypeArchive { // SAFETY free on [TypeArchiveCallbackHandle::Drop] let callback = Box::leak(Box::new(callback)); let mut notification = BNTypeArchiveNotification { - context: callback as *mut T as *mut ffi::c_void, + context: callback as *mut T as *mut c_void, typeAdded: Some(cb_type_added::), typeUpdated: Some(cb_type_updated::), typeRenamed: Some(cb_type_renamed::), @@ -618,13 +625,12 @@ impl TypeArchive { /// any open file handles pub fn close(self) { unsafe { BNCloseTypeArchive(self.as_raw()) } - // NOTE self must be dropped after, don't make it `&self` } /// Determine if `file` is a Type Archive pub fn is_type_archive(file: P) -> bool { let file = file.into_bytes_with_nul(); - unsafe { BNIsTypeArchive(file.as_ref().as_ptr() as *const ffi::c_char) } + unsafe { BNIsTypeArchive(file.as_ref().as_ptr() as *const c_char) } } // TODO implement TypeContainer @@ -632,7 +638,7 @@ impl TypeArchive { ///// at the current snapshot in the archive. //pub fn type_container(&self) -> TypeContainer { // let result = unsafe { BNGetTypeArchiveTypeContainer(self.as_raw()) }; - // unsafe { TypeContainer::from_raw(ptr::NonNull::new(result).unwrap()) } + // unsafe { TypeContainer::from_raw(NonNull::new(result).unwrap()) } //} /// Do some function in a transaction making a new snapshot whose id is passed to func. If func throws, @@ -648,21 +654,21 @@ impl TypeArchive { F: FnMut(&str) -> bool, { unsafe extern "C" fn cb_callback bool>( - ctxt: *mut ffi::c_void, - id: *const ffi::c_char, + ctxt: *mut c_void, + id: *const c_char, ) -> bool { let fun: &mut F = &mut *(ctxt as *mut F); - fun(&ffi::CStr::from_ptr(id).to_string_lossy()) + fun(&CStr::from_ptr(id).to_string_lossy()) } - // SAFETY BnString and `*const ffi::c_char` are transparent - let parents_raw = parents.as_ptr() as *const *const ffi::c_char; + // SAFETY BnString and `*const c_char` are transparent + let parents_raw = parents.as_ptr() as *const *const c_char; let result = unsafe { BNTypeArchiveNewSnapshotTransaction( self.as_raw(), Some(cb_callback::), - &mut function as *mut F as *mut ffi::c_void, + &mut function as *mut F as *mut c_void, parents_raw, parents.len(), ) @@ -699,7 +705,7 @@ impl TypeArchive { MK: BnStrCompatible, { unsafe extern "C" fn cb_callback bool>( - ctxt: *mut ffi::c_void, + ctxt: *mut c_void, progress: usize, total: usize, ) -> bool { @@ -714,21 +720,21 @@ impl TypeArchive { .into_iter() .map(|(k, v)| (BnString::new(k), BnString::new(v))) .unzip(); - // SAFETY BnString and `*const ffi::c_char` are transparent - let merge_keys_raw = merge_keys.as_ptr() as *const *const ffi::c_char; - let merge_values_raw = merge_values.as_ptr() as *const *const ffi::c_char; + // SAFETY BnString and `*const c_char` are transparent + let merge_keys_raw = merge_keys.as_ptr() as *const *const c_char; + let merge_values_raw = merge_values.as_ptr() as *const *const c_char; - let mut conflicts_errors = ptr::null_mut(); + let mut conflicts_errors = std::ptr::null_mut(); let mut conflicts_errors_count = 0; - let mut result = ptr::null_mut(); + let mut result = std::ptr::null_mut(); let success = unsafe { BNTypeArchiveMergeSnapshots( self.as_raw(), - base_snapshot.as_ref().as_ptr() as *const ffi::c_char, - first_snapshot.as_ref().as_ptr() as *const ffi::c_char, - second_snapshot.as_ref().as_ptr() as *const ffi::c_char, + base_snapshot.as_ref().as_ptr() as *const c_char, + first_snapshot.as_ref().as_ptr() as *const c_char, + second_snapshot.as_ref().as_ptr() as *const c_char, merge_keys_raw, merge_values_raw, merge_keys.len(), @@ -736,7 +742,7 @@ impl TypeArchive { &mut conflicts_errors_count, &mut result, Some(cb_callback::

), - (&mut progress) as *mut P as *mut ffi::c_void, + (&mut progress) as *mut P as *mut c_void, ) }; if success { @@ -773,7 +779,7 @@ pub struct TypeArchiveCallbackHandle { impl Drop for TypeArchiveCallbackHandle { fn drop(&mut self) { let mut notification = BNTypeArchiveNotification { - context: self.callback as *mut ffi::c_void, + context: self.callback as *mut c_void, typeAdded: Some(cb_type_added::), typeUpdated: Some(cb_type_updated::), typeRenamed: Some(cb_type_renamed::), @@ -892,7 +898,7 @@ unsafe extern "C" fn cb_type_added( let ctxt: &mut T = &mut *(ctxt as *mut T); ctxt.type_added( unsafe { TypeArchive::ref_from_raw(&archive) }, - unsafe { ffi::CStr::from_ptr(id).to_string_lossy().as_ref() }, + unsafe { CStr::from_ptr(id).to_string_lossy().as_ref() }, &Type { handle: definition }, ) } @@ -906,7 +912,7 @@ unsafe extern "C" fn cb_type_updated( let ctxt: &mut T = &mut *(ctxt as *mut T); ctxt.type_updated( unsafe { TypeArchive::ref_from_raw(&archive) }, - unsafe { ffi::CStr::from_ptr(id).to_string_lossy().as_ref() }, + unsafe { CStr::from_ptr(id).to_string_lossy().as_ref() }, &Type { handle: old_definition, }, @@ -923,11 +929,11 @@ unsafe extern "C" fn cb_type_renamed( new_name: *const BNQualifiedName, ) { let ctxt: &mut T = &mut *(ctxt as *mut T); - let old_name = mem::ManuallyDrop::new(QualifiedName(*old_name)); - let new_name = mem::ManuallyDrop::new(QualifiedName(*new_name)); + let old_name = ManuallyDrop::new(QualifiedName::from(*old_name)); + let new_name = ManuallyDrop::new(QualifiedName::from(*new_name)); ctxt.type_renamed( unsafe { TypeArchive::ref_from_raw(&archive) }, - unsafe { ffi::CStr::from_ptr(id).to_string_lossy().as_ref() }, + unsafe { CStr::from_ptr(id).to_string_lossy().as_ref() }, &old_name, &new_name, ) @@ -941,7 +947,7 @@ unsafe extern "C" fn cb_type_deleted( let ctxt: &mut T = &mut *(ctxt as *mut T); ctxt.type_deleted( unsafe { TypeArchive::ref_from_raw(&archive) }, - unsafe { ffi::CStr::from_ptr(id).to_string_lossy().as_ref() }, + unsafe { CStr::from_ptr(id).to_string_lossy().as_ref() }, &Type { handle: definition }, ) } diff --git a/rust/src/typelibrary.rs b/rust/src/typelibrary.rs index 54fff11d9..4561877d9 100644 --- a/rust/src/typelibrary.rs +++ b/rust/src/typelibrary.rs @@ -273,9 +273,8 @@ impl TypeLibrary { /// `export_object_to_library `, which will automatically pull in /// all referenced types and record additional dependencies as needed. pub fn add_named_object(&self, name: &QualifiedName, type_: &Type) { - unsafe { - BNAddTypeLibraryNamedObject(self.as_raw(), &name.0 as *const _ as *mut _, type_.handle) - } + let mut raw_name = BNQualifiedName::from(name); + unsafe { BNAddTypeLibraryNamedObject(self.as_raw(), &mut raw_name, type_.handle) } } /// Directly inserts a named object into the type library's object store. @@ -285,10 +284,9 @@ impl TypeLibrary { /// To add types and objects from an existing BinaryView, it is recommended to use /// `export_type_to_library `, which will automatically pull in /// all referenced types and record additional dependencies as needed. - pub fn add_named_type(&self, name: &QualifiedNameAndType, type_: &Type) { - unsafe { - BNAddTypeLibraryNamedType(self.as_raw(), &name.0 as *const _ as *mut _, type_.handle) - } + pub fn add_named_type(&self, name: &QualifiedName, type_: &Type) { + let mut raw_name = BNQualifiedName::from(name); + unsafe { BNAddTypeLibraryNamedType(self.as_raw(), &mut raw_name, type_.handle) } } /// Manually flag NamedTypeReferences to the given QualifiedName as originating from another source @@ -301,10 +299,11 @@ impl TypeLibrary { /// pub fn add_type_source(&self, name: &QualifiedName, source: S) { let source = source.into_bytes_with_nul(); + let mut raw_name = BNQualifiedName::from(name); unsafe { BNAddTypeLibraryNamedTypeSource( self.as_raw(), - &name.0 as *const _ as *mut _, + &mut raw_name, source.as_ref().as_ptr() as *const ffi::c_char, ) } @@ -314,8 +313,8 @@ impl TypeLibrary { /// attempting to extract types from a library into a BinaryView, consider using /// `import_library_object ` instead. pub fn get_named_object(&self, name: &QualifiedName) -> Option> { - let t = - unsafe { BNGetTypeLibraryNamedObject(self.as_raw(), &name.0 as *const _ as *mut _) }; + let mut raw_name = BNQualifiedName::from(name); + let t = unsafe { BNGetTypeLibraryNamedObject(self.as_raw(), &mut raw_name) }; (!t.is_null()).then(|| unsafe { Type::ref_from_raw(t) }) } @@ -323,7 +322,8 @@ impl TypeLibrary { /// attempting to extract types from a library into a BinaryView, consider using /// `import_library_type ` instead. pub fn get_named_type(&self, name: &QualifiedName) -> Option> { - let t = unsafe { BNGetTypeLibraryNamedType(self.as_raw(), &name.0 as *const _ as *mut _) }; + let mut raw_name = BNQualifiedName::from(name); + let t = unsafe { BNGetTypeLibraryNamedType(self.as_raw(), &mut raw_name) }; (!t.is_null()).then(|| unsafe { Type::ref_from_raw(t) }) } diff --git a/rust/src/types.rs b/rust/src/types.rs index 3d2e14251..a18a12ee3 100644 --- a/rust/src/types.rs +++ b/rust/src/types.rs @@ -27,13 +27,12 @@ use crate::{ }; use crate::confidence::{Conf, MAX_CONFIDENCE, MIN_CONFIDENCE}; -use crate::string::raw_to_string; +use crate::string::{raw_to_string, strings_to_string_list}; use crate::variable::{Variable, VariableSourceType}; use std::num::NonZeroUsize; use std::{ - borrow::Cow, collections::HashSet, - ffi::{c_char, CStr}, + ffi::CStr, fmt::{Debug, Display, Formatter}, hash::{Hash, Hasher}, iter::IntoIterator, @@ -329,13 +328,15 @@ impl TypeBuilder { } } - pub fn named_type_from_type(name: S, t: &Type) -> Self { - let mut name = QualifiedName::from(name); + pub fn named_type_from_type>(name: T, t: &Type) -> Self { + let mut raw_name = BNQualifiedName::from(name.as_ref()); + // TODO: This cant be right... + let id = BnString::new(""); unsafe { Self::from_raw(BNCreateNamedTypeReferenceBuilderFromTypeAndId( - BnString::new("").as_ptr() as *mut _, - &mut name.0, + id.as_ptr() as *mut _, + &mut raw_name, t.handle, )) } @@ -705,15 +706,15 @@ impl Type { } } - pub fn named_type_from_type(name: S, t: &Type) -> Ref { - let mut name = QualifiedName::from(name); + pub fn named_type_from_type>(name: T, t: &Type) -> Ref { + let mut raw_name = BNQualifiedName::from(name.as_ref()); // TODO: No id is present for this call? let id = BnString::new(""); unsafe { Self::ref_from_raw(BNCreateNamedTypeReferenceFromTypeAndId( id.as_ptr(), - &mut name.0, + &mut raw_name, t.handle, )) } @@ -898,9 +899,9 @@ impl Type { } } - pub fn generate_auto_demangled_type_id(name: S) -> BnString { - let mut name = QualifiedName::from(name); - unsafe { BnString::from_raw(BNGenerateAutoDemangledTypeId(&mut name.0)) } + pub fn generate_auto_demangled_type_id>(name: T) -> BnString { + let mut raw_name = BNQualifiedName::from(name.as_ref()); + unsafe { BnString::from_raw(BNGenerateAutoDemangledTypeId(&mut raw_name)) } } } @@ -1581,8 +1582,7 @@ impl Structure { unsafe { BNGetStructureType(self.handle) } } - // TODO: Omit `Option` and pass empty vec? Actually the core will only nullptr on failed allocation, use debug_assert. - pub fn members(&self) -> Option> { + pub fn members(&self) -> Vec { unsafe { let mut count = 0; let members_raw_ptr: *mut BNStructureMember = @@ -1593,15 +1593,15 @@ impl Structure { let members_raw = std::slice::from_raw_parts(members_raw_ptr, count); let members = members_raw.iter().map(Into::into).collect(); BNFreeStructureMemberList(members_raw_ptr, count); - Some(members) + members } - true => None, + true => vec![], } } } // TODO: Omit `Option` and pass empty vec? - pub fn base_structures(&self) -> Option> { + pub fn base_structures(&self) -> Vec { let mut count = 0; let bases_raw_ptr = unsafe { BNGetBaseStructuresForStructure(self.handle, &mut count) }; match bases_raw_ptr.is_null() { @@ -1609,9 +1609,9 @@ impl Structure { let bases_raw = unsafe { std::slice::from_raw_parts(bases_raw_ptr, count) }; let bases = bases_raw.iter().copied().map(Into::into).collect(); unsafe { BNFreeBaseStructureList(bases_raw_ptr, count) }; - Some(bases) + bases } - true => None, + true => vec![], } } @@ -1822,12 +1822,13 @@ impl NamedTypeReference { /// You should not assign type ids yourself, that is the responsibility of the BinaryView /// implementation after your types have been added. Just make sure the names match up and /// the core will do the id stuff for you. - pub fn new(type_class: NamedTypeReferenceClass, mut name: QualifiedName) -> Ref { + pub fn new(type_class: NamedTypeReferenceClass, name: QualifiedName) -> Ref { + let mut raw_name = BNQualifiedName::from(name); unsafe { Self::ref_from_raw(BNCreateNamedType( type_class, std::ptr::null() as *const _, - &mut name.0, + &mut raw_name, )) } } @@ -1840,22 +1841,23 @@ impl NamedTypeReference { pub fn new_with_id( type_class: NamedTypeReferenceClass, type_id: S, - mut name: QualifiedName, + name: QualifiedName, ) -> Ref { let type_id = type_id.into_bytes_with_nul(); + let mut raw_name = BNQualifiedName::from(name); unsafe { Self::ref_from_raw(BNCreateNamedType( type_class, type_id.as_ref().as_ptr() as _, - &mut name.0, + &mut raw_name, )) } } pub fn name(&self) -> QualifiedName { - let named_ref: BNQualifiedName = unsafe { BNGetTypeReferenceName(self.handle) }; - QualifiedName(named_ref) + let raw_name = unsafe { BNGetTypeReferenceName(self.handle) }; + QualifiedName::from(raw_name) } pub fn id(&self) -> BnString { @@ -1867,28 +1869,26 @@ impl NamedTypeReference { } fn target_helper(&self, bv: &BinaryView, visited: &mut HashSet) -> Option> { - // TODO : This is a clippy bug (#10088, I think); remove after we upgrade past 2022-12-12 - #[allow(clippy::manual_filter)] - if let Some(t) = bv.get_type_by_id(self.id()) { - if t.type_class() != TypeClass::NamedTypeReferenceClass { - Some(t) - } else { - let t = t.get_named_type_reference().unwrap(); - if visited.contains(&t.id()) { - error!("Can't get target for recursively defined type!"); - None - } else { - visited.insert(t.id()); - t.target_helper(bv, visited) + let ty = bv.get_type_by_id(self.id())?; + match ty.type_class() { + TypeClass::NamedTypeReferenceClass => { + // Recurse into the NTR type until we get the target type. + let ntr = ty.get_named_type_reference().unwrap(); + match visited.insert(ntr.id()) { + true => ntr.target_helper(bv, visited), + false => { + log::error!("Can't get target for recursively defined type!"); + None + } } } - } else { - None + // Found target type + _ => Some(ty), } } + /// Type referenced by this [`NamedTypeReference`]. pub fn target(&self, bv: &BinaryView) -> Option> { - //! Returns the type referenced by this named type reference self.target_helper(bv, &mut HashSet::new()) } } @@ -1917,126 +1917,123 @@ impl Debug for NamedTypeReference { } } -#[repr(transparent)] -pub struct QualifiedName(pub(crate) BNQualifiedName); +// TODO: Document usage, specifically how to make a qualified name and why it exists. +#[derive(Debug, Clone, Hash, PartialEq, Eq)] +pub struct QualifiedName { + pub items: Vec, + // TODO: Make this Option where default is "::". + pub seperator: String, +} impl QualifiedName { - // TODO : I think this is bad - pub fn string(&self) -> String { - unsafe { - std::slice::from_raw_parts(self.0.name, self.0.nameCount) - .iter() - .map(|c| CStr::from_ptr(*c).to_string_lossy()) - .collect::>() - .join("::") - } - } - - pub fn join(&self) -> Cow { - let join: *mut c_char = self.0.join; - unsafe { CStr::from_ptr(join) }.to_string_lossy() - } - - pub fn strings(&self) -> Vec> { - let names: *mut *mut c_char = self.0.name; - unsafe { - std::slice::from_raw_parts(names, self.0.nameCount) - .iter() - .map(|name| CStr::from_ptr(*name).to_string_lossy()) - .collect::>() - } - } - - pub fn len(&self) -> usize { - self.0.nameCount - } - - pub fn is_empty(&self) -> bool { - self.0.nameCount == 0 + pub fn new(items: Vec, seperator: String) -> Self { + Self { items, seperator } } } -impl From for QualifiedName { - fn from(name: S) -> Self { - let join = BnString::new("::"); - let name = name.into_bytes_with_nul(); - let mut list = vec![name.as_ref().as_ptr() as *const _]; - - QualifiedName(BNQualifiedName { - name: unsafe { BNAllocStringList(list.as_mut_ptr(), 1) }, - join: join.into_raw(), - nameCount: 1, - }) +impl From for QualifiedName { + fn from(value: BNQualifiedName) -> Self { + // TODO: This could be improved... + let raw_names = unsafe { std::slice::from_raw_parts(value.name, value.nameCount) }; + let items = raw_names + .iter() + .filter_map(|&raw_name| raw_to_string(raw_name as *const _)) + .collect(); + let seperator = raw_to_string(value.join).unwrap(); + unsafe { BNFreeStringList(value.name, value.nameCount) }; + unsafe { BNFreeString(value.join) }; + Self { items, seperator } } } -impl From> for QualifiedName { - fn from(names: Vec) -> Self { - let join = BnString::new("::"); - let names = names - .into_iter() - .map(|n| n.into_bytes_with_nul()) - .collect::>(); - let mut list = names +impl From<&BNQualifiedName> for QualifiedName { + fn from(value: &BNQualifiedName) -> Self { + // TODO: This could be improved... + // Taking this as a ref, we should not free the underlying data... + let raw_names = unsafe { std::slice::from_raw_parts(value.name, value.nameCount) }; + let items = raw_names .iter() - .map(|n| n.as_ref().as_ptr() as *const _) - .collect::>(); - - QualifiedName(BNQualifiedName { - name: unsafe { BNAllocStringList(list.as_mut_ptr(), list.len()) }, - join: join.into_raw(), - nameCount: list.len(), - }) + .filter_map(|&raw_name| raw_to_string(raw_name as *const _)) + .collect(); + let seperator = raw_to_string(value.join).unwrap(); + Self { items, seperator } } } -impl Clone for QualifiedName { - fn clone(&self) -> Self { - let strings = self.strings(); - let name = Self::from(strings.iter().collect::>>()); - name +impl From for BNQualifiedName { + fn from(value: QualifiedName) -> Self { + let bn_join = BnString::new(value.seperator); + Self { + // TODO: Check this to make sure this isnt leaking... + name: strings_to_string_list(&value.items), + join: bn_join.into_raw(), + nameCount: value.items.len(), + } } } -impl Hash for QualifiedName { - fn hash(&self, state: &mut H) { - self.join().hash(state); - self.strings().hash(state); +impl From<&QualifiedName> for BNQualifiedName { + fn from(value: &QualifiedName) -> Self { + let bn_join = BnString::new(&value.seperator); + Self { + // TODO: Check this to make sure this isnt leaking... + name: strings_to_string_list(&value.items), + join: bn_join.into_raw(), + nameCount: value.items.len(), + } } } -impl Debug for QualifiedName { - fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { - write!(f, "{}", self.string()) +impl From<&str> for QualifiedName { + fn from(value: &str) -> Self { + Self { + items: vec![value.to_string()], + // TODO: See comment in struct def. + seperator: String::from("::"), + } } } -impl Display for QualifiedName { - fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { - write!(f, "{}", self.string()) +impl From for QualifiedName { + fn from(value: String) -> Self { + Self { + items: vec![value], + // TODO: See comment in struct def. + seperator: String::from("::"), + } } } -impl PartialEq for QualifiedName { - fn eq(&self, other: &Self) -> bool { - self.strings() == other.strings() +impl From> for QualifiedName { + fn from(value: Vec) -> Self { + Self { + items: value, + // TODO: See comment in struct def. + seperator: String::from("::"), + } } } -impl Eq for QualifiedName {} +impl From> for QualifiedName { + fn from(value: Vec<&str>) -> Self { + value + .iter() + .map(ToString::to_string) + .collect::>() + .into() + } +} -impl Drop for QualifiedName { - fn drop(&mut self) { - unsafe { - BNFreeQualifiedName(&mut self.0); - } +impl Display for QualifiedName { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + write!(f, "{}", self.items.join(&self.seperator)) } } impl CoreArrayProvider for QualifiedName { type Raw = BNQualifiedName; type Context = (); - type Wrapped<'a> = &'a QualifiedName; + type Wrapped<'a> = Self; } unsafe impl CoreArrayProviderInner for QualifiedName { @@ -2045,36 +2042,79 @@ unsafe impl CoreArrayProviderInner for QualifiedName { } unsafe fn wrap_raw<'a>(raw: &'a Self::Raw, _context: &'a Self::Context) -> Self::Wrapped<'a> { - std::mem::transmute(raw) + Self::from(raw) } } -#[repr(transparent)] -pub struct QualifiedNameAndType(pub(crate) BNQualifiedNameAndType); +#[derive(Debug, Clone, Hash, PartialEq, Eq)] +pub struct QualifiedNameAndType { + pub name: QualifiedName, + pub ty: Ref, +} impl QualifiedNameAndType { - pub fn name(&self) -> &QualifiedName { - unsafe { std::mem::transmute(&self.0.name) } + pub fn new(name: QualifiedName, ty: Ref) -> Self { + Self { name, ty } } +} - pub fn type_object(&self) -> Guard { - unsafe { Guard::new(Type::from_raw(self.0.type_), self) } +impl From for QualifiedNameAndType { + fn from(value: BNQualifiedNameAndType) -> Self { + Self { + name: QualifiedName::from(value.name), + ty: unsafe { Type::ref_from_raw(value.type_) }, + } } } -impl Debug for QualifiedNameAndType { - fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { - f.debug_struct("QualifiedNameAndType") - .field("name", self.name()) - .field("type", self.type_object().as_ref()) - .finish() +impl From<&BNQualifiedNameAndType> for QualifiedNameAndType { + fn from(value: &BNQualifiedNameAndType) -> Self { + Self { + name: QualifiedName::from(&value.name), + ty: unsafe { Type::from_raw(value.type_).to_owned() }, + } } } -impl Drop for QualifiedNameAndType { - fn drop(&mut self) { - unsafe { - BNFreeQualifiedNameAndType(&mut self.0); +impl From for BNQualifiedNameAndType { + fn from(value: QualifiedNameAndType) -> Self { + Self { + name: value.name.into(), + type_: value.ty.handle, + } + } +} + +impl From<&QualifiedNameAndType> for BNQualifiedNameAndType { + fn from(value: &QualifiedNameAndType) -> Self { + Self { + name: BNQualifiedName::from(&value.name), + type_: value.ty.handle, + } + } +} + +impl From<(T, Ref)> for QualifiedNameAndType +where + T: Into, +{ + fn from(value: (T, Ref)) -> Self { + Self { + name: value.0.into(), + ty: value.1, + } + } +} + +impl From<(T, &Type)> for QualifiedNameAndType +where + T: Into, +{ + fn from(value: (T, &Type)) -> Self { + let ty = value.1.to_owned(); + Self { + name: value.0.into(), + ty, } } } @@ -2082,49 +2122,53 @@ impl Drop for QualifiedNameAndType { impl CoreArrayProvider for QualifiedNameAndType { type Raw = BNQualifiedNameAndType; type Context = (); - type Wrapped<'a> = &'a QualifiedNameAndType; + type Wrapped<'a> = Self; } unsafe impl CoreArrayProviderInner for QualifiedNameAndType { unsafe fn free(raw: *mut Self::Raw, count: usize, _context: &Self::Context) { BNFreeTypeAndNameList(raw, count); } + unsafe fn wrap_raw<'a>(raw: &'a Self::Raw, _context: &'a Self::Context) -> Self::Wrapped<'a> { - std::mem::transmute(raw) + Self::from(raw) } } -#[repr(transparent)] -pub struct QualifiedNameTypeAndId(pub(crate) BNQualifiedNameTypeAndId); - -impl QualifiedNameTypeAndId { - pub fn name(&self) -> &QualifiedName { - unsafe { std::mem::transmute(&self.0.name) } - } - - pub fn id(&self) -> &str { - unsafe { CStr::from_ptr(self.0.id).to_str().unwrap() } - } +#[derive(Debug, Clone, Hash, PartialEq, Eq)] +pub struct QualifiedNameTypeAndId { + pub name: QualifiedName, + pub ty: Ref, + pub id: String, +} - pub fn ty(&self) -> Guard { - unsafe { Guard::new(Type::from_raw(self.0.type_), self) } +impl From for QualifiedNameTypeAndId { + fn from(value: BNQualifiedNameTypeAndId) -> Self { + Self { + name: QualifiedName::from(value.name), + ty: unsafe { Type::ref_from_raw(value.type_) }, + id: unsafe { BnString::from_raw(value.id) }.to_string(), + } } } -impl Debug for QualifiedNameTypeAndId { - fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { - f.debug_struct("QualifiedNameTypeAndId") - .field("name", self.name()) - .field("id", &self.id()) - .field("type", self.ty().as_ref()) - .finish() +impl From<&BNQualifiedNameTypeAndId> for QualifiedNameTypeAndId { + fn from(value: &BNQualifiedNameTypeAndId) -> Self { + Self { + name: QualifiedName::from(&value.name), + ty: unsafe { Type::from_raw(value.type_).to_owned() }, + id: raw_to_string(value.id as *mut _).unwrap(), + } } } -impl Drop for QualifiedNameTypeAndId { - fn drop(&mut self) { - unsafe { - BNFreeQualifiedNameTypeAndId(&mut self.0); +impl From for BNQualifiedNameTypeAndId { + fn from(value: QualifiedNameTypeAndId) -> Self { + let bn_id = BnString::new(value.id); + Self { + name: value.name.into(), + id: bn_id.into_raw(), + type_: value.ty.handle, } } } @@ -2132,7 +2176,7 @@ impl Drop for QualifiedNameTypeAndId { impl CoreArrayProvider for QualifiedNameTypeAndId { type Raw = BNQualifiedNameTypeAndId; type Context = (); - type Wrapped<'a> = &'a QualifiedNameTypeAndId; + type Wrapped<'a> = QualifiedNameTypeAndId; } unsafe impl CoreArrayProviderInner for QualifiedNameTypeAndId { @@ -2141,8 +2185,7 @@ unsafe impl CoreArrayProviderInner for QualifiedNameTypeAndId { } unsafe fn wrap_raw<'a>(raw: &'a Self::Raw, _context: &'a Self::Context) -> Self::Wrapped<'a> { - // TODO: Oh my god what is wrong with all of you people - std::mem::transmute(raw) + Self::from(raw) } } diff --git a/rust/tests/demangler.rs b/rust/tests/demangler.rs new file mode 100644 index 000000000..acaa0a87b --- /dev/null +++ b/rust/tests/demangler.rs @@ -0,0 +1,85 @@ +use binaryninja::architecture::CoreArchitecture; +use binaryninja::binaryview::BinaryView; +use binaryninja::demangle::{ + demangle_generic, demangle_gnu3, demangle_llvm, demangle_ms, CustomDemangler, Demangler, +}; +use binaryninja::headless::Session; +use binaryninja::rc::Ref; +use binaryninja::types::{QualifiedName, Type}; +use rstest::*; + +#[fixture] +#[once] +fn session() -> Session { + Session::new() +} + +#[rstest] +fn test_demangler_simple(_session: &Session) { + let placeholder_arch = CoreArchitecture::by_name("x86").expect("x86 exists"); + // Example LLVM-style mangled name + let llvm_mangled = "_Z3fooi"; // "foo(int)" in LLVM mangling + let llvm_demangled = demangle_llvm(llvm_mangled, true).unwrap(); + assert_eq!(llvm_demangled, vec!["foo(int)"]); + + // Example GNU-style mangled name + let gnu_mangled = "_Z3bari"; // "bar(int)" in GNU mangling + let (gnu_demangled_name, gnu_demangled_type) = + demangle_gnu3(&placeholder_arch, gnu_mangled, true).unwrap(); + assert_eq!(gnu_demangled_name, vec!["bar"]); + // TODO: We check the type display because other means include things such as confidence which is hard to get 1:1 + assert_eq!( + gnu_demangled_type.unwrap().to_string(), + "int32_t(int32_t)".to_string() + ); + + // Example MSVC-style mangled name + let msvc_mangled = "?baz@@YAHH@Z"; // "int __cdecl baz(int)" in MSVC mangling + let (msvc_demangled_name, msvc_demangled_type) = + demangle_ms(&placeholder_arch, msvc_mangled, true).unwrap(); + assert_eq!(msvc_demangled_name, vec!["baz"]); + // TODO: We check the type display because other means include things such as confidence which is hard to get 1:1 + assert_eq!( + msvc_demangled_type.unwrap().to_string(), + "int32_t __cdecl(int32_t)".to_string() + ); +} + +#[rstest] +fn test_custom_demangler(_session: &Session) { + struct TestDemangler; + + impl CustomDemangler for TestDemangler { + fn is_mangled_string(&self, name: &str) -> bool { + name == "test_name" || name == "test_name2" + } + + fn demangle( + &self, + _arch: &CoreArchitecture, + name: &str, + _view: Option>, + ) -> Option<(QualifiedName, Option>)> { + match name { + "test_name" => Some((QualifiedName::from(vec!["test_name"]), Some(Type::bool()))), + "test_name2" => Some((QualifiedName::from(vec!["test_name2", "aaa"]), None)), + _ => None, + } + } + } + + Demangler::register("Test", TestDemangler); + + let placeholder_arch = CoreArchitecture::by_name("x86_64").expect("x86_64 exists"); + + let demangled = demangle_generic(&placeholder_arch, "test_name", None, true).unwrap(); + assert_eq!( + demangled, + (QualifiedName::from(vec!["test_name"]), Some(Type::bool())) + ); + let demangled2 = demangle_generic(&placeholder_arch, "test_name2", None, true).unwrap(); + assert_eq!( + demangled2, + (QualifiedName::from(vec!["test_name2", "aaa"]), None) + ); +} diff --git a/rust/tests/mainthread.rs b/rust/tests/mainthread.rs new file mode 100644 index 000000000..1358ae131 --- /dev/null +++ b/rust/tests/mainthread.rs @@ -0,0 +1,29 @@ +use binaryninja::headless::Session; +use rstest::*; + +// TODO: Add a test for MainThreadHandler + +#[fixture] +#[once] +fn session() -> Session { + Session::new() +} + +#[rstest] +fn test_not_main_thread(_session: &Session) { + // We should never be the main thread. + assert!(!binaryninja::is_main_thread()) +} + +#[rstest] +fn test_main_thread_different(_session: &Session) { + let calling_thread = std::thread::current(); + binaryninja::mainthread::execute_on_main_thread(move || { + let main_thread = std::thread::current(); + assert_ne!( + calling_thread.id(), + main_thread.id(), + "Expected calling thread to be the different from the main thread" + ) + }); +} diff --git a/rust/tests/project.rs b/rust/tests/project.rs index 33be592d9..c3225d242 100644 --- a/rust/tests/project.rs +++ b/rust/tests/project.rs @@ -1,16 +1,25 @@ +use binaryninja::headless::Session; use binaryninja::metadata::Metadata; use binaryninja::project::Project; use binaryninja::rc::Ref; +use rstest::*; use std::time::SystemTime; +// TODO: We should use tempdir to manage the project directory. + +#[fixture] +#[once] +fn session() -> Session { + Session::new() +} + fn unique_project(name: &str) -> String { format!("{}/{}", std::env::temp_dir().to_str().unwrap(), name) } -#[test] -fn create_delete_empty() { +#[rstest] +fn create_delete_empty(_session: &Session) { use std::fs::canonicalize; - binaryninja::headless::init(); let project_name = "create_delete_empty_project"; let project_path = unique_project(project_name); @@ -35,14 +44,10 @@ fn create_delete_empty() { // delete the project std::fs::remove_dir_all(project_path).unwrap(); - - binaryninja::headless::shutdown(); } -#[test] -fn create_close_open_close() { - binaryninja::headless::init(); - +#[rstest] +fn create_close_open_close(_session: &Session) { let project_name = "create_close_open_close"; let project_path = unique_project(project_name); // create the project @@ -67,14 +72,10 @@ fn create_close_open_close() { // delete the project std::fs::remove_dir_all(project_path).unwrap(); - - binaryninja::headless::shutdown(); } -#[test] -fn modify_project() { - binaryninja::headless::init(); - +#[rstest] +fn modify_project(_session: &Session) { let project_name = "modify_project"; let project_path = unique_project(project_name); // create the project @@ -102,7 +103,7 @@ fn modify_project() { // create file that will be imported to the project let tmp_folder_1_name = format!( "tmp_folder_{}", - std::time::SystemTime::now() + SystemTime::now() .duration_since(std::time::UNIX_EPOCH) .unwrap() .as_millis() @@ -296,17 +297,26 @@ fn modify_project() { } // check files - #[rustfmt::skip] - let files = [ - ("file_1", &file_1_data[..], None, None), - ("file_2", &file_2_data[..], Some(file_2_id), None), - ("file_3", &file_3_data[..], None, None), - ("file_4", &file_4_data[..], Some(file_4_id), Some(file_4_time)), - ("file_5", &input_file_1_data[..], None, None), - ("file_6", &input_file_2_data[..], Some(file_6_id), Some(file_6_time)), - ("input_1", &input_file_1_data[..], None, None), - ("input_2", &input_file_2_data[..], None, None), - ]; + let files = [ + ("file_1", &file_1_data[..], None, None), + ("file_2", &file_2_data[..], Some(file_2_id), None), + ("file_3", &file_3_data[..], None, None), + ( + "file_4", + &file_4_data[..], + Some(file_4_id), + Some(file_4_time), + ), + ("file_5", &input_file_1_data[..], None, None), + ( + "file_6", + &input_file_2_data[..], + Some(file_6_id), + Some(file_6_time), + ), + ("input_1", &input_file_1_data[..], None, None), + ("input_2", &input_file_2_data[..], None, None), + ]; for file in project.files().unwrap().iter() { let found = files.iter().find(|f| file.name().as_str() == f.0).unwrap(); if let Some(id) = found.2 { @@ -337,6 +347,4 @@ fn modify_project() { std::fs::remove_dir_all(project_path).unwrap(); std::fs::remove_dir_all(tmp_folder_1).unwrap(); std::fs::remove_dir_all(tmp_folder_2).unwrap(); - - binaryninja::headless::shutdown(); } diff --git a/rust/tests/typearchive.rs b/rust/tests/typearchive.rs new file mode 100644 index 000000000..2a179f108 --- /dev/null +++ b/rust/tests/typearchive.rs @@ -0,0 +1,31 @@ +use binaryninja::binaryview::BinaryView; +use binaryninja::filemetadata::FileMetadata; +use binaryninja::headless::Session; +use binaryninja::platform::Platform; +use binaryninja::rc::Ref; +use binaryninja::typearchive::TypeArchive; +use rstest::*; + +#[fixture] +#[once] +fn session() -> Session { + Session::new() +} + +#[fixture] +#[once] +fn empty_view() -> Ref { + BinaryView::from_data(&FileMetadata::new(), &[]).expect("Failed to create view") +} + +#[rstest] +fn test_main_thread_different(_session: &Session) { + let placeholder_platform = Platform::by_name("x86_64").expect("Failed to get platform"); + + let temp_dir = tempfile::tempdir().unwrap(); + let type_archive_path = temp_dir.path().with_file_name("type_archive_0"); + let type_archive = TypeArchive::create(type_archive_path, &placeholder_platform).unwrap(); + println!("{:?}", type_archive); + // TODO: It seems that type archives have to be closed. + type_archive.close(); +} diff --git a/rust/tests/types.rs b/rust/tests/types.rs index dfe216431..856d8b21a 100644 --- a/rust/tests/types.rs +++ b/rust/tests/types.rs @@ -1,8 +1,15 @@ +use binaryninja::headless::Session; use binaryninja::types::Type; +use rstest::*; -#[test] -fn test_type_to_string() { - let _session = binaryninja::headless::Session::new(); +#[fixture] +#[once] +fn session() -> Session { + Session::new() +} + +#[rstest] +fn test_type_to_string(_session: &Session) { let test_type = Type::int(4, true); assert_eq!(test_type.to_string(), "int32_t".to_string()); }