Skip to main content

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        QualifiedName, QualifiedNameAndType, Type, TypeContainer, TypeLibrary, TypeParserError,
24        TypeParserErrorSeverity, 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 [BinaryView::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    /// Register a calling convention with the platform, adding it to [`Platform::calling_conventions`].
251    ///
252    /// Unlike [`Platform::set_default_calling_convention`] and the other role setters, this does not
253    /// assign the calling convention to a role, it only makes it available on the platform.
254    ///
255    /// # Panics
256    ///
257    /// Panics if the calling convention's architecture does not match the platform's architecture.
258    pub fn register_calling_convention(&self, cc: &CoreCallingConvention) {
259        let arch = self.arch();
260
261        assert_eq!(
262            cc.arch_handle.handle, arch.handle,
263            "use of calling convention with non-matching Platform architecture!"
264        );
265
266        unsafe {
267            BNRegisterPlatformCallingConvention(self.handle, cc.handle);
268        }
269    }
270
271    pub fn calling_conventions(&self) -> Array<CoreCallingConvention> {
272        unsafe {
273            let mut count = 0;
274            let handles = BNGetPlatformCallingConventions(self.handle, &mut count);
275            Array::new(handles, count, self.arch())
276        }
277    }
278
279    pub fn types(&self) -> Array<QualifiedNameAndType> {
280        unsafe {
281            let mut count = 0;
282            let handles = BNGetPlatformTypes(self.handle, &mut count);
283            Array::new(handles, count, ())
284        }
285    }
286
287    pub fn variables(&self) -> Array<QualifiedNameAndType> {
288        unsafe {
289            let mut count = 0;
290            let handles = BNGetPlatformVariables(self.handle, &mut count);
291            Array::new(handles, count, ())
292        }
293    }
294
295    pub fn functions(&self) -> Array<QualifiedNameAndType> {
296        unsafe {
297            let mut count = 0;
298            let handles = BNGetPlatformFunctions(self.handle, &mut count);
299            Array::new(handles, count, ())
300        }
301    }
302
303    pub fn function_by_name<T: Into<QualifiedName>>(
304        &self,
305        name: T,
306        exact_match: bool,
307    ) -> Option<Ref<Type>> {
308        let mut raw_name = QualifiedName::into_raw(name.into());
309        unsafe {
310            let type_handle = BNGetPlatformFunctionByName(self.handle, &mut raw_name, exact_match);
311            QualifiedName::free_raw(raw_name);
312            if type_handle.is_null() {
313                return None;
314            }
315            Some(Type::ref_from_raw(type_handle))
316        }
317    }
318
319    // TODO: system_calls
320    // TODO: add a helper function to define a system call (platform function with a specific type)
321
322    // TODO: Documentation, specifically how this differs from the TypeParser impl
323    pub fn preprocess_source(
324        &self,
325        source: &str,
326        file_name: &str,
327        include_dirs: &[BnString],
328    ) -> Result<BnString, TypeParserError> {
329        let source_cstr = BnString::new(source);
330        let file_name_cstr = BnString::new(file_name);
331
332        let mut result = ptr::null_mut();
333        let mut error_string = ptr::null_mut();
334        let success = unsafe {
335            BNPreprocessSource(
336                source_cstr.as_ptr(),
337                file_name_cstr.as_ptr(),
338                &mut result,
339                &mut error_string,
340                include_dirs.as_ptr() as *mut *const ffi::c_char,
341                include_dirs.len(),
342            )
343        };
344
345        if success {
346            assert!(!result.is_null());
347            Ok(unsafe { BnString::from_raw(result) })
348        } else {
349            assert!(!error_string.is_null());
350            Err(TypeParserError::new(
351                TypeParserErrorSeverity::FatalSeverity,
352                unsafe { BnString::into_string(error_string) },
353                file_name.to_string(),
354                0,
355                0,
356            ))
357        }
358    }
359
360    // TODO: Documentation, specifically how this differs from the TypeParser impl
361    pub fn parse_types_from_source(
362        &self,
363        src: &str,
364        filename: &str,
365        include_dirs: &[BnString],
366        auto_type_source: &str,
367    ) -> Result<TypeParserResult, TypeParserError> {
368        let source_cstr = BnString::new(src);
369        let file_name_cstr = BnString::new(filename);
370        let auto_type_source = BnString::new(auto_type_source);
371
372        let mut raw_result = BNTypeParserResult::default();
373        let mut error_string = ptr::null_mut();
374        let success = unsafe {
375            BNParseTypesFromSource(
376                self.handle,
377                source_cstr.as_ptr(),
378                file_name_cstr.as_ptr(),
379                &mut raw_result,
380                &mut error_string,
381                include_dirs.as_ptr() as *mut *const ffi::c_char,
382                include_dirs.len(),
383                auto_type_source.as_ptr(),
384            )
385        };
386
387        if success {
388            let result = TypeParserResult::from_raw(&raw_result);
389            // NOTE: This is safe because the core allocated the TypeParserResult
390            TypeParserResult::free_raw(raw_result);
391            Ok(result)
392        } else {
393            assert!(!error_string.is_null());
394            Err(TypeParserError::new(
395                TypeParserErrorSeverity::FatalSeverity,
396                unsafe { BnString::into_string(error_string) },
397                filename.to_string(),
398                0,
399                0,
400            ))
401        }
402    }
403
404    // TODO: Documentation, specifically how this differs from the TypeParser impl
405    pub fn parse_types_from_source_file(
406        &self,
407        filename: &str,
408        include_dirs: &[BnString],
409        auto_type_source: &str,
410    ) -> Result<TypeParserResult, TypeParserError> {
411        let file_name_cstr = BnString::new(filename);
412        let auto_type_source = BnString::new(auto_type_source);
413
414        let mut raw_result = BNTypeParserResult::default();
415        let mut error_string = ptr::null_mut();
416        let success = unsafe {
417            BNParseTypesFromSourceFile(
418                self.handle,
419                file_name_cstr.as_ptr(),
420                &mut raw_result,
421                &mut error_string,
422                include_dirs.as_ptr() as *mut *const ffi::c_char,
423                include_dirs.len(),
424                auto_type_source.as_ptr(),
425            )
426        };
427
428        if success {
429            let result = TypeParserResult::from_raw(&raw_result);
430            // NOTE: This is safe because the core allocated the TypeParserResult
431            TypeParserResult::free_raw(raw_result);
432            Ok(result)
433        } else {
434            assert!(!error_string.is_null());
435            Err(TypeParserError::new(
436                TypeParserErrorSeverity::FatalSeverity,
437                unsafe { BnString::into_string(error_string) },
438                filename.to_string(),
439                0,
440                0,
441            ))
442        }
443    }
444}
445
446impl Debug for Platform {
447    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
448        f.debug_struct("Platform")
449            .field("name", &self.name())
450            .field("arch", &self.arch().name())
451            .finish()
452    }
453}
454
455impl ToOwned for Platform {
456    type Owned = Ref<Self>;
457
458    fn to_owned(&self) -> Self::Owned {
459        unsafe { RefCountable::inc_ref(self) }
460    }
461}
462
463unsafe impl RefCountable for Platform {
464    unsafe fn inc_ref(handle: &Self) -> Ref<Self> {
465        Ref::new(Self {
466            handle: BNNewPlatformReference(handle.handle),
467        })
468    }
469
470    unsafe fn dec_ref(handle: &Self) {
471        BNFreePlatform(handle.handle);
472    }
473}
474
475impl CoreArrayProvider for Platform {
476    type Raw = *mut BNPlatform;
477    type Context = ();
478    type Wrapped<'a> = Guard<'a, Platform>;
479}
480
481unsafe impl CoreArrayProviderInner for Platform {
482    unsafe fn free(raw: *mut Self::Raw, count: usize, _context: &Self::Context) {
483        BNFreePlatformList(raw, count);
484    }
485
486    unsafe fn wrap_raw<'a>(raw: &'a Self::Raw, context: &'a Self::Context) -> Self::Wrapped<'a> {
487        debug_assert!(!raw.is_null());
488        Guard::new(Self::from_raw(*raw), context)
489    }
490}