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 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195
//! 支持 "weak linkage" 到 Unix 上的符号
//!
//! 我们在 std 中执行的一些 I/O 操作需要更新版本的操作系统,但我们现在需要保持与旧版本的二进制兼容性。
//! 为了在可用时使用新功能,我们使用此模块进行检测。
//!
//! 此处使用的一种选择是弱链接,但不幸的是,这仅适用于 ELF。否则,使用 dlsym 在运行时获取符号值。
//! 这样做也是为了与 glibc 的较早版本兼容,并避免在 GLIBC_PRIVATE 符号上创建依赖项。
//! 假定我们已经动态链接到符号所来自的库,但是对于诸如 libpthread/libc 之类的东西,目前始终如此。
//!
//! 很久以前,这对 __pthread_get_minstack 符号使用了弱链接,但这导致 Debian 检测到对 libc6 (#23628) 的不必要的严格版本依赖,因为它是 GLIBC_PRIVATE。
//!
//! 我们现在使用 `dlsym` 进行该符号的运行时查找,以避免 ELF 版本依赖。
//!
//!
//!
//!
//!
// 有各种 `#[cfg]` 控制 `weak!` 和 `syscall!` 的每个实例中涉及哪些目标。
// 而不是试图统一所有这些,我们只允许某些 unix 目标根本不使用此模块。
//
#![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};
// 我们可以在 ELF 目标上使用真正的弱链接。
#[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 })
};
)
}
// 在非 ELF 目标上,使用弱链接的 dlsym 近似值。
#[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);
// 可能是调用者将读取该值 (通过调用 dlsymed 的函数)。这意味着我们需要至少用 C11 的消耗顺序加载它,以确保从指针读取的数据不是存储指针之前的数据。
//
// Rust 不具有 memory_order_consume 的等效项,所以我们使用了 acquire 栅栏 (对不起,ARM)。
//
// 现在,实际上,即使在放松和消耗意味着不同的 CPU 上也不需要这样做。
// 我们正在加载的符号可能在 init 上存在 (或不存在),即使它们不是运行时动态加载程序,也极有可能在内部具有足够的屏障 (可能是隐式的,例如,调用 `mprotect` 所提供的屏障)。
//
// 也就是说,所有这些都不能保证,所以我们使用了栅栏。
//
//
//
//
//
//
//
//
//
atomic::fence(Ordering::Acquire);
Some(func)
}
}
}
}
// Cold,因为它应该只在第一次初始化时发生。
#[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);
// 这与 `get` 中的 acquire 栅栏同步。
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 }
// 尽可能使用 libc 的弱符号,允许 `LD_PRELOAD` 插入,但如果找不到,则使用原始 syscall。
//
if let Some(fun) = $name.get() {
fun($($arg_name),*)
} else {
// 这看起来像黑客,但是 concat_idents 仅接受身份 (不包含路径)。
//
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 {
// 这看起来像黑客,但是 concat_idents 仅接受身份 (不包含路径)。
//
use libc::*;
syscall(
concat_idents!(SYS_, $name),
$($arg_name),*
) as $ret
}
)
}