     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

    //! A "compatibility layer" for spanning XP and Windows 7
    //!
    //! The standard library currently binds many functions that are not available
    //! on Windows XP, but we would also like to support building executables that
    //! run on XP. To do this we specify all non-XP APIs as having a fallback
    //! implementation to do something reasonable.
    //!
    //! This dynamic runtime detection of whether a function is available is
    //! implemented with `GetModuleHandle` and `GetProcAddress` paired with a
    //! static-per-function which caches the result of the first check. In this
    //! manner we pay a semi-large one-time cost up front for detecting whether a
    //! function is available but afterwards it's just a load and a jump.

    use crate::ffi::CString;
    use crate::sync::atomic::{AtomicUsize, Ordering};
    use crate::sys::c;

    pub fn lookup(module: &str, symbol: &str) -> Option<usize> {
        let mut module: Vec<u16> = module.encode_utf16().collect();
        module.push(0);
        let symbol = CString::new(symbol).unwrap();
        unsafe {
            let handle = c::GetModuleHandleW(module.as_ptr());
            match c::GetProcAddress(handle, symbol.as_ptr()) as usize {
                0 => None,
                n => Some(n),
            }
        }
    }

    pub fn store_func(ptr: &AtomicUsize, module: &str, symbol: &str, fallback: usize) -> usize {
        let value = lookup(module, symbol).unwrap_or(fallback);
        ptr.store(value, Ordering::SeqCst);
        value
    }

    macro_rules! compat_fn {
        ($module:ident: $(
            $(#[$meta:meta])*
            pub fn $symbol:ident($($argname:ident: $argtype:ty),*)
                                      -> $rettype:ty {
                $($body:expr);*
            }
        )*) => ($(
            #[allow(unused_variables)]
            $(#[$meta])*
            pub unsafe fn $symbol($($argname: $argtype),*) -> $rettype {
                use crate::sync::atomic::{AtomicUsize, Ordering};
                use crate::mem;
                type F = unsafe extern "system" fn($($argtype),*) -> $rettype;

                static PTR: AtomicUsize = AtomicUsize::new(0);

                fn load() -> usize {
                    crate::sys::compat::store_func(&PTR,
                                              stringify!($module),
                                              stringify!($symbol),
                                              fallback as usize)
                }
                unsafe extern "system" fn fallback($($argname: $argtype),*)
                                                   -> $rettype {
                    $($body);*
                }

                let addr = match PTR.load(Ordering::SeqCst) {
                    0 => load(),
                    n => n,
                };
                mem::transmute::<usize, F>(addr)($($argname),*)
            }
        )*)
    }