use core::fmt::Debug; use core::{mem, slice, str}; use crate::elf; use crate::endian::{self, Endianness}; use crate::pod::Pod; use crate::read::{self, Bytes, ObjectSegment, ReadError, ReadRef, SegmentFlags}; use super::{ElfFile, FileHeader, NoteIterator}; /// An iterator for the segments in an [`ElfFile32`](super::ElfFile32). pub type ElfSegmentIterator32<'data, 'file, Endian = Endianness, R = &'data [u8]> = ElfSegmentIterator<'data, 'file, elf::FileHeader32, R>; /// An iterator for the segments in an [`ElfFile64`](super::ElfFile64). pub type ElfSegmentIterator64<'data, 'file, Endian = Endianness, R = &'data [u8]> = ElfSegmentIterator<'data, 'file, elf::FileHeader64, R>; /// An iterator for the segments in an [`ElfFile`]. #[derive(Debug)] pub struct ElfSegmentIterator<'data, 'file, Elf, R = &'data [u8]> where Elf: FileHeader, R: ReadRef<'data>, { pub(super) file: &'file ElfFile<'data, Elf, R>, pub(super) iter: slice::Iter<'data, Elf::ProgramHeader>, } impl<'data, 'file, Elf, R> Iterator for ElfSegmentIterator<'data, 'file, Elf, R> where Elf: FileHeader, R: ReadRef<'data>, { type Item = ElfSegment<'data, 'file, Elf, R>; fn next(&mut self) -> Option { for segment in self.iter.by_ref() { if segment.p_type(self.file.endian) == elf::PT_LOAD { return Some(ElfSegment { file: self.file, segment, }); } } None } } /// A segment in an [`ElfFile32`](super::ElfFile32). pub type ElfSegment32<'data, 'file, Endian = Endianness, R = &'data [u8]> = ElfSegment<'data, 'file, elf::FileHeader32, R>; /// A segment in an [`ElfFile64`](super::ElfFile64). pub type ElfSegment64<'data, 'file, Endian = Endianness, R = &'data [u8]> = ElfSegment<'data, 'file, elf::FileHeader64, R>; /// A segment in an [`ElfFile`]. /// /// Most functionality is provided by the [`ObjectSegment`] trait implementation. #[derive(Debug)] pub struct ElfSegment<'data, 'file, Elf, R = &'data [u8]> where Elf: FileHeader, R: ReadRef<'data>, { pub(super) file: &'file ElfFile<'data, Elf, R>, pub(super) segment: &'data Elf::ProgramHeader, } impl<'data, 'file, Elf: FileHeader, R: ReadRef<'data>> ElfSegment<'data, 'file, Elf, R> { fn bytes(&self) -> read::Result<&'data [u8]> { self.segment .data(self.file.endian, self.file.data) .read_error("Invalid ELF segment size or offset") } } impl<'data, 'file, Elf, R> read::private::Sealed for ElfSegment<'data, 'file, Elf, R> where Elf: FileHeader, R: ReadRef<'data>, { } impl<'data, 'file, Elf, R> ObjectSegment<'data> for ElfSegment<'data, 'file, Elf, R> where Elf: FileHeader, R: ReadRef<'data>, { #[inline] fn address(&self) -> u64 { self.segment.p_vaddr(self.file.endian).into() } #[inline] fn size(&self) -> u64 { self.segment.p_memsz(self.file.endian).into() } #[inline] fn align(&self) -> u64 { self.segment.p_align(self.file.endian).into() } #[inline] fn file_range(&self) -> (u64, u64) { self.segment.file_range(self.file.endian) } #[inline] fn data(&self) -> read::Result<&'data [u8]> { self.bytes() } fn data_range(&self, address: u64, size: u64) -> read::Result> { Ok(read::util::data_range( self.bytes()?, self.address(), address, size, )) } #[inline] fn name_bytes(&self) -> read::Result> { Ok(None) } #[inline] fn name(&self) -> read::Result> { Ok(None) } #[inline] fn flags(&self) -> SegmentFlags { let p_flags = self.segment.p_flags(self.file.endian); SegmentFlags::Elf { p_flags } } } /// A trait for generic access to [`elf::ProgramHeader32`] and [`elf::ProgramHeader64`]. #[allow(missing_docs)] pub trait ProgramHeader: Debug + Pod { type Elf: FileHeader; type Word: Into; type Endian: endian::Endian; fn p_type(&self, endian: Self::Endian) -> u32; fn p_flags(&self, endian: Self::Endian) -> u32; fn p_offset(&self, endian: Self::Endian) -> Self::Word; fn p_vaddr(&self, endian: Self::Endian) -> Self::Word; fn p_paddr(&self, endian: Self::Endian) -> Self::Word; fn p_filesz(&self, endian: Self::Endian) -> Self::Word; fn p_memsz(&self, endian: Self::Endian) -> Self::Word; fn p_align(&self, endian: Self::Endian) -> Self::Word; /// Return the offset and size of the segment in the file. fn file_range(&self, endian: Self::Endian) -> (u64, u64) { (self.p_offset(endian).into(), self.p_filesz(endian).into()) } /// Return the segment data. /// /// Returns `Err` for invalid values. fn data<'data, R: ReadRef<'data>>( &self, endian: Self::Endian, data: R, ) -> Result<&'data [u8], ()> { let (offset, size) = self.file_range(endian); data.read_bytes_at(offset, size) } /// Return the segment data as a slice of the given type. /// /// Allows padding at the end of the data. /// Returns `Ok(&[])` if the segment has no data. /// Returns `Err` for invalid values, including bad alignment. fn data_as_array<'data, T: Pod, R: ReadRef<'data>>( &self, endian: Self::Endian, data: R, ) -> Result<&'data [T], ()> { let mut data = self.data(endian, data).map(Bytes)?; data.read_slice(data.len() / mem::size_of::()) } /// Return the segment data in the given virtual address range /// /// Returns `Ok(None)` if the segment does not contain the address. /// Returns `Err` for invalid values. fn data_range<'data, R: ReadRef<'data>>( &self, endian: Self::Endian, data: R, address: u64, size: u64, ) -> Result, ()> { Ok(read::util::data_range( self.data(endian, data)?, self.p_vaddr(endian).into(), address, size, )) } /// Return entries in a dynamic segment. /// /// Returns `Ok(None)` if the segment is not `PT_DYNAMIC`. /// Returns `Err` for invalid values. fn dynamic<'data, R: ReadRef<'data>>( &self, endian: Self::Endian, data: R, ) -> read::Result::Dyn]>> { if self.p_type(endian) != elf::PT_DYNAMIC { return Ok(None); } let dynamic = self .data_as_array(endian, data) .read_error("Invalid ELF dynamic segment offset or size")?; Ok(Some(dynamic)) } /// Return a note iterator for the segment data. /// /// Returns `Ok(None)` if the segment does not contain notes. /// Returns `Err` for invalid values. fn notes<'data, R: ReadRef<'data>>( &self, endian: Self::Endian, data: R, ) -> read::Result>> { if self.p_type(endian) != elf::PT_NOTE { return Ok(None); } let data = self .data(endian, data) .read_error("Invalid ELF note segment offset or size")?; let notes = NoteIterator::new(endian, self.p_align(endian), data)?; Ok(Some(notes)) } } impl ProgramHeader for elf::ProgramHeader32 { type Word = u32; type Endian = Endian; type Elf = elf::FileHeader32; #[inline] fn p_type(&self, endian: Self::Endian) -> u32 { self.p_type.get(endian) } #[inline] fn p_flags(&self, endian: Self::Endian) -> u32 { self.p_flags.get(endian) } #[inline] fn p_offset(&self, endian: Self::Endian) -> Self::Word { self.p_offset.get(endian) } #[inline] fn p_vaddr(&self, endian: Self::Endian) -> Self::Word { self.p_vaddr.get(endian) } #[inline] fn p_paddr(&self, endian: Self::Endian) -> Self::Word { self.p_paddr.get(endian) } #[inline] fn p_filesz(&self, endian: Self::Endian) -> Self::Word { self.p_filesz.get(endian) } #[inline] fn p_memsz(&self, endian: Self::Endian) -> Self::Word { self.p_memsz.get(endian) } #[inline] fn p_align(&self, endian: Self::Endian) -> Self::Word { self.p_align.get(endian) } } impl ProgramHeader for elf::ProgramHeader64 { type Word = u64; type Endian = Endian; type Elf = elf::FileHeader64; #[inline] fn p_type(&self, endian: Self::Endian) -> u32 { self.p_type.get(endian) } #[inline] fn p_flags(&self, endian: Self::Endian) -> u32 { self.p_flags.get(endian) } #[inline] fn p_offset(&self, endian: Self::Endian) -> Self::Word { self.p_offset.get(endian) } #[inline] fn p_vaddr(&self, endian: Self::Endian) -> Self::Word { self.p_vaddr.get(endian) } #[inline] fn p_paddr(&self, endian: Self::Endian) -> Self::Word { self.p_paddr.get(endian) } #[inline] fn p_filesz(&self, endian: Self::Endian) -> Self::Word { self.p_filesz.get(endian) } #[inline] fn p_memsz(&self, endian: Self::Endian) -> Self::Word { self.p_memsz.get(endian) } #[inline] fn p_align(&self, endian: Self::Endian) -> Self::Word { self.p_align.get(endian) } }