]> git.proxmox.com Git - rustc.git/blame - library/std/src/sys/windows/compat.rs
New upstream version 1.65.0+dfsg1
[rustc.git] / library / std / src / sys / windows / compat.rs
CommitLineData
5869c6ff 1//! A "compatibility layer" for supporting older versions of Windows
c1a9b12d 2//!
5869c6ff
XL
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.
c1a9b12d 9//!
f2b60f7d
FG
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.
5869c6ff 13//!
f2b60f7d
FG
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.
c1a9b12d 21
064997fb
FG
22use crate::ffi::{c_void, CStr};
23use crate::ptr::NonNull;
f2b60f7d 24use crate::sync::atomic::Ordering;
064997fb
FG
25use crate::sys::c;
26
f2b60f7d
FG
27// This uses a static initializer to preload some imported functions.
28// The CRT (C runtime) executes static initializers before `main`
29// is called (for binaries) and before `DllMain` is called (for DLLs).
30//
31// It works by contributing a global symbol to the `.CRT$XCT` section.
32// The linker builds a table of all static initializer functions.
33// The CRT startup code then iterates that table, calling each
34// initializer function.
35//
36// NOTE: User code should instead use .CRT$XCU to reliably run after std's initializer.
37// If you're reading this and would like a guarantee here, please
38// file an issue for discussion; currently we don't guarantee any functionality
39// before main.
40// See https://docs.microsoft.com/en-us/cpp/c-runtime-library/crt-initialization?view=msvc-170
41#[used]
42#[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`.
55unsafe extern "C" fn init() {
56 // In an exe this code is executed before main() so is single threaded.
57 // In a DLL the system's loader lock will be held thereby synchronizing
58 // access. So the same best practices apply here as they do to running in DllMain:
59 // https://docs.microsoft.com/en-us/windows/win32/dlls/dynamic-link-library-best-practices
60 //
61 // DO NOT do anything interesting or complicated in this function! DO NOT call
62 // any Rust functions or CRT functions if those functions touch any global state,
63 // because this function runs during global initialization. For example, DO NOT
64 // do any dynamic allocation, don't call LoadLibrary, etc.
65
66 // Attempt to preload the synch functions.
67 load_synch_functions();
68}
69
064997fb
FG
70/// Helper macro for creating CStrs from literals and symbol names.
71macro_rules! ansi_str {
72 (sym $ident:ident) => {{
73 #[allow(unused_unsafe)]
74 crate::sys::compat::const_cstr_from_bytes(concat!(stringify!($ident), "\0").as_bytes())
75 }};
76 ($lit:literal) => {{ crate::sys::compat::const_cstr_from_bytes(concat!($lit, "\0").as_bytes()) }};
77}
78
79/// Creates a C string wrapper from a byte slice, in a constant context.
80///
81/// This is a utility function used by the [`ansi_str`] macro.
82///
83/// # Panics
84///
85/// Panics if the slice is not null terminated or contains nulls, except as the last item
86pub(crate) const fn const_cstr_from_bytes(bytes: &'static [u8]) -> &'static CStr {
87 if !matches!(bytes.last(), Some(&0)) {
88 panic!("A CStr must be null terminated");
89 }
90 let mut i = 0;
91 // At this point `len()` is at least 1.
92 while i < bytes.len() - 1 {
93 if bytes[i] == 0 {
94 panic!("A CStr must not have interior nulls")
95 }
96 i += 1;
97 }
98 // SAFETY: The safety is ensured by the above checks.
99 unsafe { crate::ffi::CStr::from_bytes_with_nul_unchecked(bytes) }
100}
101
064997fb
FG
102/// Represents a loaded module.
103///
104/// Note that the modules std depends on must not be unloaded.
105/// Therefore a `Module` is always valid for the lifetime of std.
106#[derive(Copy, Clone)]
107pub(in crate::sys) struct Module(NonNull<c_void>);
108impl Module {
109 /// Try to get a handle to a loaded module.
110 ///
111 /// # SAFETY
112 ///
113 /// This should only be use for modules that exist for the lifetime of std
114 /// (e.g. kernel32 and ntdll).
115 pub unsafe fn new(name: &CStr) -> Option<Self> {
116 // SAFETY: A CStr is always null terminated.
117 let module = c::GetModuleHandleA(name.as_ptr());
118 NonNull::new(module).map(Self)
119 }
120
121 // Try to get the address of a function.
122 pub fn proc_address(self, name: &CStr) -> Option<NonNull<c_void>> {
123 // SAFETY:
124 // `self.0` will always be a valid module.
125 // A CStr is always null terminated.
126 let proc = unsafe { c::GetProcAddress(self.0.as_ptr(), name.as_ptr()) };
127 NonNull::new(proc)
128 }
129}
130
131/// Load a function or use a fallback implementation if that fails.
132macro_rules! compat_fn_with_fallback {
133 (pub static $module:ident: &CStr = $name:expr; $(
416331ca 134 $(#[$meta:meta])*
f2b60f7d 135 $vis:vis fn $symbol:ident($($argname:ident: $argtype:ty),*) -> $rettype:ty $fallback_body:block
064997fb
FG
136 )*) => (
137 pub static $module: &CStr = $name;
138 $(
416331ca 139 $(#[$meta])*
1b1a35ee 140 pub mod $symbol {
fc512014 141 #[allow(unused_imports)]
1b1a35ee 142 use super::*;
532ac7d7 143 use crate::mem;
064997fb
FG
144 use crate::ffi::CStr;
145 use crate::sync::atomic::{AtomicPtr, Ordering};
146 use crate::sys::compat::Module;
1b1a35ee 147
c1a9b12d
SL
148 type F = unsafe extern "system" fn($($argtype),*) -> $rettype;
149
064997fb
FG
150 /// `PTR` contains a function pointer to one of three functions.
151 /// It starts with the `load` function.
152 /// When that is called it attempts to load the requested symbol.
153 /// If it succeeds, `PTR` is set to the address of that symbol.
154 /// If it fails, then `PTR` is set to `fallback`.
155 static PTR: AtomicPtr<c_void> = AtomicPtr::new(load as *mut _);
156
157 unsafe extern "system" fn load($($argname: $argtype),*) -> $rettype {
158 let func = load_from_module(Module::new($module));
159 func($($argname),*)
04454e1e
FG
160 }
161
064997fb
FG
162 fn load_from_module(module: Option<Module>) -> F {
163 unsafe {
164 static SYMBOL_NAME: &CStr = ansi_str!(sym $symbol);
165 if let Some(f) = module.and_then(|m| m.proc_address(SYMBOL_NAME)) {
166 PTR.store(f.as_ptr(), Ordering::Relaxed);
167 mem::transmute(f)
168 } else {
169 PTR.store(fallback as *mut _, Ordering::Relaxed);
170 fallback
5869c6ff 171 }
1b1a35ee
XL
172 }
173 }
174
064997fb
FG
175 #[allow(unused_variables)]
176 unsafe extern "system" fn fallback($($argname: $argtype),*) -> $rettype {
177 $fallback_body
178 }
179
064997fb
FG
180 #[inline(always)]
181 pub unsafe fn call($($argname: $argtype),*) -> $rettype {
182 let func: F = mem::transmute(PTR.load(Ordering::Relaxed));
183 func($($argname),*)
184 }
185 }
186 $(#[$meta])*
f2b60f7d 187 $vis use $symbol::call as $symbol;
064997fb
FG
188 )*)
189}
190
f2b60f7d 191/// Optionally loaded functions.
064997fb 192///
f2b60f7d 193/// Actual loading of the function defers to $load_functions.
064997fb 194macro_rules! compat_fn_optional {
f2b60f7d 195 ($load_functions:expr;
064997fb 196 $(
f2b60f7d
FG
197 $(#[$meta:meta])*
198 $vis:vis fn $symbol:ident($($argname:ident: $argtype:ty),*) $(-> $rettype:ty)?;
199 )+) => (
200 $(
201 pub mod $symbol {
202 use super::*;
203 use crate::ffi::c_void;
204 use crate::mem;
205 use crate::ptr::{self, NonNull};
206 use crate::sync::atomic::{AtomicPtr, Ordering};
207
208 pub(in crate::sys) static PTR: AtomicPtr<c_void> = AtomicPtr::new(ptr::null_mut());
209
210 type F = unsafe extern "system" fn($($argtype),*) $(-> $rettype)?;
211
212 #[inline(always)]
213 pub fn option() -> Option<F> {
214 // Miri does not understand the way we do preloading
215 // therefore load the function here instead.
216 #[cfg(miri)] $load_functions;
217 NonNull::new(PTR.load(Ordering::Relaxed)).map(|f| unsafe { mem::transmute(f) })
923072b8 218 }
c1a9b12d 219 }
f2b60f7d
FG
220 )+
221 )
222}
c1a9b12d 223
f2b60f7d
FG
224/// Load all needed functions from "api-ms-win-core-synch-l1-2-0".
225pub(super) fn load_synch_functions() {
226 fn try_load() -> Option<()> {
227 const MODULE_NAME: &CStr = ansi_str!("api-ms-win-core-synch-l1-2-0");
228 const WAIT_ON_ADDRESS: &CStr = ansi_str!("WaitOnAddress");
229 const WAKE_BY_ADDRESS_SINGLE: &CStr = ansi_str!("WakeByAddressSingle");
230
231 // Try loading the library and all the required functions.
232 // If any step fails, then they all fail.
233 let library = unsafe { Module::new(MODULE_NAME) }?;
234 let wait_on_address = library.proc_address(WAIT_ON_ADDRESS)?;
235 let wake_by_address_single = library.proc_address(WAKE_BY_ADDRESS_SINGLE)?;
236
237 c::WaitOnAddress::PTR.store(wait_on_address.as_ptr(), Ordering::Relaxed);
238 c::WakeByAddressSingle::PTR.store(wake_by_address_single.as_ptr(), Ordering::Relaxed);
239 Some(())
240 }
1b1a35ee 241
f2b60f7d 242 try_load();
c1a9b12d 243}