#![allow(dead_code, unused_macros)]
use std::ffi::CStr;
use std::marker::PhantomData;
use std::mem;
use std::sync::atomic::{self, AtomicUsize, Ordering};
#[cfg(not(any(target_os = "macos", target_os = "ios")))]
macro_rules! weak {
(fn $name:ident($($t:ty),*) -> $ret:ty) => (
let ref $name: crate::weak::ExternWeak<unsafe extern "C" fn($($t),*) -> $ret> = {
extern "C" {
#[linkage = "extern_weak"]
static $name: *const libc::c_void;
}
#[allow(unused_unsafe)]
crate::weak::ExternWeak::new(unsafe { $name })
};
)
}
pub(crate) struct ExternWeak<F> {
weak_ptr: *const libc::c_void,
_marker: PhantomData<F>,
}
impl<F> ExternWeak<F> {
#[inline]
pub(crate) fn new(weak_ptr: *const libc::c_void) -> Self {
ExternWeak {
weak_ptr,
_marker: PhantomData,
}
}
}
impl<F> ExternWeak<F> {
#[inline]
pub(crate) fn get(&self) -> Option<F> {
unsafe {
if self.weak_ptr.is_null() {
None
} else {
Some(std::mem::transmute_copy::<*const libc::c_void, F>(
&self.weak_ptr,
))
}
}
}
}
pub(crate) struct DlsymWeak<F> {
name: &'static str,
addr: AtomicUsize,
_marker: PhantomData<F>,
}
impl<F> DlsymWeak<F> {
pub(crate) const fn new(name: &'static str) -> Self {
DlsymWeak {
name,
addr: AtomicUsize::new(1),
_marker: PhantomData,
}
}
#[inline]
pub(crate) fn get(&self) -> Option<F> {
unsafe {
match self.addr.load(Ordering::Relaxed) {
1 => self.initialize(),
0 => None,
addr => {
let func = mem::transmute_copy::<usize, F>(&addr);
atomic::fence(Ordering::Acquire);
Some(func)
}
}
}
}
#[cold]
unsafe fn initialize(&self) -> Option<F> {
assert_eq!(mem::size_of::<F>(), mem::size_of::<usize>());
let val = fetch(self.name);
self.addr.store(val, Ordering::Release);
match val {
0 => None,
addr => Some(mem::transmute_copy::<usize, F>(&addr)),
}
}
}
unsafe fn fetch(name: &str) -> usize {
let name = match CStr::from_bytes_with_nul(name.as_bytes()) {
Ok(cstr) => cstr,
Err(..) => return 0,
};
libc::dlsym(libc::RTLD_DEFAULT, name.as_ptr()) as usize
}
#[cfg(not(any(target_os = "linux", target_os = "android")))]
macro_rules! syscall {
(fn $name:ident($($arg_name:ident: $t:ty),*) -> $ret:ty) => (
unsafe fn $name($($arg_name: $t),*) -> $ret {
weak! { fn $name($($t),*) -> $ret }
if let Some(fun) = $name.get() {
fun($($arg_name),*)
} else {
std::sys::os::set_errno(libc::ENOSYS);
-1
}
}
)
}
#[cfg(any(target_os = "linux", target_os = "android"))]
macro_rules! syscall {
(fn $name:ident($($arg_name:ident: $t:ty),*) -> $ret:ty) => (
unsafe fn $name($($arg_name:$t),*) -> $ret {
weak! { fn $name($($t),*) -> $ret }
if let Some(fun) = $name.get() {
fun($($arg_name),*)
} else {
use libc::*;
syscall(
concat_idents!(SYS_, $name),
$($arg_name),*
) as $ret
}
}
)
}
#[cfg(any(target_os = "linux", target_os = "android"))]
macro_rules! raw_syscall {
(fn $name:ident($($arg_name:ident: $t:ty),*) -> $ret:ty) => (
unsafe fn $name($($arg_name:$t),*) -> $ret {
use libc::*;
syscall(
concat_idents!(SYS_, $name),
$($arg_name),*
) as $ret
}
)
}
macro_rules! dlsym {
(fn $name:ident($($t:ty),*) -> $ret:ty) => {
weak!(fn $name($($t),*) -> $ret, stringify!($name));
};
(fn $name:ident($($t:ty),*) -> $ret:ty, $sym:expr) => {
static DLSYM: crate::weak::DlsymWeak<unsafe extern "C" fn($($t),*) -> $ret> =
crate::weak::DlsymWeak::new(concat!($sym, '\0'));
let $name = &DLSYM;
}
}
#[cfg(any(target_os = "macos", target_os = "ios"))]
pub(crate) use dlsym as weak;
#[cfg(not(any(target_os = "macos", target_os = "ios")))]
pub(crate) use weak;