use std::ffi::{CStr, CString, OsStr, c_int, c_void};
use std::os::unix::ffi::OsStrExt;
use std::ptr::NonNull;
use super::Lookup;
use crate::{Error, Library, Result};
pub mod flags {
use std::ffi::c_int;
pub const RTLD_LAZY: c_int = libc::RTLD_LAZY;
pub const RTLD_NOW: c_int = libc::RTLD_NOW;
pub const RTLD_GLOBAL: c_int = libc::RTLD_GLOBAL;
pub const RTLD_LOCAL: c_int = libc::RTLD_LOCAL;
}
pub trait LibraryExt: Sized {
unsafe fn open_with_flags(path: impl AsRef<OsStr>, flags: c_int) -> Result<Self>;
fn this() -> Result<Self>;
}
impl LibraryExt for Library {
unsafe fn open_with_flags(path: impl AsRef<OsStr>, flags: c_int) -> Result<Self> {
unsafe { open(Some(path.as_ref()), flags) }.map(Library::from_handle)
}
fn this() -> Result<Self> {
unsafe { open(None, flags::RTLD_NOW | flags::RTLD_LOCAL) }.map(Library::from_handle)
}
}
#[derive(Debug)]
pub(crate) struct Handle(NonNull<c_void>);
unsafe impl Send for Handle {}
unsafe impl Sync for Handle {}
fn take_error() -> Option<String> {
let msg = unsafe { libc::dlerror() };
if msg.is_null() {
return None;
}
Some(
unsafe { CStr::from_ptr(msg) }
.to_string_lossy()
.into_owned(),
)
}
pub(crate) unsafe fn open(path: Option<&OsStr>, flags: c_int) -> Result<Handle> {
let cpath = path
.map(|p| CString::new(p.as_bytes()))
.transpose()
.map_err(|e| Error::InteriorNul {
position: e.nul_position(),
})?;
let ptr = cpath.as_deref().map_or(std::ptr::null(), CStr::as_ptr);
let handle = unsafe { libc::dlopen(ptr, flags) };
NonNull::new(handle).map(Handle).ok_or_else(|| Error::Open {
path: path.unwrap_or_default().to_owned(),
message: take_error().unwrap_or_else(|| "unknown error".into()),
})
}
pub(crate) fn lookup(handle: &Handle, name: &CStr) -> Lookup {
take_error();
let addr = unsafe { libc::dlsym(handle.0.as_ptr(), name.as_ptr()) };
if addr.is_null()
&& let Some(message) = take_error()
{
return Lookup::NotFound(message);
}
Lookup::Found(addr)
}
pub(crate) fn close(handle: Handle) -> Result<()> {
if unsafe { libc::dlclose(handle.0.as_ptr()) } == 0 {
Ok(())
} else {
Err(Error::Close {
message: take_error().unwrap_or_else(|| "unknown error".into()),
})
}
}