Significantly improve documentation

With these improvements browsing the documentation for os-specific
functionality should be possible from any system. In particular UNIX
documentation now has information on Windows and the Windows
documentation has information on UNIX-specific stuff.
This commit is contained in:
Simonas Kazlauskas
2020-08-22 05:56:30 +03:00
committed by Simonas Kazlauskas
parent 76e2d46e9e
commit 08cfc8f9c0
6 changed files with 174 additions and 106 deletions
+4
View File
@@ -16,3 +16,7 @@ features = [
"errhandlingapi",
"libloaderapi",
]
[package.metadata.docs.rs]
all-features = true
rustdoc-args = ["--cfg", "docsrs"]
+35 -8
View File
@@ -1,5 +1,6 @@
use std::ffi::CString;
/// A `dlerror` error.
pub struct DlDescription(pub(crate) CString);
impl std::fmt::Debug for DlDescription {
@@ -8,6 +9,7 @@ impl std::fmt::Debug for DlDescription {
}
}
/// A Windows API error.
pub struct WindowsError(pub(crate) std::io::Error);
impl std::fmt::Debug for WindowsError {
@@ -16,23 +18,36 @@ impl std::fmt::Debug for WindowsError {
}
}
/// Errors.
#[derive(Debug)]
#[non_exhaustive]
pub enum Error {
/// The `dlopen` call failed.
DlOpen { desc: DlDescription },
DlOpen {
/// The source error.
desc: DlDescription
},
/// The `dlopen` call failed and system did not report an error.
DlOpenUnknown,
/// The `dlsym` call failed.
DlSym { desc: DlDescription },
DlSym {
/// The source error.
desc: DlDescription
},
/// The `dlsym` call failed and system did not report an error.
DlSymUnknown,
/// The `dlclose` call failed.
DlClose { desc: DlDescription },
DlClose {
/// The source error.
desc: DlDescription
},
/// The `dlclose` call failed and system did not report an error.
DlCloseUnknown,
/// The `LoadLibraryW` call failed.
LoadLibraryW { source: WindowsError },
LoadLibraryW {
/// The source error.
source: WindowsError
},
/// The `LoadLibraryW` call failed and system did not report an error.
LoadLibraryWUnknown,
/// The `GetModuleHandleExW` call failed.
@@ -43,19 +58,31 @@ pub enum Error {
/// The `LoadLibraryW` call failed and system did not report an error.
GetModuleHandleExWUnknown,
/// The `GetProcAddress` call failed.
GetProcAddress { source: WindowsError },
GetProcAddress {
/// The source error.
source: WindowsError
},
/// The `GetProcAddressUnknown` call failed and system did not report an error.
GetProcAddressUnknown,
/// The `FreeLibrary` call failed.
FreeLibrary { source: WindowsError },
FreeLibrary {
/// The source error.
source: WindowsError
},
/// The `FreeLibrary` call failed and system did not report an error.
FreeLibraryUnknown,
/// The requested type cannot possibly work.
IncompatibleSize,
/// Could not create a new CString.
CreateCString { source: std::ffi::NulError },
CreateCString {
/// The source error.
source: std::ffi::NulError
},
/// Could not create a new CString from bytes with trailing null.
CreateCStringWithTrailing { source: std::ffi::FromBytesWithNulError },
CreateCStringWithTrailing {
/// The source error.
source: std::ffi::FromBytesWithNulError
},
}
impl std::error::Error for Error {
+24 -13
View File
@@ -36,6 +36,16 @@
//!
//! 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.
#![deny(
missing_docs,
clippy::all,
unreachable_pub,
unused,
)]
#![cfg_attr(docsrs, deny(broken_intra_doc_links))]
#![cfg_attr(docsrs, feature(doc_cfg))]
use std::ffi::OsStr;
use std::fmt;
use std::ops;
@@ -64,7 +74,7 @@ impl Library {
/// * Absolute path to the library;
/// * Relative (to the current working directory) path to the library.
///
/// ## Thread-safety
/// # 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.
@@ -80,12 +90,12 @@ impl Library {
/// path-less filename and library search path is modified (`SetDllDirectory` function on
/// Windows, `{DY,}LD_LIBRARY_PATH` environment variable on UNIX).
///
/// ## Platform-specific behaviour
/// # Platform-specific behaviour
///
/// When a plain library filename is supplied, locations where library is searched for is
/// platform specific and cannot be adjusted in a portable manner.
///
/// ### Windows
/// ## 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
@@ -95,7 +105,7 @@ impl Library {
/// `#[thread_local]` attributes), loading the library will fail on versions prior to Windows
/// Vista.
///
/// ## Tips
/// # Tips
///
/// Distributing your dynamic libraries under a filename common to all platforms (e.g.
/// `awesome.module`) allows to avoid code which has to account for platforms conventional
@@ -105,7 +115,7 @@ impl Library {
/// are being loaded. Platform-dependent library search locations combined with various quirks
/// related to path-less filenames may cause flaky code.
///
/// ## Examples
/// # Examples
///
/// ```no_run
/// # use ::libloading::Library;
@@ -126,12 +136,12 @@ impl Library {
/// Symbol is interpreted as-is; no mangling is done. This means that symbols like `x::y` are
/// most likely invalid.
///
/// ## Unsafety
/// # Safety
///
/// Pointer to a value of arbitrary type is returned. Using a value with wrong type is
/// undefined.
///
/// ## Platform-specific behaviour
/// # Platform-specific behaviour
///
/// On Linux and Windows, a TLS variable acts just like any regular global variable. OS X uses
/// some sort of lazy initialization scheme, which makes loading TLS variables this way
@@ -143,7 +153,7 @@ impl Library {
/// 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.
///
/// ## Examples
/// # Examples
///
/// Given a loaded library:
///
@@ -232,12 +242,13 @@ pub struct Symbol<'lib, T: 'lib> {
impl<'lib, T> Symbol<'lib, T> {
/// Extract the wrapped `os::platform::Symbol`.
///
/// ## Unsafety
/// # Safety
///
/// Using this function relinquishes all the lifetime guarantees. It is up to programmer to
/// ensure the resulting `Symbol` is not used past the lifetime of the `Library` this symbol
/// was loaded from.
///
/// ## Examples
/// # Examples
///
/// ```no_run
/// # use ::libloading::{Library, Symbol};
@@ -256,12 +267,12 @@ impl<'lib, T> Symbol<'lib, T> {
/// 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.
///
/// ## Unsafety
/// # 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.
///
/// ## Examples
/// # Examples
///
/// ```no_run
/// # use ::libloading::{Library, Symbol};
@@ -283,7 +294,7 @@ impl<'lib, T> Symbol<'lib, T> {
impl<'lib, T> Symbol<'lib, Option<T>> {
/// Lift Option out of the symbol.
///
/// ## Examples
/// # Examples
///
/// ```no_run
/// # use ::libloading::{Library, Symbol};
+8 -26
View File
@@ -16,30 +16,12 @@
//! use libloading::os::windows::*;
//! ```
macro_rules! unix {
($item: item) => {
/// UNIX implementation of dynamic library loading.
///
/// This module should be expanded with more UNIX-specific functionality in the future.
$item
}
}
/// UNIX implementation of dynamic library loading.
#[cfg(any(unix, docsrs))]
#[cfg_attr(docsrs, doc(cfg(unix)))]
pub mod unix;
macro_rules! windows {
($item: item) => {
/// Windows implementation of dynamic library loading.
///
/// This module should be expanded with more Windows-specific functionality in the future.
$item
}
}
#[cfg(unix)]
unix!(pub mod unix;);
#[cfg(unix)]
windows!(pub mod windows {});
#[cfg(windows)]
windows!(pub mod windows;);
#[cfg(windows)]
unix!(pub mod unix {});
/// Windows implementation of dynamic library loading.
#[cfg(any(windows, docsrs))]
#[cfg_attr(docsrs, doc(cfg(windows)))]
pub mod windows;
+54 -36
View File
@@ -1,9 +1,19 @@
use util::{ensure_compatible_types, cstr_cow_from_bytes};
// A hack for docs.rs to build documentation that has both windows and linux documentation in the
// same rustdoc build visible.
#[cfg(all(docsrs, not(unix)))]
mod unix_imports {
}
#[cfg(unix)]
mod unix_imports {
pub(super) use std::os::unix::ffi::OsStrExt;
}
use self::unix_imports::*;
use util::{ensure_compatible_types, cstr_cow_from_bytes};
use std::ffi::{CStr, OsStr};
use std::{fmt, marker, mem, ptr};
use std::os::raw;
use std::os::unix::ffi::OsStrExt;
// dl* family of functions did not have enough thought put into it.
//
@@ -61,7 +71,7 @@ where F: FnOnce() -> Option<T> {
})
}
/// A platform-specific equivalent of the cross-platform `Library`.
/// A platform-specific counterpart of the cross-platform [`Library`](crate::Library).
pub struct Library {
handle: *mut raw::c_void
}
@@ -85,34 +95,45 @@ unsafe impl Send for Library {}
unsafe impl Sync for Library {}
impl Library {
/// Find and load a shared library (module).
/// Find and eagerly load a shared library (module).
///
/// Locations where library is searched for is platform specific and cant be adjusted
/// portably.
/// If the `filename` contains a [path separator], the `filename` is interpreted as a `path` to
/// a file. Otherwise, platform-specific algorithms are employed to find a library with a
/// matching file name.
///
/// Corresponds to `dlopen(filename, RTLD_NOW)`.
/// This is equivalent to [`Library::open`]`(filename, RTLD_NOW)`.
///
/// [path separator]: std::path::MAIN_SEPARATOR
#[inline]
pub fn new<P: AsRef<OsStr>>(filename: P) -> Result<Library, crate::Error> {
Library::open(Some(filename), RTLD_NOW)
}
/// Load the dynamic libraries linked into main program.
/// Eagerly load the `Library` representing the current executable.
///
/// This allows retrieving symbols from any **dynamic** library linked into the program,
/// without specifying the exact library.
/// [`Library::get`] calls of the returned `Library` will look for symbols in following
/// locations in order:
///
/// Corresponds to `dlopen(NULL, RTLD_NOW)`.
/// 1. Original program image;
/// 2. Any executable object files (e.g. shared libraries) loaded at program startup;
/// 3. Executable object files loaded at runtime (e.g. via other `Library::new` calls or via
/// calls to the `dlopen` function)
///
/// 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_NOW)`.
///
/// [`os::windows::Library::this`]: crate::os::windows::Library::this
#[inline]
pub fn this() -> Library {
Library::open(None::<&OsStr>, RTLD_NOW).expect("this should never fail")
}
/// Find and load a shared library (module).
/// Find and load an executable object file (shared library).
///
/// Locations where library is searched for is platform specific and cant be adjusted
/// portably.
///
/// If the `filename` is None, null pointer is passed to `dlopen`.
/// See documentation for [`Library::this`] for further description of behaviour
/// when the `filename` is `None`. Otherwise see [`Library::new`].
///
/// Corresponds to `dlopen(filename, flags)`.
pub fn open<P>(filename: Option<P>, flags: raw::c_int) -> Result<Library, crate::Error>
@@ -174,29 +195,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 a string allocation in some cases.
/// terminated `symbol` may avoid an allocation in some cases.
///
/// Symbol is interpreted as-is; no mangling is done. This means that symbols like `x::y` are
/// most likely invalid.
///
/// ## Unsafety
/// # 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.
/// defined behaviour.
///
///
///
/// ## Platform-specific behaviour
/// # Platform-specific behaviour
///
/// OS X uses some sort of lazy initialization scheme, which makes loading TLS variables
/// impossible. Using a TLS variable loaded this way on OS X is undefined behaviour.
/// impossible. Using a TLS variable loaded this way on OS X has no defined behaviour.
///
/// 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
/// pointer without it being an error. If loading a null pointer is something you care about,
/// consider using the [`Library::get_singlethreaded`] call.
/// 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.
#[inline(always)]
pub unsafe fn get<T>(&self, symbol: &[u8]) -> Result<Symbol<T>, crate::Error> {
#[cfg(mtsafe_dlerror)]
@@ -215,20 +234,20 @@ impl Library {
/// Symbol is interpreted as-is; no mangling is done. This means that symbols like `x::y` are
/// most likely invalid.
///
/// ## Unsafety
/// # 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.
/// defined behaviour.
///
/// It is up to the user of this library to ensure that no other calls to an MT-unsafe
/// implementation of `dlerror` occur while this function is executing. Failing that the
/// results of this function are not defined.
/// implementation of `dlerror` occur during execution of this function. Failing that, the
/// behaviour of this function is not defined.
///
/// ## Platform-specific behaviour
/// # Platform-specific behaviour
///
/// OS X uses some sort of lazy initialization scheme, which makes loading TLS variables
/// impossible. Using a TLS variable loaded this way on OS X is undefined behaviour.
/// impossible. Using a TLS variable loaded this way on OS X has no defined behaviour.
#[inline(always)]
pub unsafe fn get_singlethreaded<T>(&self, symbol: &[u8]) -> Result<Symbol<T>, crate::Error> {
self.get_impl(symbol, || Ok(Symbol {
@@ -249,7 +268,7 @@ impl Library {
/// Convert a raw handle returned by `dlopen`-family of calls to a `Library`.
///
/// ## Unsafety
/// # Safety
///
/// The pointer shall be a result of a successful call of the `dlopen`-family of functions or a
/// pointer previously returned by `Library::into_raw` call. It must be valid to call `dlclose`
@@ -266,8 +285,7 @@ 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 the implementation of `Drop` for `Library` will close the
/// library and ignore the errors were they arise.
/// library is unloaded. Otherwise this will be done when `Library` is dropped.
pub fn close(self) -> Result<(), crate::Error> {
let result = with_dlerror(|desc| crate::Error::DlClose { desc }, || {
if unsafe { dlclose(self.handle) } == 0 {
+49 -23
View File
@@ -1,17 +1,31 @@
extern crate winapi;
use self::winapi::shared::minwindef::{WORD, DWORD, HMODULE, FARPROC};
use self::winapi::shared::ntdef::WCHAR;
use self::winapi::shared::winerror;
use self::winapi::um::{errhandlingapi, libloaderapi};
// A hack for docs.rs to build documentation that has both windows and linux documentation in the
// same rustdoc build visible.
#[cfg(all(docsrs, not(windows)))]
mod windows_imports {
pub(super) enum WORD {}
pub(super) enum DWORD {}
pub(super) enum HMODULE {}
pub(super) enum FARPROC {}
}
#[cfg(windows)]
mod windows_imports {
extern crate winapi;
pub(super) use self::winapi::shared::minwindef::{WORD, DWORD, HMODULE, FARPROC};
pub(super) use self::winapi::shared::ntdef::WCHAR;
pub(super) use self::winapi::shared::winerror;
pub(super) use self::winapi::um::{errhandlingapi, libloaderapi};
pub(super) use std::os::windows::ffi::{OsStrExt, OsStringExt};
pub(super) const SEM_FAILCE: DWORD = 1;
}
use self::windows_imports::*;
use util::{ensure_compatible_types, cstr_cow_from_bytes};
use std::ffi::{OsStr, OsString};
use std::{fmt, io, marker, mem, ptr};
use std::os::windows::ffi::{OsStrExt, OsStringExt};
use std::sync::atomic::{AtomicBool, Ordering};
/// A platform-specific equivalent of the cross-platform `Library`.
/// A platform-specific counterpart of the cross-platform [`Library`](crate::Library).
pub struct Library(HMODULE);
unsafe impl Send for Library {}
@@ -31,9 +45,16 @@ unsafe impl Send for Library {}
unsafe impl Sync for Library {}
impl Library {
/// Find and load a shared library (module).
/// Find and load a module.
///
/// Corresponds to `LoadLibraryW(filename, reserved: NULL, flags: 0)` which is equivalent to `LoadLibraryW(filename)`
/// If the `filename` specifies a full path, the function only searches that path for the
/// module. Otherwise, if the `filename` specifies a relative path or a module name without a
/// path, the function uses a windows-specific search strategy to find the module; for more
/// information, see the [Remarks on MSDN][msdn].
///
/// 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
#[inline]
pub fn new<P: AsRef<OsStr>>(filename: P) -> Result<Library, crate::Error> {
Library::load_with_flags(filename, 0)
@@ -41,12 +62,16 @@ impl Library {
/// Load the `Library` representing the original program executable.
///
/// Note that this is not a direct equivalent to [`os::unix::Library::this`].
/// Note that behaviour of `Library` loaded with this method is different from
/// Libraries loaded with [`os::unix::Library::this`]. For more information refer to [MSDN].
///
/// Corresponds to `GetModuleHandleExW(0, NULL, _)`.
///
/// [`os::unix::Library::this`]: crate::os::unix::Library::this
/// [MSDN]: https://docs.microsoft.com/en-us/windows/win32/api/libloaderapi/nf-libloaderapi-getmodulehandleexw
pub fn this() -> Result<Library, crate::Error> {
let handle = unsafe {
let handle: HMODULE = std::ptr::null_mut();
unsafe {
let mut handle: HMODULE = std::ptr::null_mut();
with_get_last_error(|source| crate::Error::GetModuleHandleExW { source }, || {
let result = libloaderapi::GetModuleHandleExW(0, std::ptr::null_mut(), &mut handle);
if result == 0 {
@@ -54,16 +79,18 @@ impl Library {
} else {
Some(Library(handle))
}
}).map_err(|e| e.unwrap_or(crate::Error::GetModuleHandleExWUnknown));
};
}).map_err(|e| e.unwrap_or(crate::Error::GetModuleHandleExWUnknown))
}
}
/// Find and load a shared library (module).
/// Find and load a module, additionally adjusting behaviour with flags.
///
/// Locations where library is searched for is platform specific and cant be adjusted
/// portably.
/// See [`Library::new`] for documentation on handling of the `filename` argument. See the
/// [flag table on MSDN][flags] for information on applicable values for the `flags` argument.
///
/// Corresponds to `LoadLibraryW(filename, reserved: NULL, flags)`.
/// Corresponds to `LoadLibraryExW(filename, reserved: NULL, flags)`.
///
/// [flags]: https://docs.microsoft.com/en-us/windows/win32/api/libloaderapi/nf-libloaderapi-loadlibraryexw#parameters
pub 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();
@@ -93,7 +120,7 @@ impl Library {
/// Symbol is interpreted as-is; no mangling is done. This means that symbols like `x::y` are
/// most likely invalid.
///
/// ## Unsafety
/// # 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
@@ -116,7 +143,7 @@ impl Library {
/// Get a pointer to function or static variable by ordinal number.
///
/// ## Unsafety
/// # Safety
///
/// Pointer to a value of arbitrary type is returned. Using a value with wrong type is
/// undefined.
@@ -145,7 +172,7 @@ impl Library {
/// Convert a raw handle to a `Library`.
///
/// ## Unsafety
/// # Safety
///
/// The handle shall be a result of a successful call of `LoadLibraryW` or a
/// handle previously returned by the `Library::into_raw` call.
@@ -255,7 +282,6 @@ struct ErrorModeGuard(DWORD);
impl ErrorModeGuard {
fn new() -> Option<ErrorModeGuard> {
const SEM_FAILCE: DWORD = 1;
unsafe {
if !USE_ERRORMODE.load(Ordering::Acquire) {
let mut previous_mode = 0;
@@ -354,7 +380,7 @@ mod tests {
#[test]
fn library_this_get() {
use std::sync::atomic::{AtomicBool, Ordering};
static VARIABLE = AtomicBool::new();
static VARIABLE: AtomicBool = AtomicBool::new();
#[no_mangle]
extern "C" fn library_this_get_test_fn() {