abi_stable/library/
root_mod_trait.rs

1use super::*;
2
3use crate::{prefix_type::PrefixRefTrait, utils::leak_value};
4
5/// The root module of a dynamic library,
6/// which may contain other modules,function pointers,and static references.
7///
8///
9/// # Examples
10///
11/// For a more in-context example of a type implementing this trait you can look
12/// at either the example in the readme for this crate,
13/// or the `example/example_*_interface` crates  in this crates' repository .
14///
15/// ### Basic
16///
17/// ```rust
18/// use abi_stable::{library::RootModule, sabi_types::VersionStrings, StableAbi};
19///
20/// #[repr(C)]
21/// #[derive(StableAbi)]
22/// #[sabi(kind(Prefix(prefix_ref = Module_Ref, prefix_fields = Module_Prefix)))]
23/// pub struct Module {
24///     pub first: u8,
25///     // The `#[sabi(last_prefix_field)]` attribute here means that this is
26///     // the last field in this module that was defined in the
27///     // first compatible version of the library,
28///     #[sabi(last_prefix_field)]
29///     pub second: u16,
30///     pub third: u32,
31/// }
32/// impl RootModule for Module_Ref {
33///     abi_stable::declare_root_module_statics! {Module_Ref}
34///     const BASE_NAME: &'static str = "example_root_module";
35///     const NAME: &'static str = "example_root_module";
36///     const VERSION_STRINGS: VersionStrings = abi_stable::package_version_strings!();
37/// }
38///
39
40/// # fn main(){}
41/// ```
42pub trait RootModule: Sized + StableAbi + PrefixRefTrait + 'static {
43    /// The name of the dynamic library,which is the same on all platforms.
44    /// This is generally the name of the `implementation crate`.
45    const BASE_NAME: &'static str;
46
47    /// The name of the library used in error messages.
48    const NAME: &'static str;
49
50    /// The version number of the library that this is a root module of.
51    ///
52    /// Initialize this with
53    /// [`package_version_strings!()`](../macro.package_version_strings.html)
54    const VERSION_STRINGS: VersionStrings;
55
56    /// All the constants of this trait and supertraits.
57    ///
58    /// It can safely be used as a proxy for the associated constants of this trait.
59    const CONSTANTS: RootModuleConsts = RootModuleConsts {
60        base_name: RStr::from_str(Self::BASE_NAME),
61        name: RStr::from_str(Self::NAME),
62        version_strings: Self::VERSION_STRINGS,
63        layout: IsLayoutChecked::Yes(<Self as StableAbi>::LAYOUT),
64        c_abi_testing_fns: crate::library::c_abi_testing::C_ABI_TESTING_FNS,
65        _priv: (),
66    };
67
68    /// Like `Self::CONSTANTS`,
69    /// except without including the type layout constant for the root module.
70    const CONSTANTS_NO_ABI_INFO: RootModuleConsts = RootModuleConsts {
71        layout: IsLayoutChecked::No,
72        ..Self::CONSTANTS
73    };
74
75    /// Gets the statics for Self.
76    ///
77    /// To define this associated function use:
78    /// [`abi_stable::declare_root_module_statics!{TypeOfSelf}`
79    /// ](../macro.declare_root_module_statics.html).
80    /// Passing `Self` instead of `TypeOfSelf` won't work.
81    ///
82    fn root_module_statics() -> &'static RootModuleStatics<Self>;
83
84    /// Gets the root module,returning None if the module is not yet loaded.
85    #[inline]
86    fn get_module() -> Option<Self> {
87        Self::root_module_statics().root_mod.get()
88    }
89
90    /// Gets the RawLibrary of the module,
91    /// returning None if the dynamic library failed to load
92    /// (it doesn't exist or layout checking failed).
93    ///
94    /// Note that if the root module is initialized using `Self::load_module_with`,
95    /// this will return None even though `Self::get_module` does not.
96    ///
97    #[inline]
98    fn get_raw_library() -> Option<&'static RawLibrary> {
99        Self::root_module_statics().raw_lib.get()
100    }
101
102    /// Returns the path the library would be loaded from,given a directory(folder).
103    fn get_library_path(directory: &Path) -> PathBuf {
104        let base_name = Self::BASE_NAME;
105        RawLibrary::path_in_directory(directory, base_name, LibrarySuffix::NoSuffix)
106    }
107
108    /// Loads the root module,with a closure which either
109    /// returns the root module or an error.
110    ///
111    /// If the root module was already loaded,
112    /// this will return the already loaded root module,
113    /// without calling the closure.
114    fn load_module_with<F, E>(f: F) -> Result<Self, E>
115    where
116        F: FnOnce() -> Result<Self, E>,
117    {
118        Self::root_module_statics().root_mod.try_init(f)
119    }
120
121    /// Loads this module from the path specified by `where_`,
122    /// first loading the dynamic library if it wasn't already loaded.
123    ///
124    /// Once the root module is loaded,
125    /// this will return the already loaded root module.
126    ///
127    /// # Warning
128    ///
129    /// If this function is called within a dynamic library,
130    /// it must be called either within the root module loader function or
131    /// after that function has been called.
132    ///
133    /// **DO NOT** call this in the static initializer of a dynamic library,
134    /// since this library relies on setting up its global state before
135    /// calling the root module loader.
136    ///
137    /// # Errors
138    ///
139    /// This will return these errors:
140    ///
141    /// - `LibraryError::OpenError`:
142    /// If the dynamic library itself could not be loaded.
143    ///
144    /// - `LibraryError::GetSymbolError`:
145    /// If the root module was not exported.
146    ///
147    /// - `LibraryError::InvalidAbiHeader`:
148    /// If the abi_stable version used by the library is not compatible.
149    ///
150    /// - `LibraryError::ParseVersionError`:
151    /// If the version strings in the library can't be parsed as version numbers,
152    /// this can only happen if the version strings are manually constructed.
153    ///
154    /// - `LibraryError::IncompatibleVersionNumber`:
155    /// If the version number of the library is incompatible.
156    ///
157    /// - `LibraryError::AbiInstability`:
158    /// If the layout of the root module is not the expected one.
159    ///
160    /// - `LibraryError::RootModule` :
161    /// If the root module initializer returned an error or panicked.
162    ///
163    fn load_from(where_: LibraryPath<'_>) -> Result<Self, LibraryError> {
164        let statics = Self::root_module_statics();
165        statics.root_mod.try_init(|| {
166            let lib = statics.raw_lib.try_init(|| -> Result<_, LibraryError> {
167                let raw_library = load_raw_library::<Self>(where_)?;
168
169                // if the library isn't leaked
170                // it would cause any use of the module to be a use after free.
171                //
172                // By leaking the library
173                // this allows the root module loader to do anything that'd prevent
174                // sound library unloading.
175                Ok(leak_value(raw_library))
176            })?;
177            let items = unsafe { lib_header_from_raw_library(lib)? };
178
179            items.ensure_layout::<Self>()?;
180
181            // safety: the layout was checked in the code above,
182            unsafe {
183                items
184                    .init_root_module_with_unchecked_layout::<Self>()?
185                    .initialization()
186            }
187        })
188    }
189
190    /// Loads this module from the directory specified by `where_`,
191    /// first loading the dynamic library if it wasn't already loaded.
192    ///
193    /// Once the root module is loaded,
194    /// this will return the already loaded root module.
195    ///
196    /// Warnings and Errors are detailed in [`load_from`](#method.load_from),
197    ///
198    fn load_from_directory(where_: &Path) -> Result<Self, LibraryError> {
199        Self::load_from(LibraryPath::Directory(where_))
200    }
201
202    /// Loads this module from the file at `path_`,
203    /// first loading the dynamic library if it wasn't already loaded.
204    ///
205    /// Once the root module is loaded,
206    /// this will return the already loaded root module.
207    ///
208    /// Warnings and Errors are detailed in [`load_from`](#method.load_from),
209    ///
210    fn load_from_file(path_: &Path) -> Result<Self, LibraryError> {
211        Self::load_from(LibraryPath::FullPath(path_))
212    }
213
214    /// Defines behavior that happens once the module is loaded.
215    ///
216    /// This is ran in the `RootModule::load*` associated functions
217    /// after the root module has succesfully been loaded.
218    ///
219    /// The default implementation does nothing.
220    fn initialization(self) -> Result<Self, LibraryError> {
221        Ok(self)
222    }
223}
224
225/// Loads the raw library at `where_`
226fn load_raw_library<M>(where_: LibraryPath<'_>) -> Result<RawLibrary, LibraryError>
227where
228    M: RootModule,
229{
230    let path = match where_ {
231        LibraryPath::Directory(directory) => M::get_library_path(directory),
232        LibraryPath::FullPath(full_path) => full_path.to_owned(),
233    };
234    RawLibrary::load_at(&path)
235}
236
237/// Gets the LibHeader of a library.
238///
239/// # Errors
240///
241/// This will return these errors:
242///
243/// - `LibraryError::GetSymbolError`:
244/// If the root module was not exported.
245///
246/// - `LibraryError::InvalidAbiHeader`:
247/// If the abi_stable used by the library is not compatible.
248///
249/// # Safety
250///
251/// The LibHeader is implicitly tied to the lifetime of the library,
252/// it will contain dangling `'static` references if the library is dropped before it does.
253///
254///
255pub unsafe fn lib_header_from_raw_library(
256    raw_library: &RawLibrary,
257) -> Result<&'static LibHeader, LibraryError> {
258    unsafe { abi_header_from_raw_library(raw_library)?.upgrade() }
259}
260
261/// Gets the AbiHeaderRef of a library.
262///
263/// # Errors
264///
265/// This will return these errors:
266///
267/// - `LibraryError::GetSymbolError`:
268/// If the root module was not exported.
269///
270/// # Safety
271///
272/// The AbiHeaderRef is implicitly tied to the lifetime of the library,
273/// it will contain dangling `'static` references if the library is dropped before it does.
274///
275///
276pub unsafe fn abi_header_from_raw_library(
277    raw_library: &RawLibrary,
278) -> Result<AbiHeaderRef, LibraryError> {
279    let mangled = ROOT_MODULE_LOADER_NAME_WITH_NUL;
280    let header: AbiHeaderRef = unsafe { *raw_library.get::<AbiHeaderRef>(mangled.as_bytes())? };
281
282    Ok(header)
283}
284
285/// Gets the LibHeader of the library at the path.
286///
287/// This leaks the underlying dynamic library,
288/// if you need to do this without leaking you'll need to use
289/// `lib_header_from_raw_library` instead.
290///
291/// # Errors
292///
293/// This will return these errors:
294///
295/// - `LibraryError::OpenError`:
296/// If the dynamic library itself could not be loaded.
297///
298/// - `LibraryError::GetSymbolError`:
299/// If the root module was not exported.
300///
301/// - `LibraryError::InvalidAbiHeader`:
302/// If the abi_stable version used by the library is not compatible.
303///
304///
305pub fn lib_header_from_path(path: &Path) -> Result<&'static LibHeader, LibraryError> {
306    let raw_lib = RawLibrary::load_at(path)?;
307
308    let library_getter = unsafe { lib_header_from_raw_library(&raw_lib)? };
309
310    mem::forget(raw_lib);
311
312    Ok(library_getter)
313}
314
315/// Gets the AbiHeaderRef of the library at the path.
316///
317/// This leaks the underlying dynamic library,
318/// if you need to do this without leaking you'll need to use
319/// `lib_header_from_raw_library` instead.
320///
321/// # Errors
322///
323/// This will return these errors:
324///
325/// - `LibraryError::OpenError`:
326/// If the dynamic library itself could not be loaded.
327///
328/// - `LibraryError::GetSymbolError`:
329/// If the root module was not exported.
330///
331///
332pub fn abi_header_from_path(path: &Path) -> Result<AbiHeaderRef, LibraryError> {
333    let raw_lib = RawLibrary::load_at(path)?;
334
335    let library_getter = unsafe { abi_header_from_raw_library(&raw_lib)? };
336
337    mem::forget(raw_lib);
338
339    Ok(library_getter)
340}
341
342//////////////////////////////////////////////////////////////////////
343
344macro_rules! declare_root_module_consts {
345    (
346        fields=[
347            $(
348                $(#[$field_meta:meta])*
349                method_docs=$method_docs:expr,
350                $field:ident : $field_ty:ty
351            ),* $(,)*
352        ]
353    ) => (
354        /// All the constants of the [`RootModule`] trait for some erased type.
355        ///
356        /// [`RootModule`]: ./trait.RootModule.html
357        #[repr(C)]
358        #[derive(StableAbi,Copy,Clone)]
359        pub struct RootModuleConsts{
360            $(
361                $(#[$field_meta])*
362                $field : $field_ty,
363            )*
364            _priv:(),
365        }
366
367        impl RootModuleConsts{
368            $(
369                #[doc=$method_docs]
370                pub const fn $field(&self)->$field_ty{
371                    self.$field
372                }
373            )*
374        }
375
376    )
377}
378
379declare_root_module_consts! {
380    fields=[
381        method_docs="
382         The name of the dynamic library,which is the same on all platforms.
383         This is generally the name of the implementation crate.",
384        base_name: RStr<'static>,
385
386        method_docs="The name of the library used in error messages.",
387        name: RStr<'static>,
388
389        method_docs="The version number of the library this was created from.",
390        version_strings: VersionStrings,
391
392        method_docs="The (optional) type layout constant of the root module.",
393        layout: IsLayoutChecked,
394
395        method_docs="\
396         Functions used to test that the C abi is the same in both the library 
397         and the loader\
398        ",
399        c_abi_testing_fns:&'static CAbiTestingFns,
400    ]
401}