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