Skip to content

Use CStr as the type for symbol name #174

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down
2 changes: 1 addition & 1 deletion src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@
//! fn call_dynamic() -> Result<u32, Box<dyn std::error::Error>> {
//! unsafe {
//! let lib = libloading::Library::new("/path/to/liblibrary.so")?;
//! let func: libloading::Symbol<unsafe extern fn() -> u32> = lib.get(b"my_func")?;
//! let func: libloading::Symbol<unsafe extern fn() -> u32> = lib.get(c"my_func")?;
//! Ok(func())
//! }
//! }
Expand Down
3 changes: 1 addition & 2 deletions src/os/unix/consts.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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")] {
Expand Down
40 changes: 27 additions & 13 deletions src/os/unix/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down Expand Up @@ -181,6 +183,25 @@ impl Library {
where
P: AsRef<OsStr>,
{
/// 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<Cow<'_, CStr>, 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())?),
Expand All @@ -207,12 +228,12 @@ impl Library {
.map_err(|e| e.unwrap_or(crate::Error::DlOpenUnknown))
}

unsafe fn get_impl<T, F>(&self, symbol: &[u8], on_null: F) -> Result<Symbol<T>, crate::Error>
unsafe fn get_impl<T, F>(&self, symbol: &CStr, on_null: F) -> Result<Symbol<T>, crate::Error>
where
F: FnOnce() -> Result<Symbol<T>, crate::Error>,
{
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
// pattern must be used, which is, sadly, a little bit racy.
Expand Down Expand Up @@ -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.
///
Expand All @@ -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<T>(&self, symbol: &[u8]) -> Result<Symbol<T>, crate::Error> {
extern crate cfg_if;
pub unsafe fn get<T>(&self, symbol: &CStr) -> Result<Symbol<T>, crate::Error> {
cfg_if::cfg_if! {
// These targets are known to have MT-safe `dlerror`.
if #[cfg(any(
Expand All @@ -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.
///
Expand All @@ -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<T>(&self, symbol: &[u8]) -> Result<Symbol<T>, crate::Error> {
pub unsafe fn get_singlethreaded<T>(&self, symbol: &CStr) -> Result<Symbol<T>, crate::Error> {
self.get_impl(symbol, || {
Ok(Symbol {
pointer: ptr::null_mut(),
Expand Down
11 changes: 4 additions & 7 deletions src/os/windows/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down Expand Up @@ -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<T>(&self, symbol: &[u8]) -> Result<Symbol<T>, crate::Error> {
pub unsafe fn get<T>(&self, symbol: &CStr) -> Result<Symbol<T>, crate::Error> {
ensure_compatible_types::<T, FARPROC>()?;
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() {
Expand Down
17 changes: 7 additions & 10 deletions src/safe.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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.
///
Expand Down Expand Up @@ -130,7 +127,7 @@ impl Library {
/// # };
/// unsafe {
/// let awesome_function: Symbol<unsafe extern fn(f64) -> f64> =
/// lib.get(b"awesome_function\0").unwrap();
/// lib.get(c"awesome_function").unwrap();
/// awesome_function(0.42);
/// }
/// ```
Expand All @@ -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<T>(&self, symbol: &[u8]) -> Result<Symbol<T>, Error> {
pub unsafe fn get<T>(&self, symbol: &CStr) -> Result<Symbol<T>, Error> {
self.0.get(symbol).map(|from| Symbol::from_raw(from, self))
}

Expand Down Expand Up @@ -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();
/// }
/// ```
Expand All @@ -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);
/// }
Expand Down Expand Up @@ -280,7 +277,7 @@ impl<'lib, T> Symbol<'lib, Option<T>> {
/// # use ::libloading::{Library, Symbol};
/// 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<Option<*mut u32>> = lib.get(c"symbol").unwrap();
/// let symbol: Symbol<*mut u32> = symbol.lift_option().expect("static is not null");
/// }
/// ```
Expand Down
2 changes: 1 addition & 1 deletion src/test_helpers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 _)
}
24 changes: 0 additions & 24 deletions src/util.rs
Original file line number Diff line number Diff line change
@@ -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<Cow<'_, 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)
.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<T, E>() -> Result<(), Error> {
if ::std::mem::size_of::<T>() != ::std::mem::size_of::<E>() {
Expand Down
Loading