aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.cargo_vcs_info.json2
-rw-r--r--.github/workflows/libloading.yml31
-rw-r--r--Android.bp6
-rw-r--r--Cargo.toml14
-rw-r--r--Cargo.toml.orig24
-rw-r--r--METADATA6
-rw-r--r--README.mkd4
-rw-r--r--build.rs6
-rw-r--r--src/changelog.rs20
-rw-r--r--src/error.rs78
-rw-r--r--src/lib.rs84
-rw-r--r--src/os/mod.rs34
-rw-r--r--src/os/unix/consts.rs230
-rw-r--r--src/os/unix/mod.rs120
-rw-r--r--src/os/windows/mod.rs260
-rw-r--r--src/test_helpers.rs14
-rw-r--r--src/util.rs2
-rw-r--r--tests/constants.rs13
-rw-r--r--tests/functions.rs91
-rw-r--r--tests/library_filename.rs17
-rw-r--r--tests/markers.rs17
21 files changed, 832 insertions, 241 deletions
diff --git a/.cargo_vcs_info.json b/.cargo_vcs_info.json
index 7405439..a289ae4 100644
--- a/.cargo_vcs_info.json
+++ b/.cargo_vcs_info.json
@@ -1,5 +1,5 @@
{
"git": {
- "sha1": "fa787a3be5a9a6dcc47425c7298fed5a16f06afe"
+ "sha1": "21c7cdb08ffc38ceb0b0f9e7038f88548c2ade94"
}
}
diff --git a/.github/workflows/libloading.yml b/.github/workflows/libloading.yml
index 2ba5d13..d315445 100644
--- a/.github/workflows/libloading.yml
+++ b/.github/workflows/libloading.yml
@@ -24,12 +24,17 @@ jobs:
with:
toolchain: ${{ matrix.rust_toolchain }}
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:
@@ -45,6 +50,14 @@ jobs:
with:
command: test
args: --manifest-path=Cargo.toml --release -- --nocapture
+ - name: Documentation
+ uses: actions-rs/cargo@v1
+ with:
+ command: doc
+ args: --manifest-path=Cargo.toml
+ env:
+ RUSTDOCFLAGS: --cfg docsrs
+ if: ${{ matrix.rust_toolchain == 'nightly' }}
windows-gnu-test:
runs-on: windows-latest
@@ -54,10 +67,17 @@ jobs:
rust_toolchain: [nightly, stable]
rust_target:
- x86_64-pc-windows-gnu
- # Only 64 bit GCC is preinstalled
- #- i686-pc-windows-gnu
+ - i686-pc-windows-gnu
steps:
- uses: actions/checkout@v2
+ - name: Add MSYS2 to the PATH
+ run: echo "::add-path::c:/msys64/bin"
+ - name: Add 32-bit mingw-w64 to the PATH
+ run: echo "::add-path::c:/msys64/mingw32/bin"
+ if: startsWith(matrix.rust_target, 'i686')
+ - name: Add 64-bit mingw-w64 to the PATH
+ run: echo "::add-path::c:/msys64/mingw64/bin"
+ if: startsWith(matrix.rust_target, 'x86_64')
- name: Install Rust nightly
uses: actions-rs/toolchain@v1
with:
@@ -65,13 +85,6 @@ jobs:
target: ${{ matrix.rust_target }}
profile: minimal
default: true
- # https://github.com/rust-lang/rust/issues/49078
- - name: Fix windows-gnu rust-mingw
- run : |
- for i in crt2.o dllcrt2.o libmingwex.a libmsvcrt.a ; do
- cp -f "/C/ProgramData/Chocolatey/lib/mingw/tools/install/mingw64/x86_64-w64-mingw32/lib/$i" "`rustc --print sysroot`/lib/rustlib/x86_64-pc-windows-gnu/lib"
- done
- shell: bash
- uses: actions-rs/cargo@v1
with:
command: build
diff --git a/Android.bp b/Android.bp
index b7f23ac..a40bc5a 100644
--- a/Android.bp
+++ b/Android.bp
@@ -8,8 +8,14 @@ rust_library_host {
flags: [
"--cfg mtsafe_dlerror",
],
+ rustlibs: [
+ "libcfg_if",
+ ],
// TODO: Cannot use libdl on Android host?
// shared_libs: [
// "libdl",
// ],
}
+
+// dependent_library ["feature_list"]
+// cfg-if-0.1.10
diff --git a/Cargo.toml b/Cargo.toml
index 630f1e7..41ad4e7 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -12,14 +12,26 @@
[package]
name = "libloading"
-version = "0.6.2"
+version = "0.6.3"
authors = ["Simonas Kazlauskas <libloading@kazlauskas.me>"]
build = "build.rs"
description = "A safer binding to platform’s dynamic library loading utilities"
documentation = "https://docs.rs/libloading/"
+readme = "README.mkd"
keywords = ["dlopen", "load", "shared", "dylib"]
+categories = ["api-bindings"]
license = "ISC"
repository = "https://github.com/nagisa/rust_libloading/"
+[package.metadata.docs.rs]
+all-features = true
+rustdoc-args = ["--cfg", "docsrs"]
+[dev-dependencies.libc]
+version = "0.2"
+
+[dev-dependencies.static_assertions]
+version = "1.1"
+[target."cfg(unix)".dependencies.cfg-if]
+version = "0.1"
[target."cfg(windows)".dependencies.winapi]
version = "0.3"
features = ["winerror", "errhandlingapi", "libloaderapi"]
diff --git a/Cargo.toml.orig b/Cargo.toml.orig
index bd1114b..1dfd507 100644
--- a/Cargo.toml.orig
+++ b/Cargo.toml.orig
@@ -1,13 +1,18 @@
[package]
name = "libloading"
-version = "0.6.2"
+# 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"
authors = ["Simonas Kazlauskas <libloading@kazlauskas.me>"]
-build = "build.rs"
-description = "A safer binding to platform’s dynamic library loading utilities"
-keywords = ["dlopen", "load", "shared", "dylib"]
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"
+keywords = ["dlopen", "load", "shared", "dylib"]
+categories = ["api-bindings"]
+build = "build.rs"
[target.'cfg(windows)'.dependencies.winapi]
version = "0.3"
@@ -16,3 +21,14 @@ features = [
"errhandlingapi",
"libloaderapi",
]
+
+[target.'cfg(unix)'.dependencies.cfg-if]
+version = "0.1"
+
+[dev-dependencies]
+libc = "0.2"
+static_assertions = "1.1"
+
+[package.metadata.docs.rs]
+all-features = true
+rustdoc-args = ["--cfg", "docsrs"]
diff --git a/METADATA b/METADATA
index 01da0ad..f147aaf 100644
--- a/METADATA
+++ b/METADATA
@@ -9,11 +9,11 @@ third_party {
type: GIT
value: "https://github.com/nagisa/rust_libloading"
}
- version: "0.6.2"
+ version: "0.6.3"
license_type: NOTICE
last_upgrade_date {
year: 2020
- month: 5
- day: 5
+ month: 8
+ day: 24
}
}
diff --git a/README.mkd b/README.mkd
index 006f172..8f7d342 100644
--- a/README.mkd
+++ b/README.mkd
@@ -1,6 +1,6 @@
# libloading
-A memory-safer wrapper around system dynamic library loading primitives. The most important safety
+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.
@@ -13,4 +13,4 @@ functions and static variables these libraries contain.
[docs]: https://docs.rs/libloading/
[changelog]: https://docs.rs/libloading/*/libloading/changelog/index.html
-libloading is distributed under ISC (MIT-like) license.
+libloading is available to use under ISC (MIT-like) license.
diff --git a/build.rs b/build.rs
index fdc446d..e217f80 100644
--- a/build.rs
+++ b/build.rs
@@ -61,4 +61,10 @@ fn main() {
::std::process::exit(0xfd);
}
}
+
+ // For tests
+ println!(
+ "cargo:rustc-env=LIBLOADING_TEST_TARGET={}",
+ std::env::var("TARGET").expect("$TARGET is not set")
+ );
}
diff --git a/src/changelog.rs b/src/changelog.rs
index 952eb5e..01c25ec 100644
--- a/src/changelog.rs
+++ b/src/changelog.rs
@@ -1,6 +1,18 @@
//! Project changelog
// 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.6.3 (2020-08-22)
+///
+/// * Improve documentation, allowing to view all of the os-specific functionality from
+/// documentation generated for any target;
+/// * Add [`os::windows::Library::this`];
+/// * Added constants to use with OS-specific `Library::open`;
+/// * Add [`library_filename`].
+///
+/// [`os::windows::Library::this`]: crate::os::windows::Library::this
+/// [`library_filename`]: crate::library_filename
/// Release 0.6.2 (2020-05-06)
///
@@ -12,7 +24,7 @@ pub mod r0_6_2 {}
/// * Introduced a new method [`os::windows::Library::load_with_flags`];
/// * Added support for the Illumos triple.
///
-/// [`os::windows::Library::load_with_flags`]: ../../os/windows/struct.Library.html#method.load_with_flags
+/// [`os::windows::Library::load_with_flags`]: crate::os::windows::Library::load_with_flags
pub mod r0_6_1 {}
/// Release 0.6.0 (2020-04-05)
@@ -39,9 +51,9 @@ pub mod r0_6_1 {}
/// `dlsym` returns a null pointer. For the use-cases where loading null pointers is necessary
/// consider using [`os::unix::Library::get_singlethreaded`] instead.
///
-/// [`Library::get`]: ../../struct.Library.html#method.get
-/// [`os::unix::Library::get_singlethreaded`]: ../../os/unix/struct.Library.html#method.get_singlethreaded
-/// [`Error`]: ../../enum.Error.html
+/// [`Library::get`]: crate::Library::get
+/// [`os::unix::Library::get_singlethreaded`]: crate::os::unix::Library::get_singlethreaded
+/// [`Error`]: crate::Error
pub mod r0_6_0 {}
diff --git a/src/error.rs b/src/error.rs
index dcbe2b1..6ba5367 100644
--- a/src/error.rs
+++ b/src/error.rs
@@ -1,5 +1,6 @@
use std::ffi::CString;
+/// A `dlerror` error.
pub struct DlDescription(pub(crate) CString);
impl std::fmt::Debug for DlDescription {
@@ -8,6 +9,7 @@ impl std::fmt::Debug for DlDescription {
}
}
+/// A Windows API error.
pub struct WindowsError(pub(crate) std::io::Error);
impl std::fmt::Debug for WindowsError {
@@ -16,39 +18,71 @@ impl std::fmt::Debug for WindowsError {
}
}
+/// Errors.
#[derive(Debug)]
#[non_exhaustive]
pub enum Error {
/// The `dlopen` call failed.
- DlOpen { desc: DlDescription },
+ DlOpen {
+ /// The source error.
+ desc: DlDescription
+ },
/// The `dlopen` call failed and system did not report an error.
DlOpenUnknown,
/// The `dlsym` call failed.
- DlSym { desc: DlDescription },
+ DlSym {
+ /// The source error.
+ desc: DlDescription
+ },
/// The `dlsym` call failed and system did not report an error.
DlSymUnknown,
/// The `dlclose` call failed.
- DlClose { desc: DlDescription },
+ DlClose {
+ /// The source error.
+ desc: DlDescription
+ },
/// The `dlclose` call failed and system did not report an error.
DlCloseUnknown,
/// The `LoadLibraryW` call failed.
- LoadLibraryW { source: WindowsError },
+ LoadLibraryW {
+ /// The source error.
+ source: WindowsError
+ },
/// The `LoadLibraryW` call failed and system did not report an error.
LoadLibraryWUnknown,
+ /// The `GetModuleHandleExW` call failed.
+ GetModuleHandleExW {
+ /// The source error.
+ source: WindowsError
+ },
+ /// The `LoadLibraryW` call failed and system did not report an error.
+ GetModuleHandleExWUnknown,
/// The `GetProcAddress` call failed.
- GetProcAddress { source: WindowsError },
+ GetProcAddress {
+ /// The source error.
+ source: WindowsError
+ },
/// The `GetProcAddressUnknown` call failed and system did not report an error.
GetProcAddressUnknown,
/// The `FreeLibrary` call failed.
- FreeLibrary { source: WindowsError },
+ FreeLibrary {
+ /// The source error.
+ source: WindowsError
+ },
/// The `FreeLibrary` call failed and system did not report an error.
FreeLibraryUnknown,
/// The requested type cannot possibly work.
IncompatibleSize,
/// Could not create a new CString.
- CreateCString { source: std::ffi::NulError },
+ CreateCString {
+ /// The source error.
+ source: std::ffi::NulError
+ },
/// Could not create a new CString from bytes with trailing null.
- CreateCStringWithTrailing { source: std::ffi::FromBytesWithNulError },
+ CreateCStringWithTrailing {
+ /// The source error.
+ source: std::ffi::FromBytesWithNulError
+ },
}
impl std::error::Error for Error {
@@ -75,9 +109,12 @@ impl std::fmt::Display for Error {
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, "LoadLibraryW failed"),
+ LoadLibraryW { .. } => write!(f, "LoadLibraryExW failed"),
LoadLibraryWUnknown =>
- write!(f, "LoadLibraryW failed, but system did not report the error"),
+ write!(f, "LoadLibraryExW failed, but system did not report the error"),
+ GetModuleHandleExW { .. } => write!(f, "GetModuleHandleExW failed"),
+ GetModuleHandleExWUnknown =>
+ write!(f, "GetModuleHandleExWUnknown failed, but system did not report the error"),
GetProcAddress { .. } => write!(f, "GetProcAddress failed"),
GetProcAddressUnknown =>
write!(f, "GetProcAddress failed, but system did not report the error"),
@@ -91,24 +128,3 @@ impl std::fmt::Display for Error {
}
}
}
-
-#[cfg(test)]
-mod tests {
- #[test]
- fn error_send() {
- fn assert_send<T: Send>() {}
- assert_send::<super::Error>();
- }
-
- #[test]
- fn error_sync() {
- fn assert_sync<T: Sync>() {}
- assert_sync::<super::Error>();
- }
-
- #[test]
- fn error_display() {
- fn assert_display<T: std::fmt::Display>() {}
- assert_display::<super::Error>();
- }
-}
diff --git a/src/lib.rs b/src/lib.rs
index fad7d63..f6fc58d 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -17,7 +17,7 @@
//!
//! ```toml
//! [dependencies]
-//! libloading = "0.5"
+//! libloading = "0.6"
//! ```
//!
//! Then inside your project
@@ -36,7 +36,17 @@
//!
//! 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.
-use std::ffi::OsStr;
+#![deny(
+ missing_docs,
+ clippy::all,
+ unreachable_pub,
+ unused,
+)]
+#![cfg_attr(docsrs, deny(broken_intra_doc_links))]
+#![cfg_attr(docsrs, feature(doc_cfg))]
+
+use std::env::consts::{DLL_PREFIX, DLL_SUFFIX};
+use std::ffi::{OsStr, OsString};
use std::fmt;
use std::ops;
use std::marker;
@@ -64,7 +74,7 @@ impl Library {
/// * Absolute path to the library;
/// * Relative (to the current working directory) path to the library.
///
- /// ## Thread-safety
+ /// # Thread-safety
///
/// The implementation strives to be as MT-safe as sanely possible, however due to certain
/// error-handling related resources not always being safe, this library is not MT-safe either.
@@ -77,15 +87,17 @@ impl Library {
/// [`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
- /// path-less filename and library search path is modified (`SetDllDirectory` function on
+ /// relative filenames and the library search path is modified (`SetDllDirectory` function on
/// Windows, `{DY,}LD_LIBRARY_PATH` environment variable on UNIX).
///
- /// ## Platform-specific behaviour
+ /// # Platform-specific behaviour
///
/// When a plain library filename is supplied, locations where library is searched for is
- /// platform specific and cannot be adjusted in a portable manner.
+ /// 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
+ /// ## Windows
///
/// If the `filename` specifies a library filename without path and with extension omitted,
/// `.dll` extension is implicitly added. This behaviour may be suppressed by appending a
@@ -95,17 +107,18 @@ impl Library {
/// `#[thread_local]` attributes), loading the library will fail on versions prior to Windows
/// Vista.
///
- /// ## Tips
+ /// # Tips
///
/// Distributing your dynamic libraries under a filename common to all platforms (e.g.
/// `awesome.module`) allows to avoid code which has to account for platform’s conventional
/// library filenames.
///
- /// Strive to specify absolute or 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 flaky code.
+ /// 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
+ /// # Examples
///
/// ```no_run
/// # use ::libloading::Library;
@@ -126,16 +139,16 @@ impl Library {
/// Symbol is interpreted as-is; no mangling is done. This means that symbols like `x::y` are
/// most likely invalid.
///
- /// ## Unsafety
+ /// # Safety
///
/// Pointer to a value of arbitrary type is returned. Using a value with wrong type is
/// undefined.
///
- /// ## Platform-specific behaviour
+ /// # Platform-specific behaviour
///
- /// On Linux and Windows, a TLS variable acts just like any regular global variable. OS X uses
- /// some sort of lazy initialization scheme, which makes loading TLS variables this way
- /// impossible. Using a TLS variable loaded this way on OS X is undefined 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
@@ -143,7 +156,7 @@ impl Library {
/// pointer without it being an error. If loading a null pointer is something you care about,
/// consider using the [`os::unix::Library::get_singlethreaded`] call.
///
- /// ## Examples
+ /// # Examples
///
/// Given a loaded library:
///
@@ -232,12 +245,13 @@ pub struct Symbol<'lib, T: 'lib> {
impl<'lib, T> Symbol<'lib, T> {
/// Extract the wrapped `os::platform::Symbol`.
///
- /// ## Unsafety
+ /// # Safety
+ ///
/// Using this function relinquishes all the lifetime guarantees. It is up to programmer to
/// ensure the resulting `Symbol` is not used past the lifetime of the `Library` this symbol
/// was loaded from.
///
- /// ## Examples
+ /// # Examples
///
/// ```no_run
/// # use ::libloading::{Library, Symbol};
@@ -256,12 +270,12 @@ impl<'lib, T> Symbol<'lib, T> {
/// Note that, in order to create association between the symbol and the library this symbol
/// came from, this function requires reference to the library provided.
///
- /// ## Unsafety
+ /// # Safety
///
/// It is invalid to provide a reference to any other value other than the library the `sym`
/// was loaded from. Doing so invalidates any lifetime guarantees.
///
- /// ## Examples
+ /// # Examples
///
/// ```no_run
/// # use ::libloading::{Library, Symbol};
@@ -283,7 +297,7 @@ impl<'lib, T> Symbol<'lib, T> {
impl<'lib, T> Symbol<'lib, Option<T>> {
/// Lift Option out of the symbol.
///
- /// ## Examples
+ /// # Examples
///
/// ```no_run
/// # use ::libloading::{Library, Symbol};
@@ -326,3 +340,27 @@ impl<'lib, T> fmt::Debug for Symbol<'lib, T> {
unsafe impl<'lib, T: Send> Send for Symbol<'lib, T> {}
unsafe impl<'lib, T: Sync> Sync for Symbol<'lib, T> {}
+
+/// 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
+/// `name` to construct the filename.
+///
+/// # Examples
+///
+/// It can be used to load global libraries in a platform independent manner:
+///
+/// ```
+/// use libloading::{Library, library_filename};
+/// // Will attempt to load `libLLVM.so` on Linux, `libLLVM.dylib` on macOS and `LLVM.dll` on
+/// // Windows.
+/// let library = Library::new(library_filename("LLVM"));
+/// ```
+pub fn library_filename<S: AsRef<OsStr>>(name: S) -> OsString {
+ let name = name.as_ref();
+ let mut string = OsString::with_capacity(name.len() + DLL_PREFIX.len() + DLL_SUFFIX.len());
+ string.push(DLL_PREFIX);
+ string.push(name);
+ string.push(DLL_SUFFIX);
+ string
+}
diff --git a/src/os/mod.rs b/src/os/mod.rs
index ccbc8e9..40361c5 100644
--- a/src/os/mod.rs
+++ b/src/os/mod.rs
@@ -16,30 +16,12 @@
//! use libloading::os::windows::*;
//! ```
-macro_rules! unix {
- ($item: item) => {
- /// UNIX implementation of dynamic library loading.
- ///
- /// This module should be expanded with more UNIX-specific functionality in the future.
- $item
- }
-}
+/// UNIX implementation of dynamic library loading.
+#[cfg(any(unix, docsrs))]
+#[cfg_attr(docsrs, doc(cfg(unix)))]
+pub mod unix;
-macro_rules! windows {
- ($item: item) => {
- /// Windows implementation of dynamic library loading.
- ///
- /// This module should be expanded with more Windows-specific functionality in the future.
- $item
- }
-}
-
-#[cfg(unix)]
-unix!(pub mod unix;);
-#[cfg(unix)]
-windows!(pub mod windows {});
-
-#[cfg(windows)]
-windows!(pub mod windows;);
-#[cfg(windows)]
-unix!(pub mod unix {});
+/// Windows implementation of dynamic library loading.
+#[cfg(any(windows, docsrs))]
+#[cfg_attr(docsrs, doc(cfg(windows)))]
+pub mod windows;
diff --git a/src/os/unix/consts.rs b/src/os/unix/consts.rs
new file mode 100644
index 0000000..823155e
--- /dev/null
+++ b/src/os/unix/consts.rs
@@ -0,0 +1,230 @@
+use std::os::raw::c_int;
+
+/// Perform lazy binding.
+///
+/// Relocations shall be performed at an implementation-defined time, ranging from the time
+/// of the [`Library::open`] call until the first reference to a given symbol occurs.
+/// Specifying `RTLD_LAZY` should improve performance on implementations supporting dynamic
+/// symbol binding since a process might not reference all of the symbols in an executable
+/// object file. And, for systems supporting dynamic symbol resolution for normal process
+/// execution, this behavior mimics the normal handling of process execution.
+///
+/// Conflicts with [`RTLD_NOW`].
+///
+/// [`Library::open`]: crate::os::unix::Library::open
+pub const RTLD_LAZY: c_int = posix::RTLD_LAZY;
+
+/// Perform eager binding.
+///
+/// All necessary relocations shall be performed when the executable object file is first
+/// loaded. This may waste some processing if relocations are performed for symbols
+/// that are never referenced. This behavior may be useful for applications that need to
+/// know that all symbols referenced during execution will be available before
+/// [`Library::open`] returns.
+///
+/// Conflicts with [`RTLD_LAZY`].
+///
+/// [`Library::open`]: crate::os::unix::Library::open
+pub const RTLD_NOW: c_int = posix::RTLD_NOW;
+
+/// Make loaded symbols available for resolution globally.
+///
+/// The executable object file's symbols shall be made available for relocation processing of any
+/// other executable object file. In addition, calls to [`Library::get`] on `Library` obtained from
+/// [`Library::this`] allows executable object files loaded with this mode to be searched.
+///
+/// [`Library::this`]: crate::os::unix::Library::this
+/// [`Library::get`]: crate::os::unix::Library::get
+pub const RTLD_GLOBAL: c_int = posix::RTLD_GLOBAL;
+
+/// Load symbols into an isolated namespace.
+///
+/// The executable object file's symbols shall not be made available for relocation processing of
+/// any other executable object file. This mode of operation is most appropriate for e.g. plugins.
+pub const RTLD_LOCAL: c_int = posix::RTLD_LOCAL;
+
+#[cfg(docsrs)]
+mod posix {
+ use super::c_int;
+ pub(super) const RTLD_LAZY: c_int = !0;
+ pub(super) const RTLD_NOW: c_int = !0;
+ pub(super) const RTLD_GLOBAL: c_int = !0;
+ pub(super) const RTLD_LOCAL: c_int = !0;
+}
+
+#[cfg(not(docsrs))]
+mod posix {
+ extern crate cfg_if;
+ use self::cfg_if::cfg_if;
+ use super::c_int;
+ cfg_if! {
+ if #[cfg(target_os = "haiku")] {
+ pub(super) const RTLD_LAZY: c_int = 0;
+ } else if #[cfg(any(
+ target_os = "linux",
+ target_os = "android",
+ target_os = "emscripten",
+
+ target_os = "macos",
+ target_os = "ios",
+ target_os = "freebsd",
+ target_os = "dragonfly",
+ target_os = "openbsd",
+ target_os = "netbsd",
+
+ target_os = "solaris",
+ target_os = "illumos",
+
+ target_env = "uclibc",
+ target_env = "newlib",
+
+ target_os = "fuchsia",
+ target_os = "redox",
+ ))] {
+ pub(super) const RTLD_LAZY: c_int = 1;
+ } else {
+ compile_error!(
+ "Target has no known `RTLD_LAZY` value. Please submit an issue or PR adding it."
+ );
+ }
+ }
+
+ cfg_if! {
+ if #[cfg(target_os = "haiku")] {
+ pub(super) const RTLD_NOW: c_int = 1;
+ } else if #[cfg(any(
+ target_os = "linux",
+ all(target_os = "android", target_pointer_width = "64"),
+ target_os = "emscripten",
+
+ target_os = "macos",
+ target_os = "ios",
+ target_os = "freebsd",
+ target_os = "dragonfly",
+ target_os = "openbsd",
+ target_os = "netbsd",
+
+ target_os = "solaris",
+ target_os = "illumos",
+
+ target_env = "uclibc",
+ target_env = "newlib",
+
+ target_os = "fuchsia",
+ target_os = "redox",
+ ))] {
+ pub(super) const RTLD_NOW: c_int = 2;
+ } else if #[cfg(all(target_os = "android",target_pointer_width = "32"))] {
+ pub(super) const RTLD_NOW: c_int = 0;
+ } else {
+ compile_error!(
+ "Target has no known `RTLD_NOW` value. Please submit an issue or PR adding it."
+ );
+ }
+ }
+
+ cfg_if! {
+ if #[cfg(any(
+ target_os = "haiku",
+ all(target_os = "android",target_pointer_width = "32"),
+ ))] {
+ pub(super) const RTLD_GLOBAL: c_int = 2;
+ } else if #[cfg(any(
+ target_env = "uclibc",
+ all(target_os = "linux", target_arch = "mips"),
+ all(target_os = "linux", target_arch = "mips64"),
+ ))] {
+ pub(super) const RTLD_GLOBAL: c_int = 4;
+ } else if #[cfg(any(
+ target_os = "macos",
+ target_os = "ios",
+ ))] {
+ pub(super) const RTLD_GLOBAL: c_int = 8;
+ } else if #[cfg(any(
+ target_os = "linux",
+ all(target_os = "android", target_pointer_width = "64"),
+ target_os = "emscripten",
+
+ target_os = "freebsd",
+ target_os = "dragonfly",
+ target_os = "openbsd",
+ target_os = "netbsd",
+
+ target_os = "solaris",
+ target_os = "illumos",
+
+ target_env = "newlib",
+
+ target_os = "fuchsia",
+ target_os = "redox",
+ ))] {
+ pub(super) const RTLD_GLOBAL: c_int = 0x100;
+ } else {
+ compile_error!(
+ "Target has no known `RTLD_GLOBAL` value. Please submit an issue or PR adding it."
+ );
+ }
+ }
+
+ cfg_if! {
+ if #[cfg(target_os = "netbsd")] {
+ pub(super) const RTLD_LOCAL: c_int = 0x200;
+ } else if #[cfg(any(
+ target_os = "macos",
+ target_os = "ios",
+ ))] {
+ pub(super) const RTLD_LOCAL: c_int = 4;
+ } else if #[cfg(any(
+ target_os = "linux",
+ target_os = "android",
+ target_os = "emscripten",
+
+ target_os = "freebsd",
+ target_os = "dragonfly",
+ target_os = "openbsd",
+
+ target_os = "haiku",
+
+ target_os = "solaris",
+ target_os = "illumos",
+
+ target_env = "uclibc",
+ target_env = "newlib",
+
+ target_os = "fuchsia",
+ target_os = "redox",
+ ))] {
+ pub(super) const RTLD_LOCAL: c_int = 0;
+ } else {
+ compile_error!(
+ "Target has no known `RTLD_LOCAL` value. Please submit an issue or PR adding it."
+ );
+ }
+ }
+}
+
+// Other constants that exist but are not bound because they are platform-specific (non-posix)
+// extensions. Some of these constants are only relevant to `dlsym` or `dlmopen` calls.
+//
+// RTLD_CONFGEN
+// RTLD_DEFAULT
+// RTLD_DI_CONFIGADDR
+// RTLD_DI_LINKMAP
+// RTLD_DI_LMID
+// RTLD_DI_ORIGIN
+// RTLD_DI_PROFILENAME
+// RTLD_DI_PROFILEOUT
+// RTLD_DI_SERINFO
+// RTLD_DI_SERINFOSIZE
+// RTLD_DI_TLS_DATA
+// RTLD_DI_TLS_MODID
+// RTLD_FIRST
+// RTLD_GROUP
+// RTLD_NEXT
+// RTLD_PARENT
+// RTLD_PROBE
+// RTLD_SELF
+// RTLD_WORLD
+// RTLD_NODELETE
+// RTLD_NOLOAD
+// RTLD_DEEPBIND
diff --git a/src/os/unix/mod.rs b/src/os/unix/mod.rs
index 29d2003..785c58f 100644
--- a/src/os/unix/mod.rs
+++ b/src/os/unix/mod.rs
@@ -1,9 +1,21 @@
-use util::{ensure_compatible_types, cstr_cow_from_bytes};
+// A hack for docs.rs to build documentation that has both windows and linux documentation in the
+// same rustdoc build visible.
+#[cfg(all(docsrs, not(unix)))]
+mod unix_imports {
+}
+#[cfg(any(not(docsrs), unix))]
+mod unix_imports {
+ pub(super) use std::os::unix::ffi::OsStrExt;
+}
+use self::unix_imports::*;
+use util::{ensure_compatible_types, cstr_cow_from_bytes};
use std::ffi::{CStr, OsStr};
use std::{fmt, marker, mem, ptr};
use std::os::raw;
-use std::os::unix::ffi::OsStrExt;
+pub use self::consts::*;
+
+mod consts;
// dl* family of functions did not have enough thought put into it.
//
@@ -61,7 +73,7 @@ where F: FnOnce() -> Option<T> {
})
}
-/// A platform-specific equivalent of the cross-platform `Library`.
+/// A platform-specific counterpart of the cross-platform [`Library`](crate::Library).
pub struct Library {
handle: *mut raw::c_void
}
@@ -85,34 +97,45 @@ unsafe impl Send for Library {}
unsafe impl Sync for Library {}
impl Library {
- /// Find and load a shared library (module).
+ /// Find and eagerly load a shared library (module).
///
- /// Locations where library is searched for is platform specific and can’t be adjusted
- /// portably.
+ /// If the `filename` contains a [path separator], the `filename` is interpreted as a `path` to
+ /// a file. Otherwise, platform-specific algorithms are employed to find a library with a
+ /// matching file name.
///
- /// Corresponds to `dlopen(filename, RTLD_NOW)`.
+ /// This is equivalent to [`Library::open`]`(filename, RTLD_NOW)`.
+ ///
+ /// [path separator]: std::path::MAIN_SEPARATOR
#[inline]
pub fn new<P: AsRef<OsStr>>(filename: P) -> Result<Library, crate::Error> {
Library::open(Some(filename), RTLD_NOW)
}
- /// Load the dynamic libraries linked into main program.
+ /// Eagerly load the `Library` representing the current executable.
+ ///
+ /// [`Library::get`] calls of the returned `Library` will look for symbols in following
+ /// locations in order:
+ ///
+ /// 1. Original program image;
+ /// 2. Any executable object files (e.g. shared libraries) loaded at program startup;
+ /// 3. Executable object files loaded at runtime (e.g. via other `Library::new` calls or via
+ /// calls to the `dlopen` function)
///
- /// This allows retrieving symbols from any **dynamic** library linked into the program,
- /// without specifying the exact library.
+ /// Note that behaviour of `Library` loaded with this method is different from
+ /// Libraries loaded with [`os::windows::Library::this`].
///
- /// Corresponds to `dlopen(NULL, RTLD_NOW)`.
+ /// This is equivalent to [`Library::open`]`(None, RTLD_NOW)`.
+ ///
+ /// [`os::windows::Library::this`]: crate::os::windows::Library::this
#[inline]
pub fn this() -> Library {
Library::open(None::<&OsStr>, RTLD_NOW).expect("this should never fail")
}
- /// Find and load a shared library (module).
- ///
- /// Locations where library is searched for is platform specific and can’t be adjusted
- /// portably.
+ /// Find and load an executable object file (shared library).
///
- /// If the `filename` is None, null pointer is passed to `dlopen`.
+ /// See documentation for [`Library::this`] for further description of behaviour
+ /// when the `filename` is `None`. Otherwise see [`Library::new`].
///
/// Corresponds to `dlopen(filename, flags)`.
pub fn open<P>(filename: Option<P>, flags: raw::c_int) -> Result<Library, crate::Error>
@@ -174,37 +197,33 @@ impl Library {
/// Get a pointer to function or static variable by symbol name.
///
/// The `symbol` may not contain any null bytes, with an exception of last byte. A null
- /// terminated `symbol` may avoid a string allocation in some cases.
+ /// terminated `symbol` may avoid an allocation in some cases.
///
/// Symbol is interpreted as-is; no mangling is done. This means that symbols like `x::y` are
/// most likely invalid.
///
- /// ## Unsafety
+ /// # Safety
///
/// This function does not validate the type `T`. It is up to the user of this function to
/// ensure that the loaded symbol is in fact a `T`. Using a value with a wrong type has no
- /// definied behaviour.
- ///
- ///
+ /// defined behaviour.
///
- /// ## Platform-specific behaviour
+ /// # Platform-specific behaviour
///
- /// OS X uses some sort of lazy initialization scheme, which makes loading TLS variables
- /// impossible. Using a TLS variable loaded this way on OS X is undefined behaviour.
+ /// 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.
///
/// On POSIX implementations where the `dlerror` function is not confirmed to be MT-safe (such
- /// as FreeBSD), this function will unconditionally return an error the underlying `dlsym` call
- /// returns a null pointer. There are rare situations where `dlsym` returns a genuine null
- /// pointer without it being an error. If loading a null pointer is something you care about,
- /// consider using the [`Library::get_singlethreaded`] call.
+ /// as FreeBSD), this function will unconditionally return an error when the underlying `dlsym`
+ /// call returns a null pointer. There are rare situations where `dlsym` returns a genuine null
+ /// pointer without it being an error. If loading a symbol at null address is something you
+ /// care about, consider using the [`Library::get_singlethreaded`] call.
#[inline(always)]
pub unsafe fn get<T>(&self, symbol: &[u8]) -> Result<Symbol<T>, crate::Error> {
#[cfg(mtsafe_dlerror)]
- { return self.get_singlethreaded(symbol); }
+ { self.get_singlethreaded(symbol) }
#[cfg(not(mtsafe_dlerror))]
- {
- return self.get_impl(symbol, || Err(crate::Error::DlSymUnknown));
- }
+ { self.get_impl(symbol, || Err(crate::Error::DlSymUnknown)) }
}
/// Get a pointer to function or static variable by symbol name.
@@ -215,20 +234,20 @@ impl Library {
/// Symbol is interpreted as-is; no mangling is done. This means that symbols like `x::y` are
/// most likely invalid.
///
- /// ## Unsafety
+ /// # Safety
///
/// This function does not validate the type `T`. It is up to the user of this function to
/// ensure that the loaded symbol is in fact a `T`. Using a value with a wrong type has no
- /// definied behaviour.
+ /// defined behaviour.
///
/// It is up to the user of this library to ensure that no other calls to an MT-unsafe
- /// implementation of `dlerror` occur while this function is executing. Failing that the
- /// results of this function are not defined.
+ /// implementation of `dlerror` occur during execution of this function. Failing that, the
+ /// behaviour of this function is not defined.
///
- /// ## Platform-specific behaviour
+ /// # Platform-specific behaviour
///
- /// OS X uses some sort of lazy initialization scheme, which makes loading TLS variables
- /// impossible. Using a TLS variable loaded this way on OS X is undefined behaviour.
+ /// 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.
#[inline(always)]
pub unsafe fn get_singlethreaded<T>(&self, symbol: &[u8]) -> Result<Symbol<T>, crate::Error> {
self.get_impl(symbol, || Ok(Symbol {
@@ -249,14 +268,14 @@ impl Library {
/// Convert a raw handle returned by `dlopen`-family of calls to a `Library`.
///
- /// ## Unsafety
+ /// # Safety
///
/// The pointer shall be a result of a successful call of the `dlopen`-family of functions or a
/// pointer previously returned by `Library::into_raw` call. It must be valid to call `dlclose`
/// with this pointer as an argument.
pub unsafe fn from_raw(handle: *mut raw::c_void) -> Library {
Library {
- handle: handle
+ handle
}
}
@@ -266,8 +285,7 @@ impl Library {
/// what library was opened or other platform specifics.
///
/// You only need to call this if you are interested in handling any errors that may arise when
- /// library is unloaded. Otherwise the implementation of `Drop` for `Library` will close the
- /// library and ignore the errors were they arise.
+ /// library is unloaded. Otherwise this will be done when `Library` is dropped.
pub fn close(self) -> Result<(), crate::Error> {
let result = with_dlerror(|desc| crate::Error::DlClose { desc }, || {
if unsafe { dlclose(self.handle) } == 0 {
@@ -341,7 +359,7 @@ impl<T> ::std::ops::Deref for Symbol<T> {
fn deref(&self) -> &T {
unsafe {
// Additional reference level for a dereference on `deref` return value.
- mem::transmute(&self.pointer)
+ &*(&self.pointer as *const *mut _ as *const T)
}
}
}
@@ -369,7 +387,6 @@ impl<T> fmt::Debug for Symbol<T> {
}
// Platform specific things
-
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;
@@ -378,11 +395,6 @@ extern {
fn dladdr(addr: *mut raw::c_void, info: *mut DlInfo) -> raw::c_int;
}
-#[cfg(not(target_os="android"))]
-const RTLD_NOW: raw::c_int = 2;
-#[cfg(target_os="android")]
-const RTLD_NOW: raw::c_int = 0;
-
#[repr(C)]
struct DlInfo {
dli_fname: *const raw::c_char,
@@ -390,11 +402,3 @@ struct DlInfo {
dli_sname: *const raw::c_char,
dli_saddr: *mut raw::c_void
}
-
-#[cfg(test)]
-mod tests {
- #[test]
- fn this() {
- super::Library::this();
- }
-}
diff --git a/src/os/windows/mod.rs b/src/os/windows/mod.rs
index 3620b3e..a106234 100644
--- a/src/os/windows/mod.rs
+++ b/src/os/windows/mod.rs
@@ -1,17 +1,63 @@
-extern crate winapi;
-use self::winapi::shared::minwindef::{WORD, DWORD, HMODULE, FARPROC};
-use self::winapi::shared::ntdef::WCHAR;
-use self::winapi::shared::winerror;
-use self::winapi::um::{errhandlingapi, libloaderapi};
+// A hack for docs.rs to build documentation that has both windows and linux documentation in the
+// same rustdoc build visible.
+#[cfg(all(docsrs, not(windows)))]
+mod windows_imports {
+ pub(super) enum WORD {}
+ pub(super) struct DWORD;
+ pub(super) enum HMODULE {}
+ pub(super) enum FARPROC {}
-use util::{ensure_compatible_types, cstr_cow_from_bytes};
+ pub(super) mod consts {
+ use super::DWORD;
+ pub(crate) const LOAD_IGNORE_CODE_AUTHZ_LEVEL: DWORD = DWORD;
+ pub(crate) const LOAD_LIBRARY_AS_DATAFILE: DWORD = DWORD;
+ pub(crate) const LOAD_LIBRARY_AS_DATAFILE_EXCLUSIVE: DWORD = DWORD;
+ pub(crate) const LOAD_LIBRARY_AS_IMAGE_RESOURCE: DWORD = DWORD;
+ pub(crate) const LOAD_LIBRARY_SEARCH_APPLICATION_DIR: DWORD = DWORD;
+ pub(crate) const LOAD_LIBRARY_SEARCH_DEFAULT_DIRS: DWORD = DWORD;
+ pub(crate) const LOAD_LIBRARY_SEARCH_DLL_LOAD_DIR: DWORD = DWORD;
+ pub(crate) const LOAD_LIBRARY_SEARCH_SYSTEM32: DWORD = DWORD;
+ pub(crate) const LOAD_LIBRARY_SEARCH_USER_DIRS: DWORD = DWORD;
+ pub(crate) const LOAD_WITH_ALTERED_SEARCH_PATH: DWORD = DWORD;
+ pub(crate) const LOAD_LIBRARY_REQUIRE_SIGNED_TARGET: DWORD = DWORD;
+ pub(crate) const LOAD_LIBRARY_SAFE_CURRENT_DIRS: DWORD = DWORD;
+ }
+}
+#[cfg(any(not(docsrs), windows))]
+mod windows_imports {
+ extern crate winapi;
+ pub(super) use self::winapi::shared::minwindef::{WORD, DWORD, HMODULE, FARPROC};
+ pub(super) use self::winapi::shared::ntdef::WCHAR;
+ pub(super) use self::winapi::shared::winerror;
+ pub(super) use self::winapi::um::{errhandlingapi, libloaderapi};
+ pub(super) use std::os::windows::ffi::{OsStrExt, OsStringExt};
+ pub(super) const SEM_FAILCE: DWORD = 1;
+ pub(super) mod consts {
+ pub(crate) use super::winapi::um::libloaderapi::{
+ LOAD_IGNORE_CODE_AUTHZ_LEVEL,
+ LOAD_LIBRARY_AS_DATAFILE,
+ LOAD_LIBRARY_AS_DATAFILE_EXCLUSIVE,
+ LOAD_LIBRARY_AS_IMAGE_RESOURCE,
+ LOAD_LIBRARY_SEARCH_APPLICATION_DIR,
+ LOAD_LIBRARY_SEARCH_DEFAULT_DIRS,
+ LOAD_LIBRARY_SEARCH_DLL_LOAD_DIR,
+ LOAD_LIBRARY_SEARCH_SYSTEM32,
+ LOAD_LIBRARY_SEARCH_USER_DIRS,
+ LOAD_WITH_ALTERED_SEARCH_PATH,
+ LOAD_LIBRARY_REQUIRE_SIGNED_TARGET,
+ LOAD_LIBRARY_SAFE_CURRENT_DIRS,
+ };
+ }
+}
+
+use self::windows_imports::*;
+use util::{ensure_compatible_types, cstr_cow_from_bytes};
use std::ffi::{OsStr, OsString};
use std::{fmt, io, marker, mem, ptr};
-use std::os::windows::ffi::{OsStrExt, OsStringExt};
use std::sync::atomic::{AtomicBool, Ordering};
-/// A platform-specific equivalent of the cross-platform `Library`.
+/// A platform-specific counterpart of the cross-platform [`Library`](crate::Library).
pub struct Library(HMODULE);
unsafe impl Send for Library {}
@@ -31,20 +77,56 @@ unsafe impl Send for Library {}
unsafe impl Sync for Library {}
impl Library {
- /// Find and load a shared library (module).
+ /// Find and load a module.
+ ///
+ /// If the `filename` specifies a full path, the function only searches that path for the
+ /// module. Otherwise, if the `filename` specifies a relative path or a module name without a
+ /// path, the function uses a windows-specific search strategy to find the module; for more
+ /// information, see the [Remarks on MSDN][msdn].
///
- /// Corresponds to `LoadLibraryW(filename, reserved: NULL, flags: 0)` which is equivalent to `LoadLibraryW(filename)`
+ /// 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`.
+ ///
+ /// This is equivalent to [`Library::load_with_flags`]`(filename, 0)`.
+ ///
+ /// [msdn]: https://docs.microsoft.com/en-us/windows/win32/api/libloaderapi/nf-libloaderapi-loadlibraryw#remarks
#[inline]
pub fn new<P: AsRef<OsStr>>(filename: P) -> Result<Library, crate::Error> {
Library::load_with_flags(filename, 0)
}
- /// Find and load a shared library (module).
+ /// Load the `Library` representing the original program executable.
+ ///
+ /// Note that behaviour of `Library` loaded with this method is different from
+ /// Libraries loaded with [`os::unix::Library::this`]. For more information refer to [MSDN].
+ ///
+ /// Corresponds to `GetModuleHandleExW(0, NULL, _)`.
+ ///
+ /// [`os::unix::Library::this`]: crate::os::unix::Library::this
+ /// [MSDN]: https://docs.microsoft.com/en-us/windows/win32/api/libloaderapi/nf-libloaderapi-getmodulehandleexw
+ pub fn this() -> Result<Library, crate::Error> {
+ unsafe {
+ let mut handle: HMODULE = std::ptr::null_mut();
+ with_get_last_error(|source| crate::Error::GetModuleHandleExW { source }, || {
+ let result = libloaderapi::GetModuleHandleExW(0, std::ptr::null_mut(), &mut handle);
+ if result == 0 {
+ None
+ } else {
+ Some(Library(handle))
+ }
+ }).map_err(|e| e.unwrap_or(crate::Error::GetModuleHandleExWUnknown))
+ }
+ }
+
+ /// Find and load a module, additionally adjusting behaviour with flags.
+ ///
+ /// See [`Library::new`] for documentation on handling of the `filename` argument. See the
+ /// [flag table on MSDN][flags] for information on applicable values for the `flags` argument.
///
- /// Locations where library is searched for is platform specific and can’t be adjusted
- /// portably.
+ /// Corresponds to `LoadLibraryExW(filename, reserved: NULL, flags)`.
///
- /// Corresponds to `LoadLibraryW(filename, reserved: NULL, flags)`.
+ /// [flags]: https://docs.microsoft.com/en-us/windows/win32/api/libloaderapi/nf-libloaderapi-loadlibraryexw#parameters
pub fn load_with_flags<P: AsRef<OsStr>>(filename: P, flags: DWORD) -> Result<Library, crate::Error> {
let wide_filename: Vec<u16> = filename.as_ref().encode_wide().chain(Some(0)).collect();
let _guard = ErrorModeGuard::new();
@@ -52,7 +134,9 @@ impl Library {
let ret = with_get_last_error(|source| crate::Error::LoadLibraryW { 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 = unsafe {
+ libloaderapi::LoadLibraryExW(wide_filename.as_ptr(), std::ptr::null_mut(), flags)
+ };
if handle.is_null() {
None
} else {
@@ -72,7 +156,7 @@ impl Library {
/// Symbol is interpreted as-is; no mangling is done. This means that symbols like `x::y` are
/// most likely invalid.
///
- /// ## Unsafety
+ /// # Safety
///
/// This function does not validate the type `T`. It is up to the user of this function to
/// ensure that the loaded symbol is in fact a `T`. Using a value with a wrong type has no
@@ -95,7 +179,7 @@ impl Library {
/// Get a pointer to function or static variable by ordinal number.
///
- /// ## Unsafety
+ /// # Safety
///
/// Pointer to a value of arbitrary type is returned. Using a value with wrong type is
/// undefined.
@@ -124,7 +208,7 @@ impl Library {
/// Convert a raw handle to a `Library`.
///
- /// ## Unsafety
+ /// # Safety
///
/// The handle shall be a result of a successful call of `LoadLibraryW` or a
/// handle previously returned by the `Library::into_raw` call.
@@ -217,7 +301,7 @@ impl<T> ::std::ops::Deref for Symbol<T> {
fn deref(&self) -> &T {
unsafe {
// Additional reference level for a dereference on `deref` return value.
- mem::transmute(&self.pointer)
+ &*(&self.pointer as *const *mut _ as *const T)
}
}
}
@@ -228,13 +312,12 @@ impl<T> fmt::Debug for Symbol<T> {
}
}
-
static USE_ERRORMODE: AtomicBool = AtomicBool::new(false);
struct ErrorModeGuard(DWORD);
impl ErrorModeGuard {
+ #[allow(clippy::if_same_then_else)]
fn new() -> Option<ErrorModeGuard> {
- const SEM_FAILCE: DWORD = 1;
unsafe {
if !USE_ERRORMODE.load(Ordering::Acquire) {
let mut previous_mode = 0;
@@ -302,31 +385,114 @@ where F: FnOnce() -> Option<T> {
})
}
-#[cfg(test)]
-mod tests {
- use super::*;
+/// Do not check AppLocker rules or apply Software Restriction Policies for the DLL.
+///
+/// This action applies only to the DLL being loaded and not to its dependencies. This value is
+/// recommended for use in setup programs that must run extracted DLLs during installation.
+///
+/// See [flag documentation on MSDN](https://docs.microsoft.com/en-us/windows/win32/api/libloaderapi/nf-libloaderapi-loadlibraryexw#parameters).
+pub const LOAD_IGNORE_CODE_AUTHZ_LEVEL: DWORD = consts::LOAD_IGNORE_CODE_AUTHZ_LEVEL;
- #[test]
- fn works_getlasterror() {
- let lib = Library::new("kernel32.dll").unwrap();
- let gle: Symbol<unsafe extern "system" fn() -> DWORD> = unsafe {
- lib.get(b"GetLastError").unwrap()
- };
- unsafe {
- errhandlingapi::SetLastError(42);
- assert_eq!(errhandlingapi::GetLastError(), gle())
- }
- }
+/// Map the file into the calling process’ virtual address space as if it were a data file.
+///
+/// Nothing is done to execute or prepare to execute the mapped file. Therefore, you cannot call
+/// functions like [`Library::get`] with this DLL. Using this value causes writes to read-only
+/// memory to raise an access violation. Use this flag when you want to load a DLL only to extract
+/// messages or resources from it.
+///
+/// See [flag documentation on MSDN](https://docs.microsoft.com/en-us/windows/win32/api/libloaderapi/nf-libloaderapi-loadlibraryexw#parameters).
+pub const LOAD_LIBRARY_AS_DATAFILE: DWORD = consts::LOAD_LIBRARY_AS_DATAFILE;
- #[test]
- fn works_getlasterror0() {
- let lib = Library::new("kernel32.dll").unwrap();
- let gle: Symbol<unsafe extern "system" fn() -> DWORD> = unsafe {
- lib.get(b"GetLastError\0").unwrap()
- };
- unsafe {
- errhandlingapi::SetLastError(42);
- assert_eq!(errhandlingapi::GetLastError(), gle())
- }
- }
-}
+/// Map the file into the calling process’ virtual address space as if it were a data file.
+///
+/// Similar to [`LOAD_LIBRARY_AS_DATAFILE`], except that the DLL file is opened with exclusive
+/// write access for the calling process. Other processes cannot open the DLL file for write access
+/// while it is in use. However, the DLL can still be opened by other processes.
+///
+/// See [flag documentation on MSDN](https://docs.microsoft.com/en-us/windows/win32/api/libloaderapi/nf-libloaderapi-loadlibraryexw#parameters).
+pub const LOAD_LIBRARY_AS_DATAFILE_EXCLUSIVE: DWORD = consts::LOAD_LIBRARY_AS_DATAFILE_EXCLUSIVE;
+
+/// Map the file into the process’ virtual address space as an image file.
+///
+/// The loader does not load the static imports or perform the other usual initialization steps.
+/// Use this flag when you want to load a DLL only to extract messages or resources from it.
+///
+/// Unless the application depends on the file having the in-memory layout of an image, this value
+/// should be used with either [`LOAD_LIBRARY_AS_DATAFILE_EXCLUSIVE`] or
+/// [`LOAD_LIBRARY_AS_DATAFILE`].
+///
+/// See [flag documentation on MSDN](https://docs.microsoft.com/en-us/windows/win32/api/libloaderapi/nf-libloaderapi-loadlibraryexw#parameters).
+pub const LOAD_LIBRARY_AS_IMAGE_RESOURCE: DWORD = consts::LOAD_LIBRARY_AS_IMAGE_RESOURCE;
+
+/// Search application's installation directory for the DLL and its dependencies.
+///
+/// Directories in the standard search path are not searched. This value cannot be combined with
+/// [`LOAD_WITH_ALTERED_SEARCH_PATH`].
+///
+/// See [flag documentation on MSDN](https://docs.microsoft.com/en-us/windows/win32/api/libloaderapi/nf-libloaderapi-loadlibraryexw#parameters).
+pub const LOAD_LIBRARY_SEARCH_APPLICATION_DIR: DWORD = consts::LOAD_LIBRARY_SEARCH_APPLICATION_DIR;
+
+/// Search default directories when looking for the DLL and its dependencies.
+///
+/// This value is a combination of [`LOAD_LIBRARY_SEARCH_APPLICATION_DIR`],
+/// [`LOAD_LIBRARY_SEARCH_SYSTEM32`], and [`LOAD_LIBRARY_SEARCH_USER_DIRS`]. Directories in the
+/// standard search path are not searched. This value cannot be combined with
+/// [`LOAD_WITH_ALTERED_SEARCH_PATH`].
+///
+/// See [flag documentation on MSDN](https://docs.microsoft.com/en-us/windows/win32/api/libloaderapi/nf-libloaderapi-loadlibraryexw#parameters).
+pub const LOAD_LIBRARY_SEARCH_DEFAULT_DIRS: DWORD = consts::LOAD_LIBRARY_SEARCH_DEFAULT_DIRS;
+
+/// Directory that contains the DLL is temporarily added to the beginning of the list of
+/// directories that are searched for the DLL’s dependencies.
+///
+/// Directories in the standard search path are not searched.
+///
+/// The `filename` parameter must specify a fully qualified path. This value cannot be combined
+/// with [`LOAD_WITH_ALTERED_SEARCH_PATH`].
+///
+/// See [flag documentation on MSDN](https://docs.microsoft.com/en-us/windows/win32/api/libloaderapi/nf-libloaderapi-loadlibraryexw#parameters).
+pub const LOAD_LIBRARY_SEARCH_DLL_LOAD_DIR: DWORD = consts::LOAD_LIBRARY_SEARCH_DLL_LOAD_DIR;
+
+/// Search `%windows%\system32` for the DLL and its dependencies.
+///
+/// Directories in the standard search path are not searched. This value cannot be combined with
+/// [`LOAD_WITH_ALTERED_SEARCH_PATH`].
+///
+/// See [flag documentation on MSDN](https://docs.microsoft.com/en-us/windows/win32/api/libloaderapi/nf-libloaderapi-loadlibraryexw#parameters).
+pub const LOAD_LIBRARY_SEARCH_SYSTEM32: DWORD = consts::LOAD_LIBRARY_SEARCH_SYSTEM32;
+
+/// Directories added using the `AddDllDirectory` or the `SetDllDirectory` function are searched
+/// for the DLL and its dependencies.
+///
+/// If more than one directory has been added, the order in which the directories are searched is
+/// unspecified. Directories in the standard search path are not searched. This value cannot be
+/// combined with [`LOAD_WITH_ALTERED_SEARCH_PATH`].
+///
+/// See [flag documentation on MSDN](https://docs.microsoft.com/en-us/windows/win32/api/libloaderapi/nf-libloaderapi-loadlibraryexw#parameters).
+pub const LOAD_LIBRARY_SEARCH_USER_DIRS: DWORD = consts::LOAD_LIBRARY_SEARCH_USER_DIRS;
+
+/// If `filename specifies an absolute path, the system uses the alternate file search strategy
+/// discussed in the [Remarks section] to find associated executable modules that the specified
+/// module causes to be loaded.
+///
+/// If this value is used and `filename` specifies a relative path, the behavior is undefined.
+///
+/// If this value is not used, or if `filename` does not specify a path, the system uses the
+/// standard search strategy discussed in the [Remarks section] to find associated executable
+/// modules that the specified module causes to be loaded.
+///
+/// See [flag documentation on MSDN](https://docs.microsoft.com/en-us/windows/win32/api/libloaderapi/nf-libloaderapi-loadlibraryexw#parameters).
+///
+/// [Remarks]: https://docs.microsoft.com/en-us/windows/win32/api/libloaderapi/nf-libloaderapi-loadlibraryexw#remarks
+pub const LOAD_WITH_ALTERED_SEARCH_PATH: DWORD = consts::LOAD_WITH_ALTERED_SEARCH_PATH;
+
+/// Specifies that the digital signature of the binary image must be checked at load time.
+///
+/// See [flag documentation on MSDN](https://docs.microsoft.com/en-us/windows/win32/api/libloaderapi/nf-libloaderapi-loadlibraryexw#parameters).
+pub const LOAD_LIBRARY_REQUIRE_SIGNED_TARGET: DWORD = consts::LOAD_LIBRARY_REQUIRE_SIGNED_TARGET;
+
+/// Allow loading a DLL for execution from the current directory only if it is under a directory in
+/// the Safe load list.
+///
+/// See [flag documentation on MSDN](https://docs.microsoft.com/en-us/windows/win32/api/libloaderapi/nf-libloaderapi-loadlibraryexw#parameters).
+pub const LOAD_LIBRARY_SAFE_CURRENT_DIRS: DWORD = consts::LOAD_LIBRARY_SAFE_CURRENT_DIRS;
diff --git a/src/test_helpers.rs b/src/test_helpers.rs
index 32f7023..9e3e992 100644
--- a/src/test_helpers.rs
+++ b/src/test_helpers.rs
@@ -1,7 +1,6 @@
//! This is a separate file containing helpers for tests of this library. It is built into a
//! dynamic library by the build.rs script.
-#![crate_type="dylib"] // FIXME: should become a cdylib in due time
-#![cfg_attr(test_nightly, feature(thread_local))]
+#![crate_type="cdylib"]
#[no_mangle]
pub static mut TEST_STATIC_U32: u32 = 0;
@@ -9,11 +8,6 @@ pub static mut TEST_STATIC_U32: u32 = 0;
#[no_mangle]
pub static mut TEST_STATIC_PTR: *mut () = 0 as *mut _;
-#[cfg(test_nightly)]
-#[thread_local]
-#[no_mangle]
-pub static mut TEST_THREAD_LOCAL: u32 = 0;
-
#[no_mangle]
pub extern "C" fn test_identity_u32(x: u32) -> u32 {
x
@@ -41,9 +35,3 @@ pub unsafe extern "C" fn test_get_static_u32() -> u32 {
pub unsafe extern "C" fn test_check_static_ptr() -> bool {
TEST_STATIC_PTR == (&mut TEST_STATIC_PTR as *mut *mut _ as *mut _)
}
-
-#[cfg(test_nightly)]
-#[no_mangle]
-pub unsafe extern "C" fn test_get_thread_local() -> u32 {
- TEST_THREAD_LOCAL
-}
diff --git a/src/util.rs b/src/util.rs
index e5108c2..880b689 100644
--- a/src/util.rs
+++ b/src/util.rs
@@ -7,7 +7,7 @@ use crate::Error;
/// Checks for 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<'a>(slice: &'a [u8]) -> Result<Cow<'a, CStr>, Error> {
+pub(crate) fn cstr_cow_from_bytes(slice: &[u8]) -> Result<Cow<'_, CStr>, Error> {
static ZERO: raw::c_char = 0;
Ok(match slice.last() {
// Slice out of 0 elements
diff --git a/tests/constants.rs b/tests/constants.rs
new file mode 100644
index 0000000..ad910c4
--- /dev/null
+++ b/tests/constants.rs
@@ -0,0 +1,13 @@
+extern crate libloading;
+extern crate libc;
+extern crate static_assertions;
+
+#[cfg(all(test, unix))]
+mod unix {
+ use super::static_assertions::const_assert_eq;
+
+ const_assert_eq!(libloading::os::unix::RTLD_LOCAL, libc::RTLD_LOCAL);
+ const_assert_eq!(libloading::os::unix::RTLD_GLOBAL, libc::RTLD_GLOBAL);
+ const_assert_eq!(libloading::os::unix::RTLD_NOW, libc::RTLD_NOW);
+ const_assert_eq!(libloading::os::unix::RTLD_LAZY, libc::RTLD_LAZY);
+}
diff --git a/tests/functions.rs b/tests/functions.rs
index dece58c..b642478 100644
--- a/tests/functions.rs
+++ b/tests/functions.rs
@@ -1,19 +1,25 @@
+#[cfg(windows)]
+extern crate winapi;
+
extern crate libloading;
use libloading::{Symbol, Library};
-const LIBPATH: &'static str = concat!(env!("OUT_DIR"), "/libtest_helpers.dll");
+const LIBPATH: &'static str = concat!(env!("OUT_DIR"), "/libtest_helpers.module");
fn make_helpers() {
static ONCE: ::std::sync::Once = ::std::sync::Once::new();
ONCE.call_once(|| {
- let mut outpath = String::from(if let Some(od) = option_env!("OUT_DIR") { od } else { return });
let rustc = option_env!("RUSTC").unwrap_or_else(|| { "rustc".into() });
- outpath.push_str(&"/libtest_helpers.dll"); // extension for windows required, POSIX does not care.
- let _ = ::std::process::Command::new(rustc)
+ let mut cmd = ::std::process::Command::new(rustc);
+ cmd
.arg("src/test_helpers.rs")
.arg("-o")
- .arg(outpath)
- .arg("-O")
+ .arg(LIBPATH)
+ .arg("--target")
+ .arg(env!("LIBLOADING_TEST_TARGET"))
+ .arg("-O");
+
+ cmd
.output()
.expect("could not compile the test helpers!");
});
@@ -136,20 +142,69 @@ fn test_static_ptr() {
}
}
-#[cfg(any(windows, target_os="linux"))]
-#[cfg(test_nightly)]
+#[cfg(unix)]
#[test]
-fn test_tls_static() {
+fn library_this_get() {
+ use libloading::os::unix::Library;
make_helpers();
- let lib = Library::new(LIBPATH).unwrap();
+ let _lib = Library::new(LIBPATH).unwrap();
+ let this = Library::this();
+ // SAFE: functions are never called
+ unsafe {
+ // Library we loaded in `_lib` (should be RTLD_LOCAL).
+ // FIXME: inconsistent behaviour between macos and other posix systems
+ // assert!(this.get::<unsafe extern "C" fn()>(b"test_identity_u32").is_err());
+ // Something obscure from libc...
+ assert!(this.get::<unsafe extern "C" fn()>(b"freopen").is_ok());
+ }
+}
+
+#[cfg(windows)]
+#[test]
+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 {
+ // Library we loaded in `_lib`.
+ assert!(this.get::<unsafe extern "C" fn()>(b"test_identity_u32").is_err());
+ // Something "obscure" from kernel32...
+ assert!(this.get::<unsafe extern "C" fn()>(b"GetLastError").is_err());
+ }
+}
+
+#[cfg(windows)]
+#[test]
+fn works_getlasterror() {
+ use winapi::um::errhandlingapi;
+ use winapi::shared::minwindef::DWORD;
+ use libloading::os::windows::{Library, Symbol};
+
+ let lib = Library::new("kernel32.dll").unwrap();
+ let gle: Symbol<unsafe extern "system" fn() -> DWORD> = unsafe {
+ lib.get(b"GetLastError").unwrap()
+ };
+ unsafe {
+ errhandlingapi::SetLastError(42);
+ assert_eq!(errhandlingapi::GetLastError(), gle())
+ }
+}
+
+#[cfg(windows)]
+#[test]
+fn works_getlasterror0() {
+ use winapi::um::errhandlingapi;
+ use winapi::shared::minwindef::DWORD;
+ use libloading::os::windows::{Library, Symbol};
+
+ let lib = Library::new("kernel32.dll").unwrap();
+ let gle: Symbol<unsafe extern "system" fn() -> DWORD> = unsafe {
+ lib.get(b"GetLastError\0").unwrap()
+ };
unsafe {
- let var: Symbol<*mut u32> = lib.get(b"TEST_THREAD_LOCAL\0").unwrap();
- **var = 84;
- let help: Symbol<unsafe extern fn() -> u32> = lib.get(b"test_get_thread_local\0").unwrap();
- assert_eq!(84, help());
+ errhandlingapi::SetLastError(42);
+ assert_eq!(errhandlingapi::GetLastError(), gle())
}
- ::std::thread::spawn(move || unsafe {
- let help: Symbol<unsafe extern fn() -> u32> = lib.get(b"test_get_thread_local\0").unwrap();
- assert_eq!(0, help());
- }).join().unwrap();
}
diff --git a/tests/library_filename.rs b/tests/library_filename.rs
new file mode 100644
index 0000000..efe51b8
--- /dev/null
+++ b/tests/library_filename.rs
@@ -0,0 +1,17 @@
+extern crate libloading;
+use libloading::library_filename;
+use std::path::Path;
+
+#[cfg(target_os = "windows")]
+const EXPECTED: &str = "audioengine.dll";
+#[cfg(target_os = "linux")]
+const EXPECTED: &str = "libaudioengine.so";
+#[cfg(target_os = "macos")]
+const EXPECTED: &str = "libaudioengine.dylib";
+
+#[test]
+fn test_library_filename() {
+ let name = "audioengine";
+ let resolved = library_filename(name);
+ assert!(Path::new(&resolved).ends_with(EXPECTED));
+}
diff --git a/tests/markers.rs b/tests/markers.rs
index 01da108..330c034 100644
--- a/tests/markers.rs
+++ b/tests/markers.rs
@@ -4,6 +4,23 @@ extern crate libloading;
fn assert_send<T: Send>() {}
#[cfg(test)]
fn assert_sync<T: Sync>() {}
+#[cfg(test)]
+fn assert_display<T: std::fmt::Display>() {}
+
+#[test]
+fn check_error_send() {
+ assert_send::<libloading::Error>();
+}
+
+#[test]
+fn check_error_sync() {
+ assert_sync::<libloading::Error>();
+}
+
+#[test]
+fn check_error_display() {
+ assert_display::<libloading::Error>();
+}
#[test]
fn check_library_send() {