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()
}
}
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub struct ProgramHeader {
pub p_type: ProgType,
pub p_offset: u64,
pub p_vaddr: u64,
pub p_paddr: u64,
pub p_filesz: u64,
pub p_memsz: u64,
pub p_flags: ProgFlag,
pub p_align: u64,
}
impl ProgramHeader {
pub fn parse_at<P: ParseAtExt>(
endian: Endian,
class: Class,
offset: &mut usize,
parser: &P,
) -> Result<Self, ParseError> {
if class == Class::ELF32 {
return Ok(ProgramHeader {
p_type: ProgType(parser.parse_u32_at(endian, offset)?),
p_offset: parser.parse_u32_at(endian, offset)? as u64,
p_vaddr: parser.parse_u32_at(endian, offset)? as u64,
p_paddr: parser.parse_u32_at(endian, offset)? as u64,
p_filesz: parser.parse_u32_at(endian, offset)? as u64,
p_memsz: parser.parse_u32_at(endian, offset)? as u64,
p_flags: ProgFlag(parser.parse_u32_at(endian, offset)?),
p_align: parser.parse_u32_at(endian, offset)? as u64,
});
}
let p_type = parser.parse_u32_at(endian, offset)?;
let p_flags = parser.parse_u32_at(endian, offset)?;
let p_offset = parser.parse_u64_at(endian, offset)?;
let p_vaddr = parser.parse_u64_at(endian, offset)?;
let p_paddr = parser.parse_u64_at(endian, offset)?;
let p_filesz = parser.parse_u64_at(endian, offset)?;
let p_memsz = parser.parse_u64_at(endian, offset)?;
let p_align = parser.parse_u64_at(endian, offset)?;
Ok(ProgramHeader {
p_type: ProgType(p_type),
p_offset,
p_vaddr,
p_paddr,
p_filesz,
p_memsz,
p_flags: ProgFlag(p_flags),
p_align,
})
}
}
impl core::fmt::Display for ProgramHeader {
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
write!(f, "Program Header: Type: {} Offset: {:#010x} VirtAddr: {:#010x} PhysAddr: {:#010x} FileSize: {:#06x} MemSize: {:#06x} Flags: {} Align: {:#x}",
self.p_type, self.p_offset, self.p_vaddr, self.p_paddr, self.p_filesz,
self.p_memsz, self.p_flags, self.p_align)
}
}
#[derive(Copy, Clone, PartialEq, Eq)]
pub struct ProgFlag(pub u32);
impl core::fmt::Debug for ProgFlag {
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
write!(f, "{:#x}", self.0)
}
}
impl core::fmt::Display for ProgFlag {
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
if (self.0 & gabi::PF_R) != 0 {
write!(f, "R")?;
} else {
write!(f, " ")?;
}
if (self.0 & gabi::PF_W) != 0 {
write!(f, "W")?;
} else {
write!(f, " ")?;
}
if (self.0 & gabi::PF_X) != 0 {
write!(f, "E")
} else {
write!(f, " ")
}
}
}
#[derive(Copy, Clone, PartialEq, Eq)]
pub struct ProgType(pub u32);
impl core::fmt::Debug for ProgType {
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
write!(f, "{:#x}", self.0)
}
}
impl core::fmt::Display for ProgType {
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
let str = match self.0 {
gabi::PT_NULL => "NULL",
gabi::PT_LOAD => "LOAD",
gabi::PT_DYNAMIC => "DYNAMIC",
gabi::PT_INTERP => "INTERP",
gabi::PT_NOTE => "NOTE",
gabi::PT_SHLIB => "SHLIB",
gabi::PT_PHDR => "PHDR",
gabi::PT_TLS => "TLS",
gabi::PT_GNU_EH_FRAME => "GNU_EH_FRAME",
gabi::PT_GNU_STACK => "GNU_STACK",
gabi::PT_GNU_RELRO => "GNU_RELRO",
_ => "Unknown",
};
write!(f, "{}", str)
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::parse::Endian;
#[test]
fn parse_phdr32_fuzz_too_short() {
let data = [0u8; 32];
for n in 0..32 {
let buf = data.split_at(n).0.as_ref();
let mut offset: usize = 0;
let error = ProgramHeader::parse_at(Endian::Little, Class::ELF32, &mut offset, &buf)
.expect_err("Expected an error");
assert!(
matches!(error, ParseError::BadOffset(_)),
"Unexpected Error type found: {error}"
);
}
}
#[test]
fn parse_phdr32_works() {
let mut data = [0u8; 32];
for n in 0u8..32 {
data[n as usize] = n;
}
let buf = data.as_ref();
let mut offset: usize = 0;
assert_eq!(
ProgramHeader::parse_at(Endian::Little, Class::ELF32, &mut offset, &buf).unwrap(),
ProgramHeader {
p_type: ProgType(0x03020100),
p_offset: 0x07060504,
p_vaddr: 0xB0A0908,
p_paddr: 0x0F0E0D0C,
p_filesz: 0x13121110,
p_memsz: 0x17161514,
p_flags: ProgFlag(0x1B1A1918),
p_align: 0x1F1E1D1C,
}
);
}
#[test]
fn parse_phdr64_fuzz_too_short() {
let data = [0u8; 56];
for n in 0..56 {
let buf = data.split_at(n).0.as_ref();
let mut offset: usize = 0;
let error = ProgramHeader::parse_at(Endian::Big, Class::ELF64, &mut offset, &buf)
.expect_err("Expected an error");
assert!(
matches!(error, ParseError::BadOffset(_)),
"Unexpected Error type found: {error}"
);
}
}
#[test]
fn parse_phdr64_works() {
let mut data = [0u8; 56];
for n in 0u8..56 {
data[n as usize] = n;
}
let buf = data.as_ref();
let mut offset: usize = 0;
assert_eq!(
ProgramHeader::parse_at(Endian::Big, Class::ELF64, &mut offset, &buf).unwrap(),
ProgramHeader {
p_type: ProgType(0x00010203),
p_offset: 0x08090A0B0C0D0E0F,
p_vaddr: 0x1011121314151617,
p_paddr: 0x18191A1B1C1D1E1F,
p_filesz: 0x2021222324252627,
p_memsz: 0x28292A2B2C2D2E2F,
p_flags: ProgFlag(0x04050607),
p_align: 0x3031323334353637,
}
);
}
}