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}