elf_loader:一个使用Rust编写的ELF加载器

本文介绍一个使用Rust实现的ELF加载器。

下面是elf_loader的仓库链接:

github:

https://github.com/weizhiao/elf_loaderhttps://github.com/weizhiao/elf_loader

crates.io

https://crates.io/crates/elf_loaderhttps://crates.io/crates/elf_loader

1 它能做什么?

elf_loader能够从内存、文件加载各种形式的elf文件,包括Executable file、Shared object file和Position-Independent Executable file。

PS:后面这两个其实是一类格式,Position-Independent Executable file其实就是带有入口的Shared object file,即可以执行的Shared object file。

鉴于此,它能够被使用在以下地方:

1 在操作系统内核中使用它作为elf文件的加载器

2 使用它实现Rust版本的动态链接器。PS:我自己也实现了一个https://github.com/weizhiao/dlopen-rs

3 在嵌入式设备上使用它加载elf动态库

2 优势

✨ 可以在 no_std环境中工作✨

elf_loader不依赖Rust std,也不强制依赖libc和操作系统,因此可以在内核和嵌入式设备等no_std环境中使用。

✨速度快✨

elf_loader吸取了musl和glibc里ld.so实现的优点,并充分利用了Rust的一些特性(比如静态分发),可以生成性能出色的代码。基于elf_loader实现的动态链接器dlopen-rs性能比libloading(ld.so)更好。

✨非常容易移植,具有良好的可扩展性✨

如果你想要移植elf_loader,你只需为你的平台实现 Mmap和ElfObject trait。在实现Mmap trait时可以参考elf_loader提供的默认实现:

elf_loader/src/mmap at main · weizhiao/elf_loader

此外你可以使用elf_loader提供的hook函数来拓展elf_loader的功能,实现其他任何你想要的功能,在使用hook函数时可以参考dlopen-rs里的实现:https://github.com/weizhiao/dlopen-rs/blob/main/src/loader/mod.rs

✨提供异步接口✨

elf_loader提供了加载elf文件的异步接口,这使得它在某些并发加载elf文件的场景下有更高的性能上限。不过你需要根据自己的应用场景实现 Mmap和ElfObjectAsync trait。比如不使用mmap来直接映射elf文件,转而使用mmap+文件读取的方式(mmap创建内存空间再通过文件读取将elf文件的内容读取到mmap创建的空间中)来加载elf文件,这样就能充分利用异步接口带来的优势。

✨编译期检查✨

elf_loader利用Rust的生命周期机制,在编译期检查elf文件的依赖库是否被提前销毁,大大提高了安全性。 比如说有三个被elf_loader加载的动态库a,b,c,其中c依赖b,b依赖a,如果a,b中的任意一个在c drop之前被drop了,那么将不会程序通过编译。你可以在examples/relocate中验证这一点:elf_loader/examples/relocate.rs at main · weizhiao/elf_loader · GitHub

3 示例

加载一个简单的动态库:

rust 复制代码
use elf_loader::{Loader, mmap::MmapImpl, object::ElfFile};
use elf_loader::{Loader, mmap::MmapImpl, object::ElfFile};
use std::{collections::HashMap, ptr::null};

fn main() {
    fn print(s: &str) {
        println!("{}", s);
    }

    // liba.so依赖的符号
    let mut map = HashMap::new();
    map.insert("__gmon_start__", null());
    map.insert("__cxa_finalize", null());
    map.insert("_ITM_registerTMCloneTable", null());
    map.insert("_ITM_deregisterTMCloneTable", null());
    map.insert("print", print as _);
    let pre_find = |name: &str| -> Option<*const ()> { map.get(name).copied() };
    // 加载动态库liba.so
    let mut loader = Loader::<MmapImpl>::new();
    let liba = loader
        .easy_load_dylib(ElfFile::from_path("target/liba.so").unwrap())
        .unwrap();
    // 重定位liba.so中的符号
    let a = liba.easy_relocate([].iter(), &pre_find).unwrap();
    // 调用liba.so中的函数a
    let f = unsafe { a.get::<fn() -> i32>("a").unwrap() };
    f();
}

4 补充

如果你在使用时遇到任何问题,都可以在本文的评论区或者github上提出问题,此外十分欢迎任何对elf加载器感兴趣的朋友贡献代码(改进elf_loader本身,增加样例,修改文档中存在的问题都可以)。如果觉得elf_loader对你有帮助的话不妨点个star。^v^

相关推荐
在角落发呆2 分钟前
Linux转发配置:解锁网络互联的核心密码
linux·运维·网络
齐潇宇15 分钟前
Zabbix 7 概述与配置
linux·zabbix·监控告警
江公望2 小时前
Ubuntu htop命令,10分钟讲清楚
linux·服务器
哎呦,帅小伙哦2 小时前
Linux 时间:从原子钟到 clock_gettime 的每一面
linux·运维·服务器
张小姐的猫2 小时前
【Linux】多线程 —— 线程互斥
linux·运维·服务器·c++
YuanDaima20483 小时前
Linux 进阶运维与 AI 环境实战:进程管理、网络排错与 GPU 监控
linux·运维·服务器·网络·人工智能
lolo大魔王4 小时前
Linux 数据文件处理实战:排序、搜索、压缩、归档一站式详解
linux·运维·服务器
starvapour4 小时前
Ubuntu切换到Fcitx5中文输入法
linux·运维·ubuntu
lolo大魔王5 小时前
Linux的监测程序
linux·运维·github
.YYY5 小时前
RHCE--Linux循环执行的例行性任务:crontab从入门到精通
linux·运维·服务器