README

Relink

Crates.io Crates.io downloads Docs.rs Minimum supported Rust version Build status MIT/Apache-2.0 license

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

Relink and libloading load and teardown times

Teardown times are similar overall. Context creation/destruction and host imports are outside timing.

Symbol lookup · ns

Relink and libloading symbol lookup hit and miss times

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.

Relink execution time and instruction TLB misses with and without THP

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.

Platform Support

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

Project contributors

评论

暂无评论