libloading-0.7.4/.cargo_vcs_info.json0000644000000001360000000000100131500ustar { "git": { "sha1": "6e284984aee68cc2d1b7e7d5e7b5a2a2279cf8e3" }, "path_in_vcs": "" }libloading-0.7.4/.github/workflows/libloading.yml000064400000000000000000000071221046102023000201660ustar 00000000000000concurrency: group: ${{ github.workflow }}-${{ github.ref }} cancel-in-progress: true on: push: branches: [master] paths-ignore: ['*.mkd', 'LICENSE'] pull_request: types: [opened, reopened, synchronize] jobs: native-test: runs-on: ${{ matrix.os }} strategy: fail-fast: false matrix: rust_toolchain: [nightly, stable, 1.40.0] os: [ubuntu-latest, windows-latest, macOS-latest] timeout-minutes: 20 steps: - uses: actions/checkout@v2 - run: rustup install ${{ matrix.rust_toolchain }} --profile=minimal - run: rustup default ${{ matrix.rust_toolchain }} - run: rustup component add clippy - run: cargo clippy - run: cargo test -- --nocapture - run: cargo test --release -- --nocapture - run: cargo rustdoc -Zunstable-options --config 'build.rustdocflags=["--cfg", "libloading_docs", "-D", "rustdoc::broken_intra_doc_links"]' if: ${{ matrix.rust_toolchain == 'nightly' }} # pwsh.exe drops quotes kekw. https://stackoverflow.com/a/59036879 shell: bash windows-test: runs-on: windows-latest strategy: fail-fast: false matrix: rust_toolchain: [nightly, stable] rust_target: - x86_64-pc-windows-gnu - i686-pc-windows-gnu include: - rust_target: x86_64-pc-windows-gnu mingw_path: C:/msys64/mingw64/bin package: mingw-w64-x86_64-gcc - rust_target: i686-pc-windows-gnu mingw_path: C:/msys64/mingw32/bin package: mingw-w64-i686-gcc steps: - uses: actions/checkout@v2 - run: rustup install ${{ matrix.rust_toolchain }} --profile=minimal - run: rustup default ${{ matrix.rust_toolchain }} - run: rustup target add ${{ matrix.rust_target }} - uses: msys2/setup-msys2@v2 with: release: false install: ${{ matrix.package }} - run: echo "c:/msys64/bin" | Out-File -FilePath $env:GITHUB_PATH -Append - run: echo "${{ matrix.mingw_path }}" | Out-File -FilePath $env:GITHUB_PATH -Append if: ${{ matrix.mingw_path }}" - run: cargo test --target ${{ matrix.rust_target }} env: TARGET: ${{ matrix.rust_target}} bare-cross-build: runs-on: ubuntu-latest strategy: fail-fast: false matrix: rust_toolchain: [nightly] rust_target: # BSDs: could be tested with full system emulation # - x86_64-unknown-dragonfly # - x86_64-unknown-freebsd - x86_64-unknown-haiku # - x86_64-unknown-netbsd - x86_64-unknown-openbsd - x86_64-unknown-redox - x86_64-fuchsia - wasm32-unknown-unknown timeout-minutes: 20 steps: - uses: actions/checkout@v2 - run: rustup install ${{ matrix.rust_toolchain }} --profile=minimal - run: rustup default ${{ matrix.rust_toolchain }} - run: rustup component add rust-src --toolchain nightly --target ${{ matrix.rust_target }} - run: cargo build --target ${{ matrix.rust_target }} -Zbuild-std cross-ios-build: runs-on: macos-latest strategy: fail-fast: false matrix: rust_toolchain: [nightly, stable] rust_target: - aarch64-apple-ios - x86_64-apple-ios timeout-minutes: 20 steps: - uses: actions/checkout@v2 - run: rustup install ${{ matrix.rust_toolchain }} --profile=minimal - run: rustup default ${{ matrix.rust_toolchain }} - run: rustup target add ${{ matrix.rust_target }} - run: cargo build --target=${{ matrix.rust_target }} libloading-0.7.4/.gitignore000064400000000000000000000000261046102023000137260ustar 00000000000000target Cargo.lock doc libloading-0.7.4/Cargo.toml0000644000000024620000000000100111520ustar # THIS FILE IS AUTOMATICALLY GENERATED BY CARGO # # 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. # # 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.4" authors = ["Simonas Kazlauskas "] 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", ] categories = ["api-bindings"] license = "ISC" repository = "https://github.com/nagisa/rust_libloading/" [package.metadata.docs.rs] all-features = true rustdoc-args = [ "--cfg", "libloading_docs", ] [dev-dependencies.libc] version = "0.2" [dev-dependencies.static_assertions] version = "1.1" [target."cfg(unix)".dependencies.cfg-if] version = "1" [target."cfg(windows)".dependencies.winapi] version = "0.3" features = [ "errhandlingapi", "libloaderapi", ] libloading-0.7.4/Cargo.toml.orig000064400000000000000000000016711046102023000146340ustar 00000000000000[package] name = "libloading" # When bumping # * Don’t forget to add an entry to `src/changelog.rs` # * If bumping to an incompatible version, adjust the documentation in `src/lib.rs` version = "0.7.4" authors = ["Simonas Kazlauskas "] license = "ISC" repository = "https://github.com/nagisa/rust_libloading/" documentation = "https://docs.rs/libloading/" readme = "README.mkd" 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" features = [ "errhandlingapi", "libloaderapi", ] [target.'cfg(unix)'.dependencies.cfg-if] version = "1" [dev-dependencies] libc = "0.2" static_assertions = "1.1" [package.metadata.docs.rs] all-features = true rustdoc-args = ["--cfg", "libloading_docs"] libloading-0.7.4/LICENSE000064400000000000000000000013401046102023000127430ustar 00000000000000Copyright © 2015, Simonas Kazlauskas Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies. THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. libloading-0.7.4/README.mkd000064400000000000000000000012261046102023000133730ustar 00000000000000# libloading 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 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] [docs]: https://docs.rs/libloading/ [changelog]: https://docs.rs/libloading/*/libloading/changelog/index.html libloading is available to use under ISC (MIT-like) license. libloading-0.7.4/src/changelog.rs000064400000000000000000000343001046102023000150240ustar 00000000000000//! The change log. /// Release 0.7.4 (2022-11-07) /// /// This release has no functional changes. /// /// `RTLD_LAZY`, `RTLD_GLOBAL` and `RTLD_LOCAL` constants have been implemented for AIX platforms. pub mod r0_7_4 {} /// 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 /// /// ### Loading functions are now `unsafe` /// /// A number of associated methods involved in loading a library were changed to /// be `unsafe`. The affected functions are: [`Library::new`], [`os::unix::Library::new`], /// [`os::unix::Library::open`], [`os::windows::Library::new`], /// [`os::windows::Library::load_with_flags`]. This is the most prominent breaking change in this /// release and affects majority of the users of `libloading`. /// /// In order to see why it was necessary, consider the following snippet of C++ code: /// /// ```c++ /// #include /// #include /// /// static std::vector UNSHUU = { 1, 2, 3 }; /// /// int main() { /// std::cout << UNSHUU[0] << UNSHUU[1] << UNSHUU[2] << std::endl; // Prints 123 /// return 0; /// } /// ``` /// /// The `std::vector` type, much like in Rust's `Vec`, stores its contents in a buffer allocated on /// the heap. In this example the vector object itself is stored and initialized as a static /// variable – a compile time construct. The heap, on the other hand, is a runtime construct. And /// yet the code works exactly as you'd expect – the vector contains numbers 1, 2 and 3 stored in /// a buffer on heap. So, _what_ makes it work out, exactly? /// /// Various executable and shared library formats define conventions and machinery to execute /// arbitrary code when a program or a shared library is loaded. On systems using the PE format /// (e.g. Windows) this is available via the optional `DllMain` initializer. Various systems /// utilizing the ELF format take a sightly different approach of maintaining an array of function /// pointers in the `.init_array` section. A very similar mechanism exists on systems that utilize /// the Mach-O format. /// /// For the C++ program above, the object stored in the `UNSHUU` global variable is constructed /// by code run as part of such an initializer routine. This initializer is run before the entry /// point (the `main` function) is executed, allowing for this magical behaviour to be possible. /// Were the C++ code built as a shared library instead, the initialization routines would run as /// the resulting shared library is loaded. In case of `libloading` – during the call to /// `Library::new` and other methods affected by this change. /// /// These initialization (and very closely related termination) routines can be utilized outside of /// C++ too. Anybody can build a shared library in variety of different programming languages and /// set up the initializers to execute arbitrary code. Potentially code that does all sorts of /// wildly unsound stuff. /// /// The routines are executed by components that are an integral part of the operating system. /// Changing or controlling the operation of these components is infeasible. With that in /// mind, the initializer and termination routines are something anybody loading a library must /// carefully evaluate the libraries loaded for soundness. /// /// In practice, a vast majority of the libraries can be considered a good citizen and their /// initialization and termination routines, if they have any at all, can be trusted to be sound. /// /// Also see: [issue #86]. /// /// ### Better & more consistent default behaviour on UNIX systems /// /// On UNIX systems the [`Library::new`], [`os::unix::Library::new`] and /// [`os::unix::Library::this`] methods have been changed to use /// [RTLD_LAZY] | [RTLD_LOCAL] as the default set of loader options (previously: /// [`RTLD_NOW`]). This has a couple benefits. Namely: /// /// * Lazy binding is generally quicker to execute when only a subset of symbols from a library are /// used and is typically the default when neither `RTLD_LAZY` nor `RTLD_NOW` are specified when /// calling the underlying `dlopen` API; /// * On most UNIX systems (macOS being a notable exception) `RTLD_LOCAL` is the default when /// neither `RTLD_LOCAL` nor [`RTLD_GLOBAL`] are specified. The explicit setting of the /// `RTLD_LOCAL` flag makes this behaviour consistent across platforms. /// /// ### Dropped support for Windows XP/Vista /// /// The (broken) support for Windows XP and Windows Vista environments was removed. This was /// prompted primarily by a similar policy change in the [Rust /// project](https://github.com/rust-lang/compiler-team/issues/378) but also as an acknowledgement /// to the fact that `libloading` never worked in these environments anyway. /// /// ### More accurate error variant names /// /// Finally, the `Error::LoadLibraryW` renamed to [`Error::LoadLibraryExW`] to more accurately /// represent the underlying API that's failing. No functional changes as part of this rename /// intended. /// /// [issue #86]: https://github.com/nagisa/rust_libloading/issues/86 /// [`Library::new`]: crate::Library::new /// [`Error::LoadLibraryExW`]: crate::Error::LoadLibraryExW /// [`os::unix::Library::this`]: crate::os::unix::Library::this /// [`os::unix::Library::new`]: crate::os::unix::Library::new /// [`os::unix::Library::open`]: crate::os::unix::Library::new /// [`os::windows::Library::new`]: crate::os::windows::Library::new /// [`os::windows::Library::load_with_flags`]: crate::os::windows::Library::load_with_flags /// [`RTLD_NOW`]: crate::os::unix::RTLD_NOW /// [RTLD_LAZY]: crate::os::unix::RTLD_LAZY /// [RTLD_LOCAL]: crate::os::unix::RTLD_LOCAL /// [`RTLD_GLOBAL`]: crate::os::unix::RTLD_GLOBAL pub mod r0_7_0 {} /// Release 0.6.7 (2021-01-14) /// /// * Added a [`os::windows::Library::open_already_loaded`] to obtain a handle to a library that /// must already be loaded. There is no portable equivalent for all UNIX targets. Users who do not /// care about portability across UNIX platforms may use [`os::unix::Library::open`] with /// `libc::RTLD_NOLOAD`; /// /// [`os::windows::Library::open_already_loaded`]: crate::os::windows::Library::open_already_loaded /// [`os::unix::Library::open`]: crate::os::unix::Library::open pub mod r0_6_7 {} /// Release 0.6.6 (2020-12-03) /// /// * Fix a double-release of resources when [`Library::close`] or [`os::windows::Library::close`] /// is used on Windows. /// /// [`Library::close`]: crate::Library::close /// [`os::windows::Library::close`]: crate::os::windows::Library::close pub mod r0_6_6 {} /// Release 0.6.5 (2020-10-23) /// /// * Upgrade cfg-if 0.1 to 1.0 pub mod r0_6_5 {} /// Release 0.6.4 (2020-10-10) /// /// * Remove use of `build.rs` making it easier to build `libloading` without cargo. It also /// almost halves the build time of this crate. pub mod r0_6_4 {} /// Release 0.6.3 (2020-08-22) /// /// * Improve documentation, allowing to view all of the os-specific functionality from /// documentation generated for any target; /// * Add [`os::windows::Library::this`]; /// * Added constants to use with OS-specific `Library::open`; /// * Add [`library_filename`]. /// /// [`os::windows::Library::this`]: crate::os::windows::Library::this /// [`library_filename`]: crate::library_filename pub mod r0_6_3 {} /// Release 0.6.2 (2020-05-06) /// /// * Fixed building of this library on Illumos. pub mod r0_6_2 {} /// Release 0.6.1 (2020-04-15) /// /// * Introduced a new method [`os::windows::Library::load_with_flags`]; /// * Added support for the Illumos triple. /// /// [`os::windows::Library::load_with_flags`]: crate::os::windows::Library::load_with_flags pub mod r0_6_1 {} /// Release 0.6.0 (2020-04-05) /// /// * Introduced a new method [`os::unix::Library::get_singlethreaded`]; /// * Added (untested) support for building when targeting Redox and Fuchsia; /// * The APIs exposed by this library no longer panic and instead return an `Err` when it used /// to panic. /// /// ## Breaking changes /// /// * Minimum required (stable) version of Rust to build this library is now 1.40.0; /// * This crate now implements a custom [`Error`] type and all APIs now return this type rather /// than returning the `std::io::Error`; /// * `libloading::Result` has been removed; /// * Removed the dependency on the C compiler to build this library on UNIX-like platforms. /// `libloading` used to utilize a snippet written in C to work-around the unlikely possibility /// of the target having a thread-unsafe implementation of the `dlerror` function. The effect of /// the work-around was very opportunistic: it would not work if the function was called by /// forgoing `libloading`. /// /// Starting with 0.6.0, [`Library::get`] on platforms where `dlerror` is not MT-safe (such as /// FreeBSD, DragonflyBSD or NetBSD) will unconditionally return an error when the underlying /// `dlsym` returns a null pointer. For the use-cases where loading null pointers is necessary /// consider using [`os::unix::Library::get_singlethreaded`] instead. /// /// [`Library::get`]: crate::Library::get /// [`os::unix::Library::get_singlethreaded`]: crate::os::unix::Library::get_singlethreaded /// [`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. pub mod r0_5_2 {} /// Release 0.5.1 (2019-06-01) /// /// * Build on Haiku targets. pub mod r0_5_1 {} /// Release 0.5.0 (2018-01-11) /// /// * Update to `winapi = ^0.3`; /// /// ## Breaking changes /// /// * libloading now requires a C compiler to build on UNIX; /// * This is a temporary measure until the [`linkage`] attribute is stabilised; /// * Necessary to resolve [#32]. /// /// [`linkage`]: https://github.com/rust-lang/rust/issues/29603 /// [#32]: https://github.com/nagisa/rust_libloading/issues/32 pub mod r0_5_0 {} /// Release 0.4.3 (2017-12-07) /// /// * Bump lazy-static dependency to `^1.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; /// * Improved documentation about thread-safety of Library; /// * Added `Symbol::::lift_option() -> Option>` convenience method. pub mod r0_4_2 {} /// Release 0.4.1 (2017-08-29) /// /// * Solaris support pub mod r0_4_1 {} /// Release 0.4.0 (2017-05-01) /// /// * Remove build-time dependency on target_build_utils (and by extension serde/phf); /// * Require at least version 1.14.0 of rustc to build; /// * Actually, it is cargo which has to be more recent here. The one shipped with rustc 1.14.0 /// is what’s being required from now on. pub mod r0_4_0 {} /// Release 0.3.4 (2017-03-25) /// /// * Remove rogue println! pub mod r0_3_4 {} /// Release 0.3.3 (2017-03-25) /// /// * Panics when `Library::get` is called for incompatibly sized type such as named function /// types (which are zero-sized). pub mod r0_3_3 {} /// Release 0.3.2 (2017-02-10) /// /// * Minimum version required is now rustc 1.12.0; /// * Updated dependency versions (most notably target_build_utils to 0.3.0) pub mod r0_3_2 {} /// Release 0.3.1 (2016-10-01) /// /// * `Symbol` and `os::*::Symbol` now implement `Send` where `T: Send`; /// * `Symbol` and `os::*::Symbol` now implement `Sync` where `T: Sync`; /// * `Library` and `os::*::Library` now implement `Sync` (they were `Send` in 0.3.0 already). pub mod r0_3_1 {} /// Release 0.3.0 (2016-07-27) /// /// * Greatly improved documentation, especially around platform-specific behaviours; /// * Improved test suite by building our own library to test against; /// * All `Library`-ies now implement `Send`. /// * Added `impl From for Library` and `impl From for /// os::platform::Library` allowing wrapping and extracting the platform-specific library handle; /// * Added methods to wrap (`Symbol::from_raw`) and unwrap (`Symbol::into_raw`) the safe `Symbol` /// wrapper into unsafe `os::platform::Symbol`. /// /// The last two additions focus on not restricting potential usecases of this library, allowing /// users of the library to circumvent safety checks if need be. /// /// ## Breaking Changes /// /// `Library::new` defaults to `RTLD_NOW` instead of `RTLD_LAZY` on UNIX for more consistent /// cross-platform behaviour. If a library loaded with `Library::new` had any linking errors, but /// unresolved references weren’t forced to be resolved, the library would’ve “just worked”, /// whereas now the call to `Library::new` will return an error signifying presence of such error. /// /// ## os::platform /// * Added `os::unix::Library::open` which allows specifying arbitrary flags (e.g. `RTLD_LAZY`); /// * Added `os::windows::Library::get_ordinal` which allows finding a function or variable by its /// ordinal number; pub mod r0_3_0 {} libloading-0.7.4/src/error.rs000064400000000000000000000112601046102023000142260ustar 00000000000000use std::ffi::CString; /// A `dlerror` error. pub struct DlDescription(pub(crate) CString); impl std::fmt::Debug for DlDescription { fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { std::fmt::Debug::fmt(&self.0, f) } } /// A Windows API error. pub struct WindowsError(pub(crate) std::io::Error); impl std::fmt::Debug for WindowsError { fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { std::fmt::Debug::fmt(&self.0, f) } } /// Errors. #[derive(Debug)] #[non_exhaustive] pub enum Error { /// The `dlopen` call failed. DlOpen { /// The source error. desc: DlDescription }, /// The `dlopen` call failed and system did not report an error. DlOpenUnknown, /// The `dlsym` call failed. DlSym { /// The source error. desc: DlDescription }, /// The `dlsym` call failed and system did not report an error. DlSymUnknown, /// The `dlclose` call failed. DlClose { /// The source error. desc: DlDescription }, /// The `dlclose` call failed and system did not report an error. DlCloseUnknown, /// The `LoadLibraryW` call failed. LoadLibraryExW { /// The source error. source: WindowsError }, /// The `LoadLibraryW` call failed and system did not report an error. LoadLibraryExWUnknown, /// The `GetModuleHandleExW` call failed. GetModuleHandleExW { /// The source error. source: WindowsError }, /// The `GetModuleHandleExW` call failed and system did not report an error. GetModuleHandleExWUnknown, /// The `GetProcAddress` call failed. GetProcAddress { /// The source error. source: WindowsError }, /// The `GetProcAddressUnknown` call failed and system did not report an error. GetProcAddressUnknown, /// The `FreeLibrary` call failed. 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 { /// The source error. source: std::ffi::NulError }, /// Could not create a new CString from bytes with trailing null. CreateCStringWithTrailing { /// The source error. source: std::ffi::FromBytesWithNulError }, } impl std::error::Error for Error { fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { use Error::*; match *self { CreateCString { ref source } => Some(source), CreateCStringWithTrailing { ref source } => Some(source), LoadLibraryExW { ref source } => Some(&source.0), GetProcAddress { ref source } => Some(&source.0), FreeLibrary { ref source } => Some(&source.0), _ => None, } } } impl std::fmt::Display for Error { fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { use Error::*; match *self { DlOpen { ref desc } => write!(f, "{}", desc.0.to_string_lossy()), DlOpenUnknown => write!(f, "dlopen failed, but system did not report the error"), DlSym { ref desc } => write!(f, "{}", desc.0.to_string_lossy()), DlSymUnknown => write!(f, "dlsym failed, but system did not report the error"), DlClose { ref desc } => write!(f, "{}", desc.0.to_string_lossy()), DlCloseUnknown => write!(f, "dlclose failed, but system did not report the error"), LoadLibraryExW { .. } => write!(f, "LoadLibraryExW failed"), LoadLibraryExWUnknown => write!(f, "LoadLibraryExW failed, but system did not report the error"), GetModuleHandleExW { .. } => write!(f, "GetModuleHandleExW failed"), GetModuleHandleExWUnknown => write!(f, "GetModuleHandleExWUnknown failed, but system did not report the error"), GetProcAddress { .. } => write!(f, "GetProcAddress failed"), GetProcAddressUnknown => write!(f, "GetProcAddress failed, but system did not report the error"), FreeLibrary { .. } => write!(f, "FreeLibrary failed"), FreeLibraryUnknown => write!(f, "FreeLibrary failed, but system did not report the error"), CreateCString { .. } => write!(f, "could not create a C string from bytes"), CreateCStringWithTrailing { .. } => write!(f, "could not create a C string from bytes with trailing null"), IncompatibleSize => write!(f, "requested type cannot possibly work"), } } } libloading-0.7.4/src/lib.rs000064400000000000000000000052431046102023000136470ustar 00000000000000//! Bindings around the platform's dynamic library loading primitives with greatly improved memory safety. //! //! 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. //! //! 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. //! //! # Installation //! //! Add the `libloading` library to your dependencies in `Cargo.toml`: //! //! ```toml //! [dependencies] //! libloading = "0.7" //! ``` //! //! # Usage //! //! In your code, run the following: //! //! ```no_run //! fn call_dynamic() -> Result> { //! unsafe { //! let lib = libloading::Library::new("/path/to/liblibrary.so")?; //! let func: libloading::Symbol u32> = lib.get(b"my_func")?; //! Ok(func()) //! } //! } //! ``` //! //! The compiler will ensure that the loaded function will not outlive the `Library` 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))] pub mod changelog; pub mod os; mod util; mod error; pub use self::error::Error; #[cfg(any(unix, windows, libloading_docs))] mod safe; #[cfg(any(unix, windows, libloading_docs))] pub use self::safe::{Library, Symbol}; 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. /// /// This function will prepend prefixes (such as `lib`) and suffixes (such as `.so`) to the library /// `name` to construct the filename. /// /// # Examples /// /// It can be used to load global libraries in a platform independent manner: /// /// ``` /// use libloading::{Library, library_filename}; /// // Will attempt to load `libLLVM.so` on Linux, `libLLVM.dylib` on macOS and `LLVM.dll` on /// // Windows. /// let library = unsafe { /// Library::new(library_filename("LLVM")) /// }; /// ``` pub fn library_filename>(name: S) -> OsString { let name = name.as_ref(); let mut string = OsString::with_capacity(name.len() + DLL_PREFIX.len() + DLL_SUFFIX.len()); string.push(DLL_PREFIX); string.push(name); string.push(DLL_SUFFIX); string } libloading-0.7.4/src/os/mod.rs000064400000000000000000000016151046102023000143000ustar 00000000000000//! Unsafe but flexible platform-specific bindings to dynamic library loading facilities. //! //! 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. //! //! # Examples //! //! Using these modules will likely involve conditional compilation: //! //! ```ignore //! # extern crate libloading; //! #[cfg(unix)] //! use libloading::os::unix::*; //! #[cfg(windows)] //! use libloading::os::windows::*; //! ``` /// UNIX implementation of dynamic library loading. #[cfg(any(unix, libloading_docs))] #[cfg_attr(libloading_docs, doc(cfg(unix)))] pub mod unix; /// Windows implementation of dynamic library loading. #[cfg(any(windows, libloading_docs))] #[cfg_attr(libloading_docs, doc(cfg(windows)))] pub mod windows; libloading-0.7.4/src/os/unix/consts.rs000064400000000000000000000170051046102023000160150ustar 00000000000000use std::os::raw::c_int; /// Perform lazy binding. /// /// Relocations shall be performed at an implementation-defined time, ranging from the time /// of the [`Library::open`] call until the first reference to a given symbol occurs. /// 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 behaviour mimics the normal handling of process execution. /// /// Conflicts with [`RTLD_NOW`]. /// /// [`Library::open`]: crate::os::unix::Library::open pub const RTLD_LAZY: c_int = posix::RTLD_LAZY; /// Perform eager binding. /// /// 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 behaviour may be useful for applications that need to /// know that all symbols referenced during execution will be available before /// [`Library::open`] returns. /// /// Conflicts with [`RTLD_LAZY`]. /// /// [`Library::open`]: crate::os::unix::Library::open pub const RTLD_NOW: c_int = posix::RTLD_NOW; /// Make loaded symbols available for resolution globally. /// /// The executable object file's symbols shall be made available for relocation processing of any /// other executable object file. In addition, calls to [`Library::get`] on `Library` obtained from /// [`Library::this`] allows executable object files loaded with this mode to be searched. /// /// [`Library::this`]: crate::os::unix::Library::this /// [`Library::get`]: crate::os::unix::Library::get pub const RTLD_GLOBAL: c_int = posix::RTLD_GLOBAL; /// Load symbols into an isolated namespace. /// /// The executable object file's symbols shall not be made available for relocation processing of /// 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(libloading_docs, not(unix)))] mod posix { use super::c_int; pub(super) const RTLD_LAZY: c_int = !0; pub(super) const RTLD_NOW: c_int = !0; pub(super) const RTLD_GLOBAL: c_int = !0; pub(super) const RTLD_LOCAL: c_int = !0; } #[cfg(any(not(libloading_docs), unix))] mod posix { extern crate cfg_if; use self::cfg_if::cfg_if; use super::c_int; cfg_if! { if #[cfg(target_os = "haiku")] { pub(super) const RTLD_LAZY: c_int = 0; } else if #[cfg(target_os = "aix")] { pub(super) const RTLD_LAZY: c_int = 4; } else if #[cfg(any( target_os = "linux", target_os = "android", target_os = "emscripten", target_os = "macos", target_os = "ios", target_os = "freebsd", target_os = "dragonfly", target_os = "openbsd", target_os = "netbsd", target_os = "solaris", target_os = "illumos", target_env = "uclibc", target_env = "newlib", target_os = "fuchsia", target_os = "redox", ))] { pub(super) const RTLD_LAZY: c_int = 1; } else { compile_error!( "Target has no known `RTLD_LAZY` value. Please submit an issue or PR adding it." ); } } cfg_if! { if #[cfg(target_os = "haiku")] { pub(super) const RTLD_NOW: c_int = 1; } else if #[cfg(any( target_os = "linux", all(target_os = "android", target_pointer_width = "64"), target_os = "emscripten", target_os = "macos", target_os = "ios", target_os = "freebsd", target_os = "dragonfly", target_os = "openbsd", target_os = "netbsd", target_os = "aix", target_os = "solaris", target_os = "illumos", target_env = "uclibc", target_env = "newlib", target_os = "fuchsia", target_os = "redox", ))] { pub(super) const RTLD_NOW: c_int = 2; } else if #[cfg(all(target_os = "android",target_pointer_width = "32"))] { pub(super) const RTLD_NOW: c_int = 0; } else { compile_error!( "Target has no known `RTLD_NOW` value. Please submit an issue or PR adding it." ); } } cfg_if! { if #[cfg(any( target_os = "haiku", all(target_os = "android",target_pointer_width = "32"), ))] { pub(super) const RTLD_GLOBAL: c_int = 2; } else if #[cfg(target_os = "aix")] { pub(super) const RTLD_GLOBAL: c_int = 0x10000; } else if #[cfg(any( target_env = "uclibc", all(target_os = "linux", target_arch = "mips"), all(target_os = "linux", target_arch = "mips64"), ))] { pub(super) const RTLD_GLOBAL: c_int = 4; } else if #[cfg(any( target_os = "macos", target_os = "ios", ))] { pub(super) const RTLD_GLOBAL: c_int = 8; } else if #[cfg(any( target_os = "linux", all(target_os = "android", target_pointer_width = "64"), target_os = "emscripten", target_os = "freebsd", target_os = "dragonfly", target_os = "openbsd", target_os = "netbsd", target_os = "solaris", target_os = "illumos", target_env = "newlib", target_os = "fuchsia", target_os = "redox", ))] { pub(super) const RTLD_GLOBAL: c_int = 0x100; } else { compile_error!( "Target has no known `RTLD_GLOBAL` value. Please submit an issue or PR adding it." ); } } cfg_if! { if #[cfg(target_os = "netbsd")] { pub(super) const RTLD_LOCAL: c_int = 0x200; } else if #[cfg(target_os = "aix")] { pub(super) const RTLD_LOCAL: c_int = 0x80000; } else if #[cfg(any( target_os = "macos", target_os = "ios", ))] { pub(super) const RTLD_LOCAL: c_int = 4; } else if #[cfg(any( target_os = "linux", target_os = "android", target_os = "emscripten", target_os = "freebsd", target_os = "dragonfly", target_os = "openbsd", target_os = "haiku", target_os = "solaris", target_os = "illumos", target_env = "uclibc", target_env = "newlib", target_os = "fuchsia", target_os = "redox", ))] { pub(super) const RTLD_LOCAL: c_int = 0; } else { compile_error!( "Target has no known `RTLD_LOCAL` value. Please submit an issue or PR adding it." ); } } } // Other constants that exist but are not bound because they are platform-specific (non-posix) // extensions. Some of these constants are only relevant to `dlsym` or `dlmopen` calls. // // RTLD_CONFGEN // RTLD_DEFAULT // RTLD_DI_CONFIGADDR // RTLD_DI_LINKMAP // RTLD_DI_LMID // RTLD_DI_ORIGIN // RTLD_DI_PROFILENAME // RTLD_DI_PROFILEOUT // RTLD_DI_SERINFO // RTLD_DI_SERINFOSIZE // RTLD_DI_TLS_DATA // RTLD_DI_TLS_MODID // RTLD_FIRST // RTLD_GROUP // RTLD_NEXT // RTLD_PARENT // RTLD_PROBE // RTLD_SELF // RTLD_WORLD // RTLD_NODELETE // RTLD_NOLOAD // RTLD_DEEPBIND libloading-0.7.4/src/os/unix/mod.rs000064400000000000000000000444211046102023000152650ustar 00000000000000// A hack for docs.rs to build documentation that has both windows and linux documentation in the // same rustdoc build visible. #[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 std::ffi::{CStr, OsStr}; use std::os::raw; use std::{fmt, marker, mem, ptr}; use util::{cstr_cow_from_bytes, ensure_compatible_types}; mod consts; // dl* family of functions did not have enough thought put into it. // // Whole error handling scheme is done via setting and querying some global state, therefore it is // not safe to use dynamic library loading in MT-capable environment at all. Only in POSIX 2008+TC1 // a thread-local state was allowed for `dlerror`, making the dl* family of functions MT-safe. // // In practice (as of 2020-04-01) most of the widely used targets use a thread-local for error // state and have been doing so for a long time. Regardless the comments in this function shall // remain as a documentation for the future generations. fn with_dlerror(wrap: fn(crate::error::DlDescription) -> crate::Error, closure: F) -> Result> where F: FnOnce() -> Option { // We used to guard all uses of dl* functions with our own mutex. This made them safe to use in // MT programs provided the only way a program used dl* was via this library. However, it also // had a number of downsides or cases where it failed to handle the problems. For instance, // if any other library called `dlerror` internally concurrently with `libloading` things would // still go awry. // // On platforms where `dlerror` is still MT-unsafe, `dlsym` (`Library::get`) can spuriously // succeed and return a null pointer for a symbol when the actual symbol look-up operation // fails. Instances where the actual symbol _could_ be `NULL` are platform specific. For // instance on GNU glibc based-systems (an excerpt from dlsym(3)): // // > The value of a symbol returned by dlsym() will never be NULL if the shared object is the // > result of normal compilation, since a global symbol is never placed at the NULL // > address. There are nevertheless cases where a lookup using dlsym() may return NULL as the // > value of a symbol. For example, the symbol value may be the result of a GNU indirect // > function (IFUNC) resolver function that returns NULL as the resolved value. // While we could could call `dlerror` here to clear the previous error value, only the `dlsym` // call depends on it being cleared beforehand and only in some cases too. We will instead // clear the error inside the dlsym binding instead. // // In all the other cases, clearing the error here will only be hiding misuse of these bindings // or a bug in implementation of dl* family of functions. closure().ok_or_else(|| unsafe { // This code will only get executed if the `closure` returns `None`. let error = dlerror(); if error.is_null() { // In non-dlsym case this may happen when there’re bugs in our bindings or there’s // non-libloading user of libdl; possibly in another thread. None } else { // You can’t even rely on error string being static here; call to subsequent dlerror // may invalidate or overwrite the error message. Why couldn’t they simply give up the // ownership over the message? // TODO: should do locale-aware conversion here. OTOH Rust doesn’t seem to work well in // any system that uses non-utf8 locale, so I doubt there’s a problem here. let message = CStr::from_ptr(error).into(); Some(wrap(crate::error::DlDescription(message))) // Since we do a copy of the error string above, maybe we should call dlerror again to // let libdl know it may free its copy of the string now? } }) } /// A platform-specific counterpart of the cross-platform [`Library`](crate::Library). pub struct Library { handle: *mut raw::c_void } unsafe impl Send for Library {} // That being said... this section in the volume 2 of POSIX.1-2008 states: // // > All functions defined by this volume of POSIX.1-2008 shall be thread-safe, except that the // > following functions need not be thread-safe. // // With notable absence of any dl* function other than dlerror in the list. By “this volume” // I suppose they refer precisely to the “volume 2”. dl* family of functions are specified // by this same volume, so the conclusion is indeed that dl* functions are required by POSIX // to be thread-safe. Great! // // See for more details: // // * https://github.com/nagisa/rust_libloading/pull/17 // * http://pubs.opengroup.org/onlinepubs/9699919799/functions/V2_chap02.html#tag_15_09_01 unsafe impl Sync for Library {} impl Library { /// Find and eagerly load a shared library (module). /// /// 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. /// /// This is equivalent to [Library::open](filename, [RTLD_LAZY] | [RTLD_LOCAL]). /// /// [path separator]: std::path::MAIN_SEPARATOR /// /// # Safety /// /// 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. /// /// Additionally, the callers of this function must also ensure that execution of the /// termination routines contained within the library is safe as well. These routines may be /// executed when the library is unloaded. #[inline] pub unsafe fn new>(filename: P) -> Result { Library::open(Some(filename), RTLD_LAZY | RTLD_LOCAL) } /// Load the `Library` representing the current executable. /// /// [`Library::get`] calls of the returned `Library` will look for symbols in following /// locations in order: /// /// 1. The original program image; /// 2. Any executable object files (e.g. shared libraries) loaded at program startup; /// 3. Any executable object files loaded at runtime (e.g. via other `Library::new` calls or via /// calls to the `dlopen` function). /// /// 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 [Library::open](None, [RTLD_LAZY] | [RTLD_LOCAL]). /// /// [`os::windows::Library::this`]: crate::os::windows::Library::this #[inline] pub fn this() -> Library { unsafe { // SAFE: this does not load any new shared library images, no danger in it executing // 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 the behaviour /// when the `filename` is `None`. Otherwise see [`Library::new`]. /// /// Corresponds to `dlopen(filename, flags)`. /// /// # Safety /// /// 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. /// /// Additionally, the callers of this function must also ensure that execution of the /// termination routines contained within the library is safe as well. These routines may be /// executed when the library is unloaded. pub unsafe fn open

