about summary refs log tree commit diff
path: root/library/std/src/sys/windows/compat.rs
diff options
context:
space:
mode:
Diffstat (limited to 'library/std/src/sys/windows/compat.rs')
-rw-r--r--library/std/src/sys/windows/compat.rs72
1 files changed, 72 insertions, 0 deletions
diff --git a/library/std/src/sys/windows/compat.rs b/library/std/src/sys/windows/compat.rs
new file mode 100644
index 00000000000..d6d433f9d08
--- /dev/null
+++ b/library/std/src/sys/windows/compat.rs
@@ -0,0 +1,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),*)
+        }
+    )*)
+}