From 1774322f1d816a3baa72865f7ba205c2fbc88f2d Mon Sep 17 00:00:00 2001 From: Simonas Kazlauskas Date: Sat, 6 Feb 2021 17:54:10 +0200 Subject: [PATCH] Review documentation --- src/changelog.rs | 92 +++++++++++++++++++++++++++++++--------- src/lib.rs | 98 +++++++++++++++++++------------------------ src/os/mod.rs | 2 +- src/os/unix/mod.rs | 66 ++++++++++++++++------------- src/os/windows/mod.rs | 33 +++++++++------ 5 files changed, 175 insertions(+), 116 deletions(-) diff --git a/src/changelog.rs b/src/changelog.rs index 71d0857..c927551 100644 --- a/src/changelog.rs +++ b/src/changelog.rs @@ -1,29 +1,79 @@ -//! Project changelog +//! The change log. /// Release 0.7.0 (2021-01-31) /// /// ## Breaking changes /// -/// Most importantly, a number of associated methods involved in loading a library have been marked -/// `unsafe`. As described in the [issue #86], when loading a library the loader will execute code -/// that's part of the dynamic library initialization routines. This is effectively equivalent to -/// calling an arbitrary set of FFI functions and is, therefore, `unsafe`. The affected functions -/// are: [`Library::new`], [`os::unix::Library::new`], [`os::unix::Library::open`], -/// [`os::windows::Library::new`], [`os::windows::Library::load_with_flags`]. +/// ### Loading functions are now `unsafe` /// -/// `Error::LoadLibraryW` renamed to [`Error::LoadLibraryExW`] to more accurately represent the -/// underlying API that's failing. +/// A number of associated methods involved in loading a library were changed to +/// be `unsafe`. The affected functions are: [`Library::new`], [`os::unix::Library::new`], +/// [`os::unix::Library::open`], [`os::windows::Library::new`], +/// [`os::windows::Library::load_with_flags`]. This is the most prominent breaking change in this +/// release and affects majority of the users of `libloading`. +/// +/// In order to see why it was necessary, consider the following snippet of C++ code: +/// +/// ```c++ +/// #include +/// #include +/// +/// static std::vector UNSHUU = { 1, 2, 3 }; +/// +/// int main() { +/// std::cout << UNSHUU[0] << UNSHUU[1] << UNSHUU[2] << std::endl; // Prints 123 +/// return 0; +/// } +/// ``` +/// +/// The `std::vector` type, much like in Rust's `Vec`, stores its contents in a buffer allocated on +/// the heap. In this example the vector object itself is stored and initialized as a static +/// variable – a compile time construct. The heap, on the other hand, is a runtime construct. And +/// yet the code works exactly as you'd expect – the vector contains numbers 1, 2 and 3 stored in +/// a buffer on heap. So, _what_ makes it work out, exactly? +/// +/// Various executable and shared library formats define conventions and machinery to execute +/// arbitrary code when a program or a shared library is loaded. On systems using the PE format +/// (e.g. Windows) this is available via the optional `DllMain` initializer. Various systems +/// utilizing the ELF format take a sightly different approach of maintaining an array of function +/// pointers in the `.init_array` section. A very similar mechanism exists on systems that utilize +/// the Mach-O format. +/// +/// For the C++ program above, the object stored in the `UNSHUU` global variable is constructed +/// by code run as part of such an initializer routine. This initializer is run before the entry +/// point (the `main` function) is executed, allowing for this magical behaviour to be possible. +/// Were the C++ code built as a shared library instead, the initialization routines would run as +/// the resulting shared library is loaded. In case of `libloading` – during the call to +/// `Library::new` and other methods affected by this change. +/// +/// These initialization (and very closely related termination) routines can be utilized outside of +/// C++ too. Anybody can build a shared library in variety of different programming languages and +/// set up the initializers to execute arbitrary code. Potentially code that does all sorts of +/// wildly unsound stuff. +/// +/// The routines are executed by components that are an integral part of the operating system. +/// Changing or controlling the operation of these components is infeasible. With that in +/// mind, the initializer and termination routines are something anybody loading a library must +/// carefully evaluate the libraries loaded for soundness. +/// +/// In practice, a vast majority of the libraries can be considered a good citizen and their +/// initialization and termination routines, if they have any at all, can be trusted to be sound. +/// +/// Also see: [issue #86]. +/// +/// ### Better & more consistent default behaviour on UNIX systems /// /// On UNIX systems the [`Library::new`], [`os::unix::Library::new`] and /// [`os::unix::Library::this`] methods have been changed to use -/// [`RTLD_LAZY`] | [`RTLD_LOCAL`] as the default set of loader options. This has a -/// couple benefits. Namely: +/// [RTLD_LAZY] | [RTLD_LOCAL] as the default set of loader options (previously: +/// [`RTLD_NOW`]). This has a couple benefits. Namely: /// /// * Lazy binding is generally quicker to execute when only a subset of symbols from a library are -/// used and is typically the default when neither `RTLD_LAZY` nor `RTLD_NOW` are specified; -/// * On most UNIX systems (macOS being a notable exception) [`RTLD_LOCAL`] is the default when -/// neither `RTLD_LOCAL` nor [`RTLD_GLOBAL`] are specified. Setting the `RTLD_LOCAL` flag makes -/// the behaviour more consistent across platforms. +/// used and is typically the default when neither `RTLD_LAZY` nor `RTLD_NOW` are specified when +/// calling the underlying `dlopen` API; +/// * On most UNIX systems (macOS being a notable exception) `RTLD_LOCAL` is the default when +/// neither `RTLD_LOCAL` nor [`RTLD_GLOBAL`] are specified. The explicit setting of the +/// `RTLD_LOCAL` flag makes this behaviour consistent across platforms. /// /// ### Dropped support for Windows XP/Vista /// @@ -32,10 +82,14 @@ /// project](https://github.com/rust-lang/compiler-team/issues/378) but also as an acknowledgement /// to the fact that `libloading` never worked in these environments anyway. /// +/// ### More accurate error variant names +/// +/// Finally, the `Error::LoadLibraryW` renamed to [`Error::LoadLibraryExW`] to more accurately +/// represent the underlying API that's failing. No functional changes as part of this rename +/// intended. +/// /// [issue #86]: https://github.com/nagisa/rust_libloading/issues/86 /// [`Library::new`]: crate::Library::new -/// [windows_open]: crate::os::windows::Library::load_with_flags -/// [unix_open]: crate::os::unix::Library::open /// [`Error::LoadLibraryExW`]: crate::Error::LoadLibraryExW /// [`os::unix::Library::this`]: crate::os::unix::Library::this /// [`os::unix::Library::new`]: crate::os::unix::Library::new @@ -43,8 +97,8 @@ /// [`os::windows::Library::new`]: crate::os::windows::Library::new /// [`os::windows::Library::load_with_flags`]: crate::os::windows::Library::load_with_flags /// [`RTLD_NOW`]: crate::os::unix::RTLD_NOW -/// [`RTLD_LAZY`]: crate::os::unix::RTLD_LAZY -/// [`RTLD_LOCAL`]: crate::os::unix::RTLD_LOCAL +/// [RTLD_LAZY]: crate::os::unix::RTLD_LAZY +/// [RTLD_LOCAL]: crate::os::unix::RTLD_LOCAL /// [`RTLD_GLOBAL`]: crate::os::unix::RTLD_GLOBAL pub mod r0_7_0 {} diff --git a/src/lib.rs b/src/lib.rs index 55811f6..9a531f1 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,15 +1,14 @@ //! A memory-safer wrapper around system dynamic library loading primitives. //! //! Using this library allows loading [dynamic libraries](struct.Library.html) (also known as -//! shared libraries) as well as use functions and static variables these libraries contain. +//! shared libraries) and use functions & global variables contained within the libraries. //! -//! While the library does expose a cross-platform interface to load a library and find stuff -//! inside it, little is done to paper over the platform differences, especially where library -//! loading is involved. The documentation for each function will attempt to document such -//! differences on the best-effort basis. +//! `libloading` crate exposes a cross-platform interface to load a library and utilize its +//! contents, but little is done to paper over the differences in behaviour between different +//! platforms. The API documentation strives to document such differences on the best-effort basis. //! -//! Less safe, platform specific bindings are also available. See the -//! [`os::platform`](os/index.html) module for details. +//! Platform specific APIs are also available in the [`os`](crate::os) module. These APIs are more +//! flexible but less safe. //! //! # Usage //! @@ -17,25 +16,23 @@ //! //! ```toml //! [dependencies] -//! libloading = "0.6" +//! libloading = "0.7" //! ``` //! -//! Then inside your project +//! Then inside your code: //! //! ```no_run -//! extern crate libloading as lib; -//! //! fn call_dynamic() -> Result> { //! unsafe { -//! let lib = lib::Library::new("/path/to/liblibrary.so")?; -//! let func: lib::Symbol u32> = lib.get(b"my_func")?; +//! let lib = libloading::Library::new("/path/to/liblibrary.so")?; +//! let func: libloading::Symbol u32> = lib.get(b"my_func")?; //! Ok(func()) //! } //! } //! ``` //! //! The compiler will ensure that the loaded `function` will not outlive the `Library` it comes -//! from, preventing a common cause of undefined behaviour and memory safety problems. +//! from, preventing a common class of issues. #![deny( missing_docs, clippy::all, @@ -76,24 +73,24 @@ impl Library { /// /// # Safety /// - /// When a library is loaded initializers contained within the library are executed. For the - /// purposes of soundness, execution of these initializers is conceptually the same calling a - /// FFI function and may impose whatever requirements on the caller. + /// When a library is loaded initialization routines contained within the library are executed. + /// For the purposes of safety, execution of these routines is conceptually the same calling an + /// unknown foreign function and may impose arbitrary requirements on the caller for the call + /// to be sound. + /// + /// Additionally, the callers of this function must also ensure that execution of the + /// termination routines contained within the library is safe as well. These routines may be + /// executed when the library is unloaded. /// /// # Thread-safety /// - /// The implementation strives to be as MT-safe as sanely possible, however due to certain - /// error-handling related resources not always being safe, this library is not MT-safe either. + /// The implementation strives to be as MT-safe as sanely possible, however on certain + /// platforms the underlying error-handling related APIs not always MT-safe. This library + /// shares these limitations on those platforms. In particular, on certain UNIX targets + /// `dlerror` is not MT-safe, resulting in garbage error messages in certain MT-scenarios. /// - /// * On Windows Vista and earlier error handling falls back to [`SetErrorMode`], which is not - /// MT-safe. MT-scenarios involving this function may cause a traditional data race; - /// * On some UNIX targets `dlerror` might not be MT-safe, resulting in garbage error messages - /// in certain MT-scenarios. - /// - /// [`SetErrorMode`]: https://msdn.microsoft.com/en-us/library/windows/desktop/ms680621(v=vs.85).aspx - /// - /// Calling this function from multiple threads is not safe if used in conjunction with - /// relative filenames and the library search path is modified (`SetDllDirectory` function on + /// Calling this function from multiple threads is not MT-safe if used in conjunction with + /// library filenames and the library search path is modified (`SetDllDirectory` function on /// Windows, `{DY,}LD_LIBRARY_PATH` environment variable on UNIX). /// /// # Platform-specific behaviour @@ -103,15 +100,8 @@ impl Library { /// the platform specific [`os::unix::Library::new`] and [`os::windows::Library::new`] methods /// for further information on library lookup behaviour. /// - /// ## Windows - /// /// If the `filename` specifies a library filename without path and with extension omitted, - /// `.dll` extension is implicitly added. This behaviour may be suppressed by appending a - /// trailing `.` to the `filename`. - /// - /// If the library contains thread local variables (MSVC’s `_declspec(thread)`, Rust’s - /// `#[thread_local]` attributes), loading the library will fail on versions prior to Windows - /// Vista. + /// `.dll` extension is implicitly added on Windows. /// /// # Tips /// @@ -141,26 +131,25 @@ impl Library { /// Get a pointer to function or static variable by symbol name. /// - /// The `symbol` may not contain any null bytes, with an exception of last byte. A null - /// terminated `symbol` may avoid a string allocation in some cases. + /// The `symbol` may not contain any null bytes, with an exception of last byte. Providing a + /// null terminated `symbol` may help to avoid an allocation. /// /// Symbol is interpreted as-is; no mangling is done. This means that symbols like `x::y` are /// most likely invalid. /// /// # Safety /// - /// Pointer to a value of arbitrary type is returned. Using a value with wrong type is - /// undefined. + /// Users of this API must specify the correct type of the function or variable loaded. Using a + /// `Symbol` with a wrong type is undefined. /// /// # Platform-specific behaviour /// - /// Implementation of thread local variables is extremely platform specific and uses of these - /// variables that work on e.g. Linux may have unintended behaviour on other POSIX systems or - /// Windows. + /// Implementation of thread local variables is extremely platform specific and uses of such + /// variables that work on e.g. Linux may have unintended behaviour on other targets. /// /// On POSIX implementations where the `dlerror` function is not confirmed to be MT-safe (such - /// as FreeBSD), this function will unconditionally return an error the underlying `dlsym` call - /// returns a null pointer. There are rare situations where `dlsym` returns a genuine null + /// as FreeBSD), this function will unconditionally return an error when the underlying `dlsym` + /// call returns a null pointer. There are rare situations where `dlsym` returns a genuine null /// pointer without it being an error. If loading a null pointer is something you care about, /// consider using the [`os::unix::Library::get_singlethreaded`] call. /// @@ -242,15 +231,12 @@ unsafe impl Sync for Library {} /// Symbol from a library. /// /// This type is a safeguard against using dynamically loaded symbols after a `Library` is -/// unloaded. Primary method to create an instance of a `Symbol` is via `Library::get`. +/// unloaded. Primary method to create an instance of a `Symbol` is via [`Library::get`]. /// -/// Due to implementation of the `Deref` trait, an instance of `Symbol` may be used as if it was a -/// function or variable directly, without taking care to “extract” function or variable manually -/// most of the time. +/// The `Deref` trait implementation allows use of `Symbol` as if it was a function or variable +/// itself, without taking care to “extract” function or variable manually most of the time. /// -/// See [`Library::get`] for details. -/// -/// [`Library::get`]: ./struct.Library.html#method.get +/// [`Library::get`]: Library::get pub struct Symbol<'lib, T: 'lib> { inner: imp::Symbol, pd: marker::PhantomData<&'lib T> @@ -282,12 +268,11 @@ impl<'lib, T> Symbol<'lib, T> { /// Wrap the `os::platform::Symbol` into this safe wrapper. /// /// Note that, in order to create association between the symbol and the library this symbol - /// came from, this function requires reference to the library provided. + /// came from, this function requires a reference to the library. /// /// # Safety /// - /// It is invalid to provide a reference to any other value other than the library the `sym` - /// was loaded from. Doing so invalidates any lifetime guarantees. + /// The `library` reference must be exactly the library `sym` was loaded from. /// /// # Examples /// @@ -300,7 +285,8 @@ impl<'lib, T> Symbol<'lib, T> { /// let symbol = Symbol::from_raw(symbol, &lib); /// } /// ``` - pub unsafe fn from_raw(sym: imp::Symbol, _: &'lib L) -> Symbol<'lib, T> { + pub unsafe fn from_raw(sym: imp::Symbol, library: &'lib L) -> Symbol<'lib, T> { + let _ = library; // ignore here for documentation purposes. Symbol { inner: sym, pd: marker::PhantomData diff --git a/src/os/mod.rs b/src/os/mod.rs index 40361c5..c03ebc1 100644 --- a/src/os/mod.rs +++ b/src/os/mod.rs @@ -1,4 +1,4 @@ -//! Unsafe, platform specific bindings to dynamic library loading facilities. +//! Unsafe but flexible platform specific bindings to dynamic library loading facilities. //! //! These modules expose more extensive, powerful, less principled bindings to the dynamic //! library loading facilities. Use of these bindings come at the cost of less (in most cases, diff --git a/src/os/unix/mod.rs b/src/os/unix/mod.rs index 3a679ac..45e8230 100644 --- a/src/os/unix/mod.rs +++ b/src/os/unix/mod.rs @@ -103,21 +103,26 @@ impl Library { /// a file. Otherwise, platform-specific algorithms are employed to find a library with a /// matching file name. /// - /// This is equivalent to [`Library::open`]`(filename, RTLD_LAZY | RTLD_LOCAL)`. + /// This is equivalent to [Library::open](filename, [RTLD_LAZY] | [RTLD_LOCAL]). /// /// [path separator]: std::path::MAIN_SEPARATOR /// /// # Safety /// - /// When a library is loaded initializers contained within the library are executed. For the - /// purposes of soundness, execution of these initializers is conceptually the same calling a - /// FFI function and may impose whatever requirements on the caller. + /// When a library is loaded initialization routines contained within the library are executed. + /// For the purposes of safety, execution of these routines is conceptually the same calling an + /// unknown foreign function and may impose arbitrary requirements on the caller for the call + /// to be sound. + /// + /// Additionally, the callers of this function must also ensure that execution of the + /// termination routines contained within the library is safe as well. These routines may be + /// executed when the library is unloaded. #[inline] pub unsafe fn new>(filename: P) -> Result { Library::open(Some(filename), RTLD_LAZY | RTLD_LOCAL) } - /// Eagerly load the `Library` representing the current executable. + /// Load the `Library` representing the current executable. /// /// [`Library::get`] calls of the returned `Library` will look for symbols in following /// locations in order: @@ -130,13 +135,14 @@ impl Library { /// Note that behaviour of `Library` loaded with this method is different from /// Libraries loaded with [`os::windows::Library::this`]. /// - /// This is equivalent to [`Library::open`]`(None, RTLD_LAZY | RTLD_LOCAL)`. + /// This is equivalent to [Library::open](None, [RTLD_LAZY] | [RTLD_LOCAL]). /// /// [`os::windows::Library::this`]: crate::os::windows::Library::this #[inline] pub fn this() -> Library { unsafe { - // SAFE: this obtains a handle to an existing, loaded module, no danger in + // SAFE: this does not load any new shared library images, no danger in it executing + // initializer routines. Library::open(None::<&OsStr>, RTLD_LAZY | RTLD_LOCAL).expect("this should never fail") } } @@ -150,9 +156,14 @@ impl Library { /// /// # Safety /// - /// When a library is loaded initializers contained within the library are executed. For the - /// purposes of soundness, execution of these initializers is conceptually the same calling a - /// FFI function and may impose whatever requirements on the caller. + /// When a library is loaded initialization routines contained within the library are executed. + /// For the purposes of safety, execution of these routines is conceptually the same calling an + /// unknown foreign function and may impose arbitrary requirements on the caller for the call + /// to be sound. + /// + /// Additionally, the callers of this function must also ensure that execution of the + /// termination routines contained within the library is safe as well. These routines may be + /// executed when the library is unloaded. pub unsafe fn open

(filename: Option

, flags: raw::c_int) -> Result where P: AsRef { let filename = match filename { @@ -208,28 +219,27 @@ impl Library { /// Get a pointer to function or static variable by symbol name. /// - /// The `symbol` may not contain any null bytes, with an exception of last byte. A null - /// terminated `symbol` may avoid an allocation in some cases. + /// The `symbol` may not contain any null bytes, with an exception of last byte. Providing a + /// null terminated `symbol` may help to avoid an allocation. /// /// Symbol is interpreted as-is; no mangling is done. This means that symbols like `x::y` are /// most likely invalid. /// /// # Safety /// - /// This function does not validate the type `T`. It is up to the user of this function to - /// ensure that the loaded symbol is in fact a `T`. Using a value with a wrong type has no - /// defined behaviour. + /// Users of this API must specify the correct type of the function or variable loaded. Using a + /// `Symbol` with a wrong type is undefined. /// /// # Platform-specific behaviour /// - /// Implementation of thread local variables is extremely platform specific and uses of these - /// variables that work on e.g. Linux may have unintended behaviour on other POSIX systems. + /// Implementation of thread local variables is extremely platform specific and uses of such + /// variables that work on e.g. Linux may have unintended behaviour on other targets. /// /// On POSIX implementations where the `dlerror` function is not confirmed to be MT-safe (such /// as FreeBSD), this function will unconditionally return an error when the underlying `dlsym` /// call returns a null pointer. There are rare situations where `dlsym` returns a genuine null - /// pointer without it being an error. If loading a symbol at null address is something you - /// care about, consider using the [`Library::get_singlethreaded`] call. + /// pointer without it being an error. If loading a null pointer is something you care about, + /// consider using the [`Library::get_singlethreaded`] call. #[inline(always)] pub unsafe fn get(&self, symbol: &[u8]) -> Result, crate::Error> { extern crate cfg_if; @@ -255,17 +265,16 @@ impl Library { /// Get a pointer to function or static variable by symbol name. /// - /// The `symbol` may not contain any null bytes, with an exception of last byte. A null - /// terminated `symbol` may avoid a string allocation in some cases. + /// The `symbol` may not contain any null bytes, with an exception of last byte. Providing a + /// null terminated `symbol` may help to avoid an allocation. /// /// Symbol is interpreted as-is; no mangling is done. This means that symbols like `x::y` are /// most likely invalid. /// /// # Safety /// - /// This function does not validate the type `T`. It is up to the user of this function to - /// ensure that the loaded symbol is in fact a `T`. Using a value with a wrong type has no - /// defined behaviour. + /// Users of this API must specify the correct type of the function or variable loaded. Using a + /// `Symbol` with a wrong type is undefined. /// /// It is up to the user of this library to ensure that no other calls to an MT-unsafe /// implementation of `dlerror` occur during execution of this function. Failing that, the @@ -273,8 +282,8 @@ impl Library { /// /// # Platform-specific behaviour /// - /// Implementation of thread local variables is extremely platform specific and uses of these - /// variables that work on e.g. Linux may have unintended behaviour on other POSIX systems. + /// Implementation of thread local variables is extremely platform specific and uses of such + /// variables that work on e.g. Linux may have unintended behaviour on other targets. #[inline(always)] pub unsafe fn get_singlethreaded(&self, symbol: &[u8]) -> Result, crate::Error> { self.get_impl(symbol, || Ok(Symbol { @@ -312,7 +321,8 @@ impl Library { /// what library was opened or other platform specifics. /// /// You only need to call this if you are interested in handling any errors that may arise when - /// library is unloaded. Otherwise this will be done when `Library` is dropped. + /// library is unloaded. Otherwise the implementation of `Drop` for `Library` will close the + /// library and ignore the errors were they arise. /// /// The underlying data structures may still get leaked if an error does occur. pub fn close(self) -> Result<(), crate::Error> { @@ -324,7 +334,7 @@ impl Library { } }).map_err(|e| e.unwrap_or(crate::Error::DlCloseUnknown)); // While the library is not free'd yet in case of an error, there is no reason to try - // dropping it again, because all that will do is try calling `FreeLibrary` again. only + // dropping it again, because all that will do is try calling `dlclose` again. only // this time it would ignore the return result, which we already seen failing… std::mem::forget(self); result diff --git a/src/os/windows/mod.rs b/src/os/windows/mod.rs index 775dee2..7e28527 100644 --- a/src/os/windows/mod.rs +++ b/src/os/windows/mod.rs @@ -86,15 +86,20 @@ impl Library { /// `.dll` extension is implicitly added. This behaviour may be suppressed by appending a /// trailing `.` to the `filename`. /// - /// This is equivalent to [`Library::load_with_flags`]`(filename, 0)`. + /// This is equivalent to [Library::load_with_flags](filename, 0). /// /// [msdn]: https://docs.microsoft.com/en-us/windows/win32/api/libloaderapi/nf-libloaderapi-loadlibraryw#remarks /// /// # Safety /// - /// When a library is loaded initializers contained within the library are executed. For the - /// purposes of soundness, execution of these initializers is conceptually the same calling a - /// FFI function and may impose whatever requirements on the caller. + /// When a library is loaded initialization routines contained within the library are executed. + /// For the purposes of safety, execution of these routines is conceptually the same calling an + /// unknown foreign function and may impose arbitrary requirements on the caller for the call + /// to be sound. + /// + /// Additionally, the callers of this function must also ensure that execution of the + /// termination routines contained within the library is safe as well. These routines may be + /// executed when the library is unloaded. #[inline] pub unsafe fn new>(filename: P) -> Result { Library::load_with_flags(filename, 0) @@ -173,9 +178,14 @@ impl Library { /// /// # Safety /// - /// When a library is loaded initializers contained within the library are executed. For the - /// purposes of soundness, execution of these initializers is conceptually the same calling a - /// FFI function and may impose whatever requirements on the caller. + /// When a library is loaded initialization routines contained within the library are executed. + /// For the purposes of safety, execution of these routines is conceptually the same calling an + /// unknown foreign function and may impose arbitrary requirements on the caller for the call + /// to be sound. + /// + /// Additionally, the callers of this function must also ensure that execution of the + /// termination routines contained within the library is safe as well. These routines may be + /// executed when the library is unloaded. pub unsafe fn load_with_flags>(filename: P, flags: DWORD) -> Result { let wide_filename: Vec = filename.as_ref().encode_wide().chain(Some(0)).collect(); let _guard = ErrorModeGuard::new(); @@ -206,9 +216,8 @@ impl Library { /// /// # Safety /// - /// This function does not validate the type `T`. It is up to the user of this function to - /// ensure that the loaded symbol is in fact a `T`. Using a value with a wrong type has no - /// definied behaviour. + /// Users of this API must specify the correct type of the function or variable loaded. Using a + /// `Symbol` with a wrong type is undefined. pub unsafe fn get(&self, symbol: &[u8]) -> Result, crate::Error> { ensure_compatible_types::()?; let symbol = cstr_cow_from_bytes(symbol)?; @@ -229,8 +238,8 @@ impl Library { /// /// # Safety /// - /// Pointer to a value of arbitrary type is returned. Using a value with wrong type is - /// undefined. + /// Users of this API must specify the correct type of the function or variable loaded. Using a + /// `Symbol` with a wrong type is undefined. pub unsafe fn get_ordinal(&self, ordinal: WORD) -> Result, crate::Error> { ensure_compatible_types::()?; with_get_last_error(|source| crate::Error::GetProcAddress { source }, || {