      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

    //! 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 implementation uses a static initializer to look up the DLL entry
    //! points. The CRT (C runtime) executes static initializers before `main`
    //! is called (for binaries) and before `DllMain` is called (for DLLs).
    //! This is the ideal time to look up DLL imports, because we are guaranteed
    //! that no other threads will attempt to call these entry points. Thus,
    //! we can look up the imports and store them in `static mut` fields
    //! without any synchronization.
    //!
    //! This has an additional advantage: Because the DLL import lookup happens
    //! at module initialization, the cost of these lookups is deterministic,
    //! and is removed from the code paths that actually call the DLL imports.
    //! That is, there is no unpredictable "cache miss" that occurs when calling
    //! a DLL import. For applications that benefit from predictable delays,
    //! this is a benefit. This also eliminates the comparison-and-branch
    //! from the hot path.
    //!
    //! Currently, the standard library uses only a small number of dynamic
    //! DLL imports. If this number grows substantially, then the cost of
    //! performing all of the lookups at initialization time might become
    //! substantial.
    //!
    //! The mechanism of registering a static initializer with the CRT is
    //! documented in
    //! [CRT Initialization](https://docs.microsoft.com/en-us/cpp/c-runtime-library/crt-initialization?view=msvc-160).
    //! It works by contributing a global symbol to the `.CRT$XCU` section.
    //! The linker builds a table of all static initializer functions.
    //! The CRT startup code then iterates that table, calling each
    //! initializer function.
    //!
    //! # **WARNING!!*
    //! The environment that a static initializer function runs in is highly
    //! constrained. There are **many** restrictions on what static initializers
    //! can safely do. Static initializer functions **MUST NOT** do any of the
    //! following (this list is not comprehensive):
    //! * touch any other static field that is used by a different static
    //!   initializer, because the order that static initializers run in
    //!   is not defined.
    //! * call `LoadLibrary` or any other function that acquires the DLL
    //!   loader lock.
    //! * call any Rust function or CRT function that touches any static
    //!   (global) state.

    macro_rules! compat_fn {
        ($module:literal: $(
            $(#[$meta:meta])*
            pub fn $symbol:ident($($argname:ident: $argtype:ty),*) -> $rettype:ty $fallback_body:block
        )*) => ($(
            $(#[$meta])*
            pub mod $symbol {
                #[allow(unused_imports)]
                use super::*;
                use crate::mem;

                type F = unsafe extern "system" fn($($argtype),*) -> $rettype;

                /// Points to the DLL import, or the fallback function.
                ///
                /// This static can be an ordinary, unsynchronized, mutable static because
                /// we guarantee that all of the writes finish during CRT initialization,
                /// and all of the reads occur after CRT initialization.
                static mut PTR: Option<F> = None;

                /// This symbol is what allows the CRT to find the `init` function and call it.
                /// It is marked `#[used]` because otherwise Rust would assume that it was not
                /// used, and would remove it.
                #[used]
                #[link_section = ".CRT$XCU"]
                static INIT_TABLE_ENTRY: unsafe extern "C" fn() = init;

                unsafe extern "C" fn init() {
                    // There is no locking here. This code is executed before main() is entered, and
                    // is guaranteed to be single-threaded.
                    //
                    // 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.
                    let module_name: *const u8 = concat!($module, "\0").as_ptr();
                    let symbol_name: *const u8 = concat!(stringify!($symbol), "\0").as_ptr();
                    let module_handle = $crate::sys::c::GetModuleHandleA(module_name as *const i8);
                    if !module_handle.is_null() {
                        match $crate::sys::c::GetProcAddress(module_handle, symbol_name as *const i8) as usize {
                            0 => {}
                            n => {
                                PTR = Some(mem::transmute::<usize, F>(n));
                            }
                        }
                    }
                }

                #[allow(dead_code)]
                pub fn option() -> Option<F> {
                    unsafe { PTR }
                }

                #[allow(dead_code)]
                pub unsafe fn call($($argname: $argtype),*) -> $rettype {
                    if let Some(ptr) = PTR {
                        ptr($($argname),*)
                    } else {
                        $fallback_body
                    }
                }
            }

            $(#[$meta])*
            pub use $symbol::call as $symbol;
        )*)
    }