diff options
author | android-build-team Robot <android-build-team-robot@google.com> | 2021-02-21 00:05:58 +0000 |
---|---|---|
committer | android-build-team Robot <android-build-team-robot@google.com> | 2021-02-21 00:05:58 +0000 |
commit | 9228032aff3c7ce8c541fe3a105edad1e4aa44df (patch) | |
tree | 42b59de035f0989e3c3bd61b637525689abae76d | |
parent | 1fe86c648f43a7416013a48743526fa5ba38833b (diff) | |
parent | 8d8b90a05eb5faee6af98d059194e7f811abf2ca (diff) | |
download | libloading-android12-tests-release.tar.gz |
Snap for 7160059 from 8d8b90a05eb5faee6af98d059194e7f811abf2ca to sc-releaseandroid-vts-12.0_r9android-vts-12.0_r8android-vts-12.0_r7android-vts-12.0_r6android-vts-12.0_r5android-vts-12.0_r4android-vts-12.0_r3android-vts-12.0_r2android-vts-12.0_r12android-vts-12.0_r11android-vts-12.0_r10android-vts-12.0_r1android-security-12.0.0_r60android-security-12.0.0_r59android-security-12.0.0_r58android-security-12.0.0_r57android-security-12.0.0_r56android-security-12.0.0_r55android-security-12.0.0_r54android-security-12.0.0_r53android-security-12.0.0_r52android-security-12.0.0_r51android-security-12.0.0_r50android-security-12.0.0_r49android-security-12.0.0_r48android-security-12.0.0_r47android-security-12.0.0_r46android-security-12.0.0_r45android-security-12.0.0_r44android-security-12.0.0_r43android-security-12.0.0_r42android-security-12.0.0_r41android-security-12.0.0_r40android-security-12.0.0_r39android-security-12.0.0_r38android-security-12.0.0_r37android-security-12.0.0_r36android-security-12.0.0_r35android-security-12.0.0_r34android-security-11.0.0_r71android-platform-12.0.0_r9android-platform-12.0.0_r8android-platform-12.0.0_r7android-platform-12.0.0_r6android-platform-12.0.0_r5android-platform-12.0.0_r4android-platform-12.0.0_r31android-platform-12.0.0_r30android-platform-12.0.0_r3android-platform-12.0.0_r29android-platform-12.0.0_r28android-platform-12.0.0_r27android-platform-12.0.0_r26android-platform-12.0.0_r25android-platform-12.0.0_r24android-platform-12.0.0_r23android-platform-12.0.0_r22android-platform-12.0.0_r21android-platform-12.0.0_r20android-platform-12.0.0_r2android-platform-12.0.0_r19android-platform-12.0.0_r18android-platform-12.0.0_r17android-platform-12.0.0_r16android-platform-12.0.0_r15android-platform-12.0.0_r14android-platform-12.0.0_r13android-platform-12.0.0_r12android-platform-12.0.0_r11android-platform-12.0.0_r10android-platform-12.0.0_r1android-cts-12.0_r9android-cts-12.0_r8android-cts-12.0_r7android-cts-12.0_r6android-cts-12.0_r5android-cts-12.0_r4android-cts-12.0_r3android-cts-12.0_r2android-cts-12.0_r12android-cts-12.0_r11android-cts-12.0_r10android-cts-12.0_r1android-12.0.0_r9android-12.0.0_r8android-12.0.0_r34android-12.0.0_r33android-12.0.0_r31android-12.0.0_r30android-12.0.0_r3android-12.0.0_r25android-12.0.0_r2android-12.0.0_r11android-12.0.0_r10android-12.0.0_r1android12-tests-releaseandroid12-security-releaseandroid12-s5-releaseandroid12-s4-releaseandroid12-s3-releaseandroid12-s2-releaseandroid12-s1-releaseandroid12-releaseandroid12-platform-release
Change-Id: Id45180391a44ec26e1729eff464d369de1671222
-rw-r--r-- | .cargo_vcs_info.json | 2 | ||||
-rw-r--r-- | Cargo.toml | 4 | ||||
-rw-r--r-- | Cargo.toml.orig | 3 | ||||
-rw-r--r-- | METADATA | 8 | ||||
-rw-r--r-- | TEST_MAPPING | 6 | ||||
-rw-r--r-- | src/changelog.rs | 105 | ||||
-rw-r--r-- | src/error.rs | 10 | ||||
-rw-r--r-- | src/lib.rs | 126 | ||||
-rw-r--r-- | src/os/mod.rs | 2 | ||||
-rw-r--r-- | src/os/unix/mod.rs | 90 | ||||
-rw-r--r-- | src/os/windows/mod.rs | 111 | ||||
-rw-r--r-- | tests/functions.rs | 57 | ||||
-rw-r--r-- | tests/windows.rs | 16 |
13 files changed, 328 insertions, 212 deletions
diff --git a/.cargo_vcs_info.json b/.cargo_vcs_info.json index 2bb276f..69cdb18 100644 --- a/.cargo_vcs_info.json +++ b/.cargo_vcs_info.json @@ -1,5 +1,5 @@ { "git": { - "sha1": "1cc40888d23607a066164238694f0ccab684555e" + "sha1": "7fbd3a34336f7839bdd6d6aaf83a9a763c4fa1e5" } } @@ -12,7 +12,7 @@ [package] name = "libloading" -version = "0.6.7" +version = "0.7.0" authors = ["Simonas Kazlauskas <libloading@kazlauskas.me>"] description = "A safer binding to platform’s dynamic library loading utilities" documentation = "https://docs.rs/libloading/" @@ -33,4 +33,4 @@ version = "1.1" version = "1" [target."cfg(windows)".dependencies.winapi] version = "0.3" -features = ["winerror", "errhandlingapi", "libloaderapi"] +features = ["errhandlingapi", "libloaderapi"] diff --git a/Cargo.toml.orig b/Cargo.toml.orig index 5b687a0..d946299 100644 --- a/Cargo.toml.orig +++ b/Cargo.toml.orig @@ -3,7 +3,7 @@ name = "libloading" # When bumping # * Don’t forget to add an entry to `src/changelog.rs` # * If bumping a incompatible version, adjust documentation in `src/lib.rs` -version = "0.6.7" +version = "0.7.0" authors = ["Simonas Kazlauskas <libloading@kazlauskas.me>"] license = "ISC" repository = "https://github.com/nagisa/rust_libloading/" @@ -16,7 +16,6 @@ categories = ["api-bindings"] [target.'cfg(windows)'.dependencies.winapi] version = "0.3" features = [ - "winerror", "errhandlingapi", "libloaderapi", ] @@ -7,13 +7,13 @@ third_party { } url { type: ARCHIVE - value: "https://static.crates.io/crates/libloading/libloading-0.6.7.crate" + value: "https://static.crates.io/crates/libloading/libloading-0.7.0.crate" } - version: "0.6.7" + version: "0.7.0" license_type: NOTICE last_upgrade_date { year: 2021 - month: 1 - day: 14 + month: 2 + day: 9 } } diff --git a/TEST_MAPPING b/TEST_MAPPING index 03d7e78..58584f2 100644 --- a/TEST_MAPPING +++ b/TEST_MAPPING @@ -1,11 +1,11 @@ -// Generated by cargo2android.py for tests that depend on this crate. +// Generated by update_crate_tests.py for tests that depend on this crate. { "presubmit": [ { - "name": "libsqlite3-sys_device_test_src_lib" + "name": "keystore2_test" }, { - "name": "keystore2_test" + "name": "libsqlite3-sys_device_test_src_lib" } ] } diff --git a/src/changelog.rs b/src/changelog.rs index 67f9cb0..c927551 100644 --- a/src/changelog.rs +++ b/src/changelog.rs @@ -1,7 +1,106 @@ -//! Project changelog +//! The change log. -// TODO: for the next breaking release rename `Error::LoadLibraryW` to `Error::LoadLibraryExW`. -// TODO: for the next breaking release use `RTLD_LAZY | RTLD_LOCAL` by default on unix. +/// Release 0.7.0 (2021-01-31) +/// +/// ## Breaking changes +/// +/// ### Loading functions are now `unsafe` +/// +/// A number of associated methods involved in loading a library were changed to +/// be `unsafe`. The affected functions are: [`Library::new`], [`os::unix::Library::new`], +/// [`os::unix::Library::open`], [`os::windows::Library::new`], +/// [`os::windows::Library::load_with_flags`]. This is the most prominent breaking change in this +/// release and affects majority of the users of `libloading`. +/// +/// In order to see why it was necessary, consider the following snippet of C++ code: +/// +/// ```c++ +/// #include <vector> +/// #include <iostream> +/// +/// static std::vector<unsigned int> UNSHUU = { 1, 2, 3 }; +/// +/// int main() { +/// std::cout << UNSHUU[0] << UNSHUU[1] << UNSHUU[2] << std::endl; // Prints 123 +/// return 0; +/// } +/// ``` +/// +/// The `std::vector` type, much like in Rust's `Vec`, stores its contents in a buffer allocated on +/// the heap. In this example the vector object itself is stored and initialized as a static +/// variable – a compile time construct. The heap, on the other hand, is a runtime construct. And +/// yet the code works exactly as you'd expect – the vector contains numbers 1, 2 and 3 stored in +/// a buffer on heap. So, _what_ makes it work out, exactly? +/// +/// Various executable and shared library formats define conventions and machinery to execute +/// arbitrary code when a program or a shared library is loaded. On systems using the PE format +/// (e.g. Windows) this is available via the optional `DllMain` initializer. Various systems +/// utilizing the ELF format take a sightly different approach of maintaining an array of function +/// pointers in the `.init_array` section. A very similar mechanism exists on systems that utilize +/// the Mach-O format. +/// +/// For the C++ program above, the object stored in the `UNSHUU` global variable is constructed +/// by code run as part of such an initializer routine. This initializer is run before the entry +/// point (the `main` function) is executed, allowing for this magical behaviour to be possible. +/// Were the C++ code built as a shared library instead, the initialization routines would run as +/// the resulting shared library is loaded. In case of `libloading` – during the call to +/// `Library::new` and other methods affected by this change. +/// +/// These initialization (and very closely related termination) routines can be utilized outside of +/// C++ too. Anybody can build a shared library in variety of different programming languages and +/// set up the initializers to execute arbitrary code. Potentially code that does all sorts of +/// wildly unsound stuff. +/// +/// The routines are executed by components that are an integral part of the operating system. +/// Changing or controlling the operation of these components is infeasible. With that in +/// mind, the initializer and termination routines are something anybody loading a library must +/// carefully evaluate the libraries loaded for soundness. +/// +/// In practice, a vast majority of the libraries can be considered a good citizen and their +/// initialization and termination routines, if they have any at all, can be trusted to be sound. +/// +/// Also see: [issue #86]. +/// +/// ### Better & more consistent default behaviour on UNIX systems +/// +/// On UNIX systems the [`Library::new`], [`os::unix::Library::new`] and +/// [`os::unix::Library::this`] methods have been changed to use +/// <code>[RTLD_LAZY] | [RTLD_LOCAL]</code> as the default set of loader options (previously: +/// [`RTLD_NOW`]). This has a couple benefits. Namely: +/// +/// * Lazy binding is generally quicker to execute when only a subset of symbols from a library are +/// used and is typically the default when neither `RTLD_LAZY` nor `RTLD_NOW` are specified when +/// calling the underlying `dlopen` API; +/// * On most UNIX systems (macOS being a notable exception) `RTLD_LOCAL` is the default when +/// neither `RTLD_LOCAL` nor [`RTLD_GLOBAL`] are specified. The explicit setting of the +/// `RTLD_LOCAL` flag makes this behaviour consistent across platforms. +/// +/// ### Dropped support for Windows XP/Vista +/// +/// The (broken) support for Windows XP and Windows Vista environments was removed. This was +/// prompted primarily by a similar policy change in the [Rust +/// project](https://github.com/rust-lang/compiler-team/issues/378) but also as an acknowledgement +/// to the fact that `libloading` never worked in these environments anyway. +/// +/// ### More accurate error variant names +/// +/// Finally, the `Error::LoadLibraryW` renamed to [`Error::LoadLibraryExW`] to more accurately +/// represent the underlying API that's failing. No functional changes as part of this rename +/// intended. +/// +/// [issue #86]: https://github.com/nagisa/rust_libloading/issues/86 +/// [`Library::new`]: crate::Library::new +/// [`Error::LoadLibraryExW`]: crate::Error::LoadLibraryExW +/// [`os::unix::Library::this`]: crate::os::unix::Library::this +/// [`os::unix::Library::new`]: crate::os::unix::Library::new +/// [`os::unix::Library::open`]: crate::os::unix::Library::new +/// [`os::windows::Library::new`]: crate::os::windows::Library::new +/// [`os::windows::Library::load_with_flags`]: crate::os::windows::Library::load_with_flags +/// [`RTLD_NOW`]: crate::os::unix::RTLD_NOW +/// [RTLD_LAZY]: crate::os::unix::RTLD_LAZY +/// [RTLD_LOCAL]: crate::os::unix::RTLD_LOCAL +/// [`RTLD_GLOBAL`]: crate::os::unix::RTLD_GLOBAL +pub mod r0_7_0 {} /// Release 0.6.7 (2021-01-14) /// diff --git a/src/error.rs b/src/error.rs index e1c465e..bd70ec3 100644 --- a/src/error.rs +++ b/src/error.rs @@ -44,12 +44,12 @@ pub enum Error { /// The `dlclose` call failed and system did not report an error. DlCloseUnknown, /// The `LoadLibraryW` call failed. - LoadLibraryW { + LoadLibraryExW { /// The source error. source: WindowsError }, /// The `LoadLibraryW` call failed and system did not report an error. - LoadLibraryWUnknown, + LoadLibraryExWUnknown, /// The `GetModuleHandleExW` call failed. GetModuleHandleExW { /// The source error. @@ -91,7 +91,7 @@ impl std::error::Error for Error { match *self { CreateCString { ref source } => Some(source), CreateCStringWithTrailing { ref source } => Some(source), - LoadLibraryW { ref source } => Some(&source.0), + LoadLibraryExW { ref source } => Some(&source.0), GetProcAddress { ref source } => Some(&source.0), FreeLibrary { ref source } => Some(&source.0), _ => None, @@ -109,8 +109,8 @@ 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, "LoadLibraryExW failed"), - LoadLibraryWUnknown => + LoadLibraryExW { .. } => write!(f, "LoadLibraryExW failed"), + LoadLibraryExWUnknown => write!(f, "LoadLibraryExW failed, but system did not report the error"), GetModuleHandleExW { .. } => write!(f, "GetModuleHandleExW failed"), GetModuleHandleExWUnknown => @@ -1,15 +1,14 @@ //! A memory-safer wrapper around system dynamic library loading primitives. //! //! Using this library allows loading [dynamic libraries](struct.Library.html) (also known as -//! shared libraries) as well as use functions and static variables these libraries contain. +//! shared libraries) and use functions & global variables contained within the libraries. //! -//! While the library does expose a cross-platform interface to load a library and find stuff -//! inside it, little is done to paper over the platform differences, especially where library -//! loading is involved. The documentation for each function will attempt to document such -//! differences on the best-effort basis. +//! `libloading` crate exposes a cross-platform interface to load a library and utilize its +//! contents, but little is done to paper over the differences in behaviour between different +//! platforms. The API documentation strives to document such differences on the best-effort basis. //! -//! Less safe, platform specific bindings are also available. See the -//! [`os::platform`](os/index.html) module for details. +//! Platform specific APIs are also available in the [`os`](crate::os) module. These APIs are more +//! flexible but less safe. //! //! # Usage //! @@ -17,25 +16,23 @@ //! //! ```toml //! [dependencies] -//! libloading = "0.6" +//! libloading = "0.7" //! ``` //! -//! Then inside your project +//! Then inside your code: //! //! ```no_run -//! extern crate libloading as lib; -//! //! fn call_dynamic() -> Result<u32, Box<dyn std::error::Error>> { -//! let lib = lib::Library::new("/path/to/liblibrary.so")?; //! unsafe { -//! let func: lib::Symbol<unsafe extern fn() -> u32> = lib.get(b"my_func")?; +//! let lib = libloading::Library::new("/path/to/liblibrary.so")?; +//! let func: libloading::Symbol<unsafe extern fn() -> u32> = lib.get(b"my_func")?; //! Ok(func()) //! } //! } //! ``` //! //! The compiler will ensure that the loaded `function` will not outlive the `Library` it comes -//! from, preventing a common cause of undefined behaviour and memory safety problems. +//! from, preventing a common class of issues. #![deny( missing_docs, clippy::all, @@ -74,20 +71,26 @@ impl Library { /// * Absolute path to the library; /// * Relative (to the current working directory) path to the library. /// - /// # Thread-safety + /// # 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. + /// When a library is loaded initialization routines contained within the library are executed. + /// For the purposes of safety, execution of these routines is conceptually the same calling an + /// unknown foreign function and may impose arbitrary requirements on the caller for the call + /// to be sound. /// - /// * On Windows Vista and earlier error handling falls back to [`SetErrorMode`], which is not - /// MT-safe. MT-scenarios involving this function may cause a traditional data race; - /// * On some UNIX targets `dlerror` might not be MT-safe, resulting in garbage error messages - /// in certain MT-scenarios. + /// Additionally, the callers of this function must also ensure that execution of the + /// termination routines contained within the library is safe as well. These routines may be + /// executed when the library is unloaded. /// - /// [`SetErrorMode`]: https://msdn.microsoft.com/en-us/library/windows/desktop/ms680621(v=vs.85).aspx + /// # Thread-safety + /// + /// The implementation strives to be as MT-safe as sanely possible, however on certain + /// platforms the underlying error-handling related APIs not always MT-safe. This library + /// shares these limitations on those platforms. In particular, on certain UNIX targets + /// `dlerror` is not MT-safe, resulting in garbage error messages in certain MT-scenarios. /// - /// Calling this function from multiple threads is not safe if used in conjunction with - /// relative filenames and the library search path is modified (`SetDllDirectory` function on + /// Calling this function from multiple threads is not MT-safe if used in conjunction with + /// library filenames and the library search path is modified (`SetDllDirectory` function on /// Windows, `{DY,}LD_LIBRARY_PATH` environment variable on UNIX). /// /// # Platform-specific behaviour @@ -97,15 +100,8 @@ impl Library { /// the platform specific [`os::unix::Library::new`] and [`os::windows::Library::new`] methods /// for further information on library lookup behaviour. /// - /// ## Windows - /// /// If the `filename` specifies a library filename without path and with extension omitted, - /// `.dll` extension is implicitly added. This behaviour may be suppressed by appending a - /// trailing `.` to the `filename`. - /// - /// If the library contains thread local variables (MSVC’s `_declspec(thread)`, Rust’s - /// `#[thread_local]` attributes), loading the library will fail on versions prior to Windows - /// Vista. + /// `.dll` extension is implicitly added on Windows. /// /// # Tips /// @@ -123,36 +119,37 @@ impl Library { /// ```no_run /// # use ::libloading::Library; /// // Any of the following are valid. - /// let _ = Library::new("/path/to/awesome.module").unwrap(); - /// let _ = Library::new("../awesome.module").unwrap(); - /// let _ = Library::new("libsomelib.so.1").unwrap(); + /// unsafe { + /// let _ = Library::new("/path/to/awesome.module").unwrap(); + /// let _ = Library::new("../awesome.module").unwrap(); + /// let _ = Library::new("libsomelib.so.1").unwrap(); + /// } /// ``` - pub fn new<P: AsRef<OsStr>>(filename: P) -> Result<Library, Error> { + pub unsafe fn new<P: AsRef<OsStr>>(filename: P) -> Result<Library, Error> { imp::Library::new(filename).map(From::from) } /// Get a pointer to function or static variable by symbol name. /// - /// The `symbol` may not contain any null bytes, with an exception of last byte. A null - /// terminated `symbol` may avoid a string allocation in some cases. + /// The `symbol` may not contain any null bytes, with an exception of last byte. Providing a + /// null terminated `symbol` may help to avoid an allocation. /// /// Symbol is interpreted as-is; no mangling is done. This means that symbols like `x::y` are /// most likely invalid. /// /// # Safety /// - /// Pointer to a value of arbitrary type is returned. Using a value with wrong type is - /// undefined. + /// Users of this API must specify the correct type of the function or variable loaded. Using a + /// `Symbol` with a wrong type is undefined. /// /// # Platform-specific behaviour /// - /// Implementation of thread local variables is extremely platform specific and uses of these - /// variables that work on e.g. Linux may have unintended behaviour on other POSIX systems or - /// Windows. + /// Implementation of thread local variables is extremely platform specific and uses of such + /// variables that work on e.g. Linux may have unintended behaviour on other targets. /// /// On POSIX implementations where the `dlerror` function is not confirmed to be MT-safe (such - /// as FreeBSD), this function will unconditionally return an error the underlying `dlsym` call - /// returns a null pointer. There are rare situations where `dlsym` returns a genuine null + /// as FreeBSD), this function will unconditionally return an error when the underlying `dlsym` + /// call returns a null pointer. There are rare situations where `dlsym` returns a genuine null /// pointer without it being an error. If loading a null pointer is something you care about, /// consider using the [`os::unix::Library::get_singlethreaded`] call. /// @@ -162,14 +159,18 @@ impl Library { /// /// ```no_run /// # use ::libloading::Library; - /// let lib = Library::new("/path/to/awesome.module").unwrap(); + /// let lib = unsafe { + /// Library::new("/path/to/awesome.module").unwrap() + /// }; /// ``` /// /// Loading and using a function looks like this: /// /// ```no_run /// # use ::libloading::{Library, Symbol}; - /// # let lib = Library::new("/path/to/awesome.module").unwrap(); + /// # let lib = unsafe { + /// # Library::new("/path/to/awesome.module").unwrap() + /// # }; /// unsafe { /// let awesome_function: Symbol<unsafe extern fn(f64) -> f64> = /// lib.get(b"awesome_function\0").unwrap(); @@ -181,7 +182,7 @@ impl Library { /// /// ```no_run /// # use ::libloading::{Library, Symbol}; - /// # let lib = Library::new("/path/to/awesome.module").unwrap(); + /// # let lib = unsafe { Library::new("/path/to/awesome.module").unwrap() }; /// unsafe { /// let awesome_variable: Symbol<*mut f64> = lib.get(b"awesome_variable\0").unwrap(); /// **awesome_variable = 42.0; @@ -230,15 +231,12 @@ unsafe impl Sync for Library {} /// Symbol from a library. /// /// This type is a safeguard against using dynamically loaded symbols after a `Library` is -/// unloaded. Primary method to create an instance of a `Symbol` is via `Library::get`. -/// -/// Due to implementation of the `Deref` trait, an instance of `Symbol` may be used as if it was a -/// function or variable directly, without taking care to “extract” function or variable manually -/// most of the time. +/// unloaded. Primary method to create an instance of a `Symbol` is via [`Library::get`]. /// -/// See [`Library::get`] for details. +/// The `Deref` trait implementation allows use of `Symbol` as if it was a function or variable +/// itself, without taking care to “extract” function or variable manually most of the time. /// -/// [`Library::get`]: ./struct.Library.html#method.get +/// [`Library::get`]: Library::get pub struct Symbol<'lib, T: 'lib> { inner: imp::Symbol<T>, pd: marker::PhantomData<&'lib T> @@ -257,8 +255,8 @@ impl<'lib, T> Symbol<'lib, T> { /// /// ```no_run /// # use ::libloading::{Library, Symbol}; - /// let lib = Library::new("/path/to/awesome.module").unwrap(); /// unsafe { + /// let lib = Library::new("/path/to/awesome.module").unwrap(); /// let symbol: Symbol<*mut u32> = lib.get(b"symbol\0").unwrap(); /// let symbol = symbol.into_raw(); /// } @@ -270,25 +268,25 @@ impl<'lib, T> Symbol<'lib, T> { /// Wrap the `os::platform::Symbol` into this safe wrapper. /// /// Note that, in order to create association between the symbol and the library this symbol - /// came from, this function requires reference to the library provided. + /// came from, this function requires a reference to the library. /// /// # 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. + /// The `library` reference must be exactly the library `sym` was loaded from. /// /// # Examples /// /// ```no_run /// # use ::libloading::{Library, Symbol}; - /// let lib = Library::new("/path/to/awesome.module").unwrap(); /// unsafe { + /// let lib = Library::new("/path/to/awesome.module").unwrap(); /// let symbol: Symbol<*mut u32> = lib.get(b"symbol\0").unwrap(); /// let symbol = symbol.into_raw(); /// let symbol = Symbol::from_raw(symbol, &lib); /// } /// ``` - pub unsafe fn from_raw<L>(sym: imp::Symbol<T>, _: &'lib L) -> Symbol<'lib, T> { + pub unsafe fn from_raw<L>(sym: imp::Symbol<T>, library: &'lib L) -> Symbol<'lib, T> { + let _ = library; // ignore here for documentation purposes. Symbol { inner: sym, pd: marker::PhantomData @@ -303,8 +301,8 @@ impl<'lib, T> Symbol<'lib, Option<T>> { /// /// ```no_run /// # use ::libloading::{Library, Symbol}; - /// let lib = Library::new("/path/to/awesome.module").unwrap(); /// unsafe { + /// let lib = Library::new("/path/to/awesome.module").unwrap(); /// let symbol: Symbol<Option<*mut u32>> = lib.get(b"symbol\0").unwrap(); /// let symbol: Symbol<*mut u32> = symbol.lift_option().expect("static is not null"); /// } @@ -356,7 +354,9 @@ unsafe impl<'lib, T: Sync> Sync for Symbol<'lib, T> {} /// use libloading::{Library, library_filename}; /// // Will attempt to load `libLLVM.so` on Linux, `libLLVM.dylib` on macOS and `LLVM.dll` on /// // Windows. -/// let library = Library::new(library_filename("LLVM")); +/// let library = unsafe { +/// Library::new(library_filename("LLVM")) +/// }; /// ``` pub fn library_filename<S: AsRef<OsStr>>(name: S) -> OsString { let name = name.as_ref(); diff --git a/src/os/mod.rs b/src/os/mod.rs index 40361c5..c03ebc1 100644 --- a/src/os/mod.rs +++ b/src/os/mod.rs @@ -1,4 +1,4 @@ -//! Unsafe, platform specific bindings to dynamic library loading facilities. +//! Unsafe but flexible platform specific bindings to dynamic library loading facilities. //! //! These modules expose more extensive, powerful, less principled bindings to the dynamic //! library loading facilities. Use of these bindings come at the cost of less (in most cases, diff --git a/src/os/unix/mod.rs b/src/os/unix/mod.rs index 83657a9..45e8230 100644 --- a/src/os/unix/mod.rs +++ b/src/os/unix/mod.rs @@ -103,15 +103,26 @@ impl Library { /// a file. Otherwise, platform-specific algorithms are employed to find a library with a /// matching file name. /// - /// This is equivalent to [`Library::open`]`(filename, RTLD_NOW)`. + /// This is equivalent to <code>[Library::open](filename, [RTLD_LAZY] | [RTLD_LOCAL])</code>. /// /// [path separator]: std::path::MAIN_SEPARATOR + /// + /// # Safety + /// + /// When a library is loaded initialization routines contained within the library are executed. + /// For the purposes of safety, execution of these routines is conceptually the same calling an + /// unknown foreign function and may impose arbitrary requirements on the caller for the call + /// to be sound. + /// + /// Additionally, the callers of this function must also ensure that execution of the + /// termination routines contained within the library is safe as well. These routines may be + /// executed when the library is unloaded. #[inline] - pub fn new<P: AsRef<OsStr>>(filename: P) -> Result<Library, crate::Error> { - Library::open(Some(filename), RTLD_NOW) + pub unsafe fn new<P: AsRef<OsStr>>(filename: P) -> Result<Library, crate::Error> { + Library::open(Some(filename), RTLD_LAZY | RTLD_LOCAL) } - /// Eagerly load the `Library` representing the current executable. + /// Load the `Library` representing the current executable. /// /// [`Library::get`] calls of the returned `Library` will look for symbols in following /// locations in order: @@ -124,12 +135,16 @@ impl Library { /// Note that behaviour of `Library` loaded with this method is different from /// Libraries loaded with [`os::windows::Library::this`]. /// - /// This is equivalent to [`Library::open`]`(None, RTLD_NOW)`. + /// This is equivalent to <code>[Library::open](None, [RTLD_LAZY] | [RTLD_LOCAL])</code>. /// /// [`os::windows::Library::this`]: crate::os::windows::Library::this #[inline] pub fn this() -> Library { - Library::open(None::<&OsStr>, RTLD_NOW).expect("this should never fail") + unsafe { + // SAFE: this does not load any new shared library images, no danger in it executing + // initializer routines. + Library::open(None::<&OsStr>, RTLD_LAZY | RTLD_LOCAL).expect("this should never fail") + } } /// Find and load an executable object file (shared library). @@ -138,22 +153,30 @@ impl Library { /// 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> + /// + /// # Safety + /// + /// When a library is loaded initialization routines contained within the library are executed. + /// For the purposes of safety, execution of these routines is conceptually the same calling an + /// unknown foreign function and may impose arbitrary requirements on the caller for the call + /// to be sound. + /// + /// Additionally, the callers of this function must also ensure that execution of the + /// termination routines contained within the library is safe as well. These routines may be + /// executed when the library is unloaded. + pub unsafe fn open<P>(filename: Option<P>, flags: raw::c_int) -> Result<Library, crate::Error> where P: AsRef<OsStr> { let filename = match filename { None => None, Some(ref f) => Some(cstr_cow_from_bytes(f.as_ref().as_bytes())?), }; with_dlerror(|desc| crate::Error::DlOpen { desc }, move || { - let result = unsafe { - let r = dlopen(match filename { - None => ptr::null(), - Some(ref f) => f.as_ptr() - }, flags); - // ensure filename lives until dlopen completes - drop(filename); - r - }; + let result = dlopen(match filename { + None => ptr::null(), + Some(ref f) => f.as_ptr() + }, flags); + // ensure filename lives until dlopen completes + drop(filename); if result.is_null() { None } else { @@ -196,28 +219,27 @@ 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 an allocation in some cases. + /// The `symbol` may not contain any null bytes, with an exception of last byte. Providing a + /// null terminated `symbol` may help to avoid an allocation. /// /// Symbol is interpreted as-is; no mangling is done. This means that symbols like `x::y` are /// most likely invalid. /// /// # Safety /// - /// This function does not validate the type `T`. It is up to the user of this function to - /// ensure that the loaded symbol is in fact a `T`. Using a value with a wrong type has no - /// defined behaviour. + /// Users of this API must specify the correct type of the function or variable loaded. Using a + /// `Symbol` with a wrong type is undefined. /// /// # Platform-specific behaviour /// - /// Implementation of thread local variables is extremely platform specific and uses of these - /// variables that work on e.g. Linux may have unintended behaviour on other POSIX systems. + /// Implementation of thread local variables is extremely platform specific and uses of such + /// variables that work on e.g. Linux may have unintended behaviour on other targets. /// /// On POSIX implementations where the `dlerror` function is not confirmed to be MT-safe (such /// as FreeBSD), this function will unconditionally return an error when the underlying `dlsym` /// call returns a null pointer. There are rare situations where `dlsym` returns a genuine null - /// pointer without it being an error. If loading a symbol at null address is something you - /// care about, consider using the [`Library::get_singlethreaded`] call. + /// pointer without it being an error. If loading a null pointer is something you care about, + /// consider using the [`Library::get_singlethreaded`] call. #[inline(always)] pub unsafe fn get<T>(&self, symbol: &[u8]) -> Result<Symbol<T>, crate::Error> { extern crate cfg_if; @@ -243,17 +265,16 @@ 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. + /// The `symbol` may not contain any null bytes, with an exception of last byte. Providing a + /// null terminated `symbol` may help to avoid an allocation. /// /// Symbol is interpreted as-is; no mangling is done. This means that symbols like `x::y` are /// most likely invalid. /// /// # Safety /// - /// This function does not validate the type `T`. It is up to the user of this function to - /// ensure that the loaded symbol is in fact a `T`. Using a value with a wrong type has no - /// defined behaviour. + /// Users of this API must specify the correct type of the function or variable loaded. Using a + /// `Symbol` with a wrong type is undefined. /// /// It is up to the user of this library to ensure that no other calls to an MT-unsafe /// implementation of `dlerror` occur during execution of this function. Failing that, the @@ -261,8 +282,8 @@ impl Library { /// /// # Platform-specific behaviour /// - /// Implementation of thread local variables is extremely platform specific and uses of these - /// variables that work on e.g. Linux may have unintended behaviour on other POSIX systems. + /// Implementation of thread local variables is extremely platform specific and uses of such + /// variables that work on e.g. Linux may have unintended behaviour on other targets. #[inline(always)] pub unsafe fn get_singlethreaded<T>(&self, symbol: &[u8]) -> Result<Symbol<T>, crate::Error> { self.get_impl(symbol, || Ok(Symbol { @@ -300,7 +321,8 @@ 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 this will be done when `Library` is dropped. + /// library is unloaded. Otherwise the implementation of `Drop` for `Library` will close the + /// library and ignore the errors were they arise. /// /// The underlying data structures may still get leaked if an error does occur. pub fn close(self) -> Result<(), crate::Error> { @@ -312,7 +334,7 @@ impl Library { } }).map_err(|e| e.unwrap_or(crate::Error::DlCloseUnknown)); // While the library is not free'd yet in case of an error, there is no reason to try - // dropping it again, because all that will do is try calling `FreeLibrary` again. only + // dropping it again, because all that will do is try calling `dlclose` again. only // this time it would ignore the return result, which we already seen failing… std::mem::forget(self); result diff --git a/src/os/windows/mod.rs b/src/os/windows/mod.rs index f1298f6..7e28527 100644 --- a/src/os/windows/mod.rs +++ b/src/os/windows/mod.rs @@ -28,7 +28,6 @@ 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; @@ -55,7 +54,6 @@ 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::sync::atomic::{AtomicBool, Ordering}; /// A platform-specific counterpart of the cross-platform [`Library`](crate::Library). pub struct Library(HMODULE); @@ -88,15 +86,26 @@ impl Library { /// `.dll` extension is implicitly added. This behaviour may be suppressed by appending a /// trailing `.` to the `filename`. /// - /// This is equivalent to [`Library::load_with_flags`]`(filename, 0)`. + /// This is equivalent to <code>[Library::load_with_flags](filename, 0)</code>. /// /// [msdn]: https://docs.microsoft.com/en-us/windows/win32/api/libloaderapi/nf-libloaderapi-loadlibraryw#remarks + /// + /// # Safety + /// + /// When a library is loaded initialization routines contained within the library are executed. + /// For the purposes of safety, execution of these routines is conceptually the same calling an + /// unknown foreign function and may impose arbitrary requirements on the caller for the call + /// to be sound. + /// + /// Additionally, the callers of this function must also ensure that execution of the + /// termination routines contained within the library is safe as well. These routines may be + /// executed when the library is unloaded. #[inline] - pub fn new<P: AsRef<OsStr>>(filename: P) -> Result<Library, crate::Error> { + pub unsafe fn new<P: AsRef<OsStr>>(filename: P) -> Result<Library, crate::Error> { Library::load_with_flags(filename, 0) } - /// Load the `Library` representing the original program executable. + /// Get the `Library` representing the original program executable. /// /// Note that behaviour of `Library` loaded with this method is different from /// Libraries loaded with [`os::unix::Library::this`]. For more information refer to [MSDN]. @@ -119,7 +128,7 @@ impl Library { } } - /// Load a module that is already loaded by the program. + /// Get a module that is already loaded by the program. /// /// This function returns a `Library` corresponding to a module with the given name that is /// already mapped into the address space of the process. If the module isn't found an error is @@ -166,22 +175,32 @@ impl Library { /// Corresponds to `LoadLibraryExW(filename, reserved: NULL, flags)`. /// /// [flags]: https://docs.microsoft.com/en-us/windows/win32/api/libloaderapi/nf-libloaderapi-loadlibraryexw#parameters - pub fn load_with_flags<P: AsRef<OsStr>>(filename: P, flags: DWORD) -> Result<Library, crate::Error> { + /// + /// # Safety + /// + /// When a library is loaded initialization routines contained within the library are executed. + /// For the purposes of safety, execution of these routines is conceptually the same calling an + /// unknown foreign function and may impose arbitrary requirements on the caller for the call + /// to be sound. + /// + /// Additionally, the callers of this function must also ensure that execution of the + /// termination routines contained within the library is safe as well. These routines may be + /// executed when the library is unloaded. + pub unsafe 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(); - let ret = with_get_last_error(|source| crate::Error::LoadLibraryW { source }, || { + let ret = with_get_last_error(|source| crate::Error::LoadLibraryExW { source }, || { // Make sure no winapi calls as a result of drop happen inside this closure, because // otherwise that might change the return value of the GetLastError. - let handle = unsafe { - libloaderapi::LoadLibraryExW(wide_filename.as_ptr(), std::ptr::null_mut(), flags) - }; + let handle = + libloaderapi::LoadLibraryExW(wide_filename.as_ptr(), std::ptr::null_mut(), flags); if handle.is_null() { None } else { Some(Library(handle)) } - }).map_err(|e| e.unwrap_or(crate::Error::LoadLibraryWUnknown)); + }).map_err(|e| e.unwrap_or(crate::Error::LoadLibraryExWUnknown)); drop(wide_filename); // Drop wide_filename here to ensure it doesn’t get moved and dropped // inside the closure by mistake. See comment inside the closure. ret @@ -197,9 +216,8 @@ impl Library { /// /// # Safety /// - /// This function does not validate the type `T`. It is up to the user of this function to - /// ensure that the loaded symbol is in fact a `T`. Using a value with a wrong type has no - /// definied behaviour. + /// Users of this API must specify the correct type of the function or variable loaded. Using a + /// `Symbol` with a wrong type is undefined. pub unsafe fn get<T>(&self, symbol: &[u8]) -> Result<Symbol<T>, crate::Error> { ensure_compatible_types::<T, FARPROC>()?; let symbol = cstr_cow_from_bytes(symbol)?; @@ -220,8 +238,8 @@ impl Library { /// /// # Safety /// - /// Pointer to a value of arbitrary type is returned. Using a value with wrong type is - /// undefined. + /// Users of this API must specify the correct type of the function or variable loaded. Using a + /// `Symbol` with a wrong type is undefined. pub unsafe fn get_ordinal<T>(&self, ordinal: WORD) -> Result<Symbol<T>, crate::Error> { ensure_compatible_types::<T, FARPROC>()?; with_get_last_error(|source| crate::Error::GetProcAddress { source }, || { @@ -249,8 +267,9 @@ impl Library { /// /// # Safety /// - /// The handle shall be a result of a successful call of `LoadLibraryW` or a - /// handle previously returned by the `Library::into_raw` call. + /// The handle shall be a result of a successful call of `LoadLibraryA`, `LoadLibraryW`, + /// `LoadLibraryExW`, `LoadLibraryExA` or a handle previously returned by the + /// `Library::into_raw` call. pub unsafe fn from_raw(handle: HMODULE) -> Library { Library(handle) } @@ -361,49 +380,23 @@ 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> { unsafe { - if !USE_ERRORMODE.load(Ordering::Acquire) { - let mut previous_mode = 0; - let success = errhandlingapi::SetThreadErrorMode(SEM_FAILCE, &mut previous_mode) != 0; - if !success && errhandlingapi::GetLastError() == winerror::ERROR_CALL_NOT_IMPLEMENTED { - USE_ERRORMODE.store(true, Ordering::Release); - } else if !success { - // SetThreadErrorMode failed with some other error? How in the world is it - // possible for what is essentially a simple variable swap to fail? - // For now we just ignore the error -- the worst that can happen here is - // the previous mode staying on and user seeing a dialog error on older Windows - // machines. - return None; - } else if previous_mode == SEM_FAILCE { - return None; - } else { - return Some(ErrorModeGuard(previous_mode)); - } - } - match errhandlingapi::SetErrorMode(SEM_FAILCE) { - SEM_FAILCE => { - // This is important to reduce racy-ness when this library is used on multiple - // threads. In particular this helps with following race condition: - // - // T1: SetErrorMode(SEM_FAILCE) - // T2: SetErrorMode(SEM_FAILCE) - // T1: SetErrorMode(old_mode) # not SEM_FAILCE - // T2: SetErrorMode(SEM_FAILCE) # restores to SEM_FAILCE on drop - // - // This is still somewhat racy in a sense that T1 might restore the error - // mode before T2 finishes loading the library, but that is less of a - // concern – it will only end up in end user seeing a dialog. - // - // Also, SetErrorMode itself is probably not an atomic operation. - None - } - a => Some(ErrorModeGuard(a)) + let mut previous_mode = 0; + if errhandlingapi::SetThreadErrorMode(SEM_FAILCE, &mut previous_mode) == 0 { + // How in the world is it possible for what is essentially a simple variable swap + // to fail? For now we just ignore the error -- the worst that can happen here is + // the previous mode staying on and user seeing a dialog error on older Windows + // machines. + None + } else if previous_mode == SEM_FAILCE { + None + } else { + Some(ErrorModeGuard(previous_mode)) } } } @@ -412,11 +405,7 @@ impl ErrorModeGuard { impl Drop for ErrorModeGuard { fn drop(&mut self) { unsafe { - if !USE_ERRORMODE.load(Ordering::Relaxed) { - errhandlingapi::SetThreadErrorMode(self.0, ptr::null_mut()); - } else { - errhandlingapi::SetErrorMode(self.0); - } + errhandlingapi::SetThreadErrorMode(self.0, ptr::null_mut()); } } } diff --git a/tests/functions.rs b/tests/functions.rs index 35045e8..eef41e6 100644 --- a/tests/functions.rs +++ b/tests/functions.rs @@ -31,8 +31,8 @@ fn make_helpers() { #[test] fn test_id_u32() { make_helpers(); - let lib = Library::new(LIBPATH).unwrap(); unsafe { + let lib = Library::new(LIBPATH).unwrap(); let f: Symbol<unsafe extern fn(u32) -> u32> = lib.get(b"test_identity_u32\0").unwrap(); assert_eq!(42, f(42)); } @@ -50,8 +50,8 @@ struct S { #[test] fn test_id_struct() { make_helpers(); - let lib = Library::new(LIBPATH).unwrap(); unsafe { + let lib = Library::new(LIBPATH).unwrap(); let f: Symbol<unsafe extern fn(S) -> S> = lib.get(b"test_identity_struct\0").unwrap(); assert_eq!(S { a: 1, b: 2, c: 3, d: 4 }, f(S { a: 1, b: 2, c: 3, d: 4 })); } @@ -60,8 +60,8 @@ fn test_id_struct() { #[test] fn test_0_no_0() { make_helpers(); - let lib = Library::new(LIBPATH).unwrap(); unsafe { + let lib = Library::new(LIBPATH).unwrap(); let f: Symbol<unsafe extern fn(S) -> S> = lib.get(b"test_identity_struct\0").unwrap(); let f2: Symbol<unsafe extern fn(S) -> S> = lib.get(b"test_identity_struct").unwrap(); assert_eq!(*f, *f2); @@ -70,14 +70,16 @@ fn test_0_no_0() { #[test] fn wrong_name_fails() { - Library::new("target/this_location_is_definitely_non existent:^~").err().unwrap(); + unsafe { + Library::new("target/this_location_is_definitely_non existent:^~").err().unwrap(); + } } #[test] fn missing_symbol_fails() { make_helpers(); - let lib = Library::new(LIBPATH).unwrap(); unsafe { + let lib = Library::new(LIBPATH).unwrap(); lib.get::<*mut ()>(b"test_does_not_exist").err().unwrap(); lib.get::<*mut ()>(b"test_does_not_exist\0").err().unwrap(); } @@ -86,8 +88,8 @@ fn missing_symbol_fails() { #[test] fn interior_null_fails() { make_helpers(); - let lib = Library::new(LIBPATH).unwrap(); unsafe { + let lib = Library::new(LIBPATH).unwrap(); lib.get::<*mut ()>(b"test_does\0_not_exist").err().unwrap(); lib.get::<*mut ()>(b"test\0_does_not_exist\0").err().unwrap(); } @@ -96,8 +98,8 @@ fn interior_null_fails() { #[test] fn test_incompatible_type() { make_helpers(); - let lib = Library::new(LIBPATH).unwrap(); unsafe { + let lib = Library::new(LIBPATH).unwrap(); assert!(match lib.get::<()>(b"test_identity_u32\0") { Err(libloading::Error::IncompatibleSize) => true, _ => false, @@ -111,8 +113,8 @@ fn test_incompatible_type_named_fn() { unsafe fn get<'a, T>(l: &'a Library, _: T) -> Result<Symbol<'a, T>, libloading::Error> { l.get::<T>(b"test_identity_u32\0") } - let lib = Library::new(LIBPATH).unwrap(); unsafe { + let lib = Library::new(LIBPATH).unwrap(); assert!(match get(&lib, test_incompatible_type_named_fn) { Err(libloading::Error::IncompatibleSize) => true, _ => false, @@ -123,8 +125,8 @@ fn test_incompatible_type_named_fn() { #[test] fn test_static_u32() { make_helpers(); - let lib = Library::new(LIBPATH).unwrap(); unsafe { + let lib = Library::new(LIBPATH).unwrap(); let var: Symbol<*mut u32> = lib.get(b"TEST_STATIC_U32\0").unwrap(); **var = 42; let help: Symbol<unsafe extern fn() -> u32> = lib.get(b"test_get_static_u32\0").unwrap(); @@ -135,8 +137,8 @@ fn test_static_u32() { #[test] fn test_static_ptr() { make_helpers(); - let lib = Library::new(LIBPATH).unwrap(); unsafe { + let lib = Library::new(LIBPATH).unwrap(); let var: Symbol<*mut *mut ()> = lib.get(b"TEST_STATIC_PTR\0").unwrap(); **var = *var as *mut _; let works: Symbol<unsafe extern fn() -> bool> = @@ -174,13 +176,12 @@ fn manual_close_many_times() { fn library_this_get() { use libloading::os::unix::Library; make_helpers(); - let _lib = Library::new(LIBPATH).unwrap(); - let this = Library::this(); // SAFE: functions are never called unsafe { + let _lib = Library::new(LIBPATH).unwrap(); + let this = Library::this(); // Library we loaded in `_lib` (should be RTLD_LOCAL). - // FIXME: inconsistent behaviour between macos and other posix systems - // assert!(this.get::<unsafe extern "C" fn()>(b"test_identity_u32").is_err()); + 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()); } @@ -191,10 +192,11 @@ fn library_this_get() { fn library_this() { use libloading::os::windows::Library; make_helpers(); - let _lib = Library::new(LIBPATH).unwrap(); - let this = Library::this().expect("this library"); - // SAFE: functions are never called unsafe { + // SAFE: well-known library without initializers is loaded. + let _lib = Library::new(LIBPATH).unwrap(); + let this = Library::this().expect("this library"); + // SAFE: functions are never called. // Library we loaded in `_lib`. assert!(this.get::<unsafe extern "C" fn()>(b"test_identity_u32").is_err()); // Something "obscure" from kernel32... @@ -209,11 +211,9 @@ fn works_getlasterror() { 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 { + let lib = Library::new("kernel32.dll").unwrap(); + let gle: Symbol<unsafe extern "system" fn() -> DWORD> = lib.get(b"GetLastError").unwrap(); errhandlingapi::SetLastError(42); assert_eq!(errhandlingapi::GetLastError(), gle()) } @@ -226,11 +226,9 @@ fn works_getlasterror0() { 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 lib = Library::new("kernel32.dll").unwrap(); + let gle: Symbol<unsafe extern "system" fn() -> DWORD> = lib.get(b"GetLastError\0").unwrap(); errhandlingapi::SetLastError(42); assert_eq!(errhandlingapi::GetLastError(), gle()) } @@ -250,8 +248,9 @@ fn library_open_already_loaded() { _ => false, }); - let _lib = Library::new(LIBPATH).unwrap(); - - // Loaded now. - assert!(Library::open_already_loaded(LIBPATH).is_ok()); + unsafe { + let _lib = Library::new(LIBPATH).unwrap(); + // Loaded now. + assert!(Library::open_already_loaded(LIBPATH).is_ok()); + } } diff --git a/tests/windows.rs b/tests/windows.rs index 343aaa1..b4aad4a 100644 --- a/tests/windows.rs +++ b/tests/windows.rs @@ -15,12 +15,16 @@ use std::ffi::CStr; #[cfg(target_arch="x86")] fn load_ordinal_lib() -> Library { - Library::new("tests/nagisa32.dll").expect("nagisa32.dll") + unsafe { + Library::new("tests/nagisa32.dll").expect("nagisa32.dll") + } } #[cfg(target_arch="x86_64")] fn load_ordinal_lib() -> Library { - Library::new("tests/nagisa64.dll").expect("nagisa64.dll") + unsafe { + Library::new("tests/nagisa64.dll").expect("nagisa64.dll") + } } #[cfg(any(target_arch="x86", target_arch="x86_64"))] @@ -47,10 +51,14 @@ fn test_ordinal_missing_fails() { #[test] fn test_new_kernel23() { - Library::new("kernel23").err().unwrap(); + unsafe { + Library::new("kernel23").err().unwrap(); + } } #[test] fn test_new_kernel32_no_ext() { - Library::new("kernel32").unwrap(); + unsafe { + Library::new("kernel32").unwrap(); + } } |