include!("constants_header.rs"); macro_rules! elf_header { ($size:ident) => { use core::fmt; #[repr(C)] #[derive(Clone, Copy, Default, PartialEq)] pub struct Header { /// Magic number and other info pub e_ident: [u8; SIZEOF_IDENT], /// Object file type pub e_type: u16, /// Architecture pub e_machine: u16, /// Object file version pub e_version: u32, /// Entry point virtual address pub e_entry: $size, /// Program header table file offset pub e_phoff: $size, /// Section header table file offset pub e_shoff: $size, /// Processor-specific flags pub e_flags: u32, /// ELF header size in bytes pub e_ehsize: u16, /// Program header table entry size pub e_phentsize: u16, /// Program header table entry count pub e_phnum: u16, /// Section header table entry size pub e_shentsize: u16, /// Section header table entry count pub e_shnum: u16, /// Section header string table index pub e_shstrndx: u16, } use plain; // Declare that this is a plain type. unsafe impl plain::Plain for Header {} impl Header { /// Returns the corresponding ELF header from the given byte array. pub fn from_bytes(bytes: &[u8; SIZEOF_EHDR]) -> &Header { // FIXME: Length is ensured correct because it's encoded in the type, // but it can still panic due to invalid alignment. plain::from_bytes(bytes).unwrap() } } impl fmt::Debug for Header { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { f.debug_struct("Header") .field("e_ident", &format_args!("{:?}", self.e_ident)) .field("e_type", &et_to_str(self.e_type)) .field("e_machine", &format_args!("0x{:x}", self.e_machine)) .field("e_version", &format_args!("0x{:x}", self.e_version)) .field("e_entry", &format_args!("0x{:x}", self.e_entry)) .field("e_phoff", &format_args!("0x{:x}", self.e_phoff)) .field("e_shoff", &format_args!("0x{:x}", self.e_shoff)) .field("e_flags", &format_args!("{:x}", self.e_flags)) .field("e_ehsize", &self.e_ehsize) .field("e_phentsize", &self.e_phentsize) .field("e_phnum", &self.e_phnum) .field("e_shentsize", &self.e_shentsize) .field("e_shnum", &self.e_shnum) .field("e_shstrndx", &self.e_shstrndx) .finish() } } }; } /// No file type. pub const ET_NONE: u16 = 0; /// Relocatable file. pub const ET_REL: u16 = 1; /// Executable file. pub const ET_EXEC: u16 = 2; /// Shared object file. pub const ET_DYN: u16 = 3; /// Core file. pub const ET_CORE: u16 = 4; /// Number of defined types. pub const ET_NUM: u16 = 5; /// OS-specific range start pub const ET_LOOS: u16 = 0xfe00; /// OS-specific range end pub const ET_HIOS: u16 = 0xfeff; /// Processor-specific range start pub const ET_LOPROC: u16 = 0xff00; /// Processor-specific range end pub const ET_HIPROC: u16 = 0xffff; /// The ELF magic number. pub const ELFMAG: &[u8; 4] = b"\x7FELF"; /// Sizeof ELF magic number. pub const SELFMAG: usize = 4; /// File class byte index. pub const EI_CLASS: usize = 4; /// Invalid class. pub const ELFCLASSNONE: u8 = 0; /// 32-bit objects. pub const ELFCLASS32: u8 = 1; /// 64-bit objects. pub const ELFCLASS64: u8 = 2; /// ELF class number. pub const ELFCLASSNUM: u8 = 3; /// Data encoding byte index. pub const EI_DATA: usize = 5; /// Invalid data encoding. pub const ELFDATANONE: u8 = 0; /// 2's complement, little endian. pub const ELFDATA2LSB: u8 = 1; /// 2's complement, big endian. pub const ELFDATA2MSB: u8 = 2; /// File version byte index. pub const EI_VERSION: usize = 6; /// Current ELF version. pub const EV_CURRENT: u8 = 1; /// OS ABI byte index. pub const EI_OSABI: usize = 7; /// UNIX System V ABI. pub const ELFOSABI_NONE: u8 = 0; /// UNIX System V ABI. /// /// Alias. pub const ELFOSABI_SYSV: u8 = ELFOSABI_NONE; /// HP-UX. pub const ELFOSABI_HPUX: u8 = 1; /// NetBSD. pub const ELFOSABI_NETBSD: u8 = 2; /// Object uses GNU ELF extensions. pub const ELFOSABI_GNU: u8 = 3; /// Object uses GNU ELF extensions. /// /// Alias. pub const ELFOSABI_LINUX: u8 = ELFOSABI_GNU; /// Sun Solaris. pub const ELFOSABI_SOLARIS: u8 = 6; /// IBM AIX. pub const ELFOSABI_AIX: u8 = 7; /// SGI Irix. pub const ELFOSABI_IRIX: u8 = 8; /// FreeBSD pub const ELFOSABI_FREEBSD: u8 = 9; /// Compaq TRU64 UNIX. pub const ELFOSABI_TRU64: u8 = 10; /// Novell Modesto. pub const ELFOSABI_MODESTO: u8 = 11; /// OpenBSD. pub const ELFOSABI_OPENBSD: u8 = 12; /// ARM EABI. pub const ELFOSABI_ARM_AEABI: u8 = 64; /// ARM. pub const ELFOSABI_ARM: u8 = 97; /// Standalone (embedded) application. pub const ELFOSABI_STANDALONE: u8 = 255; /// ABI version byte index. pub const EI_ABIVERSION: usize = 8; /// Number of bytes in an identifier. pub const SIZEOF_IDENT: usize = 16; /// Convert a ELF class byte to the associated string. #[inline] pub fn class_to_str(et: u8) -> &'static str { match et { ELFCLASSNONE => "NONE", ELFCLASS32 => "ELF32", ELFCLASS64 => "ELF64", _ => "UNKNOWN_CLASS", } } /// Convert an ET value to their associated string. #[inline] pub fn et_to_str(et: u16) -> &'static str { match et { ET_NONE => "NONE", ET_REL => "REL", ET_EXEC => "EXEC", ET_DYN => "DYN", ET_CORE => "CORE", ET_NUM => "NUM", _ => "UNKNOWN_ET", } } if_alloc! { use crate::error; use scroll::{ctx, Endian}; use core::fmt; use crate::container::{Ctx, Container}; use alloc::string::ToString; #[derive(Copy, Clone, PartialEq)] /// An ELF header pub struct Header { pub e_ident : [u8; SIZEOF_IDENT], pub e_type : u16, pub e_machine : u16, pub e_version : u32, pub e_entry : u64, pub e_phoff : u64, pub e_shoff : u64, pub e_flags : u32, pub e_ehsize : u16, pub e_phentsize : u16, pub e_phnum : u16, pub e_shentsize : u16, pub e_shnum : u16, pub e_shstrndx : u16, } impl Header { /// Return the size of the underlying program header, given a `container` #[inline] pub fn size(ctx: Ctx) -> usize { use scroll::ctx::SizeWith; Self::size_with(&ctx) } /// Returns the container type this header specifies pub fn container(&self) -> error::Result { use crate::error::Error; match self.e_ident[EI_CLASS] { ELFCLASS32 => { Ok(Container::Little) }, ELFCLASS64 => { Ok(Container::Big) }, class => Err(Error::Malformed(format!("Invalid class in Header: {}", class))) } } /// Returns the byte order this header specifies pub fn endianness(&self) -> error::Result { use crate::error::Error; match self.e_ident[EI_DATA] { ELFDATA2LSB => { Ok(scroll::LE) }, ELFDATA2MSB => { Ok(scroll::BE) }, class => Err(Error::Malformed(format!("Invalid endianness in Header: {}", class))) } } pub fn new(ctx: Ctx) -> Self { use crate::elf32; use crate::elf64; let (typ, ehsize, phentsize, shentsize) = match ctx.container { Container::Little => { (ELFCLASS32, header32::SIZEOF_EHDR, elf32::program_header::SIZEOF_PHDR, elf32::section_header::SIZEOF_SHDR) }, Container::Big => { (ELFCLASS64, header64::SIZEOF_EHDR, elf64::program_header::SIZEOF_PHDR, elf64::section_header::SIZEOF_SHDR) } }; let byteorder = match ctx.le { Endian::Little => ELFDATA2LSB, Endian::Big => ELFDATA2MSB }; Header { e_ident: [ 127, 69, 76, 70, typ, byteorder, EV_CURRENT, ELFOSABI_NONE, 0, 0, 0, 0, 0, 0, 0, 0 ], e_type: ET_DYN, e_machine: EM_NONE, e_version: 1, e_entry: 0x0, e_phoff: 0x0, e_shoff: 0x0, e_flags: 0, e_ehsize: ehsize as u16, e_phentsize: phentsize as u16, e_phnum: 0, e_shentsize: shentsize as u16, e_shnum: 0, e_shstrndx: 0, } } } impl fmt::Debug for Header { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { f.debug_struct("Header") .field("e_ident", &format_args!("{:?}", self.e_ident)) .field("e_type", &et_to_str(self.e_type)) .field("e_machine", &format_args!("0x{:x}", self.e_machine)) .field("e_version", &format_args!("0x{:x}", self.e_version)) .field("e_entry", &format_args!("0x{:x}", self.e_entry)) .field("e_phoff", &format_args!("0x{:x}", self.e_phoff)) .field("e_shoff", &format_args!("0x{:x}", self.e_shoff)) .field("e_flags", &format_args!("{:x}", self.e_flags)) .field("e_ehsize", &self.e_ehsize) .field("e_phentsize", &self.e_phentsize) .field("e_phnum", &self.e_phnum) .field("e_shentsize", &self.e_shentsize) .field("e_shnum", &self.e_shnum) .field("e_shstrndx", &self.e_shstrndx) .finish() } } impl ctx::SizeWith for Header { fn size_with(ctx: &crate::container::Ctx) -> usize { match ctx.container { Container::Little => { header32::SIZEOF_EHDR }, Container::Big => { header64::SIZEOF_EHDR }, } } } impl<'a> ctx::TryFromCtx<'a, scroll::Endian> for Header { type Error = crate::error::Error; fn try_from_ctx(bytes: &'a [u8], _ctx: scroll::Endian) -> error::Result<(Self, usize)> { use scroll::Pread; if bytes.len() < SIZEOF_IDENT { return Err(error::Error::Malformed("Too small".to_string())); } let ident: &[u8] = &bytes[..SIZEOF_IDENT]; if &ident[0..SELFMAG] != ELFMAG { let magic: u64 = ident.pread_with(0, scroll::LE)?; return Err(error::Error::BadMagic(magic)); } let class = ident[EI_CLASS]; match class { ELFCLASS32 => { Ok((Header::from(bytes.pread::(0)?), header32::SIZEOF_EHDR)) }, ELFCLASS64 => { Ok((Header::from(bytes.pread::(0)?), header64::SIZEOF_EHDR)) }, _ => { Err(error::Error::Malformed(format!("invalid ELF class {:x}", class))) } } } } impl ctx::TryIntoCtx for Header { type Error = crate::error::Error; fn try_into_ctx(self, bytes: &mut [u8], _ctx: scroll::Endian) -> Result { use scroll::Pwrite; match self.container()? { Container::Little => { bytes.pwrite(header32::Header::from(self), 0) }, Container::Big => { bytes.pwrite(header64::Header::from(self), 0) } } } } impl ctx::IntoCtx for Header { fn into_ctx(self, bytes: &mut [u8], ctx: crate::container::Ctx) { use scroll::Pwrite; match ctx.container { Container::Little => { bytes.pwrite_with(header32::Header::from(self), 0, ctx.le).unwrap() }, Container::Big => { bytes.pwrite_with(header64::Header::from(self), 0, ctx.le).unwrap() } }; } } } // end if_alloc macro_rules! elf_header_std_impl { ($size:expr, $width:ty) => { if_alloc! { use crate::elf::header::Header as ElfHeader; use crate::error::Error; #[cfg(any(feature = "std", feature = "endian_fd"))] use crate::error::Result; use scroll::{ctx, Pread}; use core::result; if_std! { use std::fs::File; use std::io::{Read}; } impl From for Header { fn from(eh: ElfHeader) -> Self { Header { e_ident: eh.e_ident, e_type: eh.e_type, e_machine: eh.e_machine, e_version: eh.e_version, e_entry: eh.e_entry as $width, e_phoff: eh.e_phoff as $width, e_shoff: eh.e_shoff as $width, e_flags: eh.e_flags, e_ehsize: eh.e_ehsize, e_phentsize: eh.e_phentsize, e_phnum: eh.e_phnum, e_shentsize: eh.e_shentsize, e_shnum: eh.e_shnum, e_shstrndx: eh.e_shstrndx, } } } impl From
for ElfHeader { fn from(eh: Header) -> Self { ElfHeader { e_ident: eh.e_ident, e_type: eh.e_type, e_machine: eh.e_machine, e_version: eh.e_version, e_entry: u64::from(eh.e_entry), e_phoff: u64::from(eh.e_phoff), e_shoff: u64::from(eh.e_shoff), e_flags: eh.e_flags, e_ehsize: eh.e_ehsize, e_phentsize: eh.e_phentsize, e_phnum: eh.e_phnum, e_shentsize: eh.e_shentsize, e_shnum: eh.e_shnum, e_shstrndx: eh.e_shstrndx, } } } impl<'a> ctx::TryFromCtx<'a, scroll::Endian> for Header { type Error = crate::error::Error; fn try_from_ctx(bytes: &'a [u8], _: scroll::Endian) -> result::Result<(Self, usize), Self::Error> { let mut elf_header = Header::default(); let offset = &mut 0; bytes.gread_inout(offset, &mut elf_header.e_ident)?; let endianness = match elf_header.e_ident[EI_DATA] { ELFDATA2LSB => scroll::LE, ELFDATA2MSB => scroll::BE, d => return Err(Error::Malformed(format!("invalid ELF endianness DATA type {:x}", d)).into()), }; elf_header.e_type = bytes.gread_with(offset, endianness)?; elf_header.e_machine = bytes.gread_with(offset, endianness)?; elf_header.e_version = bytes.gread_with(offset, endianness)?; elf_header.e_entry = bytes.gread_with(offset, endianness)?; elf_header.e_phoff = bytes.gread_with(offset, endianness)?; elf_header.e_shoff = bytes.gread_with(offset, endianness)?; elf_header.e_flags = bytes.gread_with(offset, endianness)?; elf_header.e_ehsize = bytes.gread_with(offset, endianness)?; elf_header.e_phentsize = bytes.gread_with(offset, endianness)?; elf_header.e_phnum = bytes.gread_with(offset, endianness)?; elf_header.e_shentsize = bytes.gread_with(offset, endianness)?; elf_header.e_shnum = bytes.gread_with(offset, endianness)?; elf_header.e_shstrndx = bytes.gread_with(offset, endianness)?; Ok((elf_header, SIZEOF_EHDR)) } } impl ctx::TryIntoCtx for Header { type Error = crate::error::Error; /// a Pwrite impl for Header: **note** we use the endianness value in the header, and not a parameter fn try_into_ctx(self, bytes: &mut [u8], _endianness: scroll::Endian) -> result::Result { use scroll::{Pwrite}; let offset = &mut 0; let endianness = match self.e_ident[EI_DATA] { ELFDATA2LSB => scroll::LE, ELFDATA2MSB => scroll::BE, d => return Err(Error::Malformed(format!("invalid ELF DATA type {:x}", d)).into()), }; for i in 0..self.e_ident.len() { bytes.gwrite(self.e_ident[i], offset)?; } bytes.gwrite_with(self.e_type , offset, endianness)?; bytes.gwrite_with(self.e_machine , offset, endianness)?; bytes.gwrite_with(self.e_version , offset, endianness)?; bytes.gwrite_with(self.e_entry , offset, endianness)?; bytes.gwrite_with(self.e_phoff , offset, endianness)?; bytes.gwrite_with(self.e_shoff , offset, endianness)?; bytes.gwrite_with(self.e_flags , offset, endianness)?; bytes.gwrite_with(self.e_ehsize , offset, endianness)?; bytes.gwrite_with(self.e_phentsize , offset, endianness)?; bytes.gwrite_with(self.e_phnum , offset, endianness)?; bytes.gwrite_with(self.e_shentsize , offset, endianness)?; bytes.gwrite_with(self.e_shnum , offset, endianness)?; bytes.gwrite_with(self.e_shstrndx , offset, endianness)?; Ok(SIZEOF_EHDR) } } impl Header { /// Load a header from a file. **You must** ensure the seek is at the correct position. #[cfg(feature = "std")] pub fn from_fd(bytes: &mut File) -> Result
{ let mut elf_header = [0; $size]; bytes.read_exact(&mut elf_header)?; Ok(*Header::from_bytes(&elf_header)) } #[cfg(feature = "endian_fd")] /// Parses an ELF header from the given bytes pub fn parse(bytes: &[u8]) -> Result
{ use super::{EI_DATA, ELFDATA2LSB, ELFDATA2MSB, SIZEOF_IDENT}; let mut elf_header = Header::default(); let mut offset = &mut 0; for i in 0..SIZEOF_IDENT { elf_header.e_ident[i] = bytes.gread(&mut offset)?; } let endianness = match elf_header.e_ident[EI_DATA] { ELFDATA2LSB => scroll::LE, ELFDATA2MSB => scroll::BE, d => return Err(Error::Malformed(format!("invalid ELF DATA type {:x}", d)).into()), }; elf_header.e_type = bytes.gread_with(offset, endianness)?; elf_header.e_machine = bytes.gread_with(offset, endianness)?; elf_header.e_version = bytes.gread_with(offset, endianness)?; elf_header.e_entry = bytes.gread_with(offset, endianness)?; elf_header.e_phoff = bytes.gread_with(offset, endianness)?; elf_header.e_shoff = bytes.gread_with(offset, endianness)?; elf_header.e_flags = bytes.gread_with(offset, endianness)?; elf_header.e_ehsize = bytes.gread_with(offset, endianness)?; elf_header.e_phentsize = bytes.gread_with(offset, endianness)?; elf_header.e_phnum = bytes.gread_with(offset, endianness)?; elf_header.e_shentsize = bytes.gread_with(offset, endianness)?; elf_header.e_shnum = bytes.gread_with(offset, endianness)?; elf_header.e_shstrndx = bytes.gread_with(offset, endianness)?; Ok(elf_header) } } } // end if_alloc }; } // tests macro_rules! elf_header_test { ($class:expr) => { #[cfg(test)] mod tests { use super::*; use crate::container::{Container, Ctx}; use crate::elf::header::Header as ElfHeader; use alloc::vec::Vec; use scroll::{Pread, Pwrite}; #[test] fn size_of() { assert_eq!(::std::mem::size_of::
(), SIZEOF_EHDR); } #[test] fn header_read_write() { let crt1: Vec = if $class == ELFCLASS64 { include!("../../etc/crt1.rs") } else { include!("../../etc/crt132.rs") }; let header: Header = crt1.pread(0).unwrap(); assert_eq!(header.e_type, ET_REL); println!("header: {:?}", &header); let mut bytes = [0u8; SIZEOF_EHDR]; bytes.pwrite(header, 0).unwrap(); let header2: Header = bytes.pread(0).unwrap(); assert_eq!(header, header2); } #[test] fn elfheader_read_write() { let (container, crt1): (Container, Vec) = if $class == ELFCLASS64 { (Container::Big, include!("../../etc/crt1.rs")) } else { (Container::Little, include!("../../etc/crt132.rs")) }; let header: Header = crt1.pread(0).unwrap(); assert_eq!(header.e_type, ET_REL); println!("header: {:?}", &header); let mut bytes = [0u8; SIZEOF_EHDR]; let header_ = Header::from(header.clone()); bytes.pwrite(header_, 0).unwrap(); let header2: Header = bytes.pread(0).unwrap(); assert_eq!(header, header2); let header = ElfHeader::new(Ctx::from(container)); println!("header: {:?}", &header); let mut bytes = vec![0; 100]; bytes.pwrite(header, 0).unwrap(); } } }; } pub mod header32 { pub use super::*; pub const SIZEOF_EHDR: usize = 52; pub const ELFCLASS: u8 = ELFCLASS32; elf_header!(u32); elf_header_std_impl!(SIZEOF_EHDR, u32); elf_header_test!(ELFCLASS); } pub mod header64 { pub use super::*; pub const SIZEOF_EHDR: usize = 64; pub const ELFCLASS: u8 = ELFCLASS64; elf_header!(u64); elf_header_std_impl!(SIZEOF_EHDR, u64); elf_header_test!(ELFCLASS); }