]>
Commit | Line | Data |
---|---|---|
064997fb FG |
1 | // Implementation derived from `weak` in Rust's |
2 | // library/std/src/sys/unix/weak.rs at revision | |
3 | // fd0cb0cdc21dd9c06025277d772108f8d42cb25f. | |
4 | ||
5 | //! Support for "weak linkage" to symbols on Unix | |
6 | //! | |
7 | //! Some I/O operations we do in libstd require newer versions of OSes but we | |
8 | //! need to maintain binary compatibility with older releases for now. In order | |
9 | //! to use the new functionality when available we use this module for | |
10 | //! detection. | |
11 | //! | |
12 | //! One option to use here is weak linkage, but that is unfortunately only | |
13 | //! really workable on Linux. Hence, use dlsym to get the symbol value at | |
14 | //! runtime. This is also done for compatibility with older versions of glibc, | |
15 | //! and to avoid creating dependencies on `GLIBC_PRIVATE` symbols. It assumes | |
16 | //! that we've been dynamically linked to the library the symbol comes from, | |
17 | //! but that is currently always the case for things like libpthread/libc. | |
18 | //! | |
19 | //! A long time ago this used weak linkage for the `__pthread_get_minstack` | |
20 | //! symbol, but that caused Debian to detect an unnecessarily strict versioned | |
21 | //! dependency on libc6 (#23628). | |
22 | ||
23 | // There are a variety of `#[cfg]`s controlling which targets are involved in | |
24 | // each instance of `weak!` and `syscall!`. Rather than trying to unify all of | |
25 | // that, we'll just allow that some unix targets don't use this module at all. | |
26 | #![allow(dead_code, unused_macros)] | |
27 | #![allow(clippy::doc_markdown)] | |
28 | ||
29 | use core::ffi::c_void; | |
30 | use core::ptr::null_mut; | |
31 | use core::sync::atomic::{self, AtomicPtr, Ordering}; | |
32 | use core::{marker, mem}; | |
33 | use rustix::ffi::CStr; | |
34 | ||
35 | const NULL: *mut c_void = null_mut(); | |
36 | const INVALID: *mut c_void = 1 as *mut c_void; | |
37 | ||
38 | macro_rules! weak { | |
39 | (fn $name:ident($($t:ty),*) -> $ret:ty) => ( | |
40 | #[allow(non_upper_case_globals)] | |
41 | static $name: $crate::weak::Weak<unsafe extern fn($($t),*) -> $ret> = | |
42 | $crate::weak::Weak::new(concat!(stringify!($name), '\0')); | |
43 | ) | |
44 | } | |
45 | ||
46 | pub(crate) struct Weak<F> { | |
47 | name: &'static str, | |
48 | addr: AtomicPtr<c_void>, | |
49 | _marker: marker::PhantomData<F>, | |
50 | } | |
51 | ||
52 | impl<F> Weak<F> { | |
53 | pub(crate) const fn new(name: &'static str) -> Self { | |
54 | Self { | |
55 | name, | |
56 | addr: AtomicPtr::new(INVALID), | |
57 | _marker: marker::PhantomData, | |
58 | } | |
59 | } | |
60 | ||
61 | pub(crate) fn get(&self) -> Option<F> { | |
62 | assert_eq!(mem::size_of::<F>(), mem::size_of::<usize>()); | |
63 | unsafe { | |
64 | // Relaxed is fine here because we fence before reading through the | |
65 | // pointer (see the comment below). | |
66 | match self.addr.load(Ordering::Relaxed) { | |
67 | INVALID => self.initialize(), | |
68 | NULL => None, | |
69 | addr => { | |
70 | let func = mem::transmute_copy::<*mut c_void, F>(&addr); | |
71 | // The caller is presumably going to read through this value | |
72 | // (by calling the function we've dlsymed). This means we'd | |
73 | // need to have loaded it with at least C11's consume | |
74 | // ordering in order to be guaranteed that the data we read | |
75 | // from the pointer isn't from before the pointer was | |
76 | // stored. Rust has no equivalent to memory_order_consume, | |
77 | // so we use an acquire fence (sorry, ARM). | |
78 | // | |
79 | // Now, in practice this likely isn't needed even on CPUs | |
80 | // where relaxed and consume mean different things. The | |
81 | // symbols we're loading are probably present (or not) at | |
82 | // init, and even if they aren't the runtime dynamic loader | |
83 | // is extremely likely have sufficient barriers internally | |
84 | // (possibly implicitly, for example the ones provided by | |
85 | // invoking `mprotect`). | |
86 | // | |
87 | // That said, none of that's *guaranteed*, and so we fence. | |
88 | atomic::fence(Ordering::Acquire); | |
89 | Some(func) | |
90 | } | |
91 | } | |
92 | } | |
93 | } | |
94 | ||
95 | // Cold because it should only happen during first-time initialization. | |
96 | #[cold] | |
97 | unsafe fn initialize(&self) -> Option<F> { | |
98 | let val = fetch(self.name); | |
99 | // This synchronizes with the acquire fence in `get`. | |
100 | self.addr.store(val, Ordering::Release); | |
101 | ||
102 | match val { | |
103 | NULL => None, | |
104 | addr => Some(mem::transmute_copy::<*mut c_void, F>(&addr)), | |
105 | } | |
106 | } | |
107 | } | |
108 | ||
109 | unsafe fn fetch(name: &str) -> *mut c_void { | |
110 | let name = match CStr::from_bytes_with_nul(name.as_bytes()) { | |
111 | Ok(c_str) => c_str, | |
112 | Err(..) => return null_mut(), | |
113 | }; | |
114 | libc::dlsym(libc::RTLD_DEFAULT, name.as_ptr().cast()) | |
115 | } | |
116 | ||
117 | #[cfg(not(any(target_os = "android", target_os = "linux")))] | |
118 | macro_rules! syscall { | |
119 | (fn $name:ident($($arg_name:ident: $t:ty),*) -> $ret:ty) => ( | |
120 | unsafe fn $name($($arg_name: $t),*) -> $ret { | |
121 | weak! { fn $name($($t),*) -> $ret } | |
122 | ||
123 | if let Some(fun) = $name.get() { | |
124 | fun($($arg_name),*) | |
125 | } else { | |
126 | errno::set_errno(errno::Errno(libc::ENOSYS)); | |
127 | -1 | |
128 | } | |
129 | } | |
130 | ) | |
131 | } | |
132 | ||
133 | #[cfg(any(target_os = "android", target_os = "linux"))] | |
134 | macro_rules! syscall { | |
135 | (fn $name:ident($($arg_name:ident: $t:ty),*) -> $ret:ty) => ( | |
136 | unsafe fn $name($($arg_name:$t),*) -> $ret { | |
137 | // This looks like a hack, but concat_idents only accepts idents | |
138 | // (not paths). | |
139 | use libc::*; | |
140 | ||
141 | trait AsSyscallArg { | |
142 | type SyscallArgType; | |
143 | fn as_syscall_arg(self) -> Self::SyscallArgType; | |
144 | } | |
145 | ||
146 | // Pass pointer types as pointers, to preserve provenance. | |
147 | impl<T> AsSyscallArg for *mut T { | |
148 | type SyscallArgType = *mut T; | |
149 | fn as_syscall_arg(self) -> Self::SyscallArgType { self } | |
150 | } | |
151 | impl<T> AsSyscallArg for *const T { | |
152 | type SyscallArgType = *const T; | |
153 | fn as_syscall_arg(self) -> Self::SyscallArgType { self } | |
154 | } | |
155 | ||
156 | // Pass `BorrowedFd` values as the integer value. | |
157 | impl AsSyscallArg for $crate::fd::BorrowedFd<'_> { | |
158 | type SyscallArgType = c::c_long; | |
159 | fn as_syscall_arg(self) -> Self::SyscallArgType { | |
160 | $crate::fd::AsRawFd::as_raw_fd(&self) as _ | |
161 | } | |
162 | } | |
163 | ||
164 | // Coerce integer values into `c_long`. | |
165 | impl AsSyscallArg for i32 { | |
166 | type SyscallArgType = c::c_long; | |
167 | fn as_syscall_arg(self) -> Self::SyscallArgType { self as _ } | |
168 | } | |
169 | impl AsSyscallArg for u32 { | |
170 | type SyscallArgType = c::c_long; | |
171 | fn as_syscall_arg(self) -> Self::SyscallArgType { self as _ } | |
172 | } | |
173 | impl AsSyscallArg for usize { | |
174 | type SyscallArgType = c::c_long; | |
175 | fn as_syscall_arg(self) -> Self::SyscallArgType { self as _ } | |
176 | } | |
177 | ||
178 | syscall( | |
179 | concat_idents!(SYS_, $name), | |
180 | $($arg_name.as_syscall_arg()),* | |
181 | ) as $ret | |
182 | } | |
183 | ) | |
184 | } | |
185 | ||
186 | macro_rules! weakcall { | |
187 | ($vis:vis fn $name:ident($($arg_name:ident: $t:ty),*) -> $ret:ty) => ( | |
188 | $vis unsafe fn $name($($arg_name: $t),*) -> $ret { | |
189 | weak! { fn $name($($t),*) -> $ret } | |
190 | ||
191 | // Use a weak symbol from libc when possible, allowing `LD_PRELOAD` | |
192 | // interposition, but if it's not found just fail. | |
193 | if let Some(fun) = $name.get() { | |
194 | fun($($arg_name),*) | |
195 | } else { | |
196 | errno::set_errno(errno::Errno(libc::ENOSYS)); | |
197 | -1 | |
198 | } | |
199 | } | |
200 | ) | |
201 | } |