Change File::segments() to return an iterator

This avoids the up front Vec allocation and parsing in favor of lazily parsing
ProgramHeaders iteratively out of the segment table bytes that were read from
the file. I opted to read the whole segment table up front with the thought that
one larger for the whole table I/O is likely faster than lots of small I/Os to get
each phdr, though that assumes that the segment table isn't huge (multi-MBs) which
is likely the common case.
This commit is contained in:
Christopher Cole 2022-10-18 12:42:12 -07:00
parent 40fd8d39bf
commit 0f60bec6db
No known key found for this signature in database
GPG Key ID: 0AC856975983E9DB
2 changed files with 42 additions and 18 deletions

View File

@ -28,29 +28,23 @@ impl<'data, D: Read + Seek> File<D> {
})
}
pub fn segments(&mut self) -> Result<Vec<segment::ProgramHeader>, ParseError> {
let phnum = self.ehdr.e_phnum as usize;
// It's OK to have zero segments
if phnum == 0 {
return Ok(Vec::<segment::ProgramHeader>::default());
pub fn segments(&mut self) -> Result<segment::SegmentIterator, ParseError> {
if self.ehdr.e_phnum == 0 {
return Ok(segment::SegmentIterator::new(
self.ehdr.endianness,
self.ehdr.class,
&[],
));
}
let start = self.ehdr.e_phoff as usize;
let size = self.ehdr.e_phentsize as usize * self.ehdr.e_phnum as usize;
let buf = self.reader.read_bytes_at(start..start + size)?;
let mut offset = 0;
let mut phdrs = Vec::<segment::ProgramHeader>::with_capacity(phnum);
for _ in 0..phnum {
let phdr = segment::ProgramHeader::parse_at(
self.ehdr.endianness,
self.ehdr.class,
&mut offset,
&buf,
)?;
phdrs.push(phdr);
}
Ok(phdrs)
Ok(segment::SegmentIterator::new(
self.ehdr.endianness,
self.ehdr.class,
buf,
))
}
/// Get the string table for the section headers

View File

@ -1,6 +1,36 @@
use crate::gabi;
use crate::parse::{Class, Endian, ParseAtExt, ParseError};
pub struct SegmentIterator<'data> {
endianness: Endian,
class: Class,
data: &'data [u8],
offset: usize,
}
impl<'data> SegmentIterator<'data> {
pub fn new(endianness: Endian, class: Class, data: &'data [u8]) -> Self {
SegmentIterator {
endianness,
class,
data,
offset: 0,
}
}
}
impl<'data> Iterator for SegmentIterator<'data> {
type Item = ProgramHeader;
fn next(&mut self) -> Option<Self::Item> {
if self.data.len() == 0 {
return None;
}
ProgramHeader::parse_at(self.endianness, self.class, &mut self.offset, &self.data).ok()
}
}
/// Encapsulates the contents of an ELF Program Header
///
/// The program header table is an array of program header structures describing