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