linker_set/lib.rs
1#![warn(missing_docs)]
2
3//! Declarative programming via embedded configuration data
4//!
5//! A linker set allows you to program declaratively rather than
6//! imperatively by embedding configuration or behavior into a program as
7//! data.
8//!
9//! Using a linker set, you can scatter instances of a certain type all
10//! over your program, and, with the proper annotations, the linker will
11//! gather them up into a special section of the ELF binary, forming an
12//! array, which can be iterated at runtime.
13//!
14//! # Example
15//!
16//! ```
17//! use std::collections::HashSet;
18//! use linker_set::*;
19//!
20//! set_declare!(stuff, u64);
21//!
22//! #[set_entry(stuff)]
23//! static FOO: u64 = 0x4F202A76B86A7299u64;
24//! #[set_entry(stuff)]
25//! static BAR: u64 = 0x560E9309456ACCE0u64;
26//!
27//! # fn main() {
28//! let actual = set!(stuff).iter().collect::<HashSet<_>>();
29//! let expect = HashSet::from([&FOO, &BAR]);
30//! assert_eq!(actual, expect);
31//! # }
32//! ```
33//!
34//! The [set_declare!] macro outputs a module definition. The module must
35//! be imported into the scope of calls to the [set_entry] attribute and the
36//! [set!] macro.
37//!
38//! If you make a linker set of an integer type, you should use typed
39//! literals, not generic integer literals. I.e.
40//!
41//! ```
42//! use linker_set::*;
43//!
44//! set_declare!(foo, u64);
45//!
46//! #[set_entry(foo)]
47//! static FOO: u64 = 1000u64; // not 1000 ❌
48//! ```
49//!
50//! The compiler might assign the wrong type to generic integer literals.
51//!
52//! All items in a set should be of the same size, the size of the declared
53//! type. Otherwise, stuff won't work. Caveat scriptor.
54//!
55//! The index operator is just for fun. Obviously you shouldn't depend on
56//! the linker to provide any specific ordering.
57//!
58//! # Safety
59//!
60//! Although the [set_entry] macro does not require an unsafe to call, THIS
61//! MACRO IS NOT ENTIRELY SAFE. The caller is required to ensure all entries
62//! in the set are valid and in the proper format. Rust may add unsafe macros
63//! at some point, but at present there is no way to declare that a given
64//! third-party macro is unsafe, even though Rust 2024 has some attributes
65//! that require an unsafe to be used.
66//!
67//! # Compatibility
68//!
69//! This crate works on Linux x86-64. It may work on other similar (i.e.
70//! ELF-based) targets.
71//!
72//! # History
73//!
74//! This idea comes from [Clustrix], the best distributed relational
75//! database in the world, which no one knew about. Clustrix was written
76//! in a very unusual but very interesting style of C. Much of it was
77//! written in [continuation-passing style]([CPS]), and continuations and
78//! lightweight threads (fibers) ran on top of a scheduler very similar to
79//! the asynchronous runtimes like Tokio which later became popular. (But
80//! Clustrix was started in 2006, before that popularity.)
81//!
82//! Linker sets were used extensively in the Clustrix code to do things
83//! such as specify initialization or other system processes via graphs
84//! (initgraphs), automatically create heaps for memory allocation,
85//! automatically allocate integers or flags for what would otherwise have
86//! to be centrally controlled constants, and automatically register
87//! structures or handlers with a subsystem.
88//!
89//! This concept was present in the oldest version of the Clustrix code in
90//! Git. A prior Subversion repository seemed to have been lost. The
91//! inspiration appears to have come from [FreeBSD], which has several
92//! macros whose names match exactly macros used in the Clustrix source
93//! code.
94//!
95//! [Clustrix]: https://en.wikipedia.org/wiki/Clustrix
96//! [CPS]: https://en.wikipedia.org/wiki/Continuation-passing_style
97//! [FreeBSD]: https://github.com/freebsd/freebsd-src/blob/main/sys/sys/linker_set.h
98
99pub use linker_set_proc::set_entry;
100pub use paste::paste;
101
102/// An iterator that yields the elements in a linker set.
103pub struct LinkerSetIter<T> {
104 next: *const T,
105 stop: *const T,
106}
107
108impl<T> LinkerSetIter<T> {
109 /// Create a new iterator for a linker set.
110 ///
111 /// Users should call the [set!] macro instead of this function.
112 ///
113 /// # Safety
114 /// The pointers must be start and end pointers generated by the linker.
115 pub unsafe fn new(start: *const T, stop: *const T) -> Self {
116 assert!(start < stop);
117 Self { next: start, stop }
118 }
119}
120
121impl<T> Iterator for LinkerSetIter<T>
122where
123 T: 'static,
124{
125 type Item = &'static T;
126
127 fn next(&mut self) -> Option<Self::Item> {
128 if self.next == self.stop {
129 None
130 } else {
131 unsafe {
132 let x = self.next.as_ref();
133 self.next = self.next.add(1);
134 x
135 }
136 }
137 }
138
139 fn count(self) -> usize {
140 self.len()
141 }
142
143 fn size_hint(&self) -> (usize, Option<usize>) {
144 let len = self.len();
145 (len, Some(len))
146 }
147}
148
149impl<T> ExactSizeIterator for LinkerSetIter<T>
150where
151 T: 'static,
152{
153 fn len(&self) -> usize {
154 unsafe { self.stop.offset_from(self.next).try_into().unwrap() }
155 }
156}
157
158impl<T> std::iter::FusedIterator for LinkerSetIter<T> where T: 'static {}
159
160unsafe impl<T: Send> Send for LinkerSetIter<T> {}
161
162/// A proxy object that represents a linker set.
163///
164/// You can store this object if you should want the ability to create
165/// multiple iterators on the linker set, or maybe if you wanted to keep
166/// track of a specific linker set out of some number of them.
167pub struct LinkerSet<T>
168where
169 T: 'static,
170{
171 start: *const T,
172 stop: *const T,
173 slice: &'static [T],
174}
175
176impl<T> LinkerSet<T>
177where
178 T: 'static,
179{
180 /// Create a new object to represent a linker set.
181 ///
182 /// # Safety
183 /// The pointers must be start and end pointers generated by the linker.
184 pub unsafe fn new(start: *const T, stop: *const T) -> Self {
185 assert!(start < stop);
186 let slice = unsafe {
187 let len = stop.offset_from(start).try_into().unwrap();
188 std::slice::from_raw_parts(start, len)
189 };
190 Self { start, stop, slice }
191 }
192
193 /// Returns an iterator over the items in the linker set.
194 pub fn iter(&self) -> LinkerSetIter<T> {
195 unsafe { LinkerSetIter::new(self.start, self.stop) }
196 }
197
198 /// Returns the number of elements in the linker set.
199 pub fn len(&self) -> usize {
200 self.slice.len()
201 }
202
203 /// Returns true if the linker set contains zero elements.
204 pub fn is_empty(&self) -> bool {
205 self.start == self.stop
206 }
207}
208
209impl<T> IntoIterator for LinkerSet<T>
210where
211 T: 'static,
212{
213 type Item = &'static T;
214 type IntoIter = LinkerSetIter<T>;
215
216 fn into_iter(self) -> Self::IntoIter {
217 self.iter()
218 }
219}
220
221impl<T, I> std::ops::Index<I> for LinkerSet<T>
222where
223 T: 'static,
224 I: std::slice::SliceIndex<[T], Output = T>,
225{
226 type Output = T;
227
228 fn index(&self, i: I) -> &Self::Output {
229 self.slice.index(i)
230 }
231}
232
233unsafe impl<T: Send> Send for LinkerSet<T> {}
234unsafe impl<T: Sync> Sync for LinkerSet<T> {} // readonly once created
235
236/// Declare the name of a linker set.
237///
238/// This macro outputs a module into the current scope. The module must
239/// be brought into scope should the linker set be used within another module.
240#[macro_export]
241macro_rules! set_declare {
242 ($set:ident, $type:ty) => {
243 pub mod $set {
244 #[allow(unused_imports)]
245 use super::*;
246 $crate::paste! {
247 unsafe extern "C" {
248 /* rust thinks we're allowing these things to come in from
249 * C code, so if type is a function, it gets cranky because
250 * it thinks we're proposing to call a function in C with
251 * rust calling convention. */
252 #[allow(improper_ctypes)]
253 pub static [<__start_set_ $set>]: $type;
254 #[allow(improper_ctypes)]
255 pub static [<__stop_set_ $set>]: $type;
256 }
257 }
258 }
259 };
260}
261
262/// Create a linker set proxy object for iteration or indexing.
263#[macro_export]
264macro_rules! set {
265 ($set:ident) => {{
266 $crate::paste! {
267 unsafe {
268 LinkerSet::new(
269 &$set::[<__start_set_ $set>],
270 &$set::[<__stop_set_ $set>],
271 )
272 }
273 }
274 }};
275}
276
277#[cfg(test)]
278mod test {
279 use super::*;
280 use std::collections::HashSet;
281
282 set_declare!(stuff, u64);
283
284 #[set_entry(stuff)]
285 static FOO: u64 = 0x4F202A76B86A7299u64;
286 #[set_entry(stuff)]
287 static BAR: u64 = 0x560E9309456ACCE0u64;
288
289 #[test]
290 fn test_set_contents() {
291 let actual = set!(stuff).iter().collect::<HashSet<_>>();
292 let expect = HashSet::from([&FOO, &BAR, &0x6666666666666666]);
293 assert_eq!(actual, expect);
294 }
295
296 #[test]
297 fn test_set_iter_len() {
298 const LEN: usize = 3;
299 let iter = set!(stuff).iter();
300 assert_eq!(iter.len(), LEN);
301 assert_eq!(iter.size_hint(), (LEN, Some(LEN)));
302 assert_eq!(iter.count(), LEN);
303 }
304
305 #[test]
306 fn test_into() {
307 let mut actual = HashSet::new();
308 for i in set!(stuff) {
309 actual.insert(i);
310 }
311 let expect = HashSet::from([&FOO, &BAR, &0x6666666666666666]);
312 assert_eq!(actual, expect);
313 }
314
315 #[test]
316 fn test_index() {
317 let set = set!(stuff);
318 assert_eq!(set.len(), 3);
319 let mut actual = HashSet::new();
320 for i in 0..set.len() {
321 actual.insert(set[i]); // this is u64; compiler auto derefs
322 }
323 let expect = HashSet::from([FOO, BAR, 0x6666666666666666]);
324 assert_eq!(actual, expect);
325 }
326
327 #[test]
328 fn test_is_empty() {
329 assert!(!set!(stuff).is_empty());
330 }
331
332 #[derive(Debug, Eq, PartialEq, Hash)]
333 pub(crate) struct Foo {
334 a: u32,
335 b: u8,
336 }
337
338 set_declare!(aaa, Foo);
339
340 #[set_entry(aaa)]
341 static AAA: Foo = Foo { a: 1, b: 5 };
342
343 #[test]
344 fn test_struct() {
345 let actual = set!(aaa).iter().collect::<HashSet<_>>();
346 let expect = HashSet::from([&AAA]);
347 assert_eq!(actual, expect);
348 }
349
350 #[test]
351 fn test_traits() {
352 fn require_send<T: Send>(_: T) {}
353 fn require_sync<T: Sync>(_: T) {}
354
355 require_send(set!(aaa));
356 require_sync(set!(aaa));
357 require_send(set!(aaa).iter());
358 }
359}
360
361#[cfg(test)]
362mod test_use_ext {
363 use super::*;
364 use test::stuff;
365
366 #[set_entry(stuff)]
367 static FOO: u64 = 0x6666666666666666;
368
369 #[test]
370 fn test_use() {
371 const LEN: usize = 3;
372 let iter = set!(stuff).iter();
373 assert_eq!(iter.len(), LEN);
374 }
375}
376
377#[cfg(test)]
378mod test_dyn {
379 use super::*;
380
381 pub trait Trait: Sync {
382 fn func(&self, x: &mut usize);
383 }
384
385 pub struct Foo;
386
387 impl Trait for Foo {
388 fn func(&self, x: &mut usize) {
389 *x += 1;
390 }
391 }
392
393 set_declare!(t, &'static dyn Trait);
394 #[set_entry(t)]
395 static FOO: &'static dyn Trait = &Foo;
396
397 #[test]
398 fn test_trait() {
399 let mut x = 0;
400 for t in set!(t) {
401 t.func(&mut x);
402 }
403 assert_eq!(x, 1);
404 }
405}