1 //! A "compatibility layer" for supporting older versions of Windows
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
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.
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.
22 use crate::ffi
::{c_void, CStr}
;
23 use crate::ptr
::NonNull
;
24 use crate::sync
::atomic
::Ordering
;
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).
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.
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
40 // See https://docs.microsoft.com/en-us/cpp/c-runtime-library/crt-initialization?view=msvc-170
42 #[link_section = ".CRT$XCT"]
43 static INIT_TABLE_ENTRY
: unsafe extern "C" fn() = init
;
45 /// Preload some imported functions.
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.
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.
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
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.
66 // Attempt to preload the synch functions.
67 load_synch_functions();
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())
76 ($lit
:literal
) => {{ crate::sys::compat::const_cstr_from_bytes(concat!($lit, "\0").as_bytes()) }
};
79 /// Creates a C string wrapper from a byte slice, in a constant context.
81 /// This is a utility function used by the [`ansi_str`] macro.
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");
91 // At this point `len()` is at least 1.
92 while i
< bytes
.len() - 1 {
94 panic
!("A CStr must not have interior nulls")
98 // SAFETY: The safety is ensured by the above checks.
99 unsafe { crate::ffi::CStr::from_bytes_with_nul_unchecked(bytes) }
102 /// Represents a loaded module.
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
>);
109 /// Try to get a handle to a loaded module.
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)
121 // Try to get the address of a function.
122 pub fn proc_address(self, name
: &CStr
) -> Option
<NonNull
<c_void
>> {
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()) }
;
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
; $
(
135 $vis
:vis
fn $symbol
:ident($
($argname
:ident
: $argtype
:ty
),*) -> $rettype
:ty $fallback_body
:block
137 pub static $module
: &CStr
= $name
;
141 #[allow(unused_imports)]
144 use crate::ffi
::CStr
;
145 use crate::sync
::atomic
::{AtomicPtr, Ordering}
;
146 use crate::sys
::compat
::Module
;
148 type F
= unsafe extern "system" fn($
($argtype
),*) -> $rettype
;
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 _
);
157 unsafe extern "system" fn load($
($argname
: $argtype
),*) -> $rettype
{
158 let func
= load_from_module(Module
::new($module
));
162 fn load_from_module(module
: Option
<Module
>) -> F
{
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
);
169 PTR
.store(fallback
as *mut _
, Ordering
::Relaxed
);
175 #[allow(unused_variables)]
176 unsafe extern "system" fn fallback($
($argname
: $argtype
),*) -> $rettype
{
181 pub unsafe fn call($
($argname
: $argtype
),*) -> $rettype
{
182 let func
: F
= mem
::transmute(PTR
.load(Ordering
::Relaxed
));
187 $vis
use $symbol
::call
as $symbol
;
191 /// Optionally loaded functions.
193 /// Actual loading of the function defers to $load_functions.
194 macro_rules
! compat_fn_optional
{
195 ($load_functions
:expr
;
198 $vis
:vis
fn $symbol
:ident($
($argname
:ident
: $argtype
:ty
),*) $
(-> $rettype
:ty
)?
;
203 use crate::ffi
::c_void
;
205 use crate::ptr
::{self, NonNull}
;
206 use crate::sync
::atomic
::{AtomicPtr, Ordering}
;
208 pub(in crate::sys
) static PTR
: AtomicPtr
<c_void
> = AtomicPtr
::new(ptr
::null_mut());
210 type F
= unsafe extern "system" fn($
($argtype
),*) $
(-> $rettype
)?
;
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) }
)
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");
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
)?
;
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
);