New Features: * Add fuzz targets for parts of our ELF parsing interface via cargo-fuzz * Add SysVHashTable which interprets the contents of a SHT_HASH section * Add StringTable::get_raw() to get an uninterpreted &[u8] * Add ParsingTable.len() method to get the number of elements in the table * Add some note n_type constants for GNU extension notes. * Add default "to_str" feature to get &str for gabi constant names Changed Interfaces: * Change File::segments() to return a ParsingTable instead of just a ParsingIterator * Change File's SectionHeader interfaces to provide a ParsingTable instead of just a ParsingIterator * Remove deprecated File::section_data_for_header() in favor of File::section_data() * Remove FileHeader wrapper types OSABI, Architecture, and ObjectFileType * Remove ProgramHeader wrapper types ProgType and ProgFlag * Remove Symbol wrapper types SymbolType SymbolBind SymbolVis * Remove wrapper type SectionType * Remove unhelpful SectionFlag wrapper type * Remove Display impl for FileHeader, SectionHeader, ProgramHeader, Symbol * Remove ParseError::UnsupportedElfVersion in favor of more general ParseError::UnsupportedVersion Bug Fixes: * Fix divide by zero panic when parsing a note with alignment of 0 (Error instead of panic) * Use checked integer math all over the parsing code (Error instead of panic or overflow) * Fix note parsing for 8-byte aligned .note.gnu.property sections (Successfully parse instead of Erroring) * Add size validation when parsing tables with entsizes (Error instead of panic)
rust-elf
The elf
crate provides a pure-rust interface for reading and parsing ELF object files.
Capabilities
Zero-alloc parser:
This crate implements parsing in a way that avoids all heap allocations. ELF structures are parsed and stored on the stack and provided by patterns such as lazily parsed iterators that yield stack allocated structures. The structures are copy-converted as needed from the underlying file data into Rust's native struct representation.
Endian-aware:
This crate properly handles translating between file and host endianness when parsing the ELF contents.
Lazy parsing:
This crate strives for lazy evaluation and parsing when possible.
File::open_stream()
reads, parses and validates the ELF
File Header, then stops there. All other i/o and parsing is deferred to
being performed on-demand by other methods on File
. For example,
File::symbol_table()
reads the data for the symbol table and associated string
table then returns them with types like symbol::SymbolTable
and
string_table::StringTable
which simply act as an interpretation layer on top
of &[u8]
s, where parsing of symbol::Symbol
s and strings take place only when
they are requested.
Lazy i/o:
This crate provides two ways of parsing ELF files:
- From a
&[u8]
into which the user has already read the full contents of the file - From a Read + Seek (such as a
std::file::File
) where file contents are read lazily on-demand based on what the user wants to inspect.
These allow you to decide what tradeoff you want to make. If you're going to be working with the whole file at once, then the byte slice approach is probably worthwhile to minimize i/o overhead by streaming the whole file into memory at once. If you're only going to be inspecting part of the file, then the Read + Seek approach would help avoid the overhead of reading a bunch of unused file data just to parse out a few things.
No unsafe code:
Many of the other rust ELF parsers out there contain bits of unsafe code deep down or in dependencies to reinterpret/transmute byte contents as structures in order to drive zero-copy parsing. They're slick, and there's typically appropriate checking to validate the assumptions to make that unsafe code work, but nevertheless it introduces unsafe code blocks (albeit small ones). This crate strives to serve as an alternate implementation with zero unsafe code blocks.
no_std option:
Only disables the std:: Read + Seek interface and limits the library to the
&[u8]
parsing impl. All other ELF parsing functionality is still available!
Example:
extern crate elf;
fn main() {
let path = std::path::PathBuf::from("some_file");
let file_data = std::fs::read(path).expect("Could not read file.").as_slice();
let mut file = File::open_stream(file_data).expect("Could not parse ELF Header");
let (symtab, strtab) = file
.symbol_table()
.expect("Failed to read symbol table")
.expect("File contained no symbol table");
let symbol = symtab.get(30).expect("Failed to get symbol");
let symbol_name = strtab
.get(symbol.st_name as usize)
.expect("Failed to get name from strtab");
println!("{symbol_name}: {symbol}");
}