use super::{
layout::{ElfLayout, NativeElfLayout},
raw::ElfPhdrRaw,
};
use crate::memory::{MappedView, VmOffset};
use alloc::vec::Vec;
use bitflags::bitflags;
use core::fmt::{self, Display};
use elf::abi::*;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[repr(transparent)]
pub struct ElfProgramType(u32);
impl ElfProgramType {
pub const NULL: Self = Self(PT_NULL);
pub const LOAD: Self = Self(PT_LOAD);
pub const DYNAMIC: Self = Self(PT_DYNAMIC);
pub const INTERP: Self = Self(PT_INTERP);
pub const NOTE: Self = Self(PT_NOTE);
pub const SHLIB: Self = Self(PT_SHLIB);
pub const PHDR: Self = Self(PT_PHDR);
pub const TLS: Self = Self(PT_TLS);
pub const GNU_EH_FRAME: Self = Self(PT_GNU_EH_FRAME);
pub const GNU_PROPERTY: Self = Self(PT_GNU_PROPERTY);
pub const GNU_STACK: Self = Self(PT_GNU_STACK);
pub const GNU_RELRO: Self = Self(PT_GNU_RELRO);
#[inline]
pub const fn new(raw: u32) -> Self {
Self(raw)
}
#[inline]
pub const fn raw(self) -> u32 {
self.0
}
}
impl From<u32> for ElfProgramType {
#[inline]
fn from(value: u32) -> Self {
Self::new(value)
}
}
impl From<ElfProgramType> for u32 {
#[inline]
fn from(value: ElfProgramType) -> Self {
value.raw()
}
}
impl Display for ElfProgramType {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self.0 {
PT_NULL => f.write_str("PT_NULL"),
PT_LOAD => f.write_str("PT_LOAD"),
PT_DYNAMIC => f.write_str("PT_DYNAMIC"),
PT_INTERP => f.write_str("PT_INTERP"),
PT_NOTE => f.write_str("PT_NOTE"),
PT_SHLIB => f.write_str("PT_SHLIB"),
PT_PHDR => f.write_str("PT_PHDR"),
PT_TLS => f.write_str("PT_TLS"),
PT_GNU_EH_FRAME => f.write_str("PT_GNU_EH_FRAME"),
PT_GNU_PROPERTY => f.write_str("PT_GNU_PROPERTY"),
PT_GNU_STACK => f.write_str("PT_GNU_STACK"),
PT_GNU_RELRO => f.write_str("PT_GNU_RELRO"),
raw => write!(f, "unknown ELF program type {raw}"),
}
}
}
bitflags! {
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub struct ElfProgramFlags: u32 {
const EXEC = PF_X;
const WRITE = PF_W;
const READ = PF_R;
}
}
#[derive(Debug)]
#[repr(transparent)]
pub struct ElfPhdr<L: ElfLayout = NativeElfLayout> {
phdr: L::Phdr,
}
impl<L: ElfLayout> ElfPhdr<L> {
#[inline]
#[allow(clippy::too_many_arguments)]
pub fn new(
program_type: ElfProgramType,
flags: ElfProgramFlags,
p_offset: usize,
p_vaddr: VmOffset,
p_paddr: usize,
p_filesz: usize,
p_memsz: usize,
p_align: usize,
) -> Self {
let mut phdr: L::Phdr = unsafe { core::mem::zeroed() };
phdr.set_p_type(program_type.raw());
phdr.set_p_flags(flags.bits());
phdr.set_p_offset(p_offset);
phdr.set_p_vaddr(p_vaddr.get());
phdr.set_p_paddr(p_paddr);
phdr.set_p_filesz(p_filesz);
phdr.set_p_memsz(p_memsz);
phdr.set_p_align(p_align);
Self { phdr }
}
#[inline]
pub fn program_type(&self) -> ElfProgramType {
ElfProgramType::new(self.phdr.p_type())
}
#[inline]
pub fn flags(&self) -> ElfProgramFlags {
ElfProgramFlags::from_bits_retain(self.phdr.p_flags())
}
#[inline]
pub fn p_offset(&self) -> usize {
self.phdr.p_offset()
}
#[inline]
pub fn p_vaddr(&self) -> VmOffset {
VmOffset::new(self.phdr.p_vaddr())
}
#[inline]
pub fn p_paddr(&self) -> usize {
self.phdr.p_paddr()
}
#[inline]
pub fn p_filesz(&self) -> usize {
self.phdr.p_filesz()
}
#[inline]
pub fn p_memsz(&self) -> usize {
self.phdr.p_memsz()
}
#[inline]
pub fn p_align(&self) -> usize {
self.phdr.p_align()
}
#[inline]
pub fn set_program_type(&mut self, program_type: ElfProgramType) {
self.phdr.set_p_type(program_type.raw());
}
#[inline]
pub fn set_flags(&mut self, flags: ElfProgramFlags) {
self.phdr.set_p_flags(flags.bits());
}
#[inline]
pub fn set_p_offset(&mut self, p_offset: usize) {
self.phdr.set_p_offset(p_offset);
}
#[inline]
pub fn set_p_vaddr(&mut self, p_vaddr: VmOffset) {
self.phdr.set_p_vaddr(p_vaddr.get());
}
#[inline]
pub fn set_p_paddr(&mut self, p_paddr: usize) {
self.phdr.set_p_paddr(p_paddr);
}
#[inline]
pub fn set_p_filesz(&mut self, p_filesz: usize) {
self.phdr.set_p_filesz(p_filesz);
}
#[inline]
pub fn set_p_memsz(&mut self, p_memsz: usize) {
self.phdr.set_p_memsz(p_memsz);
}
#[inline]
pub fn set_p_align(&mut self, p_align: usize) {
self.phdr.set_p_align(p_align);
}
}
impl<L: ElfLayout> Clone for ElfPhdr<L> {
#[inline]
fn clone(&self) -> Self {
Self::new(
self.program_type(),
self.flags(),
self.p_offset(),
self.p_vaddr(),
self.p_paddr(),
self.p_filesz(),
self.p_memsz(),
self.p_align(),
)
}
}
#[derive(Clone)]
pub(crate) enum ElfPhdrs<L: ElfLayout = NativeElfLayout> {
Mapped(MappedView<ElfPhdr<L>>),
Vec(Vec<ElfPhdr<L>>),
}
impl<L: ElfLayout> ElfPhdrs<L> {
pub(crate) fn as_slice(&self) -> &[ElfPhdr<L>] {
match self {
ElfPhdrs::Mapped(phdrs) => phdrs.as_slice(),
ElfPhdrs::Vec(phdrs) => phdrs,
}
}
}
#[cfg(test)]
mod tests {
use super::{ElfPhdr, ElfProgramFlags, ElfProgramType};
use crate::memory::VmOffset;
#[test]
fn owned_phdr_round_trips_and_mutates() {
let mut phdr: ElfPhdr = ElfPhdr::new(
ElfProgramType::LOAD,
ElfProgramFlags::READ | ElfProgramFlags::WRITE,
1,
VmOffset::new(2),
3,
4,
5,
6,
);
assert_eq!(phdr.program_type(), ElfProgramType::LOAD);
assert_eq!(phdr.flags(), ElfProgramFlags::READ | ElfProgramFlags::WRITE);
assert_eq!(phdr.p_offset(), 1);
assert_eq!(phdr.p_vaddr(), VmOffset::new(2));
assert_eq!(phdr.p_paddr(), 3);
assert_eq!(phdr.p_filesz(), 4);
assert_eq!(phdr.p_memsz(), 5);
assert_eq!(phdr.p_align(), 6);
phdr.set_program_type(ElfProgramType::GNU_PROPERTY);
phdr.set_flags(ElfProgramFlags::READ);
phdr.set_p_offset(7);
phdr.set_p_vaddr(VmOffset::new(8));
phdr.set_p_paddr(9);
phdr.set_p_filesz(10);
phdr.set_p_memsz(11);
phdr.set_p_align(12);
assert_eq!(phdr.program_type(), ElfProgramType::GNU_PROPERTY);
assert_eq!(phdr.flags(), ElfProgramFlags::READ);
assert_eq!(phdr.p_offset(), 7);
assert_eq!(phdr.p_vaddr(), VmOffset::new(8));
assert_eq!(phdr.p_paddr(), 9);
assert_eq!(phdr.p_filesz(), 10);
assert_eq!(phdr.p_memsz(), 11);
assert_eq!(phdr.p_align(), 12);
phdr.set_program_type(ElfProgramType::GNU_STACK);
assert_eq!(phdr.program_type(), ElfProgramType::GNU_STACK);
}
}