abi_stable/library/
lib_header.rs

1use super::*;
2
3use crate::{
4    prefix_type::{PrefixRef, PrefixRefTrait},
5    sabi_types::RRef,
6};
7
8/// Used to check the layout of modules returned by module-loading functions
9/// exported by dynamic libraries.
10///
11/// Module-loading functions are declared with the [`export_root_module`] attribute.
12///
13/// [`export_root_module`]: ../attr.export_root_module.html
14#[repr(C)]
15#[derive(StableAbi)]
16pub struct LibHeader {
17    header: AbiHeader,
18    root_mod_consts: RootModuleConsts,
19    init_globals_with: InitGlobalsWith,
20    module: LateStaticRef<PrefixRef<ErasedPrefix>>,
21    constructor: extern "C" fn() -> RootModuleResult,
22}
23
24impl LibHeader {
25    /// Constructs a LibHeader from the root module loader.
26    ///
27    /// # Safety
28    ///
29    /// The `PrefixRef<ErasedPrefix>` returned by the `constructor` function
30    /// must have been transmuted from a `PrefixRef<M>`.
31    pub const unsafe fn from_constructor<M>(
32        constructor: extern "C" fn() -> RootModuleResult,
33        check_layout: CheckTypeLayout,
34    ) -> Self
35    where
36        M: RootModule,
37    {
38        Self {
39            header: AbiHeader::VALUE,
40            root_mod_consts: match check_layout {
41                CheckTypeLayout::Yes => M::CONSTANTS,
42                CheckTypeLayout::No => M::CONSTANTS_NO_ABI_INFO,
43            },
44            init_globals_with: INIT_GLOBALS_WITH,
45            module: LateStaticRef::new(),
46            constructor,
47        }
48    }
49
50    /// Constructs a LibHeader from the module.
51    pub fn from_module<M>(value: M) -> Self
52    where
53        M: RootModule,
54    {
55        Self {
56            header: AbiHeader::VALUE,
57            root_mod_consts: M::CONSTANTS,
58            init_globals_with: INIT_GLOBALS_WITH,
59            module: {
60                let erased = unsafe { value.to_prefix_ref().cast::<ErasedPrefix>() };
61                LateStaticRef::from_prefixref(erased)
62            },
63            constructor: GetAbortingConstructor::aborting_constructor,
64        }
65    }
66
67    /// All the important constants of a `RootModule` for some erased type.
68    pub const fn root_mod_consts(&self) -> &RootModuleConsts {
69        &self.root_mod_consts
70    }
71
72    /// The version string of the library the module is being loaded from.
73    pub const fn version_strings(&self) -> VersionStrings {
74        self.root_mod_consts.version_strings()
75    }
76
77    /// Gets the layout of the root module.
78    ///
79    /// This returns a None if the root module layout is not included
80    /// because the `#[unsafe_no_layout_constant]`
81    /// helper attribute was used on the function exporting the root module.
82    pub const fn layout(&self) -> Option<&'static TypeLayout> {
83        self.root_mod_consts.layout().into_option()
84    }
85
86    pub(super) fn initialize_library_globals(&self, globals: &'static Globals) {
87        (self.init_globals_with.0)(globals);
88    }
89
90    fn check_version<M>(&self) -> Result<(), LibraryError>
91    where
92        M: RootModule,
93    {
94        let expected_version = M::VERSION_STRINGS.piped(VersionNumber::new)?;
95
96        let actual_version = self.version_strings().piped(VersionNumber::new)?;
97
98        if expected_version.major != actual_version.major
99            || (expected_version.major == 0) && expected_version.minor > actual_version.minor
100        {
101            return Err(LibraryError::IncompatibleVersionNumber {
102                library_name: M::NAME,
103                expected_version,
104                actual_version,
105            });
106        }
107        Ok(())
108    }
109
110    /// Checks that the library is compatible, returning the root module on success.
111    ///
112    /// It checks that these are compatible:
113    ///
114    /// - The version number of the library
115    ///
116    /// - The layout of the root module.
117    ///
118    /// # Warning
119    ///
120    /// If this function is called within a dynamic library,
121    /// it must be called at or after the function that exports its root module is called.
122    ///
123    /// **DO NOT** call this in the static initializer of a dynamic library,
124    /// since this library relies on setting up its global state before
125    /// calling the root module loader.
126    ///
127    /// # Errors
128    ///
129    /// This returns these errors:
130    ///
131    /// - `LibraryError::ParseVersionError`:
132    /// If the version strings in the library can't be parsed as version numbers,
133    /// this can only happen if the version strings are manually constructed.
134    ///
135    /// - `LibraryError::IncompatibleVersionNumber`:
136    /// If the version number of the library is incompatible.
137    ///
138    /// - `LibraryError::AbiInstability`:
139    /// If the layout of the root module is not the expected one.
140    ///
141    /// - `LibraryError::RootModule` :
142    /// If the root module initializer returned an error or panicked.
143    ///
144    ///
145    pub fn init_root_module<M>(&self) -> Result<M, LibraryError>
146    where
147        M: RootModule,
148    {
149        self.check_version::<M>()?;
150        self.check_layout::<M>()
151    }
152
153    /// Checks that the version number of the library is compatible,
154    /// returning the root module on success.
155    ///
156    /// This function transmutes the root module type,
157    /// without checking that the layout is compatible first.
158    ///
159    /// # Warning
160    ///
161    /// If this function is called within a dynamic library,
162    /// it must be called at or after the function that exports its root module is called.
163    ///
164    /// **DO NOT** call this in the static initializer of a dynamic library,
165    /// since this library relies on setting up its global state before
166    /// calling the root module loader.
167    ///
168    /// # Safety
169    ///
170    /// The caller must ensure that `M` has the expected layout.
171    ///
172    /// # Errors
173    ///
174    /// This returns these errors:
175    ///
176    /// - `LibraryError::ParseVersionError`:
177    /// If the version strings in the library can't be parsed as version numbers,
178    /// this can only happen if the version strings are manually constructed.
179    ///
180    /// - `LibraryError::IncompatibleVersionNumber`:
181    /// If the version number of the library is incompatible.
182    ///
183    /// - `LibraryError::RootModule` :
184    /// If the root module initializer returned an error or panicked.
185    ///
186    ///
187    pub unsafe fn init_root_module_with_unchecked_layout<M>(&self) -> Result<M, LibraryError>
188    where
189        M: RootModule,
190    {
191        self.check_version::<M>()?;
192        unsafe { self.unchecked_layout() }.map_err(RootModuleError::into_library_error::<M>)
193    }
194
195    /// Checks that the layout of the `M` root module from the dynamic library is
196    /// compatible with the expected layout.
197    ///     
198    /// # Errors
199    ///
200    /// This returns these errors:
201    ///
202    /// - `LibraryError::AbiInstability`:
203    /// If the layout of the root module is not the expected one.
204    ///
205    /// - `LibraryError::RootModule` :
206    /// If the root module initializer returned an error or panicked.
207    ///
208    pub fn ensure_layout<M>(&self) -> Result<(), LibraryError>
209    where
210        M: RootModule,
211    {
212        if let IsLayoutChecked::Yes(root_mod_layout) = self.root_mod_consts.layout() {
213            // Using this instead of
214            // crate::abi_stability::abi_checking::check_layout_compatibility
215            // so that if this is called in a dynamic-library that loads
216            // another dynamic-library,
217            // it uses the layout checker of the executable,
218            // ensuring a globally unique view of the layout of types.
219            //
220            // This might also reduce the code in the library,
221            // because it doesn't have to compile the layout checker for every library.
222            (globals::initialized_globals().layout_checking)(<M>::LAYOUT, root_mod_layout)
223                .into_result()
224                .map_err(|e| {
225                    // Fixes the bug where printing the error causes a segfault because it
226                    // contains static references and function pointers into the unloaded library.
227                    //
228                    // This isn't strictly required anymore because abi_stable doesn't
229                    // unload libraries right now.
230                    let formatted = e.to_formatted_error();
231                    LibraryError::AbiInstability(formatted)
232                })?;
233        }
234
235        atomic::compiler_fence(atomic::Ordering::SeqCst);
236
237        Ok(())
238    }
239
240    /// Gets the root module,first
241    /// checking that the layout of the `M` from the dynamic library is
242    /// compatible with the expected layout.
243    ///     
244    /// # Errors
245    ///
246    /// This returns these errors:
247    ///
248    /// - `LibraryError::AbiInstability`:
249    /// If the layout of the root module is not the expected one.
250    ///
251    /// - `LibraryError::RootModule` :
252    /// If the root module initializer returned an error or panicked.
253    ///
254    pub fn check_layout<M>(&self) -> Result<M, LibraryError>
255    where
256        M: RootModule,
257    {
258        self.ensure_layout::<M>()?;
259
260        unsafe {
261            self.unchecked_layout()
262                .map_err(RootModuleError::into_library_error::<M>)
263        }
264    }
265
266    /// Gets the root module without checking that the layout of `M` is the expected one.
267    /// This is effectively a transmute.
268    ///
269    /// This is useful if a user keeps a cache of which dynamic libraries
270    /// have been checked for layout compatibility.
271    ///
272    /// # Safety
273    ///
274    /// The caller must ensure that `M` has the expected layout.
275    ///
276    /// # Errors
277    ///
278    /// This function can return a `RootModuleError`
279    /// because the root module failed to initialize.
280    ///
281    pub unsafe fn unchecked_layout<M>(&self) -> Result<M, RootModuleError>
282    where
283        M: PrefixRefTrait,
284    {
285        let reff = self
286            .module
287            .try_init(|| (self.constructor)().into_result())
288            .map_err(|mut err| {
289                // Making sure that the error doesn't contain references into
290                // the unloaded library.
291                //
292                // This isn't strictly required anymore because abi_stable doesn't
293                // unload libraries right now.
294                err.reallocate();
295                err
296            })?;
297        unsafe { Ok(M::from_prefix_ref(reff.cast::<M::PrefixFields>())) }
298    }
299}
300
301//////////////////////////////////////////////////////////////////////
302
303struct GetAbortingConstructor<T>(T);
304
305impl<T> GetAbortingConstructor<T> {
306    extern "C" fn aborting_constructor() -> T {
307        extern_fn_panic_handling! {
308            panic!(
309                "BUG:\n\
310                 This function \
311                 (abi_stable::library::lib_header::GetAbortingConstructor::aborting_constructor) \
312                 must only be used \
313                 as a dummy functions when initializing `LibHeader` \
314                 within `LibHeader::from_module`."
315            );
316        }
317    }
318}
319
320//////////////////////////////////////////////////////////////////////
321
322#[repr(C)]
323#[derive(StableAbi, Copy, Clone)]
324struct InitGlobalsWith(pub extern "C" fn(&'static Globals));
325
326const INIT_GLOBALS_WITH: InitGlobalsWith = InitGlobalsWith(crate::globals::initialize_globals_with);
327
328//////////////////////////////////////////////////////////////////////
329
330/// A handle to the [`AbiHeader`] of a library.
331///
332/// This can be dereferenced into the `AbiHeader`,
333/// and [fallibly upgraded](#method.upgrade) into a `&'static LibHeader`.
334///
335/// [`AbiHeader`]: ./struct.AbiHeader.html
336#[repr(transparent)]
337#[derive(Debug, StableAbi, Copy, Clone)]
338pub struct AbiHeaderRef(pub(super) RRef<'static, AbiHeader>);
339
340impl std::ops::Deref for AbiHeaderRef {
341    type Target = AbiHeader;
342
343    fn deref(&self) -> &AbiHeader {
344        self.0.get()
345    }
346}
347
348/// Represents the abi_stable version used by a compiled dynamic library,
349/// which if incompatible would produce a [`LibraryError::InvalidAbiHeader`]
350///
351/// [`LibraryError::InvalidAbiHeader`]: ./enum.LibraryError.html#variant.InvalidAbiHeader
352#[repr(C)]
353#[derive(Debug, StableAbi, Copy, Clone)]
354// This type will never have new fields clippy, that's the point <_<
355#[allow(clippy::manual_non_exhaustive)]
356pub struct AbiHeader {
357    /// A magic string used to check that this is actually abi_stable.
358    pub magic_string: [u8; 32],
359    /// The major abi version of abi_stable
360    pub abi_major: u32,
361    /// The minor abi version of abi_stable
362    pub abi_minor: u32,
363    _priv: (),
364}
365
366impl AbiHeader {
367    /// The value of the AbiHeader stored in dynamic libraries that use this
368    /// version of abi_stable
369    pub const VALUE: AbiHeader = {
370        mod value {
371            use super::*;
372            abi_stable_derive::construct_abi_header! {}
373        }
374        value::ABI_HEADER
375    };
376}
377
378impl AbiHeader {
379    /// Checks whether this AbiHeader is compatible with `other`.
380    pub fn is_compatible(&self, other: &Self) -> bool {
381        self.magic_string == other.magic_string
382            && self.abi_major == other.abi_major
383            && (self.abi_major != 0 || self.abi_minor == other.abi_minor)
384    }
385
386    /// Checks whether the abi_stable version of this AbiHeader is
387    /// compatible with the one from where this function is called.
388    pub fn is_valid(&self) -> bool {
389        self.is_compatible(&AbiHeader::VALUE)
390    }
391}
392
393impl AbiHeaderRef {
394    /// Gets the LibHeader of a library.
395    ///
396    /// # Errors
397    ///
398    /// This returns these errors:
399    ///
400    /// - `LibraryError::InvalidAbiHeader`:
401    /// If the abi_stable used by the library is not compatible.
402    ///
403    /// - `LibraryError::InvalidCAbi`:
404    /// If the C abi used by the library is not compatible.
405    pub fn upgrade(self) -> Result<&'static LibHeader, LibraryError> {
406        if !self.is_valid() {
407            return Err(LibraryError::InvalidAbiHeader(*self));
408        }
409
410        let lib_header: &'static LibHeader = unsafe { self.0.transmute_into_ref() };
411
412        let c_abi_testing_fns = lib_header.root_mod_consts().c_abi_testing_fns();
413        crate::library::c_abi_testing::run_tests(c_abi_testing_fns)?;
414
415        let globals = globals::initialized_globals();
416
417        lib_header.initialize_library_globals(globals);
418
419        Ok(lib_header)
420    }
421}