linkme/distributed_slice.rs
1use core::fmt::{self, Debug};
2use core::hint;
3use core::mem;
4use core::ops::Deref;
5use core::slice;
6
7use crate::__private::Slice;
8
9/// Collection of static elements that are gathered into a contiguous section of
10/// the binary by the linker.
11///
12/// The implementation is based on `link_section` attributes and
13/// platform-specific linker support. It does not involve life-before-main or
14/// any other runtime initialization on any platform. This is a zero-cost safe
15/// abstraction that operates entirely during compilation and linking.
16///
17/// ## Declaration
18///
19/// A static distributed slice may be declared by writing `#[distributed_slice]`
20/// on a static item whose type is `[T]` for some type `T`.
21///
22/// ```
23/// # #![cfg_attr(feature = "used_linker", feature(used_with_arg))]
24/// #
25/// # struct Bencher;
26/// #
27/// use linkme::distributed_slice;
28///
29/// #[distributed_slice]
30/// pub static BENCHMARKS: [fn(&mut Bencher)];
31/// ```
32///
33/// The attribute rewrites the `[T]` type of the static into
34/// `DistributedSlice<[T]>`, so the static in the example technically has type
35/// `DistributedSlice<[fn(&mut Bencher)]>`.
36///
37/// ## Elements
38///
39/// Slice elements may be registered into a distributed slice by a
40/// `#[distributed_slice(...)]` attribute in which the path to the distributed
41/// slice is given in the parentheses. The initializer is required to be a const
42/// expression.
43///
44/// Elements may be defined in the same crate that declares the distributed
45/// slice, or in any downstream crate. Elements across all crates linked into
46/// the final binary will be observed to be present in the slice at runtime.
47///
48/// ```
49/// # #![cfg_attr(feature = "used_linker", feature(used_with_arg))]
50/// #
51/// # mod other_crate {
52/// # use linkme::distributed_slice;
53/// #
54/// # pub struct Bencher;
55/// #
56/// # #[distributed_slice]
57/// # pub static BENCHMARKS: [fn(&mut Bencher)];
58/// # }
59/// #
60/// # use other_crate::Bencher;
61/// #
62/// use linkme::distributed_slice;
63/// use other_crate::BENCHMARKS;
64///
65/// #[distributed_slice(BENCHMARKS)]
66/// static BENCH_DESERIALIZE: fn(&mut Bencher) = bench_deserialize;
67///
68/// fn bench_deserialize(b: &mut Bencher) {
69/// /* ... */
70/// }
71/// ```
72///
73/// The compiler will require that the static element type matches with the
74/// element type of the distributed slice. If the two do not match, the program
75/// will not compile.
76///
77/// ```compile_fail
78/// # mod other_crate {
79/// # use linkme::distributed_slice;
80/// #
81/// # pub struct Bencher;
82/// #
83/// # #[distributed_slice]
84/// # pub static BENCHMARKS: [fn(&mut Bencher)];
85/// # }
86/// #
87/// # use linkme::distributed_slice;
88/// # use other_crate::BENCHMARKS;
89/// #
90/// #[distributed_slice(BENCHMARKS)]
91/// static BENCH_WTF: usize = 999;
92/// ```
93///
94/// ```text
95/// error[E0308]: mismatched types
96/// --> src/distributed_slice.rs:65:19
97/// |
98/// 17 | static BENCH_WTF: usize = 999;
99/// | ^^^^^ expected fn pointer, found `usize`
100/// |
101/// = note: expected fn pointer `fn(&mut other_crate::Bencher)`
102/// found type `usize`
103/// ```
104///
105/// ## Function elements
106///
107/// As a shorthand for the common case of distributed slices containing function
108/// pointers, the distributed\_slice attribute may be applied directly to a
109/// function definition to place a pointer to that function into a distributed
110/// slice.
111///
112/// ```
113/// # #![cfg_attr(feature = "used_linker", feature(used_with_arg))]
114/// #
115/// # pub struct Bencher;
116/// #
117/// use linkme::distributed_slice;
118///
119/// #[distributed_slice]
120/// pub static BENCHMARKS: [fn(&mut Bencher)];
121///
122/// // Equivalent to:
123/// //
124/// // #[distributed_slice(BENCHMARKS)]
125/// // static _: fn(&mut Bencher) = bench_deserialize;
126/// //
127/// #[distributed_slice(BENCHMARKS)]
128/// fn bench_deserialize(b: &mut Bencher) {
129/// /* ... */
130/// }
131/// ```
132pub struct DistributedSlice<T: ?Sized + Slice> {
133 name: &'static str,
134 section_start: StaticPtr<T::Element>,
135 section_stop: StaticPtr<T::Element>,
136 dupcheck_start: StaticPtr<usize>,
137 dupcheck_stop: StaticPtr<usize>,
138}
139
140struct StaticPtr<T> {
141 ptr: *const T,
142}
143
144unsafe impl<T> Send for StaticPtr<T> {}
145
146unsafe impl<T> Sync for StaticPtr<T> {}
147
148impl<T> Copy for StaticPtr<T> {}
149
150impl<T> Clone for StaticPtr<T> {
151 fn clone(&self) -> Self {
152 *self
153 }
154}
155
156impl<T> DistributedSlice<[T]> {
157 #[doc(hidden)]
158 #[cfg(any(
159 target_os = "none",
160 target_os = "linux",
161 target_os = "macos",
162 target_os = "ios",
163 target_os = "tvos",
164 target_os = "android",
165 target_os = "fuchsia",
166 target_os = "illumos",
167 target_os = "freebsd",
168 target_os = "openbsd",
169 target_os = "psp",
170 ))]
171 pub const unsafe fn private_new(
172 name: &'static str,
173 section_start: *const T,
174 section_stop: *const T,
175 dupcheck_start: *const usize,
176 dupcheck_stop: *const usize,
177 ) -> Self {
178 DistributedSlice {
179 name,
180 section_start: StaticPtr { ptr: section_start },
181 section_stop: StaticPtr { ptr: section_stop },
182 dupcheck_start: StaticPtr {
183 ptr: dupcheck_start,
184 },
185 dupcheck_stop: StaticPtr { ptr: dupcheck_stop },
186 }
187 }
188
189 #[doc(hidden)]
190 #[cfg(any(target_os = "uefi", target_os = "windows"))]
191 pub const unsafe fn private_new(
192 name: &'static str,
193 section_start: *const [T; 0],
194 section_stop: *const [T; 0],
195 dupcheck_start: *const (),
196 dupcheck_stop: *const (),
197 ) -> Self {
198 DistributedSlice {
199 name,
200 section_start: StaticPtr {
201 ptr: section_start as *const T,
202 },
203 section_stop: StaticPtr {
204 ptr: section_stop as *const T,
205 },
206 dupcheck_start: StaticPtr {
207 ptr: dupcheck_start as *const usize,
208 },
209 dupcheck_stop: StaticPtr {
210 ptr: dupcheck_stop as *const usize,
211 },
212 }
213 }
214
215 #[doc(hidden)]
216 #[inline]
217 pub unsafe fn private_typecheck(self, get: fn() -> &'static T) {
218 let _ = get;
219 }
220
221 /// Retrieve a contiguous slice containing all the elements linked into this
222 /// program.
223 ///
224 /// **Note**: Ordinarily this method should not need to be called because
225 /// `DistributedSlice<[T]>` already behaves like `&'static [T]` in most ways
226 /// through the power of `Deref`. In particular, iteration and indexing and
227 /// method calls can all happen directly on the static without calling
228 /// `static_slice()`.
229 ///
230 /// ```no_run
231 /// # #![cfg_attr(feature = "used_linker", feature(used_with_arg))]
232 /// #
233 /// # struct Bencher;
234 /// #
235 /// use linkme::distributed_slice;
236 ///
237 /// #[distributed_slice]
238 /// static BENCHMARKS: [fn(&mut Bencher)];
239 ///
240 /// fn main() {
241 /// // Iterate the elements.
242 /// for bench in BENCHMARKS {
243 /// /* ... */
244 /// }
245 ///
246 /// // Index into the elements.
247 /// let first = BENCHMARKS[0];
248 ///
249 /// // Slice the elements.
250 /// let except_first = &BENCHMARKS[1..];
251 ///
252 /// // Invoke methods on the underlying slice.
253 /// let len = BENCHMARKS.len();
254 /// }
255 /// ```
256 pub fn static_slice(self) -> &'static [T] {
257 if self.dupcheck_start.ptr.wrapping_add(1) < self.dupcheck_stop.ptr {
258 panic!("duplicate #[distributed_slice] with name \"{}\"", self.name);
259 }
260
261 let stride = mem::size_of::<T>();
262 let start = self.section_start.ptr;
263 let stop = self.section_stop.ptr;
264 let byte_offset = stop as usize - start as usize;
265 let len = match byte_offset.checked_div(stride) {
266 Some(len) => len,
267 // The #[distributed_slice] call checks `size_of::<T>() > 0` before
268 // using the unsafe `private_new`.
269 None => unsafe { hint::unreachable_unchecked() },
270 };
271
272 // On Windows, the implementation involves growing a &[T; 0] to
273 // encompass elements that we have asked the linker to place immediately
274 // after that location. The compiler sees this as going "out of bounds"
275 // based on provenance, so we must conceal what is going on.
276 #[cfg(any(target_os = "uefi", target_os = "windows"))]
277 let start = hint::black_box(start);
278
279 unsafe { slice::from_raw_parts(start, len) }
280 }
281}
282
283impl<T> Copy for DistributedSlice<[T]> {}
284
285impl<T> Clone for DistributedSlice<[T]> {
286 fn clone(&self) -> Self {
287 *self
288 }
289}
290
291impl<T: 'static> Deref for DistributedSlice<[T]> {
292 type Target = [T];
293 fn deref(&self) -> &'static Self::Target {
294 self.static_slice()
295 }
296}
297
298impl<T: 'static> IntoIterator for DistributedSlice<[T]> {
299 type Item = &'static T;
300 type IntoIter = slice::Iter<'static, T>;
301 fn into_iter(self) -> Self::IntoIter {
302 self.static_slice().iter()
303 }
304}
305
306impl<T> Debug for DistributedSlice<[T]>
307where
308 T: Debug + 'static,
309{
310 fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
311 Debug::fmt(self.static_slice(), formatter)
312 }
313}