diff options
-rw-r--r-- | .cargo_vcs_info.json | 2 | ||||
-rw-r--r-- | .github/workflows/libloading.yml | 31 | ||||
-rw-r--r-- | Android.bp | 6 | ||||
-rw-r--r-- | Cargo.toml | 14 | ||||
-rw-r--r-- | Cargo.toml.orig | 24 | ||||
-rw-r--r-- | METADATA | 6 | ||||
-rw-r--r-- | README.mkd | 4 | ||||
-rw-r--r-- | build.rs | 6 | ||||
-rw-r--r-- | src/changelog.rs | 20 | ||||
-rw-r--r-- | src/error.rs | 78 | ||||
-rw-r--r-- | src/lib.rs | 84 | ||||
-rw-r--r-- | src/os/mod.rs | 34 | ||||
-rw-r--r-- | src/os/unix/consts.rs | 230 | ||||
-rw-r--r-- | src/os/unix/mod.rs | 120 | ||||
-rw-r--r-- | src/os/windows/mod.rs | 260 | ||||
-rw-r--r-- | src/test_helpers.rs | 14 | ||||
-rw-r--r-- | src/util.rs | 2 | ||||
-rw-r--r-- | tests/constants.rs | 13 | ||||
-rw-r--r-- | tests/functions.rs | 91 | ||||
-rw-r--r-- | tests/library_filename.rs | 17 | ||||
-rw-r--r-- | tests/markers.rs | 17 |
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 @@ -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 @@ -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"] @@ -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 } } @@ -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. @@ -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>(); - } -} @@ -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() { |