1 use core::hint;
2 use core::mem;
3 use core::ops::Deref;
4 use core::slice;
5 
6 use crate::__private::Slice;
7 
8 /// Collection of static elements that are gathered into a contiguous section of
9 /// the binary by the linker.
10 ///
11 /// The implementation is based on `link_section` attributes and
12 /// platform-specific linker support. It does not involve life-before-main or
13 /// any other runtime initialization on any platform. This is a zero-cost safe
14 /// abstraction that operates entirely during compilation and linking.
15 ///
16 /// ## Declaration
17 ///
18 /// A static distributed slice may be declared by writing `#[distributed_slice]`
19 /// on a static item whose type is `[T]` for some type `T`. The initializer
20 /// expression must be `[..]` to indicate that elements come from elsewhere.
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 /// ```
132 pub 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 
140 struct StaticPtr<T> {
141     ptr: *const T,
142 }
143 
144 unsafe impl<T> Send for StaticPtr<T> {}
145 
146 unsafe impl<T> Sync for StaticPtr<T> {}
147 
148 impl<T> Copy for StaticPtr<T> {}
149 
150 impl<T> Clone for StaticPtr<T> {
clone(&self) -> Self151     fn clone(&self) -> Self {
152         *self
153     }
154 }
155 
156 impl<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 = "illumos",
166         target_os = "freebsd"
167     ))]
private_new( name: &'static str, section_start: *const T, section_stop: *const T, dupcheck_start: *const usize, dupcheck_stop: *const usize, ) -> Self168     pub const unsafe fn private_new(
169         name: &'static str,
170         section_start: *const T,
171         section_stop: *const T,
172         dupcheck_start: *const usize,
173         dupcheck_stop: *const usize,
174     ) -> Self {
175         DistributedSlice {
176             name,
177             section_start: StaticPtr { ptr: section_start },
178             section_stop: StaticPtr { ptr: section_stop },
179             dupcheck_start: StaticPtr {
180                 ptr: dupcheck_start,
181             },
182             dupcheck_stop: StaticPtr { ptr: dupcheck_stop },
183         }
184     }
185 
186     #[doc(hidden)]
187     #[cfg(target_os = "windows")]
private_new( name: &'static str, section_start: *const [T; 0], section_stop: *const [T; 0], dupcheck_start: *const (), dupcheck_stop: *const (), ) -> Self188     pub const unsafe fn private_new(
189         name: &'static str,
190         section_start: *const [T; 0],
191         section_stop: *const [T; 0],
192         dupcheck_start: *const (),
193         dupcheck_stop: *const (),
194     ) -> Self {
195         DistributedSlice {
196             name,
197             section_start: StaticPtr {
198                 ptr: section_start as *const T,
199             },
200             section_stop: StaticPtr {
201                 ptr: section_stop as *const T,
202             },
203             dupcheck_start: StaticPtr {
204                 ptr: dupcheck_start as *const usize,
205             },
206             dupcheck_stop: StaticPtr {
207                 ptr: dupcheck_stop as *const usize,
208             },
209         }
210     }
211 
212     #[doc(hidden)]
213     #[inline]
private_typecheck(self, element: T)214     pub unsafe fn private_typecheck(self, element: T) {
215         mem::forget(element);
216     }
217 }
218 
219 impl<T> DistributedSlice<[T]> {
220     /// Retrieve a contiguous slice containing all the elements linked into this
221     /// program.
222     ///
223     /// **Note**: Ordinarily this method should not need to be called because
224     /// `DistributedSlice<[T]>` already behaves like `&'static [T]` in most ways
225     /// through the power of `Deref`. In particular, iteration and indexing and
226     /// method calls can all happen directly on the static without calling
227     /// `static_slice()`.
228     ///
229     /// ```no_run
230     /// # #![cfg_attr(feature = "used_linker", feature(used_with_arg))]
231     /// #
232     /// # struct Bencher;
233     /// #
234     /// use linkme::distributed_slice;
235     ///
236     /// #[distributed_slice]
237     /// static BENCHMARKS: [fn(&mut Bencher)] = [..];
238     ///
239     /// fn main() {
240     ///     // Iterate the elements.
241     ///     for bench in BENCHMARKS {
242     ///         /* ... */
243     ///     }
244     ///
245     ///     // Index into the elements.
246     ///     let first = BENCHMARKS[0];
247     ///
248     ///     // Slice the elements.
249     ///     let except_first = &BENCHMARKS[1..];
250     ///
251     ///     // Invoke methods on the underlying slice.
252     ///     let len = BENCHMARKS.len();
253     /// }
254     /// ```
static_slice(self) -> &'static [T]255     pub fn static_slice(self) -> &'static [T] {
256         if self.dupcheck_start.ptr.wrapping_add(1) < self.dupcheck_stop.ptr {
257             panic!("duplicate #[distributed_slice] with name \"{}\"", self.name);
258         }
259 
260         let stride = mem::size_of::<T>();
261         let start = self.section_start.ptr;
262         let stop = self.section_stop.ptr;
263         let byte_offset = stop as usize - start as usize;
264         let len = match byte_offset.checked_div(stride) {
265             Some(len) => len,
266             // The #[distributed_slice] call checks `size_of::<T>() > 0` before
267             // using the unsafe `private_new`.
268             None => unsafe { hint::unreachable_unchecked() },
269         };
270         unsafe { slice::from_raw_parts(start, len) }
271     }
272 }
273 
274 impl<T> Copy for DistributedSlice<[T]> {}
275 
276 impl<T> Clone for DistributedSlice<[T]> {
clone(&self) -> Self277     fn clone(&self) -> Self {
278         *self
279     }
280 }
281 
282 impl<T: 'static> Deref for DistributedSlice<[T]> {
283     type Target = [T];
deref(&self) -> &'static Self::Target284     fn deref(&self) -> &'static Self::Target {
285         self.static_slice()
286     }
287 }
288 
289 impl<T: 'static> IntoIterator for DistributedSlice<[T]> {
290     type Item = &'static T;
291     type IntoIter = slice::Iter<'static, T>;
into_iter(self) -> Self::IntoIter292     fn into_iter(self) -> Self::IntoIter {
293         self.static_slice().iter()
294     }
295 }
296