linkme/
distributed_slice.rs

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