diff options
author | Treehugger Robot <treehugger-gerrit@google.com> | 2022-03-16 00:25:04 +0000 |
---|---|---|
committer | Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com> | 2022-03-16 00:25:04 +0000 |
commit | d40a23d2b18755ab20f55f84e27cdb4bd82e17ac (patch) | |
tree | 8167ff3ef81634a34f22347866ca7517ab5783d0 | |
parent | 630e69776a65b4db6b552384a337f34f4a3b985a (diff) | |
parent | 24d9606f1bf2d3557ea7411f4699018a9413449d (diff) | |
download | libloading-d40a23d2b18755ab20f55f84e27cdb4bd82e17ac.tar.gz |
Merge "Update libloading to 0.7.3" am: 73c4575102 am: 24d9606f1b
Original change: https://android-review.googlesource.com/c/platform/external/rust/crates/libloading/+/2004417
Change-Id: I1207eb876b980d36da622e11bba7849d5fedb61c
-rw-r--r-- | .cargo_vcs_info.json | 7 | ||||
-rw-r--r-- | .github/workflows/libloading.yml | 24 | ||||
-rw-r--r-- | Android.bp | 2 | ||||
-rw-r--r-- | Cargo.toml | 16 | ||||
-rw-r--r-- | Cargo.toml.orig | 9 | ||||
-rw-r--r-- | METADATA | 12 | ||||
-rw-r--r-- | README.mkd | 7 | ||||
-rw-r--r-- | src/changelog.rs | 42 | ||||
-rw-r--r-- | src/lib.rs | 342 | ||||
-rw-r--r-- | src/os/mod.rs | 12 | ||||
-rw-r--r-- | src/os/unix/consts.rs | 8 | ||||
-rw-r--r-- | src/os/unix/mod.rs | 52 | ||||
-rw-r--r-- | src/os/windows/mod.rs | 70 | ||||
-rw-r--r-- | src/safe.rs | 299 | ||||
-rw-r--r-- | src/util.rs | 15 | ||||
-rw-r--r-- | tests/constants.rs | 2 | ||||
-rw-r--r-- | tests/functions.rs | 4 |
17 files changed, 479 insertions, 444 deletions
diff --git a/.cargo_vcs_info.json b/.cargo_vcs_info.json index 69cdb18..dfc0076 100644 --- a/.cargo_vcs_info.json +++ b/.cargo_vcs_info.json @@ -1,5 +1,6 @@ { "git": { - "sha1": "7fbd3a34336f7839bdd6d6aaf83a9a763c4fa1e5" - } -} + "sha1": "00c21d873aeeac16935ac85962486b41776fe976" + }, + "path_in_vcs": "" +}
\ No newline at end of file diff --git a/.github/workflows/libloading.yml b/.github/workflows/libloading.yml index 811681d..e7e84ac 100644 --- a/.github/workflows/libloading.yml +++ b/.github/workflows/libloading.yml @@ -26,37 +26,28 @@ jobs: profile: minimal components: clippy default: true - - name: Update - uses: actions-rs/cargo@v1 - with: - command: update - args: --manifest-path=Cargo.toml - name: Clippy uses: actions-rs/cargo@v1 with: command: clippy - - name: Build - uses: actions-rs/cargo@v1 - with: - command: build - args: --manifest-path=Cargo.toml - name: Test uses: actions-rs/cargo@v1 with: command: test - args: --manifest-path=Cargo.toml -- --nocapture + args: -- --nocapture - name: Test Release uses: actions-rs/cargo@v1 with: command: test - args: --manifest-path=Cargo.toml --release -- --nocapture + args: --release -- --nocapture - name: Documentation uses: actions-rs/cargo@v1 with: - command: doc - args: --manifest-path=Cargo.toml - env: - RUSTDOCFLAGS: --cfg docsrs + command: rustdoc + args: | + -Zunstable-options + --config + 'build.rustdogflags=["--cfg", "libloading_docs", "-D", "rustdoc::broken_intra_doc_links"]' if: ${{ matrix.rust_toolchain == 'nightly' }} windows-gnu-test: @@ -110,6 +101,7 @@ jobs: - x86_64-unknown-openbsd - x86_64-unknown-redox - x86_64-fuchsia + - wasm32-unknown-unknown timeout-minutes: 20 steps: - uses: actions/checkout@v2 @@ -23,7 +23,7 @@ rust_library { host_supported: true, crate_name: "libloading", cargo_env_compat: true, - cargo_pkg_version: "0.7.0", + cargo_pkg_version: "0.7.3", srcs: ["src/lib.rs"], edition: "2015", rustlibs: [ @@ -3,18 +3,18 @@ # When uploading crates to the registry Cargo will automatically # "normalize" Cargo.toml files for maximal compatibility # with all versions of Cargo and also rewrite `path` dependencies -# to registry (e.g., crates.io) dependencies +# to registry (e.g., crates.io) dependencies. # -# If you believe there's an error in this file please file an -# issue against the rust-lang/cargo repository. If you're -# editing this file be aware that the upstream Cargo.toml -# will likely look very different (and much more reasonable) +# If you are reading this file be aware that the original Cargo.toml +# will likely look very different (and much more reasonable). +# See Cargo.toml.orig for the original contents. [package] +rust-version = "1.40.0" name = "libloading" -version = "0.7.0" +version = "0.7.3" authors = ["Simonas Kazlauskas <libloading@kazlauskas.me>"] -description = "A safer binding to platform’s dynamic library loading utilities" +description = "Bindings around the platform's dynamic library loading primitives with greatly improved memory safety." documentation = "https://docs.rs/libloading/" readme = "README.mkd" keywords = ["dlopen", "load", "shared", "dylib"] @@ -23,7 +23,7 @@ license = "ISC" repository = "https://github.com/nagisa/rust_libloading/" [package.metadata.docs.rs] all-features = true -rustdoc-args = ["--cfg", "docsrs"] +rustdoc-args = ["--cfg", "libloading_docs"] [dev-dependencies.libc] version = "0.2" diff --git a/Cargo.toml.orig b/Cargo.toml.orig index d946299..35e8353 100644 --- a/Cargo.toml.orig +++ b/Cargo.toml.orig @@ -2,16 +2,17 @@ name = "libloading" # When bumping # * Don’t forget to add an entry to `src/changelog.rs` -# * If bumping a incompatible version, adjust documentation in `src/lib.rs` -version = "0.7.0" +# * If bumping to an incompatible version, adjust the documentation in `src/lib.rs` +version = "0.7.3" authors = ["Simonas Kazlauskas <libloading@kazlauskas.me>"] license = "ISC" repository = "https://github.com/nagisa/rust_libloading/" documentation = "https://docs.rs/libloading/" readme = "README.mkd" -description = "A safer binding to platform’s dynamic library loading utilities" +description = "Bindings around the platform's dynamic library loading primitives with greatly improved memory safety." keywords = ["dlopen", "load", "shared", "dylib"] categories = ["api-bindings"] +rust-version = "1.40.0" [target.'cfg(windows)'.dependencies.winapi] version = "0.3" @@ -29,4 +30,4 @@ static_assertions = "1.1" [package.metadata.docs.rs] all-features = true -rustdoc-args = ["--cfg", "docsrs"] +rustdoc-args = ["--cfg", "libloading_docs"] @@ -1,5 +1,5 @@ name: "libloading" -description: "A safer binding to platform\'s dynamic library loading utilities" +description: "Bindings around the platform\'s dynamic library loading primitives with greatly improved memory safety." third_party { url { type: HOMEPAGE @@ -7,13 +7,13 @@ third_party { } url { type: ARCHIVE - value: "https://static.crates.io/crates/libloading/libloading-0.7.0.crate" + value: "https://static.crates.io/crates/libloading/libloading-0.7.3.crate" } - version: "0.7.0" + version: "0.7.3" license_type: NOTICE last_upgrade_date { - year: 2021 - month: 2 - day: 9 + year: 2022 + month: 3 + day: 1 } } @@ -1,11 +1,8 @@ # libloading -Safer bindings around system dynamic library loading primitives. The most important safety -guarantee by this library is prevention of dangling-`Symbol`s that may occur after a `Library` is -unloaded. +Bindings around the platform's dynamic library loading primitives with greatly improved memory safety. The most important safety guarantee of this library is the prevention of dangling `Symbol`s that may occur after a `Library` is unloaded. -Using this library allows loading dynamic libraries (also known as shared libraries) as well as use -functions and static variables these libraries contain. +Using this library allows the loading of dynamic libraries, also known as shared libraries, as well as the use of the functions and static variables that these libraries may contain. * [Documentation][docs] * [Changelog][changelog] diff --git a/src/changelog.rs b/src/changelog.rs index c927551..f8b898e 100644 --- a/src/changelog.rs +++ b/src/changelog.rs @@ -1,6 +1,43 @@ //! The change log. -/// Release 0.7.0 (2021-01-31) +/// Release 0.7.3 (2022-01-15) +/// +/// This release has no functional changes. +/// +/// In this release the `docsrs` `cfg` has been renamed to `libloading_docs` to better reflect that +/// this `cfg` is intended to be only used by `libloading` and only specifically for the invocation +/// of `rustdoc` when documenting `libloading`. Setting this `cfg` in any other situation is +/// unsupported and will not work. +pub mod r0_7_3 {} + +/// Release 0.7.2 (2021-11-14) +/// +/// Cargo.toml now specifies the MSRV bounds, which enables tooling to report an early failure when +/// the version of the toolchain is insufficient. Refer to the [min-rust-version RFC] and its +/// [tracking issue]. +/// +/// [min-rust-version RFC]: https://rust-lang.github.io/rfcs/2495-min-rust-version.html +/// [tracking issue]: https://github.com/rust-lang/rust/issues/65262 +/// +/// Additionally, on platforms `libloading` has no support (today: `not(any(unix, windows))`), we +/// will no longer attempt to implement the cross-platform `Library` and `Symbol` types. This makes +/// `libloading` compile on targets such as `wasm32-unknown-unknown` and gives ability to the +/// downstream consumers of this library to decide how they want to handle the absence of the +/// library loading implementation in their code. One of such approaches could be depending on +/// `libloading` itself optionally as such: +/// +/// ```toml +/// [target.'cfg(any(unix, windows))'.dependencies.libloading] +/// version = "0.7" +/// ``` +pub mod r0_7_2 {} + +/// Release 0.7.1 (2021-10-09) +/// +/// Significantly improved the consistency and style of the documentation. +pub mod r0_7_1 {} + +/// Release 0.7.0 (2021-02-06) /// /// ## Breaking changes /// @@ -187,7 +224,6 @@ pub mod r0_6_1 {} /// [`Error`]: crate::Error pub mod r0_6_0 {} - /// Release 0.5.2 (2019-07-07) /// /// * Added API to convert OS-specific `Library` and `Symbol` conversion to underlying resources. @@ -218,7 +254,6 @@ pub mod r0_5_0 {} /// * `cargo test --release` now works when testing libloading. pub mod r0_4_3 {} - /// Release 0.4.2 (2017-09-24) /// /// * Improved error and race-condition handling on Windows; @@ -226,7 +261,6 @@ pub mod r0_4_3 {} /// * Added `Symbol::<Option<T>::lift_option() -> Option<Symbol<T>>` convenience method. pub mod r0_4_2 {} - /// Release 0.4.1 (2017-08-29) /// /// * Solaris support @@ -1,25 +1,27 @@ -//! A memory-safer wrapper around system dynamic library loading primitives. +//! Bindings around the platform's dynamic library loading primitives with greatly improved memory safety. //! -//! Using this library allows loading [dynamic libraries](struct.Library.html) (also known as -//! shared libraries) and use functions & global variables contained within the libraries. +//! Using this library allows the loading of [dynamic libraries](struct.Library.html), also known as +//! shared libraries, and the use of the functions and static variables they contain. //! -//! `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. +//! The `libloading` crate exposes a cross-platform interface to load a library and make use of its +//! contents, but little is done to hide the differences in behaviour between platforms. +//! The API documentation strives to document such differences as much as possible. //! -//! Platform specific APIs are also available in the [`os`](crate::os) module. These APIs are more -//! flexible but less safe. +//! Platform-specific APIs are also available in the [`os`](crate::os) module. These APIs are more +//! flexible, but less safe. //! -//! # Usage +//! # Installation //! -//! Add a dependency on this library to your `Cargo.toml`: +//! Add the `libloading` library to your dependencies in `Cargo.toml`: //! //! ```toml //! [dependencies] //! libloading = "0.7" //! ``` //! -//! Then inside your code: +//! # Usage +//! +//! In your code, run the following: //! //! ```no_run //! fn call_dynamic() -> Result<u32, Box<dyn std::error::Error>> { @@ -31,319 +33,29 @@ //! } //! ``` //! -//! The compiler will ensure that the loaded `function` will not outlive the `Library` it comes -//! from, preventing a common class of issues. -#![deny( - missing_docs, - clippy::all, - unreachable_pub, - unused, -)] -#![cfg_attr(docsrs, deny(broken_intra_doc_links))] -#![cfg_attr(docsrs, feature(doc_cfg))] - -use std::env::consts::{DLL_PREFIX, DLL_SUFFIX}; -use std::ffi::{OsStr, OsString}; -use std::fmt; -use std::ops; -use std::marker; +//! The compiler will ensure that the loaded function will not outlive the `Library` from which it comes, +//! preventing the most common memory-safety issues. +#![cfg_attr(any(unix, windows), deny(missing_docs, clippy::all, unreachable_pub, unused))] +#![cfg_attr(libloading_docs, feature(doc_cfg))] -#[cfg(unix)] -use self::os::unix as imp; -#[cfg(windows)] -use self::os::windows as imp; -pub use self::error::Error; - -pub mod os; pub mod changelog; +pub mod os; mod util; -mod error; -/// A loaded dynamic library. -pub struct Library(imp::Library); - -impl Library { - /// Find and load a dynamic library. - /// - /// The `filename` argument may be any of: - /// - /// * A library filename; - /// * Absolute path to the library; - /// * Relative (to the current working directory) path to the library. - /// - /// # Safety - /// - /// 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 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. - /// - /// 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 - /// - /// When a plain library filename is supplied, locations where library is searched for is - /// platform specific and cannot be adjusted in a portable manner. See documentation for - /// the platform specific [`os::unix::Library::new`] and [`os::windows::Library::new`] methods - /// for further information on library lookup behaviour. - /// - /// If the `filename` specifies a library filename without path and with extension omitted, - /// `.dll` extension is implicitly added on Windows. - /// - /// # 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 - /// library filenames. - /// - /// Strive to specify an absolute or at least a relative path to your library, unless - /// system-wide libraries are being loaded. Platform-dependent library search locations - /// combined with various quirks related to path-less filenames may cause flakiness in - /// programs. - /// - /// # Examples - /// - /// ```no_run - /// # use ::libloading::Library; - /// // Any of the following are valid. - /// unsafe { - /// let _ = Library::new("/path/to/awesome.module").unwrap(); - /// let _ = Library::new("../awesome.module").unwrap(); - /// let _ = Library::new("libsomelib.so.1").unwrap(); - /// } - /// ``` - pub unsafe fn new<P: AsRef<OsStr>>(filename: P) -> Result<Library, Error> { - imp::Library::new(filename).map(From::from) - } - - /// 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. 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 - /// - /// 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 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 null pointer is something you care about, - /// consider using the [`os::unix::Library::get_singlethreaded`] call. - /// - /// # Examples - /// - /// Given a loaded library: - /// - /// ```no_run - /// # use ::libloading::Library; - /// let lib = unsafe { - /// Library::new("/path/to/awesome.module").unwrap() - /// }; - /// ``` - /// - /// Loading and using a function looks like this: - /// - /// ```no_run - /// # use ::libloading::{Library, Symbol}; - /// # let lib = unsafe { - /// # Library::new("/path/to/awesome.module").unwrap() - /// # }; - /// unsafe { - /// let awesome_function: Symbol<unsafe extern fn(f64) -> f64> = - /// lib.get(b"awesome_function\0").unwrap(); - /// awesome_function(0.42); - /// } - /// ``` - /// - /// A static variable may also be loaded and inspected: - /// - /// ```no_run - /// # use ::libloading::{Library, Symbol}; - /// # let lib = unsafe { Library::new("/path/to/awesome.module").unwrap() }; - /// unsafe { - /// let awesome_variable: Symbol<*mut f64> = lib.get(b"awesome_variable\0").unwrap(); - /// **awesome_variable = 42.0; - /// }; - /// ``` - pub unsafe fn get<'lib, T>(&'lib self, symbol: &[u8]) -> Result<Symbol<'lib, T>, Error> { - self.0.get(symbol).map(|from| Symbol::from_raw(from, self)) - } - - /// Unload the library. - /// - /// This method might be a no-op, depending on the flags with which the `Library` was opened, - /// 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. - /// - /// The underlying data structures may still get leaked if an error does occur. - pub fn close(self) -> Result<(), Error> { - self.0.close() - } -} - -impl fmt::Debug for Library { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - self.0.fmt(f) - } -} - -impl From<imp::Library> for Library { - fn from(lib: imp::Library) -> Library { - Library(lib) - } -} - -impl From<Library> for imp::Library { - fn from(lib: Library) -> imp::Library { - lib.0 - } -} - -unsafe impl Send for Library {} -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`]. -/// -/// 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. -/// -/// [`Library::get`]: Library::get -pub struct Symbol<'lib, T: 'lib> { - inner: imp::Symbol<T>, - pd: marker::PhantomData<&'lib T> -} - -impl<'lib, T> Symbol<'lib, T> { - /// Extract the wrapped `os::platform::Symbol`. - /// - /// # 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 - /// - /// ```no_run - /// # use ::libloading::{Library, Symbol}; - /// unsafe { - /// let lib = Library::new("/path/to/awesome.module").unwrap(); - /// let symbol: Symbol<*mut u32> = lib.get(b"symbol\0").unwrap(); - /// let symbol = symbol.into_raw(); - /// } - /// ``` - pub unsafe fn into_raw(self) -> imp::Symbol<T> { - self.inner - } - - /// 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 a reference to the library. - /// - /// # Safety - /// - /// The `library` reference must be exactly the library `sym` was loaded from. - /// - /// # Examples - /// - /// ```no_run - /// # use ::libloading::{Library, Symbol}; - /// unsafe { - /// let lib = Library::new("/path/to/awesome.module").unwrap(); - /// let symbol: Symbol<*mut u32> = lib.get(b"symbol\0").unwrap(); - /// let symbol = symbol.into_raw(); - /// let symbol = Symbol::from_raw(symbol, &lib); - /// } - /// ``` - 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 - } - } -} - -impl<'lib, T> Symbol<'lib, Option<T>> { - /// Lift Option out of the symbol. - /// - /// # Examples - /// - /// ```no_run - /// # use ::libloading::{Library, Symbol}; - /// unsafe { - /// let lib = Library::new("/path/to/awesome.module").unwrap(); - /// let symbol: Symbol<Option<*mut u32>> = lib.get(b"symbol\0").unwrap(); - /// let symbol: Symbol<*mut u32> = symbol.lift_option().expect("static is not null"); - /// } - /// ``` - pub fn lift_option(self) -> Option<Symbol<'lib, T>> { - self.inner.lift_option().map(|is| Symbol { - inner: is, - pd: marker::PhantomData, - }) - } -} - -impl<'lib, T> Clone for Symbol<'lib, T> { - fn clone(&self) -> Symbol<'lib, T> { - Symbol { - inner: self.inner.clone(), - pd: marker::PhantomData - } - } -} - -// FIXME: implement FnOnce for callable stuff instead. -impl<'lib, T> ops::Deref for Symbol<'lib, T> { - type Target = T; - fn deref(&self) -> &T { - ops::Deref::deref(&self.inner) - } -} +mod error; +pub use self::error::Error; -impl<'lib, T> fmt::Debug for Symbol<'lib, T> { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - self.inner.fmt(f) - } -} +#[cfg(any(unix, windows, libloading_docs))] +mod safe; +#[cfg(any(unix, windows, libloading_docs))] +pub use self::safe::{Library, Symbol}; -unsafe impl<'lib, T: Send> Send for Symbol<'lib, T> {} -unsafe impl<'lib, T: Sync> Sync for Symbol<'lib, T> {} +use std::env::consts::{DLL_PREFIX, DLL_SUFFIX}; +use std::ffi::{OsStr, OsString}; /// Converts a library name to a filename generally appropriate for use on the system. /// -/// The function will prepend prefixes (such as `lib`) and suffixes (such as `.so`) to the library +/// This function will prepend prefixes (such as `lib`) and suffixes (such as `.so`) to the library /// `name` to construct the filename. /// /// # Examples diff --git a/src/os/mod.rs b/src/os/mod.rs index c03ebc1..710353f 100644 --- a/src/os/mod.rs +++ b/src/os/mod.rs @@ -1,6 +1,6 @@ -//! Unsafe but flexible 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 +//! These modules expose more extensive and powerful bindings to the dynamic //! library loading facilities. Use of these bindings come at the cost of less (in most cases, //! none at all) safety guarantees, which are provided by the top-level bindings. //! @@ -17,11 +17,11 @@ //! ``` /// UNIX implementation of dynamic library loading. -#[cfg(any(unix, docsrs))] -#[cfg_attr(docsrs, doc(cfg(unix)))] +#[cfg(any(unix, libloading_docs))] +#[cfg_attr(libloading_docs, doc(cfg(unix)))] pub mod unix; /// Windows implementation of dynamic library loading. -#[cfg(any(windows, docsrs))] -#[cfg_attr(docsrs, doc(cfg(windows)))] +#[cfg(any(windows, libloading_docs))] +#[cfg_attr(libloading_docs, doc(cfg(windows)))] pub mod windows; diff --git a/src/os/unix/consts.rs b/src/os/unix/consts.rs index 2c18f95..dbe4df9 100644 --- a/src/os/unix/consts.rs +++ b/src/os/unix/consts.rs @@ -7,7 +7,7 @@ use std::os::raw::c_int; /// Specifying `RTLD_LAZY` should improve performance on implementations supporting dynamic /// symbol binding since a process might not reference all of the symbols in an executable /// object file. And, for systems supporting dynamic symbol resolution for normal process -/// execution, this behavior mimics the normal handling of process execution. +/// execution, this behaviour mimics the normal handling of process execution. /// /// Conflicts with [`RTLD_NOW`]. /// @@ -18,7 +18,7 @@ pub const RTLD_LAZY: c_int = posix::RTLD_LAZY; /// /// All necessary relocations shall be performed when the executable object file is first /// loaded. This may waste some processing if relocations are performed for symbols -/// that are never referenced. This behavior may be useful for applications that need to +/// that are never referenced. This behaviour may be useful for applications that need to /// know that all symbols referenced during execution will be available before /// [`Library::open`] returns. /// @@ -43,7 +43,7 @@ pub const RTLD_GLOBAL: c_int = posix::RTLD_GLOBAL; /// any other executable object file. This mode of operation is most appropriate for e.g. plugins. pub const RTLD_LOCAL: c_int = posix::RTLD_LOCAL; -#[cfg(all(docsrs, not(unix)))] +#[cfg(all(libloading_docs, not(unix)))] mod posix { use super::c_int; pub(super) const RTLD_LAZY: c_int = !0; @@ -52,7 +52,7 @@ mod posix { pub(super) const RTLD_LOCAL: c_int = !0; } -#[cfg(any(not(docsrs), unix))] +#[cfg(any(not(libloading_docs), unix))] mod posix { extern crate cfg_if; use self::cfg_if::cfg_if; diff --git a/src/os/unix/mod.rs b/src/os/unix/mod.rs index 45e8230..fd0777e 100644 --- a/src/os/unix/mod.rs +++ b/src/os/unix/mod.rs @@ -1,19 +1,18 @@ // 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(any(not(docsrs), unix))] +#[cfg(all(libloading_docs, not(unix)))] +mod unix_imports {} +#[cfg(any(not(libloading_docs), unix))] mod unix_imports { pub(super) use std::os::unix::ffi::OsStrExt; } +pub use self::consts::*; 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; -pub use self::consts::*; +use std::{fmt, marker, mem, ptr}; +use util::{cstr_cow_from_bytes, ensure_compatible_types}; mod consts; @@ -109,8 +108,8 @@ impl Library { /// /// # Safety /// - /// 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 + /// When a library is loaded, initialisation routines contained within the library are executed. + /// For the purposes of safety, the 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. /// @@ -127,12 +126,12 @@ impl Library { /// [`Library::get`] calls of the returned `Library` will look for symbols in following /// locations in order: /// - /// 1. Original program image; + /// 1. The 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) + /// 3. Any 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 + /// Note that the behaviour of a `Library` loaded with this method is different from that of /// Libraries loaded with [`os::windows::Library::this`]. /// /// This is equivalent to <code>[Library::open](None, [RTLD_LAZY] | [RTLD_LOCAL])</code>. @@ -142,22 +141,22 @@ impl Library { pub fn this() -> Library { unsafe { // SAFE: this does not load any new shared library images, no danger in it executing - // initializer routines. + // initialiser routines. Library::open(None::<&OsStr>, RTLD_LAZY | RTLD_LOCAL).expect("this should never fail") } } /// Find and load an executable object file (shared library). /// - /// See documentation for [`Library::this`] for further description of behaviour + /// See documentation for [`Library::this`] for further description of the behaviour /// when the `filename` is `None`. Otherwise see [`Library::new`]. /// /// Corresponds to `dlopen(filename, flags)`. /// /// # Safety /// - /// 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 + /// When a library is loaded, initialisation routines contained within the library are executed. + /// For the purposes of safety, the 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. /// @@ -217,9 +216,9 @@ impl Library { } - /// Get a pointer to function or static variable by symbol name. + /// Get a pointer to a function or static variable by symbol name. /// - /// The `symbol` may not contain any null bytes, with an exception of last byte. Providing a + /// The `symbol` may not contain any null bytes, with the exception of the 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 @@ -265,7 +264,7 @@ 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. Providing a + /// The `symbol` may not contain any null bytes, with the exception of the 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 @@ -273,16 +272,15 @@ impl Library { /// /// # Safety /// - /// Users of this API must specify the correct type of the function or variable loaded. Using a - /// `Symbol` with a wrong type is undefined. + /// Users of this API must specify the correct type of the function or variable loaded. /// /// 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 + /// implementation of `dlerror` occur during the execution of this function. Failing that, the /// behaviour of this function is not defined. /// /// # Platform-specific behaviour /// - /// Implementation of thread local variables is extremely platform specific and uses of such + /// The 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> { @@ -357,15 +355,15 @@ impl fmt::Debug for Library { /// Symbol from a library. /// -/// A major difference compared to the cross-platform `Symbol` is that this does not ensure the -/// `Symbol` does not outlive `Library` it comes from. +/// A major difference compared to the cross-platform `Symbol` is that this does not ensure that the +/// `Symbol` does not outlive the `Library` it comes from. pub struct Symbol<T> { pointer: *mut raw::c_void, pd: marker::PhantomData<T> } impl<T> Symbol<T> { - /// Convert the loaded Symbol into a raw pointer. + /// Convert the loaded `Symbol` into a raw pointer. pub fn into_raw(self) -> *mut raw::c_void { let pointer = self.pointer; mem::forget(self); diff --git a/src/os/windows/mod.rs b/src/os/windows/mod.rs index 7e28527..eadeb69 100644 --- a/src/os/windows/mod.rs +++ b/src/os/windows/mod.rs @@ -1,6 +1,6 @@ // 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)))] +#[cfg(all(libloading_docs, not(windows)))] mod windows_imports { pub(super) enum WORD {} pub(super) struct DWORD; @@ -23,7 +23,7 @@ mod windows_imports { pub(crate) const LOAD_LIBRARY_SAFE_CURRENT_DIRS: DWORD = DWORD; } } -#[cfg(any(not(docsrs), windows))] +#[cfg(any(not(libloading_docs), windows))] mod windows_imports { extern crate winapi; pub(super) use self::winapi::shared::minwindef::{WORD, DWORD, HMODULE, FARPROC}; @@ -55,23 +55,23 @@ use util::{ensure_compatible_types, cstr_cow_from_bytes}; use std::ffi::{OsStr, OsString}; use std::{fmt, io, marker, mem, ptr}; -/// A platform-specific counterpart of the cross-platform [`Library`](crate::Library). +/// The platform-specific counterpart of the cross-platform [`Library`](crate::Library). pub struct Library(HMODULE); unsafe impl Send for Library {} // Now, this is sort-of-tricky. MSDN documentation does not really make any claims as to safety of // the Win32 APIs. Sadly, whomever I asked, even current and former Microsoft employees, couldn’t -// say for sure, whether the Win32 APIs used to implement `Library` are thread-safe or not. +// say for sure whether the Win32 APIs used to implement `Library` are thread-safe or not. // // My investigation ended up with a question about thread-safety properties of the API involved // being sent to an internal (to MS) general question mailing-list. The conclusion of the mail is // as such: // -// * Nobody inside MS (at least out of all the people who have seen the question) knows for +// * Nobody inside MS (at least out of all of the people who have seen the question) knows for // sure either; // * However, the general consensus between MS developers is that one can rely on the API being // thread-safe. In case it is not thread-safe it should be considered a bug on the Windows -// part. (NB: bugs filled at https://connect.microsoft.com/ against Windows Server) +// part. (NB: bugs filed at https://connect.microsoft.com/ against Windows Server) unsafe impl Sync for Library {} impl Library { @@ -79,11 +79,11 @@ impl Library { /// /// 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 + /// path, the function uses a Windows-specific search strategy to find the module. For more /// information, see the [Remarks on MSDN][msdn]. /// - /// 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 + /// If the `filename` specifies a library filename without a path and with the extension omitted, + /// the `.dll` extension is implicitly added. This behaviour may be suppressed by appending a /// trailing `.` to the `filename`. /// /// This is equivalent to <code>[Library::load_with_flags](filename, 0)</code>. @@ -92,8 +92,8 @@ impl Library { /// /// # Safety /// - /// 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 + /// When a library is loaded, initialisation routines contained within the library are executed. + /// For the purposes of safety, the 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. /// @@ -107,7 +107,7 @@ impl Library { /// Get the `Library` representing the original program executable. /// - /// Note that behaviour of `Library` loaded with this method is different from + /// Note that the behaviour of the `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, _)`. @@ -131,15 +131,15 @@ impl Library { /// Get a module that is already loaded by the program. /// /// This function returns a `Library` corresponding to a module with the given name that is - /// already mapped into the address space of the process. If the module isn't found an error is + /// already mapped into the address space of the process. If the module isn't found, an error is /// returned. /// /// If the `filename` does not include a full path and there are multiple different loaded /// modules corresponding to the `filename`, it is impossible to predict which module handle /// will be returned. For more information refer to [MSDN]. /// - /// 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 + /// If the `filename` specifies a library filename without a path and with the extension omitted, + /// the `.dll` extension is implicitly added. This behaviour may be suppressed by appending a /// trailing `.` to the `filename`. /// /// This is equivalent to `GetModuleHandleExW(0, filename, _)`. @@ -169,7 +169,7 @@ impl Library { /// Find and load a module, additionally adjusting behaviour with flags. /// - /// See [`Library::new`] for documentation on handling of the `filename` argument. See the + /// See [`Library::new`] for documentation on the handling of the `filename` argument. See the /// [flag table on MSDN][flags] for information on applicable values for the `flags` argument. /// /// Corresponds to `LoadLibraryExW(filename, reserved: NULL, flags)`. @@ -178,8 +178,8 @@ impl Library { /// /// # Safety /// - /// 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 + /// When a library is loaded, initialisation routines contained within the library are executed. + /// For the purposes of safety, the 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. /// @@ -206,9 +206,9 @@ impl Library { ret } - /// Get a pointer to function or static variable by symbol name. + /// Get a pointer to a function or static variable by symbol name. /// - /// The `symbol` may not contain any null bytes, with an exception of last byte. A null + /// The `symbol` may not contain any null bytes, with the exception of the last byte. A null /// terminated `symbol` may avoid a string allocation in some cases. /// /// Symbol is interpreted as-is; no mangling is done. This means that symbols like `x::y` are @@ -216,8 +216,7 @@ impl Library { /// /// # Safety /// - /// Users of this API must specify the correct type of the function or variable loaded. Using a - /// `Symbol` with a wrong type is undefined. + /// Users of this API must specify the correct type of the function or variable loaded. 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)?; @@ -234,12 +233,11 @@ impl Library { }).map_err(|e| e.unwrap_or(crate::Error::GetProcAddressUnknown)) } - /// Get a pointer to function or static variable by ordinal number. + /// Get a pointer to a function or static variable by ordinal number. /// /// # Safety /// - /// Users of this API must specify the correct type of the function or variable loaded. Using a - /// `Symbol` with a wrong type is undefined. + /// Users of this API must specify the correct type of the function or variable loaded. 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 }, || { @@ -267,8 +265,8 @@ impl Library { /// /// # Safety /// - /// The handle shall be a result of a successful call of `LoadLibraryA`, `LoadLibraryW`, - /// `LoadLibraryExW`, `LoadLibraryExA` or a handle previously returned by the + /// The handle must be the result of a successful call of `LoadLibraryA`, `LoadLibraryW`, + /// `LoadLibraryExW`, or `LoadLibraryExA`, or a handle previously returned by the /// `Library::into_raw` call. pub unsafe fn from_raw(handle: HMODULE) -> Library { Library(handle) @@ -290,7 +288,7 @@ impl Library { }).map_err(|e| e.unwrap_or(crate::Error::FreeLibraryUnknown)); // 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 - // this time it would ignore the return result, which we already seen failing… + // this time it would ignore the return result, which we already seen failing... std::mem::forget(self); result } @@ -323,17 +321,17 @@ impl fmt::Debug for Library { } } -/// Symbol from a library. +/// A symbol from a library. /// -/// A major difference compared to the cross-platform `Symbol` is that this does not ensure the -/// `Symbol` does not outlive `Library` it comes from. +/// A major difference compared to the cross-platform `Symbol` is that this does not ensure that the +/// `Symbol` does not outlive the `Library` that it comes from. pub struct Symbol<T> { pointer: FARPROC, pd: marker::PhantomData<T> } impl<T> Symbol<T> { - /// Convert the loaded Symbol into a handle. + /// Convert the loaded `Symbol` into a handle. pub fn into_raw(self) -> FARPROC { let pointer = self.pointer; mem::forget(self); @@ -452,7 +450,7 @@ pub const LOAD_LIBRARY_AS_DATAFILE_EXCLUSIVE: DWORD = consts::LOAD_LIBRARY_AS_DA /// Map the file into the process’ virtual address space as an image file. /// -/// The loader does not load the static imports or perform the other usual initialization steps. +/// The loader does not load the static imports or perform the other usual initialisation steps. /// Use this flag when you want to load a DLL only to extract messages or resources from it. /// /// Unless the application depends on the file having the in-memory layout of an image, this value @@ -462,7 +460,7 @@ pub const LOAD_LIBRARY_AS_DATAFILE_EXCLUSIVE: DWORD = consts::LOAD_LIBRARY_AS_DA /// See [flag documentation on MSDN](https://docs.microsoft.com/en-us/windows/win32/api/libloaderapi/nf-libloaderapi-loadlibraryexw#parameters). pub const LOAD_LIBRARY_AS_IMAGE_RESOURCE: DWORD = consts::LOAD_LIBRARY_AS_IMAGE_RESOURCE; -/// Search application's installation directory for the DLL and its dependencies. +/// Search the application's installation directory for the DLL and its dependencies. /// /// Directories in the standard search path are not searched. This value cannot be combined with /// [`LOAD_WITH_ALTERED_SEARCH_PATH`]. @@ -509,11 +507,11 @@ pub const LOAD_LIBRARY_SEARCH_SYSTEM32: DWORD = consts::LOAD_LIBRARY_SEARCH_SYST /// See [flag documentation on MSDN](https://docs.microsoft.com/en-us/windows/win32/api/libloaderapi/nf-libloaderapi-loadlibraryexw#parameters). pub const LOAD_LIBRARY_SEARCH_USER_DIRS: DWORD = consts::LOAD_LIBRARY_SEARCH_USER_DIRS; -/// If `filename specifies an absolute path, the system uses the alternate file search strategy +/// If `filename` specifies an absolute path, the system uses the alternate file search strategy /// discussed in the [Remarks section] to find associated executable modules that the specified /// module causes to be loaded. /// -/// If this value is used and `filename` specifies a relative path, the behavior is undefined. +/// If this value is used and `filename` specifies a relative path, the behaviour is undefined. /// /// If this value is not used, or if `filename` does not specify a path, the system uses the /// standard search strategy discussed in the [Remarks section] to find associated executable diff --git a/src/safe.rs b/src/safe.rs new file mode 100644 index 0000000..49be0cf --- /dev/null +++ b/src/safe.rs @@ -0,0 +1,299 @@ +use super::Error; +#[cfg(libloading_docs)] +use super::os::unix as imp; // the implementation used here doesn't matter particularly much... +#[cfg(all(not(libloading_docs), unix))] +use super::os::unix as imp; +#[cfg(all(not(libloading_docs), windows))] +use super::os::windows as imp; +use std::ffi::OsStr; +use std::fmt; +use std::marker; +use std::ops; + +/// A loaded dynamic library. +#[cfg_attr(libloading_docs, doc(cfg(any(unix, windows))))] +pub struct Library(imp::Library); + +impl Library { + /// Find and load a dynamic library. + /// + /// The `filename` argument may be either: + /// + /// * A library filename; + /// * The absolute path to the library; + /// * A relative (to the current working directory) path to the library. + /// + /// # Safety + /// + /// When a library is loaded, initialisation routines contained within it are executed. + /// For the purposes of safety, the 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 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. + /// + /// 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 + /// + /// When a plain library filename is supplied, the locations in which the library is searched are + /// platform specific and cannot be adjusted in a portable manner. See the documentation for + /// the platform specific [`os::unix::Library::new`] and [`os::windows::Library::new`] methods + /// for further information on library lookup behaviour. + /// + /// If the `filename` specifies a library filename without a path and with the extension omitted, + /// the `.dll` extension is implicitly added on Windows. + /// + /// [`os::unix::Library::new`]: crate::os::unix::Library::new + /// [`os::windows::Library::new`]: crate::os::windows::Library::new + /// + /// # Tips + /// + /// Distributing your dynamic libraries under a filename common to all platforms (e.g. + /// `awesome.module`) allows you to avoid code which has to account for platform’s conventional + /// library filenames. + /// + /// Strive to specify an absolute or at least a relative path to your library, unless + /// system-wide libraries are being loaded. Platform-dependent library search locations + /// combined with various quirks related to path-less filenames may cause flakiness in + /// programs. + /// + /// # Examples + /// + /// ```no_run + /// # use ::libloading::Library; + /// // Any of the following are valid. + /// unsafe { + /// let _ = Library::new("/path/to/awesome.module").unwrap(); + /// let _ = Library::new("../awesome.module").unwrap(); + /// let _ = Library::new("libsomelib.so.1").unwrap(); + /// } + /// ``` + pub unsafe fn new<P: AsRef<OsStr>>(filename: P) -> Result<Library, Error> { + imp::Library::new(filename).map(From::from) + } + + /// Get a pointer to a function or static variable by symbol name. + /// + /// The `symbol` may not contain any null bytes, with the exception of the last byte. Providing a + /// null-terminated `symbol` may help to avoid an allocation. + /// + /// The symbol is interpreted as-is; no mangling is done. This means that symbols like `x::y` are + /// most likely invalid. + /// + /// # Safety + /// + /// Users of this API must specify the correct type of the function or variable loaded. + /// + /// # Platform-specific behaviour + /// + /// The 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 null pointer is something you care about, + /// consider using the [`os::unix::Library::get_singlethreaded`] call. + /// + /// [`os::unix::Library::get_singlethreaded`]: crate::os::unix::Library::get_singlethreaded + /// + /// # Examples + /// + /// Given a loaded library: + /// + /// ```no_run + /// # use ::libloading::Library; + /// let lib = unsafe { + /// Library::new("/path/to/awesome.module").unwrap() + /// }; + /// ``` + /// + /// Loading and using a function looks like this: + /// + /// ```no_run + /// # use ::libloading::{Library, Symbol}; + /// # let lib = unsafe { + /// # Library::new("/path/to/awesome.module").unwrap() + /// # }; + /// unsafe { + /// let awesome_function: Symbol<unsafe extern fn(f64) -> f64> = + /// lib.get(b"awesome_function\0").unwrap(); + /// awesome_function(0.42); + /// } + /// ``` + /// + /// A static variable may also be loaded and inspected: + /// + /// ```no_run + /// # use ::libloading::{Library, Symbol}; + /// # let lib = unsafe { Library::new("/path/to/awesome.module").unwrap() }; + /// unsafe { + /// let awesome_variable: Symbol<*mut f64> = lib.get(b"awesome_variable\0").unwrap(); + /// **awesome_variable = 42.0; + /// }; + /// ``` + pub unsafe fn get<'lib, T>(&'lib self, symbol: &[u8]) -> Result<Symbol<'lib, T>, Error> { + self.0.get(symbol).map(|from| Symbol::from_raw(from, self)) + } + + /// Unload the library. + /// + /// This method might be a no-op, depending on the flags with which the `Library` was opened, + /// 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. + /// + /// The underlying data structures may still get leaked if an error does occur. + pub fn close(self) -> Result<(), Error> { + self.0.close() + } +} + +impl fmt::Debug for Library { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + self.0.fmt(f) + } +} + +impl From<imp::Library> for Library { + fn from(lib: imp::Library) -> Library { + Library(lib) + } +} + +impl From<Library> for imp::Library { + fn from(lib: Library) -> imp::Library { + lib.0 + } +} + +unsafe impl Send for Library {} +unsafe impl Sync for Library {} + +/// Symbol from a library. +/// +/// This type is a safeguard against using dynamically loaded symbols after a `Library` is +/// unloaded. The primary method to create an instance of a `Symbol` is via [`Library::get`]. +/// +/// The `Deref` trait implementation allows the use of `Symbol` as if it was a function or variable +/// itself, without taking care to “extract” the function or variable manually most of the time. +/// +/// [`Library::get`]: Library::get +#[cfg_attr(libloading_docs, doc(cfg(any(unix, windows))))] +pub struct Symbol<'lib, T: 'lib> { + inner: imp::Symbol<T>, + pd: marker::PhantomData<&'lib T>, +} + +impl<'lib, T> Symbol<'lib, T> { + /// Extract the wrapped `os::platform::Symbol`. + /// + /// # Safety + /// + /// Using this function relinquishes all the lifetime guarantees. It is up to the developer to + /// ensure the resulting `Symbol` is not used past the lifetime of the `Library` this symbol + /// was loaded from. + /// + /// # Examples + /// + /// ```no_run + /// # use ::libloading::{Library, Symbol}; + /// unsafe { + /// let lib = Library::new("/path/to/awesome.module").unwrap(); + /// let symbol: Symbol<*mut u32> = lib.get(b"symbol\0").unwrap(); + /// let symbol = symbol.into_raw(); + /// } + /// ``` + pub unsafe fn into_raw(self) -> imp::Symbol<T> { + self.inner + } + + /// 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 a reference to the library. + /// + /// # Safety + /// + /// The `library` reference must be exactly the library `sym` was loaded from. + /// + /// # Examples + /// + /// ```no_run + /// # use ::libloading::{Library, Symbol}; + /// unsafe { + /// let lib = Library::new("/path/to/awesome.module").unwrap(); + /// let symbol: Symbol<*mut u32> = lib.get(b"symbol\0").unwrap(); + /// let symbol = symbol.into_raw(); + /// let symbol = Symbol::from_raw(symbol, &lib); + /// } + /// ``` + 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, + } + } +} + +impl<'lib, T> Symbol<'lib, Option<T>> { + /// Lift Option out of the symbol. + /// + /// # Examples + /// + /// ```no_run + /// # use ::libloading::{Library, Symbol}; + /// unsafe { + /// let lib = Library::new("/path/to/awesome.module").unwrap(); + /// let symbol: Symbol<Option<*mut u32>> = lib.get(b"symbol\0").unwrap(); + /// let symbol: Symbol<*mut u32> = symbol.lift_option().expect("static is not null"); + /// } + /// ``` + pub fn lift_option(self) -> Option<Symbol<'lib, T>> { + self.inner.lift_option().map(|is| Symbol { + inner: is, + pd: marker::PhantomData, + }) + } +} + +impl<'lib, T> Clone for Symbol<'lib, T> { + fn clone(&self) -> Symbol<'lib, T> { + Symbol { + inner: self.inner.clone(), + pd: marker::PhantomData, + } + } +} + +// FIXME: implement FnOnce for callable stuff instead. +impl<'lib, T> ops::Deref for Symbol<'lib, T> { + type Target = T; + fn deref(&self) -> &T { + ops::Deref::deref(&self.inner) + } +} + +impl<'lib, T> fmt::Debug for Symbol<'lib, T> { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + self.inner.fmt(f) + } +} + +unsafe impl<'lib, T: Send> Send for Symbol<'lib, T> {} +unsafe impl<'lib, T: Sync> Sync for Symbol<'lib, T> {} diff --git a/src/util.rs b/src/util.rs index 880b689..599e6c2 100644 --- a/src/util.rs +++ b/src/util.rs @@ -1,10 +1,10 @@ -use std::ffi::{CStr, CString}; use std::borrow::Cow; +use std::ffi::{CStr, CString}; use std::os::raw; use crate::Error; -/// Checks for last byte and avoids allocating if it is zero. +/// Checks for the last byte and avoids allocating if it is zero. /// /// Non-last null bytes still result in an error. pub(crate) fn cstr_cow_from_bytes(slice: &[u8]) -> Result<Cow<'_, CStr>, Error> { @@ -13,11 +13,14 @@ pub(crate) fn cstr_cow_from_bytes(slice: &[u8]) -> Result<Cow<'_, CStr>, Error> // Slice out of 0 elements None => unsafe { Cow::Borrowed(CStr::from_ptr(&ZERO)) }, // Slice with trailing 0 - Some(&0) => Cow::Borrowed(CStr::from_bytes_with_nul(slice) - .map_err(|source| Error::CreateCStringWithTrailing { source })?), + Some(&0) => Cow::Borrowed( + CStr::from_bytes_with_nul(slice) + .map_err(|source| Error::CreateCStringWithTrailing { source })?, + ), // Slice with no trailing 0 - Some(_) => Cow::Owned(CString::new(slice) - .map_err(|source| Error::CreateCString { source })?), + Some(_) => { + Cow::Owned(CString::new(slice).map_err(|source| Error::CreateCString { source })?) + } }) } diff --git a/tests/constants.rs b/tests/constants.rs index ad910c4..6ae5a84 100644 --- a/tests/constants.rs +++ b/tests/constants.rs @@ -1,5 +1,5 @@ -extern crate libloading; extern crate libc; +extern crate libloading; extern crate static_assertions; #[cfg(all(test, unix))] diff --git a/tests/functions.rs b/tests/functions.rs index eef41e6..795f0cf 100644 --- a/tests/functions.rs +++ b/tests/functions.rs @@ -148,7 +148,7 @@ fn test_static_ptr() { } #[test] -// Something about i686-pc-windows-gnu, makes dll initialization code call abort when it is loaded +// Something about i686-pc-windows-gnu, makes dll initialisation code call abort when it is loaded // and unloaded many times. So far it seems like an issue with mingw, not libloading, so ignoring // the target. Especially since it is very unlikely to be fixed given the state of support its // support. @@ -193,7 +193,7 @@ fn library_this() { use libloading::os::windows::Library; make_helpers(); unsafe { - // SAFE: well-known library without initializers is loaded. + // SAFE: well-known library without initialisers is loaded. let _lib = Library::new(LIBPATH).unwrap(); let this = Library::this().expect("this library"); // SAFE: functions are never called. |