xref: /drstd/src/std/sys/windows/compat.rs (revision 0fe3ff0054d3aec7fbf9bddecfecb10bc7d23a51)
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 
22 use crate::std::ffi::{c_void, CStr};
23 use crate::std::ptr::NonNull;
24 use crate::std::sync::atomic::Ordering;
25 use crate::std::sys::c;
26 
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"]
43 static 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 unsafe 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 
70 /// Helper macro for creating CStrs from literals and symbol names.
71 macro_rules! ansi_str {
72     (sym $ident:ident) => {{
73         crate::std::sys::compat::const_cstr_from_bytes(concat!(stringify!($ident), "\0").as_bytes())
74     }};
75     ($lit:literal) => {{
76         crate::std::sys::compat::const_cstr_from_bytes(concat!($lit, "\0").as_bytes())
77     }};
78 }
79 
80 /// Creates a C string wrapper from a byte slice, in a constant context.
81 ///
82 /// This is a utility function used by the [`ansi_str`] macro.
83 ///
84 /// # Panics
85 ///
86 /// Panics if the slice is not null terminated or contains nulls, except as the last item
87 pub(crate) const fn const_cstr_from_bytes(bytes: &'static [u8]) -> &'static CStr {
88     if !matches!(bytes.last(), Some(&0)) {
89         panic!("A CStr must be null terminated");
90     }
91     let mut i = 0;
92     // At this point `len()` is at least 1.
93     while i < bytes.len() - 1 {
94         if bytes[i] == 0 {
95             panic!("A CStr must not have interior nulls")
96         }
97         i += 1;
98     }
99     // SAFETY: The safety is ensured by the above checks.
100     unsafe { crate::std::ffi::CStr::from_bytes_with_nul_unchecked(bytes) }
101 }
102 
103 /// Represents a loaded module.
104 ///
105 /// Note that the modules std depends on must not be unloaded.
106 /// Therefore a `Module` is always valid for the lifetime of std.
107 #[derive(Copy, Clone)]
108 pub(in crate::std::sys) struct Module(NonNull<c_void>);
109 impl Module {
110     /// Try to get a handle to a loaded module.
111     ///
112     /// # SAFETY
113     ///
114     /// This should only be use for modules that exist for the lifetime of std
115     /// (e.g. kernel32 and ntdll).
116     pub unsafe fn new(name: &CStr) -> Option<Self> {
117         // SAFETY: A CStr is always null terminated.
118         let module = c::GetModuleHandleA(name.as_ptr().cast::<u8>());
119         NonNull::new(module).map(Self)
120     }
121 
122     // Try to get the address of a function.
123     pub fn proc_address(self, name: &CStr) -> Option<NonNull<c_void>> {
124         unsafe {
125             // SAFETY:
126             // `self.0` will always be a valid module.
127             // A CStr is always null terminated.
128             let proc = c::GetProcAddress(self.0.as_ptr(), name.as_ptr().cast::<u8>());
129             // SAFETY: `GetProcAddress` returns None on null.
130             proc.map(|p| NonNull::new_unchecked(p as *mut c_void))
131         }
132     }
133 }
134 
135 /// Load a function or use a fallback implementation if that fails.
136 macro_rules! compat_fn_with_fallback {
137     (pub static $module:ident: &CStr = $name:expr; $(
138         $(#[$meta:meta])*
139         $vis:vis fn $symbol:ident($($argname:ident: $argtype:ty),*) -> $rettype:ty $fallback_body:block
140     )*) => (
141         pub static $module: &CStr = $name;
142     $(
143         $(#[$meta])*
144         pub mod $symbol {
145             #[allow(unused_imports)]
146             use super::*;
147             use crate::std::mem;
148             use crate::std::ffi::CStr;
149             use crate::std::sync::atomic::{AtomicPtr, Ordering};
150             use crate::std::sys::compat::Module;
151 
152             type F = unsafe extern "system" fn($($argtype),*) -> $rettype;
153 
154             /// `PTR` contains a function pointer to one of three functions.
155             /// It starts with the `load` function.
156             /// When that is called it attempts to load the requested symbol.
157             /// If it succeeds, `PTR` is set to the address of that symbol.
158             /// If it fails, then `PTR` is set to `fallback`.
159             static PTR: AtomicPtr<c_void> = AtomicPtr::new(load as *mut _);
160 
161             unsafe extern "system" fn load($($argname: $argtype),*) -> $rettype {
162                 let func = load_from_module(Module::new($module));
163                 func($($argname),*)
164             }
165 
166             fn load_from_module(module: Option<Module>) -> F {
167                 unsafe {
168                     static SYMBOL_NAME: &CStr = ansi_str!(sym $symbol);
169                     if let Some(f) = module.and_then(|m| m.proc_address(SYMBOL_NAME)) {
170                         PTR.store(f.as_ptr(), Ordering::Relaxed);
171                         mem::transmute(f)
172                     } else {
173                         PTR.store(fallback as *mut _, Ordering::Relaxed);
174                         fallback
175                     }
176                 }
177             }
178 
179             #[allow(unused_variables)]
180             unsafe extern "system" fn fallback($($argname: $argtype),*) -> $rettype {
181                 $fallback_body
182             }
183 
184             #[inline(always)]
185             pub unsafe fn call($($argname: $argtype),*) -> $rettype {
186                 let func: F = mem::transmute(PTR.load(Ordering::Relaxed));
187                 func($($argname),*)
188             }
189         }
190         $(#[$meta])*
191         $vis use $symbol::call as $symbol;
192     )*)
193 }
194 
195 /// Optionally loaded functions.
196 ///
197 /// Actual loading of the function defers to $load_functions.
198 macro_rules! compat_fn_optional {
199     ($load_functions:expr;
200     $(
201         $(#[$meta:meta])*
202         $vis:vis fn $symbol:ident($($argname:ident: $argtype:ty),*) $(-> $rettype:ty)?;
203     )+) => (
204         $(
205             pub mod $symbol {
206                 #[allow(unused_imports)]
207                 use super::*;
208                 use crate::std::ffi::c_void;
209                 use crate::std::mem;
210                 use crate::std::ptr::{self, NonNull};
211                 use crate::std::sync::atomic::{AtomicPtr, Ordering};
212 
213                 pub(in crate::std::sys) static PTR: AtomicPtr<c_void> = AtomicPtr::new(ptr::null_mut());
214 
215                 type F = unsafe extern "system" fn($($argtype),*) $(-> $rettype)?;
216 
217                 #[inline(always)]
218                 pub fn option() -> Option<F> {
219                     // Miri does not understand the way we do preloading
220                     // therefore load the function here instead.
221                     #[cfg(miri)] $load_functions;
222                     NonNull::new(PTR.load(Ordering::Relaxed)).map(|f| unsafe { mem::transmute(f) })
223                 }
224             }
225         )+
226     )
227 }
228 
229 /// Load all needed functions from "api-ms-win-core-synch-l1-2-0".
230 pub(super) fn load_synch_functions() {
231     fn try_load() -> Option<()> {
232         const MODULE_NAME: &CStr = ansi_str!("api-ms-win-core-synch-l1-2-0");
233         const WAIT_ON_ADDRESS: &CStr = ansi_str!("WaitOnAddress");
234         const WAKE_BY_ADDRESS_SINGLE: &CStr = ansi_str!("WakeByAddressSingle");
235 
236         // Try loading the library and all the required functions.
237         // If any step fails, then they all fail.
238         let library = unsafe { Module::new(MODULE_NAME) }?;
239         let wait_on_address = library.proc_address(WAIT_ON_ADDRESS)?;
240         let wake_by_address_single = library.proc_address(WAKE_BY_ADDRESS_SINGLE)?;
241 
242         c::WaitOnAddress::PTR.store(wait_on_address.as_ptr(), Ordering::Relaxed);
243         c::WakeByAddressSingle::PTR.store(wake_by_address_single.as_ptr(), Ordering::Relaxed);
244         Some(())
245     }
246 
247     try_load();
248 }
249