1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70
use std::ffi::CString; use std::marker; use std::mem; use std::sync::atomic::{AtomicUsize, Ordering}; use libc::{self, c_char, c_void}; pub struct Dylib { pub init: AtomicUsize, } pub struct Symbol<T> { pub name: &'static str, pub addr: AtomicUsize, pub _marker: marker::PhantomData<T>, } impl Dylib { pub unsafe fn get<'a, T>(&self, sym: &'a Symbol<T>) -> Option<&'a T> { self.load().and_then(|handle| { sym.get(handle) }) } pub unsafe fn init(&self, path: &str) -> bool { if self.init.load(Ordering::SeqCst) != 0 { return true } let name = CString::new(path).unwrap(); let ptr = libc::dlopen(name.as_ptr() as *const c_char, libc::RTLD_LAZY); if ptr.is_null() { return false } match self.init.compare_and_swap(0, ptr as usize, Ordering::SeqCst) { 0 => {} _ => { libc::dlclose(ptr); } } return true } unsafe fn load(&self) -> Option<*mut c_void> { match self.init.load(Ordering::SeqCst) { 0 => None, n => Some(n as *mut c_void), } } } impl<T> Symbol<T> { unsafe fn get(&self, handle: *mut c_void) -> Option<&T> { assert_eq!(mem::size_of::<T>(), mem::size_of_val(&self.addr)); if self.addr.load(Ordering::SeqCst) == 0 { self.addr.store(fetch(handle, self.name.as_ptr()), Ordering::SeqCst) } if self.addr.load(Ordering::SeqCst) == 1 { None } else { mem::transmute::<&AtomicUsize, Option<&T>>(&self.addr) } } } unsafe fn fetch(handle: *mut c_void, name: *const u8) -> usize { let ptr = libc::dlsym(handle, name as *const _); if ptr.is_null() { 1 } else { ptr as usize } }