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