This commit is contained in:
nora 2023-02-12 00:11:29 +01:00
parent 8aa4068b1b
commit 478a3c15e1
3 changed files with 149 additions and 84 deletions

View file

@ -2,10 +2,14 @@
//!
//! See https://man7.org/linux/man-pages/man5/elf.5.html
use crate::consts as c;
use crate::{
consts as c,
idx::{define_idx, ElfIndexExt, ToIdxUsize},
ElfParseError, Result,
};
use bstr::BStr;
use std::{fmt::Debug, mem, ops, slice::SliceIndex, string};
use std::{fmt::Debug, mem, string};
use bytemuck::{Pod, PodCastError, Zeroable};
@ -18,49 +22,23 @@ pub struct Addr(pub u64);
#[repr(transparent)]
pub struct Offset(pub u64);
impl Offset {
fn usize(self) -> usize {
impl ToIdxUsize for Offset {
fn to_idx_usize(self) -> usize {
self.0 as usize
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Zeroable, Pod)]
#[repr(transparent)]
pub struct ShStringIdx(pub u32);
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Zeroable, Pod)]
#[repr(transparent)]
pub struct StringIdx(pub u32);
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Zeroable, Pod)]
#[repr(transparent)]
pub struct SymIdx(pub u32);
#[derive(Debug, Clone, thiserror::Error)]
pub enum ElfParseError {
#[error("The file is too small. Expected at least {0} bytes, found {1} bytes")]
FileTooSmall(usize, usize),
#[error("The input is not aligned in memory. Expected align {0}, found align {1}")]
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),
#[error("The SHT_SYMTAB section was not found")]
SymtabNotFound,
#[error("The section with the name {0:?} was not found")]
SectionNotFound(std::result::Result<string::String, Vec<u8>>),
define_idx! {
pub struct ShStringIdx(u32);
}
type Result<T> = std::result::Result<T, ElfParseError>;
define_idx! {
pub struct StringIdx(u32);
}
define_idx! {
pub struct SymIdx(u32);
}
/// A raw ELF. Does not come with cute ears for now.
#[derive(Debug)]
@ -228,9 +206,8 @@ impl<'a> Elf<'a> {
));
}
let off = header.phoff.usize();
load_slice(
self.data.get_elf(off.., "program header offset")?,
self.data.get_elf(header.phoff.., "program header offset")?,
header.phnum.into(),
)
}
@ -250,9 +227,8 @@ impl<'a> Elf<'a> {
actual_ent_size,
));
}
let off = header.shoff.usize();
load_slice(
self.data.get_elf(off.., "sectoin header offset")?,
self.data.get_elf(header.shoff.., "sectoin header offset")?,
header.shnum.into(),
)
}
@ -281,8 +257,8 @@ impl<'a> Elf<'a> {
}
self.data
.get_elf(sh.offset.usize().., "section offset")?
.get_elf(..(sh.size as usize), "section size")
.get_elf(sh.offset.., "section offset")?
.get_elf(..sh.size, "section size")
}
pub fn sh_str_table(&self) -> Result<&[u8]> {
@ -313,24 +289,22 @@ impl<'a> Elf<'a> {
}
pub fn sh_string(&self, idx: ShStringIdx) -> Result<&BStr> {
let idx = idx.0 as usize;
let str_table = self.sh_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_or(ElfParseError::NoStringNulTerm(idx.to_idx_usize()))?;
Ok(BStr::new(&indexed[..end]))
}
pub fn string(&self, idx: StringIdx) -> Result<&BStr> {
let idx = idx.0 as usize;
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_or(ElfParseError::NoStringNulTerm(idx.to_idx_usize()))?;
Ok(BStr::new(&indexed[..end]))
}
@ -362,7 +336,6 @@ impl<'a> Elf<'a> {
}
pub fn symbol(&self, idx: SymIdx) -> Result<&Sym> {
let idx = idx.0 as usize;
self.symbols()?.get_elf(idx, "symbol index")
}
}
@ -396,40 +369,6 @@ fn load_slice<T: Pod>(data: &[u8], amount_of_elems: usize) -> Result<&[T]> {
})
}
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))
}
}
#[cfg(test)]
mod tests {
use std::{fs, path::Path};

99
elven-parser/src/idx.rs Normal file
View file

@ -0,0 +1,99 @@
use std::{
ops::{RangeFrom, RangeTo},
slice::SliceIndex,
};
use crate::{ElfParseError, Result};
macro_rules! define_idx {
(
$vis:vis struct $name:ident($ty:ty);
) => {
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, bytemuck::Zeroable, bytemuck::Pod)]
#[repr(transparent)]
$vis struct $name(pub $ty);
impl crate::idx::ToIdxUsize for $name {
fn to_idx_usize(self) -> usize {
self.0 as usize
}
}
};
}
pub(crate) use define_idx;
pub(crate) trait ElfIndex<T: ?Sized> {
type SliceIdx: SliceIndex<T>;
fn bound(&self) -> usize;
fn to_slice_idx(self) -> Self::SliceIdx;
}
impl<T, U: ToIdxUsize> ElfIndex<[T]> for U {
type SliceIdx = usize;
fn bound(&self) -> usize {
self.to_idx_usize()
}
fn to_slice_idx(self) -> Self::SliceIdx {
self.to_idx_usize()
}
}
impl<T, U: ToIdxUsize> ElfIndex<[T]> for RangeFrom<U> {
type SliceIdx = RangeFrom<usize>;
fn bound(&self) -> usize {
self.start.to_idx_usize()
}
fn to_slice_idx(self) -> Self::SliceIdx {
RangeFrom {
start: self.start.to_idx_usize(),
}
}
}
impl<T, U: ToIdxUsize> ElfIndex<[T]> for RangeTo<U> {
type SliceIdx = RangeTo<usize>;
fn bound(&self) -> usize {
self.end.to_idx_usize()
}
fn to_slice_idx(self) -> Self::SliceIdx {
RangeTo {
end: self.end.to_idx_usize(),
}
}
}
pub(crate) trait ToIdxUsize: Copy {
fn to_idx_usize(self) -> usize;
}
impl ToIdxUsize for usize {
fn to_idx_usize(self) -> usize {
self
}
}
impl ToIdxUsize for u64 {
fn to_idx_usize(self) -> usize {
self as usize
}
}
pub(crate) trait ElfIndexExt {
fn get_elf<I: ElfIndex<Self>>(
&self,
idx: I,
msg: &'static str,
) -> Result<&<I::SliceIdx as SliceIndex<Self>>::Output>;
}
impl<T> ElfIndexExt for [T] {
fn get_elf<I: ElfIndex<Self>>(
&self,
idx: I,
msg: &'static str,
) -> Result<&<I::SliceIdx as SliceIndex<Self>>::Output> {
let bound = idx.bound();
self.get(idx.to_slice_idx())
.ok_or(ElfParseError::IndexOutOfBounds(msg, bound))
}
}

View file

@ -1,2 +1,29 @@
pub mod consts;
pub mod defs;
mod idx;
#[derive(Debug, Clone, thiserror::Error)]
pub enum ElfParseError {
#[error("The file is too small. Expected at least {0} bytes, found {1} bytes")]
FileTooSmall(usize, usize),
#[error("The input is not aligned in memory. Expected align {0}, found align {1}")]
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),
#[error("The SHT_SYMTAB section was not found")]
SymtabNotFound,
#[error("The section with the name {0:?} was not found")]
SectionNotFound(std::result::Result<String, Vec<u8>>),
}
pub type Result<T> = std::result::Result<T, ElfParseError>;