blob: 7417a7345139b5856fc9604ff0227313a3935cbc [file] [log] [blame]
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;
/// 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;
/// 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<Container> {
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<scroll::Endian> {
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<crate::container::Ctx> 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::<header32::Header>(0)?), header32::SIZEOF_EHDR))
},
ELFCLASS64 => {
Ok((Header::from(bytes.pread::<header64::Header>(0)?), header64::SIZEOF_EHDR))
},
_ => {
Err(error::Error::Malformed(format!("invalid ELF class {:x}", class)))
}
}
}
}
impl ctx::TryIntoCtx<scroll::Endian> for Header {
type Error = crate::error::Error;
fn try_into_ctx(self, bytes: &mut [u8], _ctx: scroll::Endian) -> Result<usize, Self::Error> {
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<crate::container::Ctx> 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<ElfHeader> 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<Header> 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<scroll::Endian> 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<usize, Self::Error> {
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<Header> {
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<Header> {
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::<Header>(), SIZEOF_EHDR);
}
#[test]
fn header_read_write() {
let crt1: Vec<u8> = 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<u8>) = 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);
}