use util::{ensure_compatible_types, cstr_cow_from_bytes};
use std::ffi::{CStr, OsStr};
use std::{fmt, io, marker, mem, ptr};
use std::os::raw;
use std::os::unix::ffi::OsStrExt;
extern "C" {
fn rust_libloading_dlerror_mutex_lock();
fn rust_libloading_dlerror_mutex_unlock();
}
struct DlerrorMutexGuard(());
impl DlerrorMutexGuard {
fn new() -> DlerrorMutexGuard {
unsafe {
rust_libloading_dlerror_mutex_lock();
}
DlerrorMutexGuard(())
}
}
impl Drop for DlerrorMutexGuard {
fn drop(&mut self) {
unsafe {
rust_libloading_dlerror_mutex_unlock();
}
}
}
fn with_dlerror<T, F>(closure: F) -> Result<T, Option<io::Error>>
where F: FnOnce() -> Option<T> {
let _lock = DlerrorMutexGuard::new();
closure().ok_or_else(|| unsafe {
let error = dlerror();
if error.is_null() {
None
} else {
let message = CStr::from_ptr(error).to_string_lossy().into_owned();
Some(io::Error::new(io::ErrorKind::Other, message))
}
})
}
pub struct Library {
handle: *mut raw::c_void
}
unsafe impl Send for Library {}
unsafe impl Sync for Library {}
impl Library {
#[inline]
pub fn new<P: AsRef<OsStr>>(filename: P) -> ::Result<Library> {
Library::open(Some(filename), RTLD_NOW)
}
#[inline]
pub fn this() -> Library {
Library::open(None::<&OsStr>, RTLD_NOW).unwrap()
}
pub fn open<P>(filename: Option<P>, flags: raw::c_int) -> ::Result<Library>
where P: AsRef<OsStr> {
let filename = match filename {
None => None,
Some(ref f) => Some(try!(cstr_cow_from_bytes(f.as_ref().as_bytes()))),
};
with_dlerror(move || {
let result = unsafe {
let r = dlopen(match filename {
None => ptr::null(),
Some(ref f) => f.as_ptr()
}, flags);
drop(filename);
r
};
if result.is_null() {
None
} else {
Some(Library {
handle: result
})
}
}).map_err(|e| e.unwrap_or_else(||
panic!("dlopen failed but dlerror did not report anything")
))
}
pub unsafe fn get<T>(&self, symbol: &[u8]) -> ::Result<Symbol<T>> {
ensure_compatible_types::<T, *mut raw::c_void>();
let symbol = try!(cstr_cow_from_bytes(symbol));
match with_dlerror(|| {
dlerror();
let symbol = dlsym(self.handle, symbol.as_ptr());
if symbol.is_null() {
None
} else {
Some(Symbol {
pointer: symbol,
pd: marker::PhantomData
})
}
}) {
Err(None) => Ok(Symbol {
pointer: ptr::null_mut(),
pd: marker::PhantomData
}),
Err(Some(e)) => Err(e),
Ok(x) => Ok(x)
}
}
pub fn into_raw(self) -> *mut raw::c_void {
let handle = self.handle;
mem::forget(self);
handle
}
pub unsafe fn from_raw(handle: *mut raw::c_void) -> Library {
Library {
handle: handle
}
}
}
impl Drop for Library {
fn drop(&mut self) {
with_dlerror(|| if unsafe { dlclose(self.handle) } == 0 {
Some(())
} else {
None
}).unwrap();
}
}
impl fmt::Debug for Library {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.write_str(&format!("Library@{:p}", self.handle))
}
}
pub struct Symbol<T> {
pointer: *mut raw::c_void,
pd: marker::PhantomData<T>
}
impl<T> Symbol<T> {
pub fn into_raw(self) -> *mut raw::c_void {
let pointer = self.pointer;
mem::forget(self);
pointer
}
}
impl<T> Symbol<Option<T>> {
pub fn lift_option(self) -> Option<Symbol<T>> {
if self.pointer.is_null() {
None
} else {
Some(Symbol {
pointer: self.pointer,
pd: marker::PhantomData,
})
}
}
}
unsafe impl<T: Send> Send for Symbol<T> {}
unsafe impl<T: Sync> Sync for Symbol<T> {}
impl<T> Clone for Symbol<T> {
fn clone(&self) -> Symbol<T> {
Symbol { ..*self }
}
}
impl<T> ::std::ops::Deref for Symbol<T> {
type Target = T;
fn deref(&self) -> &T {
unsafe {
mem::transmute(&self.pointer)
}
}
}
impl<T> fmt::Debug for Symbol<T> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
unsafe {
let mut info: DlInfo = mem::uninitialized();
if dladdr(self.pointer, &mut info) != 0 {
if info.dli_sname.is_null() {
f.write_str(&format!("Symbol@{:p} from {:?}",
self.pointer,
CStr::from_ptr(info.dli_fname)))
} else {
f.write_str(&format!("Symbol {:?}@{:p} from {:?}",
CStr::from_ptr(info.dli_sname), self.pointer,
CStr::from_ptr(info.dli_fname)))
}
} else {
f.write_str(&format!("Symbol@{:p}", self.pointer))
}
}
}
}
extern {
fn dlopen(filename: *const raw::c_char, flags: raw::c_int) -> *mut raw::c_void;
fn dlclose(handle: *mut raw::c_void) -> raw::c_int;
fn dlsym(handle: *mut raw::c_void, symbol: *const raw::c_char) -> *mut raw::c_void;
fn dlerror() -> *mut raw::c_char;
fn dladdr(addr: *mut raw::c_void, info: *mut DlInfo) -> raw::c_int;
}
#[cfg(not(target_os="android"))]
const RTLD_NOW: raw::c_int = 2;
#[cfg(target_os="android")]
const RTLD_NOW: raw::c_int = 0;
#[repr(C)]
struct DlInfo {
dli_fname: *const raw::c_char,
dli_fbase: *mut raw::c_void,
dli_sname: *const raw::c_char,
dli_saddr: *mut raw::c_void
}
#[test]
fn this() {
Library::this();
}