]>
Commit | Line | Data |
---|---|---|
7453a54e SL |
1 | //! Support for "weak linkage" to symbols on Unix |
2 | //! | |
3 | //! Some I/O operations we do in libstd require newer versions of OSes but we | |
4 | //! need to maintain binary compatibility with older releases for now. In order | |
5 | //! to use the new functionality when available we use this module for | |
6 | //! detection. | |
7 | //! | |
8 | //! One option to use here is weak linkage, but that is unfortunately only | |
9 | //! really workable on Linux. Hence, use dlsym to get the symbol value at | |
10 | //! runtime. This is also done for compatibility with older versions of glibc, | |
11 | //! and to avoid creating dependencies on GLIBC_PRIVATE symbols. It assumes that | |
12 | //! we've been dynamically linked to the library the symbol comes from, but that | |
13 | //! is currently always the case for things like libpthread/libc. | |
14 | //! | |
15 | //! A long time ago this used weak linkage for the __pthread_get_minstack | |
16 | //! symbol, but that caused Debian to detect an unnecessarily strict versioned | |
17 | //! dependency on libc6 (#23628). | |
18 | ||
532ac7d7 XL |
19 | use crate::ffi::CStr; |
20 | use crate::marker; | |
21 | use crate::mem; | |
22 | use crate::sync::atomic::{AtomicUsize, Ordering}; | |
7453a54e SL |
23 | |
24 | macro_rules! weak { | |
25 | (fn $name:ident($($t:ty),*) -> $ret:ty) => ( | |
532ac7d7 XL |
26 | static $name: crate::sys::weak::Weak<unsafe extern fn($($t),*) -> $ret> = |
27 | crate::sys::weak::Weak::new(concat!(stringify!($name), '\0')); | |
7453a54e SL |
28 | ) |
29 | } | |
30 | ||
31 | pub struct Weak<F> { | |
32 | name: &'static str, | |
33 | addr: AtomicUsize, | |
34 | _marker: marker::PhantomData<F>, | |
35 | } | |
36 | ||
37 | impl<F> Weak<F> { | |
38 | pub const fn new(name: &'static str) -> Weak<F> { | |
60c5eb7d | 39 | Weak { name, addr: AtomicUsize::new(1), _marker: marker::PhantomData } |
7453a54e SL |
40 | } |
41 | ||
9fa01778 | 42 | pub fn get(&self) -> Option<F> { |
7453a54e SL |
43 | assert_eq!(mem::size_of::<F>(), mem::size_of::<usize>()); |
44 | unsafe { | |
45 | if self.addr.load(Ordering::SeqCst) == 1 { | |
46 | self.addr.store(fetch(self.name), Ordering::SeqCst); | |
47 | } | |
9fa01778 XL |
48 | match self.addr.load(Ordering::SeqCst) { |
49 | 0 => None, | |
50 | addr => Some(mem::transmute_copy::<usize, F>(&addr)), | |
7453a54e SL |
51 | } |
52 | } | |
53 | } | |
54 | } | |
55 | ||
56 | unsafe fn fetch(name: &str) -> usize { | |
9fa01778 | 57 | let name = match CStr::from_bytes_with_nul(name.as_bytes()) { |
7453a54e SL |
58 | Ok(cstr) => cstr, |
59 | Err(..) => return 0, | |
60 | }; | |
54a0048b | 61 | libc::dlsym(libc::RTLD_DEFAULT, name.as_ptr()) as usize |
7453a54e | 62 | } |
0731742a XL |
63 | |
64 | #[cfg(not(target_os = "linux"))] | |
65 | macro_rules! syscall { | |
66 | (fn $name:ident($($arg_name:ident: $t:ty),*) -> $ret:ty) => ( | |
67 | unsafe fn $name($($arg_name: $t),*) -> $ret { | |
0731742a XL |
68 | use super::os; |
69 | ||
70 | weak! { fn $name($($t),*) -> $ret } | |
71 | ||
72 | if let Some(fun) = $name.get() { | |
73 | fun($($arg_name),*) | |
74 | } else { | |
75 | os::set_errno(libc::ENOSYS); | |
76 | -1 | |
77 | } | |
78 | } | |
79 | ) | |
80 | } | |
81 | ||
82 | #[cfg(target_os = "linux")] | |
83 | macro_rules! syscall { | |
84 | (fn $name:ident($($arg_name:ident: $t:ty),*) -> $ret:ty) => ( | |
85 | unsafe fn $name($($arg_name:$t),*) -> $ret { | |
86 | // This looks like a hack, but concat_idents only accepts idents | |
87 | // (not paths). | |
88 | use libc::*; | |
89 | ||
90 | syscall( | |
91 | concat_idents!(SYS_, $name), | |
92 | $($arg_name as c_long),* | |
93 | ) as $ret | |
94 | } | |
95 | ) | |
96 | } |