]> git.lizzy.rs Git - rust.git/blob - library/std/src/sys/windows/compat.rs
Merge commit '05677b6bd6c938ed760835d9b1f6514992654ae3' into sync_cg_clif-2021-08-06
[rust.git] / library / std / src / sys / windows / compat.rs
1 //! A "compatibility layer" for supporting older versions of Windows
2 //!
3 //! The standard library uses some Windows API functions that are not present
4 //! on older versions of Windows.  (Note that the oldest version of Windows
5 //! that Rust supports is Windows 7 (client) and Windows Server 2008 (server).)
6 //! This module implements a form of delayed DLL import binding, using
7 //! `GetModuleHandle` and `GetProcAddress` to look up DLL entry points at
8 //! runtime.
9 //!
10 //! This implementation uses a static initializer to look up the DLL entry
11 //! points. The CRT (C runtime) executes static initializers before `main`
12 //! is called (for binaries) and before `DllMain` is called (for DLLs).
13 //! This is the ideal time to look up DLL imports, because we are guaranteed
14 //! that no other threads will attempt to call these entry points. Thus,
15 //! we can look up the imports and store them in `static mut` fields
16 //! without any synchronization.
17 //!
18 //! This has an additional advantage: Because the DLL import lookup happens
19 //! at module initialization, the cost of these lookups is deterministic,
20 //! and is removed from the code paths that actually call the DLL imports.
21 //! That is, there is no unpredictable "cache miss" that occurs when calling
22 //! a DLL import. For applications that benefit from predictable delays,
23 //! this is a benefit. This also eliminates the comparison-and-branch
24 //! from the hot path.
25 //!
26 //! Currently, the standard library uses only a small number of dynamic
27 //! DLL imports. If this number grows substantially, then the cost of
28 //! performing all of the lookups at initialization time might become
29 //! substantial.
30 //!
31 //! The mechanism of registering a static initializer with the CRT is
32 //! documented in
33 //! [CRT Initialization](https://docs.microsoft.com/en-us/cpp/c-runtime-library/crt-initialization?view=msvc-160).
34 //! It works by contributing a global symbol to the `.CRT$XCU` section.
35 //! The linker builds a table of all static initializer functions.
36 //! The CRT startup code then iterates that table, calling each
37 //! initializer function.
38 //!
39 //! # **WARNING!!*
40 //! The environment that a static initializer function runs in is highly
41 //! constrained. There are **many** restrictions on what static initializers
42 //! can safely do. Static initializer functions **MUST NOT** do any of the
43 //! following (this list is not comprehensive):
44 //! * touch any other static field that is used by a different static
45 //!   initializer, because the order that static initializers run in
46 //!   is not defined.
47 //! * call `LoadLibrary` or any other function that acquires the DLL
48 //!   loader lock.
49 //! * call any Rust function or CRT function that touches any static
50 //!   (global) state.
51
52 macro_rules! compat_fn {
53     ($module:literal: $(
54         $(#[$meta:meta])*
55         pub fn $symbol:ident($($argname:ident: $argtype:ty),*) -> $rettype:ty $fallback_body:block
56     )*) => ($(
57         $(#[$meta])*
58         pub mod $symbol {
59             #[allow(unused_imports)]
60             use super::*;
61             use crate::mem;
62
63             type F = unsafe extern "system" fn($($argtype),*) -> $rettype;
64
65             /// Points to the DLL import, or the fallback function.
66             ///
67             /// This static can be an ordinary, unsynchronized, mutable static because
68             /// we guarantee that all of the writes finish during CRT initialization,
69             /// and all of the reads occur after CRT initialization.
70             static mut PTR: Option<F> = None;
71
72             /// This symbol is what allows the CRT to find the `init` function and call it.
73             /// It is marked `#[used]` because otherwise Rust would assume that it was not
74             /// used, and would remove it.
75             #[used]
76             #[link_section = ".CRT$XCU"]
77             static INIT_TABLE_ENTRY: unsafe extern "C" fn() = init;
78
79             unsafe extern "C" fn init() {
80                 // There is no locking here. This code is executed before main() is entered, and
81                 // is guaranteed to be single-threaded.
82                 //
83                 // DO NOT do anything interesting or complicated in this function! DO NOT call
84                 // any Rust functions or CRT functions, if those functions touch any global state,
85                 // because this function runs during global initialization. For example, DO NOT
86                 // do any dynamic allocation, don't call LoadLibrary, etc.
87                 let module_name: *const u8 = concat!($module, "\0").as_ptr();
88                 let symbol_name: *const u8 = concat!(stringify!($symbol), "\0").as_ptr();
89                 let module_handle = $crate::sys::c::GetModuleHandleA(module_name as *const i8);
90                 if !module_handle.is_null() {
91                     match $crate::sys::c::GetProcAddress(module_handle, symbol_name as *const i8) as usize {
92                         0 => {}
93                         n => {
94                             PTR = Some(mem::transmute::<usize, F>(n));
95                         }
96                     }
97                 }
98             }
99
100             #[allow(dead_code)]
101             pub fn option() -> Option<F> {
102                 unsafe { PTR }
103             }
104
105             #[allow(dead_code)]
106             pub unsafe fn call($($argname: $argtype),*) -> $rettype {
107                 if let Some(ptr) = PTR {
108                     ptr($($argname),*)
109                 } else {
110                     $fallback_body
111                 }
112             }
113         }
114
115         $(#[$meta])*
116         pub use $symbol::call as $symbol;
117     )*)
118 }