use crate::{
IoError, ParseEhdrError, ParsePhdrError, ParseShdrError, ReadBoundsError, Result,
elf::{
ElfClass, ElfDataEncoding, ElfEhdrRaw, ElfFileType, ElfLayout, ElfMachine, ElfPhdr,
ElfShdr, NativeElfLayout,
},
};
use alloc::boxed::Box;
use core::mem::size_of;
use elf::abi::{EI_CLASS, EI_DATA, EI_VERSION, ELFMAGIC, EV_CURRENT};
#[repr(transparent)]
pub struct ElfHeader<L: ElfLayout = NativeElfLayout> {
ehdr: L::Ehdr,
}
impl<L: ElfLayout> ElfHeader<L> {
#[inline]
pub(crate) fn from_raw(ehdr: L::Ehdr, expected_machine: Option<ElfMachine>) -> Result<Self> {
let ehdr = Self { ehdr };
ehdr.validate(expected_machine)?;
Ok(ehdr)
}
#[inline]
pub fn is_dylib(&self) -> bool {
self.file_type() == ElfFileType::DYN
}
#[inline]
pub fn is_executable(&self) -> bool {
let file_type = self.file_type();
file_type == ElfFileType::EXEC || file_type == ElfFileType::DYN
}
#[inline]
pub fn class(&self) -> ElfClass {
ElfClass::new(self.ehdr.e_ident()[EI_CLASS])
}
#[inline]
pub fn data_encoding(&self) -> ElfDataEncoding {
ElfDataEncoding::new(self.ehdr.e_ident()[EI_DATA])
}
#[inline]
pub fn machine(&self) -> ElfMachine {
ElfMachine::new(self.ehdr.e_machine())
}
#[inline]
pub fn e_flags(&self) -> u32 {
self.ehdr.e_flags()
}
#[inline]
pub fn file_type(&self) -> ElfFileType {
ElfFileType::new(self.ehdr.e_type())
}
#[inline]
pub fn e_entry(&self) -> usize {
self.ehdr.e_entry()
}
pub(crate) fn validate(&self, expected_machine: Option<ElfMachine>) -> Result<()> {
if self.ehdr.e_ident()[0..4] != ELFMAGIC {
return Err(ParseEhdrError::InvalidMagic.into());
}
let class = self.class();
if class.raw() != L::E_CLASS {
return Err(ParseEhdrError::FileClassMismatch {
expected: ElfClass::new(L::E_CLASS),
found: class,
}
.into());
}
let data_encoding = self.data_encoding();
if data_encoding != L::DATA_ENCODING {
return Err(ParseEhdrError::FileEndianMismatch {
expected: L::DATA_ENCODING,
found: data_encoding,
}
.into());
}
if self.ehdr.e_ident()[EI_VERSION] != EV_CURRENT {
return Err(ParseEhdrError::InvalidVersion.into());
}
if let Some(expected) = expected_machine {
let machine = self.machine();
if machine != expected {
return Err(ParseEhdrError::FileArchMismatch {
expected,
found: machine,
}
.into());
}
}
Ok(())
}
#[inline]
pub fn e_phnum(&self) -> usize {
self.ehdr.e_phnum()
}
#[inline]
pub fn e_phentsize(&self) -> usize {
self.ehdr.e_phentsize()
}
#[inline]
pub fn e_phoff(&self) -> usize {
self.ehdr.e_phoff()
}
#[inline]
pub fn e_shoff(&self) -> usize {
self.ehdr.e_shoff()
}
#[inline]
pub fn e_shentsize(&self) -> usize {
self.ehdr.e_shentsize()
}
#[inline]
pub fn e_shnum(&self) -> usize {
self.ehdr.e_shnum()
}
#[inline]
pub fn e_shstrndx(&self) -> usize {
self.ehdr.e_shstrndx()
}
#[inline]
pub fn phdr_range(&self) -> Result<Option<(usize, usize)>> {
checked_table_range(self.e_phentsize(), self.e_phnum(), self.e_phoff())
}
#[inline]
pub(crate) fn checked_phdr_layout(&self, object_len: usize) -> Result<Option<(usize, usize)>> {
if self.e_phentsize() != size_of::<ElfPhdr<L>>() {
return Err(ParsePhdrError::InvalidEntrySize {
expected: size_of::<ElfPhdr<L>>(),
found: self.e_phentsize(),
}
.into());
}
checked_table_layout(
self.e_phentsize(),
self.e_phnum(),
self.e_phoff(),
object_len,
)
}
#[inline]
pub fn shdr_range(&self) -> Result<Option<(usize, usize)>> {
checked_table_range(self.e_shentsize(), self.e_shnum(), self.e_shoff())
}
#[inline]
pub(crate) fn checked_shdr_layout(&self, object_len: usize) -> Result<Option<(usize, usize)>> {
if self.e_shentsize() != size_of::<ElfShdr<L>>() {
return Err(ParseShdrError::InvalidEntrySize {
expected: size_of::<ElfShdr<L>>(),
found: self.e_shentsize(),
}
.into());
}
checked_table_layout(
self.e_shentsize(),
self.e_shnum(),
self.e_shoff(),
object_len,
)
}
}
#[inline]
fn checked_table_layout(
entsize: usize,
count: usize,
offset: usize,
object_len: usize,
) -> Result<Option<(usize, usize)>> {
let Some((offset, end)) = checked_table_range(entsize, count, offset)? else {
return Ok(None);
};
let size = end - offset;
if end > object_len {
return Err(IoError::ReadOutOfBounds(Box::new(ReadBoundsError::new(
offset, size, object_len,
)))
.into());
}
Ok(Some((offset, size)))
}
#[inline]
fn checked_table_range(
entsize: usize,
count: usize,
offset: usize,
) -> Result<Option<(usize, usize)>> {
let size = entsize
.checked_mul(count)
.ok_or(IoError::ReadBufferTooLarge)?;
if size == 0 {
return Ok(None);
}
let end = offset
.checked_add(size)
.ok_or(IoError::ReadBufferTooLarge)?;
Ok(Some((offset, end)))
}