[](https://crates.io/crates/elf_loader)
[](https://crates.io/crates/elf_loader)
[](https://crates.io/crates/elf_loader)
[](https://docs.rs/elf_loader)
[](https://github.com/weizhiao/elf_loader)
[](https://github.com/weizhiao/elf_loader/actions)
# elf_loader
English | [中文](README_zh.md)
`elf_loader` can load and relocate various forms of ELF files from memory or files, including `Executable file`, `Shared object file`, and `Position-Independent Executable file`.
[Documentation](https://docs.rs/elf_loader/)
# Usage
`elf_loader` can load various ELF files and provides interfaces for extended functionality. It can be used in the following areas:
* Use it as an ELF file loader in operating system kernels.
* Use it to implement a Rust version of the dynamic linker.
* Use it to load ELF dynamic libraries on embedded devices.
# Capabilities
### ✨ Works in `no_std` environments ✨
`elf_loader` does not depend on Rust `std`, nor does it enforce `libc` and OS dependencies, so it can be used in `no_std` environments such as kernel and embedded devices.
### ✨ Load ELF dynamic libraries on Windows ✨
`elf_loader` can load ELF dynamic libraries on Windows. See [windows-elf-loader](https://github.com/weizhiao/rust-elfloader/tree/main/crates/windows-elf-loader).
### ✨ Compact Size ✨
The `elf_loader` is extremely small in size. The [mini-loader](https://github.com/weizhiao/rust-elfloader/tree/main/crates/mini-loader) implemented based on `elf_loader` compiles to a binary file of only **26KB**. Below are the results from analyzing the binary using the `bloat` tool:
```shell
cargo bloat --crates --release --target=x86_64-unknown-none -Zbuild-std=core,alloc,panic_abort -Zbuild-std-features=panic_immediate_abort,optimize_for_size
Finished `release` profile [optimized] target(s) in 0.28s
Analyzing target/x86_64-unknown-none/release/mini-loader
File .text Size Crate
23.1% 47.9% 5.9KiB elf_loader
9.1% 18.9% 2.3KiB alloc
7.1% 14.8% 1.8KiB core
3.7% 7.7% 974B [Unknown]
3.2% 6.7% 854B linked_list_allocator
1.5% 3.0% 383B compiler_builtins
0.4% 0.8% 105B __rustc
48.2% 100.0% 12.4KiB .text section size, the file size is 25.7KiB
Note: numbers above are a result of guesswork. They are not 100% correct and never will be.
```
### ✨ Fast speed ✨
This library draws on the strengths of `musl` and `glibc`'s `ld.so` implementation and fully utilizes some features of Rust (such as static dispatch), allowing it to generate `high-performance` code.
Below are the performance test results. You can view them in the `bench` job on GitHub Actions.
```shell
elf_loader:new time: [36.333 µs 36.478 µs 36.628 µs]
Found 9 outliers among 100 measurements (9.00%)
2 (2.00%) low mild
2 (2.00%) high mild
5 (5.00%) high severe
Benchmarking libloading:new
Benchmarking libloading:new: Warming up for 3.0000 s
Benchmarking libloading:new: Collecting 100 samples in estimated 5.2174 s (111k iterations)
Benchmarking libloading:new: Analyzing
libloading:new time: [46.348 µs 47.065 µs 47.774 µs]
Found 4 outliers among 100 measurements (4.00%)
3 (3.00%) high mild
1 (1.00%) high severe
Benchmarking elf_loader:get
Benchmarking elf_loader:get: Warming up for 3.0000 s
Benchmarking elf_loader:get: Collecting 100 samples in estimated 5.0000 s (476M iterations)
Benchmarking elf_loader:get: Analyzing
elf_loader:get time: [10.459 ns 10.477 ns 10.498 ns]
Found 1 outliers among 100 measurements (1.00%)
1 (1.00%) high severe
Benchmarking libloading:get
Benchmarking libloading:get: Warming up for 3.0000 s
Benchmarking libloading:get: Collecting 100 samples in estimated 5.0002 s (54M iterations)
Benchmarking libloading:get: Analyzing
libloading:get time: [93.226 ns 93.369 ns 93.538 ns]
Found 11 outliers among 100 measurements (11.00%)
7 (7.00%) high mild
4 (4.00%) high severe
```
It's important to note that `elf_loader` is not a dynamic linker and cannot automatically resolve dynamic library dependencies. However, it can serve as the underlying layer for implementing a dynamic linker.
### ✨ Very easy to port and has good extensibility ✨
If you want to port `elf_loader`, you only need to implement the `Mmap` and `ElfObject` traits for your platform. When implementing the `Mmap` trait, you can refer to the default implementation provided by `elf_loader`: [os](https://github.com/weizhiao/rust-elfloader/tree/main/src/os). In addition, you can use the `hook` functions provided by this library to extend the functionality of `elf_loader` to implement any other features you want. When using the `hook` functions, you can refer to: [hook](https://github.com/weizhiao/rust-dlopen/blob/main/src/loader.rs) in `dlopen-rs`.
### ✨ Provides asynchronous interfaces ✨
`elf_loader` provides asynchronous interfaces for loading ELF files, which can achieve higher performance in scenarios where ELF files are loaded concurrently.
However, you need to implement the `Mmap` and `ElfObjectAsync` traits according to your application scenario. For example, instead of using `mmap` to directly map ELF files, you can use a combination of `mmap` and file reading (`mmap` creates memory space, and then the content of the ELF file is read into the space created by `mmap`) to load ELF files, thus fully utilizing the advantages brought by the asynchronous interface.
### ✨ Compile-time checking ✨
Utilize Rust's lifetime mechanism to check at compile time whether the dependent libraries of a dynamic library are deallocated prematurely.
For example, there are three dynamic libraries loaded by `elf_loader`: `a`, `b`, and `c`. Library `c` depends on `b`, and `b` depends on `a`. If either `a` or `b` is dropped before `c` is dropped, the program will not pass compilation. (You can try this in the [examples/relocate](https://github.com/weizhiao/rust-elfloader/blob/main/examples/relocate_dylib.rs).)
### ✨ Supports Lazy Binding ✨
The `elf_loader` supports lazy binding, which means that when a symbol is resolved, it is not resolved immediately, but is instead resolved when it is first called.
### ✨ Supports RELR relative relocation format ✨
The `elf_loader` supports the RELR relative relocation format. For detailed information on RELR, please refer to: [Relative relocations and RELR](https://maskray.me/blog/2021-10-31-relative-relocations-and-relr).
# Feature
| Feature | Description |
| --------------- | ---------------------------------------------------------------------------------- |
| use-syscall | If `use-syscall` is enabled, `elf_loader` will use `linux syscalls` as the backend |
| version | Use the version information of symbols when resolving them. |
| log | Enable logging |
| rel | Use rel as the relocation type |
| portable-atomic | support target without native pointer size atomic operation |
Disable the `use-syscall` feature if you don't have an operating system.
# Architecture Support
| Arch | Support | Lazy Binding | Test |
| ----------- | ------- | ------------ | --------- |
| x86_64 | ✅ | ✅ | ✅(CI) |
| aarch64 | ✅ | ✅ | ✅(CI) |
| riscv64 | ✅ | ✅ | ✅(CI) |
| riscv32 | ✅ | ✅ | ✅(Manual) |
| loongarch64 | ✅ | ✅ | ✅(CI) |
| x86 | ✅ | ✅ | ✅(CI) |
| arm | ✅ | ✅ | ✅(CI) |
# Example
## Load a simple dynamic library
```rust
use elf_loader::load_dylib;
use std::collections::HashMap;
fn main() {
fn print(s: &str) {
println!("{}", s);
}
// Symbols required by dynamic library liba.so
let mut map = HashMap::new();
map.insert("print", print as _);
let pre_find = |name: &str| -> Option<*const ()> { map.get(name).copied() };
// Load and relocate dynamic library liba.so
let liba = load_dylib!("target/liba.so")
.unwrap()
.easy_relocate([].iter(), &pre_find)
.unwrap();
// Call function a in liba.so
let f = unsafe { liba.get::<fn() -> i32>("a").unwrap() };
println!("{}", f());
}
```
# Minimum Supported Rust Version
Rust 1.88 or higher.
# Supplement
If you encounter any issues while using it, you can raise an issue on GitHub. Additionally, we warmly welcome any friends interested in the `elf_loader` to contribute code (improving `elf_loader` itself, adding examples, and fixing issues in the documentation are all welcome). If you find `elf_loader` helpful, feel free to give it a star.
😊