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