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
//! "compatibility layer",用于支持较早版本的 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 导入时,不会发生不可预测的 "cache miss"。 //! 对于受益于可预测的延迟的应用程序,这是一个好处。这也消除了热路径中的比较和分支。 //! //! 当前,标准库仅使用少量动态 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; )*) }