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//! Contains all information related to the execution environment of the binary, mainly the calling conventions used
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#[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    /// Get the address size of the platform, this is typically the same as the architecture's address size,
169    /// but some platforms like Linux x86_64 x32 ABI have differing address sizes from architecture.
170    pub fn address_size(&self) -> usize {
171        unsafe { BNGetPlatformAddressSize(self.handle) }
172    }
173
174    pub fn type_container(&self) -> TypeContainer {
175        let type_container_ptr = NonNull::new(unsafe { BNGetPlatformTypeContainer(self.handle) });
176        // NOTE: I have no idea how this isn't a UAF, see the note in `TypeContainer::from_raw`
177        // TODO: We are cloning here for platforms but we dont need to do this for [BinaryViewExt::type_container]
178        // TODO: Why does this require that we, construct a TypeContainer, duplicate the type container, then drop the original.
179        unsafe { TypeContainer::from_raw(type_container_ptr.unwrap()) }
180    }
181
182    /// Get all the type libraries that have been registered with the name.
183    ///
184    /// NOTE: This is a list because libraries can have `alternate_names`, use [`Platform::get_type_library_by_name`]
185    /// if you want to get _the_ type library with that name, skipping alternate names.
186    pub fn get_type_libraries_by_name(&self, name: &str) -> Array<TypeLibrary> {
187        let mut count = 0;
188        let name = name.to_cstr();
189        let result =
190            unsafe { BNGetPlatformTypeLibrariesByName(self.handle, name.as_ptr(), &mut count) };
191        assert!(!result.is_null());
192        unsafe { Array::new(result, count, ()) }
193    }
194
195    /// Get the type library with the given name.
196    ///
197    /// NOTE: This finds the first type library that has the given name, skipping alternate names.
198    pub fn get_type_library_by_name(&self, name: &str) -> Option<Ref<TypeLibrary>> {
199        let libraries = self.get_type_libraries_by_name(name);
200        libraries
201            .iter()
202            .find(|lib| lib.name() == name)
203            .map(|lib| lib.to_owned())
204    }
205
206    pub fn register_os(&self, os: &str) {
207        let os = os.to_cstr();
208        unsafe {
209            BNRegisterPlatform(os.as_ptr(), self.handle);
210        }
211    }
212
213    cc_func!(
214        get_default_calling_convention,
215        BNGetPlatformDefaultCallingConvention,
216        set_default_calling_convention,
217        BNRegisterPlatformDefaultCallingConvention
218    );
219
220    cc_func!(
221        get_cdecl_calling_convention,
222        BNGetPlatformCdeclCallingConvention,
223        set_cdecl_calling_convention,
224        BNRegisterPlatformCdeclCallingConvention
225    );
226
227    cc_func!(
228        get_stdcall_calling_convention,
229        BNGetPlatformStdcallCallingConvention,
230        set_stdcall_calling_convention,
231        BNRegisterPlatformStdcallCallingConvention
232    );
233
234    cc_func!(
235        get_fastcall_calling_convention,
236        BNGetPlatformFastcallCallingConvention,
237        set_fastcall_calling_convention,
238        BNRegisterPlatformFastcallCallingConvention
239    );
240
241    cc_func!(
242        get_syscall_convention,
243        BNGetPlatformSystemCallConvention,
244        set_syscall_convention,
245        BNSetPlatformSystemCallConvention
246    );
247
248    pub fn calling_conventions(&self) -> Array<CoreCallingConvention> {
249        unsafe {
250            let mut count = 0;
251            let handles = BNGetPlatformCallingConventions(self.handle, &mut count);
252            Array::new(handles, count, self.arch())
253        }
254    }
255
256    pub fn types(&self) -> Array<QualifiedNameAndType> {
257        unsafe {
258            let mut count = 0;
259            let handles = BNGetPlatformTypes(self.handle, &mut count);
260            Array::new(handles, count, ())
261        }
262    }
263
264    pub fn variables(&self) -> Array<QualifiedNameAndType> {
265        unsafe {
266            let mut count = 0;
267            let handles = BNGetPlatformVariables(self.handle, &mut count);
268            Array::new(handles, count, ())
269        }
270    }
271
272    pub fn functions(&self) -> Array<QualifiedNameAndType> {
273        unsafe {
274            let mut count = 0;
275            let handles = BNGetPlatformFunctions(self.handle, &mut count);
276            Array::new(handles, count, ())
277        }
278    }
279
280    // TODO: system_calls
281    // TODO: add a helper function to define a system call (platform function with a specific type)
282
283    // TODO: Documentation, specifically how this differs from the TypeParser impl
284    pub fn preprocess_source(
285        &self,
286        source: &str,
287        file_name: &str,
288        include_dirs: &[BnString],
289    ) -> Result<BnString, TypeParserError> {
290        let source_cstr = BnString::new(source);
291        let file_name_cstr = BnString::new(file_name);
292
293        let mut result = ptr::null_mut();
294        let mut error_string = ptr::null_mut();
295        let success = unsafe {
296            BNPreprocessSource(
297                source_cstr.as_ptr(),
298                file_name_cstr.as_ptr(),
299                &mut result,
300                &mut error_string,
301                include_dirs.as_ptr() as *mut *const ffi::c_char,
302                include_dirs.len(),
303            )
304        };
305
306        if success {
307            assert!(!result.is_null());
308            Ok(unsafe { BnString::from_raw(result) })
309        } else {
310            assert!(!error_string.is_null());
311            Err(TypeParserError::new(
312                TypeParserErrorSeverity::FatalSeverity,
313                unsafe { BnString::into_string(error_string) },
314                file_name.to_string(),
315                0,
316                0,
317            ))
318        }
319    }
320
321    // TODO: Documentation, specifically how this differs from the TypeParser impl
322    pub fn parse_types_from_source(
323        &self,
324        src: &str,
325        filename: &str,
326        include_dirs: &[BnString],
327        auto_type_source: &str,
328    ) -> Result<TypeParserResult, TypeParserError> {
329        let source_cstr = BnString::new(src);
330        let file_name_cstr = BnString::new(filename);
331        let auto_type_source = BnString::new(auto_type_source);
332
333        let mut raw_result = BNTypeParserResult::default();
334        let mut error_string = ptr::null_mut();
335        let success = unsafe {
336            BNParseTypesFromSource(
337                self.handle,
338                source_cstr.as_ptr(),
339                file_name_cstr.as_ptr(),
340                &mut raw_result,
341                &mut error_string,
342                include_dirs.as_ptr() as *mut *const ffi::c_char,
343                include_dirs.len(),
344                auto_type_source.as_ptr(),
345            )
346        };
347
348        if success {
349            let result = TypeParserResult::from_raw(&raw_result);
350            // NOTE: This is safe because the core allocated the TypeParserResult
351            TypeParserResult::free_raw(raw_result);
352            Ok(result)
353        } else {
354            assert!(!error_string.is_null());
355            Err(TypeParserError::new(
356                TypeParserErrorSeverity::FatalSeverity,
357                unsafe { BnString::into_string(error_string) },
358                filename.to_string(),
359                0,
360                0,
361            ))
362        }
363    }
364
365    // TODO: Documentation, specifically how this differs from the TypeParser impl
366    pub fn parse_types_from_source_file(
367        &self,
368        filename: &str,
369        include_dirs: &[BnString],
370        auto_type_source: &str,
371    ) -> Result<TypeParserResult, TypeParserError> {
372        let file_name_cstr = BnString::new(filename);
373        let auto_type_source = BnString::new(auto_type_source);
374
375        let mut raw_result = BNTypeParserResult::default();
376        let mut error_string = ptr::null_mut();
377        let success = unsafe {
378            BNParseTypesFromSourceFile(
379                self.handle,
380                file_name_cstr.as_ptr(),
381                &mut raw_result,
382                &mut error_string,
383                include_dirs.as_ptr() as *mut *const ffi::c_char,
384                include_dirs.len(),
385                auto_type_source.as_ptr(),
386            )
387        };
388
389        if success {
390            let result = TypeParserResult::from_raw(&raw_result);
391            // NOTE: This is safe because the core allocated the TypeParserResult
392            TypeParserResult::free_raw(raw_result);
393            Ok(result)
394        } else {
395            assert!(!error_string.is_null());
396            Err(TypeParserError::new(
397                TypeParserErrorSeverity::FatalSeverity,
398                unsafe { BnString::into_string(error_string) },
399                filename.to_string(),
400                0,
401                0,
402            ))
403        }
404    }
405}
406
407impl Debug for Platform {
408    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
409        f.debug_struct("Platform")
410            .field("name", &self.name())
411            .field("arch", &self.arch().name())
412            .finish()
413    }
414}
415
416impl ToOwned for Platform {
417    type Owned = Ref<Self>;
418
419    fn to_owned(&self) -> Self::Owned {
420        unsafe { RefCountable::inc_ref(self) }
421    }
422}
423
424unsafe impl RefCountable for Platform {
425    unsafe fn inc_ref(handle: &Self) -> Ref<Self> {
426        Ref::new(Self {
427            handle: BNNewPlatformReference(handle.handle),
428        })
429    }
430
431    unsafe fn dec_ref(handle: &Self) {
432        BNFreePlatform(handle.handle);
433    }
434}
435
436impl CoreArrayProvider for Platform {
437    type Raw = *mut BNPlatform;
438    type Context = ();
439    type Wrapped<'a> = Guard<'a, Platform>;
440}
441
442unsafe impl CoreArrayProviderInner for Platform {
443    unsafe fn free(raw: *mut Self::Raw, count: usize, _context: &Self::Context) {
444        BNFreePlatformList(raw, count);
445    }
446
447    unsafe fn wrap_raw<'a>(raw: &'a Self::Raw, context: &'a Self::Context) -> Self::Wrapped<'a> {
448        debug_assert!(!raw.is_null());
449        Guard::new(Self::from_raw(*raw), context)
450    }
451}