25次发布
0.7.4 | 2023年11月22日 |
---|---|
0.7.2 | 2023年2月15日 |
0.7.0 | 2022年11月14日 |
0.0.10 | 2016年9月23日 |
0.0.4 | 2015年2月4日 |
3 in 嵌入式开发
145,915 每月下载量
用于 83 个Crate (41 直接)
435KB
8K SLoC
rust-elf
该elf
crate提供用于读取ELF对象文件的纯安全Rust接口。
功能
✨ 在 no_std
环境中运行 ✨
此crate提供了一种elf解析接口,它不分配或使用任何std功能,因此可以在内核和引导加载程序等no_std
环境中使用。no_std变体仅禁用了额外的流式std:: Read + Seek
接口。所有核心解析功能都是相同的!
✨ 大小端感知 ✨
此crate在解析ELF内容时处理文件和主机大小端之间的转换,并提供四个针对不同的常见ELF解析库用例优化的端字节解析实现。解析在规范之间是通用的,每个trait实现代表一个封装了从一组允许的字节顺序解析整数接口的规范。
AnyEndian
:在运行时根据解析的ELF对象类型动态解析任意字节顺序。BigEndian/
LittleEndian
:对于知道它们只想解析在编译时已知的单个给定字节顺序的工具。NativeEndian
:对于知道它们想要解析与编译目标字节顺序相同的工具。
当使用有限规范时,如果请求解析具有意外字节顺序的ELF文件,将正确返回错误。
✨ 零分配解析器 ✨
此crate以避免堆分配的方式实现解析。ELF结构在栈上解析和存储,并通过如延迟解析迭代器(它产生栈分配的rust类型)或延迟解析表(仅在table.get(index)时解析特定条目)等模式提供。根据需要将结构从底层文件数据复制转换为Rust的本地结构表示。
✨ 模糊测试 ✨
库的各个部分都进行了模糊测试,以检测恐慌和崩溃(请参阅fuzz/
)。
内存安全是一个核心目标,提供一种在遇到错误数据时出错而非崩溃或恐慌的安全接口。在适当的地方使用检查整数数学,并在遇到损坏或损坏的ELF结构时返回ParseErrors。
✨ 仅使用安全接口 ✨
由于内存安全是一个核心目标,这个crate包含零自己的不安全代码块,并且仅使用来自core和std的安全接口方法,因此您可以在不信任这个库开发者是否真正“正确”地认为某些不安全块是安全的情况下,信任Rust的内存安全保证。💃
注意:我很乐意看到这个crate在Rust提供安全转换之后得到进一步增强。
参阅:https://github.com/rust-lang/project-safe-transmute
✨ 一些零拷贝接口 ✨
例如,StringTable产生由原始字符串表字节支持的&[u8]
和&str
。
ElfBytes解析器类型也不会为解析器懒解析接口ParsingIterator
和ParsingTable
制作底层文件数据的原始副本。它们只是内部包装字节切片,并在需要时产生Rust repr值,这涉及到将字节复制到解析的Rust本地格式中。
根据使用情况,可以通过将原始ELF重新结构化为不同的布局来更有效地解释,例如,通过将平面表重新索引到HashMap中。ParsingIterator使其变得简单且rustily直观。
ParsingIterator也很棒,您可以根据需要轻松地将它们zip/enumerate/filter/collect。你知道你想要多次遍历不同表的对吗?只需将它们zip/collect到另一种类型中,这样你只需解析/endian-flip每个条目一次!
✨ 基于流的懒I/O接口 ✨
ElfStream解析器类型接受一个std:: Read + Seek
(例如std::fs::File
),其中根据用户想要解析的内容,按需懒惰地读取文件内容范围。
这,加上字节导向的接口,允许您决定您想要做出哪些权衡。如果您将要处理整个文件内容,那么将整个文件一次性流式传输到内存中的字节切片方法可能值得考虑,以最小化I/O开销。如果您只想检查文件的一部分,那么ElfStream方法将有助于避免读取大量未使用的文件数据以解析出一些内容的开销(例如,获取.gnu.note.build-id
)
✨ 无依赖项且编译速度快的小型库 ✨
在这个开发者2021年的m1 macbook上,发布目标的编译时间小于一秒。
使用ElfBytes
的示例
use elf::ElfBytes;
use elf::endian::AnyEndian;
use elf::note::Note;
use elf::note::NoteGnuBuildId;
use elf::section::SectionHeader;
let path = std::path::PathBuf::from("sample-objects/symver.x86_64.so");
let file_data = std::fs::read(path).expect("Could not read file.");
let slice = file_data.as_slice();
let file = ElfBytes::<AnyEndian>::minimal_parse(slice).expect("Open test1");
// Get the ELF file's build-id
let abi_shdr: SectionHeader = file
.section_header_by_name(".note.gnu.build-id")
.expect("section table should be parseable")
.expect("file should have a .note.ABI-tag section");
let notes: Vec<Note> = file
.section_data_as_notes(&abi_shdr)
.expect("Should be able to get note section data")
.collect();
assert_eq!(
notes[0],
Note::GnuBuildId(NoteGnuBuildId(
&[140, 51, 19, 23, 221, 90, 215, 131, 169, 13,
210, 183, 215, 77, 216, 175, 167, 110, 3, 209]))
);
// Find lazy-parsing types for the common ELF sections (we want .dynsym, .dynstr, .hash)
let common = file.find_common_data().expect("shdrs should parse");
let (dynsyms, strtab) = (common.dynsyms.unwrap(), common.dynsyms_strs.unwrap());
let hash_table = common.sysv_hash.unwrap();
// Use the hash table to find a given symbol in it.
let name = b"memset";
let (sym_idx, sym) = hash_table.find(name, &dynsyms, &strtab)
.expect("hash table and symbols should parse").unwrap();
// Verify that we got the same symbol from the hash table we expected
assert_eq!(sym_idx, 2);
assert_eq!(strtab.get(sym.st_name as usize).unwrap(), "memset");
assert_eq!(sym, dynsyms.get(sym_idx).unwrap());