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
        }
    )
}