Files
dusk/src/format/elf.rs
T

106 lines
3.2 KiB
Rust

pub struct ElfError;
#[cfg(target_arch = "x86_64")]
const MACHINE: u16 = 62;
#[cfg(target_arch = "aarch64")]
const MACHINE: u16 = 183;
#[cfg(target_arch = "riscv64")]
const MACHINE: u16 = 243;
pub struct Elf<'a> {
bytes: &'a [u8],
headers: &'a [u8],
pub entry: usize,
}
pub struct Segment<'a> {
pub data: &'a [u8],
pub address: usize,
pub memory_size: usize,
pub writable: bool,
pub executable: bool,
}
impl<'a> Elf<'a> {
pub fn parse(bytes: &'a [u8]) -> Result<Self, ElfError> {
let header = bytes.get(..64).ok_or(ElfError)?;
// Bootstrap images are static ELF64 executables in the native ISA, always LE.
if &header[..7] != b"\x7fELF\x02\x01\x01"
|| u16_at(header, 16) != 2
|| u16_at(header, 18) != MACHINE
|| u32_at(header, 20) != 1
|| u16_at(header, 52) != 64
|| u16_at(header, 54) != 56
{
return Err(ElfError);
}
let offset = usize_at(header, 32);
let count = usize::from(u16_at(header, 56));
let end = offset.checked_add(count * 56).ok_or(ElfError)?;
let headers = bytes.get(offset..end).ok_or(ElfError)?;
if headers
.chunks_exact(56)
.any(|h| matches!(u32_at(h, 0), 2 | 3))
{
// There is no dynamic linker or relocation processing during bootstrap.
return Err(ElfError);
}
Ok(Self {
bytes,
headers,
entry: usize_at(header, 24),
})
}
pub fn segments(&self) -> impl Iterator<Item = Result<Segment<'a>, ElfError>> + '_ {
self.headers
.chunks_exact(56)
.filter(|h| u32_at(h, 0) == 1)
.map(|h| {
let offset = usize_at(h, 8);
let address = usize_at(h, 16);
let file_size = usize_at(h, 32);
let memory_size = usize_at(h, 40);
let alignment = usize_at(h, 48);
if file_size > memory_size
|| (alignment > 1
&& (!alignment.is_power_of_two()
|| address % alignment != offset % alignment))
{
return Err(ElfError);
}
let end = offset.checked_add(file_size).ok_or(ElfError)?;
let data = self.bytes.get(offset..end).ok_or(ElfError)?;
let flags = u32_at(h, 4);
Ok(Segment {
data,
address,
memory_size,
writable: flags & 2 != 0,
executable: flags & 1 != 0,
})
})
}
}
// Callers only read fixed offsets within already bounds-checked headers.
fn u16_at(bytes: &[u8], offset: usize) -> u16 {
let mut value = [0; 2];
value.copy_from_slice(&bytes[offset..offset + 2]);
u16::from_le_bytes(value)
}
fn u32_at(bytes: &[u8], offset: usize) -> u32 {
let mut value = [0; 4];
value.copy_from_slice(&bytes[offset..offset + 4]);
u32::from_le_bytes(value)
}
fn usize_at(bytes: &[u8], offset: usize) -> usize {
let mut value = [0; 8];
value.copy_from_slice(&bytes[offset..offset + 8]);
u64::from_le_bytes(value) as usize
}