mirror of
https://github.com/Noratrieb/elven-forest.git
synced 2026-01-14 10:45:03 +01:00
elf
This commit is contained in:
parent
d3118af82d
commit
bf2734d9b7
2 changed files with 393 additions and 43 deletions
|
|
@ -1,25 +1,36 @@
|
|||
//! Structures and parsers for ELF64. ELF32 can knock itself out.
|
||||
//!
|
||||
//! See https://man7.org/linux/man-pages/man5/elf.5.html
|
||||
|
||||
use std::mem;
|
||||
pub mod consts;
|
||||
use consts as c;
|
||||
|
||||
use std::{ffi::CStr, fmt::Debug, mem, ops, slice::SliceIndex};
|
||||
|
||||
use bytemuck::{Pod, PodCastError, Zeroable};
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Zeroable, Pod)]
|
||||
#[repr(transparent)]
|
||||
|
||||
pub struct Addr(u64);
|
||||
pub struct Addr(pub u64);
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Zeroable, Pod)]
|
||||
#[repr(transparent)]
|
||||
pub struct Offset(u64);
|
||||
pub struct Offset(pub u64);
|
||||
|
||||
impl Offset {
|
||||
fn usize(self) -> usize {
|
||||
self.0 as usize
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Zeroable, Pod)]
|
||||
#[repr(transparent)]
|
||||
pub struct Section(u16);
|
||||
pub struct Section(pub u16);
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Zeroable, Pod)]
|
||||
#[repr(transparent)]
|
||||
pub struct Versym(u16);
|
||||
pub struct Versym(pub u16);
|
||||
|
||||
#[derive(Debug, Clone, thiserror::Error)]
|
||||
pub enum ElfParseError {
|
||||
|
|
@ -29,18 +40,30 @@ pub enum ElfParseError {
|
|||
UnalignedInput(usize, usize),
|
||||
#[error("The magic of the file did not match. Maybe it's not an ELF file?. Found {0:x?}")]
|
||||
WrongMagic([u8; 4]),
|
||||
#[error("A program header entry has a different size than expected. Expected {0}, found {1}")]
|
||||
InvalidPhEntSize(usize, usize),
|
||||
#[error("A section header entry has a different size than expected. Expected {0}, found {1}")]
|
||||
InvalidShEntSize(usize, usize),
|
||||
#[error("The string table section is marked as UNDEF")]
|
||||
StrTableSectionNotPresent,
|
||||
#[error("An index is out of bounds: {0}: {1}")]
|
||||
IndexOutOfBounds(&'static str, usize),
|
||||
#[error("String in string table does not end with a nul terminator: String offset: {0}")]
|
||||
NoStringNulTerm(usize),
|
||||
}
|
||||
|
||||
type Result<T> = std::result::Result<T, ElfParseError>;
|
||||
|
||||
/// A raw ELF. Does not come with cute ears for now.
|
||||
#[derive(Debug)]
|
||||
pub struct Elf<'a> {
|
||||
pub header: &'a ElfHeader,
|
||||
pub data: &'a [u8],
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, Zeroable, Pod)]
|
||||
#[repr(C)]
|
||||
pub struct ElfHeader {
|
||||
pub ident: [u8; 16],
|
||||
pub ident: ElfIdent,
|
||||
pub r#type: u16,
|
||||
pub machine: u16,
|
||||
pub version: u32,
|
||||
|
|
@ -56,47 +79,211 @@ pub struct ElfHeader {
|
|||
pub shstrndex: u16,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
#[derive(Debug, Clone, Copy, Zeroable, Pod)]
|
||||
#[repr(C)]
|
||||
pub struct Phdr {}
|
||||
pub struct ElfIdent {
|
||||
magic: [u8; c::SELFMAG],
|
||||
class: u8,
|
||||
data: u8,
|
||||
version: u8,
|
||||
osabi: u8,
|
||||
abiversion: u8,
|
||||
_pad: [u8; 7],
|
||||
}
|
||||
|
||||
const _: [u8; c::EI_NIDENT] = [0; mem::size_of::<ElfIdent>()];
|
||||
|
||||
#[derive(Debug, Clone, Copy, Zeroable, Pod)]
|
||||
#[repr(C)]
|
||||
pub struct Phdr {
|
||||
pub r#type: u32,
|
||||
pub flags: u32,
|
||||
pub offset: Offset,
|
||||
pub vaddr: Addr,
|
||||
pub paddr: Addr,
|
||||
pub filesz: u64,
|
||||
pub memsz: u64,
|
||||
pub align: u64,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, Zeroable, Pod)]
|
||||
#[repr(C)]
|
||||
pub struct Shdr {
|
||||
pub name: u32,
|
||||
pub r#type: c::ShType,
|
||||
pub flags: u64,
|
||||
pub addr: Addr,
|
||||
pub offset: Offset,
|
||||
pub size: u64,
|
||||
pub link: u32,
|
||||
pub info: u32,
|
||||
pub addralign: u64,
|
||||
pub entsize: u64,
|
||||
}
|
||||
|
||||
impl<'a> Elf<'a> {
|
||||
pub fn parse(input: &'a [u8]) -> Result<Self, ElfParseError> {
|
||||
const HEADER_SIZE: usize = mem::size_of::<ElfHeader>();
|
||||
const HEADER_ALIGN: usize = mem::align_of::<ElfHeader>();
|
||||
pub fn new(data: &'a [u8]) -> Result<Self> {
|
||||
let magic = data[..c::SELFMAG].try_into().unwrap();
|
||||
|
||||
if input.len() < HEADER_SIZE {
|
||||
return Err(ElfParseError::FileTooSmall(HEADER_SIZE, input.len()));
|
||||
}
|
||||
|
||||
let input_addr = input as *const [u8] as *const u8 as usize;
|
||||
let input_align = input_addr.trailing_zeros() as usize;
|
||||
|
||||
let input_header = &input[..HEADER_SIZE];
|
||||
|
||||
let header_slice = match bytemuck::try_cast_slice::<_, ElfHeader>(input_header) {
|
||||
Ok(slice) => slice,
|
||||
Err(
|
||||
PodCastError::SizeMismatch
|
||||
| PodCastError::OutputSliceWouldHaveSlop
|
||||
| PodCastError::AlignmentMismatch,
|
||||
) => {
|
||||
unreachable!()
|
||||
}
|
||||
Err(PodCastError::TargetAlignmentGreaterAndInputNotAligned) => {
|
||||
return Err(ElfParseError::UnalignedInput(HEADER_ALIGN, input_align))
|
||||
}
|
||||
};
|
||||
|
||||
let header = &header_slice[0];
|
||||
|
||||
let magic = header.ident[..4].try_into().unwrap();
|
||||
|
||||
if magic != [0x7f, b'E', b'L', b'F'] {
|
||||
if magic != *c::ELFMAG {
|
||||
return Err(ElfParseError::WrongMagic(magic));
|
||||
}
|
||||
|
||||
Ok(Elf { header })
|
||||
let elf = Elf { data };
|
||||
|
||||
Ok(elf)
|
||||
}
|
||||
|
||||
pub fn header(&self) -> Result<&ElfHeader> {
|
||||
load_ref(self.data)
|
||||
}
|
||||
|
||||
pub fn program_headers(&self) -> Result<&[Phdr]> {
|
||||
let header = self.header()?;
|
||||
|
||||
let expected_ent_size = mem::size_of::<Phdr>();
|
||||
let actual_ent_size = usize::from(header.phentsize);
|
||||
if actual_ent_size != expected_ent_size {
|
||||
return Err(ElfParseError::InvalidPhEntSize(
|
||||
expected_ent_size,
|
||||
actual_ent_size,
|
||||
));
|
||||
}
|
||||
|
||||
let off = header.phoff.usize();
|
||||
load_slice(
|
||||
&self.data.get_elf(off.., "program header offset")?,
|
||||
header.phnum.into(),
|
||||
)
|
||||
}
|
||||
|
||||
pub fn section_headers(&self) -> Result<&[Shdr]> {
|
||||
let header = self.header()?;
|
||||
|
||||
let expected_ent_size = mem::size_of::<Shdr>();
|
||||
let actual_ent_size = usize::from(header.shentsize);
|
||||
if actual_ent_size != expected_ent_size {
|
||||
return Err(ElfParseError::InvalidPhEntSize(
|
||||
expected_ent_size,
|
||||
actual_ent_size,
|
||||
));
|
||||
}
|
||||
let off = header.shoff.usize();
|
||||
load_slice(
|
||||
&self.data.get_elf(off.., "sectoin header offset")?,
|
||||
header.shnum.into(),
|
||||
)
|
||||
}
|
||||
|
||||
pub fn section_header(&self, idx: usize) -> Result<&Shdr> {
|
||||
let sections = self.section_headers()?;
|
||||
sections.get_elf(idx, "section number")
|
||||
}
|
||||
|
||||
pub fn section_content(&self, sh: &Shdr) -> Result<&[u8]> {
|
||||
if sh.r#type.0 == c::SHT_NOBITS {
|
||||
return Ok(&[]);
|
||||
}
|
||||
|
||||
Ok(&self
|
||||
.data
|
||||
.get_elf(sh.offset.usize().., "section offset")?
|
||||
.get_elf(..(sh.size as usize), "section size")?)
|
||||
}
|
||||
|
||||
pub fn str_table(&self) -> Result<&[u8]> {
|
||||
let header = self.header()?;
|
||||
let shstrndex = header.shstrndex;
|
||||
|
||||
if shstrndex == c::SHN_UNDEF {
|
||||
return Err(ElfParseError::StrTableSectionNotPresent);
|
||||
}
|
||||
|
||||
if shstrndex >= c::SHN_LORESERVE {
|
||||
todo!(
|
||||
"the real index of the
|
||||
section name string table section is held in the sh_link
|
||||
member of the initial entry in section header table.
|
||||
Otherwise, the sh_link member of the initial entry in
|
||||
section header table contains the value zero."
|
||||
)
|
||||
}
|
||||
|
||||
let strtab_header = self.section_header(shstrndex as usize)?;
|
||||
self.section_content(strtab_header)
|
||||
}
|
||||
|
||||
pub fn string(&self, idx: usize) -> Result<&CStr> {
|
||||
let str_table = self.str_table()?;
|
||||
let indexed = str_table.get_elf(idx.., "string offset")?;
|
||||
let end = indexed
|
||||
.iter()
|
||||
.position(|&c| c == b'\0')
|
||||
.ok_or(ElfParseError::NoStringNulTerm(idx))?;
|
||||
Ok(CStr::from_bytes_with_nul(&indexed[..=end]).unwrap())
|
||||
}
|
||||
}
|
||||
|
||||
fn load_ref<T: Pod>(data: &[u8]) -> Result<&T> {
|
||||
load_slice(data, 1).map(|slice| &slice[0])
|
||||
}
|
||||
|
||||
fn load_slice<T: Pod>(data: &[u8], amount_of_elems: usize) -> Result<&[T]> {
|
||||
let size = mem::size_of::<T>() * amount_of_elems;
|
||||
let align = mem::align_of::<T>();
|
||||
|
||||
if data.len() < size {
|
||||
return Err(ElfParseError::FileTooSmall(size, data.len()));
|
||||
}
|
||||
|
||||
let data_addr = data as *const [u8] as *const u8 as usize;
|
||||
let data_align = data_addr.trailing_zeros() as usize;
|
||||
|
||||
let data = &data[..size];
|
||||
|
||||
bytemuck::try_cast_slice::<_, T>(data).map_err(|e| match e {
|
||||
e @ (PodCastError::SizeMismatch
|
||||
| PodCastError::OutputSliceWouldHaveSlop
|
||||
| PodCastError::AlignmentMismatch) => {
|
||||
unreachable!("already checked for these errors: {e}")
|
||||
}
|
||||
PodCastError::TargetAlignmentGreaterAndInputNotAligned => {
|
||||
ElfParseError::UnalignedInput(align, data_align)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
trait ElfIndex<T: ?Sized>: SliceIndex<T> {
|
||||
fn bound(&self) -> usize;
|
||||
}
|
||||
|
||||
impl<T> ElfIndex<[T]> for usize {
|
||||
fn bound(&self) -> usize {
|
||||
*self
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> ElfIndex<[T]> for ops::RangeFrom<usize> {
|
||||
fn bound(&self) -> usize {
|
||||
self.start
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> ElfIndex<[T]> for ops::RangeTo<usize> {
|
||||
fn bound(&self) -> usize {
|
||||
self.end
|
||||
}
|
||||
}
|
||||
|
||||
trait ElfIndexExt {
|
||||
fn get_elf<I: ElfIndex<Self>>(&self, idx: I, msg: &'static str) -> Result<&I::Output>;
|
||||
}
|
||||
|
||||
impl<T> ElfIndexExt for [T] {
|
||||
fn get_elf<I: ElfIndex<Self>>(&self, idx: I, msg: &'static str) -> Result<&I::Output> {
|
||||
let bound = idx.bound();
|
||||
self.get(idx)
|
||||
.ok_or(ElfParseError::IndexOutOfBounds(msg, bound))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -126,8 +313,25 @@ mod tests {
|
|||
}
|
||||
|
||||
#[test]
|
||||
fn rust_hello_world_bin() {
|
||||
fn rust_hello_world_bin() -> super::Result<()> {
|
||||
let file = load_test_file("hello_world");
|
||||
let _ = Elf::parse(&file).unwrap();
|
||||
let elf = Elf::new(&file)?;
|
||||
let header = elf.header()?;
|
||||
|
||||
assert_eq!(header.ident.class, c::ELFCLASS64);
|
||||
assert_eq!(header.ident.data, c::ELFDATA2LSB);
|
||||
assert_eq!(header.ident.osabi, c::ELFOSABI_SYSV);
|
||||
assert_eq!(header.r#type, c::ET_DYN);
|
||||
assert_ne!(header.entry, Addr(0));
|
||||
|
||||
elf.program_headers()?;
|
||||
elf.section_headers()?;
|
||||
|
||||
for section in elf.section_headers()? {
|
||||
let name = elf.string(section.name as usize)?.to_str().unwrap();
|
||||
println!("{name:20} {:5} {:?}", section.size, section.r#type);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
|
|
|||
146
elven-parser/src/raw/consts.rs
Normal file
146
elven-parser/src/raw/consts.rs
Normal file
|
|
@ -0,0 +1,146 @@
|
|||
#![allow(non_upper_case_globals)]
|
||||
|
||||
macro_rules! const_group_with_fmt {
|
||||
(
|
||||
pub struct $struct_name:ident($ty:ty): $group_name:literal
|
||||
|
||||
$(
|
||||
pub const $name:ident = $value:expr;
|
||||
)*
|
||||
) => {
|
||||
$(
|
||||
pub const $name: $ty = $value;
|
||||
)*
|
||||
|
||||
#[derive(Clone, Copy, bytemuck::Zeroable, bytemuck::Pod)]
|
||||
#[repr(transparent)]
|
||||
pub struct $struct_name(pub $ty);
|
||||
|
||||
impl std::fmt::Debug for $struct_name {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match self.0 {
|
||||
$(
|
||||
$value => f.write_str(stringify!($name)),
|
||||
)*
|
||||
a => write!(f, "Invalid {}: {a}", $group_name,)
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
// ------------------
|
||||
// Header
|
||||
// ------------------
|
||||
|
||||
/* Conglomeration of the identification bytes, for easy testing as a word. */
|
||||
pub const ELFMAG: &[u8; SELFMAG] = b"\x7fELF";
|
||||
pub const SELFMAG: usize = 4;
|
||||
|
||||
pub const EI_CLASS: usize = 4; /* File class byte index */
|
||||
pub const ELFCLASSNONE: u8 = 0; /* Invalid class */
|
||||
pub const ELFCLASS32: u8 = 1; /* 32-bit objects */
|
||||
pub const ELFCLASS64: u8 = 2; /* 64-bit objects */
|
||||
pub const ELFCLASSNUM: u8 = 3;
|
||||
|
||||
pub const EI_DATA: usize = 5; /* Data encoding byte index */
|
||||
pub const ELFDATANONE: u8 = 0; /* Invalid data encoding */
|
||||
pub const ELFDATA2LSB: u8 = 1; /* 2's complement, little endian */
|
||||
pub const ELFDATA2MSB: u8 = 2; /* 2's complement, big endian */
|
||||
pub const ELFDATANUM: u8 = 3;
|
||||
|
||||
pub const EI_VERSION: usize = 6; /* File version byte index */
|
||||
|
||||
pub const EI_OSABI: usize = 7; /* OS ABI identification */
|
||||
pub const ELFOSABI_NONE: u8 = 0; /* UNIX System V ABI */
|
||||
pub const ELFOSABI_SYSV: u8 = 0; /* Alias. */
|
||||
pub const ELFOSABI_HPUX: u8 = 1; /* HP-UX */
|
||||
pub const ELFOSABI_NETBSD: u8 = 2; /* NetBSD. */
|
||||
pub const ELFOSABI_GNU: u8 = 3; /* Object uses GNU ELF extensions. */
|
||||
pub const ELFOSABI_LINUX: u8 = ELFOSABI_GNU; /* Compatibility alias. */
|
||||
pub const ELFOSABI_SOLARIS: u8 = 6; /* Sun Solaris. */
|
||||
pub const ELFOSABI_AIX: u8 = 7; /* IBM AIX. */
|
||||
pub const ELFOSABI_IRIX: u8 = 8; /* SGI Irix. */
|
||||
pub const ELFOSABI_FREEBSD: u8 = 9; /* FreeBSD. */
|
||||
pub const ELFOSABI_TRU64: u8 = 10; /* Compaq TRU64 UNIX. */
|
||||
pub const ELFOSABI_MODESTO: u8 = 11; /* Novell Modesto. */
|
||||
pub const ELFOSABI_OPENBSD: u8 = 12; /* OpenBSD. */
|
||||
pub const ELFOSABI_ARM_AEABI: u8 = 64; /* ARM EABI */
|
||||
pub const ELFOSABI_ARM: u8 = 97; /* ARM */
|
||||
pub const ELFOSABI_STANDALONE: u8 = 255; /* Standalone (embedded) application */
|
||||
|
||||
pub const EI_ABIVERSION: usize = 8; /* ABI version */
|
||||
|
||||
pub const EI_PAD: usize = 9; /* Byte index of padding bytes */
|
||||
|
||||
pub const EI_NIDENT: usize = 16;
|
||||
|
||||
pub const ET_NONE: u16 = 0;
|
||||
pub const ET_REL: u16 = 1;
|
||||
pub const ET_EXEC: u16 = 2;
|
||||
pub const ET_DYN: u16 = 3;
|
||||
pub const ET_CORE: u16 = 4;
|
||||
|
||||
pub const EM_NONE: u16 = 0; /* No machine */
|
||||
pub const EM_X86_64: u16 = 62; /* AMD x86-64 architecture */
|
||||
|
||||
pub const EV_NONE: u32 = 0;
|
||||
|
||||
// ------------------
|
||||
// Sections
|
||||
// ------------------
|
||||
|
||||
pub const SHN_UNDEF: u16 = 0; /* Undefined section */
|
||||
pub const SHN_LORESERVE: u16 = 0xff00; /* Start of reserved indices */
|
||||
pub const SHN_LOPROC: u16 = 0xff00; /* Start of processor-specific */
|
||||
pub const SHN_BEFORE: u16 = 0xff00; /* Order section before all others (Solaris). */
|
||||
pub const SHN_AFTER: u16 = 0xff01; /* Order section after all others (Solaris). */
|
||||
pub const SHN_HIPROC: u16 = 0xff1f; /* End of processor-specific */
|
||||
pub const SHN_LOOS: u16 = 0xff20; /* Start of OS-specific */
|
||||
pub const SHN_HIOS: u16 = 0xff3f; /* End of OS-specific */
|
||||
pub const SHN_ABS: u16 = 0xfff1; /* Associated symbol is absolute */
|
||||
pub const SHN_COMMON: u16 = 0xfff2; /* Associated symbol is common */
|
||||
pub const SHN_XINDEX: u16 = 0xffff; /* Index is in extra table. */
|
||||
pub const SHN_HIRESERVE: u16 = 0xffff; /* End of reserved indices */
|
||||
|
||||
const_group_with_fmt! {
|
||||
pub struct ShType(u32): "Section header type"
|
||||
|
||||
pub const SHT_NULL = 0; /* Section header table entry unused */
|
||||
pub const SHT_PROGBITS = 1; /* Program data */
|
||||
pub const SHT_SYMTAB = 2; /* Symbol table */
|
||||
pub const SHT_STRTAB = 3; /* String table */
|
||||
pub const SHT_RELA = 4; /* Relocation entries with addends */
|
||||
pub const SHT_HASH = 5; /* Symbol hash table */
|
||||
pub const SHT_DYNAMIC = 6; /* Dynamic linking information */
|
||||
pub const SHT_NOTE = 7; /* Notes */
|
||||
pub const SHT_NOBITS = 8; /* Program space with no data (bss) */
|
||||
pub const SHT_REL = 9; /* Relocation entries, no addends */
|
||||
pub const SHT_SHLIB = 10; /* Reserved */
|
||||
pub const SHT_DYNSYM = 11; /* Dynamic linker symbol table */
|
||||
pub const SHT_INIT_ARRAY = 14; /* Array of constructors */
|
||||
pub const SHT_FINI_ARRAY = 15; /* Array of destructors */
|
||||
pub const SHT_PREINIT_ARRAY = 16; /* Array of pre-constructors */
|
||||
pub const SHT_GROUP = 17; /* Section group */
|
||||
pub const SHT_SYMTAB_SHNDX = 18; /* Extended section indices */
|
||||
pub const SHT_NUM = 19; /* Number of defined types. */
|
||||
pub const SHT_GNU_ATTRIBUTES = 0x6ffffff5; /* Object attributes. */
|
||||
pub const SHT_GNU_HASH = 0x6ffffff6; /* GNU-style hash table. */
|
||||
pub const SHT_GNU_LIBLIST = 0x6ffffff7; /* Prelink library list */
|
||||
pub const SHT_CHECKSUM = 0x6ffffff8; /* Checksum for DSO content. */
|
||||
pub const SHT_SUNW_move = 0x6ffffffa;
|
||||
pub const SHT_SUNW_COMDAT = 0x6ffffffb;
|
||||
pub const SHT_SUNW_syminfo = 0x6ffffffc;
|
||||
pub const SHT_GNU_verdef = 0x6ffffffd; /* Version definition section. */
|
||||
pub const SHT_GNU_verneed = 0x6ffffffe; /* Version needs section. */
|
||||
pub const SHT_GNU_versym = 0x6fffffff; /* Version symbol table. */
|
||||
pub const SHT_LOPROC = 0x70000000; /* Start of processor-specific */
|
||||
pub const SHT_HIPROC = 0x7fffffff; /* End of processor-specific */
|
||||
pub const SHT_LOUSER = 0x80000000; /* Start of application-specific */
|
||||
pub const SHT_HIUSER = 0x8fffffff; /* End of application-specific */
|
||||
}
|
||||
|
||||
pub const SHT_LOOS: u32 = 0x60000000; /* Start OS-specific. */
|
||||
pub const SHT_LOSUNW: u32 = 0x6ffffffa; /* Sun-specific low bound. */
|
||||
pub const SHT_HISUNW: u32 = 0x6fffffff; /* Sun-specific high bound. */
|
||||
pub const SHT_HIOS: u32 = 0x6fffffff; /* End OS-specific type */
|
||||
Loading…
Add table
Add a link
Reference in a new issue