mirror of
https://github.com/openharmony/third_party_rust_libloading.git
synced 2026-07-19 21:23:39 -04:00
Implement the Error type
This allows us to: a) remove the panics in this library and give the control over what to do to the user; b) expose the error conditions to the user better, even if just slightly; c) implement the modern error handling currently implemented in many of the modern Rust libraries.
This commit is contained in:
@@ -14,7 +14,7 @@ jobs:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
rust_toolchain: [nightly, stable, 1.36.0]
|
||||
rust_toolchain: [nightly, stable, 1.40.0]
|
||||
os: [ubuntu-latest, windows-latest, macOS-latest]
|
||||
timeout-minutes: 20
|
||||
steps:
|
||||
|
||||
+7
-1
@@ -4,10 +4,15 @@
|
||||
///
|
||||
/// * Introduced a new method [`os::unix::Library::get_singlethreaded`];
|
||||
/// * Added (untested) support for building when targetting Redox and Fuchsia;
|
||||
/// * The APIs exposed by this library no longer panic and instead return an `Err` when it used
|
||||
/// to panic.
|
||||
///
|
||||
/// ## Breaking changes
|
||||
///
|
||||
/// * Minimum required (stable) version of Rust to build this library is now 1.36.0;
|
||||
/// * Minimum required (stable) version of Rust to build this library is now 1.40.0;
|
||||
/// * This crate now implements a custom [`Error`] type and all APIs now return this type rather
|
||||
/// than returning the `std::io::Error`;
|
||||
/// * `libloading::Result` has been removed;
|
||||
/// * Removed the dependency on the C compiler to build this library on UNIX-like platforms.
|
||||
/// `libloading` used to utilize a snippet written in C to work-around the unlikely possibility
|
||||
/// of the target having a thread-unsafe implementation of the `dlerror` function. The effect of
|
||||
@@ -21,6 +26,7 @@
|
||||
///
|
||||
/// [`Library::get`]: crate::Library::get
|
||||
/// [`os::unix::Library::get_singlethreaded`]: crate::os::unix::Library::get_singlethreaded
|
||||
/// [`Error`]: crate::Error
|
||||
pub mod r0_6_0 {}
|
||||
|
||||
|
||||
|
||||
+114
@@ -0,0 +1,114 @@
|
||||
use std::ffi::CString;
|
||||
|
||||
pub struct DlDescription(pub(crate) CString);
|
||||
|
||||
impl std::fmt::Debug for DlDescription {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||
std::fmt::Debug::fmt(&self.0, f)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct WindowsError(pub(crate) std::io::Error);
|
||||
|
||||
impl std::fmt::Debug for WindowsError {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||
std::fmt::Debug::fmt(&self.0, f)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
#[non_exhaustive]
|
||||
pub enum Error {
|
||||
/// The `dlopen` call failed.
|
||||
DlOpen { desc: DlDescription },
|
||||
/// The `dlopen` call failed and system did not report an error.
|
||||
DlOpenUnknown,
|
||||
/// The `dlsym` call failed.
|
||||
DlSym { desc: DlDescription },
|
||||
/// The `dlsym` call failed and system did not report an error.
|
||||
DlSymUnknown,
|
||||
/// The `dlclose` call failed.
|
||||
DlClose { desc: DlDescription },
|
||||
/// The `dlclose` call failed and system did not report an error.
|
||||
DlCloseUnknown,
|
||||
/// The `LoadLibraryW` call failed.
|
||||
LoadLibraryW { source: WindowsError },
|
||||
/// The `LoadLibraryW` call failed and system did not report an error.
|
||||
LoadLibraryWUnknown,
|
||||
/// The `GetProcAddress` call failed.
|
||||
GetProcAddress { source: WindowsError },
|
||||
/// The `GetProcAddressUnknown` call failed and system did not report an error.
|
||||
GetProcAddressUnknown,
|
||||
/// The `FreeLibrary` call failed.
|
||||
FreeLibrary { 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 },
|
||||
/// Could not create a new CString from bytes with trailing null.
|
||||
CreateCStringWithTrailing { source: std::ffi::FromBytesWithNulError },
|
||||
}
|
||||
|
||||
impl std::error::Error for Error {
|
||||
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
|
||||
use Error::*;
|
||||
match *self {
|
||||
CreateCString { ref source } => Some(source),
|
||||
CreateCStringWithTrailing { ref source } => Some(source),
|
||||
LoadLibraryW { ref source } => Some(&source.0),
|
||||
GetProcAddress { ref source } => Some(&source.0),
|
||||
FreeLibrary { ref source } => Some(&source.0),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl std::fmt::Display for Error {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||
use Error::*;
|
||||
match *self {
|
||||
DlOpen { ref desc } => write!(f, "{}", desc.0.to_string_lossy()),
|
||||
DlOpenUnknown => write!(f, "dlopen failed, but system did not report the error"),
|
||||
DlSym { ref desc } => write!(f, "{}", desc.0.to_string_lossy()),
|
||||
DlSymUnknown => write!(f, "dlsym failed, but system did not report the error"),
|
||||
DlClose { ref desc } => write!(f, "{}", desc.0.to_string_lossy()),
|
||||
DlCloseUnknown => write!(f, "dlclose failed, but system did not report the error"),
|
||||
LoadLibraryW { .. } => write!(f, "LoadLibraryW failed"),
|
||||
LoadLibraryWUnknown =>
|
||||
write!(f, "LoadLibraryW failed, but system did not report the error"),
|
||||
GetProcAddress { .. } => write!(f, "GetProcAddress failed"),
|
||||
GetProcAddressUnknown =>
|
||||
write!(f, "GetProcAddress failed, but system did not report the error"),
|
||||
FreeLibrary { .. } => write!(f, "FreeLibrary failed"),
|
||||
FreeLibraryUnknown =>
|
||||
write!(f, "FreeLibrary failed, but system did not report the error"),
|
||||
CreateCString { .. } => write!(f, "could not create a C string from bytes"),
|
||||
CreateCStringWithTrailing { .. } =>
|
||||
write!(f, "could not create a C string from bytes with trailing null"),
|
||||
IncompatibleSize => write!(f, "requested type cannot possibly work"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[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
-5
@@ -25,7 +25,7 @@
|
||||
//! ```no_run
|
||||
//! extern crate libloading as lib;
|
||||
//!
|
||||
//! fn call_dynamic() -> lib::Result<u32> {
|
||||
//! 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")?;
|
||||
@@ -45,12 +45,12 @@ use std::marker;
|
||||
use self::os::unix as imp;
|
||||
#[cfg(windows)]
|
||||
use self::os::windows as imp;
|
||||
pub use self::error::Error;
|
||||
|
||||
pub mod os;
|
||||
pub mod changelog;
|
||||
mod util;
|
||||
|
||||
pub type Result<T> = ::std::io::Result<T>;
|
||||
mod error;
|
||||
|
||||
/// A loaded dynamic library.
|
||||
pub struct Library(imp::Library);
|
||||
@@ -114,7 +114,7 @@ impl Library {
|
||||
/// let _ = Library::new("../awesome.module").unwrap();
|
||||
/// let _ = Library::new("libsomelib.so.1").unwrap();
|
||||
/// ```
|
||||
pub fn new<P: AsRef<OsStr>>(filename: P) -> Result<Library> {
|
||||
pub fn new<P: AsRef<OsStr>>(filename: P) -> Result<Library, Error> {
|
||||
imp::Library::new(filename).map(From::from)
|
||||
}
|
||||
|
||||
@@ -174,9 +174,21 @@ impl Library {
|
||||
/// **awesome_variable = 42.0;
|
||||
/// };
|
||||
/// ```
|
||||
pub unsafe fn get<'lib, T>(&'lib self, symbol: &[u8]) -> Result<Symbol<'lib, T>> {
|
||||
pub unsafe fn get<'lib, T>(&'lib self, symbol: &[u8]) -> Result<Symbol<'lib, T>, Error> {
|
||||
self.0.get(symbol).map(|from| Symbol::from_raw(from, self))
|
||||
}
|
||||
|
||||
/// Unload the library.
|
||||
///
|
||||
/// This method might be a no-op, depending on the flags with which the `Library` was opened,
|
||||
/// what library was opened or other platform specifics.
|
||||
///
|
||||
/// You only need to call this if you are interested in handling any errors that may arise when
|
||||
/// library is unloaded. Otherwise the implementation of `Drop` for `Library` will close the
|
||||
/// library and ignore the errors were they arise.
|
||||
pub fn close(self) -> Result<(), Error> {
|
||||
self.0.close()
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for Library {
|
||||
|
||||
+46
-34
@@ -1,7 +1,7 @@
|
||||
use util::{ensure_compatible_types, cstr_cow_from_bytes};
|
||||
|
||||
use std::ffi::{CStr, OsStr};
|
||||
use std::{fmt, io, marker, mem, ptr};
|
||||
use std::{fmt, marker, mem, ptr};
|
||||
use std::os::raw;
|
||||
use std::os::unix::ffi::OsStrExt;
|
||||
|
||||
@@ -14,7 +14,8 @@ use std::os::unix::ffi::OsStrExt;
|
||||
// In practice (as of 2020-04-01) most of the widely used targets use a thread-local for error
|
||||
// state and have been doing so for a long time. Regardless the comments in this function shall
|
||||
// remain as a documentation for the future generations.
|
||||
fn with_dlerror<T, F>(closure: F) -> Result<T, Option<io::Error>>
|
||||
fn with_dlerror<T, F>(wrap: fn(crate::error::DlDescription) -> crate::Error, closure: F)
|
||||
-> Result<T, Option<crate::Error>>
|
||||
where F: FnOnce() -> Option<T> {
|
||||
// We used to guard all uses of dl* functions with our own mutex. This made them safe to use in
|
||||
// MT programs provided the only way a program used dl* was via this library. However, it also
|
||||
@@ -52,8 +53,8 @@ where F: FnOnce() -> Option<T> {
|
||||
// ownership over the message?
|
||||
// TODO: should do locale-aware conversion here. OTOH Rust doesn’t seem to work well in
|
||||
// any system that uses non-utf8 locale, so I doubt there’s a problem here.
|
||||
let message = CStr::from_ptr(error).to_string_lossy().into_owned();
|
||||
Some(io::Error::new(io::ErrorKind::Other, message))
|
||||
let message = CStr::from_ptr(error).into();
|
||||
Some(wrap(crate::error::DlDescription(message)))
|
||||
// Since we do a copy of the error string above, maybe we should call dlerror again to
|
||||
// let libdl know it may free its copy of the string now?
|
||||
}
|
||||
@@ -91,7 +92,7 @@ impl Library {
|
||||
///
|
||||
/// Corresponds to `dlopen(filename, RTLD_NOW)`.
|
||||
#[inline]
|
||||
pub fn new<P: AsRef<OsStr>>(filename: P) -> ::Result<Library> {
|
||||
pub fn new<P: AsRef<OsStr>>(filename: P) -> Result<Library, crate::Error> {
|
||||
Library::open(Some(filename), RTLD_NOW)
|
||||
}
|
||||
|
||||
@@ -103,7 +104,7 @@ impl Library {
|
||||
/// Corresponds to `dlopen(NULL, RTLD_NOW)`.
|
||||
#[inline]
|
||||
pub fn this() -> Library {
|
||||
Library::open(None::<&OsStr>, RTLD_NOW).unwrap()
|
||||
Library::open(None::<&OsStr>, RTLD_NOW).expect("this should never fail")
|
||||
}
|
||||
|
||||
/// Find and load a shared library (module).
|
||||
@@ -114,13 +115,13 @@ impl Library {
|
||||
/// If the `filename` is None, null pointer is passed to `dlopen`.
|
||||
///
|
||||
/// Corresponds to `dlopen(filename, flags)`.
|
||||
pub fn open<P>(filename: Option<P>, flags: raw::c_int) -> ::Result<Library>
|
||||
pub 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(move || {
|
||||
with_dlerror(|desc| crate::Error::DlOpen { desc }, move || {
|
||||
let result = unsafe {
|
||||
let r = dlopen(match filename {
|
||||
None => ptr::null(),
|
||||
@@ -137,18 +138,13 @@ impl Library {
|
||||
handle: result
|
||||
})
|
||||
}
|
||||
}).map_err(|e| e.unwrap_or_else(||
|
||||
io::Error::new(
|
||||
io::ErrorKind::Other,
|
||||
"dlopen returned a null and dlerror reported no error"
|
||||
)
|
||||
))
|
||||
}).map_err(|e| e.unwrap_or(crate::Error::DlOpenUnknown))
|
||||
}
|
||||
|
||||
unsafe fn get_impl<T, F>(&self, symbol: &[u8], on_null: F) -> ::Result<Symbol<T>>
|
||||
where F: FnOnce() -> ::Result<Symbol<T>>
|
||||
unsafe fn get_impl<T, F>(&self, symbol: &[u8], on_null: F) -> Result<Symbol<T>, crate::Error>
|
||||
where F: FnOnce() -> Result<Symbol<T>, crate::Error>
|
||||
{
|
||||
ensure_compatible_types::<T, *mut raw::c_void>();
|
||||
ensure_compatible_types::<T, *mut raw::c_void>()?;
|
||||
let symbol = cstr_cow_from_bytes(symbol)?;
|
||||
// `dlsym` may return nullptr in two cases: when a symbol genuinely points to a null
|
||||
// pointer or the symbol cannot be found. In order to detect this case a double dlerror
|
||||
@@ -156,7 +152,7 @@ impl Library {
|
||||
//
|
||||
// We try to leave as little space as possible for this to occur, but we can’t exactly
|
||||
// fully prevent it.
|
||||
match with_dlerror(|| {
|
||||
match with_dlerror(|desc| crate::Error::DlSym { desc }, || {
|
||||
dlerror();
|
||||
let symbol = dlsym(self.handle, symbol.as_ptr());
|
||||
if symbol.is_null() {
|
||||
@@ -202,17 +198,12 @@ impl Library {
|
||||
/// 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>> {
|
||||
pub unsafe fn get<T>(&self, symbol: &[u8]) -> Result<Symbol<T>, crate::Error> {
|
||||
#[cfg(mtsafe_dlerror)]
|
||||
{ return self.get_singlethreaded(symbol); }
|
||||
#[cfg(not(mtsafe_dlerror))]
|
||||
{
|
||||
return self.get_impl(symbol, || {
|
||||
Err(io::Error::new(
|
||||
io::ErrorKind::Other,
|
||||
"dlsym returned a null and MT-unsafe dlerror reported no error"
|
||||
))
|
||||
});
|
||||
return self.get_impl(symbol, || Err(crate::Error::DlSymUnknown));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -239,7 +230,7 @@ impl Library {
|
||||
/// 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.
|
||||
#[inline(always)]
|
||||
pub unsafe fn get_singlethreaded<T>(&self, symbol: &[u8]) -> ::Result<Symbol<T>> {
|
||||
pub unsafe fn get_singlethreaded<T>(&self, symbol: &[u8]) -> Result<Symbol<T>, crate::Error> {
|
||||
self.get_impl(symbol, || Ok(Symbol {
|
||||
pointer: ptr::null_mut(),
|
||||
pd: marker::PhantomData
|
||||
@@ -268,15 +259,33 @@ impl Library {
|
||||
handle: handle
|
||||
}
|
||||
}
|
||||
|
||||
/// Unload the library.
|
||||
///
|
||||
/// This method might be a no-op, depending on the flags with which the `Library` was opened,
|
||||
/// what library was opened or other platform specifics.
|
||||
///
|
||||
/// You only need to call this if you are interested in handling any errors that may arise when
|
||||
/// library is unloaded. Otherwise the implementation of `Drop` for `Library` will close the
|
||||
/// library and ignore the errors were they arise.
|
||||
pub fn close(self) -> Result<(), crate::Error> {
|
||||
let result = with_dlerror(|desc| crate::Error::DlClose { desc }, || {
|
||||
if unsafe { dlclose(self.handle) } == 0 {
|
||||
Some(())
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}).map_err(|e| e.unwrap_or(crate::Error::DlCloseUnknown));
|
||||
std::mem::forget(self);
|
||||
result
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for Library {
|
||||
fn drop(&mut self) {
|
||||
with_dlerror(|| if unsafe { dlclose(self.handle) } == 0 {
|
||||
Some(())
|
||||
} else {
|
||||
None
|
||||
}).unwrap();
|
||||
unsafe {
|
||||
dlclose(self.handle);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -382,7 +391,10 @@ struct DlInfo {
|
||||
dli_saddr: *mut raw::c_void
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn this() {
|
||||
Library::this();
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
#[test]
|
||||
fn this() {
|
||||
super::Library::this();
|
||||
}
|
||||
}
|
||||
|
||||
+49
-46
@@ -11,7 +11,6 @@ use std::{fmt, io, marker, mem, ptr};
|
||||
use std::os::windows::ffi::{OsStrExt, OsStringExt};
|
||||
use std::sync::atomic::{AtomicBool, ATOMIC_BOOL_INIT, Ordering};
|
||||
|
||||
|
||||
/// A platform-specific equivalent of the cross-platform `Library`.
|
||||
pub struct Library(HMODULE);
|
||||
|
||||
@@ -36,11 +35,11 @@ impl Library {
|
||||
///
|
||||
/// Corresponds to `LoadLibraryW(filename)`.
|
||||
#[inline]
|
||||
pub fn new<P: AsRef<OsStr>>(filename: P) -> ::Result<Library> {
|
||||
pub fn new<P: AsRef<OsStr>>(filename: P) -> 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(|| {
|
||||
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::LoadLibraryW(wide_filename.as_ptr()) };
|
||||
@@ -49,10 +48,7 @@ impl Library {
|
||||
} else {
|
||||
Some(Library(handle))
|
||||
}
|
||||
}).map_err(|e| e.unwrap_or_else(||
|
||||
panic!("LoadLibraryW failed but GetLastError did not report the error")
|
||||
));
|
||||
|
||||
}).map_err(|e| e.unwrap_or(crate::Error::LoadLibraryWUnknown));
|
||||
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
|
||||
@@ -71,10 +67,10 @@ impl Library {
|
||||
/// 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.
|
||||
pub unsafe fn get<T>(&self, symbol: &[u8]) -> ::Result<Symbol<T>> {
|
||||
ensure_compatible_types::<T, FARPROC>();
|
||||
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)?;
|
||||
with_get_last_error(|| {
|
||||
with_get_last_error(|source| crate::Error::GetProcAddress { source }, || {
|
||||
let symbol = libloaderapi::GetProcAddress(self.0, symbol.as_ptr());
|
||||
if symbol.is_null() {
|
||||
None
|
||||
@@ -84,9 +80,7 @@ impl Library {
|
||||
pd: marker::PhantomData
|
||||
})
|
||||
}
|
||||
}).map_err(|e| e.unwrap_or_else(||
|
||||
panic!("GetProcAddress failed but GetLastError did not report the error")
|
||||
))
|
||||
}).map_err(|e| e.unwrap_or(crate::Error::GetProcAddressUnknown))
|
||||
}
|
||||
|
||||
/// Get a pointer to function or static variable by ordinal number.
|
||||
@@ -95,9 +89,9 @@ impl Library {
|
||||
///
|
||||
/// Pointer to a value of arbitrary type is returned. Using a value with wrong type is
|
||||
/// undefined.
|
||||
pub unsafe fn get_ordinal<T>(&self, ordinal: WORD) -> ::Result<Symbol<T>> {
|
||||
ensure_compatible_types::<T, FARPROC>();
|
||||
with_get_last_error(|| {
|
||||
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 }, || {
|
||||
let ordinal = ordinal as usize as *mut _;
|
||||
let symbol = libloaderapi::GetProcAddress(self.0, ordinal);
|
||||
if symbol.is_null() {
|
||||
@@ -108,9 +102,7 @@ impl Library {
|
||||
pd: marker::PhantomData
|
||||
})
|
||||
}
|
||||
}).map_err(|e| e.unwrap_or_else(||
|
||||
panic!("GetProcAddress failed but GetLastError did not report the error")
|
||||
))
|
||||
}).map_err(|e| e.unwrap_or(crate::Error::GetProcAddressUnknown))
|
||||
}
|
||||
|
||||
/// Convert the `Library` to a raw handle.
|
||||
@@ -129,17 +121,22 @@ impl Library {
|
||||
pub unsafe fn from_raw(handle: HMODULE) -> Library {
|
||||
Library(handle)
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for Library {
|
||||
fn drop(&mut self) {
|
||||
with_get_last_error(|| {
|
||||
/// Unload the library.
|
||||
pub fn close(self) -> Result<(), crate::Error> {
|
||||
with_get_last_error(|source| crate::Error::FreeLibrary { source }, || {
|
||||
if unsafe { libloaderapi::FreeLibrary(self.0) == 0 } {
|
||||
None
|
||||
} else {
|
||||
Some(())
|
||||
}
|
||||
}).unwrap()
|
||||
}).map_err(|e| e.unwrap_or(crate::Error::FreeLibraryUnknown))
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for Library {
|
||||
fn drop(&mut self) {
|
||||
unsafe { libloaderapi::FreeLibrary(self.0); }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -277,38 +274,44 @@ impl Drop for ErrorModeGuard {
|
||||
}
|
||||
}
|
||||
|
||||
fn with_get_last_error<T, F>(closure: F) -> Result<T, Option<io::Error>>
|
||||
fn with_get_last_error<T, F>(wrap: fn(crate::error::WindowsError) -> crate::Error, closure: F)
|
||||
-> Result<T, Option<crate::Error>>
|
||||
where F: FnOnce() -> Option<T> {
|
||||
closure().ok_or_else(|| {
|
||||
let error = unsafe { errhandlingapi::GetLastError() };
|
||||
if error == 0 {
|
||||
None
|
||||
} else {
|
||||
Some(io::Error::from_raw_os_error(error as i32))
|
||||
Some(wrap(crate::error::WindowsError(io::Error::from_raw_os_error(error as i32))))
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
#[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())
|
||||
}
|
||||
}
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[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())
|
||||
#[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())
|
||||
}
|
||||
}
|
||||
|
||||
#[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())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+11
-41
@@ -1,61 +1,31 @@
|
||||
use std::ffi::{CStr, CString, NulError, FromBytesWithNulError};
|
||||
use std::ffi::{CStr, CString};
|
||||
use std::borrow::Cow;
|
||||
use std::os::raw;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct NullError;
|
||||
|
||||
impl From<NulError> for NullError {
|
||||
fn from(_: NulError) -> NullError {
|
||||
NullError
|
||||
}
|
||||
}
|
||||
|
||||
impl From<FromBytesWithNulError> for NullError {
|
||||
fn from(_: FromBytesWithNulError) -> NullError {
|
||||
NullError
|
||||
}
|
||||
}
|
||||
|
||||
impl From<NullError> for ::std::io::Error {
|
||||
fn from(e: NullError) -> ::std::io::Error {
|
||||
::std::io::Error::new(::std::io::ErrorKind::Other, format!("{}", e))
|
||||
}
|
||||
}
|
||||
|
||||
impl ::std::error::Error for NullError {
|
||||
fn description(&self) -> &str { "non-final null byte found" }
|
||||
}
|
||||
|
||||
impl ::std::fmt::Display for NullError {
|
||||
fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
|
||||
write!(f, "non-final null byte found")
|
||||
}
|
||||
}
|
||||
use crate::Error;
|
||||
|
||||
/// Checks for last byte and avoids allocating if it is zero.
|
||||
///
|
||||
/// Non-last null bytes still result in an error.
|
||||
pub fn cstr_cow_from_bytes<'a>(slice: &'a [u8]) -> Result<Cow<'a, CStr>, NullError> {
|
||||
pub(crate) fn cstr_cow_from_bytes<'a>(slice: &'a [u8]) -> Result<Cow<'a, CStr>, Error> {
|
||||
static ZERO: raw::c_char = 0;
|
||||
Ok(match slice.last() {
|
||||
// Slice out of 0 elements
|
||||
None => unsafe { Cow::Borrowed(CStr::from_ptr(&ZERO)) },
|
||||
// Slice with trailing 0
|
||||
Some(&0) => Cow::Borrowed(CStr::from_bytes_with_nul(slice)?),
|
||||
Some(&0) => Cow::Borrowed(CStr::from_bytes_with_nul(slice)
|
||||
.map_err(|source| Error::CreateCStringWithTrailing { source })?),
|
||||
// Slice with no trailing 0
|
||||
Some(_) => Cow::Owned(CString::new(slice)?),
|
||||
Some(_) => Cow::Owned(CString::new(slice)
|
||||
.map_err(|source| Error::CreateCString { source })?),
|
||||
})
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn ensure_compatible_types<T, E>() {
|
||||
#[cold]
|
||||
#[inline(never)]
|
||||
fn dopanic() {
|
||||
panic!("value of requested type cannot be dynamically loaded");
|
||||
}
|
||||
pub(crate) fn ensure_compatible_types<T, E>() -> Result<(), Error> {
|
||||
if ::std::mem::size_of::<T>() != ::std::mem::size_of::<E>() {
|
||||
dopanic()
|
||||
Err(Error::IncompatibleSize)
|
||||
} else {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
+10
-6
@@ -4,7 +4,7 @@ use libloading::{Symbol, Library};
|
||||
const LIBPATH: &'static str = concat!(env!("OUT_DIR"), "/libtest_helpers.dll");
|
||||
|
||||
fn make_helpers() {
|
||||
static ONCE: ::std::sync::Once = ::std::sync::ONCE_INIT;
|
||||
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() });
|
||||
@@ -85,25 +85,29 @@ fn interior_null_fails() {
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic]
|
||||
fn test_incompatible_type() {
|
||||
make_helpers();
|
||||
let lib = Library::new(LIBPATH).unwrap();
|
||||
unsafe {
|
||||
let _ = lib.get::<()>(b"test_identity_u32\0");
|
||||
assert!(match lib.get::<()>(b"test_identity_u32\0") {
|
||||
Err(libloading::Error::IncompatibleSize) => true,
|
||||
_ => false,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic]
|
||||
fn test_incompatible_type_named_fn() {
|
||||
make_helpers();
|
||||
unsafe fn get<'a, T>(l: &'a Library, _: T) -> libloading::Result<Symbol<'a, T>> {
|
||||
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 _ = get(&lib, test_incompatible_type_named_fn);
|
||||
assert!(match get(&lib, test_incompatible_type_named_fn) {
|
||||
Err(libloading::Error::IncompatibleSize) => true,
|
||||
_ => false,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user