binaryninja/
platform.rs

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