use core::ffi::{c_int, c_void};
use std::{
error::Error,
marker::PhantomData,
ops::{Deref, DerefMut},
path::Path,
};
#[cfg(unix)]
pub mod unix;
#[cfg(windows)]
pub mod windows;
pub enum OpenMode {
Unix(c_int),
Win32(u32),
}
impl std::fmt::Display for OpenMode {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
OpenMode::Unix(_) => write!(f, "unix")?,
OpenMode::Win32(_) => write!(f, "windows")?,
}
Ok(())
}
}
pub struct Dl {
hnd: *mut c_void,
}
unsafe impl Send for Dl {}
unsafe impl Sync for Dl {}
impl Dl {
pub unsafe fn open<P: AsRef<Path>>(file: Option<P>) -> Result<Self, Box<dyn Error>> {
#[cfg(unix)]
unsafe {
Dl::open_mode(file, OpenMode::Unix(unix::RTLD_NOW))
}
#[cfg(windows)]
unsafe {
Dl::open_mode(file, OpenMode::Win32(0))
}
}
pub unsafe fn open_mode<P: AsRef<Path>>(
file: Option<P>,
mode: OpenMode,
) -> Result<Self, Box<dyn Error>> {
let result;
#[cfg(unix)]
unsafe {
result = unix::do_dlopen(file, mode);
}
#[cfg(windows)]
unsafe {
result = windows::load_library_or_get_module_handle(file, mode)
}
Ok(Self { hnd: result? })
}
pub unsafe fn handle(&self) -> *mut c_void {
self.hnd
}
pub unsafe fn sym<T>(&self, symbol_name: &str) -> Result<Sym<'_, T>, Box<dyn Error>> {
if size_of::<T>() != size_of::<*mut ()>() {
panic!("type T has a different size than a pointer");
}
let result;
#[cfg(unix)]
unsafe {
result = unix::do_dlsym(self.hnd, symbol_name);
}
#[cfg(windows)]
unsafe {
result = windows::get_proc_address(self.hnd, symbol_name);
}
let sym_ptr = result?;
let sym_transmute = unsafe { std::mem::transmute_copy(&sym_ptr) };
Ok(Sym::new(sym_transmute, sym_ptr as usize))
}
pub unsafe fn close(self) -> Result<(), Box<dyn Error>> {
#[cfg(unix)]
unsafe {
unix::do_dlclose(self.hnd)
}
#[cfg(windows)]
unsafe {
windows::free_library(self.hnd)
}
}
}
#[derive(Debug, Clone, Copy)]
pub struct Sym<'lib, T: 'lib> {
symbol: T,
addr: usize,
pd: PhantomData<&'lib T>,
}
impl<'lib, T> Sym<'lib, T> {
pub fn new(symbol: T, addr: usize) -> Sym<'lib, T> {
Sym {
symbol,
addr,
pd: PhantomData,
}
}
pub fn addr(&self) -> usize {
self.addr
}
}
impl<'lib, T> Deref for Sym<'lib, T> {
type Target = T;
fn deref(&self) -> &T {
&self.symbol
}
}
impl<'lib, T> DerefMut for Sym<'lib, T> {
fn deref_mut(&mut self) -> &mut T {
&mut self.symbol
}
}
unsafe impl<'lib, T: Send> Send for Sym<'lib, T> {}
unsafe impl<'lib, T: Sync> Sync for Sym<'lib, T> {}