Add File::section_headers() iterator method for lazy header parsing

When called, this reads the section header table and wraps the data reference
in an iterator which lazily parses SectionHeader structs out of it.
This commit is contained in:
Christopher Cole 2022-10-20 19:19:08 -07:00
parent bab7e10ca4
commit 6ec11531d2
No known key found for this signature in database
GPG Key ID: 0AC856975983E9DB
2 changed files with 152 additions and 0 deletions

View File

@ -54,6 +54,37 @@ impl<R: ReadBytesAt> File<R> {
))
}
/// Get an iterator over the Section Headers in the file.
///
/// The underlying ELF bytes backing the segment table are read all at once
/// when the iterator is requested, but parsing is deferred to be lazily
/// parsed on demand on each Iterator::next() call.
///
/// Returns a `ParseError` if the data bytes for the segment table cannot be
/// read i.e. if the ELF [FileHeader]'s
/// [e_shnum](FileHeader#structfield.e_shnum),
/// [e_shoff](FileHeader#structfield.e_shoff),
/// [e_shentsize](FileHeader#structfield.e_shentsize) are invalid and point
/// to a range in the file data that does not actually exist.
pub fn section_headers(&mut self) -> Result<section::SectionHeaderIterator, ParseError> {
if self.ehdr.e_shnum == 0 {
return Ok(section::SectionHeaderIterator::new(
self.ehdr.endianness,
self.ehdr.class,
&[],
));
}
let start = self.ehdr.e_shoff as usize;
let size = self.ehdr.e_shentsize as usize * self.ehdr.e_shnum as usize;
let buf = self.reader.read_bytes_at(start..start + size)?;
Ok(section::SectionHeaderIterator::new(
self.ehdr.endianness,
self.ehdr.class,
buf,
))
}
pub fn sections(&self) -> Result<&section::SectionTable, ParseError> {
Ok(&self.sections)
}

View File

@ -3,6 +3,36 @@ use crate::gabi;
use crate::parse::{Class, Endian, ParseAtExt, ParseError, ReadBytesAt};
use crate::string_table::StringTable;
pub struct SectionHeaderIterator<'data> {
endianness: Endian,
class: Class,
data: &'data [u8],
offset: usize,
}
impl<'data> SectionHeaderIterator<'data> {
pub fn new(endianness: Endian, class: Class, data: &'data [u8]) -> Self {
SectionHeaderIterator {
endianness,
class,
data,
offset: 0,
}
}
}
impl<'data> Iterator for SectionHeaderIterator<'data> {
type Item = SectionHeader;
fn next(&mut self) -> Option<Self::Item> {
if self.data.len() == 0 {
return None;
}
SectionHeader::parse_at(self.endianness, self.class, &mut self.offset, &self.data).ok()
}
}
#[derive(Debug)]
pub struct SectionTable {
headers: Vec<SectionHeader>,
@ -386,6 +416,97 @@ mod table_tests {
}
}
#[cfg(test)]
mod iter_tests {
use super::*;
#[test]
fn get_32_lsb() {
// init data buf with two header's worth of increasing byte values
let mut data = [0u8; 2 * ELF32SHDRSIZE as usize];
for n in 0..(2 * ELF32SHDRSIZE) {
data[n as usize] = n as u8;
}
let mut iter = SectionHeaderIterator::new(Endian::Little, Class::ELF32, &data);
assert_eq!(
iter.next().unwrap(),
SectionHeader {
sh_name: 0x03020100,
sh_type: SectionType(0x07060504),
sh_flags: SectionFlag(0xB0A0908),
sh_addr: 0x0F0E0D0C,
sh_offset: 0x13121110,
sh_size: 0x17161514,
sh_link: 0x1B1A1918,
sh_info: 0x1F1E1D1C,
sh_addralign: 0x23222120,
sh_entsize: 0x27262524,
}
);
assert_eq!(
iter.next().unwrap(),
SectionHeader {
sh_name: 0x2B2A2928,
sh_type: SectionType(0x2F2E2D2C),
sh_flags: SectionFlag(0x33323130),
sh_addr: 0x37363534,
sh_offset: 0x3B3A3938,
sh_size: 0x3F3E3D3C,
sh_link: 0x43424140,
sh_info: 0x47464544,
sh_addralign: 0x4B4A4948,
sh_entsize: 0x4F4E4D4C,
}
);
let next = iter.next();
assert!(next.is_none());
}
#[test]
fn get_64_msb() {
// init data buf with two header's worth of increasing byte values
let mut data = [0u8; 2 * ELF64SHDRSIZE as usize];
for n in 0..(2 * ELF64SHDRSIZE) {
data[n as usize] = n as u8;
}
let mut iter = SectionHeaderIterator::new(Endian::Big, Class::ELF64, &data);
assert_eq!(
iter.next().unwrap(),
SectionHeader {
sh_name: 0x00010203,
sh_type: SectionType(0x04050607),
sh_flags: SectionFlag(0x08090A0B0C0D0E0F),
sh_addr: 0x1011121314151617,
sh_offset: 0x18191A1B1C1D1E1F,
sh_size: 0x2021222324252627,
sh_link: 0x28292A2B,
sh_info: 0x2C2D2E2F,
sh_addralign: 0x3031323334353637,
sh_entsize: 0x38393A3B3C3D3E3F,
}
);
assert_eq!(
iter.next().unwrap(),
SectionHeader {
sh_name: 0x40414243,
sh_type: SectionType(0x44454647),
sh_flags: SectionFlag(0x48494A4B4C4D4E4F),
sh_addr: 0x5051525354555657,
sh_offset: 0x58595A5B5C5D5E5F,
sh_size: 0x6061626364656667,
sh_link: 0x68696A6B,
sh_info: 0x6C6D6E6F,
sh_addralign: 0x7071727374757677,
sh_entsize: 0x78797A7B7C7D7E7F,
}
);
let next = iter.next();
assert!(next.is_none());
}
}
#[cfg(test)]
mod shdr_tests {
use super::*;