#![allow(clippy::cast_lossless, clippy::transmute_ptr_to_ref)]
use std::mem::{size_of, transmute};
use std::slice;
pub const ELF_IDENT_MAGIC: u32 = 0x7f45_4c46;
pub const ELF_IDENT_VERSION_CURRENT: u8 = 1;
pub const ELF_IDENT_CLASS_32: u8 = 1;
pub const ELF_IDENT_DATA_2LSB: u8 = 1;
pub const ELF_IDENT_ABI_SYSV: u8 = 0;
pub const ELF_TYPE_EXECUTABLE: u16 = 2;
pub const ELF_MACHINE_RISCV: u16 = 243;
pub const ELF_VERSION_CURRENT: u32 = 1;
pub const ELF_PROGRAM_TYPE_LOADABLE: u32 = 1;
pub const ELF_SECTION_TYPE_NOBITS: u32 = 8;
trait ElfFileAddressable {
fn get_range(&self) -> (u32, u32);
}
#[derive(Clone,Copy,Debug)]
#[repr(packed)]
pub struct ElfIdent {
pub magic: u32,
pub class: u8,
pub data: u8,
pub version: u8,
pub abi: u8,
pub abi_version: u8,
pub padding: [u8; 7],
}
#[derive(Clone,Copy,Debug)]
#[repr(packed)]
pub struct ElfHeader32 {
pub typ: u16,
pub machine: u16,
pub version: u32,
pub entry: u32,
pub phoff: u32,
pub shoff: u32,
pub flags: u32,
pub ehsize: u16,
pub phentsize: u16,
pub phnum: u16,
pub shentsize: u16,
pub shnum: u16,
pub shstrndx: u16,
}
#[derive(Clone,Copy,Debug)]
#[repr(packed)]
pub struct ElfProgramHeader32 {
pub typ: u32,
pub offset: u32,
pub vaddr: u32,
pub paddr: u32,
pub filesz: u32,
pub memsz: u32,
pub flags: u32,
pub align: u32,
}
impl ElfFileAddressable for ElfProgramHeader32 {
fn get_range(&self) -> (u32, u32) {
(self.offset, self.filesz)
}
}
#[derive(Clone,Copy,Debug)]
#[repr(packed)]
pub struct ElfSectionHeader32 {
pub name: u32,
pub typ: u32,
pub flags: u32,
pub addr: u32,
pub offset: u32,
pub size: u32,
pub link: u32,
pub info: u32,
pub addralign: u32,
pub entsize: u32,
}
impl ElfFileAddressable for ElfSectionHeader32 {
fn get_range(&self) -> (u32, u32) {
(self.offset, if self.typ == ELF_SECTION_TYPE_NOBITS { 0 } else { self.size })
}
}
#[derive(Debug)]
pub struct Elf32<'a> {
pub ident: &'a ElfIdent,
pub header: &'a ElfHeader32,
pub ph: Vec<&'a ElfProgramHeader32>,
pub sh: Vec<&'a ElfSectionHeader32>,
pub p: Vec<&'a [u8]>,
pub s: Vec<&'a [u8]>,
}
impl<'a> Elf32<'a> {
pub fn parse(data: &'a [u8]) -> Result<Elf32<'a>, String> {
if data.len() < size_of::<ElfIdent>() + size_of::<ElfHeader32>() {
return Err("file too short to contain headers".to_owned());
}
let ident: &'a ElfIdent = unsafe {
transmute(data.as_ptr())
};
if u32::from_be(ident.magic) != ELF_IDENT_MAGIC {
return Err("magic mismatch, likely not an ELF".to_owned());
}
if ident.version != ELF_IDENT_VERSION_CURRENT {
let ident_version = ident.version;
return Err(format!("unsupported version {}", ident_version));
}
if ident.class != ELF_IDENT_CLASS_32 {
return Err("only 32-bit class supported".to_owned());
}
let header: &'a ElfHeader32 = unsafe {
transmute(data.as_ptr().add(size_of::<ElfIdent>()))
};
if header.version != ELF_VERSION_CURRENT {
let header_version = header.version;
return Err(format!("unsupported version {}", header_version));
}
if header.typ != ELF_TYPE_EXECUTABLE {
let header_typ = header.typ;
return Err(format!("unsupported type {}", header_typ));
}
let (ph, p) = resolve_parts::<ElfProgramHeader32>(
data, header.phoff, header.phentsize, header.phnum)?;
let (sh, s) = resolve_parts::<ElfSectionHeader32>(
data, header.shoff, header.shentsize, header.shnum)?;
Ok(Elf32 { ident, header, ph, sh, p, s })
}
}
fn resolve_parts<'a, T>(data: &'a [u8], offset: u32, entsize16: u16, num16: u16)
-> Result<(Vec<&'a T>, Vec<&'a [u8]>), String>
where T: ElfFileAddressable {
let entsize = entsize16 as u32;
let num = num16 as u32;
let headers = if offset == 0 { Vec::new() } else {
if (entsize as usize) < size_of::<T>() {
return Err("headers smaller than defined in specification".to_owned());
}
if data.len() < (offset + entsize * num) as usize {
return Err("reference to data beyond end of file".to_owned());
}
(0..num).map(|i| unsafe {
transmute(data.as_ptr().offset((offset + i * entsize) as isize))
}).collect::<Vec<&'a T>>()
};
let blocks = headers.iter().map(|h| -> Result<&'a [u8], String> {
let (offset, size) = h.get_range();
if size == 0 {
Ok(&[])
} else if data.len() < (offset + size) as usize {
Err("reference to data beyond end of file".to_owned())
} else {
Ok(unsafe {
slice::from_raw_parts(
data.as_ptr().offset(offset as isize),
size as usize
)
})
}
}).collect::<Result<Vec<_>, _>>()?;
Ok((headers, blocks))
}