(filename: Option

, flags: raw::c_int) -> Result where P: AsRef { let filename = match filename { None => None, Some(ref f) => Some(cstr_cow_from_bytes(f.as_ref().as_bytes())?), }; with_dlerror(|desc| crate::Error::DlOpen { desc }, move || { let result = dlopen(match filename { None => ptr::null(), Some(ref f) => f.as_ptr() }, flags); // ensure filename lives until dlopen completes drop(filename); if result.is_null() { None } else { Some(Library { handle: result }) } }).map_err(|e| e.unwrap_or(crate::Error::DlOpenUnknown)) } unsafe fn get_impl(&self, symbol: &[u8], on_null: F) -> Result, crate::Error> where F: FnOnce() -> Result, crate::Error> { ensure_compatible_types::()?; let symbol = cstr_cow_from_bytes(symbol)?; // `dlsym` may return nullptr in two cases: when a symbol genuinely points to a null // pointer or the symbol cannot be found. In order to detect this case a double dlerror // pattern must be used, which is, sadly, a little bit racy. // // We try to leave as little space as possible for this to occur, but we can’t exactly // fully prevent it. match with_dlerror(|desc| crate::Error::DlSym { desc }, || { dlerror(); let symbol = dlsym(self.handle, symbol.as_ptr()); if symbol.is_null() { None } else { Some(Symbol { pointer: symbol, pd: marker::PhantomData }) } }) { Err(None) => on_null(), Err(Some(e)) => Err(e), Ok(x) => Ok(x) } } /// 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. /// /// 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 [`Library::get_singlethreaded`] call. #[inline(always)] pub unsafe fn get(&self, symbol: &[u8]) -> Result, crate::Error> { extern crate cfg_if; cfg_if::cfg_if! { // These targets are known to have MT-safe `dlerror`. if #[cfg(any( target_os = "linux", target_os = "android", target_os = "openbsd", target_os = "macos", target_os = "ios", target_os = "solaris", target_os = "illumos", target_os = "redox", target_os = "fuchsia" ))] { self.get_singlethreaded(symbol) } else { self.get_impl(symbol, || Err(crate::Error::DlSymUnknown)) } } } /// Get a pointer to 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. /// /// 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. /// /// It is up to the user of this library to ensure that no other calls to an MT-unsafe /// implementation of `dlerror` occur during the execution of this function. Failing that, the /// behaviour of this function is not defined. /// /// # 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. #[inline(always)] pub unsafe fn get_singlethreaded(&self, symbol: &[u8]) -> Result, crate::Error> { self.get_impl(symbol, || Ok(Symbol { pointer: ptr::null_mut(), pd: marker::PhantomData })) } /// Convert the `Library` to a raw handle. /// /// The handle returned by this function shall be usable with APIs which accept handles /// as returned by `dlopen`. pub fn into_raw(self) -> *mut raw::c_void { let handle = self.handle; mem::forget(self); handle } /// Convert a raw handle returned by `dlopen`-family of calls to a `Library`. /// /// # 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` /// with this pointer as an argument. pub unsafe fn from_raw(handle: *mut raw::c_void) -> Library { Library { handle } } /// 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<(), crate::Error> { let result = with_dlerror(|desc| crate::Error::DlClose { desc }, || { if unsafe { dlclose(self.handle) } == 0 { Some(()) } else { None } }).map_err(|e| e.unwrap_or(crate::Error::DlCloseUnknown)); // While the library is not free'd yet in case of an error, there is no reason to try // dropping it again, because all that will do is try calling `dlclose` again. only // this time it would ignore the return result, which we already seen failing… std::mem::forget(self); result } } impl Drop for Library { fn drop(&mut self) { unsafe { dlclose(self.handle); } } } impl fmt::Debug for Library { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { f.write_str(&format!("Library@{:p}", self.handle)) } } /// Symbol from a library. /// /// 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 { pointer: *mut raw::c_void, pd: marker::PhantomData } impl Symbol { /// Convert the loaded `Symbol` into a raw pointer. pub fn into_raw(self) -> *mut raw::c_void { self.pointer } } impl Symbol> { /// Lift Option out of the symbol. pub fn lift_option(self) -> Option> { if self.pointer.is_null() { None } else { Some(Symbol { pointer: self.pointer, pd: marker::PhantomData, }) } } } unsafe impl Send for Symbol {} unsafe impl Sync for Symbol {} impl Clone for Symbol { fn clone(&self) -> Symbol { Symbol { ..*self } } } impl ::std::ops::Deref for Symbol { type Target = T; fn deref(&self) -> &T { unsafe { // Additional reference level for a dereference on `deref` return value. &*(&self.pointer as *const *mut _ as *const T) } } } impl fmt::Debug for Symbol { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { unsafe { let mut info = mem::MaybeUninit::::uninit(); if dladdr(self.pointer, info.as_mut_ptr()) != 0 { let info = info.assume_init(); if info.dli_sname.is_null() { f.write_str(&format!("Symbol@{:p} from {:?}", self.pointer, CStr::from_ptr(info.dli_fname))) } else { f.write_str(&format!("Symbol {:?}@{:p} from {:?}", CStr::from_ptr(info.dli_sname), self.pointer, CStr::from_ptr(info.dli_fname))) } } else { f.write_str(&format!("Symbol@{:p}", self.pointer)) } } } } // Platform specific things #[cfg_attr(any(target_os = "linux", target_os = "android"), link(name="dl"))] #[cfg_attr(any(target_os = "freebsd", target_os = "dragonfly"), link(name="c"))] extern { fn dlopen(filename: *const raw::c_char, flags: raw::c_int) -> *mut raw::c_void; fn dlclose(handle: *mut raw::c_void) -> raw::c_int; fn dlsym(handle: *mut raw::c_void, symbol: *const raw::c_char) -> *mut raw::c_void; fn dlerror() -> *mut raw::c_char; fn dladdr(addr: *mut raw::c_void, info: *mut DlInfo) -> raw::c_int; } #[repr(C)] struct DlInfo { dli_fname: *const raw::c_char, dli_fbase: *mut raw::c_void, dli_sname: *const raw::c_char, dli_saddr: *mut raw::c_void } libloading-0.7.4/src/os/windows/mod.rs000064400000000000000000000602011046102023000157660ustar 00000000000000// A hack for docs.rs to build documentation that has both windows and linux documentation in the // same rustdoc build visible. #[cfg(all(libloading_docs, not(windows)))] mod windows_imports { pub(super) enum WORD {} pub(super) struct DWORD; pub(super) enum HMODULE {} pub(super) enum FARPROC {} pub(super) mod consts { use super::DWORD; pub(crate) const LOAD_IGNORE_CODE_AUTHZ_LEVEL: DWORD = DWORD; pub(crate) const LOAD_LIBRARY_AS_DATAFILE: DWORD = DWORD; pub(crate) const LOAD_LIBRARY_AS_DATAFILE_EXCLUSIVE: DWORD = DWORD; pub(crate) const LOAD_LIBRARY_AS_IMAGE_RESOURCE: DWORD = DWORD; pub(crate) const LOAD_LIBRARY_SEARCH_APPLICATION_DIR: DWORD = DWORD; pub(crate) const LOAD_LIBRARY_SEARCH_DEFAULT_DIRS: DWORD = DWORD; pub(crate) const LOAD_LIBRARY_SEARCH_DLL_LOAD_DIR: DWORD = DWORD; pub(crate) const LOAD_LIBRARY_SEARCH_SYSTEM32: DWORD = DWORD; pub(crate) const LOAD_LIBRARY_SEARCH_USER_DIRS: DWORD = DWORD; pub(crate) const LOAD_WITH_ALTERED_SEARCH_PATH: DWORD = DWORD; pub(crate) const LOAD_LIBRARY_REQUIRE_SIGNED_TARGET: DWORD = DWORD; pub(crate) const LOAD_LIBRARY_SAFE_CURRENT_DIRS: DWORD = DWORD; } } #[cfg(any(not(libloading_docs), 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::um::{errhandlingapi, libloaderapi}; pub(super) use std::os::windows::ffi::{OsStrExt, OsStringExt}; pub(super) const SEM_FAILCE: DWORD = 1; pub(super) mod consts { pub(crate) use super::winapi::um::libloaderapi::{ LOAD_IGNORE_CODE_AUTHZ_LEVEL, LOAD_LIBRARY_AS_DATAFILE, LOAD_LIBRARY_AS_DATAFILE_EXCLUSIVE, LOAD_LIBRARY_AS_IMAGE_RESOURCE, LOAD_LIBRARY_SEARCH_APPLICATION_DIR, LOAD_LIBRARY_SEARCH_DEFAULT_DIRS, LOAD_LIBRARY_SEARCH_DLL_LOAD_DIR, LOAD_LIBRARY_SEARCH_SYSTEM32, LOAD_LIBRARY_SEARCH_USER_DIRS, LOAD_WITH_ALTERED_SEARCH_PATH, LOAD_LIBRARY_REQUIRE_SIGNED_TARGET, LOAD_LIBRARY_SAFE_CURRENT_DIRS, }; } } 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}; /// 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. // // 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 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 filed at https://connect.microsoft.com/ against Windows Server) unsafe impl Sync for Library {} impl Library { /// Find and load a module. /// /// 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]. /// /// 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 [Library::load_with_flags](filename, 0). /// /// [msdn]: https://docs.microsoft.com/en-us/windows/win32/api/libloaderapi/nf-libloaderapi-loadlibraryw#remarks /// /// # Safety /// /// When a library is loaded, 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. /// /// Additionally, the callers of this function must also ensure that execution of the /// termination routines contained within the library is safe as well. These routines may be /// executed when the library is unloaded. #[inline] pub unsafe fn new>(filename: P) -> Result { Library::load_with_flags(filename, 0) } /// Get the `Library` representing the original program executable. /// /// 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, _)`. /// /// [`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 { 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 { None } else { Some(Library(handle)) } }).map_err(|e| e.unwrap_or(crate::Error::GetModuleHandleExWUnknown)) } } /// 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 /// 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 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, _)`. /// /// [MSDN]: https://docs.microsoft.com/en-us/windows/win32/api/libloaderapi/nf-libloaderapi-getmodulehandleexw pub fn open_already_loaded>(filename: P) -> Result { let wide_filename: Vec = filename.as_ref().encode_wide().chain(Some(0)).collect(); let ret = unsafe { let mut handle: HMODULE = std::ptr::null_mut(); with_get_last_error(|source| crate::Error::GetModuleHandleExW { source }, || { // Make sure no winapi calls as a result of drop happen inside this closure, because // otherwise that might change the return value of the GetLastError. let result = libloaderapi::GetModuleHandleExW(0, wide_filename.as_ptr(), &mut handle); if result == 0 { None } else { Some(Library(handle)) } }).map_err(|e| e.unwrap_or(crate::Error::GetModuleHandleExWUnknown)) }; drop(wide_filename); // Drop wide_filename here to ensure it doesn’t get moved and dropped // inside the closure by mistake. See comment inside the closure. ret } /// Find and load a module, additionally adjusting behaviour with flags. /// /// 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)`. /// /// [flags]: https://docs.microsoft.com/en-us/windows/win32/api/libloaderapi/nf-libloaderapi-loadlibraryexw#parameters /// /// # Safety /// /// 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. /// /// Additionally, the callers of this function must also ensure that execution of the /// termination routines contained within the library is safe as well. These routines may be /// executed when the library is unloaded. pub unsafe fn load_with_flags>(filename: P, flags: DWORD) -> Result { let wide_filename: Vec = filename.as_ref().encode_wide().chain(Some(0)).collect(); let _guard = ErrorModeGuard::new(); let ret = with_get_last_error(|source| crate::Error::LoadLibraryExW { source }, || { // Make sure no winapi calls as a result of drop happen inside this closure, because // otherwise that might change the return value of the GetLastError. let handle = libloaderapi::LoadLibraryExW(wide_filename.as_ptr(), std::ptr::null_mut(), flags); if handle.is_null() { None } else { Some(Library(handle)) } }).map_err(|e| e.unwrap_or(crate::Error::LoadLibraryExWUnknown)); drop(wide_filename); // Drop wide_filename here to ensure it doesn’t get moved and dropped // inside the closure by mistake. See comment inside the closure. ret } /// 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. 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 /// most likely invalid. /// /// # Safety /// /// Users of this API must specify the correct type of the function or variable loaded. pub unsafe fn get(&self, symbol: &[u8]) -> Result, crate::Error> { ensure_compatible_types::()?; let symbol = cstr_cow_from_bytes(symbol)?; with_get_last_error(|source| crate::Error::GetProcAddress { source }, || { let symbol = libloaderapi::GetProcAddress(self.0, symbol.as_ptr()); if symbol.is_null() { None } else { Some(Symbol { pointer: symbol, pd: marker::PhantomData }) } }).map_err(|e| e.unwrap_or(crate::Error::GetProcAddressUnknown)) } /// 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. pub unsafe fn get_ordinal(&self, ordinal: WORD) -> Result, crate::Error> { ensure_compatible_types::()?; with_get_last_error(|source| crate::Error::GetProcAddress { source }, || { let ordinal = ordinal as usize as *mut _; let symbol = libloaderapi::GetProcAddress(self.0, ordinal); if symbol.is_null() { None } else { Some(Symbol { pointer: symbol, pd: marker::PhantomData }) } }).map_err(|e| e.unwrap_or(crate::Error::GetProcAddressUnknown)) } /// Convert the `Library` to a raw handle. pub fn into_raw(self) -> HMODULE { let handle = self.0; mem::forget(self); handle } /// Convert a raw handle to a `Library`. /// /// # Safety /// /// 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) } /// Unload the library. /// /// You only need to call this if you are interested in handling any errors that may arise when /// library is unloaded. Otherwise this will be done when `Library` is dropped. /// /// The underlying data structures may still get leaked if an error does occur. pub fn close(self) -> Result<(), crate::Error> { let result = with_get_last_error(|source| crate::Error::FreeLibrary { source }, || { if unsafe { libloaderapi::FreeLibrary(self.0) == 0 } { None } else { Some(()) } }).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... std::mem::forget(self); result } } impl Drop for Library { fn drop(&mut self) { unsafe { libloaderapi::FreeLibrary(self.0); } } } impl fmt::Debug for Library { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { unsafe { // FIXME: use Maybeuninit::uninit_array when stable let mut buf = mem::MaybeUninit::<[mem::MaybeUninit::; 1024]>::uninit().assume_init(); let len = libloaderapi::GetModuleFileNameW(self.0, buf[..].as_mut_ptr().cast(), 1024) as usize; if len == 0 { f.write_str(&format!("Library@{:p}", self.0)) } else { let string: OsString = OsString::from_wide( // FIXME: use Maybeuninit::slice_get_ref when stable &*(&buf[..len] as *const [_] as *const [WCHAR]) ); f.write_str(&format!("Library@{:p} from {:?}", self.0, string)) } } } } /// A symbol from a library. /// /// 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 { pointer: FARPROC, pd: marker::PhantomData } impl Symbol { /// Convert the loaded `Symbol` into a handle. pub fn into_raw(self) -> FARPROC { self.pointer } } impl Symbol> { /// Lift Option out of the symbol. pub fn lift_option(self) -> Option> { if self.pointer.is_null() { None } else { Some(Symbol { pointer: self.pointer, pd: marker::PhantomData, }) } } } unsafe impl Send for Symbol {} unsafe impl Sync for Symbol {} impl Clone for Symbol { fn clone(&self) -> Symbol { Symbol { ..*self } } } impl ::std::ops::Deref for Symbol { type Target = T; fn deref(&self) -> &T { unsafe { // Additional reference level for a dereference on `deref` return value. &*(&self.pointer as *const *mut _ as *const T) } } } impl fmt::Debug for Symbol { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { f.write_str(&format!("Symbol@{:p}", self.pointer)) } } struct ErrorModeGuard(DWORD); impl ErrorModeGuard { #[allow(clippy::if_same_then_else)] fn new() -> Option { unsafe { let mut previous_mode = 0; if errhandlingapi::SetThreadErrorMode(SEM_FAILCE, &mut previous_mode) == 0 { // How in the world is it possible for what is essentially a simple variable swap // to fail? For now we just ignore the error -- the worst that can happen here is // the previous mode staying on and user seeing a dialog error on older Windows // machines. None } else if previous_mode == SEM_FAILCE { None } else { Some(ErrorModeGuard(previous_mode)) } } } } impl Drop for ErrorModeGuard { fn drop(&mut self) { unsafe { errhandlingapi::SetThreadErrorMode(self.0, ptr::null_mut()); } } } fn with_get_last_error(wrap: fn(crate::error::WindowsError) -> crate::Error, closure: F) -> Result> where F: FnOnce() -> Option { closure().ok_or_else(|| { let error = unsafe { errhandlingapi::GetLastError() }; if error == 0 { None } else { Some(wrap(crate::error::WindowsError(io::Error::from_raw_os_error(error as i32)))) } }) } /// Do not check AppLocker rules or apply Software Restriction Policies for the DLL. /// /// This action applies only to the DLL being loaded and not to its dependencies. This value is /// recommended for use in setup programs that must run extracted DLLs during installation. /// /// See [flag documentation on MSDN](https://docs.microsoft.com/en-us/windows/win32/api/libloaderapi/nf-libloaderapi-loadlibraryexw#parameters). pub const LOAD_IGNORE_CODE_AUTHZ_LEVEL: DWORD = consts::LOAD_IGNORE_CODE_AUTHZ_LEVEL; /// Map the file into the calling process’ virtual address space as if it were a data file. /// /// Nothing is done to execute or prepare to execute the mapped file. Therefore, you cannot call /// functions like [`Library::get`] with this DLL. Using this value causes writes to read-only /// memory to raise an access violation. Use this flag when you want to load a DLL only to extract /// messages or resources from it. /// /// See [flag documentation on MSDN](https://docs.microsoft.com/en-us/windows/win32/api/libloaderapi/nf-libloaderapi-loadlibraryexw#parameters). pub const LOAD_LIBRARY_AS_DATAFILE: DWORD = consts::LOAD_LIBRARY_AS_DATAFILE; /// Map the file into the calling process’ virtual address space as if it were a data file. /// /// Similar to [`LOAD_LIBRARY_AS_DATAFILE`], except that the DLL file is opened with exclusive /// write access for the calling process. Other processes cannot open the DLL file for write access /// while it is in use. However, the DLL can still be opened by other processes. /// /// See [flag documentation on MSDN](https://docs.microsoft.com/en-us/windows/win32/api/libloaderapi/nf-libloaderapi-loadlibraryexw#parameters). pub const LOAD_LIBRARY_AS_DATAFILE_EXCLUSIVE: DWORD = consts::LOAD_LIBRARY_AS_DATAFILE_EXCLUSIVE; /// 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 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 /// should be used with either [`LOAD_LIBRARY_AS_DATAFILE_EXCLUSIVE`] or /// [`LOAD_LIBRARY_AS_DATAFILE`]. /// /// 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 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`]. /// /// See [flag documentation on MSDN](https://docs.microsoft.com/en-us/windows/win32/api/libloaderapi/nf-libloaderapi-loadlibraryexw#parameters). pub const LOAD_LIBRARY_SEARCH_APPLICATION_DIR: DWORD = consts::LOAD_LIBRARY_SEARCH_APPLICATION_DIR; /// Search default directories when looking for the DLL and its dependencies. /// /// This value is a combination of [`LOAD_LIBRARY_SEARCH_APPLICATION_DIR`], /// [`LOAD_LIBRARY_SEARCH_SYSTEM32`], and [`LOAD_LIBRARY_SEARCH_USER_DIRS`]. Directories in the /// standard search path are not searched. This value cannot be combined with /// [`LOAD_WITH_ALTERED_SEARCH_PATH`]. /// /// See [flag documentation on MSDN](https://docs.microsoft.com/en-us/windows/win32/api/libloaderapi/nf-libloaderapi-loadlibraryexw#parameters). pub const LOAD_LIBRARY_SEARCH_DEFAULT_DIRS: DWORD = consts::LOAD_LIBRARY_SEARCH_DEFAULT_DIRS; /// Directory that contains the DLL is temporarily added to the beginning of the list of /// directories that are searched for the DLL’s dependencies. /// /// Directories in the standard search path are not searched. /// /// The `filename` parameter must specify a fully qualified path. This value cannot be combined /// with [`LOAD_WITH_ALTERED_SEARCH_PATH`]. /// /// See [flag documentation on MSDN](https://docs.microsoft.com/en-us/windows/win32/api/libloaderapi/nf-libloaderapi-loadlibraryexw#parameters). pub const LOAD_LIBRARY_SEARCH_DLL_LOAD_DIR: DWORD = consts::LOAD_LIBRARY_SEARCH_DLL_LOAD_DIR; /// Search `%windows%\system32` 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`]. /// /// See [flag documentation on MSDN](https://docs.microsoft.com/en-us/windows/win32/api/libloaderapi/nf-libloaderapi-loadlibraryexw#parameters). pub const LOAD_LIBRARY_SEARCH_SYSTEM32: DWORD = consts::LOAD_LIBRARY_SEARCH_SYSTEM32; /// Directories added using the `AddDllDirectory` or the `SetDllDirectory` function are searched /// for the DLL and its dependencies. /// /// If more than one directory has been added, the order in which the directories are searched is /// unspecified. Directories in the standard search path are not searched. This value cannot be /// combined with [`LOAD_WITH_ALTERED_SEARCH_PATH`]. /// /// 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 /// 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 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 /// modules that the specified module causes to be loaded. /// /// See [flag documentation on MSDN](https://docs.microsoft.com/en-us/windows/win32/api/libloaderapi/nf-libloaderapi-loadlibraryexw#parameters). /// /// [Remarks]: https://docs.microsoft.com/en-us/windows/win32/api/libloaderapi/nf-libloaderapi-loadlibraryexw#remarks pub const LOAD_WITH_ALTERED_SEARCH_PATH: DWORD = consts::LOAD_WITH_ALTERED_SEARCH_PATH; /// Specifies that the digital signature of the binary image must be checked at load time. /// /// See [flag documentation on MSDN](https://docs.microsoft.com/en-us/windows/win32/api/libloaderapi/nf-libloaderapi-loadlibraryexw#parameters). pub const LOAD_LIBRARY_REQUIRE_SIGNED_TARGET: DWORD = consts::LOAD_LIBRARY_REQUIRE_SIGNED_TARGET; /// Allow loading a DLL for execution from the current directory only if it is under a directory in /// the Safe load list. /// /// See [flag documentation on MSDN](https://docs.microsoft.com/en-us/windows/win32/api/libloaderapi/nf-libloaderapi-loadlibraryexw#parameters). pub const LOAD_LIBRARY_SAFE_CURRENT_DIRS: DWORD = consts::LOAD_LIBRARY_SAFE_CURRENT_DIRS; libloading-0.7.4/src/safe.rs000064400000000000000000000255121046102023000140200ustar 00000000000000use 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>(filename: P) -> Result { 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 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, 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 for Library { fn from(lib: imp::Library) -> Library { Library(lib) } } impl From 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, 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 { 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(sym: imp::Symbol, 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> { /// 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> = 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> { 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> {} libloading-0.7.4/src/test_helpers.rs000064400000000000000000000013531046102023000156000ustar 00000000000000//! This is a separate file containing helpers for tests of this library. It is built into a //! dynamic library by the build.rs script. #![crate_type="cdylib"] #[no_mangle] pub static mut TEST_STATIC_U32: u32 = 0; #[no_mangle] pub static mut TEST_STATIC_PTR: *mut () = 0 as *mut _; #[no_mangle] pub extern "C" fn test_identity_u32(x: u32) -> u32 { x } #[repr(C)] pub struct S { a: u64, b: u32, c: u16, d: u8 } #[no_mangle] pub extern "C" fn test_identity_struct(x: S) -> S { x } #[no_mangle] pub unsafe extern "C" fn test_get_static_u32() -> u32 { TEST_STATIC_U32 } #[no_mangle] pub unsafe extern "C" fn test_check_static_ptr() -> bool { TEST_STATIC_PTR == (&mut TEST_STATIC_PTR as *mut *mut _ as *mut _) } libloading-0.7.4/src/util.rs000064400000000000000000000020321046102023000140470ustar 00000000000000use std::borrow::Cow; use std::ffi::{CStr, CString}; use std::os::raw; use crate::Error; /// 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, Error> { static ZERO: raw::c_char = 0; Ok(match slice.last() { // 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 })?, ), // Slice with no trailing 0 Some(_) => { Cow::Owned(CString::new(slice).map_err(|source| Error::CreateCString { source })?) } }) } #[inline] pub(crate) fn ensure_compatible_types() -> Result<(), Error> { if ::std::mem::size_of::() != ::std::mem::size_of::() { Err(Error::IncompatibleSize) } else { Ok(()) } } libloading-0.7.4/tests/constants.rs000064400000000000000000000007121046102023000154640ustar 00000000000000extern crate libc; extern crate libloading; extern crate static_assertions; #[cfg(all(test, unix))] mod unix { use super::static_assertions::const_assert_eq; const_assert_eq!(libloading::os::unix::RTLD_LOCAL, libc::RTLD_LOCAL); const_assert_eq!(libloading::os::unix::RTLD_GLOBAL, libc::RTLD_GLOBAL); const_assert_eq!(libloading::os::unix::RTLD_NOW, libc::RTLD_NOW); const_assert_eq!(libloading::os::unix::RTLD_LAZY, libc::RTLD_LAZY); } libloading-0.7.4/tests/functions.rs000064400000000000000000000200501046102023000154550ustar 00000000000000#[cfg(windows)] extern crate winapi; extern crate libloading; use libloading::{Library, Symbol}; const TARGET_DIR: Option<&'static str> = option_env!("CARGO_TARGET_DIR"); const TARGET_TMPDIR: Option<&'static str> = option_env!("CARGO_TARGET_TMPDIR"); fn lib_path() -> std::path::PathBuf { [ TARGET_TMPDIR.unwrap_or(TARGET_DIR.unwrap_or("target")), "libtest_helpers.module", ] .iter() .collect() } fn make_helpers() { static ONCE: ::std::sync::Once = ::std::sync::Once::new(); ONCE.call_once(|| { let rustc = std::env::var_os("RUSTC").unwrap_or_else(|| "rustc".into()); let mut cmd = ::std::process::Command::new(rustc); cmd.arg("src/test_helpers.rs").arg("-o").arg(lib_path()); if let Some(target) = std::env::var_os("TARGET") { cmd.arg("--target").arg(target); } else { eprintln!("WARNING: $TARGET NOT SPECIFIED! BUILDING HELPER MODULE FOR NATIVE TARGET."); } assert!(cmd .status() .expect("could not compile the test helpers!") .success()); }); } #[test] fn test_id_u32() { make_helpers(); unsafe { let lib = Library::new(lib_path()).unwrap(); let f: Symbol u32> = lib.get(b"test_identity_u32\0").unwrap(); assert_eq!(42, f(42)); } } #[repr(C)] #[derive(Clone, Copy, PartialEq, Debug)] struct S { a: u64, b: u32, c: u16, d: u8, } #[test] fn test_id_struct() { make_helpers(); unsafe { let lib = Library::new(lib_path()).unwrap(); let f: Symbol S> = lib.get(b"test_identity_struct\0").unwrap(); assert_eq!( S { a: 1, b: 2, c: 3, d: 4 }, f(S { a: 1, b: 2, c: 3, d: 4 }) ); } } #[test] fn test_0_no_0() { make_helpers(); unsafe { let lib = Library::new(lib_path()).unwrap(); let f: Symbol S> = lib.get(b"test_identity_struct\0").unwrap(); let f2: Symbol S> = lib.get(b"test_identity_struct").unwrap(); assert_eq!(*f, *f2); } } #[test] fn wrong_name_fails() { unsafe { Library::new("target/this_location_is_definitely_non existent:^~") .err() .unwrap(); } } #[test] fn missing_symbol_fails() { make_helpers(); unsafe { let lib = Library::new(lib_path()).unwrap(); lib.get::<*mut ()>(b"test_does_not_exist").err().unwrap(); lib.get::<*mut ()>(b"test_does_not_exist\0").err().unwrap(); } } #[test] fn interior_null_fails() { make_helpers(); unsafe { let lib = Library::new(lib_path()).unwrap(); lib.get::<*mut ()>(b"test_does\0_not_exist").err().unwrap(); lib.get::<*mut ()>(b"test\0_does_not_exist\0") .err() .unwrap(); } } #[test] fn test_incompatible_type() { make_helpers(); unsafe { let lib = Library::new(lib_path()).unwrap(); assert!(match lib.get::<()>(b"test_identity_u32\0") { Err(libloading::Error::IncompatibleSize) => true, _ => false, }) } } #[test] fn test_incompatible_type_named_fn() { make_helpers(); unsafe fn get<'a, T>(l: &'a Library, _: T) -> Result, libloading::Error> { l.get::(b"test_identity_u32\0") } unsafe { let lib = Library::new(lib_path()).unwrap(); assert!(match get(&lib, test_incompatible_type_named_fn) { Err(libloading::Error::IncompatibleSize) => true, _ => false, }) } } #[test] fn test_static_u32() { make_helpers(); unsafe { let lib = Library::new(lib_path()).unwrap(); let var: Symbol<*mut u32> = lib.get(b"TEST_STATIC_U32\0").unwrap(); **var = 42; let help: Symbol u32> = lib.get(b"test_get_static_u32\0").unwrap(); assert_eq!(42, help()); } } #[test] fn test_static_ptr() { make_helpers(); unsafe { let lib = Library::new(lib_path()).unwrap(); let var: Symbol<*mut *mut ()> = lib.get(b"TEST_STATIC_PTR\0").unwrap(); **var = *var as *mut _; let works: Symbol bool> = lib.get(b"test_check_static_ptr\0").unwrap(); assert!(works()); } } #[test] // 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. #[cfg(not(all(target_arch = "x86", target_os = "windows", target_env = "gnu")))] fn manual_close_many_times() { make_helpers(); let join_handles: Vec<_> = (0..16) .map(|_| { std::thread::spawn(|| unsafe { for _ in 0..10000 { let lib = Library::new(lib_path()).expect("open library"); let _: Symbol u32> = lib.get(b"test_identity_u32").expect("get fn"); lib.close().expect("close is successful"); } }) }) .collect(); for handle in join_handles { handle.join().expect("thread should succeed"); } } #[cfg(unix)] #[test] fn library_this_get() { use libloading::os::unix::Library; make_helpers(); // SAFE: functions are never called unsafe { let _lib = Library::new(lib_path()).unwrap(); let this = Library::this(); // Library we loaded in `_lib` (should be RTLD_LOCAL). assert!(this .get::(b"test_identity_u32") .is_err()); // Something obscure from libc... assert!(this.get::(b"freopen").is_ok()); } } #[cfg(windows)] #[test] fn library_this() { use libloading::os::windows::Library; make_helpers(); unsafe { // SAFE: well-known library without initialisers is loaded. let _lib = Library::new(lib_path()).unwrap(); let this = Library::this().expect("this library"); // SAFE: functions are never called. // Library we loaded in `_lib`. assert!(this .get::(b"test_identity_u32") .is_err()); // Something "obscure" from kernel32... assert!(this.get::(b"GetLastError").is_err()); } } #[cfg(windows)] #[test] fn works_getlasterror() { use libloading::os::windows::{Library, Symbol}; use winapi::shared::minwindef::DWORD; use winapi::um::errhandlingapi; unsafe { let lib = Library::new("kernel32.dll").unwrap(); let gle: Symbol DWORD> = lib.get(b"GetLastError").unwrap(); errhandlingapi::SetLastError(42); assert_eq!(errhandlingapi::GetLastError(), gle()) } } #[cfg(windows)] #[test] fn works_getlasterror0() { use libloading::os::windows::{Library, Symbol}; use winapi::shared::minwindef::DWORD; use winapi::um::errhandlingapi; unsafe { let lib = Library::new("kernel32.dll").unwrap(); let gle: Symbol DWORD> = lib.get(b"GetLastError\0").unwrap(); errhandlingapi::SetLastError(42); assert_eq!(errhandlingapi::GetLastError(), gle()) } } #[cfg(windows)] #[test] fn library_open_already_loaded() { use libloading::os::windows::Library; // Present on Windows systems and NOT used by any other tests to prevent races. const LIBPATH: &str = "Msftedit.dll"; // Not loaded yet. assert!(match Library::open_already_loaded(LIBPATH) { Err(libloading::Error::GetModuleHandleExW { .. }) => true, _ => false, }); unsafe { let _lib = Library::new(LIBPATH).unwrap(); // Loaded now. assert!(Library::open_already_loaded(LIBPATH).is_ok()); } } libloading-0.7.4/tests/library_filename.rs000064400000000000000000000007241046102023000167570ustar 00000000000000extern crate libloading; use libloading::library_filename; use std::path::Path; #[cfg(target_os = "windows")] const EXPECTED: &str = "audioengine.dll"; #[cfg(target_os = "linux")] const EXPECTED: &str = "libaudioengine.so"; #[cfg(target_os = "macos")] const EXPECTED: &str = "libaudioengine.dylib"; #[test] fn test_library_filename() { let name = "audioengine"; let resolved = library_filename(name); assert!(Path::new(&resolved).ends_with(EXPECTED)); } libloading-0.7.4/tests/markers.rs000064400000000000000000000037131046102023000151200ustar 00000000000000extern crate libloading; #[cfg(test)] fn assert_send() {} #[cfg(test)] fn assert_sync() {} #[cfg(test)] fn assert_display() {} #[test] fn check_error_send() { assert_send::(); } #[test] fn check_error_sync() { assert_sync::(); } #[test] fn check_error_display() { assert_display::(); } #[test] fn check_library_send() { assert_send::(); } #[cfg(unix)] #[test] fn check_unix_library_send() { assert_send::(); } #[cfg(windows)] #[test] fn check_windows_library_send() { assert_send::(); } #[test] fn check_library_sync() { assert_sync::(); } #[cfg(unix)] #[test] fn check_unix_library_sync() { assert_sync::(); } #[cfg(windows)] #[test] fn check_windows_library_sync() { assert_sync::(); } #[test] fn check_symbol_send() { assert_send:: ()>>(); // assert_not_send::>(); } #[cfg(unix)] #[test] fn check_unix_symbol_send() { assert_send:: ()>>(); // assert_not_send::>(); } #[cfg(windows)] #[test] fn check_windows_symbol_send() { assert_send:: ()>>(); } #[test] fn check_symbol_sync() { assert_sync:: ()>>(); // assert_not_sync::>(); } #[cfg(unix)] #[test] fn check_unix_symbol_sync() { assert_sync:: ()>>(); // assert_not_sync::>(); } #[cfg(windows)] #[test] fn check_windows_symbol_sync() { assert_sync:: ()>>(); // assert_not_sync::>(); } libloading-0.7.4/tests/nagisa32.dll000064400000000000000000000060001046102023000152020ustar 00000000000000MZ@ !L!This program cannot be run in DOS mode. $3ݵd]d]d]9Yd]9]d]9_d]Richd]PELQW!   P@ H@ .text `.rdata @@.data0@.reloc @ @BU0]U3@] QW `h hQWR H L P _ nagisa32.dllwindows.text$mn .rdata H.edatah `.rdata$zzzdbg0.databunny 0libloading-0.7.4/tests/nagisa64.dll000064400000000000000000000050001046102023000152060ustar 00000000000000MZ@ !L!This program cannot be run in DOS mode. $3ݵd]d]d]9Yd]9]d]9_d]Richd]PEd3W" @` H .text$ `.rdata @@.data0@HLD$T$HL$3W `h h3WR H L P _ nagisa64.dllwindows$.text$mn .rdata H.edatah `.rdata$zzzdbg0.databunnylibloading-0.7.4/tests/windows.rs000064400000000000000000000034711046102023000151470ustar 00000000000000#![cfg(windows)] extern crate libloading; use libloading::os::windows::*; use std::ffi::CStr; // The ordinal DLL contains exactly one function (other than DllMain, that is) with ordinal number // 1. This function has the sugnature `fn() -> *const c_char` and returns a string "bunny\0" (in // reference to WindowsBunny). // // Both x86_64 and x86 versions of the .dll are functionally the same. Ideally we would compile the // dlls with well known ordinals from our own testing helpers library, but rustc does not allow // specifying a custom .def file (https://github.com/rust-lang/rust/issues/35089) // // The DLLs were kindly compiled by WindowsBunny (aka. @retep998). #[cfg(target_arch="x86")] fn load_ordinal_lib() -> Library { unsafe { Library::new("tests/nagisa32.dll").expect("nagisa32.dll") } } #[cfg(target_arch="x86_64")] fn load_ordinal_lib() -> Library { unsafe { Library::new("tests/nagisa64.dll").expect("nagisa64.dll") } } #[cfg(any(target_arch="x86", target_arch="x86_64"))] #[test] fn test_ordinal() { let lib = load_ordinal_lib(); unsafe { let windows: Symbol *const i8> = lib.get_ordinal(1).expect("function"); assert_eq!(CStr::from_ptr(windows()).to_bytes(), b"bunny"); } } #[cfg(any(target_arch="x86", target_arch="x86_64"))] #[test] fn test_ordinal_missing_fails() { let lib = load_ordinal_lib(); unsafe { let r: Result *const i8>, _> = lib.get_ordinal(2); r.err().unwrap(); let r: Result *const i8>, _> = lib.get_ordinal(!0); r.err().unwrap(); } } #[test] fn test_new_kernel23() { unsafe { Library::new("kernel23").err().unwrap(); } } #[test] fn test_new_kernel32_no_ext() { unsafe { Library::new("kernel32").unwrap(); } }