elf_loader 0.13.0

A pure-rust library for loading all ELF format files from both memory and files.
Documentation
[![](https://img.shields.io/crates/v/elf_loader.svg)](https://crates.io/crates/elf_loader)
[![](https://img.shields.io/crates/d/elf_loader.svg)](https://crates.io/crates/elf_loader)
[![license](https://img.shields.io/crates/l/elf_loader.svg)](https://crates.io/crates/elf_loader)
[![elf_loader on docs.rs](https://docs.rs/elf_loader/badge.svg)](https://docs.rs/elf_loader)
[![Rust](https://img.shields.io/badge/rust-1.88.0%2B-blue.svg?maxAge=3600)](https://github.com/weizhiao/elf_loader)
[![Build Status](https://github.com/weizhiao/elf_loader/actions/workflows/rust.yml/badge.svg)](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.
😊