Skip to content

Commit

Permalink
read: implement Iterator for more types (#714)
Browse files Browse the repository at this point in the history
This also changes the underlying `next` methods to fuse
on error.
  • Loading branch information
philipc authored Aug 6, 2024
1 parent 8a25b8e commit b1aa6c3
Show file tree
Hide file tree
Showing 6 changed files with 261 additions and 60 deletions.
52 changes: 43 additions & 9 deletions src/read/elf/attributes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -70,14 +70,14 @@ impl<'data, Elf: FileHeader> AttributesSubsectionIterator<'data, Elf> {
return Ok(None);
}

let result = self.parse();
let result = self.parse().map(Some);
if result.is_err() {
self.data = Bytes(&[]);
}
result
}

fn parse(&mut self) -> Result<Option<AttributesSubsection<'data, Elf>>> {
fn parse(&mut self) -> Result<AttributesSubsection<'data, Elf>> {
// First read the subsection length.
let mut data = self.data;
let length = data
Expand All @@ -94,16 +94,25 @@ impl<'data, Elf: FileHeader> AttributesSubsectionIterator<'data, Elf> {
data.skip(4)
.read_error("Invalid ELF attributes subsection length")?;

// TODO: errors here should not prevent reading the next subsection.
let vendor = data
.read_string()
.read_error("Invalid ELF attributes vendor")?;

Ok(Some(AttributesSubsection {
Ok(AttributesSubsection {
endian: self.endian,
length,
vendor,
data,
}))
})
}
}

impl<'data, Elf: FileHeader> Iterator for AttributesSubsectionIterator<'data, Elf> {
type Item = Result<AttributesSubsection<'data, Elf>>;

fn next(&mut self) -> Option<Self::Item> {
self.next().transpose()
}
}

Expand Down Expand Up @@ -153,14 +162,14 @@ impl<'data, Elf: FileHeader> AttributesSubsubsectionIterator<'data, Elf> {
return Ok(None);
}

let result = self.parse();
let result = self.parse().map(Some);
if result.is_err() {
self.data = Bytes(&[]);
}
result
}

fn parse(&mut self) -> Result<Option<AttributesSubsubsection<'data>>> {
fn parse(&mut self) -> Result<AttributesSubsubsection<'data>> {
// The format of a sub-section looks like this:
//
// <file-tag> <size> <attribute>*
Expand All @@ -184,6 +193,7 @@ impl<'data, Elf: FileHeader> AttributesSubsubsectionIterator<'data, Elf> {
data.skip(1 + 4)
.read_error("Invalid ELF attributes sub-subsection length")?;

// TODO: errors here should not prevent reading the next sub-subsection.
let indices = if tag == elf::Tag_Section || tag == elf::Tag_Symbol {
data.read_string()
.map(Bytes)
Expand All @@ -194,12 +204,20 @@ impl<'data, Elf: FileHeader> AttributesSubsubsectionIterator<'data, Elf> {
return Err(Error("Unimplemented ELF attributes sub-subsection tag"));
};

Ok(Some(AttributesSubsubsection {
Ok(AttributesSubsubsection {
tag,
length,
indices,
data,
}))
})
}
}

impl<'data, Elf: FileHeader> Iterator for AttributesSubsubsectionIterator<'data, Elf> {
type Item = Result<AttributesSubsubsection<'data>>;

fn next(&mut self) -> Option<Self::Item> {
self.next().transpose()
}
}

Expand Down Expand Up @@ -263,14 +281,30 @@ impl<'data> AttributeIndexIterator<'data> {
if self.data.is_empty() {
return Ok(None);
}

let result = self.parse().map(Some);
if result.is_err() {
self.data = Bytes(&[]);
}
result
}

fn parse(&mut self) -> Result<u32> {
let err = "Invalid ELF attribute index";
self.data
.read_uleb128()
.read_error(err)?
.try_into()
.map_err(|_| ())
.read_error(err)
.map(Some)
}
}

impl<'data> Iterator for AttributeIndexIterator<'data> {
type Item = Result<u32>;

fn next(&mut self) -> Option<Self::Item> {
self.next().transpose()
}
}

