binaryninja/
platform.rs

1// Copyright 2021-2026 Vector 35 Inc.
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15//! A [`Platform`] models the information related to the execution environment of the binary.
16
17use crate::{
18    architecture::{Architecture, CoreArchitecture},
19    calling_convention::CoreCallingConvention,
20    rc::*,
21    string::*,
22    types::{
23        QualifiedNameAndType, TypeContainer, TypeLibrary, TypeParserError, TypeParserErrorSeverity,
24        TypeParserResult,
25    },
26};
27use binaryninjacore_sys::*;
28use std::fmt::Debug;
29use std::ptr::NonNull;
30use std::{borrow::Borrow, ffi, ptr};
31
32/// A platform describes the target [`CoreArchitecture`] and platform-specific information such as
33/// the calling conventions and generic types (think `HRESULT` on Windows).
34#[derive(PartialEq, Eq, Hash)]
35pub struct Platform {
36    pub(crate) handle: *mut BNPlatform,
37}
38
39unsafe impl Send for Platform {}
40unsafe impl Sync for Platform {}
41
42macro_rules! cc_func {
43    ($get_name:ident, $get_api:ident, $set_name:ident, $set_api:ident) => {
44        pub fn $get_name(&self) -> Option<Ref<CoreCallingConvention>> {
45            let arch = self.arch();
46
47            unsafe {
48                let cc = $get_api(self.handle);
49
50                if cc.is_null() {
51                    None
52                } else {
53                    Some(CoreCallingConvention::ref_from_raw(
54                        cc,
55                        arch.as_ref().handle(),
56                    ))
57                }
58            }
59        }
60
61        pub fn $set_name(&self, cc: &CoreCallingConvention) {
62            let arch = self.arch();
63
64            assert!(
65                cc.arch_handle.borrow().as_ref().handle == arch.handle,
66                "use of calling convention with non-matching Platform architecture!"
67            );
68
69            unsafe {
70                $set_api(self.handle, cc.handle);
71            }
72        }
73    };
74}
75
76impl Platform {
77    pub unsafe fn from_raw(handle: *mut BNPlatform) -> Self {
78        debug_assert!(!handle.is_null());
79        Self { handle }
80    }
81
82    pub(crate) unsafe fn ref_from_raw(handle: *mut BNPlatform) -> Ref<Self> {
83        debug_assert!(!handle.is_null());
84        Ref::new(Self { handle })
85    }
86
87    pub fn by_name(name: &str) -> Option<Ref<Self>> {
88        let raw_name = name.to_cstr();
89        unsafe {
90            let res = BNGetPlatformByName(raw_name.as_ptr());
91
92            if res.is_null() {
93                None
94            } else {
95                Some(Self::ref_from_raw(res))
96            }
97        }
98    }
99
100    pub fn list_all() -> Array<Platform> {
101        unsafe {
102            let mut count = 0;
103            let handles = BNGetPlatformList(&mut count);
104
105            Array::new(handles, count, ())
106        }
107    }
108
109    pub fn list_by_arch(arch: &CoreArchitecture) -> Array<Platform> {
110        unsafe {
111            let mut count = 0;
112            let handles = BNGetPlatformListByArchitecture(arch.handle, &mut count);
113
114            Array::new(handles, count, ())
115        }
116    }
117
118    pub fn list_by_os(name: &str) -> Array<Platform> {
119        let raw_name = name.to_cstr();
120
121        unsafe {
122            let mut count = 0;
123            let handles = BNGetPlatformListByOS(raw_name.as_ptr(), &mut count);
124
125            Array::new(handles, count, ())
126        }
127    }
128
129    pub fn list_by_os_and_arch(name: &str, arch: &CoreArchitecture) -> Array<Platform> {
130        let raw_name = name.to_cstr();
131
132        unsafe {
133            let mut count = 0;
134            let handles =
135                BNGetPlatformListByOSAndArchitecture(raw_name.as_ptr(), arch.handle, &mut count);
136
137            Array::new(handles, count, ())
138        }
139    }
140
141    pub fn list_available_os() -> Array<BnString> {
142        unsafe {
143            let mut count = 0;
144            let list = BNGetPlatformOSList(&mut count);
145
146            Array::new(list, count, ())
147        }
148    }
149
150    pub fn new<A: Architecture>(arch: &A, name: &str) -> Ref<Self> {
151        let name = name.to_cstr();
152        unsafe {
153            let handle = BNCreatePlatform(arch.as_ref().handle, name.as_ptr());
154            assert!(!handle.is_null());
155            Ref::new(Self { handle })
156        }
157    }
158
159    pub fn name(&self) -> String {
160        unsafe {
161            let raw_name = BNGetPlatformName(self.handle);
162            BnString::into_string(raw_name)
163        }
164    }
165
166    pub fn arch(&self) -> CoreArchitecture {
167        unsafe { CoreArchitecture::from_raw(BNGetPlatformArchitecture(self.handle)) }
168    }
169
170    /// Get the address size of the platform, this is typically the same as the architecture's address size,
171    /// but some platforms like Linux x86_64 x32 ABI have differing address sizes from architecture.
172    pub fn address_size(&self) -> usize {
173        unsafe { BNGetPlatformAddressSize(self.handle) }
174    }
175
176    pub fn type_container(&self) -> TypeContainer {
177        let type_container_ptr = NonNull::new(unsafe { BNGetPlatformTypeContainer(self.handle) });
178        // NOTE: I have no idea how this isn't a UAF, see the note in `TypeContainer::from_raw`
179        // TODO: We are cloning here for platforms but we dont need to do this for [BinaryViewExt::type_container]
180        // TODO: Why does this require that we, construct a TypeContainer, duplicate the type container, then drop the original.
181        unsafe { TypeContainer::from_raw(type_container_ptr.unwrap()) }
182    }
183
184    /// Get all the type libraries that have been registered with the name.
185    ///
186    /// NOTE: This is a list because libraries can have `alternate_names`, use [`Platform::get_type_library_by_name`]
187    /// if you want to get _the_ type library with that name, skipping alternate names.
188    pub fn get_type_libraries_by_name(&self, name: &str) -> Array<TypeLibrary> {
189        let mut count = 0;
190        let name = name.to_cstr();
191        let result =
192            unsafe { BNGetPlatformTypeLibrariesByName(self.handle, name.as_ptr(), &mut count) };
193        assert!(!result.is_null());
194        unsafe { Array::new(result, count, ()) }
195    }
196
197    /// Get the type library with the given name.
198    ///
199    /// NOTE: This finds the first type library that has the given name, skipping alternate names.
200    pub fn get_type_library_by_name(&self, name: &str) -> Option<Ref<TypeLibrary>> {
201        let libraries = self.get_type_libraries_by_name(name);
202        libraries
203            .iter()
204            .find(|lib| lib.name() == name)
205            .map(|lib| lib.to_owned())
206    }
207
208    pub fn register_os(&self, os: &str) {
209        let os = os.to_cstr();
210        unsafe {
211            BNRegisterPlatform(os.as_ptr(), self.handle);
212        }
213    }
214
215    cc_func!(
216        get_default_calling_convention,
217        BNGetPlatformDefaultCallingConvention,
218        set_default_calling_convention,
219        BNRegisterPlatformDefaultCallingConvention
220    );
221
222    cc_func!(
223        get_cdecl_calling_convention,
224        BNGetPlatformCdeclCallingConvention,
225        set_cdecl_calling_convention,
226        BNRegisterPlatformCdeclCallingConvention
227    );
228
229    cc_func!(
230        get_stdcall_calling_convention,
231        BNGetPlatformStdcallCallingConvention,
232        set_stdcall_calling_convention,
233        BNRegisterPlatformStdcallCallingConvention
234    );
235
236    cc_func!(
237        get_fastcall_calling_convention,
238        BNGetPlatformFastcallCallingConvention,
239        set_fastcall_calling_convention,
240        BNRegisterPlatformFastcallCallingConvention
241    );
242
243    cc_func!(
244        get_syscall_convention,
245        BNGetPlatformSystemCallConvention,
246        set_syscall_convention,
247        BNSetPlatformSystemCallConvention
248    );
249
250    pub fn calling_conventions(&self) -> Array<CoreCallingConvention> {
251        unsafe {
252            let mut count = 0;
253            let handles = BNGetPlatformCallingConventions(self.handle, &mut count);
254            Array::new(handles, count, self.arch())
255        }
256    }
257
258    pub fn types(&self) -> Array<QualifiedNameAndType> {
259        unsafe {
260            let mut count = 0;
261            let handles = BNGetPlatformTypes(self.handle, &mut count);
262            Array::new(handles, count, ())
263        }
264    }
265
266    pub fn variables(&self) -> Array<QualifiedNameAndType> {
267        unsafe {
268            let mut count = 0;
269            let handles = BNGetPlatformVariables(self.handle, &mut count);
270            Array::new(handles, count, ())
271        }
272    }
273
274    pub fn functions(&self) -> Array<QualifiedNameAndType> {
275        unsafe {
276            let mut count = 0;
277            let handles = BNGetPlatformFunctions(self.handle, &mut count);
278            Array::new(handles, count, ())
279        }
280    }
281
282    // TODO: system_calls
283    // TODO: add a helper function to define a system call (platform function with a specific type)
284
285    // TODO: Documentation, specifically how this differs from the TypeParser impl
286    pub fn preprocess_source(
287        &self,
288        source: &str,
289        file_name: &str,
290        include_dirs: &[BnString],
291    ) -> Result<BnString, TypeParserError> {
292        let source_cstr = BnString::new(source);
293        let file_name_cstr = BnString::new(file_name);
294
295        let mut result = ptr::null_mut();
296        let mut error_string = ptr::null_mut();
297        let success = unsafe {
298            BNPreprocessSource(
299                source_cstr.as_ptr(),
300                file_name_cstr.as_ptr(),
301                &mut result,
302                &mut error_string,
303                include_dirs.as_ptr() as *mut *const ffi::c_char,
304                include_dirs.len(),
305            )
306        };
307
308        if success {
309            assert!(!result.is_null());
310            Ok(unsafe { BnString::from_raw(result) })
311        } else {
312            assert!(!error_string.is_null());
313            Err(TypeParserError::new(
314                TypeParserErrorSeverity::FatalSeverity,
315                unsafe { BnString::into_string(error_string) },
316                file_name.to_string(),
317                0,
318                0,
319            ))
320        }
321    }
322
323    // TODO: Documentation, specifically how this differs from the TypeParser impl
324    pub fn parse_types_from_source(
325        &self,
326        src: &str,
327        filename: &str,
328        include_dirs: &[BnString],
329        auto_type_source: &str,
330    ) -> Result<TypeParserResult, TypeParserError> {
331        let source_cstr = BnString::new(src);
332        let file_name_cstr = BnString::new(filename);
333        let auto_type_source = BnString::new(auto_type_source);
334
335        let mut raw_result = BNTypeParserResult::default();
336        let mut error_string = ptr::null_mut();
337        let success = unsafe {
338            BNParseTypesFromSource(
339                self.handle,
340                source_cstr.as_ptr(),
341                file_name_cstr.as_ptr(),
342                &mut raw_result,
343                &mut error_string,
344                include_dirs.as_ptr() as *mut *const ffi::c_char,
345                include_dirs.len(),
346                auto_type_source.as_ptr(),
347            )
348        };
349
350        if success {
351            let result = TypeParserResult::from_raw(&raw_result);
352            // NOTE: This is safe because the core allocated the TypeParserResult
353            TypeParserResult::free_raw(raw_result);
354            Ok(result)
355        } else {
356            assert!(!error_string.is_null());
357            Err(TypeParserError::new(
358                TypeParserErrorSeverity::FatalSeverity,
359                unsafe { BnString::into_string(error_string) },
360                filename.to_string(),
361                0,
362                0,
363            ))
364        }
365    }
366
367    // TODO: Documentation, specifically how this differs from the TypeParser impl
368    pub fn parse_types_from_source_file(
369        &self,
370        filename: &str,
371        include_dirs: &[BnString],
372        auto_type_source: &str,
373    ) -> Result<TypeParserResult, TypeParserError> {
374        let file_name_cstr = BnString::new(filename);
375        let auto_type_source = BnString::new(auto_type_source);
376
377        let mut raw_result = BNTypeParserResult::default();
378        let mut error_string = ptr::null_mut();
379        let success = unsafe {
380            BNParseTypesFromSourceFile(
381                self.handle,
382                file_name_cstr.as_ptr(),
383                &mut raw_result,
384                &mut error_string,
385                include_dirs.as_ptr() as *mut *const ffi::c_char,
386                include_dirs.len(),
387                auto_type_source.as_ptr(),
388            )
389        };
390
391        if success {
392            let result = TypeParserResult::from_raw(&raw_result);
393            // NOTE: This is safe because the core allocated the TypeParserResult
394            TypeParserResult::free_raw(raw_result);
395            Ok(result)
396        } else {
397            assert!(!error_string.is_null());
398            Err(TypeParserError::new(
399                TypeParserErrorSeverity::FatalSeverity,
400                unsafe { BnString::into_string(error_string) },
401                filename.to_string(),
402                0,
403                0,
404            ))
405        }
406    }
407}
408
409impl Debug for Platform {
410    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
411        f.debug_struct("Platform")
412            .field("name", &self.name())
413            .field("arch", &self.arch().name())
414            .finish()
415    }
416}
417
418impl ToOwned for Platform {
419    type Owned = Ref<Self>;
420
421    fn to_owned(&self) -> Self::Owned {
422        unsafe { RefCountable::inc_ref(self) }
423    }
424}
425
426unsafe impl RefCountable for Platform {
427    unsafe fn inc_ref(handle: &Self) -> Ref<Self> {
428        Ref::new(Self {
429            handle: BNNewPlatformReference(handle.handle),
430        })
431    }
432
433    unsafe fn dec_ref(handle: &Self) {
434        BNFreePlatform(handle.handle);
435    }
436}
437
438impl CoreArrayProvider for Platform {
439    type Raw = *mut BNPlatform;
440    type Context = ();
441    type Wrapped<'a> = Guard<'a, Platform>;
442}
443
444unsafe impl CoreArrayProviderInner for Platform {
445    unsafe fn free(raw: *mut Self::Raw, count: usize, _context: &Self::Context) {
446        BNFreePlatformList(raw, count);
447    }
448
449    unsafe fn wrap_raw<'a>(raw: &'a Self::Raw, context: &'a Self::Context) -> Self::Wrapped<'a> {
450        debug_assert!(!raw.is_null());
451        Guard::new(Self::from_raw(*raw), context)
452    }
453}