#![allow(dead_code, unused_macros)]
#![allow(clippy::doc_markdown)]
use core::sync::atomic::{self, AtomicUsize, Ordering};
use core::{marker, mem};
use rustix::ffi::ZStr;
macro_rules! weak {
(fn $name:ident($($t:ty),*) -> $ret:ty) => (
#[allow(non_upper_case_globals)]
static $name: $crate::weak::Weak<unsafe extern fn($($t),*) -> $ret> =
$crate::weak::Weak::new(concat!(stringify!($name), '\0'));
)
}
pub(crate) struct Weak<F> {
name: &'static str,
addr: AtomicUsize,
_marker: marker::PhantomData<F>,
}
impl<F> Weak<F> {
pub(crate) const fn new(name: &'static str) -> Self {
Self {
name,
addr: AtomicUsize::new(1),
_marker: marker::PhantomData,
}
}
pub(crate) fn get(&self) -> Option<F> {
assert_eq!(mem::size_of::<F>(), mem::size_of::<usize>());
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> {
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 ZStr::from_bytes_with_nul(name.as_bytes()) {
Ok(c_str) => c_str,
Err(..) => return 0,
};
libc::dlsym(libc::RTLD_DEFAULT, name.as_ptr().cast()) as usize
}
#[cfg(not(any(target_os = "android", target_os = "linux")))]
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 {
errno::set_errno(errno::Errno(libc::ENOSYS));
-1
}
}
)
}
#[cfg(any(target_os = "android", target_os = "linux"))]
macro_rules! 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 c_long),*
) as $ret
}
)
}
macro_rules! weakcall {
($vis:vis fn $name:ident($($arg_name:ident: $t:ty),*) -> $ret:ty) => (
$vis unsafe fn $name($($arg_name: $t),*) -> $ret {
weak! { fn $name($($t),*) -> $ret }
if let Some(fun) = $name.get() {
fun($($arg_name),*)
} else {
errno::set_errno(errno::Errno(libc::ENOSYS));
-1
}
}
)
}