#![allow(unsafe_code)]
use std::ffi::{CStr, CString, c_char, c_int, c_void};
use std::io;
use std::os::unix::ffi::OsStrExt;
use std::path::Path;
unsafe extern "C" {
fn dlopen(filename: *const c_char, flags: c_int) -> *mut c_void;
fn dlsym(handle: *mut c_void, symbol: *const c_char) -> *mut c_void;
fn dlerror() -> *mut c_char;
}
const RTLD_NOW: c_int = 2;
#[cfg(any(target_os = "macos", target_os = "ios"))]
const RTLD_LOCAL: c_int = 4;
#[cfg(not(any(target_os = "macos", target_os = "ios")))]
const RTLD_LOCAL: c_int = 0;
#[derive(Debug)]
pub(crate) struct Library {
handle: *mut c_void,
}
impl Library {
pub(crate) fn open(path: &Path) -> io::Result<Self> {
let name = CString::new(path.as_os_str().as_bytes())
.map_err(|_| io::Error::new(io::ErrorKind::InvalidInput, "path contains a NUL byte"))?;
let handle = unsafe {
dlerror();
dlopen(name.as_ptr(), RTLD_NOW | RTLD_LOCAL)
};
if handle.is_null() {
return Err(io::Error::new(io::ErrorKind::InvalidData, last_error()));
}
Ok(Self { handle })
}
pub(crate) fn id(&self) -> usize {
self.handle as usize
}
pub(crate) fn symbol(&self, symbol: &CStr) -> io::Result<*mut c_void> {
let address = unsafe {
dlerror();
dlsym(self.handle, symbol.as_ptr())
};
if address.is_null() {
return Err(io::Error::new(io::ErrorKind::NotFound, last_error()));
}
Ok(address)
}
}
fn last_error() -> String {
unsafe {
let text = dlerror();
if text.is_null() {
"unknown dynamic loader error".to_owned()
} else {
CStr::from_ptr(text).to_string_lossy().into_owned()
}
}
}