Expand Down
67 changes: 42 additions & 25 deletions src/read/elf/note.rs
Original file line number Diff line number Diff line change
Expand Up @@ -51,53 +51,56 @@ where

/// Returns the next note.
pub fn next(&mut self) -> read::Result<Option<Note<'data, Elf>>> {
let mut data = self.data;
if data.is_empty() {
if self.data.is_empty() {
return Ok(None);
}

let header = data
let result = self.parse().map(Some);
if result.is_err() {
self.data = Bytes(&[]);
}
result
}

fn parse(&mut self) -> read::Result<Note<'data, Elf>> {
let header = self
.data
.read_at::<Elf::NoteHeader>(0)
.read_error("ELF note is too short")?;

// The name has no alignment requirement.
let offset = mem::size_of::<Elf::NoteHeader>();
let namesz = header.n_namesz(self.endian) as usize;
let name = data
let name = self
.data
.read_bytes_at(offset, namesz)
.read_error("Invalid ELF note namesz")?
.0;

// The descriptor must be aligned.
let offset = util::align(offset + namesz, self.align);
let descsz = header.n_descsz(self.endian) as usize;
let desc = data
let desc = self
.data
.read_bytes_at(offset, descsz)
.read_error("Invalid ELF note descsz")?
.0;

// The next note (if any) must be aligned.
let offset = util::align(offset + descsz, self.align);
if data.skip(offset).is_err() {
data = Bytes(&[]);
if self.data.skip(offset).is_err() {
self.data = Bytes(&[]);
}
self.data = data;

Ok(Some(Note { header, name, desc }))
Ok(Note { header, name, desc })
}
}

impl<'data, Elf: FileHeader> Iterator for NoteIterator<'data, Elf> {
type Item = read::Result<Note<'data, Elf>>;

fn next(&mut self) -> Option<Self::Item> {
match self.next() {
Err(e) => {
self.data = Bytes(&[]);
Some(Err(e))
}
Ok(Some(v)) => Some(Ok(v)),
Ok(None) => None,
}
self.next().transpose()
}
}

Expand Down Expand Up @@ -238,23 +241,37 @@ pub struct GnuPropertyIterator<'data, Endian: endian::Endian> {
impl<'data, Endian: endian::Endian> GnuPropertyIterator<'data, Endian> {
/// Returns the next property.
pub fn next(&mut self) -> read::Result<Option<GnuProperty<'data>>> {
let mut data = self.data;
if data.is_empty() {
if self.data.is_empty() {
return Ok(None);
}

let result = self.parse().map(Some);
if result.is_err() {
self.data = Bytes(&[]);
}
result
}

fn parse(&mut self) -> read::Result<GnuProperty<'data>> {
(|| -> Result<_, ()> {
let pr_type = data.read_at::<U32<Endian>>(0)?.get(self.endian);
let pr_datasz = data.read_at::<U32<Endian>>(4)?.get(self.endian) as usize;
let pr_data = data.read_bytes_at(8, pr_datasz)?.0;
data.skip(util::align(8 + pr_datasz, self.align))?;
self.data = data;
Ok(Some(GnuProperty { pr_type, pr_data }))
let pr_type = self.data.read_at::<U32<Endian>>(0)?.get(self.endian);
let pr_datasz = self.data.read_at::<U32<Endian>>(4)?.get(self.endian) as usize;
let pr_data = self.data.read_bytes_at(8, pr_datasz)?.0;
self.data.skip(util::align(8 + pr_datasz, self.align))?;
Ok(GnuProperty { pr_type, pr_data })
})()
.read_error("Invalid ELF GNU property")
}
}

impl<'data, Endian: endian::Endian> Iterator for GnuPropertyIterator<'data, Endian> {
type Item = read::Result<GnuProperty<'data>>;

fn next(&mut self) -> Option<Self::Item> {
self.next().transpose()
}
}

/// A property in a [`elf::NT_GNU_PROPERTY_TYPE_0`] note.
#[derive(Debug)]
pub struct GnuProperty<'data> {
Expand Down
87 changes: 80 additions & 7 deletions src/read/elf/version.rs
Original file line number Diff line number Diff line change
Expand Up @@ -252,6 +252,14 @@ impl<'data, Elf: FileHeader> VerdefIterator<'data, Elf> {
return Ok(None);
}

