本文介紹一個使用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 示例
加載一個簡單的動態庫:
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.solet 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中的函數alet f = unsafe { a.get::<fn() -> i32>("a").unwrap() };f();
}
4 補充
如果你在使用時遇到任何問題,都可以在本文的評論區或者github上提出問題,此外十分歡迎任何對elf加載器感興趣的朋友貢獻代碼(改進elf_loader本身,增加樣例,修改文檔中存在的問題都可以)。如果覺得elf_loader對你有幫助的話不妨點個star。^v^