diff --git a/Cargo.toml b/Cargo.toml index 75e146265..b447d9b1f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -13,7 +13,7 @@ description = "Bindings around the platform's dynamic library loading primitives keywords = ["dlopen", "load", "shared", "dylib"] categories = ["api-bindings"] rust-version = "1.56.0" -edition = "2015" +edition = "2021" [target.'cfg(windows)'.dependencies.windows-targets] version = ">=0.48, <0.54" diff --git a/src/lib.rs b/src/lib.rs index 3ddf98a34..4c8aaf0de 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -27,7 +27,7 @@ //! fn call_dynamic() -> Result> { //! unsafe { //! let lib = libloading::Library::new("/path/to/liblibrary.so")?; -//! let func: libloading::Symbol u32> = lib.get(b"my_func")?; +//! let func: libloading::Symbol u32> = lib.get(c"my_func")?; //! Ok(func()) //! } //! } diff --git a/src/os/unix/consts.rs b/src/os/unix/consts.rs index 4ae00592d..883094c8b 100644 --- a/src/os/unix/consts.rs +++ b/src/os/unix/consts.rs @@ -54,8 +54,7 @@ mod posix { #[cfg(any(not(libloading_docs), unix))] mod posix { - extern crate cfg_if; - use self::cfg_if::cfg_if; + use cfg_if::cfg_if; use super::c_int; cfg_if! { if #[cfg(target_os = "haiku")] { diff --git a/src/os/unix/mod.rs b/src/os/unix/mod.rs index 0e42c50d9..dd10610b1 100644 --- a/src/os/unix/mod.rs +++ b/src/os/unix/mod.rs @@ -8,11 +8,13 @@ mod unix_imports { } pub use self::consts::*; +use crate::error::Error; +use crate::util::ensure_compatible_types; use self::unix_imports::*; -use std::ffi::{CStr, OsStr}; +use std::borrow::Cow; +use std::ffi::{CStr, CString, OsStr}; use std::os::raw; use std::{fmt, marker, mem, ptr}; -use util::{cstr_cow_from_bytes, ensure_compatible_types}; mod consts; @@ -181,6 +183,25 @@ impl Library { where P: AsRef, { + /// Checks for the last byte and avoids allocating if it is zero. + /// + /// Non-last null bytes still result in an error. + fn cstr_cow_from_bytes(slice: &[u8]) -> Result, Error> { + Ok(match slice.last() { + // Slice out of 0 elements + None => Cow::Borrowed(c""), + // Slice with trailing 0 + Some(&0) => Cow::Borrowed( + CStr::from_bytes_with_nul(slice) + .map_err(|source| Error::CreateCStringWithTrailing { source })?, + ), + // Slice with no trailing 0 + Some(_) => { + Cow::Owned(CString::new(slice).map_err(|source| Error::CreateCString { source })?) + } + }) + } + let filename = match filename { None => None, Some(ref f) => Some(cstr_cow_from_bytes(f.as_ref().as_bytes())?), @@ -207,12 +228,12 @@ impl Library { .map_err(|e| e.unwrap_or(crate::Error::DlOpenUnknown)) } - unsafe fn get_impl(&self, symbol: &[u8], on_null: F) -> Result, crate::Error> + unsafe fn get_impl(&self, symbol: &CStr, on_null: F) -> Result, crate::Error> where F: FnOnce() -> Result, crate::Error>, { ensure_compatible_types::()?; - 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 // pattern must be used, which is, sadly, a little bit racy. @@ -243,9 +264,6 @@ impl Library { /// Get a pointer to a function or static variable by symbol name. /// - /// The `symbol` may not contain any null bytes, with the exception of the last byte. Providing a - /// null terminated `symbol` may help to avoid an allocation. - /// /// Symbol is interpreted as-is; no mangling is done. This means that symbols like `x::y` are /// most likely invalid. /// @@ -265,8 +283,7 @@ 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(&self, symbol: &[u8]) -> Result, crate::Error> { - extern crate cfg_if; + pub unsafe fn get(&self, symbol: &CStr) -> Result, crate::Error> { cfg_if::cfg_if! { // These targets are known to have MT-safe `dlerror`. if #[cfg(any( @@ -290,9 +307,6 @@ impl Library { /// Get a pointer to function or static variable by symbol name. /// - /// The `symbol` may not contain any null bytes, with the exception of the last byte. Providing a - /// null terminated `symbol` may help to avoid an allocation. - /// /// Symbol is interpreted as-is; no mangling is done. This means that symbols like `x::y` are /// most likely invalid. /// @@ -309,7 +323,7 @@ impl Library { /// The implementation of thread-local variables is extremely platform specific and uses of such /// variables that work on e.g. Linux may have unintended behaviour on other targets. #[inline(always)] - pub unsafe fn get_singlethreaded(&self, symbol: &[u8]) -> Result, crate::Error> { + pub unsafe fn get_singlethreaded(&self, symbol: &CStr) -> Result, crate::Error> { self.get_impl(symbol, || { Ok(Symbol { pointer: ptr::null_mut(), diff --git a/src/os/windows/mod.rs b/src/os/windows/mod.rs index 1cbc57be3..fcfc70168 100644 --- a/src/os/windows/mod.rs +++ b/src/os/windows/mod.rs @@ -15,9 +15,9 @@ mod windows_imports { windows_targets::link!("kernel32.dll" "system" fn GetProcAddress(module: HMODULE, procname: *const u8) -> FARPROC); } +use crate::util::ensure_compatible_types; use self::windows_imports::*; -use util::{ensure_compatible_types, cstr_cow_from_bytes}; -use std::ffi::{OsStr, OsString}; +use std::ffi::{CStr, OsStr, OsString}; use std::{fmt, io, marker, mem, ptr}; use std::os::raw; @@ -173,18 +173,15 @@ impl Library { /// Get a pointer to a function or static variable by symbol name. /// - /// The `symbol` may not contain any null bytes, with the exception of the last byte. A null - /// terminated `symbol` may avoid a string allocation in some cases. - /// /// Symbol is interpreted as-is; no mangling is done. This means that symbols like `x::y` are /// most likely invalid. /// /// # Safety /// /// Users of this API must specify the correct type of the function or variable loaded. - pub unsafe fn get(&self, symbol: &[u8]) -> Result, crate::Error> { + pub unsafe fn get(&self, symbol: &CStr) -> Result, crate::Error> { ensure_compatible_types::()?; - let symbol = cstr_cow_from_bytes(symbol)?; + with_get_last_error(|source| crate::Error::GetProcAddress { source }, || { let symbol = GetProcAddress(self.0, symbol.as_ptr().cast()); if symbol.is_none() { diff --git a/src/safe.rs b/src/safe.rs index 9788e1e35..1873a14fa 100644 --- a/src/safe.rs +++ b/src/safe.rs @@ -5,7 +5,7 @@ use super::os::unix as imp; #[cfg(all(not(libloading_docs), windows))] use super::os::windows as imp; use super::Error; -use std::ffi::OsStr; +use std::ffi::{CStr, OsStr}; use std::fmt; use std::marker; use std::ops; @@ -87,9 +87,6 @@ impl Library { /// Get a pointer to a function or static variable by symbol name. /// - /// The `symbol` may not contain any null bytes, with the exception of the last byte. Providing a - /// null-terminated `symbol` may help to avoid an allocation. - /// /// The symbol is interpreted as-is; no mangling is done. This means that symbols like `x::y` are /// most likely invalid. /// @@ -130,7 +127,7 @@ impl Library { /// # }; /// unsafe { /// let awesome_function: Symbol f64> = - /// lib.get(b"awesome_function\0").unwrap(); + /// lib.get(c"awesome_function").unwrap(); /// awesome_function(0.42); /// } /// ``` @@ -141,11 +138,11 @@ impl Library { /// # use ::libloading::{Library, Symbol}; /// # let lib = unsafe { Library::new("/path/to/awesome.module").unwrap() }; /// unsafe { - /// let awesome_variable: Symbol<*mut f64> = lib.get(b"awesome_variable\0").unwrap(); + /// let awesome_variable: Symbol<*mut f64> = lib.get(c"awesome_variable").unwrap(); /// **awesome_variable = 42.0; /// }; /// ``` - pub unsafe fn get(&self, symbol: &[u8]) -> Result, Error> { + pub unsafe fn get(&self, symbol: &CStr) -> Result, Error> { self.0.get(symbol).map(|from| Symbol::from_raw(from, self)) } @@ -215,7 +212,7 @@ impl<'lib, T> Symbol<'lib, T> { /// # use ::libloading::{Library, Symbol}; /// unsafe { /// let lib = Library::new("/path/to/awesome.module").unwrap(); - /// let symbol: Symbol<*mut u32> = lib.get(b"symbol\0").unwrap(); + /// let symbol: Symbol<*mut u32> = lib.get(c"symbol").unwrap(); /// let symbol = symbol.into_raw(); /// } /// ``` @@ -238,7 +235,7 @@ impl<'lib, T> Symbol<'lib, T> { /// # use ::libloading::{Library, Symbol}; /// unsafe { /// let lib = Library::new("/path/to/awesome.module").unwrap(); - /// let symbol: Symbol<*mut u32> = lib.get(b"symbol\0").unwrap(); + /// let symbol: Symbol<*mut u32> = lib.get(c"symbol").unwrap(); /// let symbol = symbol.into_raw(); /// let symbol = Symbol::from_raw(symbol, &lib); /// } @@ -280,7 +277,7 @@ impl<'lib, T> Symbol<'lib, Option> { /// # use ::libloading::{Library, Symbol}; /// unsafe { /// let lib = Library::new("/path/to/awesome.module").unwrap(); - /// let symbol: Symbol> = lib.get(b"symbol\0").unwrap(); + /// let symbol: Symbol> = lib.get(c"symbol").unwrap(); /// let symbol: Symbol<*mut u32> = symbol.lift_option().expect("static is not null"); /// } /// ``` diff --git a/src/test_helpers.rs b/src/test_helpers.rs index 9e3e9924f..49c5aa6b4 100644 --- a/src/test_helpers.rs +++ b/src/test_helpers.rs @@ -33,5 +33,5 @@ pub unsafe extern "C" fn test_get_static_u32() -> u32 { #[no_mangle] pub unsafe extern "C" fn test_check_static_ptr() -> bool { - TEST_STATIC_PTR == (&mut TEST_STATIC_PTR as *mut *mut _ as *mut _) + TEST_STATIC_PTR == (&raw mut TEST_STATIC_PTR as *mut *mut _ as *mut _) } diff --git a/src/util.rs b/src/util.rs index 599e6c254..222f6068f 100644 --- a/src/util.rs +++ b/src/util.rs @@ -1,29 +1,5 @@ -use std::borrow::Cow; -use std::ffi::{CStr, CString}; -use std::os::raw; - use crate::Error; -/// Checks for the last byte and avoids allocating if it is zero. -/// -/// Non-last null bytes still result in an error. -pub(crate) fn cstr_cow_from_bytes(slice: &[u8]) -> Result, Error> { - 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) - .map_err(|source| Error::CreateCStringWithTrailing { source })?, - ), - // Slice with no trailing 0 - Some(_) => { - Cow::Owned(CString::new(slice).map_err(|source| Error::CreateCString { source })?) - } - }) -} - #[inline] pub(crate) fn ensure_compatible_types() -> Result<(), Error> { if ::std::mem::size_of::() != ::std::mem::size_of::() { diff --git a/tests/functions.rs b/tests/functions.rs index c94592e7a..87b717668 100644 --- a/tests/functions.rs +++ b/tests/functions.rs @@ -2,8 +2,8 @@ extern crate windows_sys; extern crate libloading; -use std::os::raw::c_void; use libloading::{Library, Symbol}; +use std::os::raw::c_void; const TARGET_DIR: Option<&'static str> = option_env!("CARGO_TARGET_DIR"); const TARGET_TMPDIR: Option<&'static str> = option_env!("CARGO_TARGET_TMPDIR"); @@ -40,7 +40,7 @@ fn test_id_u32() { make_helpers(); unsafe { let lib = Library::new(lib_path()).unwrap(); - let f: Symbol u32> = lib.get(b"test_identity_u32\0").unwrap(); + let f: Symbol u32> = lib.get(c"test_identity_u32").unwrap(); assert_eq!(42, f(42)); } } @@ -50,10 +50,10 @@ fn test_try_into_ptr() { make_helpers(); unsafe { let lib = Library::new(lib_path()).unwrap(); - let f: Symbol u32> = lib.get(b"test_identity_u32\0").unwrap(); + let f: Symbol u32> = lib.get(c"test_identity_u32").unwrap(); let ptr: *mut c_void = f.try_as_raw_ptr().unwrap(); assert!(!ptr.is_null()); - let ptr_casted : extern "C" fn(u32) -> u32 = std::mem::transmute(ptr); + let ptr_casted: extern "C" fn(u32) -> u32 = std::mem::transmute(ptr); assert_eq!(42, ptr_casted(42)); } } @@ -72,7 +72,7 @@ fn test_id_struct() { make_helpers(); unsafe { let lib = Library::new(lib_path()).unwrap(); - let f: Symbol S> = lib.get(b"test_identity_struct\0").unwrap(); + let f: Symbol S> = lib.get(c"test_identity_struct").unwrap(); assert_eq!( S { a: 1, @@ -90,17 +90,6 @@ fn test_id_struct() { } } -#[test] -fn test_0_no_0() { - make_helpers(); - unsafe { - let lib = Library::new(lib_path()).unwrap(); - let f: Symbol S> = lib.get(b"test_identity_struct\0").unwrap(); - let f2: Symbol S> = lib.get(b"test_identity_struct").unwrap(); - assert_eq!(*f, *f2); - } -} - #[test] fn wrong_name_fails() { unsafe { @@ -115,20 +104,7 @@ fn missing_symbol_fails() { make_helpers(); unsafe { let lib = Library::new(lib_path()).unwrap(); - lib.get::<*mut ()>(b"test_does_not_exist").err().unwrap(); - lib.get::<*mut ()>(b"test_does_not_exist\0").err().unwrap(); - } -} - -#[test] -fn interior_null_fails() { - make_helpers(); - unsafe { - let lib = Library::new(lib_path()).unwrap(); - lib.get::<*mut ()>(b"test_does\0_not_exist").err().unwrap(); - lib.get::<*mut ()>(b"test\0_does_not_exist\0") - .err() - .unwrap(); + lib.get::<*mut ()>(c"test_does_not_exist").err().unwrap(); } } @@ -137,7 +113,7 @@ fn test_incompatible_type() { make_helpers(); unsafe { let lib = Library::new(lib_path()).unwrap(); - assert!(match lib.get::<()>(b"test_identity_u32\0") { + assert!(match lib.get::<()>(c"test_identity_u32") { Err(libloading::Error::IncompatibleSize) => true, _ => false, }) @@ -148,7 +124,7 @@ fn test_incompatible_type() { fn test_incompatible_type_named_fn() { make_helpers(); unsafe fn get<'a, T>(l: &'a Library, _: T) -> Result, libloading::Error> { - l.get::(b"test_identity_u32\0") + l.get::(c"test_identity_u32") } unsafe { let lib = Library::new(lib_path()).unwrap(); @@ -164,10 +140,9 @@ fn test_static_u32() { make_helpers(); unsafe { let lib = Library::new(lib_path()).unwrap(); - let var: Symbol<*mut u32> = lib.get(b"TEST_STATIC_U32\0").unwrap(); + let var: Symbol<*mut u32> = lib.get(c"TEST_STATIC_U32").unwrap(); **var = 42; - let help: Symbol u32> = - lib.get(b"test_get_static_u32\0").unwrap(); + let help: Symbol u32> = lib.get(c"test_get_static_u32").unwrap(); assert_eq!(42, help()); } } @@ -177,10 +152,10 @@ fn test_static_ptr() { make_helpers(); unsafe { let lib = Library::new(lib_path()).unwrap(); - let var: Symbol<*mut *mut ()> = lib.get(b"TEST_STATIC_PTR\0").unwrap(); + let var: Symbol<*mut *mut ()> = lib.get(c"TEST_STATIC_PTR").unwrap(); **var = *var as *mut _; let works: Symbol bool> = - lib.get(b"test_check_static_ptr\0").unwrap(); + lib.get(c"test_check_static_ptr").unwrap(); assert!(works()); } } @@ -201,7 +176,7 @@ fn manual_close_many_times() { for _ in 0..10000 { let lib = Library::new(lib_path()).expect("open library"); let _: Symbol u32> = - lib.get(b"test_identity_u32").expect("get fn"); + lib.get(c"test_identity_u32").expect("get fn"); lib.close().expect("close is successful"); } }) @@ -223,12 +198,12 @@ fn library_this_get() { let this = Library::this(); // Library we loaded in `_lib` (should be RTLD_LOCAL). assert!(this - .get::(b"test_identity_u32") + .get::(c"test_identity_u32") .is_err()); // Something obscure from libc... // Cygwin behaves like Windows so ignore it. #[cfg(not(target_os = "cygwin"))] - assert!(this.get::(b"freopen").is_ok()); + assert!(this.get::(c"freopen").is_ok()); } } @@ -244,10 +219,10 @@ fn library_this() { // SAFE: functions are never called. // Library we loaded in `_lib`. assert!(this - .get::(b"test_identity_u32") + .get::(c"test_identity_u32") .is_err()); // Something "obscure" from kernel32... - assert!(this.get::(b"GetLastError").is_err()); + assert!(this.get::(c"GetLastError").is_err()); } } @@ -259,21 +234,7 @@ fn works_getlasterror() { unsafe { let lib = Library::new("kernel32.dll").unwrap(); - let gle: Symbol u32> = lib.get(b"GetLastError").unwrap(); - SetLastError(42); - assert_eq!(GetLastError(), gle()) - } -} - -#[cfg(windows)] -#[test] -fn works_getlasterror0() { - use libloading::os::windows::{Library, Symbol}; - use windows_sys::Win32::Foundation::{GetLastError, SetLastError}; - - unsafe { - let lib = Library::new("kernel32.dll").unwrap(); - let gle: Symbol u32> = lib.get(b"GetLastError\0").unwrap(); + let gle: Symbol u32> = lib.get(c"GetLastError").unwrap(); SetLastError(42); assert_eq!(GetLastError(), gle()) }