use crate::{
ParseEhdrError, ParsePhdrError, Result,
elf::{
ElfClass, ElfEhdrRaw, ElfFileType, ElfLayout, ElfMachine, ElfPhdr, ElfShdr, NativeElfLayout,
},
};
use core::mem::size_of;
use elf::abi::{EI_CLASS, 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 machine(&self) -> ElfMachine {
ElfMachine::new(self.ehdr.e_machine())
}
#[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());
}
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) -> (usize, usize) {
table_range(self.e_phoff(), self.e_phentsize(), self.e_phnum())
}
#[inline]
pub(crate) fn checked_phdr_layout(&self) -> Result<Option<(usize, usize)>> {
checked_table_layout(
self.e_phentsize(),
size_of::<ElfPhdr<L>>(),
self.e_phnum(),
self.e_phoff(),
|| ParsePhdrError::malformed("program header table layout is invalid").into(),
)
}
#[inline]
pub fn shdr_range(&self) -> (usize, usize) {
table_range(self.e_shoff(), self.e_shentsize(), self.e_shnum())
}
#[inline]
#[cfg_attr(not(feature = "object"), allow(dead_code))]
pub(crate) fn checked_shdr_layout(&self) -> Result<Option<(usize, usize)>> {
checked_table_layout(
self.e_shentsize(),
size_of::<ElfShdr<L>>(),
self.e_shnum(),
self.e_shoff(),
|| ParseEhdrError::MissingSectionHeaders.into(),
)
}
}
#[inline]
const fn table_range(offset: usize, entsize: usize, count: usize) -> (usize, usize) {
let size = entsize * count;
(offset, offset + size)
}
#[inline]
fn checked_table_layout(
entsize: usize,
expected_entsize: usize,
count: usize,
offset: usize,
err: impl Fn() -> crate::Error,
) -> Result<Option<(usize, usize)>> {
if entsize != expected_entsize {
return Err(err());
}
let size = entsize.checked_mul(count).ok_or_else(&err)?;
if size == 0 {
return Ok(None);
}
offset.checked_add(size).ok_or_else(err)?;
Ok(Some((offset, size)))
}