Relink: Rust ELF Loader and Runtime Linker
English | 简体中文
Fast · Customizable · no_std support
Relink is a Rust ELF loader and runtime linker for plugins, JITs, and custom runtimes. Take control of dependency resolution, symbol binding, memory layout, and the loading lifecycle.
Quick start · Performance · Platforms · Examples · API docs
Highlights
- Flexible inputs: load shared libraries, executables, and relocatable objects (
.o / .ko) from files or memory.
- Control over linking: customize dependency resolution, symbol scopes, and relocation; observe or intervene in loading through observers.
- Optimize before loading: scan before mapping, reorder sections, and group hot code. See platform support for architecture coverage.
- Fit your runtime:
no_std support with replaceable mmap and memory-access backends for kernels, embedded systems, or remote memory.
Benchmarks
Across the 10 system libraries in this run, Relink used 6–33% less load time in
9 libraries, with PCRE2 about 0.3% slower, compared with libloading 0.9.0 (glibc).
Symbol-hit time was about one-third of libloading across all 10 libraries.
Figures show means and 95% confidence intervals; lower is better.
Load & teardown · µs

Teardown times are similar overall. Context creation/destruction and host imports are outside timing.
Symbol lookup · ns

Symbol lookup compares public APIs: Relink searches root exports, while dlsym may also
search dependencies. Misses include each API’s error handling.
Reproduce with taskset -c 2 bash ci/bench.sh. See the benchmark guide
for dependencies and measurement scope.
Transparent Huge Page Optimization
In these Clang / LLVM compilation workloads, enabling ThpPolicy::Collapse
reduced execution time by 2.2–4.7% and instruction TLB misses by about 99.95% in both workloads.

Execution shows means and 95% confidence intervals, excluding loading, page
collapse, setup and validation. iTLB counts cover the whole user-mode worker,
including those stages, and show means of two runs. Lower is better.
Tested on 2026-11-07 at e5a5b92: Ryzen 9 7945HX, Linux 7.0.11 x86_64,
glibc 2.43 and Rust 1.96.0; pinned to CPU 2 with a warm file cache.
See the benchmark guide
for sampling settings, THP configuration, memory costs and reproduction steps.
Quick Start
[dependencies]
elf_loader = "0.19.1"
Load a shared library, call a function, then release its modules. First
build the example library, then run
from the repository root. You can also run cargo run --example linker_load directly.
use elf_loader::{LinkContext, Linker, input::PathBuf, linker::SearchPathResolver};
use std::ffi::c_int;
fn main() -> Result<(), Box<dyn std::error::Error>> {
let mut context = LinkContext::for_process();
let loaded = Linker::new()
.with_resolver(SearchPathResolver::new())
.run()
.load(&mut context, PathBuf::from("target/example-library/libexample.so"))?;
// SAFETY: example.c defines int example_add(int, int).
unsafe {
let add = context.module(loaded.root())?
.get::<unsafe extern "C" fn(c_int, c_int) -> c_int>("example_add")?
.ok_or("missing example_add")?;
assert_eq!(add(2, 3), 5);
}
drop(loaded.release(&mut context)?);
Ok(())
}
More examples: memory loading · object files · layout changes · host imports.
| Instruction set |
Dynamic libraries / executables |
Section reordering |
.o / ET_REL |
x86_64 |
✅ |
✅ |
✅ |
x86 |
✅ |
🚧 |
🚧 |
aarch64 |
✅ |
🚧 |
🚧 |
arm |
✅ |
🚧 |
🚧 |
riscv64 |
✅ |
🚧 |
✅ |
riscv32 |
✅ |
🚧 |
🚧 |
loongarch64 |
✅ |
🚧 |
🚧 |
xtensa |
🔧 |
🚧 |
🚧 |
✅ Supported · 🔧 Partial support · 🚧 Pending.
Xtensa currently supports dynamic relocation, but not lazy binding or TLS
relocations.
Feature Flags
The default full set enables std, object, and lazy-binding. For a minimal hosted build:
elf_loader = { version = "0.19.1", default-features = false, features = ["std"] }
All feature flags
| Feature |
Default |
Purpose |
std |
Yes |
Enable standard-library integration and default thread storage; use native platform services on Linux/Android and enable host::open on Linux/glibc |
lazy-binding |
Yes |
Enable the built-in native PLT/GOT lazy binder |
object |
Yes |
Enable relocatable object (ET_REL) loading and LoaderRun::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; incompatible with std |
full |
Yes |
Convenience bundle: lazy-binding, object, std |
TLS is configured through a resolver, without a feature flag. Host libraries require
explicit imports; see the example.
The default TLS resolver requires std on Linux/Android, including for static TLS
and custom providers. Other targets and no_std builds need a custom resolver.
See the TLS API for
thread cleanup and pointer-lifetime requirements.
For use-syscall, disable default features and do not enable std.
Contributing
Report problems in Issues, or follow the
contribution guide to submit improvements.
License
Dual-licensed under MIT or Apache-2.0.
Contributors
评论