Review documentation

This commit is contained in:
Simonas Kazlauskas
2021-02-06 17:54:10 +02:00
parent 4182c7e7e8
commit 1774322f1d
5 changed files with 175 additions and 116 deletions
+73 -19
View File
@@ -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 <vector>
/// #include <iostream>
///
/// static std::vector<unsigned int> 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
/// <code>[`RTLD_LAZY`] | [`RTLD_LOCAL`]</code> as the default set of loader options. This has a
/// couple benefits. Namely:
/// <code>[RTLD_LAZY] | [RTLD_LOCAL]</code> 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 {}
+42 -56
View File
@@ -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<u32, Box<dyn std::error::Error>> {
//! unsafe {
//! let lib = lib::Library::new("/path/to/liblibrary.so")?;
//! let func: lib::Symbol<unsafe extern fn() -> u32> = lib.get(b"my_func")?;
//! let lib = libloading::Library::new("/path/to/liblibrary.so")?;
//! let func: libloading::Symbol<unsafe extern fn() -> 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 (MSVCs `_declspec(thread)`, Rusts
/// `#[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<T>,
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<L>(sym: imp::Symbol<T>, _: &'lib L) -> Symbol<'lib, T> {
pub unsafe fn from_raw<L>(sym: imp::Symbol<T>, library: &'lib L) -> Symbol<'lib, T> {
let _ = library; // ignore here for documentation purposes.
Symbol {
inner: sym,
pd: marker::PhantomData
+1 -1
View File
@@ -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,
+38 -28
View File
@@ -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 <code>[Library::open](filename, [RTLD_LAZY] | [RTLD_LOCAL])</code>.
///
/// [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<P: AsRef<OsStr>>(filename: P) -> Result<Library, crate::Error> {
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 <code>[Library::open](None, [RTLD_LAZY] | [RTLD_LOCAL])</code>.
///
/// [`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<P>(filename: Option<P>, flags: raw::c_int) -> Result<Library, crate::Error>
where P: AsRef<OsStr> {
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<T>(&self, symbol: &[u8]) -> Result<Symbol<T>, 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<T>(&self, symbol: &[u8]) -> Result<Symbol<T>, 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
+21 -12
View File
@@ -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 <code>[Library::load_with_flags](filename, 0)</code>.
///
/// [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<P: AsRef<OsStr>>(filename: P) -> Result<Library, crate::Error> {
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<P: AsRef<OsStr>>(filename: P, flags: DWORD) -> Result<Library, crate::Error> {
let wide_filename: Vec<u16> = 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<T>(&self, symbol: &[u8]) -> Result<Symbol<T>, crate::Error> {
ensure_compatible_types::<T, FARPROC>()?;
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<T>(&self, ordinal: WORD) -> Result<Symbol<T>, crate::Error> {
ensure_compatible_types::<T, FARPROC>()?;
with_get_last_error(|source| crate::Error::GetProcAddress { source }, || {