Crate linker_set

Source
Expand description

Declarative programming via embedded configuration data

A linker set allows you to program declaratively rather than imperatively by embedding configuration or behavior into a program as data.

Using a linker set, you can scatter instances of a certain type all over your program, and, with the proper annotations, the linker will gather them up into a special section of the ELF binary, forming an array, which can be iterated at runtime.

§Example

use std::collections::HashSet;
use linker_set::*;

set_declare!(stuff, u64);

#[set_entry(stuff)]
static FOO: u64 = 0x4F202A76B86A7299u64;
#[set_entry(stuff)]
static BAR: u64 = 0x560E9309456ACCE0u64;

let actual = set!(stuff).iter().collect::<HashSet<_>>();
let expect = HashSet::from([&FOO, &BAR]);
assert_eq!(actual, expect);

The set_declare! macro outputs a module definition. The module must be imported into the scope of calls to the set_entry attribute and the set! macro.

If you make a linker set of an integer type, you should use typed literals, not generic integer literals. I.e.

use linker_set::*;

set_declare!(foo, u64);

#[set_entry(foo)]
static FOO: u64 = 1000u64; // not 1000 ❌

The compiler might assign the wrong type to generic integer literals.

All items in a set should be of the same size, the size of the declared type. Otherwise, stuff won’t work. Caveat scriptor.

The index operator is just for fun. Obviously you shouldn’t depend on the linker to provide any specific ordering.

§Safety

Although the set_entry macro does not require an unsafe to call, THIS MACRO IS NOT ENTIRELY SAFE. The caller is required to ensure all entries in the set are valid and in the proper format. Rust may add unsafe macros at some point, but at present there is no way to declare that a given third-party macro is unsafe, even though Rust 2024 has some attributes that require an unsafe to be used.

§Compatibility

This crate works on Linux x86-64. It may work on other similar (i.e. ELF-based) targets.

§History

This idea comes from Clustrix, the best distributed relational database in the world, which no one knew about. Clustrix was written in a very unusual but very interesting style of C. Much of it was written in continuation-passing style, and continuations and lightweight threads (fibers) ran on top of a scheduler very similar to the asynchronous runtimes like Tokio which later became popular. (But Clustrix was started in 2006, before that popularity.)

Linker sets were used extensively in the Clustrix code to do things such as specify initialization or other system processes via graphs (initgraphs), automatically create heaps for memory allocation, automatically allocate integers or flags for what would otherwise have to be centrally controlled constants, and automatically register structures or handlers with a subsystem.

This concept was present in the oldest version of the Clustrix code in Git. A prior Subversion repository seemed to have been lost. The inspiration appears to have come from FreeBSD, which has several macros whose names match exactly macros used in the Clustrix source code.

Macros§

paste
set
Create a linker set proxy object for iteration or indexing.
set_declare
Declare the name of a linker set.

Structs§

LinkerSet
A proxy object that represents a linker set.
LinkerSetIter
An iterator that yields the elements in a linker set.

Attribute Macros§

set_entry
Attribute macro that puts an item into a linker set.