// Copyright 2021-2026 Vector 35 Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. //! A [`Platform`] models the information related to the execution environment of the binary. use crate::{ architecture::{Architecture, CoreArchitecture}, calling_convention::CoreCallingConvention, rc::*, string::*, types::{ QualifiedName, QualifiedNameAndType, Type, TypeContainer, TypeLibrary, TypeParserError, TypeParserErrorSeverity, TypeParserResult, }, }; use binaryninjacore_sys::*; use std::fmt::Debug; use std::ptr::NonNull; use std::{borrow::Borrow, ffi, ptr}; /// A platform describes the target [`CoreArchitecture`] and platform-specific information such as /// the calling conventions and generic types (think `HRESULT` on Windows). #[derive(PartialEq, Eq, Hash)] pub struct Platform { pub(crate) handle: *mut BNPlatform, } unsafe impl Send for Platform {} unsafe impl Sync for Platform {} macro_rules! cc_func { ($get_name:ident, $get_api:ident, $set_name:ident, $set_api:ident) => { pub fn $get_name(&self) -> Option> { let arch = self.arch(); unsafe { let cc = $get_api(self.handle); if cc.is_null() { None } else { Some(CoreCallingConvention::ref_from_raw( cc, arch.as_ref().handle(), )) } } } pub fn $set_name(&self, cc: &CoreCallingConvention) { let arch = self.arch(); assert!( cc.arch_handle.borrow().as_ref().handle == arch.handle, "use of calling convention with non-matching Platform architecture!" ); unsafe { $set_api(self.handle, cc.handle); } } }; } impl Platform { pub unsafe fn from_raw(handle: *mut BNPlatform) -> Self { debug_assert!(!handle.is_null()); Self { handle } } pub(crate) unsafe fn ref_from_raw(handle: *mut BNPlatform) -> Ref { debug_assert!(!handle.is_null()); Ref::new(Self { handle }) } pub fn by_name(name: &str) -> Option> { let raw_name = name.to_cstr(); unsafe { let res = BNGetPlatformByName(raw_name.as_ptr()); if res.is_null() { None } else { Some(Self::ref_from_raw(res)) } } } pub fn list_all() -> Array { unsafe { let mut count = 0; let handles = BNGetPlatformList(&mut count); Array::new(handles, count, ()) } } pub fn list_by_arch(arch: &CoreArchitecture) -> Array { unsafe { let mut count = 0; let handles = BNGetPlatformListByArchitecture(arch.handle, &mut count); Array::new(handles, count, ()) } } pub fn list_by_os(name: &str) -> Array { let raw_name = name.to_cstr(); unsafe { let mut count = 0; let handles = BNGetPlatformListByOS(raw_name.as_ptr(), &mut count); Array::new(handles, count, ()) } } pub fn list_by_os_and_arch(name: &str, arch: &CoreArchitecture) -> Array { let raw_name = name.to_cstr(); unsafe { let mut count = 0; let handles = BNGetPlatformListByOSAndArchitecture(raw_name.as_ptr(), arch.handle, &mut count); Array::new(handles, count, ()) } } pub fn list_available_os() -> Array { unsafe { let mut count = 0; let list = BNGetPlatformOSList(&mut count); Array::new(list, count, ()) } } pub fn new(arch: &A, name: &str) -> Ref { let name = name.to_cstr(); unsafe { let handle = BNCreatePlatform(arch.as_ref().handle, name.as_ptr()); assert!(!handle.is_null()); Ref::new(Self { handle }) } } pub fn name(&self) -> String { unsafe { let raw_name = BNGetPlatformName(self.handle); BnString::into_string(raw_name) } } pub fn arch(&self) -> CoreArchitecture { unsafe { CoreArchitecture::from_raw(BNGetPlatformArchitecture(self.handle)) } } /// Get the address size of the platform, this is typically the same as the architecture's address size, /// but some platforms like Linux x86_64 x32 ABI have differing address sizes from architecture. pub fn address_size(&self) -> usize { unsafe { BNGetPlatformAddressSize(self.handle) } } pub fn type_container(&self) -> TypeContainer { let type_container_ptr = NonNull::new(unsafe { BNGetPlatformTypeContainer(self.handle) }); // NOTE: I have no idea how this isn't a UAF, see the note in `TypeContainer::from_raw` // TODO: We are cloning here for platforms but we dont need to do this for [BinaryView::type_container] // TODO: Why does this require that we, construct a TypeContainer, duplicate the type container, then drop the original. unsafe { TypeContainer::from_raw(type_container_ptr.unwrap()) } } /// Get all the type libraries that have been registered with the name. /// /// NOTE: This is a list because libraries can have `alternate_names`, use [`Platform::get_type_library_by_name`] /// if you want to get _the_ type library with that name, skipping alternate names. pub fn get_type_libraries_by_name(&self, name: &str) -> Array { let mut count = 0; let name = name.to_cstr(); let result = unsafe { BNGetPlatformTypeLibrariesByName(self.handle, name.as_ptr(), &mut count) }; assert!(!result.is_null()); unsafe { Array::new(result, count, ()) } } /// Get the type library with the given name. /// /// NOTE: This finds the first type library that has the given name, skipping alternate names. pub fn get_type_library_by_name(&self, name: &str) -> Option> { let libraries = self.get_type_libraries_by_name(name); libraries .iter() .find(|lib| lib.name() == name) .map(|lib| lib.to_owned()) } pub fn register_os(&self, os: &str) { let os = os.to_cstr(); unsafe { BNRegisterPlatform(os.as_ptr(), self.handle); } } cc_func!( get_default_calling_convention, BNGetPlatformDefaultCallingConvention, set_default_calling_convention, BNRegisterPlatformDefaultCallingConvention ); cc_func!( get_cdecl_calling_convention, BNGetPlatformCdeclCallingConvention, set_cdecl_calling_convention, BNRegisterPlatformCdeclCallingConvention ); cc_func!( get_stdcall_calling_convention, BNGetPlatformStdcallCallingConvention, set_stdcall_calling_convention, BNRegisterPlatformStdcallCallingConvention ); cc_func!( get_fastcall_calling_convention, BNGetPlatformFastcallCallingConvention, set_fastcall_calling_convention, BNRegisterPlatformFastcallCallingConvention ); cc_func!( get_syscall_convention, BNGetPlatformSystemCallConvention, set_syscall_convention, BNSetPlatformSystemCallConvention ); pub fn calling_conventions(&self) -> Array { unsafe { let mut count = 0; let handles = BNGetPlatformCallingConventions(self.handle, &mut count); Array::new(handles, count, self.arch()) } } pub fn types(&self) -> Array { unsafe { let mut count = 0; let handles = BNGetPlatformTypes(self.handle, &mut count); Array::new(handles, count, ()) } } pub fn variables(&self) -> Array { unsafe { let mut count = 0; let handles = BNGetPlatformVariables(self.handle, &mut count); Array::new(handles, count, ()) } } pub fn functions(&self) -> Array { unsafe { let mut count = 0; let handles = BNGetPlatformFunctions(self.handle, &mut count); Array::new(handles, count, ()) } } pub fn function_by_name>( &self, name: T, exact_match: bool, ) -> Option> { let mut raw_name = QualifiedName::into_raw(name.into()); unsafe { let type_handle = BNGetPlatformFunctionByName(self.handle, &mut raw_name, exact_match); QualifiedName::free_raw(raw_name); if type_handle.is_null() { return None; } Some(Type::ref_from_raw(type_handle)) } } // TODO: system_calls // TODO: add a helper function to define a system call (platform function with a specific type) // TODO: Documentation, specifically how this differs from the TypeParser impl pub fn preprocess_source( &self, source: &str, file_name: &str, include_dirs: &[BnString], ) -> Result { let source_cstr = BnString::new(source); let file_name_cstr = BnString::new(file_name); let mut result = ptr::null_mut(); let mut error_string = ptr::null_mut(); let success = unsafe { BNPreprocessSource( source_cstr.as_ptr(), file_name_cstr.as_ptr(), &mut result, &mut error_string, include_dirs.as_ptr() as *mut *const ffi::c_char, include_dirs.len(), ) }; if success { assert!(!result.is_null()); Ok(unsafe { BnString::from_raw(result) }) } else { assert!(!error_string.is_null()); Err(TypeParserError::new( TypeParserErrorSeverity::FatalSeverity, unsafe { BnString::into_string(error_string) }, file_name.to_string(), 0, 0, )) } } // TODO: Documentation, specifically how this differs from the TypeParser impl pub fn parse_types_from_source( &self, src: &str, filename: &str, include_dirs: &[BnString], auto_type_source: &str, ) -> Result { let source_cstr = BnString::new(src); let file_name_cstr = BnString::new(filename); let auto_type_source = BnString::new(auto_type_source); let mut raw_result = BNTypeParserResult::default(); let mut error_string = ptr::null_mut(); let success = unsafe { BNParseTypesFromSource( self.handle, source_cstr.as_ptr(), file_name_cstr.as_ptr(), &mut raw_result, &mut error_string, include_dirs.as_ptr() as *mut *const ffi::c_char, include_dirs.len(), auto_type_source.as_ptr(), ) }; if success { let result = TypeParserResult::from_raw(&raw_result); // NOTE: This is safe because the core allocated the TypeParserResult TypeParserResult::free_raw(raw_result); Ok(result) } else { assert!(!error_string.is_null()); Err(TypeParserError::new( TypeParserErrorSeverity::FatalSeverity, unsafe { BnString::into_string(error_string) }, filename.to_string(), 0, 0, )) } } // TODO: Documentation, specifically how this differs from the TypeParser impl pub fn parse_types_from_source_file( &self, filename: &str, include_dirs: &[BnString], auto_type_source: &str, ) -> Result { let file_name_cstr = BnString::new(filename); let auto_type_source = BnString::new(auto_type_source); let mut raw_result = BNTypeParserResult::default(); let mut error_string = ptr::null_mut(); let success = unsafe { BNParseTypesFromSourceFile( self.handle, file_name_cstr.as_ptr(), &mut raw_result, &mut error_string, include_dirs.as_ptr() as *mut *const ffi::c_char, include_dirs.len(), auto_type_source.as_ptr(), ) }; if success { let result = TypeParserResult::from_raw(&raw_result); // NOTE: This is safe because the core allocated the TypeParserResult TypeParserResult::free_raw(raw_result); Ok(result) } else { assert!(!error_string.is_null()); Err(TypeParserError::new( TypeParserErrorSeverity::FatalSeverity, unsafe { BnString::into_string(error_string) }, filename.to_string(), 0, 0, )) } } } impl Debug for Platform { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { f.debug_struct("Platform") .field("name", &self.name()) .field("arch", &self.arch().name()) .finish() } } impl ToOwned for Platform { type Owned = Ref; fn to_owned(&self) -> Self::Owned { unsafe { RefCountable::inc_ref(self) } } } unsafe impl RefCountable for Platform { unsafe fn inc_ref(handle: &Self) -> Ref { Ref::new(Self { handle: BNNewPlatformReference(handle.handle), }) } unsafe fn dec_ref(handle: &Self) { BNFreePlatform(handle.handle); } } impl CoreArrayProvider for Platform { type Raw = *mut BNPlatform; type Context = (); type Wrapped<'a> = Guard<'a, Platform>; } unsafe impl CoreArrayProviderInner for Platform { unsafe fn free(raw: *mut Self::Raw, count: usize, _context: &Self::Context) { BNFreePlatformList(raw, count); } unsafe fn wrap_raw<'a>(raw: &'a Self::Raw, context: &'a Self::Context) -> Self::Wrapped<'a> { debug_assert!(!raw.is_null()); Guard::new(Self::from_raw(*raw), context) } }