diff --git a/Cargo.toml b/Cargo.toml index bd1114b..fae39d3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -16,3 +16,7 @@ features = [ "errhandlingapi", "libloaderapi", ] + +[package.metadata.docs.rs] +all-features = true +rustdoc-args = ["--cfg", "docsrs"] diff --git a/src/error.rs b/src/error.rs index 80bab7a..1114510 100644 --- a/src/error.rs +++ b/src/error.rs @@ -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 { diff --git a/src/lib.rs b/src/lib.rs index fad7d63..ab98cb3 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -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 platform’s 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> { /// Lift Option out of the symbol. /// - /// ## Examples + /// # Examples /// /// ```no_run /// # use ::libloading::{Library, Symbol}; diff --git a/src/os/mod.rs b/src/os/mod.rs index ccbc8e9..40361c5 100644 --- a/src/os/mod.rs +++ b/src/os/mod.rs @@ -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; diff --git a/src/os/unix/mod.rs b/src/os/unix/mod.rs index 29d2003..b4a7a0c 100644 --- a/src/os/unix/mod.rs +++ b/src/os/unix/mod.rs @@ -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 { }) } -/// 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 can’t 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>(filename: P) -> Result { 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 can’t 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

(filename: Option

, flags: raw::c_int) -> Result @@ -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(&self, symbol: &[u8]) -> Result, 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(&self, symbol: &[u8]) -> Result, 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 { diff --git a/src/os/windows/mod.rs b/src/os/windows/mod.rs index 898037a..747e44d 100644 --- a/src/os/windows/mod.rs +++ b/src/os/windows/mod.rs @@ -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>(filename: P) -> Result { 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 { - 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 can’t 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>(filename: P, flags: DWORD) -> Result { let wide_filename: Vec = 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 { - 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() {