diff --git a/src/elf/parser.rs b/src/elf/parser.rs index 77ca2dc1..8d655138 100644 --- a/src/elf/parser.rs +++ b/src/elf/parser.rs @@ -9,6 +9,7 @@ use std::io::SeekFrom; use std::mem; use std::mem::MaybeUninit; use std::ops::ControlFlow; +use std::ops::Deref; use std::ops::Deref as _; use std::path::Path; use std::path::PathBuf; @@ -567,18 +568,126 @@ impl Debug for Cache<'_> { } } +#[derive(Clone, Copy, Debug)] +struct EhdrExt2<'bcknd, B> +where + B: Backend<'bcknd>, +{ + /// The ELF header. + ehdr: B::Item, + /// Override of `ehdr.e_shnum`, handling of which is special-cased by + /// the ELF standard. + shnum: usize, + /// Override of `ehdr.e_phnum`, handling of which is special-cased by + /// the ELF standard. + phnum: usize, +} + +struct Cache2<'bcknd, B> +where + B: Backend<'bcknd>, +{ + /// A slice of the raw ELF data that we are about to parse. + backend: &'bcknd B, + /// The cached ELF header. + ehdr: OnceCell>, +} + +impl<'bcknd, B> Cache2<'bcknd, B> +where + B: Backend<'bcknd>, +{ + /// Create a new `Cache2` using the provided backend. + fn new(backend: &'bcknd B) -> Self { + Self { + backend, + ehdr: OnceCell::new(), + } + } + + /// Read the very first section header. + /// + /// ELF contains a couple of clauses that special case data ranges + /// of certain member variables to reference data from this header, + /// which otherwise is zeroed out. + #[inline] + fn read_first_shdr(&self, ehdr: &Elf64_Ehdr) -> Result> { + let shdr = self + .backend + .read_pod::(ehdr.e_shoff as _) + .context("failed to read Elf64_Shdr")?; + Ok(shdr) + } + + fn parse_ehdr(&self) -> Result> { + let ehdr = self.backend + .read_pod::(0) + .context("failed to read Elf64_Ehdr")?; + if !(ehdr.e_ident[0] == 0x7f + && ehdr.e_ident[1] == b'E' + && ehdr.e_ident[2] == b'L' + && ehdr.e_ident[3] == b'F') + { + return Err(Error::with_invalid_data(format!( + "encountered unexpected e_ident: {:x?}", + &ehdr.e_ident[0..4] + ))) + } + + // "If the number of entries in the section header table is larger than + // or equal to SHN_LORESERVE, e_shnum holds the value zero and the real + // number of entries in the section header table is held in the sh_size + // member of the initial entry in section header table." + let shnum = if ehdr.e_shnum == 0 { + let shdr = self.read_first_shdr(ehdr)?; + usize::try_from(shdr.sh_size).ok().ok_or_invalid_data(|| { + format!( + "ELF file contains unsupported number of sections ({})", + shdr.sh_size + ) + })? + } else { + ehdr.e_shnum.into() + }; + + // "If the number of entries in the program header table is + // larger than or equal to PN_XNUM (0xffff), this member holds + // PN_XNUM (0xffff) and the real number of entries in the + // program header table is held in the sh_info member of the + // initial entry in section header table." + let phnum = if ehdr.e_phnum == PN_XNUM { + let shdr = self.read_first_shdr(&ehdr)?; + usize::try_from(shdr.sh_info).ok().ok_or_invalid_data(|| { + format!( + "ELF file contains unsupported number of program headers ({})", + shdr.sh_info + ) + })? + } else { + ehdr.e_phnum.into() + }; + + let ehdr = EhdrExt2 { ehdr, shnum, phnum }; + Ok(ehdr) + } + + fn ensure_ehdr(&self) -> Result<&EhdrExt2<'bcknd, B>> { + self.ehdr.get_or_try_init(|| self.parse_ehdr()) + } +} + trait Backend<'r> { - type Item; - type Array; + type Item: Deref; + type Array: Deref; fn read_pod(&mut self, offset: u64) -> Result, Error> where - T: Clone + Pod + 'r; + T: Copy + Pod + 'r; fn read_pod_slice(&mut self, offset: u64, count: usize) -> Result, Error> where - T: Clone + Pod + 'r; + T: Copy + Pod + 'r; } impl<'r> Backend<'r> for &'r Mmap { @@ -587,7 +696,7 @@ impl<'r> Backend<'r> for &'r Mmap { fn read_pod(&mut self, offset: u64) -> Result, Error> where - T: Clone + Pod + 'r, + T: Pod + 'r, { let value = self .get(offset as _..) @@ -599,7 +708,7 @@ impl<'r> Backend<'r> for &'r Mmap { fn read_pod_slice(&mut self, offset: u64, count: usize) -> Result, Error> where - T: Clone + Pod + 'r, + T: Pod + 'r, { let value = self .get(offset as _..) @@ -616,7 +725,7 @@ impl<'r> Backend<'r> for &'r File { fn read_pod(&mut self, offset: u64) -> Result, Error> where - T: Clone + Pod + 'r, + T: Pod + 'r, { let _pos = self.seek(SeekFrom::Start(offset))?; @@ -634,7 +743,7 @@ impl<'r> Backend<'r> for &'r File { fn read_pod_slice(&mut self, offset: u64, count: usize) -> Result, Error> where - T: Clone + Pod + 'r, + T: Pod + 'r, { let _pos = self.seek(SeekFrom::Start(offset))?; let mut vec = Vec::::new(); diff --git a/src/elf/types.rs b/src/elf/types.rs index 3b6b8fd0..fe3cab17 100644 --- a/src/elf/types.rs +++ b/src/elf/types.rs @@ -12,7 +12,7 @@ type Elf64_Xword = u64; pub(crate) const ET_EXEC: u16 = 2; pub(crate) const ET_DYN: u16 = 3; -#[derive(Debug)] +#[derive(Clone, Copy, Debug)] #[repr(C)] pub(crate) struct Elf64_Ehdr { pub e_ident: [u8; EI_NIDENT], /* ELF "magic number" */ @@ -36,7 +36,7 @@ unsafe impl Pod for Elf64_Ehdr {} pub(crate) const PT_LOAD: u32 = 1; -#[derive(Debug)] +#[derive(Clone, Copy, Debug)] #[repr(C)] pub(crate) struct Elf64_Phdr { pub p_type: Elf64_Word, @@ -56,7 +56,7 @@ pub(crate) const PF_X: Elf64_Word = 1; pub(crate) const PN_XNUM: u16 = 0xffff; -#[derive(Debug)] +#[derive(Clone, Copy, Debug)] #[repr(C)] pub(crate) struct Elf64_Shdr { pub sh_name: Elf64_Word, /* Section name, index in string tbl */ @@ -87,7 +87,7 @@ pub(crate) const STT_OBJECT: u8 = 1; pub(crate) const STT_FUNC: u8 = 2; pub(crate) const STT_GNU_IFUNC: u8 = 10; -#[derive(Clone, Debug)] +#[derive(Clone, Copy, Debug)] #[repr(C)] pub(crate) struct Elf64_Sym { pub st_name: Elf64_Word, /* Symbol name, index in string tbl */ @@ -138,7 +138,7 @@ unsafe impl Pod for Elf64_Sym {} pub(crate) const NT_GNU_BUILD_ID: Elf64_Word = 3; -#[derive(Debug)] +#[derive(Clone, Copy, Debug)] #[repr(C)] pub(crate) struct Elf64_Nhdr { pub n_namesz: Elf64_Word, @@ -150,7 +150,7 @@ pub(crate) struct Elf64_Nhdr { unsafe impl Pod for Elf64_Nhdr {} -#[derive(Debug)] +#[derive(Clone, Copy, Debug)] #[repr(C)] pub(crate) struct Elf64_Chdr { /// Compression format. diff --git a/src/util.rs b/src/util.rs index 2f7f2c33..6758e20b 100644 --- a/src/util.rs +++ b/src/util.rs @@ -400,7 +400,7 @@ where /// /// # Safety /// Only safe to implement for types that are valid for any bit pattern. -pub(crate) unsafe trait Pod {} +pub(crate) unsafe trait Pod: Copy + Clone {} unsafe impl Pod for i8 {} unsafe impl Pod for u8 {}