binaryninja/types/
library.rs

1use crate::rc::{Guard, RefCountable};
2use crate::types::TypeContainer;
3use crate::{
4    architecture::CoreArchitecture,
5    metadata::Metadata,
6    platform::Platform,
7    rc::{Array, CoreArrayProvider, CoreArrayProviderInner, Ref},
8    string::{BnString, IntoCStr},
9    types::{QualifiedName, QualifiedNameAndType, Type},
10};
11use binaryninjacore_sys::*;
12use std::fmt::{Debug, Formatter};
13use std::hash::{Hash, Hasher};
14use std::path::Path;
15use std::ptr::NonNull;
16
17// Used for doc comments
18#[allow(unused_imports)]
19use crate::binary_view::BinaryView;
20
21// TODO: Introduce a FinalizedTypeLibrary that cannot be mutated, so we do not have APIs that are unusable.
22
23/// A [`TypeLibrary`] is a collection of function symbols and their associated types and metadata that
24/// correspond to a shared library or are used in conjunction with a shared library. Type libraries
25/// are the main way external functions in a binary view are annotated and are crucial to allowing
26/// proper analysis of the binary.
27///
28/// Type libraries can share common types between them by forwarding named type references to a specified
29/// source type library. If a type library is made available to a view, it may also pull in other type
30/// libraries, it is important to not treat a type library as a complete source of information.
31#[repr(transparent)]
32pub struct TypeLibrary {
33    handle: NonNull<BNTypeLibrary>,
34}
35
36impl TypeLibrary {
37    pub(crate) unsafe fn from_raw(handle: NonNull<BNTypeLibrary>) -> Self {
38        Self { handle }
39    }
40
41    pub(crate) unsafe fn ref_from_raw(handle: NonNull<BNTypeLibrary>) -> Ref<Self> {
42        Ref::new(Self { handle })
43    }
44
45    #[allow(clippy::mut_from_ref)]
46    pub(crate) unsafe fn as_raw(&self) -> &mut BNTypeLibrary {
47        &mut *self.handle.as_ptr()
48    }
49
50    /// Duplicate the type library. This creates a new, non-finalized type library object that shares
51    /// the same underlying name and architecture.
52    ///
53    /// IMPORTANT: This does not *actually* duplicate the type library currently, you still need to
54    /// copy over the named types, named objects, platforms, and metadata.
55    pub fn duplicate(&self) -> Ref<Self> {
56        unsafe { Self::ref_from_raw(NonNull::new(BNDuplicateTypeLibrary(self.as_raw())).unwrap()) }
57    }
58
59    /// Creates an empty type library object with a random GUID and the provided name.
60    pub fn new(arch: CoreArchitecture, name: &str) -> Ref<TypeLibrary> {
61        let name = name.to_cstr();
62        let new_lib = unsafe { BNNewTypeLibrary(arch.handle, name.as_ptr()) };
63        unsafe { TypeLibrary::ref_from_raw(NonNull::new(new_lib).unwrap()) }
64    }
65
66    pub fn all(arch: CoreArchitecture) -> Array<TypeLibrary> {
67        let mut count = 0;
68        let result = unsafe { BNGetArchitectureTypeLibraries(arch.handle, &mut count) };
69        assert!(!result.is_null());
70        unsafe { Array::new(result, count, ()) }
71    }
72
73    /// Loads a finalized type library instance from the given `path`.
74    ///
75    /// The returned type library cannot be modified.
76    pub fn load_from_file(path: &Path) -> Option<Ref<TypeLibrary>> {
77        let path = path.to_cstr();
78        let handle = unsafe { BNLoadTypeLibraryFromFile(path.as_ptr()) };
79        NonNull::new(handle).map(|h| unsafe { TypeLibrary::ref_from_raw(h) })
80    }
81
82    /// Saves a type library at the given `path` on disk, overwriting any existing file.
83    ///
84    /// The path must be writable, and the parent directory must exist.
85    pub fn write_to_file(&self, path: &Path) -> bool {
86        let path = path.to_cstr();
87        unsafe { BNWriteTypeLibraryToFile(self.as_raw(), path.as_ptr()) }
88    }
89
90    /// Decompresses the type library file to a JSON file at the given `output_path`.
91    pub fn decompress_to_file(&self, output_path: &Path) -> bool {
92        let path = output_path.to_cstr();
93        unsafe { BNTypeLibraryDecompressToFile(self.handle.as_ptr(), path.as_ptr()) }
94    }
95
96    /// Looks up the first type library found with a matching name. Keep in mind that names are not
97    /// necessarily unique.
98    ///
99    /// NOTE: If the type library architecture's associated platform has not been initialized, this will
100    /// return `None`. To make sure that the platform has been initialized, one should instead get the type
101    /// libraries through [`Platform::get_type_libraries_by_name`].
102    pub fn from_name(arch: CoreArchitecture, name: &str) -> Option<Ref<TypeLibrary>> {
103        let name = name.to_cstr();
104        let handle = unsafe { BNLookupTypeLibraryByName(arch.handle, name.as_ptr()) };
105        NonNull::new(handle).map(|h| unsafe { TypeLibrary::ref_from_raw(h) })
106    }
107
108    /// Attempts to grab a type library associated with the provided Architecture and GUID pair.
109    ///
110    /// NOTE: If the associated platform for the architecture has not been initialized,  
111    /// this will return `None`. Avoid calling this outside of a view context.
112    pub fn from_guid(arch: CoreArchitecture, guid: &str) -> Option<Ref<TypeLibrary>> {
113        let guid = guid.to_cstr();
114        let handle = unsafe { BNLookupTypeLibraryByGuid(arch.handle, guid.as_ptr()) };
115        NonNull::new(handle).map(|h| unsafe { TypeLibrary::ref_from_raw(h) })
116    }
117
118    /// The [`CoreArchitecture`] this type library is associated with.
119    ///
120    /// Type libraries will always have a single architecture associated with it. It can have multiple
121    /// platforms associated with it, see [`TypeLibrary::platform_names`] for more detail.
122    pub fn arch(&self) -> CoreArchitecture {
123        let arch = unsafe { BNGetTypeLibraryArchitecture(self.as_raw()) };
124        assert!(!arch.is_null());
125        unsafe { CoreArchitecture::from_raw(arch) }
126    }
127
128    /// The primary name associated with this type library, this will not be used for importing type
129    /// libraries automatically into a binary view, that is the job of [`TypeLibrary::dependency_name`].
130    pub fn name(&self) -> String {
131        let result = unsafe { BNGetTypeLibraryName(self.as_raw()) };
132        assert!(!result.is_null());
133        unsafe { BnString::into_string(result) }
134    }
135
136    /// Sets the name of a type library that has not been finalized.
137    pub fn set_name(&self, value: &str) {
138        let value = value.to_cstr();
139        unsafe { BNSetTypeLibraryName(self.as_raw(), value.as_ptr()) }
140    }
141
142    /// The `dependency_name` of a library is the name used to record dependencies across
143    /// type libraries. This allows, for example, a library with the name "musl_libc" to have
144    /// dependencies on it recorded as "libc_generic", allowing a type library to be used across
145    /// multiple platforms where each has a specific libc that also provides the name "libc_generic"
146    /// as an `alternate_name`.
147    pub fn dependency_name(&self) -> String {
148        let result = unsafe { BNGetTypeLibraryDependencyName(self.as_raw()) };
149        assert!(!result.is_null());
150        unsafe { BnString::into_string(result) }
151    }
152
153    /// Sets the dependency name of a type library instance that has not been finalized
154    pub fn set_dependency_name(&self, value: &str) {
155        let value = value.to_cstr();
156        unsafe { BNSetTypeLibraryDependencyName(self.as_raw(), value.as_ptr()) }
157    }
158
159    /// Returns the GUID associated with the type library
160    pub fn guid(&self) -> String {
161        let result = unsafe { BNGetTypeLibraryGuid(self.as_raw()) };
162        assert!(!result.is_null());
163        unsafe { BnString::into_string(result) }
164    }
165
166    /// Sets the GUID of a type library instance that has not been finalized.
167    pub fn set_guid(&self, value: &str) {
168        let value = value.to_cstr();
169        unsafe { BNSetTypeLibraryGuid(self.as_raw(), value.as_ptr()) }
170    }
171
172    /// A list of extra names that will be considered a match by [`Platform::get_type_libraries_by_name`]
173    pub fn alternate_names(&self) -> Array<BnString> {
174        let mut count = 0;
175        let result = unsafe { BNGetTypeLibraryAlternateNames(self.as_raw(), &mut count) };
176        assert!(!result.is_null());
177        unsafe { Array::new(result, count, ()) }
178    }
179
180    /// Adds an extra name to this type library used during library lookups and dependency resolution
181    pub fn add_alternate_name(&self, value: &str) {
182        let value = value.to_cstr();
183        unsafe { BNAddTypeLibraryAlternateName(self.as_raw(), value.as_ptr()) }
184    }
185
186    /// Returns a list of all platform names that this type library will register with during platform
187    /// type registration.
188    ///
189    /// Because type libraries can be distributed with platforms that do not exist, we return the names.
190    pub fn platform_names(&self) -> Array<BnString> {
191        let mut count = 0;
192        let result = unsafe { BNGetTypeLibraryPlatforms(self.as_raw(), &mut count) };
193        assert!(!result.is_null());
194        unsafe { Array::new(result, count, ()) }
195    }
196
197    /// Associate a platform with a type library instance that has not been finalized.
198    ///
199    /// This will cause the library to be searchable by [`Platform::get_type_libraries_by_name`]
200    /// when loaded.
201    ///
202    /// This does not have side effects until finalization of the type library.
203    pub fn add_platform(&self, plat: &Platform) {
204        unsafe { BNAddTypeLibraryPlatform(self.as_raw(), plat.handle) }
205    }
206
207    /// Clears the list of platforms associated with a type library instance that has not been finalized.
208    pub fn clear_platforms(&self) {
209        unsafe { BNClearTypeLibraryPlatforms(self.as_raw()) }
210    }
211
212    /// Flags a newly created type library instance as finalized and makes it available for Platform
213    /// and Architecture type library searches.
214    pub fn finalize(&self) -> bool {
215        unsafe { BNFinalizeTypeLibrary(self.as_raw()) }
216    }
217
218    /// Make a created or loaded Type Library available for Platforms to use when loading binaries.
219    pub fn register(&self) {
220        unsafe { BNRegisterTypeLibrary(self.as_raw()) }
221    }
222
223    /// Retrieves the metadata associated with the given key stored in the type library.
224    pub fn query_metadata(&self, key: &str) -> Option<Ref<Metadata>> {
225        let key = key.to_cstr();
226        let result = unsafe { BNTypeLibraryQueryMetadata(self.as_raw(), key.as_ptr()) };
227        (!result.is_null()).then(|| unsafe { Metadata::ref_from_raw(result) })
228    }
229
230    /// Stores a [`Metadata`] object in the given key for the type library.
231    ///
232    /// This is primarily intended as a way to store Platform specific information relevant to BinaryView implementations;
233    /// for example, the PE BinaryViewType uses type library metadata to retrieve ordinal information, when available.
234    pub fn store_metadata(&self, key: &str, md: &Metadata) {
235        let key = key.to_cstr();
236        unsafe { BNTypeLibraryStoreMetadata(self.as_raw(), key.as_ptr(), md.handle) }
237    }
238
239    /// Removes the metadata associated with key from the type library.
240    pub fn remove_metadata(&self, key: &str) {
241        let key = key.to_cstr();
242        unsafe { BNTypeLibraryRemoveMetadata(self.as_raw(), key.as_ptr()) }
243    }
244
245    /// Retrieves the metadata associated with the type library.
246    pub fn metadata(&self) -> Ref<Metadata> {
247        let md_handle = unsafe { BNTypeLibraryGetMetadata(self.as_raw()) };
248        assert!(!md_handle.is_null());
249        unsafe { Metadata::ref_from_raw(md_handle) }
250    }
251
252    pub fn type_container(&self) -> TypeContainer {
253        let result = unsafe { BNGetTypeLibraryTypeContainer(self.as_raw()) };
254        unsafe { TypeContainer::from_raw(NonNull::new(result).unwrap()) }
255    }
256
257    /// Directly inserts a named object into the type library's object store.
258    ///
259    /// Referenced types will not automatically be added, so make sure to add referenced types to the
260    /// library or use [`TypeLibrary::add_type_source`] to mark the references originating source.
261    ///
262    /// To add objects from a binary view, prefer using [`BinaryView::export_object_to_library`] which
263    /// will automatically pull in all referenced types and record additional dependencies as needed.
264    pub fn add_named_object(&self, name: QualifiedName, type_: &Type) {
265        let mut raw_name = QualifiedName::into_raw(name);
266        unsafe { BNAddTypeLibraryNamedObject(self.as_raw(), &mut raw_name, type_.handle) }
267        QualifiedName::free_raw(raw_name);
268    }
269
270    pub fn remove_named_object(&self, name: QualifiedName) {
271        let mut raw_name = QualifiedName::into_raw(name);
272        unsafe { BNRemoveTypeLibraryNamedObject(self.as_raw(), &mut raw_name) }
273        QualifiedName::free_raw(raw_name);
274    }
275
276    /// Directly inserts a named type into the type library's type store.
277    ///
278    /// Referenced types will not automatically be added, so make sure to add referenced types to the
279    /// library or use [`TypeLibrary::add_type_source`] to mark the references originating source.
280    ///
281    /// To add types from a binary view, prefer using [`BinaryView::export_type_to_library`] which
282    /// will automatically pull in all referenced types and record additional dependencies as needed.
283    pub fn add_named_type(&self, name: QualifiedName, type_: &Type) {
284        let mut raw_name = QualifiedName::into_raw(name);
285        unsafe { BNAddTypeLibraryNamedType(self.as_raw(), &mut raw_name, type_.handle) }
286        QualifiedName::free_raw(raw_name);
287    }
288
289    pub fn remove_named_type(&self, name: QualifiedName) {
290        let mut raw_name = QualifiedName::into_raw(name);
291        unsafe { BNRemoveTypeLibraryNamedType(self.as_raw(), &mut raw_name) }
292        QualifiedName::free_raw(raw_name);
293    }
294
295    /// Flag any outgoing named type reference with the given `name` as belonging to the `source` type library.
296    ///
297    /// This allows type libraries to share types between them, automatically pulling in dependencies
298    /// into the binary view as needed.
299    pub fn add_type_source(&self, name: QualifiedName, source: &str) {
300        let source = source.to_cstr();
301        let mut raw_name = QualifiedName::into_raw(name);
302        unsafe { BNAddTypeLibraryNamedTypeSource(self.as_raw(), &mut raw_name, source.as_ptr()) }
303        QualifiedName::free_raw(raw_name);
304    }
305
306    /// Retrieve the source type library associated with the given named type, if any.
307    pub fn get_named_type_source(&self, name: QualifiedName) -> Option<String> {
308        let mut raw_name = QualifiedName::into_raw(name);
309        let result = unsafe { BNGetTypeLibraryNamedTypeSource(self.as_raw(), &mut raw_name) };
310        QualifiedName::free_raw(raw_name);
311        let str = unsafe { BnString::into_string(result) };
312        if str.is_empty() {
313            None
314        } else {
315            Some(str)
316        }
317    }
318
319    /// Get the object (function) associated with the given name, if any.
320    ///
321    /// Prefer [`BinaryView::import_type_library_object`] as it will recursively import types required.
322    pub fn get_named_object(&self, name: QualifiedName) -> Option<Ref<Type>> {
323        let mut raw_name = QualifiedName::into_raw(name);
324        let t = unsafe { BNGetTypeLibraryNamedObject(self.as_raw(), &mut raw_name) };
325        QualifiedName::free_raw(raw_name);
326        (!t.is_null()).then(|| unsafe { Type::ref_from_raw(t) })
327    }
328
329    /// Get the type associated with the given name, if any.
330    ///
331    /// Prefer [`BinaryView::import_type_library_type`] as it will recursively import types required.
332    pub fn get_named_type(&self, name: QualifiedName) -> Option<Ref<Type>> {
333        let mut raw_name = QualifiedName::into_raw(name);
334        let t = unsafe { BNGetTypeLibraryNamedType(self.as_raw(), &mut raw_name) };
335        QualifiedName::free_raw(raw_name);
336        (!t.is_null()).then(|| unsafe { Type::ref_from_raw(t) })
337    }
338
339    /// The list of all named objects provided by a type library
340    pub fn named_objects(&self) -> Array<QualifiedNameAndType> {
341        let mut count = 0;
342        let result = unsafe { BNGetTypeLibraryNamedObjects(self.as_raw(), &mut count) };
343        assert!(!result.is_null());
344        unsafe { Array::new(result, count, ()) }
345    }
346
347    /// The list of all named types provided by a type library
348    pub fn named_types(&self) -> Array<QualifiedNameAndType> {
349        let mut count = 0;
350        let result = unsafe { BNGetTypeLibraryNamedTypes(self.as_raw(), &mut count) };
351        assert!(!result.is_null());
352        unsafe { Array::new(result, count, ()) }
353    }
354}
355
356impl Debug for TypeLibrary {
357    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
358        f.debug_struct("TypeLibrary")
359            .field("name", &self.name())
360            .field("dependency_name", &self.dependency_name())
361            .field("arch", &self.arch())
362            .field("guid", &self.guid())
363            .field("alternate_names", &self.alternate_names().to_vec())
364            .field("platform_names", &self.platform_names().to_vec())
365            .field("metadata", &self.metadata())
366            // These two are too verbose.
367            // .field("named_objects", &self.named_objects().to_vec())
368            // .field("named_types", &self.named_types().to_vec())
369            .finish()
370    }
371}
372
373impl PartialEq for TypeLibrary {
374    fn eq(&self, other: &Self) -> bool {
375        self.guid() == other.guid()
376    }
377}
378
379impl Eq for TypeLibrary {}
380
381impl Hash for TypeLibrary {
382    fn hash<H: Hasher>(&self, state: &mut H) {
383        self.guid().hash(state);
384    }
385}
386
387unsafe impl RefCountable for TypeLibrary {
388    unsafe fn inc_ref(handle: &Self) -> Ref<Self> {
389        Ref::new(Self {
390            handle: NonNull::new(BNNewTypeLibraryReference(handle.handle.as_ptr())).unwrap(),
391        })
392    }
393
394    unsafe fn dec_ref(handle: &Self) {
395        BNFreeTypeLibrary(handle.handle.as_ptr());
396    }
397}
398
399impl ToOwned for TypeLibrary {
400    type Owned = Ref<Self>;
401
402    fn to_owned(&self) -> Self::Owned {
403        unsafe { RefCountable::inc_ref(self) }
404    }
405}
406
407impl CoreArrayProvider for TypeLibrary {
408    type Raw = *mut BNTypeLibrary;
409    type Context = ();
410    type Wrapped<'a> = Guard<'a, Self>;
411}
412
413unsafe impl CoreArrayProviderInner for TypeLibrary {
414    unsafe fn free(raw: *mut Self::Raw, count: usize, _context: &Self::Context) {
415        BNFreeTypeLibraryList(raw, count)
416    }
417
418    unsafe fn wrap_raw<'a>(raw: &'a Self::Raw, context: &'a Self::Context) -> Self::Wrapped<'a> {
419        Guard::new(Self::from_raw(NonNull::new(*raw).unwrap()), context)
420    }
421}
422
423unsafe impl Send for TypeLibrary {}
424unsafe impl Sync for TypeLibrary {}