]> git.proxmox.com Git - rustc.git/blame - library/std/src/sys/windows/compat.rs
New upstream version 1.48.0~beta.8+dfsg1
[rustc.git] / library / std / src / sys / windows / compat.rs
CommitLineData
c1a9b12d
SL
1//! A "compatibility layer" for spanning XP and Windows 7
2//!
3//! The standard library currently binds many functions that are not available
4//! on Windows XP, but we would also like to support building executables that
5//! run on XP. To do this we specify all non-XP APIs as having a fallback
6//! implementation to do something reasonable.
7//!
8//! This dynamic runtime detection of whether a function is available is
9//! implemented with `GetModuleHandle` and `GetProcAddress` paired with a
10//! static-per-function which caches the result of the first check. In this
11//! manner we pay a semi-large one-time cost up front for detecting whether a
12//! function is available but afterwards it's just a load and a jump.
13
532ac7d7 14use crate::ffi::CString;
532ac7d7 15use crate::sys::c;
c1a9b12d
SL
16
17pub fn lookup(module: &str, symbol: &str) -> Option<usize> {
7453a54e 18 let mut module: Vec<u16> = module.encode_utf16().collect();
c1a9b12d
SL
19 module.push(0);
20 let symbol = CString::new(symbol).unwrap();
21 unsafe {
92a42be0
SL
22 let handle = c::GetModuleHandleW(module.as_ptr());
23 match c::GetProcAddress(handle, symbol.as_ptr()) as usize {
c1a9b12d
SL
24 0 => None,
25 n => Some(n),
26 }
27 }
28}
29
c1a9b12d 30macro_rules! compat_fn {
1b1a35ee 31 ($module:literal: $(
416331ca 32 $(#[$meta:meta])*
1b1a35ee 33 pub fn $symbol:ident($($argname:ident: $argtype:ty),*) -> $rettype:ty $body:block
c1a9b12d 34 )*) => ($(
416331ca 35 $(#[$meta])*
1b1a35ee
XL
36 pub mod $symbol {
37 use super::*;
532ac7d7
XL
38 use crate::sync::atomic::{AtomicUsize, Ordering};
39 use crate::mem;
1b1a35ee 40
c1a9b12d
SL
41 type F = unsafe extern "system" fn($($argtype),*) -> $rettype;
42
43 static PTR: AtomicUsize = AtomicUsize::new(0);
44
1b1a35ee
XL
45 #[allow(unused_variables)]
46 unsafe extern "system" fn fallback($($argname: $argtype),*) -> $rettype $body
47
48 /// This address is stored in `PTR` to incidate an unavailable API.
49 ///
50 /// This way, call() will end up calling fallback() if it is unavailable.
51 ///
52 /// This is a `static` to avoid rustc duplicating `fn fallback()`
53 /// into both load() and is_available(), which would break
54 /// is_available()'s comparison. By using the same static variable
55 /// in both places, they'll refer to the same (copy of the)
56 /// function.
57 ///
58 /// LLVM merging the address of fallback with other functions
59 /// (because of unnamed_addr) is fine, since it's only compared to
60 /// an address from GetProcAddress from an external dll.
61 static FALLBACK: F = fallback;
62
63 #[cold]
c1a9b12d 64 fn load() -> usize {
1b1a35ee
XL
65 // There is no locking here. It's okay if this is executed by multiple threads in
66 // parallel. `lookup` will result in the same value, and it's okay if they overwrite
67 // eachothers result as long as they do so atomically. We don't need any guarantees
68 // about memory ordering, as this involves just a single atomic variable which is
69 // not used to protect or order anything else.
70 let addr = crate::sys::compat::lookup($module, stringify!($symbol))
71 .unwrap_or(FALLBACK as usize);
72 PTR.store(addr, Ordering::Relaxed);
73 addr
c1a9b12d 74 }
1b1a35ee
XL
75
76 fn addr() -> usize {
77 match PTR.load(Ordering::Relaxed) {
78 0 => load(),
79 addr => addr,
80 }
81 }
82
83 #[allow(dead_code)]
84 pub fn is_available() -> bool {
85 addr() != FALLBACK as usize
c1a9b12d
SL
86 }
87
1b1a35ee
XL
88 pub unsafe fn call($($argname: $argtype),*) -> $rettype {
89 mem::transmute::<usize, F>(addr())($($argname),*)
90 }
c1a9b12d 91 }
1b1a35ee
XL
92
93 pub use $symbol::call as $symbol;
c1a9b12d
SL
94 )*)
95}