std\sys\pal\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 is implemented simply by storing a function pointer in an atomic.
11//! Loading and calling this function will have little or no overhead
12//! compared with calling any other dynamically imported function.
13//!
14//! The stored function pointer starts out as an importer function which will
15//! swap itself with the real function when it's called for the first time. If
16//! the real function can't be imported then a fallback function is used in its
17//! place. While this is low cost for the happy path (where the function is
18//! already loaded) it does mean there's some overhead the first time the
19//! function is called. In the worst case, multiple threads may all end up
20//! importing the same function unnecessarily.
21
22use crate::ffi::{CStr, c_void};
23use crate::ptr::NonNull;
24use crate::sys::c;
25
26// This uses a static initializer to preload some imported functions.
27// The CRT (C runtime) executes static initializers before `main`
28// is called (for binaries) and before `DllMain` is called (for DLLs).
29//
30// It works by contributing a global symbol to the `.CRT$XCT` section.
31// The linker builds a table of all static initializer functions.
32// The CRT startup code then iterates that table, calling each
33// initializer function.
34//
35// NOTE: User code should instead use .CRT$XCU to reliably run after std's initializer.
36// If you're reading this and would like a guarantee here, please
37// file an issue for discussion; currently we don't guarantee any functionality
38// before main.
39// See https://docs.microsoft.com/en-us/cpp/c-runtime-library/crt-initialization?view=msvc-170
40#[cfg(target_vendor = "win7")]
41#[used]
42#[unsafe(link_section = ".CRT$XCT")]
43static INIT_TABLE_ENTRY: unsafe extern "C" fn() = init;
44
45/// Preload some imported functions.
46///
47/// Note that any functions included here will be unconditionally loaded in
48/// the final binary, regardless of whether or not they're actually used.
49///
50/// Therefore, this should be limited to `compat_fn_optional` functions which
51/// must be preloaded or any functions where lazier loading demonstrates a
52/// negative performance impact in practical situations.
53///
54/// Currently we only preload `WaitOnAddress` and `WakeByAddressSingle`.
55#[cfg(target_vendor = "win7")]
56unsafe extern "C" fn init() {
57    // In an exe this code is executed before main() so is single threaded.
58    // In a DLL the system's loader lock will be held thereby synchronizing
59    // access. So the same best practices apply here as they do to running in DllMain:
60    // https://docs.microsoft.com/en-us/windows/win32/dlls/dynamic-link-library-best-practices
61    //
62    // DO NOT do anything interesting or complicated in this function! DO NOT call
63    // any Rust functions or CRT functions if those functions touch any global state,
64    // because this function runs during global initialization. For example, DO NOT
65    // do any dynamic allocation, don't call LoadLibrary, etc.
66
67    // Attempt to preload the synch functions.
68    load_synch_functions();
69}
70
71/// Helper macro for creating CStrs from literals and symbol names.
72macro_rules! ansi_str {
73    (sym $ident:ident) => {{ crate::sys::compat::const_cstr_from_bytes(concat!(stringify!($ident), "\0").as_bytes()) }};
74    ($lit:literal) => {{ crate::sys::compat::const_cstr_from_bytes(concat!($lit, "\0").as_bytes()) }};
75}
76
77/// Creates a C string wrapper from a byte slice, in a constant context.
78///
79/// This is a utility function used by the [`ansi_str`] macro.
80///
81/// # Panics
82///
83/// Panics if the slice is not null terminated or contains nulls, except as the last item
84pub(crate) const fn const_cstr_from_bytes(bytes: &'static [u8]) -> &'static CStr {
85    if !matches!(bytes.last(), Some(&0)) {
86        panic!("A CStr must be null terminated");
87    }
88    let mut i = 0;
89    // At this point `len()` is at least 1.
90    while i < bytes.len() - 1 {
91        if bytes[i] == 0 {
92            panic!("A CStr must not have interior nulls")
93        }
94        i += 1;
95    }
96    // SAFETY: The safety is ensured by the above checks.
97    unsafe { crate::ffi::CStr::from_bytes_with_nul_unchecked(bytes) }
98}
99
100/// Represents a loaded module.
101///
102/// Note that the modules std depends on must not be unloaded.
103/// Therefore a `Module` is always valid for the lifetime of std.
104#[derive(Copy, Clone)]
105pub(in crate::sys) struct Module(NonNull<c_void>);
106impl Module {
107    /// Try to get a handle to a loaded module.
108    ///
109    /// # SAFETY
110    ///
111    /// This should only be use for modules that exist for the lifetime of std
112    /// (e.g. kernel32 and ntdll).
113    pub unsafe fn new(name: &CStr) -> Option<Self> {
114        // SAFETY: A CStr is always null terminated.
115        unsafe {
116            let module = c::GetModuleHandleA(name.as_ptr().cast::<u8>());
117            NonNull::new(module).map(Self)
118        }
119    }
120
121    // Try to get the address of a function.
122    pub fn proc_address(self, name: &CStr) -> Option<NonNull<c_void>> {
123        unsafe {
124            // SAFETY:
125            // `self.0` will always be a valid module.
126            // A CStr is always null terminated.
127            let proc = c::GetProcAddress(self.0.as_ptr(), name.as_ptr().cast::<u8>());
128            // SAFETY: `GetProcAddress` returns None on null.
129            proc.map(|p| NonNull::new_unchecked(p as *mut c_void))
130        }
131    }
132}
133
134/// Load a function or use a fallback implementation if that fails.
135macro_rules! compat_fn_with_fallback {
136    (pub static $module:ident: &CStr = $name:expr; $(
137        $(#[$meta:meta])*
138        $vis:vis fn $symbol:ident($($argname:ident: $argtype:ty),*) -> $rettype:ty $fallback_body:block
139    )*) => (
140        pub static $module: &CStr = $name;
141    $(
142        $(#[$meta])*
143        pub mod $symbol {
144            #[allow(unused_imports)]
145            use super::*;
146            use crate::mem;
147            use crate::ffi::CStr;
148            use crate::sync::atomic::{Atomic, AtomicPtr, Ordering};
149            use crate::sys::compat::Module;
150
151            type F = unsafe extern "system" fn($($argtype),*) -> $rettype;
152
153            /// `PTR` contains a function pointer to one of three functions.
154            /// It starts with the `load` function.
155            /// When that is called it attempts to load the requested symbol.
156            /// If it succeeds, `PTR` is set to the address of that symbol.
157            /// If it fails, then `PTR` is set to `fallback`.
158            static PTR: Atomic<*mut c_void> = AtomicPtr::new(load as *mut _);
159
160            unsafe extern "system" fn load($($argname: $argtype),*) -> $rettype {
161                unsafe {
162                    let func = load_from_module(Module::new($module));
163                    func($($argname),*)
164                }
165            }
166
167            fn load_from_module(module: Option<Module>) -> F {
168                unsafe {
169                    static SYMBOL_NAME: &CStr = ansi_str!(sym $symbol);
170                    if let Some(f) = module.and_then(|m| m.proc_address(SYMBOL_NAME)) {
171                        PTR.store(f.as_ptr(), Ordering::Relaxed);
172                        mem::transmute(f)
173                    } else {
174                        PTR.store(fallback as *mut _, Ordering::Relaxed);
175                        fallback
176                    }
177                }
178            }
179
180            #[allow(unused_variables)]
181            unsafe extern "system" fn fallback($($argname: $argtype),*) -> $rettype {
182                $fallback_body
183            }
184
185            #[inline(always)]
186            pub unsafe fn call($($argname: $argtype),*) -> $rettype {
187                unsafe {
188                    let func: F = mem::transmute(PTR.load(Ordering::Relaxed));
189                    func($($argname),*)
190                }
191            }
192        }
193        #[allow(unused)]
194        $(#[$meta])*
195        $vis use $symbol::call as $symbol;
196    )*)
197}
198
199/// Optionally loaded functions.
200///
201/// Relies on the functions being pre-loaded elsewhere.
202#[cfg(target_vendor = "win7")]
203macro_rules! compat_fn_optional {
204    ($(
205        $(#[$meta:meta])*
206        $vis:vis fn $symbol:ident($($argname:ident: $argtype:ty),*) $(-> $rettype:ty)?;
207    )+) => (
208        $(
209            pub mod $symbol {
210                #[allow(unused_imports)]
211                use super::*;
212                use crate::ffi::c_void;
213                use crate::mem;
214                use crate::ptr::{self, NonNull};
215                use crate::sync::atomic::{Atomic, AtomicPtr, Ordering};
216
217                pub(in crate::sys) static PTR: Atomic<*mut c_void> = AtomicPtr::new(ptr::null_mut());
218
219                type F = unsafe extern "system" fn($($argtype),*) $(-> $rettype)?;
220
221                #[inline(always)]
222                pub fn option() -> Option<F> {
223                    NonNull::new(PTR.load(Ordering::Relaxed)).map(|f| unsafe { mem::transmute(f) })
224                }
225            }
226            #[inline]
227            pub unsafe extern "system" fn $symbol($($argname: $argtype),*) $(-> $rettype)? {
228                unsafe { $symbol::option().unwrap()($($argname),*) }
229            }
230        )+
231    )
232}
233
234/// Load all needed functions from "api-ms-win-core-synch-l1-2-0".
235#[cfg(target_vendor = "win7")]
236pub(super) fn load_synch_functions() {
237    fn try_load() -> Option<()> {
238        use crate::sync::atomic::Ordering;
239        const MODULE_NAME: &CStr = c"api-ms-win-core-synch-l1-2-0";
240        const WAIT_ON_ADDRESS: &CStr = c"WaitOnAddress";
241        const WAKE_BY_ADDRESS_SINGLE: &CStr = c"WakeByAddressSingle";
242
243        // Try loading the library and all the required functions.
244        // If any step fails, then they all fail.
245        let library = unsafe { Module::new(MODULE_NAME) }?;
246        let wait_on_address = library.proc_address(WAIT_ON_ADDRESS)?;
247        let wake_by_address_single = library.proc_address(WAKE_BY_ADDRESS_SINGLE)?;
248
249        c::WaitOnAddress::PTR.store(wait_on_address.as_ptr(), Ordering::Relaxed);
250        c::WakeByAddressSingle::PTR.store(wake_by_address_single.as_ptr(), Ordering::Relaxed);
251        Some(())
252    }
253
254    try_load();
255}