]>
Commit | Line | Data |
---|---|---|
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 | 14 | use crate::ffi::CString; |
532ac7d7 | 15 | use crate::sys::c; |
c1a9b12d SL |
16 | |
17 | pub 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 | 30 | macro_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 | } |