diff -Nru rust-libloading-0.6.3/build.rs rust-libloading-0.7.2/build.rs --- rust-libloading-0.6.3/build.rs 2020-08-23 16:09:31.000000000 +0000 +++ rust-libloading-0.7.2/build.rs 1970-01-01 00:00:00.000000000 +0000 @@ -1,70 +0,0 @@ -use std::io::Write; -use std::env; - -fn dlerror_is_mtsafe(target_os: &str) { - match target_os { - // Confirmed MT-safe: - "linux" - | "android" - | "openbsd" - | "macos" - | "ios" - | "solaris" - | "illumos" - | "redox" - | "fuchsia" => { - println!("cargo:rustc-cfg=mtsafe_dlerror"); - } - // Confirmed not MT-safe: - "freebsd" - | "dragonfly" - | "netbsd" - | "bitrig" - | "haiku" => {} - // Unknown: - _ => {} - } -} - -fn link_libraries(target_os: &str) { - match target_os { - "linux" | "android" => println!("cargo:rustc-link-lib=dl"), - "freebsd" | "dragonfly" => println!("cargo:rustc-link-lib=c"), - // netbsd claims dl* will be available to any dynamically linked binary, but I haven’t - // found any libraries that have to be linked to on other platforms. - // What happens if the executable is not linked up dynamically? - "openbsd" | "bitrig" | "netbsd" | "macos" | "ios" => {} - "solaris" | "illumos" => {} - "haiku" => {} - "redox" => {} - "fuchsia" => {} - // dependencies come with winapi - "windows" => {} - tos => { - writeln!(::std::io::stderr(), - "Building for an unknown target_os=`{:?}`!\nPlease report an issue ", - tos).expect("could not report the error"); - ::std::process::exit(0xfc); - } - } -} - -fn main() { - match env::var("CARGO_CFG_TARGET_OS") { - Ok(target_os) => { - dlerror_is_mtsafe(&target_os); - link_libraries(&target_os); - } - Err(e) => { - writeln!(::std::io::stderr(), - "Unable to get target_os=`{}`!", e).expect("could not report the error"); - ::std::process::exit(0xfd); - } - } - - // For tests - println!( - "cargo:rustc-env=LIBLOADING_TEST_TARGET={}", - std::env::var("TARGET").expect("$TARGET is not set") - ); -} diff -Nru rust-libloading-0.6.3/Cargo.toml rust-libloading-0.7.2/Cargo.toml --- rust-libloading-0.6.3/Cargo.toml 2020-08-24 01:29:39.000000000 +0000 +++ rust-libloading-0.7.2/Cargo.toml 1970-01-01 00:00:01.000000000 +0000 @@ -3,19 +3,18 @@ # When uploading crates to the registry Cargo will automatically # "normalize" Cargo.toml files for maximal compatibility # with all versions of Cargo and also rewrite `path` dependencies -# to registry (e.g., crates.io) dependencies +# to registry (e.g., crates.io) dependencies. # -# If you believe there's an error in this file please file an -# issue against the rust-lang/cargo repository. If you're -# editing this file be aware that the upstream Cargo.toml -# will likely look very different (and much more reasonable) +# If you are reading this file be aware that the original Cargo.toml +# will likely look very different (and much more reasonable). +# See Cargo.toml.orig for the original contents. [package] +rust-version = "1.40.0" name = "libloading" -version = "0.6.3" +version = "0.7.2" authors = ["Simonas Kazlauskas "] -build = "build.rs" -description = "A safer binding to platform’s dynamic library loading utilities" +description = "Bindings around the platform's dynamic library loading primitives with greatly improved memory safety." documentation = "https://docs.rs/libloading/" readme = "README.mkd" keywords = ["dlopen", "load", "shared", "dylib"] @@ -31,7 +30,7 @@ [dev-dependencies.static_assertions] version = "1.1" [target."cfg(unix)".dependencies.cfg-if] -version = "0.1" +version = "1" [target."cfg(windows)".dependencies.winapi] version = "0.3" -features = ["winerror", "errhandlingapi", "libloaderapi"] +features = ["errhandlingapi", "libloaderapi"] diff -Nru rust-libloading-0.6.3/Cargo.toml.orig rust-libloading-0.7.2/Cargo.toml.orig --- rust-libloading-0.6.3/Cargo.toml.orig 2020-08-24 01:29:15.000000000 +0000 +++ rust-libloading-0.7.2/Cargo.toml.orig 1973-11-29 21:33:09.000000000 +0000 @@ -2,28 +2,27 @@ name = "libloading" # When bumping # * Don’t forget to add an entry to `src/changelog.rs` -# * If bumping a incompatible version, adjust documentation in `src/lib.rs` -version = "0.6.3" +# * If bumping to an incompatible version, adjust the documentation in `src/lib.rs` +version = "0.7.2" authors = ["Simonas Kazlauskas "] license = "ISC" repository = "https://github.com/nagisa/rust_libloading/" documentation = "https://docs.rs/libloading/" readme = "README.mkd" -description = "A safer binding to platform’s dynamic library loading utilities" +description = "Bindings around the platform's dynamic library loading primitives with greatly improved memory safety." keywords = ["dlopen", "load", "shared", "dylib"] categories = ["api-bindings"] -build = "build.rs" +rust-version = "1.40.0" [target.'cfg(windows)'.dependencies.winapi] version = "0.3" features = [ - "winerror", "errhandlingapi", "libloaderapi", ] [target.'cfg(unix)'.dependencies.cfg-if] -version = "0.1" +version = "1" [dev-dependencies] libc = "0.2" diff -Nru rust-libloading-0.6.3/.cargo_vcs_info.json rust-libloading-0.7.2/.cargo_vcs_info.json --- rust-libloading-0.6.3/.cargo_vcs_info.json 2020-08-24 01:29:39.000000000 +0000 +++ rust-libloading-0.7.2/.cargo_vcs_info.json 1970-01-01 00:00:01.000000000 +0000 @@ -1,5 +1,5 @@ { "git": { - "sha1": "21c7cdb08ffc38ceb0b0f9e7038f88548c2ade94" + "sha1": "b706286d0f5627e22d15f7a8bc9be341fa26736a" } -} +} \ No newline at end of file diff -Nru rust-libloading-0.6.3/debian/cargo-checksum.json rust-libloading-0.7.2/debian/cargo-checksum.json --- rust-libloading-0.6.3/debian/cargo-checksum.json 2020-10-04 14:44:46.000000000 +0000 +++ rust-libloading-0.7.2/debian/cargo-checksum.json 2021-12-19 06:00:04.000000000 +0000 @@ -1 +1 @@ -{"package":"2443d8f0478b16759158b2f66d525991a05491138bc05814ef52a250148ef4f9","files":{}} +{"package":"Could not get crate checksum","files":{}} diff -Nru rust-libloading-0.6.3/debian/changelog rust-libloading-0.7.2/debian/changelog --- rust-libloading-0.6.3/debian/changelog 2020-10-04 14:44:46.000000000 +0000 +++ rust-libloading-0.7.2/debian/changelog 2021-12-19 06:00:04.000000000 +0000 @@ -1,3 +1,11 @@ +rust-libloading (0.7.2-1) unstable; urgency=medium + + * Team upload. + * Package libloading 0.7.2 from crates.io using debcargo 2.5.0 + * Honor Cargo's target directory when building LIBPATH + + -- James McCoy Sun, 19 Dec 2021 01:00:04 -0500 + rust-libloading (0.6.3-1) unstable; urgency=medium * Package libloading 0.6.3 from crates.io using debcargo 2.4.2 diff -Nru rust-libloading-0.6.3/debian/compat rust-libloading-0.7.2/debian/compat --- rust-libloading-0.6.3/debian/compat 2020-10-04 14:44:46.000000000 +0000 +++ rust-libloading-0.7.2/debian/compat 2021-12-19 06:00:04.000000000 +0000 @@ -1 +1 @@ -11 +12 diff -Nru rust-libloading-0.6.3/debian/control rust-libloading-0.7.2/debian/control --- rust-libloading-0.6.3/debian/control 2020-10-04 14:44:46.000000000 +0000 +++ rust-libloading-0.7.2/debian/control 2021-12-19 06:00:04.000000000 +0000 @@ -1,41 +1,41 @@ Source: rust-libloading Section: rust Priority: optional -Build-Depends: debhelper (>= 11), - dh-cargo (>= 18), +Build-Depends: debhelper (>= 12), + dh-cargo (>= 25), cargo:native , rustc:native , libstd-rust-dev , - librust-cfg-if-0.1+default-dev , + librust-cfg-if-1+default-dev , librust-winapi-0.3+default-dev , librust-winapi-0.3+errhandlingapi-dev , - librust-winapi-0.3+libloaderapi-dev , - librust-winapi-0.3+winerror-dev + librust-winapi-0.3+libloaderapi-dev Maintainer: Debian Rust Maintainers Uploaders: kpcyrd -Standards-Version: 4.4.1 +Standards-Version: 4.5.1 Vcs-Git: https://salsa.debian.org/rust-team/debcargo-conf.git [src/libloading] Vcs-Browser: https://salsa.debian.org/rust-team/debcargo-conf/tree/master/src/libloading +X-Cargo-Crate: libloading +Rules-Requires-Root: no Package: librust-libloading-dev Architecture: any Multi-Arch: same Depends: ${misc:Depends}, - librust-cfg-if-0.1+default-dev, + librust-cfg-if-1+default-dev, librust-winapi-0.3+default-dev, librust-winapi-0.3+errhandlingapi-dev, - librust-winapi-0.3+libloaderapi-dev, - librust-winapi-0.3+winerror-dev + librust-winapi-0.3+libloaderapi-dev Provides: librust-libloading+default-dev (= ${binary:Version}), librust-libloading-0-dev (= ${binary:Version}), librust-libloading-0+default-dev (= ${binary:Version}), - librust-libloading-0.6-dev (= ${binary:Version}), - librust-libloading-0.6+default-dev (= ${binary:Version}), - librust-libloading-0.6.3-dev (= ${binary:Version}), - librust-libloading-0.6.3+default-dev (= ${binary:Version}) + librust-libloading-0.7-dev (= ${binary:Version}), + librust-libloading-0.7+default-dev (= ${binary:Version}), + librust-libloading-0.7.2-dev (= ${binary:Version}), + librust-libloading-0.7.2+default-dev (= ${binary:Version}) Description: Safer binding to dynamic library loading utilities - Rust source code This package contains the source for the Rust libloading crate, packaged by debcargo for use with cargo and dh-cargo. diff -Nru rust-libloading-0.6.3/debian/copyright.debcargo.hint rust-libloading-0.7.2/debian/copyright.debcargo.hint --- rust-libloading-0.6.3/debian/copyright.debcargo.hint 2020-10-04 14:44:46.000000000 +0000 +++ rust-libloading-0.7.2/debian/copyright.debcargo.hint 2021-12-19 06:00:04.000000000 +0000 @@ -21,8 +21,8 @@ Files: debian/* Copyright: - 2018-2020 Debian Rust Maintainers - 2018-2020 kpcyrd + 2018-2021 Debian Rust Maintainers + 2018-2021 kpcyrd License: ISC License: ISC diff -Nru rust-libloading-0.6.3/debian/patches/series rust-libloading-0.7.2/debian/patches/series --- rust-libloading-0.6.3/debian/patches/series 1970-01-01 00:00:00.000000000 +0000 +++ rust-libloading-0.7.2/debian/patches/series 2021-12-19 06:00:04.000000000 +0000 @@ -0,0 +1 @@ +test-target_dir.patch diff -Nru rust-libloading-0.6.3/debian/patches/test-target_dir.patch rust-libloading-0.7.2/debian/patches/test-target_dir.patch --- rust-libloading-0.6.3/debian/patches/test-target_dir.patch 1970-01-01 00:00:00.000000000 +0000 +++ rust-libloading-0.7.2/debian/patches/test-target_dir.patch 2021-12-19 06:00:04.000000000 +0000 @@ -0,0 +1,13 @@ +Index: libloading/tests/functions.rs +=================================================================== +--- libloading.orig/tests/functions.rs ++++ libloading/tests/functions.rs +@@ -4,7 +4,7 @@ extern crate winapi; + extern crate libloading; + use libloading::{Symbol, Library}; + +-const LIBPATH: &'static str = "target/libtest_helpers.module"; ++const LIBPATH: &'static str = concat!(env!("CARGO_TARGET_DIR"), "/libtest_helpers.module"); + + fn make_helpers() { + static ONCE: ::std::sync::Once = ::std::sync::Once::new(); diff -Nru rust-libloading-0.6.3/debian/tests/control rust-libloading-0.7.2/debian/tests/control --- rust-libloading-0.6.3/debian/tests/control 2020-10-04 14:44:46.000000000 +0000 +++ rust-libloading-0.7.2/debian/tests/control 2021-12-19 06:00:04.000000000 +0000 @@ -1,9 +1,14 @@ -Test-Command: /usr/share/cargo/bin/cargo-auto-test libloading 0.6.3 --all-targets --all-features -Features: test-name=@ +Test-Command: /usr/share/cargo/bin/cargo-auto-test libloading 0.7.2 --all-targets --all-features +Features: test-name=rust-libloading:@ Depends: dh-cargo (>= 18), librust-libc-0.2+default-dev, librust-static-assertions-1+default-dev (>= 1.1-~~), @ Restrictions: allow-stderr, skip-not-installable -Test-Command: /usr/share/cargo/bin/cargo-auto-test libloading 0.6.3 --all-targets --no-default-features -Features: test-name=librust-libloading-dev +Test-Command: /usr/share/cargo/bin/cargo-auto-test libloading 0.7.2 --all-targets +Features: test-name=librust-libloading-dev:default +Depends: dh-cargo (>= 18), librust-libc-0.2+default-dev, librust-static-assertions-1+default-dev (>= 1.1-~~), @ +Restrictions: allow-stderr, skip-not-installable + +Test-Command: /usr/share/cargo/bin/cargo-auto-test libloading 0.7.2 --all-targets --no-default-features +Features: test-name=librust-libloading-dev: Depends: dh-cargo (>= 18), librust-libc-0.2+default-dev, librust-static-assertions-1+default-dev (>= 1.1-~~), @ Restrictions: allow-stderr, skip-not-installable diff -Nru rust-libloading-0.6.3/.github/workflows/libloading.yml rust-libloading-0.7.2/.github/workflows/libloading.yml --- rust-libloading-0.6.3/.github/workflows/libloading.yml 2020-08-23 16:09:31.000000000 +0000 +++ rust-libloading-0.7.2/.github/workflows/libloading.yml 1973-11-29 21:33:09.000000000 +0000 @@ -6,7 +6,7 @@ - '*.mkd' - 'LICENSE' pull_request: - types: [opened, repoened, synchronize] + types: [opened, reopened, synchronize] jobs: native-test: @@ -26,37 +26,26 @@ profile: minimal components: clippy default: true - - name: Update - uses: actions-rs/cargo@v1 - with: - command: update - args: --manifest-path=Cargo.toml - name: Clippy uses: actions-rs/cargo@v1 with: command: clippy - - name: Build - uses: actions-rs/cargo@v1 - with: - command: build - args: --manifest-path=Cargo.toml - name: Test uses: actions-rs/cargo@v1 with: command: test - args: --manifest-path=Cargo.toml -- --nocapture + args: -- --nocapture - name: Test Release uses: actions-rs/cargo@v1 with: command: test - args: --manifest-path=Cargo.toml --release -- --nocapture + args: --release -- --nocapture - name: Documentation uses: actions-rs/cargo@v1 with: command: doc - args: --manifest-path=Cargo.toml env: - RUSTDOCFLAGS: --cfg docsrs + RUSTDOCFLAGS: --cfg docsrs -Drustdoc::broken_intra_doc_links if: ${{ matrix.rust_toolchain == 'nightly' }} windows-gnu-test: @@ -71,13 +60,15 @@ steps: - uses: actions/checkout@v2 - name: Add MSYS2 to the PATH - run: echo "::add-path::c:/msys64/bin" + run: echo "c:/msys64/bin" | Out-File -FilePath $env:GITHUB_PATH -Append - name: Add 32-bit mingw-w64 to the PATH - run: echo "::add-path::c:/msys64/mingw32/bin" + run: echo "c:/msys64/mingw32/bin" | Out-File -FilePath $env:GITHUB_PATH -Append if: startsWith(matrix.rust_target, 'i686') - name: Add 64-bit mingw-w64 to the PATH - run: echo "::add-path::c:/msys64/mingw64/bin" + run: echo "c:/msys64/mingw64/bin" | Out-File -FilePath $env:GITHUB_PATH -Append if: startsWith(matrix.rust_target, 'x86_64') + - name: Set TARGET variable + run: echo "TARGET=${{ matrix.rust_target}}" | Out-File -FilePath $env:GITHUB_ENV -Append - name: Install Rust nightly uses: actions-rs/toolchain@v1 with: @@ -108,6 +99,7 @@ - x86_64-unknown-openbsd - x86_64-unknown-redox - x86_64-fuchsia + - wasm32-unknown-unknown timeout-minutes: 20 steps: - uses: actions/checkout@v2 diff -Nru rust-libloading-0.6.3/README.mkd rust-libloading-0.7.2/README.mkd --- rust-libloading-0.6.3/README.mkd 2020-08-23 16:09:31.000000000 +0000 +++ rust-libloading-0.7.2/README.mkd 1973-11-29 21:33:09.000000000 +0000 @@ -1,11 +1,8 @@ # libloading -Safer bindings around system dynamic library loading primitives. The most important safety -guarantee by this library is prevention of dangling-`Symbol`s that may occur after a `Library` is -unloaded. +Bindings around the platform's dynamic library loading primitives with greatly improved memory safety. The most important safety guarantee of this library is the prevention of dangling `Symbol`s that may occur after a `Library` is unloaded. -Using this library allows loading dynamic libraries (also known as shared libraries) as well as use -functions and static variables these libraries contain. +Using this library allows the loading of dynamic libraries, also known as shared libraries, as well as the use of the functions and static variables that these libraries may contain. * [Documentation][docs] * [Changelog][changelog] diff -Nru rust-libloading-0.6.3/src/changelog.rs rust-libloading-0.7.2/src/changelog.rs --- rust-libloading-0.6.3/src/changelog.rs 2020-08-23 18:25:03.000000000 +0000 +++ rust-libloading-0.7.2/src/changelog.rs 1973-11-29 21:33:09.000000000 +0000 @@ -1,7 +1,164 @@ -//! Project changelog +//! The change log. -// TODO: for the next breaking release rename `Error::LoadLibraryW` to `Error::LoadLibraryExW`. -// TODO: for the next breaking release use `RTLD_LAZY | RTLD_LOCAL` by default on unix. +/// 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) /// @@ -13,6 +170,7 @@ /// /// [`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) /// @@ -56,7 +214,6 @@ /// [`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. @@ -87,7 +244,6 @@ /// * `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; @@ -95,7 +251,6 @@ /// * Added `Symbol::::lift_option() -> Option>` convenience method. pub mod r0_4_2 {} - /// Release 0.4.1 (2017-08-29) /// /// * Solaris support diff -Nru rust-libloading-0.6.3/src/error.rs rust-libloading-0.7.2/src/error.rs --- rust-libloading-0.6.3/src/error.rs 2020-08-23 16:09:31.000000000 +0000 +++ rust-libloading-0.7.2/src/error.rs 1973-11-29 21:33:09.000000000 +0000 @@ -44,18 +44,18 @@ /// The `dlclose` call failed and system did not report an error. DlCloseUnknown, /// The `LoadLibraryW` call failed. - LoadLibraryW { + LoadLibraryExW { /// The source error. source: WindowsError }, /// The `LoadLibraryW` call failed and system did not report an error. - LoadLibraryWUnknown, + LoadLibraryExWUnknown, /// The `GetModuleHandleExW` call failed. GetModuleHandleExW { /// The source error. source: WindowsError }, - /// The `LoadLibraryW` call failed and system did not report an error. + /// The `GetModuleHandleExW` call failed and system did not report an error. GetModuleHandleExWUnknown, /// The `GetProcAddress` call failed. GetProcAddress { @@ -91,7 +91,7 @@ match *self { CreateCString { ref source } => Some(source), CreateCStringWithTrailing { ref source } => Some(source), - LoadLibraryW { ref source } => Some(&source.0), + LoadLibraryExW { ref source } => Some(&source.0), GetProcAddress { ref source } => Some(&source.0), FreeLibrary { ref source } => Some(&source.0), _ => None, @@ -109,8 +109,8 @@ 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"), - LoadLibraryW { .. } => write!(f, "LoadLibraryExW failed"), - LoadLibraryWUnknown => + LoadLibraryExW { .. } => write!(f, "LoadLibraryExW failed"), + LoadLibraryExWUnknown => write!(f, "LoadLibraryExW failed, but system did not report the error"), GetModuleHandleExW { .. } => write!(f, "GetModuleHandleExW failed"), GetModuleHandleExWUnknown => diff -Nru rust-libloading-0.6.3/src/lib.rs rust-libloading-0.7.2/src/lib.rs --- rust-libloading-0.6.3/src/lib.rs 2020-08-23 18:13:12.000000000 +0000 +++ rust-libloading-0.7.2/src/lib.rs 1973-11-29 21:33:09.000000000 +0000 @@ -1,349 +1,61 @@ -//! A memory-safer wrapper around system dynamic library loading primitives. +//! Bindings around the platform's dynamic library loading primitives with greatly improved memory safety. //! -//! Using this library allows loading [dynamic libraries](struct.Library.html) (also known as -//! shared libraries) as well as use functions and static variables these libraries contain. +//! 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. //! -//! While the library does expose a cross-platform interface to load a library and find stuff -//! inside it, little is done to paper over the platform differences, especially where library -//! loading is involved. The documentation for each function will attempt to document such -//! differences on the best-effort basis. +//! 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. //! -//! Less safe, platform specific bindings are also available. See the -//! [`os::platform`](os/index.html) module for details. +//! Platform-specific APIs are also available in the [`os`](crate::os) module. These APIs are more +//! flexible, but less safe. //! -//! # Usage +//! # Installation //! -//! Add a dependency on this library to your `Cargo.toml`: +//! Add the `libloading` library to your dependencies in `Cargo.toml`: //! //! ```toml //! [dependencies] -//! libloading = "0.6" +//! libloading = "0.7" //! ``` //! -//! Then inside your project +//! # Usage //! -//! ```no_run -//! extern crate libloading as lib; +//! In your code, run the following: //! +//! ```no_run //! fn call_dynamic() -> Result> { -//! let lib = lib::Library::new("/path/to/liblibrary.so")?; //! unsafe { -//! let func: lib::Symbol u32> = lib.get(b"my_func")?; +//! 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` it comes -//! from, preventing a common cause of undefined behaviour and memory safety problems. -#![deny( - missing_docs, - clippy::all, - unreachable_pub, - unused, -)] -#![cfg_attr(docsrs, deny(broken_intra_doc_links))] +//! 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(docsrs, feature(doc_cfg))] -use std::env::consts::{DLL_PREFIX, DLL_SUFFIX}; -use std::ffi::{OsStr, OsString}; -use std::fmt; -use std::ops; -use std::marker; - -#[cfg(unix)] -use self::os::unix as imp; -#[cfg(windows)] -use self::os::windows as imp; -pub use self::error::Error; - -pub mod os; pub mod changelog; +pub mod os; mod util; -mod error; - -/// A loaded dynamic library. -pub struct Library(imp::Library); - -impl Library { - /// Find and load a dynamic library. - /// - /// The `filename` argument may be any of: - /// - /// * A library filename; - /// * Absolute path to the library; - /// * Relative (to the current working directory) path to the library. - /// - /// # Thread-safety - /// - /// The implementation strives to be as MT-safe as sanely possible, however due to certain - /// error-handling related resources not always being safe, this library is not MT-safe either. - /// - /// * On Windows Vista and earlier error handling falls back to [`SetErrorMode`], which is not - /// MT-safe. MT-scenarios involving this function may cause a traditional data race; - /// * On some UNIX targets `dlerror` might not be MT-safe, resulting in garbage error messages - /// in certain MT-scenarios. - /// - /// [`SetErrorMode`]: https://msdn.microsoft.com/en-us/library/windows/desktop/ms680621(v=vs.85).aspx - /// - /// Calling this function from multiple threads is not safe if used in conjunction with - /// relative filenames and the library search path is modified (`SetDllDirectory` function on - /// Windows, `{DY,}LD_LIBRARY_PATH` environment variable on UNIX). - /// - /// # Platform-specific behaviour - /// - /// When a plain library filename is supplied, locations where library is searched for is - /// platform specific and cannot be adjusted in a portable manner. See documentation for - /// the platform specific [`os::unix::Library::new`] and [`os::windows::Library::new`] methods - /// for further information on library lookup behaviour. - /// - /// ## Windows - /// - /// If the `filename` specifies a library filename without path and with extension omitted, - /// `.dll` extension is implicitly added. This behaviour may be suppressed by appending a - /// trailing `.` to the `filename`. - /// - /// If the library contains thread local variables (MSVC’s `_declspec(thread)`, Rust’s - /// `#[thread_local]` attributes), loading the library will fail on versions prior to Windows - /// Vista. - /// - /// # Tips - /// - /// Distributing your dynamic libraries under a filename common to all platforms (e.g. - /// `awesome.module`) allows to avoid code which has to account for platform’s conventional - /// library filenames. - /// - /// Strive to specify an absolute or at least a relative path to your library, unless - /// system-wide libraries are being loaded. Platform-dependent library search locations - /// combined with various quirks related to path-less filenames may cause flakiness in - /// programs. - /// - /// # Examples - /// - /// ```no_run - /// # use ::libloading::Library; - /// // Any of the following are valid. - /// let _ = Library::new("/path/to/awesome.module").unwrap(); - /// let _ = Library::new("../awesome.module").unwrap(); - /// let _ = Library::new("libsomelib.so.1").unwrap(); - /// ``` - pub fn new>(filename: P) -> Result { - imp::Library::new(filename).map(From::from) - } - - /// Get a pointer to function or static variable by symbol name. - /// - /// The `symbol` may not contain any null bytes, with an exception of last byte. 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 - /// - /// Pointer to a value of arbitrary type is returned. Using a value with wrong type is - /// undefined. - /// - /// # Platform-specific behaviour - /// - /// Implementation of thread local variables is extremely platform specific and uses of these - /// variables that work on e.g. Linux may have unintended behaviour on other POSIX systems or - /// Windows. - /// - /// On POSIX implementations where the `dlerror` function is not confirmed to be MT-safe (such - /// as FreeBSD), this function will unconditionally return an error the underlying `dlsym` call - /// returns a null pointer. There are rare situations where `dlsym` returns a genuine null - /// pointer without it being an error. If loading a null pointer is something you care about, - /// consider using the [`os::unix::Library::get_singlethreaded`] call. - /// - /// # Examples - /// - /// Given a loaded library: - /// - /// ```no_run - /// # use ::libloading::Library; - /// let lib = Library::new("/path/to/awesome.module").unwrap(); - /// ``` - /// - /// Loading and using a function looks like this: - /// - /// ```no_run - /// # use ::libloading::{Library, Symbol}; - /// # let lib = 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 = 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. - 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. Primary method to create an instance of a `Symbol` is via `Library::get`. -/// -/// Due to implementation of the `Deref` trait, an instance of `Symbol` may be used as if it was a -/// function or variable directly, without taking care to “extract” function or variable manually -/// most of the time. -/// -/// See [`Library::get`] for details. -/// -/// [`Library::get`]: ./struct.Library.html#method.get -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 programmer to - /// ensure the resulting `Symbol` is not used past the lifetime of the `Library` this symbol - /// was loaded from. - /// - /// # Examples - /// - /// ```no_run - /// # use ::libloading::{Library, Symbol}; - /// let lib = Library::new("/path/to/awesome.module").unwrap(); - /// unsafe { - /// 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 reference to the library provided. - /// - /// # Safety - /// - /// It is invalid to provide a reference to any other value other than the library the `sym` - /// was loaded from. Doing so invalidates any lifetime guarantees. - /// - /// # Examples - /// - /// ```no_run - /// # use ::libloading::{Library, Symbol}; - /// let lib = Library::new("/path/to/awesome.module").unwrap(); - /// unsafe { - /// 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, _: &'lib L) -> Symbol<'lib, T> { - 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}; - /// let lib = Library::new("/path/to/awesome.module").unwrap(); - /// unsafe { - /// 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) - } -} +mod error; +pub use self::error::Error; -impl<'lib, T> fmt::Debug for Symbol<'lib, T> { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - self.inner.fmt(f) - } -} +#[cfg(any(unix, windows, docsrs))] +mod safe; +#[cfg(any(unix, windows, docsrs))] +pub use self::safe::{Library, Symbol}; -unsafe impl<'lib, T: Send> Send for Symbol<'lib, T> {} -unsafe impl<'lib, T: Sync> Sync for Symbol<'lib, T> {} +use std::env::consts::{DLL_PREFIX, DLL_SUFFIX}; +use std::ffi::{OsStr, OsString}; /// Converts a library name to a filename generally appropriate for use on the system. /// -/// The function will prepend prefixes (such as `lib`) and suffixes (such as `.so`) to the library +/// This function will prepend prefixes (such as `lib`) and suffixes (such as `.so`) to the library /// `name` to construct the filename. /// /// # Examples @@ -354,7 +66,9 @@ /// use libloading::{Library, library_filename}; /// // Will attempt to load `libLLVM.so` on Linux, `libLLVM.dylib` on macOS and `LLVM.dll` on /// // Windows. -/// let library = Library::new(library_filename("LLVM")); +/// let library = unsafe { +/// Library::new(library_filename("LLVM")) +/// }; /// ``` pub fn library_filename>(name: S) -> OsString { let name = name.as_ref(); diff -Nru rust-libloading-0.6.3/src/os/mod.rs rust-libloading-0.7.2/src/os/mod.rs --- rust-libloading-0.6.3/src/os/mod.rs 2020-08-23 16:09:31.000000000 +0000 +++ rust-libloading-0.7.2/src/os/mod.rs 1973-11-29 21:33:09.000000000 +0000 @@ -1,6 +1,6 @@ -//! Unsafe, platform specific bindings to dynamic library loading facilities. +//! Unsafe but flexible platform-specific bindings to dynamic library loading facilities. //! -//! These modules expose more extensive, powerful, less principled bindings to the dynamic +//! 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. //! diff -Nru rust-libloading-0.6.3/src/os/unix/consts.rs rust-libloading-0.7.2/src/os/unix/consts.rs --- rust-libloading-0.6.3/src/os/unix/consts.rs 2020-08-23 16:09:31.000000000 +0000 +++ rust-libloading-0.7.2/src/os/unix/consts.rs 1973-11-29 21:33:09.000000000 +0000 @@ -7,7 +7,7 @@ /// Specifying `RTLD_LAZY` should improve performance on implementations supporting dynamic /// symbol binding since a process might not reference all of the symbols in an executable /// object file. And, for systems supporting dynamic symbol resolution for normal process -/// execution, this behavior mimics the normal handling of process execution. +/// execution, this behaviour mimics the normal handling of process execution. /// /// Conflicts with [`RTLD_NOW`]. /// @@ -18,7 +18,7 @@ /// /// All necessary relocations shall be performed when the executable object file is first /// loaded. This may waste some processing if relocations are performed for symbols -/// that are never referenced. This behavior may be useful for applications that need to +/// that are never referenced. This behaviour may be useful for applications that need to /// know that all symbols referenced during execution will be available before /// [`Library::open`] returns. /// @@ -43,7 +43,7 @@ /// 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(docsrs)] +#[cfg(all(docsrs, not(unix)))] mod posix { use super::c_int; pub(super) const RTLD_LAZY: c_int = !0; @@ -52,7 +52,7 @@ pub(super) const RTLD_LOCAL: c_int = !0; } -#[cfg(not(docsrs))] +#[cfg(any(not(docsrs), unix))] mod posix { extern crate cfg_if; use self::cfg_if::cfg_if; diff -Nru rust-libloading-0.6.3/src/os/unix/mod.rs rust-libloading-0.7.2/src/os/unix/mod.rs --- rust-libloading-0.6.3/src/os/unix/mod.rs 2020-08-23 16:09:31.000000000 +0000 +++ rust-libloading-0.7.2/src/os/unix/mod.rs 1973-11-29 21:33:09.000000000 +0000 @@ -1,19 +1,18 @@ // A hack for docs.rs to build documentation that has both windows and linux documentation in the // same rustdoc build visible. #[cfg(all(docsrs, not(unix)))] -mod unix_imports { -} +mod unix_imports {} #[cfg(any(not(docsrs), unix))] mod unix_imports { pub(super) use std::os::unix::ffi::OsStrExt; } +pub use self::consts::*; use self::unix_imports::*; -use util::{ensure_compatible_types, cstr_cow_from_bytes}; use std::ffi::{CStr, OsStr}; -use std::{fmt, marker, mem, ptr}; use std::os::raw; -pub use self::consts::*; +use std::{fmt, marker, mem, ptr}; +use util::{cstr_cow_from_bytes, ensure_compatible_types}; mod consts; @@ -103,57 +102,80 @@ /// 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_NOW)`. + /// 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 fn new>(filename: P) -> Result { - Library::open(Some(filename), RTLD_NOW) + pub unsafe fn new>(filename: P) -> Result { + Library::open(Some(filename), RTLD_LAZY | RTLD_LOCAL) } - /// Eagerly load the `Library` representing the current executable. + /// Load the `Library` representing the current executable. /// /// [`Library::get`] calls of the returned `Library` will look for symbols in following /// locations in order: /// - /// 1. Original program image; + /// 1. The original program image; /// 2. Any executable object files (e.g. shared libraries) loaded at program startup; - /// 3. Executable object files loaded at runtime (e.g. via other `Library::new` calls or via - /// calls to the `dlopen` function) + /// 3. Any executable object files loaded at runtime (e.g. via other `Library::new` calls or via + /// calls to the `dlopen` function). /// - /// Note that behaviour of `Library` loaded with this method is different from + /// Note that the behaviour of a `Library` loaded with this method is different from that of /// Libraries loaded with [`os::windows::Library::this`]. /// - /// This is equivalent to [`Library::open`]`(None, RTLD_NOW)`. + /// 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 { - Library::open(None::<&OsStr>, RTLD_NOW).expect("this should never fail") + 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 behaviour + /// See documentation for [`Library::this`] for further description of the behaviour /// when the `filename` is `None`. Otherwise see [`Library::new`]. /// /// Corresponds to `dlopen(filename, flags)`. - pub fn open

(filename: Option

, flags: raw::c_int) -> Result + /// + /// # 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 = unsafe { - let r = dlopen(match filename { - None => ptr::null(), - Some(ref f) => f.as_ptr() - }, flags); - // ensure filename lives until dlopen completes - drop(filename); - r - }; + 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 { @@ -194,60 +216,72 @@ } - /// Get a pointer to function or static variable by symbol name. + /// Get a pointer to a function or static variable by symbol name. /// - /// The `symbol` may not contain any null bytes, with an exception of last byte. A null - /// terminated `symbol` may avoid an allocation in some cases. + /// 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 /// - /// This function does not validate the type `T`. It is up to the user of this function to - /// ensure that the loaded symbol is in fact a `T`. Using a value with a wrong type has no - /// defined behaviour. + /// Users of this API must specify the correct type of the function or variable loaded. Using a + /// `Symbol` with a wrong type is undefined. /// /// # Platform-specific behaviour /// - /// Implementation of thread local variables is extremely platform specific and uses of these - /// variables that work on e.g. Linux may have unintended behaviour on other POSIX systems. + /// Implementation of thread local variables is extremely platform specific and uses of such + /// variables that work on e.g. Linux may have unintended behaviour on other targets. /// /// On POSIX implementations where the `dlerror` function is not confirmed to be MT-safe (such /// as FreeBSD), this function will unconditionally return an error when the underlying `dlsym` /// call returns a null pointer. There are rare situations where `dlsym` returns a genuine null - /// pointer without it being an error. If loading a symbol at null address is something you - /// care about, consider using the [`Library::get_singlethreaded`] call. + /// pointer without it being an error. If loading a null pointer is something you care about, + /// consider using the [`Library::get_singlethreaded`] call. #[inline(always)] pub unsafe fn get(&self, symbol: &[u8]) -> Result, crate::Error> { - #[cfg(mtsafe_dlerror)] - { self.get_singlethreaded(symbol) } - #[cfg(not(mtsafe_dlerror))] - { self.get_impl(symbol, || Err(crate::Error::DlSymUnknown)) } + 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 an exception of last byte. A null - /// terminated `symbol` may avoid a string allocation in some cases. + /// The `symbol` may not contain any null bytes, with 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 /// - /// This function does not validate the type `T`. It is up to the user of this function to - /// ensure that the loaded symbol is in fact a `T`. Using a value with a wrong type has no - /// defined behaviour. + /// Users of this API must specify the correct type of the function or variable loaded. /// /// It is up to the user of this library to ensure that no other calls to an MT-unsafe - /// implementation of `dlerror` occur during execution of this function. Failing that, the + /// implementation of `dlerror` occur during the execution of this function. Failing that, the /// behaviour of this function is not defined. /// /// # Platform-specific behaviour /// - /// Implementation of thread local variables is extremely platform specific and uses of these - /// variables that work on e.g. Linux may have unintended behaviour on other POSIX systems. + /// 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 { @@ -285,7 +319,10 @@ /// what library was opened or other platform specifics. /// /// You only need to call this if you are interested in handling any errors that may arise when - /// library is unloaded. Otherwise this will be done when `Library` is dropped. + /// library is unloaded. Otherwise the implementation of `Drop` for `Library` will close the + /// library and ignore the errors were they arise. + /// + /// The underlying data structures may still get leaked if an error does occur. pub fn close(self) -> Result<(), crate::Error> { let result = with_dlerror(|desc| crate::Error::DlClose { desc }, || { if unsafe { dlclose(self.handle) } == 0 { @@ -294,6 +331,9 @@ 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 } @@ -315,15 +355,15 @@ /// Symbol from a library. /// -/// A major difference compared to the cross-platform `Symbol` is that this does not ensure the -/// `Symbol` does not outlive `Library` it comes from. +/// A major difference compared to the cross-platform `Symbol` is that this does not ensure that the +/// `Symbol` does not outlive the `Library` it comes from. pub struct Symbol { pointer: *mut raw::c_void, pd: marker::PhantomData } impl Symbol { - /// Convert the loaded Symbol into a raw pointer. + /// Convert the loaded `Symbol` into a raw pointer. pub fn into_raw(self) -> *mut raw::c_void { let pointer = self.pointer; mem::forget(self); @@ -387,6 +427,8 @@ } // 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; diff -Nru rust-libloading-0.6.3/src/os/windows/mod.rs rust-libloading-0.7.2/src/os/windows/mod.rs --- rust-libloading-0.6.3/src/os/windows/mod.rs 2020-08-23 16:53:40.000000000 +0000 +++ rust-libloading-0.7.2/src/os/windows/mod.rs 1973-11-29 21:33:09.000000000 +0000 @@ -28,7 +28,6 @@ extern crate winapi; pub(super) use self::winapi::shared::minwindef::{WORD, DWORD, HMODULE, FARPROC}; pub(super) use self::winapi::shared::ntdef::WCHAR; - pub(super) use self::winapi::shared::winerror; pub(super) use self::winapi::um::{errhandlingapi, libloaderapi}; pub(super) use std::os::windows::ffi::{OsStrExt, OsStringExt}; pub(super) const SEM_FAILCE: DWORD = 1; @@ -55,25 +54,24 @@ use util::{ensure_compatible_types, cstr_cow_from_bytes}; use std::ffi::{OsStr, OsString}; use std::{fmt, io, marker, mem, ptr}; -use std::sync::atomic::{AtomicBool, Ordering}; -/// A platform-specific counterpart of the cross-platform [`Library`](crate::Library). +/// The platform-specific counterpart of the cross-platform [`Library`](crate::Library). pub struct Library(HMODULE); unsafe impl Send for Library {} // Now, this is sort-of-tricky. MSDN documentation does not really make any claims as to safety of // the Win32 APIs. Sadly, whomever I asked, even current and former Microsoft employees, couldn’t -// say for sure, whether the Win32 APIs used to implement `Library` are thread-safe or not. +// say for sure whether the Win32 APIs used to implement `Library` are thread-safe or not. // // My investigation ended up with a question about thread-safety properties of the API involved // being sent to an internal (to MS) general question mailing-list. The conclusion of the mail is // as such: // -// * Nobody inside MS (at least out of all the people who have seen the question) knows for +// * Nobody inside MS (at least out of all of the people who have seen the question) knows for // sure either; // * However, the general consensus between MS developers is that one can rely on the API being // thread-safe. In case it is not thread-safe it should be considered a bug on the Windows -// part. (NB: bugs filled at https://connect.microsoft.com/ against Windows Server) +// part. (NB: bugs filed at https://connect.microsoft.com/ against Windows Server) unsafe impl Sync for Library {} impl Library { @@ -81,24 +79,35 @@ /// /// If the `filename` specifies a full path, the function only searches that path for the /// module. Otherwise, if the `filename` specifies a relative path or a module name without a - /// path, the function uses a windows-specific search strategy to find the module; for more + /// path, the function uses a Windows-specific search strategy to find the module. For more /// information, see the [Remarks on MSDN][msdn]. /// - /// If the `filename` specifies a library filename without path and with extension omitted, - /// `.dll` extension is implicitly added. This behaviour may be suppressed by appending a + /// If the `filename` specifies a library filename without a path and with the extension omitted, + /// the `.dll` extension is implicitly added. This behaviour may be suppressed by appending a /// trailing `.` to the `filename`. /// - /// This is equivalent to [`Library::load_with_flags`]`(filename, 0)`. + /// 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 fn new>(filename: P) -> Result { + pub unsafe fn new>(filename: P) -> Result { Library::load_with_flags(filename, 0) } - /// Load the `Library` representing the original program executable. + /// Get the `Library` representing the original program executable. /// - /// Note that behaviour of `Library` loaded with this method is different from + /// Note that the behaviour of the `Library` loaded with this method is different from /// Libraries loaded with [`os::unix::Library::this`]. For more information refer to [MSDN]. /// /// Corresponds to `GetModuleHandleExW(0, NULL, _)`. @@ -119,38 +128,87 @@ } } + /// 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 handling of the `filename` argument. See the + /// See [`Library::new`] for documentation on the handling of the `filename` argument. See the /// [flag table on MSDN][flags] for information on applicable values for the `flags` argument. /// /// Corresponds to `LoadLibraryExW(filename, reserved: NULL, flags)`. /// /// [flags]: https://docs.microsoft.com/en-us/windows/win32/api/libloaderapi/nf-libloaderapi-loadlibraryexw#parameters - pub fn load_with_flags>(filename: P, flags: DWORD) -> Result { + /// + /// # 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::LoadLibraryW { source }, || { + 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 = unsafe { - libloaderapi::LoadLibraryExW(wide_filename.as_ptr(), std::ptr::null_mut(), flags) - }; + 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::LoadLibraryWUnknown)); + }).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 function or static variable by symbol name. + /// Get a pointer to a function or static variable by symbol name. /// - /// The `symbol` may not contain any null bytes, with an exception of last byte. A null + /// The `symbol` may not contain any null bytes, with the exception of the last byte. A null /// terminated `symbol` may avoid a string allocation in some cases. /// /// Symbol is interpreted as-is; no mangling is done. This means that symbols like `x::y` are @@ -158,9 +216,7 @@ /// /// # Safety /// - /// This function does not validate the type `T`. It is up to the user of this function to - /// ensure that the loaded symbol is in fact a `T`. Using a value with a wrong type has no - /// definied behaviour. + /// Users of this API must specify the correct type of the function or variable loaded. pub unsafe fn get(&self, symbol: &[u8]) -> Result, crate::Error> { ensure_compatible_types::()?; let symbol = cstr_cow_from_bytes(symbol)?; @@ -177,12 +233,11 @@ }).map_err(|e| e.unwrap_or(crate::Error::GetProcAddressUnknown)) } - /// Get a pointer to function or static variable by ordinal number. + /// Get a pointer to a function or static variable by ordinal number. /// /// # Safety /// - /// Pointer to a value of arbitrary type is returned. Using a value with wrong type is - /// undefined. + /// Users of this API must specify the correct type of the function or variable loaded. pub unsafe fn get_ordinal(&self, ordinal: WORD) -> Result, crate::Error> { ensure_compatible_types::()?; with_get_last_error(|source| crate::Error::GetProcAddress { source }, || { @@ -210,21 +265,32 @@ /// /// # Safety /// - /// The handle shall be a result of a successful call of `LoadLibraryW` or a - /// handle previously returned by the `Library::into_raw` call. + /// 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> { - with_get_last_error(|source| crate::Error::FreeLibrary { source }, || { + 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)) + }).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 } } @@ -255,17 +321,17 @@ } } -/// Symbol from a library. +/// A symbol from a library. /// -/// A major difference compared to the cross-platform `Symbol` is that this does not ensure the -/// `Symbol` does not outlive `Library` it comes from. +/// A major difference compared to the cross-platform `Symbol` is that this does not ensure that the +/// `Symbol` does not outlive the `Library` that it comes from. pub struct Symbol { pointer: FARPROC, pd: marker::PhantomData } impl Symbol { - /// Convert the loaded Symbol into a handle. + /// Convert the loaded `Symbol` into a handle. pub fn into_raw(self) -> FARPROC { let pointer = self.pointer; mem::forget(self); @@ -312,49 +378,23 @@ } } -static USE_ERRORMODE: AtomicBool = AtomicBool::new(false); struct ErrorModeGuard(DWORD); impl ErrorModeGuard { #[allow(clippy::if_same_then_else)] fn new() -> Option { unsafe { - if !USE_ERRORMODE.load(Ordering::Acquire) { - let mut previous_mode = 0; - let success = errhandlingapi::SetThreadErrorMode(SEM_FAILCE, &mut previous_mode) != 0; - if !success && errhandlingapi::GetLastError() == winerror::ERROR_CALL_NOT_IMPLEMENTED { - USE_ERRORMODE.store(true, Ordering::Release); - } else if !success { - // SetThreadErrorMode failed with some other error? 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. - return None; - } else if previous_mode == SEM_FAILCE { - return None; - } else { - return Some(ErrorModeGuard(previous_mode)); - } - } - match errhandlingapi::SetErrorMode(SEM_FAILCE) { - SEM_FAILCE => { - // This is important to reduce racy-ness when this library is used on multiple - // threads. In particular this helps with following race condition: - // - // T1: SetErrorMode(SEM_FAILCE) - // T2: SetErrorMode(SEM_FAILCE) - // T1: SetErrorMode(old_mode) # not SEM_FAILCE - // T2: SetErrorMode(SEM_FAILCE) # restores to SEM_FAILCE on drop - // - // This is still somewhat racy in a sense that T1 might restore the error - // mode before T2 finishes loading the library, but that is less of a - // concern – it will only end up in end user seeing a dialog. - // - // Also, SetErrorMode itself is probably not an atomic operation. - None - } - a => Some(ErrorModeGuard(a)) + 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)) } } } @@ -363,11 +403,7 @@ impl Drop for ErrorModeGuard { fn drop(&mut self) { unsafe { - if !USE_ERRORMODE.load(Ordering::Relaxed) { - errhandlingapi::SetThreadErrorMode(self.0, ptr::null_mut()); - } else { - errhandlingapi::SetErrorMode(self.0); - } + errhandlingapi::SetThreadErrorMode(self.0, ptr::null_mut()); } } } @@ -414,7 +450,7 @@ /// Map the file into the process’ virtual address space as an image file. /// -/// The loader does not load the static imports or perform the other usual initialization steps. +/// The loader does not load the static imports or perform the other usual initialisation steps. /// Use this flag when you want to load a DLL only to extract messages or resources from it. /// /// Unless the application depends on the file having the in-memory layout of an image, this value @@ -424,7 +460,7 @@ /// See [flag documentation on MSDN](https://docs.microsoft.com/en-us/windows/win32/api/libloaderapi/nf-libloaderapi-loadlibraryexw#parameters). pub const LOAD_LIBRARY_AS_IMAGE_RESOURCE: DWORD = consts::LOAD_LIBRARY_AS_IMAGE_RESOURCE; -/// Search application's installation directory for the DLL and its dependencies. +/// Search the application's installation directory for the DLL and its dependencies. /// /// Directories in the standard search path are not searched. This value cannot be combined with /// [`LOAD_WITH_ALTERED_SEARCH_PATH`]. @@ -471,11 +507,11 @@ /// See [flag documentation on MSDN](https://docs.microsoft.com/en-us/windows/win32/api/libloaderapi/nf-libloaderapi-loadlibraryexw#parameters). pub const LOAD_LIBRARY_SEARCH_USER_DIRS: DWORD = consts::LOAD_LIBRARY_SEARCH_USER_DIRS; -/// If `filename specifies an absolute path, the system uses the alternate file search strategy +/// If `filename` specifies an absolute path, the system uses the alternate file search strategy /// discussed in the [Remarks section] to find associated executable modules that the specified /// module causes to be loaded. /// -/// If this value is used and `filename` specifies a relative path, the behavior is undefined. +/// If this value is used and `filename` specifies a relative path, the behaviour is undefined. /// /// If this value is not used, or if `filename` does not specify a path, the system uses the /// standard search strategy discussed in the [Remarks section] to find associated executable diff -Nru rust-libloading-0.6.3/src/safe.rs rust-libloading-0.7.2/src/safe.rs --- rust-libloading-0.6.3/src/safe.rs 1970-01-01 00:00:00.000000000 +0000 +++ rust-libloading-0.7.2/src/safe.rs 1973-11-29 21:33:09.000000000 +0000 @@ -0,0 +1,299 @@ +use super::Error; +#[cfg(docsrs)] +use super::os::unix as imp; // the implementation used here doesn't matter particularly much... +#[cfg(all(not(docsrs), unix))] +use super::os::unix as imp; +#[cfg(all(not(docsrs), 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(docsrs, 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(docsrs, 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> {} diff -Nru rust-libloading-0.6.3/src/util.rs rust-libloading-0.7.2/src/util.rs --- rust-libloading-0.6.3/src/util.rs 2020-08-23 16:09:31.000000000 +0000 +++ rust-libloading-0.7.2/src/util.rs 1973-11-29 21:33:09.000000000 +0000 @@ -1,10 +1,10 @@ -use std::ffi::{CStr, CString}; use std::borrow::Cow; +use std::ffi::{CStr, CString}; use std::os::raw; use crate::Error; -/// Checks for last byte and avoids allocating if it is zero. +/// Checks for the last byte and avoids allocating if it is zero. /// /// Non-last null bytes still result in an error. pub(crate) fn cstr_cow_from_bytes(slice: &[u8]) -> Result, Error> { @@ -13,11 +13,14 @@ // Slice out of 0 elements None => unsafe { Cow::Borrowed(CStr::from_ptr(&ZERO)) }, // Slice with trailing 0 - Some(&0) => Cow::Borrowed(CStr::from_bytes_with_nul(slice) - .map_err(|source| Error::CreateCStringWithTrailing { source })?), + Some(&0) => Cow::Borrowed( + CStr::from_bytes_with_nul(slice) + .map_err(|source| Error::CreateCStringWithTrailing { source })?, + ), // Slice with no trailing 0 - Some(_) => Cow::Owned(CString::new(slice) - .map_err(|source| Error::CreateCString { source })?), + Some(_) => { + Cow::Owned(CString::new(slice).map_err(|source| Error::CreateCString { source })?) + } }) } diff -Nru rust-libloading-0.6.3/tests/constants.rs rust-libloading-0.7.2/tests/constants.rs --- rust-libloading-0.6.3/tests/constants.rs 2020-08-23 16:09:31.000000000 +0000 +++ rust-libloading-0.7.2/tests/constants.rs 1973-11-29 21:33:09.000000000 +0000 @@ -1,5 +1,5 @@ -extern crate libloading; extern crate libc; +extern crate libloading; extern crate static_assertions; #[cfg(all(test, unix))] diff -Nru rust-libloading-0.6.3/tests/functions.rs rust-libloading-0.7.2/tests/functions.rs --- rust-libloading-0.6.3/tests/functions.rs 2020-08-23 16:09:31.000000000 +0000 +++ rust-libloading-0.7.2/tests/functions.rs 1973-11-29 21:33:09.000000000 +0000 @@ -4,32 +4,35 @@ extern crate libloading; use libloading::{Symbol, Library}; -const LIBPATH: &'static str = concat!(env!("OUT_DIR"), "/libtest_helpers.module"); +const LIBPATH: &'static str = "target/libtest_helpers.module"; fn make_helpers() { static ONCE: ::std::sync::Once = ::std::sync::Once::new(); ONCE.call_once(|| { - let rustc = option_env!("RUSTC").unwrap_or_else(|| { "rustc".into() }); + 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(LIBPATH) - .arg("--target") - .arg(env!("LIBLOADING_TEST_TARGET")) - .arg("-O"); - - cmd - .output() - .expect("could not compile the test helpers!"); + .arg(LIBPATH); + 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(); - let lib = Library::new(LIBPATH).unwrap(); unsafe { + let lib = Library::new(LIBPATH).unwrap(); let f: Symbol u32> = lib.get(b"test_identity_u32\0").unwrap(); assert_eq!(42, f(42)); } @@ -47,8 +50,8 @@ #[test] fn test_id_struct() { make_helpers(); - let lib = Library::new(LIBPATH).unwrap(); unsafe { + let lib = Library::new(LIBPATH).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 })); } @@ -57,8 +60,8 @@ #[test] fn test_0_no_0() { make_helpers(); - let lib = Library::new(LIBPATH).unwrap(); unsafe { + let lib = Library::new(LIBPATH).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); @@ -67,14 +70,16 @@ #[test] fn wrong_name_fails() { - Library::new(concat!(env!("OUT_DIR"), "/libtest_help")).err().unwrap(); + unsafe { + Library::new("target/this_location_is_definitely_non existent:^~").err().unwrap(); + } } #[test] fn missing_symbol_fails() { make_helpers(); - let lib = Library::new(LIBPATH).unwrap(); unsafe { + let lib = Library::new(LIBPATH).unwrap(); lib.get::<*mut ()>(b"test_does_not_exist").err().unwrap(); lib.get::<*mut ()>(b"test_does_not_exist\0").err().unwrap(); } @@ -83,8 +88,8 @@ #[test] fn interior_null_fails() { make_helpers(); - let lib = Library::new(LIBPATH).unwrap(); unsafe { + let lib = Library::new(LIBPATH).unwrap(); lib.get::<*mut ()>(b"test_does\0_not_exist").err().unwrap(); lib.get::<*mut ()>(b"test\0_does_not_exist\0").err().unwrap(); } @@ -93,8 +98,8 @@ #[test] fn test_incompatible_type() { make_helpers(); - let lib = Library::new(LIBPATH).unwrap(); unsafe { + let lib = Library::new(LIBPATH).unwrap(); assert!(match lib.get::<()>(b"test_identity_u32\0") { Err(libloading::Error::IncompatibleSize) => true, _ => false, @@ -108,8 +113,8 @@ unsafe fn get<'a, T>(l: &'a Library, _: T) -> Result, libloading::Error> { l.get::(b"test_identity_u32\0") } - let lib = Library::new(LIBPATH).unwrap(); unsafe { + let lib = Library::new(LIBPATH).unwrap(); assert!(match get(&lib, test_incompatible_type_named_fn) { Err(libloading::Error::IncompatibleSize) => true, _ => false, @@ -120,8 +125,8 @@ #[test] fn test_static_u32() { make_helpers(); - let lib = Library::new(LIBPATH).unwrap(); unsafe { + let lib = Library::new(LIBPATH).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(); @@ -132,8 +137,8 @@ #[test] fn test_static_ptr() { make_helpers(); - let lib = Library::new(LIBPATH).unwrap(); unsafe { + let lib = Library::new(LIBPATH).unwrap(); let var: Symbol<*mut *mut ()> = lib.get(b"TEST_STATIC_PTR\0").unwrap(); **var = *var as *mut _; let works: Symbol bool> = @@ -142,18 +147,41 @@ } } +#[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(LIBPATH).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(); - let _lib = Library::new(LIBPATH).unwrap(); - let this = Library::this(); // SAFE: functions are never called unsafe { + let _lib = Library::new(LIBPATH).unwrap(); + let this = Library::this(); // Library we loaded in `_lib` (should be RTLD_LOCAL). - // FIXME: inconsistent behaviour between macos and other posix systems - // assert!(this.get::(b"test_identity_u32").is_err()); + assert!(this.get::(b"test_identity_u32").is_err()); // Something obscure from libc... assert!(this.get::(b"freopen").is_ok()); } @@ -164,10 +192,11 @@ fn library_this() { use libloading::os::windows::Library; make_helpers(); - let _lib = Library::new(LIBPATH).unwrap(); - let this = Library::this().expect("this library"); - // SAFE: functions are never called unsafe { + // SAFE: well-known library without initialisers is loaded. + let _lib = Library::new(LIBPATH).unwrap(); + let this = Library::this().expect("this library"); + // SAFE: functions are never called. // Library we loaded in `_lib`. assert!(this.get::(b"test_identity_u32").is_err()); // Something "obscure" from kernel32... @@ -182,11 +211,9 @@ use winapi::shared::minwindef::DWORD; use libloading::os::windows::{Library, Symbol}; - let lib = Library::new("kernel32.dll").unwrap(); - let gle: Symbol DWORD> = unsafe { - lib.get(b"GetLastError").unwrap() - }; 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()) } @@ -199,12 +226,31 @@ use winapi::shared::minwindef::DWORD; use libloading::os::windows::{Library, Symbol}; - let lib = Library::new("kernel32.dll").unwrap(); - let gle: Symbol DWORD> = unsafe { - lib.get(b"GetLastError\0").unwrap() - }; 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()); + } +} diff -Nru rust-libloading-0.6.3/tests/windows.rs rust-libloading-0.7.2/tests/windows.rs --- rust-libloading-0.6.3/tests/windows.rs 2017-05-03 21:19:58.000000000 +0000 +++ rust-libloading-0.7.2/tests/windows.rs 1973-11-29 21:33:09.000000000 +0000 @@ -15,12 +15,16 @@ #[cfg(target_arch="x86")] fn load_ordinal_lib() -> Library { - Library::new("tests/nagisa32.dll").expect("nagisa32.dll") + unsafe { + Library::new("tests/nagisa32.dll").expect("nagisa32.dll") + } } #[cfg(target_arch="x86_64")] fn load_ordinal_lib() -> Library { - Library::new("tests/nagisa64.dll").expect("nagisa64.dll") + unsafe { + Library::new("tests/nagisa64.dll").expect("nagisa64.dll") + } } #[cfg(any(target_arch="x86", target_arch="x86_64"))] @@ -47,10 +51,14 @@ #[test] fn test_new_kernel23() { - Library::new("kernel23").err().unwrap(); + unsafe { + Library::new("kernel23").err().unwrap(); + } } #[test] fn test_new_kernel32_no_ext() { - Library::new("kernel32").unwrap(); + unsafe { + Library::new("kernel32").unwrap(); + } }