Change the Parse trait to take a ReadExt

This eliminates the Endian argument from Parse::parse by bundling it into the ReadExt's responsibility and
allows implementors of Parse to not have to care about endianness and plumbing it around correctly.

Also, change FileHeader::parse to no longer implement Parse. It previously needed to take bogus unused Endian and Class
specifications. There's a unique bootstrapping situation with parsing the FileHeader, as it is the structure that informs
the Endian and Class specifications used by all the other parsers. I opted to simply make this implement parse() (but not
for the trait Parse) by taking the underlying delegate reader rather than a ReadExt which seemed intuitive to me.
This commit is contained in:
Christopher Cole 2022-10-07 12:53:36 -07:00
parent 79a54e21cb
commit 3fc22a31bf
No known key found for this signature in database
GPG Key ID: 0AC856975983E9DB
5 changed files with 83 additions and 137 deletions

View File

@ -1,5 +1,5 @@
use crate::gabi;
use crate::parse::{Endian, Parse, Reader, ReadExt};
use crate::parse::{Endian, Reader, ReadExt};
/// Encapsulates the contents of the ELF File Header
///
@ -97,13 +97,8 @@ impl FileHeader {
return Ok(());
}
}
impl<R> Parse<R> for FileHeader
where
R: std::io::Read,
{
fn parse(_endian: Endian, _class: Class, reader: &mut R) -> Result<Self, crate::ParseError> {
pub fn parse<R: std::io::Read>(reader: &mut R) -> Result<Self, crate::ParseError> {
let mut ident = [0u8; gabi::EI_NIDENT];
Self::parse_ident(reader, &mut ident)?;
@ -111,7 +106,7 @@ where
let endian = if ident[gabi::EI_DATA] == gabi::ELFDATA2LSB { Endian::Little } else { Endian::Big };
let mut io_r = Reader::new(reader, endian);
let elftype = ObjectFileType(io_r.read_u16()?);
let arch = Architecture(io_r.read_u16()?);
let version = io_r.read_u32()?;
@ -473,7 +468,6 @@ impl std::fmt::Display for Architecture {
mod tests {
use crate::file::{Architecture, Class, Endian, FileHeader, ObjectFileType, OSABI};
use crate::gabi;
use crate::parse::Parse;
#[test]
fn test_parse_ident_empty_buf_errors() {
@ -653,12 +647,7 @@ mod tests {
let slice: &mut [u8] = data.as_mut_slice();
assert_eq!(
FileHeader::parse(
Endian::Little,
Class(gabi::ELFCLASSNONE),
&mut slice.as_ref()
)
.unwrap(),
FileHeader::parse(&mut slice.as_ref()).unwrap(),
FileHeader {
class: Class(gabi::ELFCLASS32),
endianness: Endian::Little,
@ -705,12 +694,7 @@ mod tests {
for n in 0..36 {
let slice = data.as_mut_slice().split_at(gabi::EI_NIDENT + n).0;
assert!(FileHeader::parse(
Endian::Little,
Class(gabi::ELFCLASSNONE),
&mut slice.as_ref()
)
.is_err());
assert!(FileHeader::parse(&mut slice.as_ref()).is_err());
}
}
@ -741,12 +725,7 @@ mod tests {
let slice: &mut [u8] = data.as_mut_slice();
assert_eq!(
FileHeader::parse(
Endian::Little,
Class(gabi::ELFCLASSNONE),
&mut slice.as_ref()
)
.unwrap(),
FileHeader::parse(&mut slice.as_ref()).unwrap(),
FileHeader {
class: Class(gabi::ELFCLASS64),
endianness: Endian::Big,
@ -793,12 +772,7 @@ mod tests {
for n in 0..48 {
let slice = data.as_mut_slice().split_at(gabi::EI_NIDENT + n).0;
assert!(FileHeader::parse(
Endian::Little,
Class(gabi::ELFCLASSNONE),
&mut slice.as_ref()
)
.is_err());
assert!(FileHeader::parse(&mut slice.as_ref()).is_err());
}
}
}

View File

@ -10,7 +10,7 @@ pub mod section;
pub mod symbol;
pub mod parse;
use crate::parse::{Endian, Parse, read_u16, read_u32, read_u64};
use crate::parse::{Parse, Reader, read_u16, read_u32, read_u64};
mod utils;
@ -73,14 +73,15 @@ impl File {
}
pub fn open_stream<T: Read + Seek>(io_file: &mut T) -> Result<File, ParseError> {
let ehdr = file::FileHeader::parse(Endian::Little, file::Class(gabi::ELFCLASSNONE), io_file)?;
let ehdr = file::FileHeader::parse(io_file)?;
// Parse the program headers
io_file.seek(io::SeekFrom::Start(ehdr.e_phoff))?;
let mut phdrs = Vec::<segment::ProgramHeader>::default();
for _ in 0..ehdr.e_phnum {
let phdr = segment::ProgramHeader::parse(ehdr.endianness, ehdr.class, io_file)?;
let mut reader = Reader::new(io_file, ehdr.endianness);
let phdr = segment::ProgramHeader::parse(ehdr.class, &mut reader)?;
phdrs.push(phdr);
}
@ -89,7 +90,8 @@ impl File {
// Parse the section headers
io_file.seek(io::SeekFrom::Start(ehdr.e_shoff))?;
for _ in 0..ehdr.e_shnum {
let shdr = section::SectionHeader::parse(ehdr.endianness, ehdr.class, io_file)?;
let mut reader = Reader::new(io_file, ehdr.endianness);
let shdr = section::SectionHeader::parse(ehdr.class, &mut reader)?;
sections.push(
Section {
name: String::new(),

View File

@ -98,9 +98,9 @@ pub fn read_u64<T: std::io::Read>(endian: Endian, io: &mut T) -> Result<u64, Par
}
pub trait Parse<R>: Copy {
fn parse(endian: Endian, class: Class, reader: &mut R) -> Result<Self, crate::ParseError>
fn parse(class: Class, reader: &mut R) -> Result<Self, crate::ParseError>
where
R: std::io::Read;
R: ReadExt;
}
#[cfg(test)]

View File

@ -1,6 +1,6 @@
use crate::file::Class;
use crate::gabi;
use crate::parse::{Endian, Parse, Reader, ReadExt};
use crate::parse::{Parse, ReadExt};
/// Encapsulates the contents of an ELF Section Header
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
@ -29,36 +29,35 @@ pub struct SectionHeader {
impl<R> Parse<R> for SectionHeader
where
R: std::io::Read,
R: ReadExt,
{
fn parse(endian: Endian, class: Class, reader: &mut R) -> Result<Self, crate::ParseError> {
let mut io_r = Reader::new(reader, endian);
fn parse(class: Class, reader: &mut R) -> Result<Self, crate::ParseError> {
if class == gabi::ELFCLASS32 {
return Ok(SectionHeader {
sh_name: io_r.read_u32()?,
sh_type: SectionType(io_r.read_u32()?),
sh_flags: SectionFlag(io_r.read_u32()? as u64),
sh_addr: io_r.read_u32()? as u64,
sh_offset: io_r.read_u32()? as u64,
sh_size: io_r.read_u32()? as u64,
sh_link: io_r.read_u32()?,
sh_info: io_r.read_u32()?,
sh_addralign: io_r.read_u32()? as u64,
sh_entsize: io_r.read_u32()? as u64,
sh_name: reader.read_u32()?,
sh_type: SectionType(reader.read_u32()?),
sh_flags: SectionFlag(reader.read_u32()? as u64),
sh_addr: reader.read_u32()? as u64,
sh_offset: reader.read_u32()? as u64,
sh_size: reader.read_u32()? as u64,
sh_link: reader.read_u32()?,
sh_info: reader.read_u32()?,
sh_addralign: reader.read_u32()? as u64,
sh_entsize: reader.read_u32()? as u64,
});
}
Ok(SectionHeader {
sh_name: io_r.read_u32()?,
sh_type: SectionType(io_r.read_u32()?),
sh_flags: SectionFlag(io_r.read_u64()?),
sh_addr: io_r.read_u64()?,
sh_offset: io_r.read_u64()?,
sh_size: io_r.read_u64()?,
sh_link: io_r.read_u32()?,
sh_info: io_r.read_u32()?,
sh_addralign: io_r.read_u64()?,
sh_entsize: io_r.read_u64()?,
sh_name: reader.read_u32()?,
sh_type: SectionType(reader.read_u32()?),
sh_flags: SectionFlag(reader.read_u64()?),
sh_addr: reader.read_u64()?,
sh_offset: reader.read_u64()?,
sh_size: reader.read_u64()?,
sh_link: reader.read_u32()?,
sh_info: reader.read_u32()?,
sh_addralign: reader.read_u64()?,
sh_entsize: reader.read_u64()?,
})
}
}
@ -136,20 +135,16 @@ impl std::fmt::Display for SectionFlag {
mod tests {
use crate::file::Class;
use crate::gabi;
use crate::parse::{Endian, Parse};
use crate::parse::{Endian, Parse, Reader};
use crate::section::{SectionFlag, SectionHeader, SectionType};
#[test]
fn parse_shdr32_fuzz_too_short() {
let data = [0u8; 40];
for n in 0..40 {
let slice = data.split_at(n).0;
assert!(SectionHeader::parse(
Endian::Little,
Class(gabi::ELFCLASS32),
&mut slice.as_ref()
)
.is_err());
let mut slice = data.split_at(n).0.as_ref();
let mut reader = Reader::new(&mut slice, Endian::Little);
assert!(SectionHeader::parse(Class(gabi::ELFCLASS32), &mut reader).is_err());
}
}
@ -160,13 +155,10 @@ mod tests {
data[n as usize] = n;
}
let mut slice = data.as_ref();
let mut reader = Reader::new(&mut slice, Endian::Little);
assert_eq!(
SectionHeader::parse(
Endian::Little,
Class(gabi::ELFCLASS32),
&mut data.as_ref()
)
.unwrap(),
SectionHeader::parse(Class(gabi::ELFCLASS32), &mut reader).unwrap(),
SectionHeader {
sh_name: 0x03020100,
sh_type: SectionType(0x07060504),
@ -186,13 +178,9 @@ mod tests {
fn parse_shdr64_fuzz_too_short() {
let data = [0u8; 64];
for n in 0..64 {
let slice = data.split_at(n).0;
assert!(SectionHeader::parse(
Endian::Big,
Class(gabi::ELFCLASS64),
&mut slice.as_ref()
)
.is_err());
let mut slice = data.split_at(n).0.as_ref();
let mut reader = Reader::new(&mut slice, Endian::Big);
assert!(SectionHeader::parse(Class(gabi::ELFCLASS64), &mut reader).is_err());
}
}
@ -203,13 +191,10 @@ mod tests {
data[n as usize] = n;
}
let mut slice = data.as_ref();
let mut reader = Reader::new(&mut slice, Endian::Big);
assert_eq!(
SectionHeader::parse(
Endian::Big,
Class(gabi::ELFCLASS64),
&mut data.as_ref()
)
.unwrap(),
SectionHeader::parse(Class(gabi::ELFCLASS64), &mut reader).unwrap(),
SectionHeader {
sh_name: 0x00010203,
sh_type: SectionType(0x04050607),

View File

@ -1,6 +1,6 @@
use crate::file::Class;
use crate::gabi;
use crate::parse::{Endian, Parse, Reader, ReadExt};
use crate::parse::{Parse, ReadExt};
/// Encapsulates the contents of an ELF Program Header
///
@ -28,19 +28,18 @@ pub struct ProgramHeader {
impl<R> Parse<R> for ProgramHeader
where
R: std::io::Read,
R: ReadExt,
{
fn parse(endian: Endian, class: Class, reader: &mut R) -> Result<Self, crate::ParseError> {
let mut io_r = Reader::new(reader, endian);
fn parse(class: Class, reader: &mut R) -> Result<Self, crate::ParseError> {
if class == gabi::ELFCLASS32 {
let p_type = io_r.read_u32()?;
let p_offset = io_r.read_u32()?;
let p_vaddr = io_r.read_u32()?;
let p_paddr = io_r.read_u32()?;
let p_filesz = io_r.read_u32()?;
let p_memsz = io_r.read_u32()?;
let p_flags = io_r.read_u32()?;
let p_align = io_r.read_u32()?;
let p_type = reader.read_u32()?;
let p_offset = reader.read_u32()?;
let p_vaddr = reader.read_u32()?;
let p_paddr = reader.read_u32()?;
let p_filesz = reader.read_u32()?;
let p_memsz = reader.read_u32()?;
let p_flags = reader.read_u32()?;
let p_align = reader.read_u32()?;
return Ok(ProgramHeader {
progtype: ProgType(p_type),
offset: p_offset as u64,
@ -53,14 +52,14 @@ where
});
}
let p_type = io_r.read_u32()?;
let p_flags = io_r.read_u32()?;
let p_offset = io_r.read_u64()?;
let p_vaddr = io_r.read_u64()?;
let p_paddr = io_r.read_u64()?;
let p_filesz = io_r.read_u64()?;
let p_memsz = io_r.read_u64()?;
let p_align = io_r.read_u64()?;
let p_type = reader.read_u32()?;
let p_flags = reader.read_u32()?;
let p_offset = reader.read_u64()?;
let p_vaddr = reader.read_u64()?;
let p_paddr = reader.read_u64()?;
let p_filesz = reader.read_u64()?;
let p_memsz = reader.read_u64()?;
let p_align = reader.read_u64()?;
Ok(ProgramHeader {
progtype: ProgType(p_type),
offset: p_offset,
@ -146,20 +145,16 @@ impl std::fmt::Display for ProgType {
mod tests {
use crate::file::Class;
use crate::gabi;
use crate::parse::{Endian, Parse};
use crate::parse::{Endian, Parse, Reader};
use crate::segment::{ProgFlag, ProgType, ProgramHeader};
#[test]
fn parse_phdr32_fuzz_too_short() {
let data = [0u8; 32];
for n in 0..32 {
let slice = data.split_at(n).0;
assert!(ProgramHeader::parse(
Endian::Little,
Class(gabi::ELFCLASS32),
&mut slice.as_ref()
)
.is_err());
let mut slice = data.split_at(n).0.as_ref();
let mut reader = Reader::new(&mut slice, Endian::Little);
assert!(ProgramHeader::parse(Class(gabi::ELFCLASS32), &mut reader).is_err());
}
}
@ -170,13 +165,10 @@ mod tests {
data[n as usize] = n;
}
let mut slice = data.as_ref();
let mut reader = Reader::new(&mut slice, Endian::Little);
assert_eq!(
ProgramHeader::parse(
Endian::Little,
Class(gabi::ELFCLASS32),
&mut data.as_ref()
)
.unwrap(),
ProgramHeader::parse(Class(gabi::ELFCLASS32), &mut reader).unwrap(),
ProgramHeader {
progtype: ProgType(0x03020100),
offset: 0x07060504,
@ -194,13 +186,9 @@ mod tests {
fn parse_phdr64_fuzz_too_short() {
let data = [0u8; 56];
for n in 0..56 {
let slice = data.split_at(n).0;
assert!(ProgramHeader::parse(
Endian::Big,
Class(gabi::ELFCLASS64),
&mut slice.as_ref()
)
.is_err());
let mut slice = data.split_at(n).0.as_ref();
let mut reader = Reader::new(&mut slice, Endian::Big);
assert!(ProgramHeader::parse(Class(gabi::ELFCLASS64), &mut reader).is_err());
}
}
@ -211,13 +199,10 @@ mod tests {
data[n as usize] = n;
}
let mut slice = data.as_ref();
let mut reader = Reader::new(&mut slice, Endian::Big);
assert_eq!(
ProgramHeader::parse(
Endian::Big,
Class(gabi::ELFCLASS64),
&mut data.as_ref()
)
.unwrap(),
ProgramHeader::parse(Class(gabi::ELFCLASS64), &mut reader).unwrap(),
ProgramHeader {
progtype: ProgType(0x00010203),
offset: 0x08090A0B0C0D0E0F,