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