Relink: Rust ELF Loader and Runtime Linker
Relink is a highly customizable, high-performance Rust ELF loading and runtime linking library. It can load .so files, executables, and object files from disk or memory, then resolve dependencies, apply relocations, and look up symbols. It also exposes observer hooks for load, relocation, symbol binding, and lifecycle events.
When To Use It
- Load plugins, JIT artifacts, or hot-reload modules at runtime.
- Control
DT_NEEDEDdependencies, symbol scopes, or relocation handling yourself. - Track or customize load, relocation, symbol binding, and init/fini flow through observer hooks.
- Load ELF from memory, or plug in your own mmap or memory-management backend.
- Scan dependencies and sections first, then reorder layout, pack hot code, use huge pages, or run custom handling.
- Load relocatable ELF files such as
.o/.ko. - Keep ELF loading available in
no_std, kernels, embedded systems, or non-standard runtimes.
What It Loads
- Shared objects / dynamic libraries (
ET_DYN) - Executables and PIE-style images (
ET_EXEC, plus executable-styleET_DYN) - Relocatable object files (
ET_REL, for example.o/.ko)
Compared With dlopen
| Capability | Relink | dlopen-style loading |
|---|---|---|
| In-memory loading | ✅ Load from paths, byte buffers, or already parsed ELF inputs | ❌ |
ET_REL loading |
✅ Load and relocate .o / .ko / ET_REL files |
❌ |
| Pre-link planning | ✅ Resolve dependencies and sections first, then decide how to map | ❌ |
| Pre-load layout optimization | ✅ Adjust section layout before mapping for hot-code packing or custom reordering | ❌ |
| Mapping policy | ✅ Replace mmap, page size, permissions, and memory-access backends | ❌ |
| Dependency and symbol policy | ✅ Customize DT_NEEDED resolution, symbol scopes, and relocation interception |
❌ |
| Observer events | ✅ Hook load, symbol binding, relocation, and related stages | ❌ |
| Context isolation | ✅ Multiple LinkContexts keep independent modules, dependency graphs, and symbol scopes |
❌ |
| Remote / heterogeneous loading | ✅ Use custom memory access to load remote devices or heterogeneous target ELFs locally | ❌ |
Quick Start
The default feature set is full, which is suitable for loading dynamic libraries, executables,
relocatable objects, TLS, and lazy binding:
[]
= "0.16.0"
For a smaller build, disable default features and opt in only to what you need:
[]
= { = "0.16.0", = false, = ["libc"] }
Use Linker to Load Dependencies
use ;
const LINKER: = new;
Benchmarks
The table below is a GitHub Actions performance snapshot. Use it only as a reference for the current test suite. Full environment details are in actions/runs/25632675040/job/75239090388. The fixture is the repository's libc -> libb -> liba test chain, not the system C library.
Lower is better for loading. scan_first includes dependency scanning and section planning, so it is not a direct dlopen replacement.
| Benchmark | Time | Relative time |
|---|---|---|
elf_loader/memory |
89.531 µs |
0.78x |
elf_loader/file |
101.01 µs |
0.88x |
linker/runtime |
111.32 µs |
0.97x |
libloading/lazy |
115.34 µs |
1.00x |
libloading/now |
115.77 µs |
1.00x |
linker/scan_first |
288.92 µs |
2.51x |
Symbol lookup was measured after both loaders had already loaded the fixture chain:
| Benchmark | Time | Relative time |
|---|---|---|
symbol/elf_loader/hit |
10.280 ns |
0.13x |
symbol/libloading/hit |
80.154 ns |
1.00x |
symbol/elf_loader/miss |
11.548 ns |
0.03x |
symbol/libloading/miss |
375.49 ns |
1.00x |
Feature Flags
| Feature | Default | Purpose |
|---|---|---|
libc |
Yes | Use the libc backend on Unix-like platforms |
tls |
Yes | Enable the built-in same-process TLS resolver |
lazy-binding |
Yes | Enable PLT/GOT lazy binding and lazy-fixup lookup configuration |
object |
Yes | Enable relocatable object (ET_REL) loading and Loader::load_object() |
version |
No | Enable version-aware symbol lookup such as get_version() |
log |
No | Enable log integration for loader and relocation diagnostics |
portable-atomic |
No | Support targets without native pointer-sized atomics |
use-syscall |
No | Use the Linux syscall backend instead of libc |
full |
Yes | Convenience bundle: tls, lazy-binding, object, libc |
Notes:
- The default feature set is
full, equivalent totls+lazy-binding+object+libc. tlsonly provides the default resolver; custom TLS resolvers do not need this feature.load_object()is still controlled by theobjectfeature. Default builds include it; with--no-default-features, enable it explicitly.
Platform Support
| Instruction set | Dynamic libraries / executables | Pre-load layout optimization | .o / ET_REL |
|---|---|---|---|
x86_64 |
✅ | ✅ | ✅ |
x86 |
✅ | 🔧 | 🚧 |
aarch64 |
✅ | 🔧 | 🚧 |
arm |
✅ | 🔧 | 🚧 |
riscv64 |
✅ | 🔧 | ✅ |
riscv32 |
✅ | 🔧 | 🚧 |
loongarch64 |
✅ | 🔧 | 🚧 |
Legend: ✅ supported, 🔧 basic support, 🚧 pending. Complex section-reorder repair and .o / ET_REL support are currently centered on x86_64 and riscv64 relocation handling; contributions for the other architectures are welcome.
Contributing
Issues and pull requests are welcome. Star the project if it is useful in your work.
License
This project is dual-licensed under either of the following: