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}