let result = self.parse().map(Some);
if result.is_err() {
self.data = Bytes(&[]);
}
result
}

fn parse(&mut self) -> Result<(&'data elf::Verdef<Elf::Endian>, VerdauxIterator<'data, Elf>)> {
let verdef = self
.data
.read_at::<elf::Verdef<_>>(0)
Expand All @@ -272,7 +280,15 @@ impl<'data, Elf: FileHeader> VerdefIterator<'data, Elf> {
} else {
self.data = Bytes(&[]);
}
Ok(Some((verdef, verdaux)))
Ok((verdef, verdaux))
}
}

impl<'data, Elf: FileHeader> Iterator for VerdefIterator<'data, Elf> {
type Item = Result<(&'data elf::Verdef<Elf::Endian>, VerdauxIterator<'data, Elf>)>;

fn next(&mut self) -> Option<Self::Item> {
self.next().transpose()
}
}

Expand All @@ -299,6 +315,16 @@ impl<'data, Elf: FileHeader> VerdauxIterator<'data, Elf> {
return Ok(None);
}

let result = self.parse().map(Some);
if result.is_err() {
self.count = 0;
} else {
self.count -= 1;
}
result
}

fn parse(&mut self) -> Result<&'data elf::Verdaux<Elf::Endian>> {
let verdaux = self
.data
.read_at::<elf::Verdaux<_>>(0)
Expand All @@ -307,8 +333,15 @@ impl<'data, Elf: FileHeader> VerdauxIterator<'data, Elf> {
self.data
.skip(verdaux.vda_next.get(self.endian) as usize)
.read_error("Invalid ELF vda_next")?;
self.count -= 1;
Ok(Some(verdaux))
Ok(verdaux)
}
}

impl<'data, Elf: FileHeader> Iterator for VerdauxIterator<'data, Elf> {
type Item = Result<&'data elf::Verdaux<Elf::Endian>>;

fn next(&mut self) -> Option<Self::Item> {
self.next().transpose()
}
}

Expand Down Expand Up @@ -340,6 +373,19 @@ impl<'data, Elf: FileHeader> VerneedIterator<'data, Elf> {
return Ok(None);
}

let result = self.parse().map(Some);
if result.is_err() {
self.data = Bytes(&[]);
}
result
}

fn parse(
&mut self,
) -> Result<(
&'data elf::Verneed<Elf::Endian>,
VernauxIterator<'data, Elf>,
)> {
let verneed = self
.data
.read_at::<elf::Verneed<_>>(0)
Expand All @@ -360,7 +406,18 @@ impl<'data, Elf: FileHeader> VerneedIterator<'data, Elf> {
} else {
self.data = Bytes(&[]);
}
Ok(Some((verneed, vernaux)))
Ok((verneed, vernaux))
}
}

impl<'data, Elf: FileHeader> Iterator for VerneedIterator<'data, Elf> {
type Item = Result<(
&'data elf::Verneed<Elf::Endian>,
VernauxIterator<'data, Elf>,
)>;

fn next(&mut self) -> Option<Self::Item> {
self.next().transpose()
}
}

Expand All @@ -387,16 +444,32 @@ impl<'data, Elf: FileHeader> VernauxIterator<'data, Elf> {
return Ok(None);
}

let result = self.parse().map(Some);
if result.is_err() {
self.count = 0;
} else {
self.count -= 1;
}
result
}

fn parse(&mut self) -> Result<&'data elf::Vernaux<Elf::Endian>> {
let vernaux = self
.data
.read_at::<elf::Vernaux<_>>(0)
.read_error("ELF vernaux is too short")?;

self.data
.skip(vernaux.vna_next.get(self.endian) as usize)
.read_error("Invalid ELF vna_next")?;
self.count -= 1;
Ok(Some(vernaux))
Ok(vernaux)
}
}

impl<'data, Elf: FileHeader> Iterator for VernauxIterator<'data, Elf> {
type Item = Result<&'data elf::Vernaux<Elf::Endian>>;

fn next(&mut self) -> Option<Self::Item> {
self.next().transpose()
}
}

Expand Down
Loading

0 comments on commit b1aa6c3

Please sign in to comment.