# Relink:Rust ELF 加载器与运行时链接器
<p align="center">
<img src="https://raw.githubusercontent.com/weizhiao/elf_loader/main/docs/assets/logo.svg" width="560" alt="Relink logo">
</p>
<p align="center">
<a href="https://crates.io/crates/elf_loader"><img src="https://img.shields.io/crates/v/elf_loader.svg" alt="Crates.io"></a>
<a href="https://crates.io/crates/elf_loader"><img src="https://img.shields.io/crates/d/elf_loader.svg" alt="Crates.io downloads"></a>
<a href="https://docs.rs/elf_loader"><img src="https://docs.rs/elf_loader/badge.svg" alt="Docs.rs"></a>
<img src="https://img.shields.io/badge/rust-1.93.0%2B-blue.svg" alt="Minimum supported Rust version">
<a href="https://github.com/weizhiao/elf_loader/actions/workflows/rust.yml"><img src="https://github.com/weizhiao/elf_loader/actions/workflows/rust.yml/badge.svg" alt="Build status"></a>
<img src="https://img.shields.io/crates/l/elf_loader.svg" alt="MIT/Apache-2.0 license">
</p>
<p align="center">
<a href="README.md">English</a> | <a href="README_zh.md">简体中文</a> |
<a href="CONTRIBUTING.md">Contributing</a> | <a href="CONTRIBUTING_zh.md">贡献指南</a>
</p>
<p align="center">
<strong>在 no_std 环境中加载、链接和改写 ELF。</strong>
</p>
Relink 是一个高可定制、高性能的 Rust ELF 加载与运行时链接库。它可以从文件或内存加载 `.so`、可执行文件和目标文件,并完成依赖解析、重定位和符号查找;同时提供 observer 机制,用于观察或定制加载、重定位、符号绑定与生命周期事件。
## 什么时候用
- 运行时加载插件、JIT 产物或热更新模块。
- 需要自己控制 `DT_NEEDED` 依赖、符号作用域或重定位处理。
- 需要通过 observer 机制跟踪或改写加载、重定位、符号绑定与 init/fini 流程。
- 需要从内存加载 ELF,或接入自己的 mmap / 内存管理后端。
- 需要先扫描依赖和 section,再做布局重排、热路径代码聚集、大页映射或自定义处理。
- 需要加载 `.o` / `.ko` 这类可重定位 ELF。
- 需要在 `no_std`、内核、嵌入式或非标准运行时中保留 ELF 加载能力。
## 可以加载什么
- 共享对象 / 动态库(`ET_DYN`)
- 可执行文件与 PIE 风格镜像(`ET_EXEC`,以及按可执行文件处理的 `ET_DYN`)
- 可重定位目标文件(`ET_REL`,例如 `.o` / `.ko`)
## 和 `dlopen` 相比
| 内存加载 | ✅ 可从路径、字节缓冲区或已解析 ELF 输入加载 | ❌ |
| `ET_REL` 加载 | ✅ 可加载和重定位 `.o` / `.ko` / `ET_REL` 文件 | ❌ |
| 链接前规划 | ✅ 可先解析依赖和 section,再决定映射方式 | ❌ |
| 加载前布局优化 | ✅ 可在映射前调整 section 布局,用于热路径聚集或自定义重排 | ❌ |
| 映射策略 | ✅ 可替换 mmap、页大小、权限和内存访问后端 | ❌ |
| 依赖与符号策略 | ✅ 可自定义 `DT_NEEDED` 解析、符号 scope 和重定位拦截 | ❌ |
| Observer 事件 | ✅ 可在加载、符号绑定、重定位等阶段插入 hook | ❌ |
| 上下文隔离 | ✅ 多个 `LinkContext` 独立保存模块、依赖图和符号作用域 | ❌ |
| 远程 / 异构加载 | ✅ 可用自定义内存访问在本地装载远程设备或异构目标 ELF | ❌ |
## 快速开始
默认 feature 集合是 `full`,适合直接加载动态库、可执行文件、可重定位目标文件、TLS 和 lazy binding:
```toml
[dependencies]
elf_loader = "0.16.0"
```
如果你需要更小的构建,可以关闭默认 feature 后按需选择:
```toml
[dependencies]
elf_loader = { version = "0.16.0", default-features = false, features = ["libc"] }
```
### 使用 Linker 加载依赖
```rust
use elf_loader::{
LinkContext, Linker, Result,
input::PathBuf,
linker::SearchPathResolver,
};
const LINKER: Linker<'static, PathBuf> = Linker::new();
fn main() -> Result<()> {
let root = PathBuf::from("path/to/plugin.so");
let mut context: LinkContext<PathBuf, ()> = LinkContext::new();
let loaded = LINKER
.resolver(SearchPathResolver::new())
.load(&mut context, root)?;
let run = unsafe {
loaded
.get::<extern "C" fn() -> i32>("run")
.expect("symbol `run` not found")
};
let _ = run();
Ok(())
}
```
## 基准测试
下表是 GitHub Actions 上的一次性能快照,只适合作为当前测试集的参考。完整环境见 [actions/runs/25632675040/job/75239090388](https://github.com/weizhiao/Relink/actions/runs/25632675040/job/75239090388)。fixture 是仓库内的 `libc -> libb -> liba` 测试链,不是系统 C library。
加载耗时越低越好。`scan_first` 包含依赖扫描和 section 规划成本,因此不是和 `dlopen` 完全同质的直接替换。
| `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` |
符号查找对比是在两边都已经加载完 fixture chain 后测量的:
| `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 开关
| `libc` | 是 | 在 Unix-like 平台使用 libc 后端 |
| `tls` | 是 | 启用内置同进程 TLS resolver |
| `lazy-binding` | 是 | 启用 PLT/GOT lazy binding 和 lazy fixup 查找配置 |
| `object` | 是 | 启用可重定位目标文件(`ET_REL`)加载和 `Loader::load_object()` |
| `version` | 否 | 启用带符号版本的查找,例如 `get_version()` |
| `log` | 否 | 启用基于 `log` 的加载与重定位诊断输出 |
| `portable-atomic` | 否 | 为不支持原生指针宽度原子操作的目标提供支持 |
| `use-syscall` | 否 | 在 Linux 上使用 syscall 后端,而不是 libc |
| `full` | 是 | 便捷组合:`tls`、`lazy-binding`、`object`、`libc` |
说明:
- 默认 feature 是 `full`,等价于 `tls` + `lazy-binding` + `object` + `libc`。
- `tls` 只提供默认 resolver;使用自定义 TLS resolver 时,不需要开启这个 feature。
- `load_object()` 仍由 `object` feature 控制;默认构建已包含该 feature,`--no-default-features` 时需要显式开启。
## 平台支持
| `x86_64` | ✅ | ✅ | ✅ |
| `x86` | ✅ | 🔧 | 🚧 |
| `aarch64` | ✅ | 🔧 | 🚧 |
| `arm` | ✅ | 🔧 | 🚧 |
| `riscv64` | ✅ | 🔧 | ✅ |
| `riscv32` | ✅ | 🔧 | 🚧 |
| `loongarch64` | ✅ | 🔧 | 🚧 |
符号:✅ 支持,🔧 基础支持,🚧 待补齐。复杂 section 重排修复和 `.o` / `ET_REL` 目前主要围绕 `x86_64` 与 `riscv64` 的重定位实现展开;其他架构欢迎补齐。
## 参与贡献
欢迎通过 Issue 和 PR 一起把 Relink 做得更好。如果项目对你有帮助,也欢迎点个 Star。
## 许可证
本项目采用双许可证:
- [MIT License](LICENSE-MIT)
- [Apache License 2.0](LICENSE-APACHE)
## 贡献者
<a href="https://github.com/weizhiao/elf_loader/graphs/contributors">
<img src="https://contributors-img.web.app/image?repo=weizhiao/elf_loader" alt="Project contributors">
</a>