]>
Commit | Line | Data |
---|---|---|
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 |
22 | use crate::ffi::{c_void, CStr}; |
23 | use crate::ptr::NonNull; | |
f2b60f7d | 24 | use crate::sync::atomic::Ordering; |
064997fb FG |
25 | use 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"] | |
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 | ||
064997fb FG |
70 | /// Helper macro for creating CStrs from literals and symbol names. |
71 | macro_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 | |
86 | pub(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)] | |
107 | pub(in crate::sys) struct Module(NonNull<c_void>); | |
108 | impl 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. | |
132 | macro_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 | 194 | macro_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". |
225 | pub(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 | } |