| use core::fmt::Debug; |
| use core::{iter, mem, slice, str}; |
| |
| use crate::elf; |
| use crate::endian::{self, Endianness, U32Bytes}; |
| use crate::pod::Pod; |
| use crate::read::{ |
| self, Bytes, CompressedData, CompressedFileRange, CompressionFormat, Error, ObjectSection, |
| ReadError, ReadRef, SectionFlags, SectionIndex, SectionKind, StringTable, |
| }; |
| |
| use super::{ |
| AttributesSection, CompressionHeader, ElfFile, ElfSectionRelocationIterator, FileHeader, |
| GnuHashTable, HashTable, NoteIterator, RelocationSections, SymbolTable, VerdefIterator, |
| VerneedIterator, VersionTable, |
| }; |
| |
| /// The table of section headers in an ELF file. |
| /// |
| /// Also includes the string table used for the section names. |
| #[derive(Debug, Default, Clone, Copy)] |
| pub struct SectionTable<'data, Elf: FileHeader, R = &'data [u8]> |
| where |
| R: ReadRef<'data>, |
| { |
| sections: &'data [Elf::SectionHeader], |
| strings: StringTable<'data, R>, |
| } |
| |
| impl<'data, Elf: FileHeader, R: ReadRef<'data>> SectionTable<'data, Elf, R> { |
| /// Create a new section table. |
| #[inline] |
| pub fn new(sections: &'data [Elf::SectionHeader], strings: StringTable<'data, R>) -> Self { |
| SectionTable { sections, strings } |
| } |
| |
| /// Iterate over the section headers. |
| #[inline] |
| pub fn iter(&self) -> slice::Iter<'data, Elf::SectionHeader> { |
| self.sections.iter() |
| } |
| |
| /// Return true if the section table is empty. |
| #[inline] |
| pub fn is_empty(&self) -> bool { |
| self.sections.is_empty() |
| } |
| |
| /// The number of section headers. |
| #[inline] |
| pub fn len(&self) -> usize { |
| self.sections.len() |
| } |
| |
| /// Return the section header at the given index. |
| pub fn section(&self, index: SectionIndex) -> read::Result<&'data Elf::SectionHeader> { |
| self.sections |
| .get(index.0) |
| .read_error("Invalid ELF section index") |
| } |
| |
| /// Return the section header with the given name. |
| /// |
| /// Ignores sections with invalid names. |
| pub fn section_by_name( |
| &self, |
| endian: Elf::Endian, |
| name: &[u8], |
| ) -> Option<(usize, &'data Elf::SectionHeader)> { |
| self.sections |
| .iter() |
| .enumerate() |
| .find(|(_, section)| self.section_name(endian, section) == Ok(name)) |
| } |
| |
| /// Return the section name for the given section header. |
| pub fn section_name( |
| &self, |
| endian: Elf::Endian, |
| section: &'data Elf::SectionHeader, |
| ) -> read::Result<&'data [u8]> { |
| section.name(endian, self.strings) |
| } |
| |
| /// Return the string table at the given section index. |
| /// |
| /// Returns an error if the section is not a string table. |
| #[inline] |
| pub fn strings( |
| &self, |
| endian: Elf::Endian, |
| data: R, |
| index: SectionIndex, |
| ) -> read::Result<StringTable<'data, R>> { |
| self.section(index)? |
| .strings(endian, data)? |
| .read_error("Invalid ELF string section type") |
| } |
| |
| /// Return the symbol table of the given section type. |
| /// |
| /// Returns an empty symbol table if the symbol table does not exist. |
| #[inline] |
| pub fn symbols( |
| &self, |
| endian: Elf::Endian, |
| data: R, |
| sh_type: u32, |
| ) -> read::Result<SymbolTable<'data, Elf, R>> { |
| debug_assert!(sh_type == elf::SHT_DYNSYM || sh_type == elf::SHT_SYMTAB); |
| |
| let (index, section) = match self |
| .iter() |
| .enumerate() |
| .find(|s| s.1.sh_type(endian) == sh_type) |
| { |
| Some(s) => s, |
| None => return Ok(SymbolTable::default()), |
| }; |
| |
| SymbolTable::parse(endian, data, self, SectionIndex(index), section) |
| } |
| |
| /// Return the symbol table at the given section index. |
| /// |
| /// Returns an error if the section is not a symbol table. |
| #[inline] |
| pub fn symbol_table_by_index( |
| &self, |
| endian: Elf::Endian, |
| data: R, |
| index: SectionIndex, |
| ) -> read::Result<SymbolTable<'data, Elf, R>> { |
| let section = self.section(index)?; |
| match section.sh_type(endian) { |
| elf::SHT_DYNSYM | elf::SHT_SYMTAB => {} |
| _ => return Err(Error("Invalid ELF symbol table section type")), |
| } |
| SymbolTable::parse(endian, data, self, index, section) |
| } |
| |
| /// Create a mapping from section index to associated relocation sections. |
| #[inline] |
| pub fn relocation_sections( |
| &self, |
| endian: Elf::Endian, |
| symbol_section: SectionIndex, |
| ) -> read::Result<RelocationSections> { |
| RelocationSections::parse(endian, self, symbol_section) |
| } |
| |
| /// Return the contents of a dynamic section. |
| /// |
| /// Also returns the linked string table index. |
| /// |
| /// Returns `Ok(None)` if there is no `SHT_DYNAMIC` section. |
| /// Returns `Err` for invalid values. |
| pub fn dynamic( |
| &self, |
| endian: Elf::Endian, |
| data: R, |
| ) -> read::Result<Option<(&'data [Elf::Dyn], SectionIndex)>> { |
| for section in self.sections { |
| if let Some(dynamic) = section.dynamic(endian, data)? { |
| return Ok(Some(dynamic)); |
| } |
| } |
| Ok(None) |
| } |
| |
| /// Return the header of a SysV hash section. |
| /// |
| /// Returns `Ok(None)` if there is no SysV GNU hash section. |
| /// Returns `Err` for invalid values. |
| pub fn hash_header( |
| &self, |
| endian: Elf::Endian, |
| data: R, |
| ) -> read::Result<Option<&'data elf::HashHeader<Elf::Endian>>> { |
| for section in self.sections { |
| if let Some(hash) = section.hash_header(endian, data)? { |
| return Ok(Some(hash)); |
| } |
| } |
| Ok(None) |
| } |
| |
| /// Return the contents of a SysV hash section. |
| /// |
| /// Also returns the linked symbol table index. |
| /// |
| /// Returns `Ok(None)` if there is no SysV hash section. |
| /// Returns `Err` for invalid values. |
| pub fn hash( |
| &self, |
| endian: Elf::Endian, |
| data: R, |
| ) -> read::Result<Option<(HashTable<'data, Elf>, SectionIndex)>> { |
| for section in self.sections { |
| if let Some(hash) = section.hash(endian, data)? { |
| return Ok(Some(hash)); |
| } |
| } |
| Ok(None) |
| } |
| |
| /// Return the header of a GNU hash section. |
| /// |
| /// Returns `Ok(None)` if there is no GNU hash section. |
| /// Returns `Err` for invalid values. |
| pub fn gnu_hash_header( |
| &self, |
| endian: Elf::Endian, |
| data: R, |
| ) -> read::Result<Option<&'data elf::GnuHashHeader<Elf::Endian>>> { |
| for section in self.sections { |
| if let Some(hash) = section.gnu_hash_header(endian, data)? { |
| return Ok(Some(hash)); |
| } |
| } |
| Ok(None) |
| } |
| |
| /// Return the contents of a GNU hash section. |
| /// |
| /// Also returns the linked symbol table index. |
| /// |
| /// Returns `Ok(None)` if there is no GNU hash section. |
| /// Returns `Err` for invalid values. |
| pub fn gnu_hash( |
| &self, |
| endian: Elf::Endian, |
| data: R, |
| ) -> read::Result<Option<(GnuHashTable<'data, Elf>, SectionIndex)>> { |
| for section in self.sections { |
| if let Some(hash) = section.gnu_hash(endian, data)? { |
| return Ok(Some(hash)); |
| } |
| } |
| Ok(None) |
| } |
| |
| /// Return the contents of a `SHT_GNU_VERSYM` section. |
| /// |
| /// Also returns the linked symbol table index. |
| /// |
| /// Returns `Ok(None)` if there is no `SHT_GNU_VERSYM` section. |
| /// Returns `Err` for invalid values. |
| pub fn gnu_versym( |
| &self, |
| endian: Elf::Endian, |
| data: R, |
| ) -> read::Result<Option<(&'data [elf::Versym<Elf::Endian>], SectionIndex)>> { |
| for section in self.sections { |
| if let Some(syms) = section.gnu_versym(endian, data)? { |
| return Ok(Some(syms)); |
| } |
| } |
| Ok(None) |
| } |
| |
| /// Return the contents of a `SHT_GNU_VERDEF` section. |
| /// |
| /// Also returns the linked string table index. |
| /// |
| /// Returns `Ok(None)` if there is no `SHT_GNU_VERDEF` section. |
| /// Returns `Err` for invalid values. |
| pub fn gnu_verdef( |
| &self, |
| endian: Elf::Endian, |
| data: R, |
| ) -> read::Result<Option<(VerdefIterator<'data, Elf>, SectionIndex)>> { |
| for section in self.sections { |
| if let Some(defs) = section.gnu_verdef(endian, data)? { |
| return Ok(Some(defs)); |
| } |
| } |
| Ok(None) |
| } |
| |
| /// Return the contents of a `SHT_GNU_VERNEED` section. |
| /// |
| /// Also returns the linked string table index. |
| /// |
| /// Returns `Ok(None)` if there is no `SHT_GNU_VERNEED` section. |
| /// Returns `Err` for invalid values. |
| pub fn gnu_verneed( |
| &self, |
| endian: Elf::Endian, |
| data: R, |
| ) -> read::Result<Option<(VerneedIterator<'data, Elf>, SectionIndex)>> { |
| for section in self.sections { |
| if let Some(needs) = section.gnu_verneed(endian, data)? { |
| return Ok(Some(needs)); |
| } |
| } |
| Ok(None) |
| } |
| |
| /// Returns the symbol version table. |
| /// |
| /// Returns `Ok(None)` if there is no `SHT_GNU_VERSYM` section. |
| /// Returns `Err` for invalid values. |
| pub fn versions( |
| &self, |
| endian: Elf::Endian, |
| data: R, |
| ) -> read::Result<Option<VersionTable<'data, Elf>>> { |
| let (versyms, link) = match self.gnu_versym(endian, data)? { |
| Some(val) => val, |
| None => return Ok(None), |
| }; |
| let strings = self.symbol_table_by_index(endian, data, link)?.strings(); |
| // TODO: check links? |
| let verdefs = self.gnu_verdef(endian, data)?.map(|x| x.0); |
| let verneeds = self.gnu_verneed(endian, data)?.map(|x| x.0); |
| VersionTable::parse(endian, versyms, verdefs, verneeds, strings).map(Some) |
| } |
| } |
| |
| /// An iterator over the sections of an `ElfFile32`. |
| pub type ElfSectionIterator32<'data, 'file, Endian = Endianness, R = &'data [u8]> = |
| ElfSectionIterator<'data, 'file, elf::FileHeader32<Endian>, R>; |
| /// An iterator over the sections of an `ElfFile64`. |
| pub type ElfSectionIterator64<'data, 'file, Endian = Endianness, R = &'data [u8]> = |
| ElfSectionIterator<'data, 'file, elf::FileHeader64<Endian>, R>; |
| |
| /// An iterator over the sections of an `ElfFile`. |
| #[derive(Debug)] |
| pub struct ElfSectionIterator<'data, 'file, Elf, R = &'data [u8]> |
| where |
| Elf: FileHeader, |
| R: ReadRef<'data>, |
| { |
| pub(super) file: &'file ElfFile<'data, Elf, R>, |
| pub(super) iter: iter::Enumerate<slice::Iter<'data, Elf::SectionHeader>>, |
| } |
| |
| impl<'data, 'file, Elf, R> Iterator for ElfSectionIterator<'data, 'file, Elf, R> |
| where |
| Elf: FileHeader, |
| R: ReadRef<'data>, |
| { |
| type Item = ElfSection<'data, 'file, Elf, R>; |
| |
| fn next(&mut self) -> Option<Self::Item> { |
| self.iter.next().map(|(index, section)| ElfSection { |
| index: SectionIndex(index), |
| file: self.file, |
| section, |
| }) |
| } |
| } |
| |
| /// A section of an `ElfFile32`. |
| pub type ElfSection32<'data, 'file, Endian = Endianness, R = &'data [u8]> = |
| ElfSection<'data, 'file, elf::FileHeader32<Endian>, R>; |
| /// A section of an `ElfFile64`. |
| pub type ElfSection64<'data, 'file, Endian = Endianness, R = &'data [u8]> = |
| ElfSection<'data, 'file, elf::FileHeader64<Endian>, R>; |
| |
| /// A section of an `ElfFile`. |
| #[derive(Debug)] |
| pub struct ElfSection<'data, 'file, Elf, R = &'data [u8]> |
| where |
| Elf: FileHeader, |
| R: ReadRef<'data>, |
| { |
| pub(super) file: &'file ElfFile<'data, Elf, R>, |
| pub(super) index: SectionIndex, |
| pub(super) section: &'data Elf::SectionHeader, |
| } |
| |
| impl<'data, 'file, Elf: FileHeader, R: ReadRef<'data>> ElfSection<'data, 'file, Elf, R> { |
| fn bytes(&self) -> read::Result<&'data [u8]> { |
| self.section |
| .data(self.file.endian, self.file.data) |
| .read_error("Invalid ELF section size or offset") |
| } |
| |
| fn maybe_compressed(&self) -> read::Result<Option<CompressedFileRange>> { |
| let endian = self.file.endian; |
| if let Some((header, offset, compressed_size)) = |
| self.section.compression(endian, self.file.data)? |
| { |
| let format = match header.ch_type(endian) { |
| elf::ELFCOMPRESS_ZLIB => CompressionFormat::Zlib, |
| elf::ELFCOMPRESS_ZSTD => CompressionFormat::Zstandard, |
| _ => return Err(Error("Unsupported ELF compression type")), |
| }; |
| let uncompressed_size = header.ch_size(endian).into(); |
| Ok(Some(CompressedFileRange { |
| format, |
| offset, |
| compressed_size, |
| uncompressed_size, |
| })) |
| } else { |
| Ok(None) |
| } |
| } |
| |
| /// Try GNU-style "ZLIB" header decompression. |
| fn maybe_compressed_gnu(&self) -> read::Result<Option<CompressedFileRange>> { |
| let name = match self.name() { |
| Ok(name) => name, |
| // I think it's ok to ignore this error? |
| Err(_) => return Ok(None), |
| }; |
| if !name.starts_with(".zdebug_") { |
| return Ok(None); |
| } |
| let (section_offset, section_size) = self |
| .section |
| .file_range(self.file.endian) |
| .read_error("Invalid ELF GNU compressed section type")?; |
| let mut offset = section_offset; |
| let data = self.file.data; |
| // Assume ZLIB-style uncompressed data is no more than 4GB to avoid accidentally |
| // huge allocations. This also reduces the chance of accidentally matching on a |
| // .debug_str that happens to start with "ZLIB". |
| if data |
| .read_bytes(&mut offset, 8) |
| .read_error("ELF GNU compressed section is too short")? |
| != b"ZLIB\0\0\0\0" |
| { |
| return Err(Error("Invalid ELF GNU compressed section header")); |
| } |
| let uncompressed_size = data |
| .read::<U32Bytes<_>>(&mut offset) |
| .read_error("ELF GNU compressed section is too short")? |
| .get(endian::BigEndian) |
| .into(); |
| let compressed_size = section_size |
| .checked_sub(offset - section_offset) |
| .read_error("ELF GNU compressed section is too short")?; |
| Ok(Some(CompressedFileRange { |
| format: CompressionFormat::Zlib, |
| offset, |
| compressed_size, |
| uncompressed_size, |
| })) |
| } |
| } |
| |
| impl<'data, 'file, Elf, R> read::private::Sealed for ElfSection<'data, 'file, Elf, R> |
| where |
| Elf: FileHeader, |
| R: ReadRef<'data>, |
| { |
| } |
| |
| impl<'data, 'file, Elf, R> ObjectSection<'data> for ElfSection<'data, 'file, Elf, R> |
| where |
| Elf: FileHeader, |
| R: ReadRef<'data>, |
| { |
| type RelocationIterator = ElfSectionRelocationIterator<'data, 'file, Elf, R>; |
| |
| #[inline] |
| fn index(&self) -> SectionIndex { |
| self.index |
| } |
| |
| #[inline] |
| fn address(&self) -> u64 { |
| self.section.sh_addr(self.file.endian).into() |
| } |
| |
| #[inline] |
| fn size(&self) -> u64 { |
| self.section.sh_size(self.file.endian).into() |
| } |
| |
| #[inline] |
| fn align(&self) -> u64 { |
| self.section.sh_addralign(self.file.endian).into() |
| } |
| |
| #[inline] |
| fn file_range(&self) -> Option<(u64, u64)> { |
| self.section.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<Option<&'data [u8]>> { |
| Ok(read::util::data_range( |
| self.bytes()?, |
| self.address(), |
| address, |
| size, |
| )) |
| } |
| |
| fn compressed_file_range(&self) -> read::Result<CompressedFileRange> { |
| Ok(if let Some(data) = self.maybe_compressed()? { |
| data |
| } else if let Some(data) = self.maybe_compressed_gnu()? { |
| data |
| } else { |
| CompressedFileRange::none(self.file_range()) |
| }) |
| } |
| |
| fn compressed_data(&self) -> read::Result<CompressedData<'data>> { |
| self.compressed_file_range()?.data(self.file.data) |
| } |
| |
| fn name_bytes(&self) -> read::Result<&[u8]> { |
| self.file |
| .sections |
| .section_name(self.file.endian, self.section) |
| } |
| |
| fn name(&self) -> read::Result<&str> { |
| let name = self.name_bytes()?; |
| str::from_utf8(name) |
| .ok() |
| .read_error("Non UTF-8 ELF section name") |
| } |
| |
| #[inline] |
| fn segment_name_bytes(&self) -> read::Result<Option<&[u8]>> { |
| Ok(None) |
| } |
| |
| #[inline] |
| fn segment_name(&self) -> read::Result<Option<&str>> { |
| Ok(None) |
| } |
| |
| fn kind(&self) -> SectionKind { |
| let flags = self.section.sh_flags(self.file.endian).into(); |
| let sh_type = self.section.sh_type(self.file.endian); |
| match sh_type { |
| elf::SHT_PROGBITS => { |
| if flags & u64::from(elf::SHF_ALLOC) != 0 { |
| if flags & u64::from(elf::SHF_EXECINSTR) != 0 { |
| SectionKind::Text |
| } else if flags & u64::from(elf::SHF_TLS) != 0 { |
| SectionKind::Tls |
| } else if flags & u64::from(elf::SHF_WRITE) != 0 { |
| SectionKind::Data |
| } else if flags & u64::from(elf::SHF_STRINGS) != 0 { |
| SectionKind::ReadOnlyString |
| } else { |
| SectionKind::ReadOnlyData |
| } |
| } else if flags & u64::from(elf::SHF_STRINGS) != 0 { |
| SectionKind::OtherString |
| } else { |
| SectionKind::Other |
| } |
| } |
| elf::SHT_NOBITS => { |
| if flags & u64::from(elf::SHF_TLS) != 0 { |
| SectionKind::UninitializedTls |
| } else { |
| SectionKind::UninitializedData |
| } |
| } |
| elf::SHT_NOTE => SectionKind::Note, |
| elf::SHT_NULL |
| | elf::SHT_SYMTAB |
| | elf::SHT_STRTAB |
| | elf::SHT_RELA |
| | elf::SHT_HASH |
| | elf::SHT_DYNAMIC |
| | elf::SHT_REL |
| | elf::SHT_DYNSYM |
| | elf::SHT_GROUP => SectionKind::Metadata, |
| _ => SectionKind::Elf(sh_type), |
| } |
| } |
| |
| fn relocations(&self) -> ElfSectionRelocationIterator<'data, 'file, Elf, R> { |
| ElfSectionRelocationIterator { |
| section_index: self.index, |
| file: self.file, |
| relocations: None, |
| } |
| } |
| |
| fn flags(&self) -> SectionFlags { |
| SectionFlags::Elf { |
| sh_flags: self.section.sh_flags(self.file.endian).into(), |
| } |
| } |
| } |
| |
| /// A trait for generic access to `SectionHeader32` and `SectionHeader64`. |
| #[allow(missing_docs)] |
| pub trait SectionHeader: Debug + Pod { |
| type Elf: FileHeader<SectionHeader = Self, Endian = Self::Endian, Word = Self::Word>; |
| type Word: Into<u64>; |
| type Endian: endian::Endian; |
| |
| fn sh_name(&self, endian: Self::Endian) -> u32; |
| fn sh_type(&self, endian: Self::Endian) -> u32; |
| fn sh_flags(&self, endian: Self::Endian) -> Self::Word; |
| fn sh_addr(&self, endian: Self::Endian) -> Self::Word; |
| fn sh_offset(&self, endian: Self::Endian) -> Self::Word; |
| fn sh_size(&self, endian: Self::Endian) -> Self::Word; |
| fn sh_link(&self, endian: Self::Endian) -> u32; |
| fn sh_info(&self, endian: Self::Endian) -> u32; |
| fn sh_addralign(&self, endian: Self::Endian) -> Self::Word; |
| fn sh_entsize(&self, endian: Self::Endian) -> Self::Word; |
| |
| /// Parse the section name from the string table. |
| fn name<'data, R: ReadRef<'data>>( |
| &self, |
| endian: Self::Endian, |
| strings: StringTable<'data, R>, |
| ) -> read::Result<&'data [u8]> { |
| strings |
| .get(self.sh_name(endian)) |
| .read_error("Invalid ELF section name offset") |
| } |
| |
| /// Return the offset and size of the section in the file. |
| /// |
| /// Returns `None` for sections that have no data in the file. |
| fn file_range(&self, endian: Self::Endian) -> Option<(u64, u64)> { |
| if self.sh_type(endian) == elf::SHT_NOBITS { |
| None |
| } else { |
| Some((self.sh_offset(endian).into(), self.sh_size(endian).into())) |
| } |
| } |
| |
| /// Return the section data. |
| /// |
| /// Returns `Ok(&[])` if the section has no data. |
| /// Returns `Err` for invalid values. |
| fn data<'data, R: ReadRef<'data>>( |
| &self, |
| endian: Self::Endian, |
| data: R, |
| ) -> read::Result<&'data [u8]> { |
| if let Some((offset, size)) = self.file_range(endian) { |
| data.read_bytes_at(offset, size) |
| .read_error("Invalid ELF section size or offset") |
| } else { |
| Ok(&[]) |
| } |
| } |
| |
| /// Return the section data as a slice of the given type. |
| /// |
| /// Allows padding at the end of the data. |
| /// Returns `Ok(&[])` if the section 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, |
| ) -> read::Result<&'data [T]> { |
| let mut data = self.data(endian, data).map(Bytes)?; |
| data.read_slice(data.len() / mem::size_of::<T>()) |
| .read_error("Invalid ELF section size or offset") |
| } |
| |
| /// Return the strings in the section. |
| /// |
| /// Returns `Ok(None)` if the section does not contain strings. |
| /// Returns `Err` for invalid values. |
| fn strings<'data, R: ReadRef<'data>>( |
| &self, |
| endian: Self::Endian, |
| data: R, |
| ) -> read::Result<Option<StringTable<'data, R>>> { |
| if self.sh_type(endian) != elf::SHT_STRTAB { |
| return Ok(None); |
| } |
| let str_offset = self.sh_offset(endian).into(); |
| let str_size = self.sh_size(endian).into(); |
| let str_end = str_offset |
| .checked_add(str_size) |
| .read_error("Invalid ELF string section offset or size")?; |
| Ok(Some(StringTable::new(data, str_offset, str_end))) |
| } |
| |
| /// Return the symbols in the section. |
| /// |
| /// Also finds the linked string table in `sections`. |
| /// |
| /// `section_index` must be the 0-based index of this section, and is used |
| /// to find the corresponding extended section index table in `sections`. |
| /// |
| /// Returns `Ok(None)` if the section does not contain symbols. |
| /// Returns `Err` for invalid values. |
| fn symbols<'data, R: ReadRef<'data>>( |
| &self, |
| endian: Self::Endian, |
| data: R, |
| sections: &SectionTable<'data, Self::Elf, R>, |
| section_index: SectionIndex, |
| ) -> read::Result<Option<SymbolTable<'data, Self::Elf, R>>> { |
| let sh_type = self.sh_type(endian); |
| if sh_type != elf::SHT_SYMTAB && sh_type != elf::SHT_DYNSYM { |
| return Ok(None); |
| } |
| SymbolTable::parse(endian, data, sections, section_index, self).map(Some) |
| } |
| |
| /// Return the `Elf::Rel` entries in the section. |
| /// |
| /// Also returns the linked symbol table index. |
| /// |
| /// Returns `Ok(None)` if the section does not contain relocations. |
| /// Returns `Err` for invalid values. |
| fn rel<'data, R: ReadRef<'data>>( |
| &self, |
| endian: Self::Endian, |
| data: R, |
| ) -> read::Result<Option<(&'data [<Self::Elf as FileHeader>::Rel], SectionIndex)>> { |
| if self.sh_type(endian) != elf::SHT_REL { |
| return Ok(None); |
| } |
| let rel = self |
| .data_as_array(endian, data) |
| .read_error("Invalid ELF relocation section offset or size")?; |
| let link = SectionIndex(self.sh_link(endian) as usize); |
| Ok(Some((rel, link))) |
| } |
| |
| /// Return the `Elf::Rela` entries in the section. |
| /// |
| /// Also returns the linked symbol table index. |
| /// |
| /// Returns `Ok(None)` if the section does not contain relocations. |
| /// Returns `Err` for invalid values. |
| fn rela<'data, R: ReadRef<'data>>( |
| &self, |
| endian: Self::Endian, |
| data: R, |
| ) -> read::Result<Option<(&'data [<Self::Elf as FileHeader>::Rela], SectionIndex)>> { |
| if self.sh_type(endian) != elf::SHT_RELA { |
| return Ok(None); |
| } |
| let rela = self |
| .data_as_array(endian, data) |
| .read_error("Invalid ELF relocation section offset or size")?; |
| let link = SectionIndex(self.sh_link(endian) as usize); |
| Ok(Some((rela, link))) |
| } |
| |
| /// Return entries in a dynamic section. |
| /// |
| /// Also returns the linked string table index. |
| /// |
| /// Returns `Ok(None)` if the section type is not `SHT_DYNAMIC`. |
| /// Returns `Err` for invalid values. |
| fn dynamic<'data, R: ReadRef<'data>>( |
| &self, |
| endian: Self::Endian, |
| data: R, |
| ) -> read::Result<Option<(&'data [<Self::Elf as FileHeader>::Dyn], SectionIndex)>> { |
| if self.sh_type(endian) != elf::SHT_DYNAMIC { |
| return Ok(None); |
| } |
| let dynamic = self |
| .data_as_array(endian, data) |
| .read_error("Invalid ELF dynamic section offset or size")?; |
| let link = SectionIndex(self.sh_link(endian) as usize); |
| Ok(Some((dynamic, link))) |
| } |
| |
| /// Return a note iterator for the section data. |
| /// |
| /// Returns `Ok(None)` if the section does not contain notes. |
| /// Returns `Err` for invalid values. |
| fn notes<'data, R: ReadRef<'data>>( |
| &self, |
| endian: Self::Endian, |
| data: R, |
| ) -> read::Result<Option<NoteIterator<'data, Self::Elf>>> { |
| if self.sh_type(endian) != elf::SHT_NOTE { |
| return Ok(None); |
| } |
| let data = self |
| .data(endian, data) |
| .read_error("Invalid ELF note section offset or size")?; |
| let notes = NoteIterator::new(endian, self.sh_addralign(endian), data)?; |
| Ok(Some(notes)) |
| } |
| |
| /// Return the contents of a group section. |
| /// |
| /// The first value is a `GRP_*` value, and the remaining values |
| /// are section indices. |
| /// |
| /// Returns `Ok(None)` if the section does not define a group. |
| /// Returns `Err` for invalid values. |
| fn group<'data, R: ReadRef<'data>>( |
| &self, |
| endian: Self::Endian, |
| data: R, |
| ) -> read::Result<Option<(u32, &'data [U32Bytes<Self::Endian>])>> { |
| if self.sh_type(endian) != elf::SHT_GROUP { |
| return Ok(None); |
| } |
| let mut data = self |
| .data(endian, data) |
| .read_error("Invalid ELF group section offset or size") |
| .map(Bytes)?; |
| let flag = data |
| .read::<U32Bytes<_>>() |
| .read_error("Invalid ELF group section offset or size")? |
| .get(endian); |
| let count = data.len() / mem::size_of::<U32Bytes<Self::Endian>>(); |
| let sections = data |
| .read_slice(count) |
| .read_error("Invalid ELF group section offset or size")?; |
| Ok(Some((flag, sections))) |
| } |
| |
| /// Return the header of a SysV hash section. |
| /// |
| /// Returns `Ok(None)` if the section does not contain a SysV hash. |
| /// Returns `Err` for invalid values. |
| fn hash_header<'data, R: ReadRef<'data>>( |
| &self, |
| endian: Self::Endian, |
| data: R, |
| ) -> read::Result<Option<&'data elf::HashHeader<Self::Endian>>> { |
| if self.sh_type(endian) != elf::SHT_HASH { |
| return Ok(None); |
| } |
| let data = self |
| .data(endian, data) |
| .read_error("Invalid ELF hash section offset or size")?; |
| let header = data |
| .read_at::<elf::HashHeader<Self::Endian>>(0) |
| .read_error("Invalid hash header")?; |
| Ok(Some(header)) |
| } |
| |
| /// Return the contents of a SysV hash section. |
| /// |
| /// Also returns the linked symbol table index. |
| /// |
| /// Returns `Ok(None)` if the section does not contain a SysV hash. |
| /// Returns `Err` for invalid values. |
| fn hash<'data, R: ReadRef<'data>>( |
| &self, |
| endian: Self::Endian, |
| data: R, |
| ) -> read::Result<Option<(HashTable<'data, Self::Elf>, SectionIndex)>> { |
| if self.sh_type(endian) != elf::SHT_HASH { |
| return Ok(None); |
| } |
| let data = self |
| .data(endian, data) |
| .read_error("Invalid ELF hash section offset or size")?; |
| let hash = HashTable::parse(endian, data)?; |
| let link = SectionIndex(self.sh_link(endian) as usize); |
| Ok(Some((hash, link))) |
| } |
| |
| /// Return the header of a GNU hash section. |
| /// |
| /// Returns `Ok(None)` if the section does not contain a GNU hash. |
| /// Returns `Err` for invalid values. |
| fn gnu_hash_header<'data, R: ReadRef<'data>>( |
| &self, |
| endian: Self::Endian, |
| data: R, |
| ) -> read::Result<Option<&'data elf::GnuHashHeader<Self::Endian>>> { |
| if self.sh_type(endian) != elf::SHT_GNU_HASH { |
| return Ok(None); |
| } |
| let data = self |
| .data(endian, data) |
| .read_error("Invalid ELF GNU hash section offset or size")?; |
| let header = data |
| .read_at::<elf::GnuHashHeader<Self::Endian>>(0) |
| .read_error("Invalid GNU hash header")?; |
| Ok(Some(header)) |
| } |
| |
| /// Return the contents of a GNU hash section. |
| /// |
| /// Also returns the linked symbol table index. |
| /// |
| /// Returns `Ok(None)` if the section does not contain a GNU hash. |
| /// Returns `Err` for invalid values. |
| fn gnu_hash<'data, R: ReadRef<'data>>( |
| &self, |
| endian: Self::Endian, |
| data: R, |
| ) -> read::Result<Option<(GnuHashTable<'data, Self::Elf>, SectionIndex)>> { |
| if self.sh_type(endian) != elf::SHT_GNU_HASH { |
| return Ok(None); |
| } |
| let data = self |
| .data(endian, data) |
| .read_error("Invalid ELF GNU hash section offset or size")?; |
| let hash = GnuHashTable::parse(endian, data)?; |
| let link = SectionIndex(self.sh_link(endian) as usize); |
| Ok(Some((hash, link))) |
| } |
| |
| /// Return the contents of a `SHT_GNU_VERSYM` section. |
| /// |
| /// Also returns the linked symbol table index. |
| /// |
| /// Returns `Ok(None)` if the section type is not `SHT_GNU_VERSYM`. |
| /// Returns `Err` for invalid values. |
| fn gnu_versym<'data, R: ReadRef<'data>>( |
| &self, |
| endian: Self::Endian, |
| data: R, |
| ) -> read::Result<Option<(&'data [elf::Versym<Self::Endian>], SectionIndex)>> { |
| if self.sh_type(endian) != elf::SHT_GNU_VERSYM { |
| return Ok(None); |
| } |
| let versym = self |
| .data_as_array(endian, data) |
| .read_error("Invalid ELF GNU versym section offset or size")?; |
| let link = SectionIndex(self.sh_link(endian) as usize); |
| Ok(Some((versym, link))) |
| } |
| |
| /// Return an iterator for the entries of a `SHT_GNU_VERDEF` section. |
| /// |
| /// Also returns the linked string table index. |
| /// |
| /// Returns `Ok(None)` if the section type is not `SHT_GNU_VERDEF`. |
| /// Returns `Err` for invalid values. |
| fn gnu_verdef<'data, R: ReadRef<'data>>( |
| &self, |
| endian: Self::Endian, |
| data: R, |
| ) -> read::Result<Option<(VerdefIterator<'data, Self::Elf>, SectionIndex)>> { |
| if self.sh_type(endian) != elf::SHT_GNU_VERDEF { |
| return Ok(None); |
| } |
| let verdef = self |
| .data(endian, data) |
| .read_error("Invalid ELF GNU verdef section offset or size")?; |
| let link = SectionIndex(self.sh_link(endian) as usize); |
| Ok(Some((VerdefIterator::new(endian, verdef), link))) |
| } |
| |
| /// Return an iterator for the entries of a `SHT_GNU_VERNEED` section. |
| /// |
| /// Also returns the linked string table index. |
| /// |
| /// Returns `Ok(None)` if the section type is not `SHT_GNU_VERNEED`. |
| /// Returns `Err` for invalid values. |
| fn gnu_verneed<'data, R: ReadRef<'data>>( |
| &self, |
| endian: Self::Endian, |
| data: R, |
| ) -> read::Result<Option<(VerneedIterator<'data, Self::Elf>, SectionIndex)>> { |
| if self.sh_type(endian) != elf::SHT_GNU_VERNEED { |
| return Ok(None); |
| } |
| let verneed = self |
| .data(endian, data) |
| .read_error("Invalid ELF GNU verneed section offset or size")?; |
| let link = SectionIndex(self.sh_link(endian) as usize); |
| Ok(Some((VerneedIterator::new(endian, verneed), link))) |
| } |
| |
| /// Return the contents of a `SHT_GNU_ATTRIBUTES` section. |
| /// |
| /// Returns `Ok(None)` if the section type is not `SHT_GNU_ATTRIBUTES`. |
| /// Returns `Err` for invalid values. |
| fn gnu_attributes<'data, R: ReadRef<'data>>( |
| &self, |
| endian: Self::Endian, |
| data: R, |
| ) -> read::Result<Option<AttributesSection<'data, Self::Elf>>> { |
| if self.sh_type(endian) != elf::SHT_GNU_ATTRIBUTES { |
| return Ok(None); |
| } |
| self.attributes(endian, data).map(Some) |
| } |
| |
| /// Parse the contents of the section as attributes. |
| /// |
| /// This function does not check whether section type corresponds |
| /// to a section that contains attributes. |
| /// |
| /// Returns `Err` for invalid values. |
| fn attributes<'data, R: ReadRef<'data>>( |
| &self, |
| endian: Self::Endian, |
| data: R, |
| ) -> read::Result<AttributesSection<'data, Self::Elf>> { |
| let data = self.data(endian, data)?; |
| AttributesSection::new(endian, data) |
| } |
| |
| /// Parse the compression header if present. |
| /// |
| /// Returns the header, and the offset and size of the compressed section data |
| /// in the file. |
| /// |
| /// Returns `Ok(None)` if the section flags do not have `SHF_COMPRESSED`. |
| /// Returns `Err` for invalid values. |
| fn compression<'data, R: ReadRef<'data>>( |
| &self, |
| endian: Self::Endian, |
| data: R, |
| ) -> read::Result< |
| Option<( |
| &'data <Self::Elf as FileHeader>::CompressionHeader, |
| u64, |
| u64, |
| )>, |
| > { |
| if (self.sh_flags(endian).into() & u64::from(elf::SHF_COMPRESSED)) == 0 { |
| return Ok(None); |
| } |
| let (section_offset, section_size) = self |
| .file_range(endian) |
| .read_error("Invalid ELF compressed section type")?; |
| let mut offset = section_offset; |
| let header = data |
| .read::<<Self::Elf as FileHeader>::CompressionHeader>(&mut offset) |
| .read_error("Invalid ELF compressed section offset")?; |
| let compressed_size = section_size |
| .checked_sub(offset - section_offset) |
| .read_error("Invalid ELF compressed section size")?; |
| Ok(Some((header, offset, compressed_size))) |
| } |
| } |
| |
| impl<Endian: endian::Endian> SectionHeader for elf::SectionHeader32<Endian> { |
| type Elf = elf::FileHeader32<Endian>; |
| type Word = u32; |
| type Endian = Endian; |
| |
| #[inline] |
| fn sh_name(&self, endian: Self::Endian) -> u32 { |
| self.sh_name.get(endian) |
| } |
| |
| #[inline] |
| fn sh_type(&self, endian: Self::Endian) -> u32 { |
| self.sh_type.get(endian) |
| } |
| |
| #[inline] |
| fn sh_flags(&self, endian: Self::Endian) -> Self::Word { |
| self.sh_flags.get(endian) |
| } |
| |
| #[inline] |
| fn sh_addr(&self, endian: Self::Endian) -> Self::Word { |
| self.sh_addr.get(endian) |
| } |
| |
| #[inline] |
| fn sh_offset(&self, endian: Self::Endian) -> Self::Word { |
| self.sh_offset.get(endian) |
| } |
| |
| #[inline] |
| fn sh_size(&self, endian: Self::Endian) -> Self::Word { |
| self.sh_size.get(endian) |
| } |
| |
| #[inline] |
| fn sh_link(&self, endian: Self::Endian) -> u32 { |
| self.sh_link.get(endian) |
| } |
| |
| #[inline] |
| fn sh_info(&self, endian: Self::Endian) -> u32 { |
| self.sh_info.get(endian) |
| } |
| |
| #[inline] |
| fn sh_addralign(&self, endian: Self::Endian) -> Self::Word { |
| self.sh_addralign.get(endian) |
| } |
| |
| #[inline] |
| fn sh_entsize(&self, endian: Self::Endian) -> Self::Word { |
| self.sh_entsize.get(endian) |
| } |
| } |
| |
| impl<Endian: endian::Endian> SectionHeader for elf::SectionHeader64<Endian> { |
| type Word = u64; |
| type Endian = Endian; |
| type Elf = elf::FileHeader64<Endian>; |
| |
| #[inline] |
| fn sh_name(&self, endian: Self::Endian) -> u32 { |
| self.sh_name.get(endian) |
| } |
| |
| #[inline] |
| fn sh_type(&self, endian: Self::Endian) -> u32 { |
| self.sh_type.get(endian) |
| } |
| |
| #[inline] |
| fn sh_flags(&self, endian: Self::Endian) -> Self::Word { |
| self.sh_flags.get(endian) |
| } |
| |
| #[inline] |
| fn sh_addr(&self, endian: Self::Endian) -> Self::Word { |
| self.sh_addr.get(endian) |
| } |
| |
| #[inline] |
| fn sh_offset(&self, endian: Self::Endian) -> Self::Word { |
| self.sh_offset.get(endian) |
| } |
| |
| #[inline] |
| fn sh_size(&self, endian: Self::Endian) -> Self::Word { |
| self.sh_size.get(endian) |
| } |
| |
| #[inline] |
| fn sh_link(&self, endian: Self::Endian) -> u32 { |
| self.sh_link.get(endian) |
| } |
| |
| #[inline] |
| fn sh_info(&self, endian: Self::Endian) -> u32 { |
| self.sh_info.get(endian) |
| } |
| |
| #[inline] |
| fn sh_addralign(&self, endian: Self::Endian) -> Self::Word { |
| self.sh_addralign.get(endian) |
| } |
| |
| #[inline] |
| fn sh_entsize(&self, endian: Self::Endian) -> Self::Word { |
| self.sh_entsize.get(endian) |
| } |
| } |