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
//! 用于支持旧版本 Windows 的 "兼容层"
//!
//! 标准库使用一些 Windows API 函数,这些 Windows API 函数在 Windows 的较早版本中不存在。
//! (请注意,Rust 支持的最早的 Windows 版本是 Windows 7 (client) 和 Windows Server 2008 (服务器)。) 该模块实现了一种延迟的 DLL 导入绑定形式,使用 `GetModuleHandle` 和 `GetProcAddress` 在运行时查找 DLL 入口点。
//!
//!
//! 此实现使用静态初始化来查找 DLL 入口点。CRT (C 运行时) 在调用 `main` (对于二进制文件) 和调用 `DllMain` (对于 DLL) 之前执行静态初始化程序。
//! 这是查找 DLL 导入的理想时间,因为我们保证没有其他线程会尝试调用这些入口点。
//! 因此,我们可以查找导入并将其存储在 `static mut` 字段中,而无需进行任何同步。
//!
//! 这还有一个额外的优势:因为 DLL 导入查找是在模块初始化时发生的,所以这些查找的开销是确定性的,并且从实际上调用 DLL 导入的代码路径中删除了。
//! 也就是说,调用 DLL 导入时,不会发生不可预测的 "缓存未命中"。
//! 对于受益于可预测的延迟的应用程序,这是一个好处。这也消除了热路径中的比较和分支。
//!
//! 当前,标准库仅使用少量动态 DLL 导入。如果该数目显着增加,则在初始化时执行所有查找的成本可能会变得很高。
//!
//! [CRT 初始化](https://docs.microsoft.com/en-us/cpp/c-runtime-library/crt-initialization?view=msvc-160) 中记录了向 CRT 注册静态初始化的机制。
//! 它通过在 `.CRT$XCU` 部分添加一个符号来起作用。
//! 链接器会建立一个包含所有静态初始化函数的表。
//! 然后,CRT 启动代码将迭代该表,并调用每个初始化函数。
//!
//! # **WARNING!!*
//! 静态初始化函数运行的环境受到严格限制。静态初始值设定项可以安全执行的操作有很多限制。静态初始化函数 **不得** 执行以下任何一项操作 (此列表并不全面) :
//! * 触摸其他静态初始化所使用的任何其他静态字段,因为未定义静态初始化程序运行的顺序。
//! * 调用 `LoadLibrary` 或任何其他获得 DLL 加载程序锁的函数。
//! * 调用任何接触 (global) 静态状态的 Rust 函数或 CRT 函数。
//!
//!
//!
//!
//!
//!
//!
//!
//!
//!
//!
//!
//!
//!
//!
//!
//!
//!
//!
//!
//!
//!
//!
//!
macro_rules! compat_fn {
($module:literal: $(
$(#[$meta:meta])*
pub fn $symbol:ident($($argname:ident: $argtype:ty),*) -> $rettype:ty $fallback_body:block
)*) => ($(
$(#[$meta])*
pub mod $symbol {
#[allow(unused_imports)]
use super::*;
use crate::mem;
type F = unsafe extern "system" fn($($argtype),*) -> $rettype;
/// 指向 DLL 导入或后备函数。
///
/// 该静态可以是普通的,非同步的静态静态,因为我们保证在 CRT 初始化期间所有写操作都将完成,并且所有读取都在 CRT 初始化之后进行。
///
///
static mut PTR: Option<F> = None;
/// 这个符号使 CRT 可以找到 `init` 函数并调用它。
/// 之所以标记为 `#[used]`,是因为否则 Rust 会假定未使用它,并将其删除。
///
#[used]
#[link_section = ".CRT$XCU"]
static INIT_TABLE_ENTRY: unsafe extern "C" fn() = init;
unsafe extern "C" fn init() {
// 这里没有锁定。该代码在输入 main() 之前执行,并且保证是单线程的。
//
// 不要在此函数中做任何有趣或复杂的事情! 如果那些 Rust 函数或 CRT 函数接触到任何状态,则不要调用任何 Rust 函数或 CRT 函数,因为此函数在初始化时运行。
// 例如,请勿进行任何动态分配,请勿调用 LoadLibrary 等。
//
//
//
let module_name: *const u8 = concat!($module, "\0").as_ptr();
let symbol_name: *const u8 = concat!(stringify!($symbol), "\0").as_ptr();
let module_handle = $crate::sys::c::GetModuleHandleA(module_name as *const i8);
if !module_handle.is_null() {
match $crate::sys::c::GetProcAddress(module_handle, symbol_name as *const i8) as usize {
0 => {}
n => {
PTR = Some(mem::transmute::<usize, F>(n));
}
}
}
}
#[allow(dead_code)]
pub fn option() -> Option<F> {
unsafe { PTR }
}
#[allow(dead_code)]
pub unsafe fn call($($argname: $argtype),*) -> $rettype {
if let Some(ptr) = PTR {
ptr($($argname),*)
} else {
$fallback_body
}
}
}
$(#[$meta])*
pub use $symbol::call as $symbol;
)*)
}