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