use std::{borrow::Borrow, collections::HashMap, os::raw, path::Path, ptr, slice};
use binaryninjacore_sys::*;
use crate::{
architecture::{Architecture, CoreArchitecture},
callingconvention::CallingConvention,
rc::*,
string::*,
typelibrary::TypeLibrary,
types::{QualifiedName, QualifiedNameAndType, Type},
};
#[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<Ref<CallingConvention<CoreArchitecture>>> {
let arch = self.arch();
unsafe {
let cc = $get_api(self.handle);
if cc.is_null() {
None
} else {
Some(CallingConvention::ref_from_raw(cc, arch))
}
}
}
pub fn $set_name<A: Architecture>(&self, cc: &CallingConvention<A>) {
let arch = self.arch();
assert!(
cc.arch_handle.borrow().as_ref().0 == arch.0,
"use of calling convention with non-matching Platform architecture!"
);
unsafe {
$set_api(self.handle, cc.handle);
}
}
};
}
impl Platform {
pub(crate) unsafe fn ref_from_raw(handle: *mut BNPlatform) -> Ref<Self> {
debug_assert!(!handle.is_null());
Ref::new(Self { handle })
}
pub fn by_name<S: BnStrCompatible>(name: S) -> Option<Ref<Self>> {
let raw_name = name.into_bytes_with_nul();
unsafe {
let res = BNGetPlatformByName(raw_name.as_ref().as_ptr() as *mut _);
if res.is_null() {
None
} else {
Some(Ref::new(Self { handle: res }))
}
}
}
pub fn list_all() -> Array<Platform> {
unsafe {
let mut count = 0;
let handles = BNGetPlatformList(&mut count);
Array::new(handles, count, ())
}
}
pub fn list_by_arch(arch: &CoreArchitecture) -> Array<Platform> {
unsafe {
let mut count = 0;
let handles = BNGetPlatformListByArchitecture(arch.0, &mut count);
Array::new(handles, count, ())
}
}
pub fn list_by_os<S: BnStrCompatible>(name: S) -> Array<Platform> {
let raw_name = name.into_bytes_with_nul();
unsafe {
let mut count = 0;
let handles = BNGetPlatformListByOS(raw_name.as_ref().as_ptr() as *mut _, &mut count);
Array::new(handles, count, ())
}
}
pub fn list_by_os_and_arch<S: BnStrCompatible>(
name: S,
arch: &CoreArchitecture,
) -> Array<Platform> {
let raw_name = name.into_bytes_with_nul();
unsafe {
let mut count = 0;
let handles = BNGetPlatformListByOSAndArchitecture(
raw_name.as_ref().as_ptr() as *mut _,
arch.0,
&mut count,
);
Array::new(handles, count, ())
}
}
pub fn list_available_os() -> Array<BnString> {
unsafe {
let mut count = 0;
let list = BNGetPlatformOSList(&mut count);
Array::new(list, count, ())
}
}
pub fn new<A: Architecture, S: BnStrCompatible>(arch: &A, name: S) -> Ref<Self> {
let name = name.into_bytes_with_nul();
unsafe {
let handle = BNCreatePlatform(arch.as_ref().0, name.as_ref().as_ptr() as *mut _);
assert!(!handle.is_null());
Ref::new(Self { handle })
}
}
pub fn name(&self) -> BnString {
unsafe {
let raw_name = BNGetPlatformName(self.handle);
BnString::from_raw(raw_name)
}
}
pub fn arch(&self) -> CoreArchitecture {
unsafe { CoreArchitecture::from_raw(BNGetPlatformArchitecture(self.handle)) }
}
pub fn get_type_libraries_by_name(&self, name: &QualifiedName) -> Array<TypeLibrary> {
let mut count = 0;
let result = unsafe {
BNGetPlatformTypeLibrariesByName(self.handle, &name.0 as *const _ as *mut _, &mut count)
};
assert!(!result.is_null());
unsafe { Array::new(result, count, ()) }
}
pub fn register_os<S: BnStrCompatible>(&self, os: S) {
let os = os.into_bytes_with_nul();
unsafe {
BNRegisterPlatform(os.as_ref().as_ptr() as *mut _, 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<CallingConvention<CoreArchitecture>> {
unsafe {
let mut count = 0;
let handles = BNGetPlatformCallingConventions(self.handle, &mut count);
Array::new(handles, count, self.arch())
}
}
pub fn types(&self) -> Array<QualifiedNameAndType> {
unsafe {
let mut count = 0;
let handles = BNGetPlatformTypes(self.handle, &mut count);
Array::new(handles, count, ())
}
}
pub fn variables(&self) -> Array<QualifiedNameAndType> {
unsafe {
let mut count = 0;
let handles = BNGetPlatformVariables(self.handle, &mut count);
Array::new(handles, count, ())
}
}
pub fn functions(&self) -> Array<QualifiedNameAndType> {
unsafe {
let mut count = 0;
let handles = BNGetPlatformFunctions(self.handle, &mut count);
Array::new(handles, count, ())
}
}
}
pub trait TypeParser {
fn parse_types_from_source<S: BnStrCompatible, P: AsRef<Path>>(
&self,
_source: S,
_filename: S,
_include_directories: &[P],
_auto_type_source: S,
) -> Result<TypeParserResult, String> {
Err(String::new())
}
}
#[derive(Clone, Default)]
pub struct TypeParserResult {
pub types: HashMap<String, Ref<Type>>,
pub variables: HashMap<String, Ref<Type>>,
pub functions: HashMap<String, Ref<Type>>,
}
impl TypeParser for Platform {
fn parse_types_from_source<S: BnStrCompatible, P: AsRef<Path>>(
&self,
source: S,
filename: S,
include_directories: &[P],
auto_type_source: S,
) -> Result<TypeParserResult, String> {
let mut result = BNTypeParserResult {
functionCount: 0,
typeCount: 0,
variableCount: 0,
functions: ptr::null_mut(),
types: ptr::null_mut(),
variables: ptr::null_mut(),
};
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();
let filename = filename.into_bytes_with_nul();
let auto_type_source = auto_type_source.into_bytes_with_nul();
let mut include_dirs = vec![];
for dir in include_directories.iter() {
let d = dir
.as_ref()
.to_string_lossy()
.to_string()
.into_bytes_with_nul();
include_dirs.push(d.as_ptr() as _);
}
unsafe {
let success = BNParseTypesFromSource(
self.handle,
src.as_ref().as_ptr() as _,
filename.as_ref().as_ptr() as _,
&mut result,
&mut error_string,
include_dirs.as_mut_ptr(),
include_dirs.len(),
auto_type_source.as_ref().as_ptr() as _,
);
let error_msg = BnString::from_raw(error_string);
if !success {
return Err(error_msg.to_string());
}
for i in slice::from_raw_parts(result.types, result.typeCount) {
let name = QualifiedName(i.name);
type_parser_result
.types
.insert(name.string(), Type::ref_from_raw(i.type_));
}
for i in slice::from_raw_parts(result.functions, result.functionCount) {
let name = QualifiedName(i.name);
type_parser_result
.functions
.insert(name.string(), Type::ref_from_raw(i.type_));
}
for i in slice::from_raw_parts(result.variables, result.variableCount) {
let name = QualifiedName(i.name);
type_parser_result
.variables
.insert(name.string(), Type::ref_from_raw(i.type_));
}
}
Ok(type_parser_result)
}
}
impl ToOwned for Platform {
type Owned = Ref<Self>;
fn to_owned(&self) -> Self::Owned {
unsafe { RefCountable::inc_ref(self) }
}
}
unsafe impl RefCountable for Platform {
unsafe fn inc_ref(handle: &Self) -> Ref<Self> {
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 *mut BNPlatform, count: usize, _context: &()) {
BNFreePlatformList(raw, count);
}
unsafe fn wrap_raw<'a>(raw: &'a *mut BNPlatform, context: &'a ()) -> Self::Wrapped<'a> {
debug_assert!(!raw.is_null());
Guard::new(Platform { handle: *raw }, context)
}
}