120 lines
3.9 KiB
Rust
120 lines
3.9 KiB
Rust
// SPDX-License-Identifier: GPL-3.0-or-later
|
|
use thiserror::Error;
|
|
|
|
#[derive(Error, Debug)]
|
|
pub enum ElfError {
|
|
#[error("File is smaller than minimum ELF64 header (64 bytes)")]
|
|
TooSmall,
|
|
#[error("Invalid ELF magic header: expected 0x7F 'ELF'")]
|
|
InvalidMagic,
|
|
#[error("Unsupported ELF class: expected 64-bit")]
|
|
UnsupportedClass,
|
|
#[error("Unsupported endianness: expected Little Endian")]
|
|
UnsupportedEndianness,
|
|
#[error("Unsupported architecture: expected x86_64 (62), found {0}")]
|
|
UnsupportedMachine(u16),
|
|
#[error("Invalid ELF type: expected ET_DYN (3), found {0}")]
|
|
UnsupportedType(u16),
|
|
#[error("ELF file is truncated or corrupted: segment extends beyond file boundaries")]
|
|
Truncated,
|
|
}
|
|
|
|
pub const PT_LOAD: u32 = 1;
|
|
pub const PT_DYNAMIC: u32 = 2;
|
|
pub const PT_TLS: u32 = 7;
|
|
|
|
pub const PF_X: u32 = 1;
|
|
pub const PF_W: u32 = 2;
|
|
pub const PF_R: u32 = 4;
|
|
|
|
#[derive(Debug, Clone)]
|
|
pub struct ProgramHeader {
|
|
pub p_type: u32,
|
|
pub p_flags: u32,
|
|
pub p_offset: u64,
|
|
pub p_vaddr: u64,
|
|
pub p_paddr: u64,
|
|
pub p_filesz: u64,
|
|
pub p_memsz: u64,
|
|
pub p_align: u64,
|
|
}
|
|
|
|
#[derive(Debug, Clone)]
|
|
pub struct ElfBinary<'a> {
|
|
pub raw: &'a [u8],
|
|
pub entry_point: u64,
|
|
pub program_headers: Vec<ProgramHeader>,
|
|
}
|
|
|
|
impl<'a> ElfBinary<'a> {
|
|
pub fn parse(bytes: &'a [u8]) -> Result<Self, ElfError> {
|
|
if bytes.len() < 64 {
|
|
return Err(ElfError::TooSmall);
|
|
}
|
|
|
|
if &bytes[0..4] != b"\x7fELF" {
|
|
return Err(ElfError::InvalidMagic);
|
|
}
|
|
|
|
if bytes[4] != 2 {
|
|
return Err(ElfError::UnsupportedClass);
|
|
}
|
|
|
|
if bytes[5] != 1 {
|
|
return Err(ElfError::UnsupportedEndianness);
|
|
}
|
|
|
|
let e_type = u16::from_le_bytes([bytes[16], bytes[17]]);
|
|
if e_type != 3 {
|
|
return Err(ElfError::UnsupportedType(e_type));
|
|
}
|
|
|
|
let e_machine = u16::from_le_bytes([bytes[18], bytes[19]]);
|
|
if e_machine != 62 {
|
|
return Err(ElfError::UnsupportedMachine(e_machine));
|
|
}
|
|
|
|
let entry_point = u64::from_le_bytes(bytes[24..32].try_into().unwrap());
|
|
let phoff = u64::from_le_bytes(bytes[32..40].try_into().unwrap()) as usize;
|
|
let phentsize = u16::from_le_bytes([bytes[54], bytes[55]]) as usize;
|
|
let phnum = u16::from_le_bytes([bytes[56], bytes[57]]) as usize;
|
|
|
|
let mut program_headers = Vec::with_capacity(phnum);
|
|
|
|
if phnum > 0 && phentsize >= 56 {
|
|
for i in 0..phnum {
|
|
let offset = phoff + i * phentsize;
|
|
if offset + 56 > bytes.len() {
|
|
return Err(ElfError::Truncated);
|
|
}
|
|
|
|
let ph = ProgramHeader {
|
|
p_type: u32::from_le_bytes(bytes[offset..offset + 4].try_into().unwrap()),
|
|
p_flags: u32::from_le_bytes(bytes[offset + 4..offset + 8].try_into().unwrap()),
|
|
p_offset: u64::from_le_bytes(bytes[offset + 8..offset + 16].try_into().unwrap()),
|
|
p_vaddr: u64::from_le_bytes(bytes[offset + 16..offset + 24].try_into().unwrap()),
|
|
p_paddr: u64::from_le_bytes(bytes[offset + 24..offset + 32].try_into().unwrap()),
|
|
p_filesz: u64::from_le_bytes(bytes[offset + 32..offset + 40].try_into().unwrap()),
|
|
p_memsz: u64::from_le_bytes(bytes[offset + 40..offset + 48].try_into().unwrap()),
|
|
p_align: u64::from_le_bytes(bytes[offset + 48..offset + 56].try_into().unwrap()),
|
|
};
|
|
|
|
program_headers.push(ph);
|
|
}
|
|
}
|
|
|
|
Ok(Self {
|
|
raw: bytes,
|
|
entry_point,
|
|
program_headers,
|
|
})
|
|
}
|
|
|
|
pub fn load_segments(&self) -> impl Iterator<Item = &ProgramHeader> {
|
|
self.program_headers.iter().filter(|ph| ph.p_type == PT_LOAD)
|
|
}
|
|
|
|
pub fn dynamic_segment(&self) -> Option<&ProgramHeader> {
|
|
self.program_headers.iter().find(|ph| ph.p_type == PT_DYNAMIC)
|
|
}
|
|
}
|