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}