diff options
Diffstat (limited to 'third_party/rust/object/src/read/elf')
-rw-r--r-- | third_party/rust/object/src/read/elf/comdat.rs | 162 | ||||
-rw-r--r-- | third_party/rust/object/src/read/elf/compression.rs | 56 | ||||
-rw-r--r-- | third_party/rust/object/src/read/elf/dynamic.rs | 117 | ||||
-rw-r--r-- | third_party/rust/object/src/read/elf/file.rs | 885 | ||||
-rw-r--r-- | third_party/rust/object/src/read/elf/hash.rs | 220 | ||||
-rw-r--r-- | third_party/rust/object/src/read/elf/mod.rs | 39 | ||||
-rw-r--r-- | third_party/rust/object/src/read/elf/note.rs | 185 | ||||
-rw-r--r-- | third_party/rust/object/src/read/elf/relocation.rs | 571 | ||||
-rw-r--r-- | third_party/rust/object/src/read/elf/section.rs | 1090 | ||||
-rw-r--r-- | third_party/rust/object/src/read/elf/segment.rs | 333 | ||||
-rw-r--r-- | third_party/rust/object/src/read/elf/symbol.rs | 580 | ||||
-rw-r--r-- | third_party/rust/object/src/read/elf/version.rs | 421 |
12 files changed, 4659 insertions, 0 deletions
diff --git a/third_party/rust/object/src/read/elf/comdat.rs b/third_party/rust/object/src/read/elf/comdat.rs new file mode 100644 index 0000000000..7cee85bb44 --- /dev/null +++ b/third_party/rust/object/src/read/elf/comdat.rs @@ -0,0 +1,162 @@ +use core::fmt::Debug; +use core::{iter, slice, str}; + +use crate::elf; +use crate::endian::{Endianness, U32Bytes}; +use crate::read::{self, ComdatKind, ObjectComdat, ReadError, ReadRef, SectionIndex, SymbolIndex}; + +use super::{ElfFile, FileHeader, SectionHeader, Sym}; + +/// An iterator over the COMDAT section groups of an `ElfFile32`. +pub type ElfComdatIterator32<'data, 'file, Endian = Endianness, R = &'data [u8]> = + ElfComdatIterator<'data, 'file, elf::FileHeader32<Endian>, R>; +/// An iterator over the COMDAT section groups of an `ElfFile64`. +pub type ElfComdatIterator64<'data, 'file, Endian = Endianness, R = &'data [u8]> = + ElfComdatIterator<'data, 'file, elf::FileHeader64<Endian>, R>; + +/// An iterator over the COMDAT section groups of an `ElfFile`. +#[derive(Debug)] +pub struct ElfComdatIterator<'data, 'file, Elf, R = &'data [u8]> +where + 'data: 'file, + 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 ElfComdatIterator<'data, 'file, Elf, R> +where + Elf: FileHeader, + R: ReadRef<'data>, +{ + type Item = ElfComdat<'data, 'file, Elf, R>; + + fn next(&mut self) -> Option<Self::Item> { + for (_index, section) in self.iter.by_ref() { + if let Some(comdat) = ElfComdat::parse(self.file, section) { + return Some(comdat); + } + } + None + } +} + +/// A COMDAT section group of an `ElfFile32`. +pub type ElfComdat32<'data, 'file, Endian = Endianness, R = &'data [u8]> = + ElfComdat<'data, 'file, elf::FileHeader32<Endian>, R>; +/// A COMDAT section group of an `ElfFile64`. +pub type ElfComdat64<'data, 'file, Endian = Endianness, R = &'data [u8]> = + ElfComdat<'data, 'file, elf::FileHeader64<Endian>, R>; + +/// A COMDAT section group of an `ElfFile`. +#[derive(Debug)] +pub struct ElfComdat<'data, 'file, Elf, R = &'data [u8]> +where + Elf: FileHeader, + R: ReadRef<'data>, +{ + file: &'file ElfFile<'data, Elf, R>, + section: &'data Elf::SectionHeader, + sections: &'data [U32Bytes<Elf::Endian>], +} + +impl<'data, 'file, Elf, R> ElfComdat<'data, 'file, Elf, R> +where + Elf: FileHeader, + R: ReadRef<'data>, +{ + fn parse( + file: &'file ElfFile<'data, Elf, R>, + section: &'data Elf::SectionHeader, + ) -> Option<ElfComdat<'data, 'file, Elf, R>> { + let (flag, sections) = section.group(file.endian, file.data).ok()??; + if flag != elf::GRP_COMDAT { + return None; + } + Some(ElfComdat { + file, + section, + sections, + }) + } +} + +impl<'data, 'file, Elf, R> read::private::Sealed for ElfComdat<'data, 'file, Elf, R> +where + Elf: FileHeader, + R: ReadRef<'data>, +{ +} + +impl<'data, 'file, Elf, R> ObjectComdat<'data> for ElfComdat<'data, 'file, Elf, R> +where + Elf: FileHeader, + R: ReadRef<'data>, +{ + type SectionIterator = ElfComdatSectionIterator<'data, 'file, Elf, R>; + + #[inline] + fn kind(&self) -> ComdatKind { + ComdatKind::Any + } + + #[inline] + fn symbol(&self) -> SymbolIndex { + SymbolIndex(self.section.sh_info(self.file.endian) as usize) + } + + fn name_bytes(&self) -> read::Result<&[u8]> { + // FIXME: check sh_link + let index = self.section.sh_info(self.file.endian) as usize; + let symbol = self.file.symbols.symbol(index)?; + symbol.name(self.file.endian, self.file.symbols.strings()) + } + + fn name(&self) -> read::Result<&str> { + let name = self.name_bytes()?; + str::from_utf8(name) + .ok() + .read_error("Non UTF-8 ELF COMDAT name") + } + + fn sections(&self) -> Self::SectionIterator { + ElfComdatSectionIterator { + file: self.file, + sections: self.sections.iter(), + } + } +} + +/// An iterator over the sections in a COMDAT section group of an `ElfFile32`. +pub type ElfComdatSectionIterator32<'data, 'file, Endian = Endianness, R = &'data [u8]> = + ElfComdatSectionIterator<'data, 'file, elf::FileHeader32<Endian>, R>; +/// An iterator over the sections in a COMDAT section group of an `ElfFile64`. +pub type ElfComdatSectionIterator64<'data, 'file, Endian = Endianness, R = &'data [u8]> = + ElfComdatSectionIterator<'data, 'file, elf::FileHeader64<Endian>, R>; + +/// An iterator over the sections in a COMDAT section group of an `ElfFile`. +#[derive(Debug)] +pub struct ElfComdatSectionIterator<'data, 'file, Elf, R = &'data [u8]> +where + 'data: 'file, + Elf: FileHeader, + R: ReadRef<'data>, +{ + file: &'file ElfFile<'data, Elf, R>, + sections: slice::Iter<'data, U32Bytes<Elf::Endian>>, +} + +impl<'data, 'file, Elf, R> Iterator for ElfComdatSectionIterator<'data, 'file, Elf, R> +where + Elf: FileHeader, + R: ReadRef<'data>, +{ + type Item = SectionIndex; + + fn next(&mut self) -> Option<Self::Item> { + let index = self.sections.next()?; + Some(SectionIndex(index.get(self.file.endian) as usize)) + } +} diff --git a/third_party/rust/object/src/read/elf/compression.rs b/third_party/rust/object/src/read/elf/compression.rs new file mode 100644 index 0000000000..7242dd39c8 --- /dev/null +++ b/third_party/rust/object/src/read/elf/compression.rs @@ -0,0 +1,56 @@ +use core::fmt::Debug; + +use crate::elf; +use crate::endian; +use crate::pod::Pod; + +/// A trait for generic access to `CompressionHeader32` and `CompressionHeader64`. +#[allow(missing_docs)] +pub trait CompressionHeader: Debug + Pod { + type Word: Into<u64>; + type Endian: endian::Endian; + + fn ch_type(&self, endian: Self::Endian) -> u32; + fn ch_size(&self, endian: Self::Endian) -> Self::Word; + fn ch_addralign(&self, endian: Self::Endian) -> Self::Word; +} + +impl<Endian: endian::Endian> CompressionHeader for elf::CompressionHeader32<Endian> { + type Word = u32; + type Endian = Endian; + + #[inline] + fn ch_type(&self, endian: Self::Endian) -> u32 { + self.ch_type.get(endian) + } + + #[inline] + fn ch_size(&self, endian: Self::Endian) -> Self::Word { + self.ch_size.get(endian) + } + + #[inline] + fn ch_addralign(&self, endian: Self::Endian) -> Self::Word { + self.ch_addralign.get(endian) + } +} + +impl<Endian: endian::Endian> CompressionHeader for elf::CompressionHeader64<Endian> { + type Word = u64; + type Endian = Endian; + + #[inline] + fn ch_type(&self, endian: Self::Endian) -> u32 { + self.ch_type.get(endian) + } + + #[inline] + fn ch_size(&self, endian: Self::Endian) -> Self::Word { + self.ch_size.get(endian) + } + + #[inline] + fn ch_addralign(&self, endian: Self::Endian) -> Self::Word { + self.ch_addralign.get(endian) + } +} diff --git a/third_party/rust/object/src/read/elf/dynamic.rs b/third_party/rust/object/src/read/elf/dynamic.rs new file mode 100644 index 0000000000..5fe15b560d --- /dev/null +++ b/third_party/rust/object/src/read/elf/dynamic.rs @@ -0,0 +1,117 @@ +use core::convert::TryInto; +use core::fmt::Debug; + +use crate::elf; +use crate::endian; +use crate::pod::Pod; +use crate::read::{ReadError, Result, StringTable}; + +/// A trait for generic access to `Dyn32` and `Dyn64`. +#[allow(missing_docs)] +pub trait Dyn: Debug + Pod { + type Word: Into<u64>; + type Endian: endian::Endian; + + fn d_tag(&self, endian: Self::Endian) -> Self::Word; + fn d_val(&self, endian: Self::Endian) -> Self::Word; + + /// Try to convert the tag to a `u32`. + fn tag32(&self, endian: Self::Endian) -> Option<u32> { + self.d_tag(endian).into().try_into().ok() + } + + /// Try to convert the value to a `u32`. + fn val32(&self, endian: Self::Endian) -> Option<u32> { + self.d_val(endian).into().try_into().ok() + } + + /// Return true if the value is an offset in the dynamic string table. + fn is_string(&self, endian: Self::Endian) -> bool { + if let Some(tag) = self.tag32(endian) { + match tag { + elf::DT_NEEDED + | elf::DT_SONAME + | elf::DT_RPATH + | elf::DT_RUNPATH + | elf::DT_AUXILIARY + | elf::DT_FILTER => true, + _ => false, + } + } else { + false + } + } + + /// Use the value to get a string in a string table. + /// + /// Does not check for an appropriate tag. + fn string<'data>( + &self, + endian: Self::Endian, + strings: StringTable<'data>, + ) -> Result<&'data [u8]> { + self.val32(endian) + .and_then(|val| strings.get(val).ok()) + .read_error("Invalid ELF dyn string") + } + + /// Return true if the value is an address. + fn is_address(&self, endian: Self::Endian) -> bool { + if let Some(tag) = self.tag32(endian) { + match tag { + elf::DT_PLTGOT + | elf::DT_HASH + | elf::DT_STRTAB + | elf::DT_SYMTAB + | elf::DT_RELA + | elf::DT_INIT + | elf::DT_FINI + | elf::DT_SYMBOLIC + | elf::DT_REL + | elf::DT_DEBUG + | elf::DT_JMPREL + | elf::DT_FINI_ARRAY + | elf::DT_INIT_ARRAY + | elf::DT_PREINIT_ARRAY + | elf::DT_SYMTAB_SHNDX + | elf::DT_VERDEF + | elf::DT_VERNEED + | elf::DT_VERSYM + | elf::DT_ADDRRNGLO..=elf::DT_ADDRRNGHI => true, + _ => false, + } + } else { + false + } + } +} + +impl<Endian: endian::Endian> Dyn for elf::Dyn32<Endian> { + type Word = u32; + type Endian = Endian; + + #[inline] + fn d_tag(&self, endian: Self::Endian) -> Self::Word { + self.d_tag.get(endian) + } + + #[inline] + fn d_val(&self, endian: Self::Endian) -> Self::Word { + self.d_val.get(endian) + } +} + +impl<Endian: endian::Endian> Dyn for elf::Dyn64<Endian> { + type Word = u64; + type Endian = Endian; + + #[inline] + fn d_tag(&self, endian: Self::Endian) -> Self::Word { + self.d_tag.get(endian) + } + + #[inline] + fn d_val(&self, endian: Self::Endian) -> Self::Word { + self.d_val.get(endian) + } +} diff --git a/third_party/rust/object/src/read/elf/file.rs b/third_party/rust/object/src/read/elf/file.rs new file mode 100644 index 0000000000..259da7906d --- /dev/null +++ b/third_party/rust/object/src/read/elf/file.rs @@ -0,0 +1,885 @@ +use alloc::vec::Vec; +use core::convert::TryInto; +use core::fmt::Debug; +use core::mem; + +use crate::read::{ + self, util, Architecture, ByteString, Bytes, Error, Export, FileFlags, Import, Object, + ObjectKind, ReadError, ReadRef, SectionIndex, StringTable, SymbolIndex, +}; +use crate::{elf, endian, Endian, Endianness, Pod, U32}; + +use super::{ + CompressionHeader, Dyn, ElfComdat, ElfComdatIterator, ElfDynamicRelocationIterator, ElfSection, + ElfSectionIterator, ElfSegment, ElfSegmentIterator, ElfSymbol, ElfSymbolIterator, + ElfSymbolTable, NoteHeader, ProgramHeader, Rel, Rela, RelocationSections, SectionHeader, + SectionTable, Sym, SymbolTable, +}; + +/// A 32-bit ELF object file. +pub type ElfFile32<'data, Endian = Endianness, R = &'data [u8]> = + ElfFile<'data, elf::FileHeader32<Endian>, R>; +/// A 64-bit ELF object file. +pub type ElfFile64<'data, Endian = Endianness, R = &'data [u8]> = + ElfFile<'data, elf::FileHeader64<Endian>, R>; + +/// A partially parsed ELF file. +/// +/// Most of the functionality of this type is provided by the `Object` trait implementation. +#[derive(Debug)] +pub struct ElfFile<'data, Elf, R = &'data [u8]> +where + Elf: FileHeader, + R: ReadRef<'data>, +{ + pub(super) endian: Elf::Endian, + pub(super) data: R, + pub(super) header: &'data Elf, + pub(super) segments: &'data [Elf::ProgramHeader], + pub(super) sections: SectionTable<'data, Elf, R>, + pub(super) relocations: RelocationSections, + pub(super) symbols: SymbolTable<'data, Elf, R>, + pub(super) dynamic_symbols: SymbolTable<'data, Elf, R>, +} + +impl<'data, Elf, R> ElfFile<'data, Elf, R> +where + Elf: FileHeader, + R: ReadRef<'data>, +{ + /// Parse the raw ELF file data. + pub fn parse(data: R) -> read::Result<Self> { + let header = Elf::parse(data)?; + let endian = header.endian()?; + let segments = header.program_headers(endian, data)?; + let sections = header.sections(endian, data)?; + let symbols = sections.symbols(endian, data, elf::SHT_SYMTAB)?; + // TODO: get dynamic symbols from DT_SYMTAB if there are no sections + let dynamic_symbols = sections.symbols(endian, data, elf::SHT_DYNSYM)?; + // The API we provide requires a mapping from section to relocations, so build it now. + let relocations = sections.relocation_sections(endian, symbols.section())?; + + Ok(ElfFile { + endian, + data, + header, + segments, + sections, + relocations, + symbols, + dynamic_symbols, + }) + } + + /// Returns the endianness. + pub fn endian(&self) -> Elf::Endian { + self.endian + } + + /// Returns the raw data. + pub fn data(&self) -> R { + self.data + } + + /// Returns the raw ELF file header. + pub fn raw_header(&self) -> &'data Elf { + self.header + } + + /// Returns the raw ELF segments. + pub fn raw_segments(&self) -> &'data [Elf::ProgramHeader] { + self.segments + } + + fn raw_section_by_name<'file>( + &'file self, + section_name: &[u8], + ) -> Option<ElfSection<'data, 'file, Elf, R>> { + self.sections + .section_by_name(self.endian, section_name) + .map(|(index, section)| ElfSection { + file: self, + index: SectionIndex(index), + section, + }) + } + + #[cfg(feature = "compression")] + fn zdebug_section_by_name<'file>( + &'file self, + section_name: &[u8], + ) -> Option<ElfSection<'data, 'file, Elf, R>> { + if !section_name.starts_with(b".debug_") { + return None; + } + let mut name = Vec::with_capacity(section_name.len() + 1); + name.extend_from_slice(b".zdebug_"); + name.extend_from_slice(§ion_name[7..]); + self.raw_section_by_name(&name) + } + + #[cfg(not(feature = "compression"))] + fn zdebug_section_by_name<'file>( + &'file self, + _section_name: &[u8], + ) -> Option<ElfSection<'data, 'file, Elf, R>> { + None + } +} + +impl<'data, Elf, R> read::private::Sealed for ElfFile<'data, Elf, R> +where + Elf: FileHeader, + R: ReadRef<'data>, +{ +} + +impl<'data, 'file, Elf, R> Object<'data, 'file> for ElfFile<'data, Elf, R> +where + 'data: 'file, + Elf: FileHeader, + R: 'file + ReadRef<'data>, +{ + type Segment = ElfSegment<'data, 'file, Elf, R>; + type SegmentIterator = ElfSegmentIterator<'data, 'file, Elf, R>; + type Section = ElfSection<'data, 'file, Elf, R>; + type SectionIterator = ElfSectionIterator<'data, 'file, Elf, R>; + type Comdat = ElfComdat<'data, 'file, Elf, R>; + type ComdatIterator = ElfComdatIterator<'data, 'file, Elf, R>; + type Symbol = ElfSymbol<'data, 'file, Elf, R>; + type SymbolIterator = ElfSymbolIterator<'data, 'file, Elf, R>; + type SymbolTable = ElfSymbolTable<'data, 'file, Elf, R>; + type DynamicRelocationIterator = ElfDynamicRelocationIterator<'data, 'file, Elf, R>; + + fn architecture(&self) -> Architecture { + match ( + self.header.e_machine(self.endian), + self.header.is_class_64(), + ) { + (elf::EM_AARCH64, true) => Architecture::Aarch64, + (elf::EM_AARCH64, false) => Architecture::Aarch64_Ilp32, + (elf::EM_ARM, _) => Architecture::Arm, + (elf::EM_AVR, _) => Architecture::Avr, + (elf::EM_BPF, _) => Architecture::Bpf, + (elf::EM_386, _) => Architecture::I386, + (elf::EM_X86_64, false) => Architecture::X86_64_X32, + (elf::EM_X86_64, true) => Architecture::X86_64, + (elf::EM_HEXAGON, _) => Architecture::Hexagon, + (elf::EM_LOONGARCH, true) => Architecture::LoongArch64, + (elf::EM_MIPS, false) => Architecture::Mips, + (elf::EM_MIPS, true) => Architecture::Mips64, + (elf::EM_MSP430, _) => Architecture::Msp430, + (elf::EM_PPC, _) => Architecture::PowerPc, + (elf::EM_PPC64, _) => Architecture::PowerPc64, + (elf::EM_RISCV, false) => Architecture::Riscv32, + (elf::EM_RISCV, true) => Architecture::Riscv64, + // This is either s390 or s390x, depending on the ELF class. + // We only support the 64-bit variant s390x here. + (elf::EM_S390, true) => Architecture::S390x, + (elf::EM_SBF, _) => Architecture::Sbf, + (elf::EM_SPARCV9, true) => Architecture::Sparc64, + (elf::EM_XTENSA, false) => Architecture::Xtensa, + _ => Architecture::Unknown, + } + } + + #[inline] + fn is_little_endian(&self) -> bool { + self.header.is_little_endian() + } + + #[inline] + fn is_64(&self) -> bool { + self.header.is_class_64() + } + + fn kind(&self) -> ObjectKind { + match self.header.e_type(self.endian) { + elf::ET_REL => ObjectKind::Relocatable, + elf::ET_EXEC => ObjectKind::Executable, + // TODO: check for `DF_1_PIE`? + elf::ET_DYN => ObjectKind::Dynamic, + elf::ET_CORE => ObjectKind::Core, + _ => ObjectKind::Unknown, + } + } + + fn segments(&'file self) -> ElfSegmentIterator<'data, 'file, Elf, R> { + ElfSegmentIterator { + file: self, + iter: self.segments.iter(), + } + } + + fn section_by_name_bytes( + &'file self, + section_name: &[u8], + ) -> Option<ElfSection<'data, 'file, Elf, R>> { + self.raw_section_by_name(section_name) + .or_else(|| self.zdebug_section_by_name(section_name)) + } + + fn section_by_index( + &'file self, + index: SectionIndex, + ) -> read::Result<ElfSection<'data, 'file, Elf, R>> { + let section = self.sections.section(index)?; + Ok(ElfSection { + file: self, + index, + section, + }) + } + + fn sections(&'file self) -> ElfSectionIterator<'data, 'file, Elf, R> { + ElfSectionIterator { + file: self, + iter: self.sections.iter().enumerate(), + } + } + + fn comdats(&'file self) -> ElfComdatIterator<'data, 'file, Elf, R> { + ElfComdatIterator { + file: self, + iter: self.sections.iter().enumerate(), + } + } + + fn symbol_by_index( + &'file self, + index: SymbolIndex, + ) -> read::Result<ElfSymbol<'data, 'file, Elf, R>> { + let symbol = self.symbols.symbol(index.0)?; + Ok(ElfSymbol { + endian: self.endian, + symbols: &self.symbols, + index, + symbol, + }) + } + + fn symbols(&'file self) -> ElfSymbolIterator<'data, 'file, Elf, R> { + ElfSymbolIterator { + endian: self.endian, + symbols: &self.symbols, + index: 0, + } + } + + fn symbol_table(&'file self) -> Option<ElfSymbolTable<'data, 'file, Elf, R>> { + if self.symbols.is_empty() { + return None; + } + Some(ElfSymbolTable { + endian: self.endian, + symbols: &self.symbols, + }) + } + + fn dynamic_symbols(&'file self) -> ElfSymbolIterator<'data, 'file, Elf, R> { + ElfSymbolIterator { + endian: self.endian, + symbols: &self.dynamic_symbols, + index: 0, + } + } + + fn dynamic_symbol_table(&'file self) -> Option<ElfSymbolTable<'data, 'file, Elf, R>> { + if self.dynamic_symbols.is_empty() { + return None; + } + Some(ElfSymbolTable { + endian: self.endian, + symbols: &self.dynamic_symbols, + }) + } + + fn dynamic_relocations( + &'file self, + ) -> Option<ElfDynamicRelocationIterator<'data, 'file, Elf, R>> { + Some(ElfDynamicRelocationIterator { + section_index: SectionIndex(1), + file: self, + relocations: None, + }) + } + + /// Get the imported symbols. + fn imports(&self) -> read::Result<Vec<Import<'data>>> { + let mut imports = Vec::new(); + for symbol in self.dynamic_symbols.iter() { + if symbol.is_undefined(self.endian) { + let name = symbol.name(self.endian, self.dynamic_symbols.strings())?; + if !name.is_empty() { + // TODO: use symbol versioning to determine library + imports.push(Import { + name: ByteString(name), + library: ByteString(&[]), + }); + } + } + } + Ok(imports) + } + + /// Get the exported symbols. + fn exports(&self) -> read::Result<Vec<Export<'data>>> { + let mut exports = Vec::new(); + for symbol in self.dynamic_symbols.iter() { + if symbol.is_definition(self.endian) { + let name = symbol.name(self.endian, self.dynamic_symbols.strings())?; + let address = symbol.st_value(self.endian).into(); + exports.push(Export { + name: ByteString(name), + address, + }); + } + } + Ok(exports) + } + + fn has_debug_symbols(&self) -> bool { + for section in self.sections.iter() { + if let Ok(name) = self.sections.section_name(self.endian, section) { + if name == b".debug_info" || name == b".zdebug_info" { + return true; + } + } + } + false + } + + fn build_id(&self) -> read::Result<Option<&'data [u8]>> { + let endian = self.endian; + // Use section headers if present, otherwise use program headers. + if !self.sections.is_empty() { + for section in self.sections.iter() { + if let Some(mut notes) = section.notes(endian, self.data)? { + while let Some(note) = notes.next()? { + if note.name() == elf::ELF_NOTE_GNU + && note.n_type(endian) == elf::NT_GNU_BUILD_ID + { + return Ok(Some(note.desc())); + } + } + } + } + } else { + for segment in self.segments { + if let Some(mut notes) = segment.notes(endian, self.data)? { + while let Some(note) = notes.next()? { + if note.name() == elf::ELF_NOTE_GNU + && note.n_type(endian) == elf::NT_GNU_BUILD_ID + { + return Ok(Some(note.desc())); + } + } + } + } + } + Ok(None) + } + + fn gnu_debuglink(&self) -> read::Result<Option<(&'data [u8], u32)>> { + let section = match self.raw_section_by_name(b".gnu_debuglink") { + Some(section) => section, + None => return Ok(None), + }; + let data = section + .section + .data(self.endian, self.data) + .read_error("Invalid ELF .gnu_debuglink section offset or size") + .map(Bytes)?; + let filename = data + .read_string_at(0) + .read_error("Missing ELF .gnu_debuglink filename")?; + let crc_offset = util::align(filename.len() + 1, 4); + let crc = data + .read_at::<U32<_>>(crc_offset) + .read_error("Missing ELF .gnu_debuglink crc")? + .get(self.endian); + Ok(Some((filename, crc))) + } + + fn gnu_debugaltlink(&self) -> read::Result<Option<(&'data [u8], &'data [u8])>> { + let section = match self.raw_section_by_name(b".gnu_debugaltlink") { + Some(section) => section, + None => return Ok(None), + }; + let mut data = section + .section + .data(self.endian, self.data) + .read_error("Invalid ELF .gnu_debugaltlink section offset or size") + .map(Bytes)?; + let filename = data + .read_string() + .read_error("Missing ELF .gnu_debugaltlink filename")?; + let build_id = data.0; + Ok(Some((filename, build_id))) + } + + fn relative_address_base(&self) -> u64 { + 0 + } + + fn entry(&self) -> u64 { + self.header.e_entry(self.endian).into() + } + + fn flags(&self) -> FileFlags { + FileFlags::Elf { + os_abi: self.header.e_ident().os_abi, + abi_version: self.header.e_ident().abi_version, + e_flags: self.header.e_flags(self.endian), + } + } +} + +/// A trait for generic access to `FileHeader32` and `FileHeader64`. +#[allow(missing_docs)] +pub trait FileHeader: Debug + Pod { + // Ideally this would be a `u64: From<Word>`, but can't express that. + type Word: Into<u64>; + type Sword: Into<i64>; + type Endian: endian::Endian; + type ProgramHeader: ProgramHeader<Elf = Self, Endian = Self::Endian, Word = Self::Word>; + type SectionHeader: SectionHeader<Elf = Self, Endian = Self::Endian, Word = Self::Word>; + type CompressionHeader: CompressionHeader<Endian = Self::Endian, Word = Self::Word>; + type NoteHeader: NoteHeader<Endian = Self::Endian>; + type Dyn: Dyn<Endian = Self::Endian, Word = Self::Word>; + type Sym: Sym<Endian = Self::Endian, Word = Self::Word>; + type Rel: Rel<Endian = Self::Endian, Word = Self::Word>; + type Rela: Rela<Endian = Self::Endian, Word = Self::Word> + From<Self::Rel>; + + /// Return true if this type is a 64-bit header. + /// + /// This is a property of the type, not a value in the header data. + fn is_type_64(&self) -> bool; + + fn e_ident(&self) -> &elf::Ident; + fn e_type(&self, endian: Self::Endian) -> u16; + fn e_machine(&self, endian: Self::Endian) -> u16; + fn e_version(&self, endian: Self::Endian) -> u32; + fn e_entry(&self, endian: Self::Endian) -> Self::Word; + fn e_phoff(&self, endian: Self::Endian) -> Self::Word; + fn e_shoff(&self, endian: Self::Endian) -> Self::Word; + fn e_flags(&self, endian: Self::Endian) -> u32; + fn e_ehsize(&self, endian: Self::Endian) -> u16; + fn e_phentsize(&self, endian: Self::Endian) -> u16; + fn e_phnum(&self, endian: Self::Endian) -> u16; + fn e_shentsize(&self, endian: Self::Endian) -> u16; + fn e_shnum(&self, endian: Self::Endian) -> u16; + fn e_shstrndx(&self, endian: Self::Endian) -> u16; + + // Provided methods. + + /// Read the file header. + /// + /// Also checks that the ident field in the file header is a supported format. + fn parse<'data, R: ReadRef<'data>>(data: R) -> read::Result<&'data Self> { + let header = data + .read_at::<Self>(0) + .read_error("Invalid ELF header size or alignment")?; + if !header.is_supported() { + return Err(Error("Unsupported ELF header")); + } + // TODO: Check self.e_ehsize? + Ok(header) + } + + /// Check that the ident field in the file header is a supported format. + /// + /// This checks the magic number, version, class, and endianness. + fn is_supported(&self) -> bool { + let ident = self.e_ident(); + // TODO: Check self.e_version too? Requires endian though. + ident.magic == elf::ELFMAG + && (self.is_type_64() || self.is_class_32()) + && (!self.is_type_64() || self.is_class_64()) + && (self.is_little_endian() || self.is_big_endian()) + && ident.version == elf::EV_CURRENT + } + + fn is_class_32(&self) -> bool { + self.e_ident().class == elf::ELFCLASS32 + } + + fn is_class_64(&self) -> bool { + self.e_ident().class == elf::ELFCLASS64 + } + + fn is_little_endian(&self) -> bool { + self.e_ident().data == elf::ELFDATA2LSB + } + + fn is_big_endian(&self) -> bool { + self.e_ident().data == elf::ELFDATA2MSB + } + + fn endian(&self) -> read::Result<Self::Endian> { + Self::Endian::from_big_endian(self.is_big_endian()).read_error("Unsupported ELF endian") + } + + /// Return the first section header, if present. + /// + /// Section 0 is a special case because getting the section headers normally + /// requires `shnum`, but `shnum` may be in the first section header. + fn section_0<'data, R: ReadRef<'data>>( + &self, + endian: Self::Endian, + data: R, + ) -> read::Result<Option<&'data Self::SectionHeader>> { + let shoff: u64 = self.e_shoff(endian).into(); + if shoff == 0 { + // No section headers is ok. + return Ok(None); + } + let shentsize = usize::from(self.e_shentsize(endian)); + if shentsize != mem::size_of::<Self::SectionHeader>() { + // Section header size must match. + return Err(Error("Invalid ELF section header entry size")); + } + data.read_at(shoff) + .map(Some) + .read_error("Invalid ELF section header offset or size") + } + + /// Return the `e_phnum` field of the header. Handles extended values. + /// + /// Returns `Err` for invalid values. + fn phnum<'data, R: ReadRef<'data>>( + &self, + endian: Self::Endian, + data: R, + ) -> read::Result<usize> { + let e_phnum = self.e_phnum(endian); + if e_phnum < elf::PN_XNUM { + Ok(e_phnum as usize) + } else if let Some(section_0) = self.section_0(endian, data)? { + Ok(section_0.sh_info(endian) as usize) + } else { + // Section 0 must exist if e_phnum overflows. + Err(Error("Missing ELF section headers for e_phnum overflow")) + } + } + + /// Return the `e_shnum` field of the header. Handles extended values. + /// + /// Returns `Err` for invalid values. + fn shnum<'data, R: ReadRef<'data>>( + &self, + endian: Self::Endian, + data: R, + ) -> read::Result<usize> { + let e_shnum = self.e_shnum(endian); + if e_shnum > 0 { + Ok(e_shnum as usize) + } else if let Some(section_0) = self.section_0(endian, data)? { + section_0 + .sh_size(endian) + .into() + .try_into() + .ok() + .read_error("Invalid ELF extended e_shnum") + } else { + // No section headers is ok. + Ok(0) + } + } + + /// Return the `e_shstrndx` field of the header. Handles extended values. + /// + /// Returns `Err` for invalid values (including if the index is 0). + fn shstrndx<'data, R: ReadRef<'data>>( + &self, + endian: Self::Endian, + data: R, + ) -> read::Result<u32> { + let e_shstrndx = self.e_shstrndx(endian); + let index = if e_shstrndx != elf::SHN_XINDEX { + e_shstrndx.into() + } else if let Some(section_0) = self.section_0(endian, data)? { + section_0.sh_link(endian) + } else { + // Section 0 must exist if we're trying to read e_shstrndx. + return Err(Error("Missing ELF section headers for e_shstrndx overflow")); + }; + if index == 0 { + return Err(Error("Missing ELF e_shstrndx")); + } + Ok(index) + } + + /// Return the slice of program headers. + /// + /// Returns `Ok(&[])` if there are no program headers. + /// Returns `Err` for invalid values. + fn program_headers<'data, R: ReadRef<'data>>( + &self, + endian: Self::Endian, + data: R, + ) -> read::Result<&'data [Self::ProgramHeader]> { + let phoff: u64 = self.e_phoff(endian).into(); + if phoff == 0 { + // No program headers is ok. + return Ok(&[]); + } + let phnum = self.phnum(endian, data)?; + if phnum == 0 { + // No program headers is ok. + return Ok(&[]); + } + let phentsize = self.e_phentsize(endian) as usize; + if phentsize != mem::size_of::<Self::ProgramHeader>() { + // Program header size must match. + return Err(Error("Invalid ELF program header entry size")); + } + data.read_slice_at(phoff, phnum) + .read_error("Invalid ELF program header size or alignment") + } + + /// Return the slice of section headers. + /// + /// Returns `Ok(&[])` if there are no section headers. + /// Returns `Err` for invalid values. + fn section_headers<'data, R: ReadRef<'data>>( + &self, + endian: Self::Endian, + data: R, + ) -> read::Result<&'data [Self::SectionHeader]> { + let shoff: u64 = self.e_shoff(endian).into(); + if shoff == 0 { + // No section headers is ok. + return Ok(&[]); + } + let shnum = self.shnum(endian, data)?; + if shnum == 0 { + // No section headers is ok. + return Ok(&[]); + } + let shentsize = usize::from(self.e_shentsize(endian)); + if shentsize != mem::size_of::<Self::SectionHeader>() { + // Section header size must match. + return Err(Error("Invalid ELF section header entry size")); + } + data.read_slice_at(shoff, shnum) + .read_error("Invalid ELF section header offset/size/alignment") + } + + /// Return the string table for the section headers. + fn section_strings<'data, R: ReadRef<'data>>( + &self, + endian: Self::Endian, + data: R, + sections: &[Self::SectionHeader], + ) -> read::Result<StringTable<'data, R>> { + if sections.is_empty() { + return Ok(StringTable::default()); + } + let index = self.shstrndx(endian, data)? as usize; + let shstrtab = sections.get(index).read_error("Invalid ELF e_shstrndx")?; + let strings = if let Some((shstrtab_offset, shstrtab_size)) = shstrtab.file_range(endian) { + let shstrtab_end = shstrtab_offset + .checked_add(shstrtab_size) + .read_error("Invalid ELF shstrtab size")?; + StringTable::new(data, shstrtab_offset, shstrtab_end) + } else { + StringTable::default() + }; + Ok(strings) + } + + /// Return the section table. + fn sections<'data, R: ReadRef<'data>>( + &self, + endian: Self::Endian, + data: R, + ) -> read::Result<SectionTable<'data, Self, R>> { + let sections = self.section_headers(endian, data)?; + let strings = self.section_strings(endian, data, sections)?; + Ok(SectionTable::new(sections, strings)) + } + + /// Returns whether this is a mips64el elf file. + fn is_mips64el(&self, endian: Self::Endian) -> bool { + self.is_class_64() && self.is_little_endian() && self.e_machine(endian) == elf::EM_MIPS + } +} + +impl<Endian: endian::Endian> FileHeader for elf::FileHeader32<Endian> { + type Word = u32; + type Sword = i32; + type Endian = Endian; + type ProgramHeader = elf::ProgramHeader32<Endian>; + type SectionHeader = elf::SectionHeader32<Endian>; + type CompressionHeader = elf::CompressionHeader32<Endian>; + type NoteHeader = elf::NoteHeader32<Endian>; + type Dyn = elf::Dyn32<Endian>; + type Sym = elf::Sym32<Endian>; + type Rel = elf::Rel32<Endian>; + type Rela = elf::Rela32<Endian>; + + #[inline] + fn is_type_64(&self) -> bool { + false + } + + #[inline] + fn e_ident(&self) -> &elf::Ident { + &self.e_ident + } + + #[inline] + fn e_type(&self, endian: Self::Endian) -> u16 { + self.e_type.get(endian) + } + + #[inline] + fn e_machine(&self, endian: Self::Endian) -> u16 { + self.e_machine.get(endian) + } + + #[inline] + fn e_version(&self, endian: Self::Endian) -> u32 { + self.e_version.get(endian) + } + + #[inline] + fn e_entry(&self, endian: Self::Endian) -> Self::Word { + self.e_entry.get(endian) + } + + #[inline] + fn e_phoff(&self, endian: Self::Endian) -> Self::Word { + self.e_phoff.get(endian) + } + + #[inline] + fn e_shoff(&self, endian: Self::Endian) -> Self::Word { + self.e_shoff.get(endian) + } + + #[inline] + fn e_flags(&self, endian: Self::Endian) -> u32 { + self.e_flags.get(endian) + } + + #[inline] + fn e_ehsize(&self, endian: Self::Endian) -> u16 { + self.e_ehsize.get(endian) + } + + #[inline] + fn e_phentsize(&self, endian: Self::Endian) -> u16 { + self.e_phentsize.get(endian) + } + + #[inline] + fn e_phnum(&self, endian: Self::Endian) -> u16 { + self.e_phnum.get(endian) + } + + #[inline] + fn e_shentsize(&self, endian: Self::Endian) -> u16 { + self.e_shentsize.get(endian) + } + + #[inline] + fn e_shnum(&self, endian: Self::Endian) -> u16 { + self.e_shnum.get(endian) + } + + #[inline] + fn e_shstrndx(&self, endian: Self::Endian) -> u16 { + self.e_shstrndx.get(endian) + } +} + +impl<Endian: endian::Endian> FileHeader for elf::FileHeader64<Endian> { + type Word = u64; + type Sword = i64; + type Endian = Endian; + type ProgramHeader = elf::ProgramHeader64<Endian>; + type SectionHeader = elf::SectionHeader64<Endian>; + type CompressionHeader = elf::CompressionHeader64<Endian>; + type NoteHeader = elf::NoteHeader32<Endian>; + type Dyn = elf::Dyn64<Endian>; + type Sym = elf::Sym64<Endian>; + type Rel = elf::Rel64<Endian>; + type Rela = elf::Rela64<Endian>; + + #[inline] + fn is_type_64(&self) -> bool { + true + } + + #[inline] + fn e_ident(&self) -> &elf::Ident { + &self.e_ident + } + + #[inline] + fn e_type(&self, endian: Self::Endian) -> u16 { + self.e_type.get(endian) + } + + #[inline] + fn e_machine(&self, endian: Self::Endian) -> u16 { + self.e_machine.get(endian) + } + + #[inline] + fn e_version(&self, endian: Self::Endian) -> u32 { + self.e_version.get(endian) + } + + #[inline] + fn e_entry(&self, endian: Self::Endian) -> Self::Word { + self.e_entry.get(endian) + } + + #[inline] + fn e_phoff(&self, endian: Self::Endian) -> Self::Word { + self.e_phoff.get(endian) + } + + #[inline] + fn e_shoff(&self, endian: Self::Endian) -> Self::Word { + self.e_shoff.get(endian) + } + + #[inline] + fn e_flags(&self, endian: Self::Endian) -> u32 { + self.e_flags.get(endian) + } + + #[inline] + fn e_ehsize(&self, endian: Self::Endian) -> u16 { + self.e_ehsize.get(endian) + } + + #[inline] + fn e_phentsize(&self, endian: Self::Endian) -> u16 { + self.e_phentsize.get(endian) + } + + #[inline] + fn e_phnum(&self, endian: Self::Endian) -> u16 { + self.e_phnum.get(endian) + } + + #[inline] + fn e_shentsize(&self, endian: Self::Endian) -> u16 { + self.e_shentsize.get(endian) + } + + #[inline] + fn e_shnum(&self, endian: Self::Endian) -> u16 { + self.e_shnum.get(endian) + } + + #[inline] + fn e_shstrndx(&self, endian: Self::Endian) -> u16 { + self.e_shstrndx.get(endian) + } +} diff --git a/third_party/rust/object/src/read/elf/hash.rs b/third_party/rust/object/src/read/elf/hash.rs new file mode 100644 index 0000000000..aa1039ac10 --- /dev/null +++ b/third_party/rust/object/src/read/elf/hash.rs @@ -0,0 +1,220 @@ +use core::mem; + +use crate::elf; +use crate::read::{ReadError, ReadRef, Result}; +use crate::{U32, U64}; + +use super::{FileHeader, Sym, SymbolTable, Version, VersionTable}; + +/// A SysV symbol hash table in an ELF file. +#[derive(Debug)] +pub struct HashTable<'data, Elf: FileHeader> { + buckets: &'data [U32<Elf::Endian>], + chains: &'data [U32<Elf::Endian>], +} + +impl<'data, Elf: FileHeader> HashTable<'data, Elf> { + /// Parse a SysV hash table. + /// + /// `data` should be from a `SHT_HASH` section, or from a + /// segment pointed to via the `DT_HASH` entry. + /// + /// The header is read at offset 0 in the given `data`. + pub fn parse(endian: Elf::Endian, data: &'data [u8]) -> Result<Self> { + let mut offset = 0; + let header = data + .read::<elf::HashHeader<Elf::Endian>>(&mut offset) + .read_error("Invalid hash header")?; + let buckets = data + .read_slice(&mut offset, header.bucket_count.get(endian) as usize) + .read_error("Invalid hash buckets")?; + let chains = data + .read_slice(&mut offset, header.chain_count.get(endian) as usize) + .read_error("Invalid hash chains")?; + Ok(HashTable { buckets, chains }) + } + + /// Return the symbol table length. + pub fn symbol_table_length(&self) -> u32 { + self.chains.len() as u32 + } + + /// Use the hash table to find the symbol table entry with the given name, hash and version. + pub fn find<R: ReadRef<'data>>( + &self, + endian: Elf::Endian, + name: &[u8], + hash: u32, + version: Option<&Version>, + symbols: &SymbolTable<'data, Elf, R>, + versions: &VersionTable<'data, Elf>, + ) -> Option<(usize, &'data Elf::Sym)> { + // Get the chain start from the bucket for this hash. + let mut index = self.buckets[(hash as usize) % self.buckets.len()].get(endian) as usize; + // Avoid infinite loop. + let mut i = 0; + let strings = symbols.strings(); + while index != 0 && i < self.chains.len() { + if let Ok(symbol) = symbols.symbol(index) { + if symbol.name(endian, strings) == Ok(name) + && versions.matches(endian, index, version) + { + return Some((index, symbol)); + } + } + index = self.chains.get(index)?.get(endian) as usize; + i += 1; + } + None + } +} + +/// A GNU symbol hash table in an ELF file. +#[derive(Debug)] +pub struct GnuHashTable<'data, Elf: FileHeader> { + symbol_base: u32, + bloom_shift: u32, + bloom_filters: &'data [u8], + buckets: &'data [U32<Elf::Endian>], + values: &'data [U32<Elf::Endian>], +} + +impl<'data, Elf: FileHeader> GnuHashTable<'data, Elf> { + /// Parse a GNU hash table. + /// + /// `data` should be from a `SHT_GNU_HASH` section, or from a + /// segment pointed to via the `DT_GNU_HASH` entry. + /// + /// The header is read at offset 0 in the given `data`. + /// + /// The header does not contain a length field, and so all of `data` + /// will be used as the hash table values. It does not matter if this + /// is longer than needed, and this will often the case when accessing + /// the hash table via the `DT_GNU_HASH` entry. + pub fn parse(endian: Elf::Endian, data: &'data [u8]) -> Result<Self> { + let mut offset = 0; + let header = data + .read::<elf::GnuHashHeader<Elf::Endian>>(&mut offset) + .read_error("Invalid GNU hash header")?; + let bloom_len = + u64::from(header.bloom_count.get(endian)) * mem::size_of::<Elf::Word>() as u64; + let bloom_filters = data + .read_bytes(&mut offset, bloom_len) + .read_error("Invalid GNU hash bloom filters")?; + let buckets = data + .read_slice(&mut offset, header.bucket_count.get(endian) as usize) + .read_error("Invalid GNU hash buckets")?; + let chain_count = (data.len() - offset as usize) / 4; + let values = data + .read_slice(&mut offset, chain_count) + .read_error("Invalid GNU hash values")?; + Ok(GnuHashTable { + symbol_base: header.symbol_base.get(endian), + bloom_shift: header.bloom_shift.get(endian), + bloom_filters, + buckets, + values, + }) + } + + /// Return the symbol table index of the first symbol in the hash table. + pub fn symbol_base(&self) -> u32 { + self.symbol_base + } + + /// Determine the symbol table length by finding the last entry in the hash table. + /// + /// Returns `None` if the hash table is empty or invalid. + pub fn symbol_table_length(&self, endian: Elf::Endian) -> Option<u32> { + // Ensure we find a non-empty bucket. + if self.symbol_base == 0 { + return None; + } + + // Find the highest chain index in a bucket. + let mut max_symbol = 0; + for bucket in self.buckets { + let bucket = bucket.get(endian); + if max_symbol < bucket { + max_symbol = bucket; + } + } + + // Find the end of the chain. + for value in self + .values + .get(max_symbol.checked_sub(self.symbol_base)? as usize..)? + { + max_symbol += 1; + if value.get(endian) & 1 != 0 { + return Some(max_symbol); + } + } + + None + } + + /// Use the hash table to find the symbol table entry with the given name, hash, and version. + pub fn find<R: ReadRef<'data>>( + &self, + endian: Elf::Endian, + name: &[u8], + hash: u32, + version: Option<&Version>, + symbols: &SymbolTable<'data, Elf, R>, + versions: &VersionTable<'data, Elf>, + ) -> Option<(usize, &'data Elf::Sym)> { + let word_bits = mem::size_of::<Elf::Word>() as u32 * 8; + + // Test against bloom filter. + let bloom_count = self.bloom_filters.len() / mem::size_of::<Elf::Word>(); + let offset = + ((hash / word_bits) & (bloom_count as u32 - 1)) * mem::size_of::<Elf::Word>() as u32; + let filter = if word_bits == 64 { + self.bloom_filters + .read_at::<U64<Elf::Endian>>(offset.into()) + .ok()? + .get(endian) + } else { + self.bloom_filters + .read_at::<U32<Elf::Endian>>(offset.into()) + .ok()? + .get(endian) + .into() + }; + if filter & (1 << (hash % word_bits)) == 0 { + return None; + } + if filter & (1 << ((hash >> self.bloom_shift) % word_bits)) == 0 { + return None; + } + + // Get the chain start from the bucket for this hash. + let mut index = self.buckets[(hash as usize) % self.buckets.len()].get(endian) as usize; + if index == 0 { + return None; + } + + // Test symbols in the chain. + let strings = symbols.strings(); + let symbols = symbols.symbols().get(index..)?; + let values = self + .values + .get(index.checked_sub(self.symbol_base as usize)?..)?; + for (symbol, value) in symbols.iter().zip(values.iter()) { + let value = value.get(endian); + if value | 1 == hash | 1 { + if symbol.name(endian, strings) == Ok(name) + && versions.matches(endian, index, version) + { + return Some((index, symbol)); + } + } + if value & 1 != 0 { + break; + } + index += 1; + } + None + } +} diff --git a/third_party/rust/object/src/read/elf/mod.rs b/third_party/rust/object/src/read/elf/mod.rs new file mode 100644 index 0000000000..5b7d7f9f7f --- /dev/null +++ b/third_party/rust/object/src/read/elf/mod.rs @@ -0,0 +1,39 @@ +//! Support for reading ELF files. +//! +//! Defines traits to abstract over the difference between ELF32/ELF64, +//! and implements read functionality in terms of these traits. +//! +//! Also provides `ElfFile` and related types which implement the `Object` trait. + +mod file; +pub use file::*; + +mod segment; +pub use segment::*; + +mod section; +pub use section::*; + +mod symbol; +pub use symbol::*; + +mod relocation; +pub use relocation::*; + +mod comdat; +pub use comdat::*; + +mod dynamic; +pub use dynamic::*; + +mod compression; +pub use compression::*; + +mod note; +pub use note::*; + +mod hash; +pub use hash::*; + +mod version; +pub use version::*; diff --git a/third_party/rust/object/src/read/elf/note.rs b/third_party/rust/object/src/read/elf/note.rs new file mode 100644 index 0000000000..34024dbb89 --- /dev/null +++ b/third_party/rust/object/src/read/elf/note.rs @@ -0,0 +1,185 @@ +use core::fmt::Debug; +use core::mem; + +use crate::elf; +use crate::endian; +use crate::pod::Pod; +use crate::read::util; +use crate::read::{self, Bytes, Error, ReadError}; + +use super::FileHeader; + +/// An iterator over the notes in an ELF section or segment. +#[derive(Debug)] +pub struct NoteIterator<'data, Elf> +where + Elf: FileHeader, +{ + endian: Elf::Endian, + align: usize, + data: Bytes<'data>, +} + +impl<'data, Elf> NoteIterator<'data, Elf> +where + Elf: FileHeader, +{ + /// Returns `Err` if `align` is invalid. + pub(super) fn new( + endian: Elf::Endian, + align: Elf::Word, + data: &'data [u8], + ) -> read::Result<Self> { + let align = match align.into() { + 0u64..=4 => 4, + 8 => 8, + _ => return Err(Error("Invalid ELF note alignment")), + }; + // TODO: check data alignment? + Ok(NoteIterator { + endian, + align, + data: Bytes(data), + }) + } + + /// Returns the next note. + pub fn next(&mut self) -> read::Result<Option<Note<'data, Elf>>> { + let mut data = self.data; + if data.is_empty() { + return Ok(None); + } + + let header = data + .read_at::<Elf::NoteHeader>(0) + .read_error("ELF note is too short")?; + + // The name has no alignment requirement. + let offset = mem::size_of::<Elf::NoteHeader>(); + let namesz = header.n_namesz(self.endian) as usize; + let name = data + .read_bytes_at(offset, namesz) + .read_error("Invalid ELF note namesz")? + .0; + + // The descriptor must be aligned. + let offset = util::align(offset + namesz, self.align); + let descsz = header.n_descsz(self.endian) as usize; + let desc = data + .read_bytes_at(offset, descsz) + .read_error("Invalid ELF note descsz")? + .0; + + // The next note (if any) must be aligned. + let offset = util::align(offset + descsz, self.align); + if data.skip(offset).is_err() { + data = Bytes(&[]); + } + self.data = data; + + Ok(Some(Note { header, name, desc })) + } +} + +/// A parsed `NoteHeader`. +#[derive(Debug)] +pub struct Note<'data, Elf> +where + Elf: FileHeader, +{ + header: &'data Elf::NoteHeader, + name: &'data [u8], + desc: &'data [u8], +} + +impl<'data, Elf: FileHeader> Note<'data, Elf> { + /// Return the `n_type` field of the `NoteHeader`. + /// + /// The meaning of this field is determined by `name`. + pub fn n_type(&self, endian: Elf::Endian) -> u32 { + self.header.n_type(endian) + } + + /// Return the `n_namesz` field of the `NoteHeader`. + pub fn n_namesz(&self, endian: Elf::Endian) -> u32 { + self.header.n_namesz(endian) + } + + /// Return the `n_descsz` field of the `NoteHeader`. + pub fn n_descsz(&self, endian: Elf::Endian) -> u32 { + self.header.n_descsz(endian) + } + + /// Return the bytes for the name field following the `NoteHeader`, + /// excluding any null terminator. + /// + /// This field is usually a string including a null terminator + /// (but it is not required to be). + /// + /// The length of this field (including any null terminator) is given by + /// `n_namesz`. + pub fn name(&self) -> &'data [u8] { + if let Some((last, name)) = self.name.split_last() { + if *last == 0 { + return name; + } + } + self.name + } + + /// Return the bytes for the desc field following the `NoteHeader`. + /// + /// The length of this field is given by `n_descsz`. The meaning + /// of this field is determined by `name` and `n_type`. + pub fn desc(&self) -> &'data [u8] { + self.desc + } +} + +/// A trait for generic access to `NoteHeader32` and `NoteHeader64`. +#[allow(missing_docs)] +pub trait NoteHeader: Debug + Pod { + type Endian: endian::Endian; + + fn n_namesz(&self, endian: Self::Endian) -> u32; + fn n_descsz(&self, endian: Self::Endian) -> u32; + fn n_type(&self, endian: Self::Endian) -> u32; +} + +impl<Endian: endian::Endian> NoteHeader for elf::NoteHeader32<Endian> { + type Endian = Endian; + + #[inline] + fn n_namesz(&self, endian: Self::Endian) -> u32 { + self.n_namesz.get(endian) + } + + #[inline] + fn n_descsz(&self, endian: Self::Endian) -> u32 { + self.n_descsz.get(endian) + } + + #[inline] + fn n_type(&self, endian: Self::Endian) -> u32 { + self.n_type.get(endian) + } +} + +impl<Endian: endian::Endian> NoteHeader for elf::NoteHeader64<Endian> { + type Endian = Endian; + + #[inline] + fn n_namesz(&self, endian: Self::Endian) -> u32 { + self.n_namesz.get(endian) + } + + #[inline] + fn n_descsz(&self, endian: Self::Endian) -> u32 { + self.n_descsz.get(endian) + } + + #[inline] + fn n_type(&self, endian: Self::Endian) -> u32 { + self.n_type.get(endian) + } +} diff --git a/third_party/rust/object/src/read/elf/relocation.rs b/third_party/rust/object/src/read/elf/relocation.rs new file mode 100644 index 0000000000..8443dbc750 --- /dev/null +++ b/third_party/rust/object/src/read/elf/relocation.rs @@ -0,0 +1,571 @@ +use alloc::fmt; +use alloc::vec::Vec; +use core::fmt::Debug; +use core::slice; + +use crate::elf; +use crate::endian::{self, Endianness}; +use crate::pod::Pod; +use crate::read::{ + self, Error, ReadRef, Relocation, RelocationEncoding, RelocationKind, RelocationTarget, + SectionIndex, SymbolIndex, +}; + +use super::{ElfFile, FileHeader, SectionHeader, SectionTable}; + +/// A mapping from section index to associated relocation sections. +#[derive(Debug)] +pub struct RelocationSections { + relocations: Vec<usize>, +} + +impl RelocationSections { + /// Create a new mapping using the section table. + /// + /// Skips relocation sections that do not use the given symbol table section. + pub fn parse<'data, Elf: FileHeader, R: ReadRef<'data>>( + endian: Elf::Endian, + sections: &SectionTable<'data, Elf, R>, + symbol_section: SectionIndex, + ) -> read::Result<Self> { + let mut relocations = vec![0; sections.len()]; + for (index, section) in sections.iter().enumerate().rev() { + let sh_type = section.sh_type(endian); + if sh_type == elf::SHT_REL || sh_type == elf::SHT_RELA { + // The symbol indices used in relocations must be for the symbol table + // we are expecting to use. + let sh_link = SectionIndex(section.sh_link(endian) as usize); + if sh_link != symbol_section { + continue; + } + + let sh_info = section.sh_info(endian) as usize; + if sh_info == 0 { + // Skip dynamic relocations. + continue; + } + if sh_info >= relocations.len() { + return Err(Error("Invalid ELF sh_info for relocation section")); + } + + // Handle multiple relocation sections by chaining them. + let next = relocations[sh_info]; + relocations[sh_info] = index; + relocations[index] = next; + } + } + Ok(Self { relocations }) + } + + /// Given a section index, return the section index of the associated relocation section. + /// + /// This may also be called with a relocation section index, and it will return the + /// next associated relocation section. + pub fn get(&self, index: usize) -> Option<usize> { + self.relocations.get(index).cloned().filter(|x| *x != 0) + } +} + +pub(super) enum ElfRelaIterator<'data, Elf: FileHeader> { + Rel(slice::Iter<'data, Elf::Rel>), + Rela(slice::Iter<'data, Elf::Rela>), +} + +impl<'data, Elf: FileHeader> ElfRelaIterator<'data, Elf> { + fn is_rel(&self) -> bool { + match self { + ElfRelaIterator::Rel(_) => true, + ElfRelaIterator::Rela(_) => false, + } + } +} + +impl<'data, Elf: FileHeader> Iterator for ElfRelaIterator<'data, Elf> { + type Item = Elf::Rela; + + fn next(&mut self) -> Option<Self::Item> { + match self { + ElfRelaIterator::Rel(ref mut i) => i.next().cloned().map(Self::Item::from), + ElfRelaIterator::Rela(ref mut i) => i.next().cloned(), + } + } +} + +/// An iterator over the dynamic relocations for an `ElfFile32`. +pub type ElfDynamicRelocationIterator32<'data, 'file, Endian = Endianness, R = &'data [u8]> = + ElfDynamicRelocationIterator<'data, 'file, elf::FileHeader32<Endian>, R>; +/// An iterator over the dynamic relocations for an `ElfFile64`. +pub type ElfDynamicRelocationIterator64<'data, 'file, Endian = Endianness, R = &'data [u8]> = + ElfDynamicRelocationIterator<'data, 'file, elf::FileHeader64<Endian>, R>; + +/// An iterator over the dynamic relocations for an `ElfFile`. +pub struct ElfDynamicRelocationIterator<'data, 'file, Elf, R = &'data [u8]> +where + Elf: FileHeader, + R: ReadRef<'data>, +{ + /// The current relocation section index. + pub(super) section_index: SectionIndex, + pub(super) file: &'file ElfFile<'data, Elf, R>, + pub(super) relocations: Option<ElfRelaIterator<'data, Elf>>, +} + +impl<'data, 'file, Elf, R> Iterator for ElfDynamicRelocationIterator<'data, 'file, Elf, R> +where + Elf: FileHeader, + R: ReadRef<'data>, +{ + type Item = (u64, Relocation); + + fn next(&mut self) -> Option<Self::Item> { + let endian = self.file.endian; + loop { + if let Some(ref mut relocations) = self.relocations { + if let Some(reloc) = relocations.next() { + let relocation = + parse_relocation(self.file.header, endian, reloc, relocations.is_rel()); + return Some((reloc.r_offset(endian).into(), relocation)); + } + self.relocations = None; + } + + let section = self.file.sections.section(self.section_index).ok()?; + self.section_index.0 += 1; + + let sh_link = SectionIndex(section.sh_link(endian) as usize); + if sh_link != self.file.dynamic_symbols.section() { + continue; + } + + match section.sh_type(endian) { + elf::SHT_REL => { + if let Ok(relocations) = section.data_as_array(endian, self.file.data) { + self.relocations = Some(ElfRelaIterator::Rel(relocations.iter())); + } + } + elf::SHT_RELA => { + if let Ok(relocations) = section.data_as_array(endian, self.file.data) { + self.relocations = Some(ElfRelaIterator::Rela(relocations.iter())); + } + } + _ => {} + } + } + } +} + +impl<'data, 'file, Elf, R> fmt::Debug for ElfDynamicRelocationIterator<'data, 'file, Elf, R> +where + Elf: FileHeader, + R: ReadRef<'data>, +{ + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("ElfDynamicRelocationIterator").finish() + } +} + +/// An iterator over the relocations for an `ElfSection32`. +pub type ElfSectionRelocationIterator32<'data, 'file, Endian = Endianness, R = &'data [u8]> = + ElfSectionRelocationIterator<'data, 'file, elf::FileHeader32<Endian>, R>; +/// An iterator over the relocations for an `ElfSection64`. +pub type ElfSectionRelocationIterator64<'data, 'file, Endian = Endianness, R = &'data [u8]> = + ElfSectionRelocationIterator<'data, 'file, elf::FileHeader64<Endian>, R>; + +/// An iterator over the relocations for an `ElfSection`. +pub struct ElfSectionRelocationIterator<'data, 'file, Elf, R = &'data [u8]> +where + Elf: FileHeader, + R: ReadRef<'data>, +{ + /// The current pointer in the chain of relocation sections. + pub(super) section_index: SectionIndex, + pub(super) file: &'file ElfFile<'data, Elf, R>, + pub(super) relocations: Option<ElfRelaIterator<'data, Elf>>, +} + +impl<'data, 'file, Elf, R> Iterator for ElfSectionRelocationIterator<'data, 'file, Elf, R> +where + Elf: FileHeader, + R: ReadRef<'data>, +{ + type Item = (u64, Relocation); + + fn next(&mut self) -> Option<Self::Item> { + let endian = self.file.endian; + loop { + if let Some(ref mut relocations) = self.relocations { + if let Some(reloc) = relocations.next() { + let relocation = + parse_relocation(self.file.header, endian, reloc, relocations.is_rel()); + return Some((reloc.r_offset(endian).into(), relocation)); + } + self.relocations = None; + } + self.section_index = SectionIndex(self.file.relocations.get(self.section_index.0)?); + // The construction of RelocationSections ensures section_index is valid. + let section = self.file.sections.section(self.section_index).unwrap(); + match section.sh_type(endian) { + elf::SHT_REL => { + if let Ok(relocations) = section.data_as_array(endian, self.file.data) { + self.relocations = Some(ElfRelaIterator::Rel(relocations.iter())); + } + } + elf::SHT_RELA => { + if let Ok(relocations) = section.data_as_array(endian, self.file.data) { + self.relocations = Some(ElfRelaIterator::Rela(relocations.iter())); + } + } + _ => {} + } + } + } +} + +impl<'data, 'file, Elf, R> fmt::Debug for ElfSectionRelocationIterator<'data, 'file, Elf, R> +where + Elf: FileHeader, + R: ReadRef<'data>, +{ + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("ElfSectionRelocationIterator").finish() + } +} + +fn parse_relocation<Elf: FileHeader>( + header: &Elf, + endian: Elf::Endian, + reloc: Elf::Rela, + implicit_addend: bool, +) -> Relocation { + let mut encoding = RelocationEncoding::Generic; + let is_mips64el = header.is_mips64el(endian); + let (kind, size) = match header.e_machine(endian) { + elf::EM_AARCH64 => { + if header.is_type_64() { + match reloc.r_type(endian, false) { + elf::R_AARCH64_ABS64 => (RelocationKind::Absolute, 64), + elf::R_AARCH64_ABS32 => (RelocationKind::Absolute, 32), + elf::R_AARCH64_ABS16 => (RelocationKind::Absolute, 16), + elf::R_AARCH64_PREL64 => (RelocationKind::Relative, 64), + elf::R_AARCH64_PREL32 => (RelocationKind::Relative, 32), + elf::R_AARCH64_PREL16 => (RelocationKind::Relative, 16), + elf::R_AARCH64_CALL26 => { + encoding = RelocationEncoding::AArch64Call; + (RelocationKind::PltRelative, 26) + } + r_type => (RelocationKind::Elf(r_type), 0), + } + } else { + match reloc.r_type(endian, false) { + elf::R_AARCH64_P32_ABS32 => (RelocationKind::Absolute, 32), + r_type => (RelocationKind::Elf(r_type), 0), + } + } + } + elf::EM_ARM => match reloc.r_type(endian, false) { + elf::R_ARM_ABS32 => (RelocationKind::Absolute, 32), + r_type => (RelocationKind::Elf(r_type), 0), + }, + elf::EM_AVR => match reloc.r_type(endian, false) { + elf::R_AVR_32 => (RelocationKind::Absolute, 32), + elf::R_AVR_16 => (RelocationKind::Absolute, 16), + r_type => (RelocationKind::Elf(r_type), 0), + }, + elf::EM_BPF => match reloc.r_type(endian, false) { + elf::R_BPF_64_64 => (RelocationKind::Absolute, 64), + elf::R_BPF_64_32 => (RelocationKind::Absolute, 32), + r_type => (RelocationKind::Elf(r_type), 0), + }, + elf::EM_386 => match reloc.r_type(endian, false) { + elf::R_386_32 => (RelocationKind::Absolute, 32), + elf::R_386_PC32 => (RelocationKind::Relative, 32), + elf::R_386_GOT32 => (RelocationKind::Got, 32), + elf::R_386_PLT32 => (RelocationKind::PltRelative, 32), + elf::R_386_GOTOFF => (RelocationKind::GotBaseOffset, 32), + elf::R_386_GOTPC => (RelocationKind::GotBaseRelative, 32), + elf::R_386_16 => (RelocationKind::Absolute, 16), + elf::R_386_PC16 => (RelocationKind::Relative, 16), + elf::R_386_8 => (RelocationKind::Absolute, 8), + elf::R_386_PC8 => (RelocationKind::Relative, 8), + r_type => (RelocationKind::Elf(r_type), 0), + }, + elf::EM_X86_64 => match reloc.r_type(endian, false) { + elf::R_X86_64_64 => (RelocationKind::Absolute, 64), + elf::R_X86_64_PC32 => (RelocationKind::Relative, 32), + elf::R_X86_64_GOT32 => (RelocationKind::Got, 32), + elf::R_X86_64_PLT32 => (RelocationKind::PltRelative, 32), + elf::R_X86_64_GOTPCREL => (RelocationKind::GotRelative, 32), + elf::R_X86_64_32 => (RelocationKind::Absolute, 32), + elf::R_X86_64_32S => { + encoding = RelocationEncoding::X86Signed; + (RelocationKind::Absolute, 32) + } + elf::R_X86_64_16 => (RelocationKind::Absolute, 16), + elf::R_X86_64_PC16 => (RelocationKind::Relative, 16), + elf::R_X86_64_8 => (RelocationKind::Absolute, 8), + elf::R_X86_64_PC8 => (RelocationKind::Relative, 8), + r_type => (RelocationKind::Elf(r_type), 0), + }, + elf::EM_HEXAGON => match reloc.r_type(endian, false) { + elf::R_HEX_32 => (RelocationKind::Absolute, 32), + r_type => (RelocationKind::Elf(r_type), 0), + }, + elf::EM_LOONGARCH => match reloc.r_type(endian, false) { + elf::R_LARCH_32 => (RelocationKind::Absolute, 32), + elf::R_LARCH_64 => (RelocationKind::Absolute, 64), + elf::R_LARCH_32_PCREL => (RelocationKind::Relative, 32), + elf::R_LARCH_B16 => { + encoding = RelocationEncoding::LoongArchBranch; + (RelocationKind::Relative, 16) + } + elf::R_LARCH_B21 => { + encoding = RelocationEncoding::LoongArchBranch; + (RelocationKind::Relative, 21) + } + elf::R_LARCH_B26 => { + encoding = RelocationEncoding::LoongArchBranch; + (RelocationKind::Relative, 26) + } + r_type => (RelocationKind::Elf(r_type), 0), + }, + elf::EM_MIPS => match reloc.r_type(endian, is_mips64el) { + elf::R_MIPS_16 => (RelocationKind::Absolute, 16), + elf::R_MIPS_32 => (RelocationKind::Absolute, 32), + elf::R_MIPS_64 => (RelocationKind::Absolute, 64), + r_type => (RelocationKind::Elf(r_type), 0), + }, + elf::EM_MSP430 => match reloc.r_type(endian, false) { + elf::R_MSP430_32 => (RelocationKind::Absolute, 32), + elf::R_MSP430_16_BYTE => (RelocationKind::Absolute, 16), + r_type => (RelocationKind::Elf(r_type), 0), + }, + elf::EM_PPC => match reloc.r_type(endian, false) { + elf::R_PPC_ADDR32 => (RelocationKind::Absolute, 32), + r_type => (RelocationKind::Elf(r_type), 0), + }, + elf::EM_PPC64 => match reloc.r_type(endian, false) { + elf::R_PPC64_ADDR32 => (RelocationKind::Absolute, 32), + elf::R_PPC64_ADDR64 => (RelocationKind::Absolute, 64), + r_type => (RelocationKind::Elf(r_type), 0), + }, + elf::EM_RISCV => match reloc.r_type(endian, false) { + elf::R_RISCV_32 => (RelocationKind::Absolute, 32), + elf::R_RISCV_64 => (RelocationKind::Absolute, 64), + r_type => (RelocationKind::Elf(r_type), 0), + }, + elf::EM_S390 => match reloc.r_type(endian, false) { + elf::R_390_8 => (RelocationKind::Absolute, 8), + elf::R_390_16 => (RelocationKind::Absolute, 16), + elf::R_390_32 => (RelocationKind::Absolute, 32), + elf::R_390_64 => (RelocationKind::Absolute, 64), + elf::R_390_PC16 => (RelocationKind::Relative, 16), + elf::R_390_PC32 => (RelocationKind::Relative, 32), + elf::R_390_PC64 => (RelocationKind::Relative, 64), + elf::R_390_PC16DBL => { + encoding = RelocationEncoding::S390xDbl; + (RelocationKind::Relative, 16) + } + elf::R_390_PC32DBL => { + encoding = RelocationEncoding::S390xDbl; + (RelocationKind::Relative, 32) + } + elf::R_390_PLT16DBL => { + encoding = RelocationEncoding::S390xDbl; + (RelocationKind::PltRelative, 16) + } + elf::R_390_PLT32DBL => { + encoding = RelocationEncoding::S390xDbl; + (RelocationKind::PltRelative, 32) + } + elf::R_390_GOT16 => (RelocationKind::Got, 16), + elf::R_390_GOT32 => (RelocationKind::Got, 32), + elf::R_390_GOT64 => (RelocationKind::Got, 64), + elf::R_390_GOTENT => { + encoding = RelocationEncoding::S390xDbl; + (RelocationKind::GotRelative, 32) + } + elf::R_390_GOTOFF16 => (RelocationKind::GotBaseOffset, 16), + elf::R_390_GOTOFF32 => (RelocationKind::GotBaseOffset, 32), + elf::R_390_GOTOFF64 => (RelocationKind::GotBaseOffset, 64), + elf::R_390_GOTPC => (RelocationKind::GotBaseRelative, 64), + elf::R_390_GOTPCDBL => { + encoding = RelocationEncoding::S390xDbl; + (RelocationKind::GotBaseRelative, 32) + } + r_type => (RelocationKind::Elf(r_type), 0), + }, + elf::EM_SBF => match reloc.r_type(endian, false) { + elf::R_SBF_64_64 => (RelocationKind::Absolute, 64), + elf::R_SBF_64_32 => (RelocationKind::Absolute, 32), + r_type => (RelocationKind::Elf(r_type), 0), + }, + elf::EM_SPARC | elf::EM_SPARC32PLUS | elf::EM_SPARCV9 => { + match reloc.r_type(endian, false) { + elf::R_SPARC_32 | elf::R_SPARC_UA32 => (RelocationKind::Absolute, 32), + elf::R_SPARC_64 | elf::R_SPARC_UA64 => (RelocationKind::Absolute, 64), + r_type => (RelocationKind::Elf(r_type), 0), + } + } + elf::EM_XTENSA => match reloc.r_type(endian, false) { + elf::R_XTENSA_32 => (RelocationKind::Absolute, 32), + elf::R_XTENSA_32_PCREL => (RelocationKind::Relative, 32), + r_type => (RelocationKind::Elf(r_type), 0), + }, + _ => (RelocationKind::Elf(reloc.r_type(endian, false)), 0), + }; + let sym = reloc.r_sym(endian, is_mips64el) as usize; + let target = if sym == 0 { + RelocationTarget::Absolute + } else { + RelocationTarget::Symbol(SymbolIndex(sym)) + }; + Relocation { + kind, + encoding, + size, + target, + addend: reloc.r_addend(endian).into(), + implicit_addend, + } +} + +/// A trait for generic access to `Rel32` and `Rel64`. +#[allow(missing_docs)] +pub trait Rel: Debug + Pod + Clone { + type Word: Into<u64>; + type Sword: Into<i64>; + type Endian: endian::Endian; + + fn r_offset(&self, endian: Self::Endian) -> Self::Word; + fn r_info(&self, endian: Self::Endian) -> Self::Word; + fn r_sym(&self, endian: Self::Endian) -> u32; + fn r_type(&self, endian: Self::Endian) -> u32; +} + +impl<Endian: endian::Endian> Rel for elf::Rel32<Endian> { + type Word = u32; + type Sword = i32; + type Endian = Endian; + + #[inline] + fn r_offset(&self, endian: Self::Endian) -> Self::Word { + self.r_offset.get(endian) + } + + #[inline] + fn r_info(&self, endian: Self::Endian) -> Self::Word { + self.r_info.get(endian) + } + + #[inline] + fn r_sym(&self, endian: Self::Endian) -> u32 { + self.r_sym(endian) + } + + #[inline] + fn r_type(&self, endian: Self::Endian) -> u32 { + self.r_type(endian) + } +} + +impl<Endian: endian::Endian> Rel for elf::Rel64<Endian> { + type Word = u64; + type Sword = i64; + type Endian = Endian; + + #[inline] + fn r_offset(&self, endian: Self::Endian) -> Self::Word { + self.r_offset.get(endian) + } + + #[inline] + fn r_info(&self, endian: Self::Endian) -> Self::Word { + self.r_info.get(endian) + } + + #[inline] + fn r_sym(&self, endian: Self::Endian) -> u32 { + self.r_sym(endian) + } + + #[inline] + fn r_type(&self, endian: Self::Endian) -> u32 { + self.r_type(endian) + } +} + +/// A trait for generic access to `Rela32` and `Rela64`. +#[allow(missing_docs)] +pub trait Rela: Debug + Pod + Clone { + type Word: Into<u64>; + type Sword: Into<i64>; + type Endian: endian::Endian; + + fn r_offset(&self, endian: Self::Endian) -> Self::Word; + fn r_info(&self, endian: Self::Endian, is_mips64el: bool) -> Self::Word; + fn r_addend(&self, endian: Self::Endian) -> Self::Sword; + fn r_sym(&self, endian: Self::Endian, is_mips64el: bool) -> u32; + fn r_type(&self, endian: Self::Endian, is_mips64el: bool) -> u32; +} + +impl<Endian: endian::Endian> Rela for elf::Rela32<Endian> { + type Word = u32; + type Sword = i32; + type Endian = Endian; + + #[inline] + fn r_offset(&self, endian: Self::Endian) -> Self::Word { + self.r_offset.get(endian) + } + + #[inline] + fn r_info(&self, endian: Self::Endian, _is_mips64el: bool) -> Self::Word { + self.r_info.get(endian) + } + + #[inline] + fn r_addend(&self, endian: Self::Endian) -> Self::Sword { + self.r_addend.get(endian) + } + + #[inline] + fn r_sym(&self, endian: Self::Endian, _is_mips64el: bool) -> u32 { + self.r_sym(endian) + } + + #[inline] + fn r_type(&self, endian: Self::Endian, _is_mips64el: bool) -> u32 { + self.r_type(endian) + } +} + +impl<Endian: endian::Endian> Rela for elf::Rela64<Endian> { + type Word = u64; + type Sword = i64; + type Endian = Endian; + + #[inline] + fn r_offset(&self, endian: Self::Endian) -> Self::Word { + self.r_offset.get(endian) + } + + #[inline] + fn r_info(&self, endian: Self::Endian, is_mips64el: bool) -> Self::Word { + self.get_r_info(endian, is_mips64el) + } + + #[inline] + fn r_addend(&self, endian: Self::Endian) -> Self::Sword { + self.r_addend.get(endian) + } + + #[inline] + fn r_sym(&self, endian: Self::Endian, is_mips64el: bool) -> u32 { + self.r_sym(endian, is_mips64el) + } + + #[inline] + fn r_type(&self, endian: Self::Endian, is_mips64el: bool) -> u32 { + self.r_type(endian, is_mips64el) + } +} diff --git a/third_party/rust/object/src/read/elf/section.rs b/third_party/rust/object/src/read/elf/section.rs new file mode 100644 index 0000000000..3f8a082165 --- /dev/null +++ b/third_party/rust/object/src/read/elf/section.rs @@ -0,0 +1,1090 @@ +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::{ + 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 + 'data: 'file, + 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 (self.section.sh_flags(endian).into() & u64::from(elf::SHF_COMPRESSED)) == 0 { + return Ok(None); + } + let (section_offset, section_size) = self + .section + .file_range(endian) + .read_error("Invalid ELF compressed section type")?; + let mut offset = section_offset; + let header = self + .file + .data + .read::<Elf::CompressionHeader>(&mut offset) + .read_error("Invalid ELF compressed section offset")?; + if header.ch_type(endian) != elf::ELFCOMPRESS_ZLIB { + return Err(Error("Unsupported ELF compression type")); + } + let uncompressed_size = header.ch_size(endian).into(); + let compressed_size = section_size + .checked_sub(offset - section_offset) + .read_error("Invalid ELF compressed section size")?; + Ok(Some(CompressedFileRange { + format: CompressionFormat::Zlib, + offset, + compressed_size, + uncompressed_size, + })) + } + + /// 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))) + } +} + +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) + } +} diff --git a/third_party/rust/object/src/read/elf/segment.rs b/third_party/rust/object/src/read/elf/segment.rs new file mode 100644 index 0000000000..445893c8d1 --- /dev/null +++ b/third_party/rust/object/src/read/elf/segment.rs @@ -0,0 +1,333 @@ +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 over the segments of an `ElfFile32`. +pub type ElfSegmentIterator32<'data, 'file, Endian = Endianness, R = &'data [u8]> = + ElfSegmentIterator<'data, 'file, elf::FileHeader32<Endian>, R>; +/// An iterator over the segments of an `ElfFile64`. +pub type ElfSegmentIterator64<'data, 'file, Endian = Endianness, R = &'data [u8]> = + ElfSegmentIterator<'data, 'file, elf::FileHeader64<Endian>, R>; + +/// An iterator over the segments of 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<Self::Item> { + 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 of an `ElfFile32`. +pub type ElfSegment32<'data, 'file, Endian = Endianness, R = &'data [u8]> = + ElfSegment<'data, 'file, elf::FileHeader32<Endian>, R>; +/// A segment of an `ElfFile64`. +pub type ElfSegment64<'data, 'file, Endian = Endianness, R = &'data [u8]> = + ElfSegment<'data, 'file, elf::FileHeader64<Endian>, R>; + +/// A segment of an `ElfFile`. +#[derive(Debug)] +pub struct ElfSegment<'data, 'file, Elf, R = &'data [u8]> +where + 'data: 'file, + 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<Option<&'data [u8]>> { + Ok(read::util::data_range( + self.bytes()?, + self.address(), + address, + size, + )) + } + + #[inline] + fn name_bytes(&self) -> read::Result<Option<&[u8]>> { + Ok(None) + } + + #[inline] + fn name(&self) -> read::Result<Option<&str>> { + 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 `ProgramHeader32` and `ProgramHeader64`. +#[allow(missing_docs)] +pub trait ProgramHeader: Debug + Pod { + type Elf: FileHeader<ProgramHeader = Self, Endian = Self::Endian, Word = Self::Word>; + type Word: Into<u64>; + 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::<T>()) + } + + /// 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<Option<&'data [u8]>, ()> { + 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<Option<&'data [<Self::Elf as FileHeader>::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<Option<NoteIterator<'data, Self::Elf>>> { + 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<Endian: endian::Endian> ProgramHeader for elf::ProgramHeader32<Endian> { + type Word = u32; + type Endian = Endian; + type Elf = elf::FileHeader32<Endian>; + + #[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<Endian: endian::Endian> ProgramHeader for elf::ProgramHeader64<Endian> { + type Word = u64; + type Endian = Endian; + type Elf = elf::FileHeader64<Endian>; + + #[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) + } +} diff --git a/third_party/rust/object/src/read/elf/symbol.rs b/third_party/rust/object/src/read/elf/symbol.rs new file mode 100644 index 0000000000..5d8d29f276 --- /dev/null +++ b/third_party/rust/object/src/read/elf/symbol.rs @@ -0,0 +1,580 @@ +use alloc::fmt; +use alloc::vec::Vec; +use core::fmt::Debug; +use core::slice; +use core::str; + +use crate::endian::{self, Endianness}; +use crate::pod::Pod; +use crate::read::util::StringTable; +use crate::read::{ + self, ObjectSymbol, ObjectSymbolTable, ReadError, ReadRef, SectionIndex, SymbolFlags, + SymbolIndex, SymbolKind, SymbolMap, SymbolMapEntry, SymbolScope, SymbolSection, +}; +use crate::{elf, U32}; + +use super::{FileHeader, SectionHeader, SectionTable}; + +/// A table of symbol entries in an ELF file. +/// +/// Also includes the string table used for the symbol names. +#[derive(Debug, Clone, Copy)] +pub struct SymbolTable<'data, Elf: FileHeader, R = &'data [u8]> +where + R: ReadRef<'data>, +{ + section: SectionIndex, + string_section: SectionIndex, + shndx_section: SectionIndex, + symbols: &'data [Elf::Sym], + strings: StringTable<'data, R>, + shndx: &'data [U32<Elf::Endian>], +} + +impl<'data, Elf: FileHeader, R: ReadRef<'data>> Default for SymbolTable<'data, Elf, R> { + fn default() -> Self { + SymbolTable { + section: SectionIndex(0), + string_section: SectionIndex(0), + shndx_section: SectionIndex(0), + symbols: &[], + strings: Default::default(), + shndx: &[], + } + } +} + +impl<'data, Elf: FileHeader, R: ReadRef<'data>> SymbolTable<'data, Elf, R> { + /// Parse the given symbol table section. + pub fn parse( + endian: Elf::Endian, + data: R, + sections: &SectionTable<'data, Elf, R>, + section_index: SectionIndex, + section: &Elf::SectionHeader, + ) -> read::Result<SymbolTable<'data, Elf, R>> { + debug_assert!( + section.sh_type(endian) == elf::SHT_DYNSYM + || section.sh_type(endian) == elf::SHT_SYMTAB + ); + + let symbols = section + .data_as_array(endian, data) + .read_error("Invalid ELF symbol table data")?; + + let link = SectionIndex(section.sh_link(endian) as usize); + let strings = sections.strings(endian, data, link)?; + + let mut shndx_section = SectionIndex(0); + let mut shndx = &[][..]; + for (i, s) in sections.iter().enumerate() { + if s.sh_type(endian) == elf::SHT_SYMTAB_SHNDX + && s.sh_link(endian) as usize == section_index.0 + { + shndx_section = SectionIndex(i); + shndx = s + .data_as_array(endian, data) + .read_error("Invalid ELF symtab_shndx data")?; + } + } + + Ok(SymbolTable { + section: section_index, + string_section: link, + symbols, + strings, + shndx, + shndx_section, + }) + } + + /// Return the section index of this symbol table. + #[inline] + pub fn section(&self) -> SectionIndex { + self.section + } + + /// Return the section index of the shndx table. + #[inline] + pub fn shndx_section(&self) -> SectionIndex { + self.shndx_section + } + + /// Return the section index of the linked string table. + #[inline] + pub fn string_section(&self) -> SectionIndex { + self.string_section + } + + /// Return the string table used for the symbol names. + #[inline] + pub fn strings(&self) -> StringTable<'data, R> { + self.strings + } + + /// Return the symbol table. + #[inline] + pub fn symbols(&self) -> &'data [Elf::Sym] { + self.symbols + } + + /// Iterate over the symbols. + #[inline] + pub fn iter(&self) -> slice::Iter<'data, Elf::Sym> { + self.symbols.iter() + } + + /// Return true if the symbol table is empty. + #[inline] + pub fn is_empty(&self) -> bool { + self.symbols.is_empty() + } + + /// The number of symbols. + #[inline] + pub fn len(&self) -> usize { + self.symbols.len() + } + + /// Return the symbol at the given index. + pub fn symbol(&self, index: usize) -> read::Result<&'data Elf::Sym> { + self.symbols + .get(index) + .read_error("Invalid ELF symbol index") + } + + /// Return the extended section index for the given symbol if present. + #[inline] + pub fn shndx(&self, endian: Elf::Endian, index: usize) -> Option<u32> { + self.shndx.get(index).map(|x| x.get(endian)) + } + + /// Return the section index for the given symbol. + /// + /// This uses the extended section index if present. + pub fn symbol_section( + &self, + endian: Elf::Endian, + symbol: &'data Elf::Sym, + index: usize, + ) -> read::Result<Option<SectionIndex>> { + match symbol.st_shndx(endian) { + elf::SHN_UNDEF => Ok(None), + elf::SHN_XINDEX => self + .shndx(endian, index) + .read_error("Missing ELF symbol extended index") + .map(|index| Some(SectionIndex(index as usize))), + shndx if shndx < elf::SHN_LORESERVE => Ok(Some(SectionIndex(shndx.into()))), + _ => Ok(None), + } + } + + /// Return the symbol name for the given symbol. + pub fn symbol_name( + &self, + endian: Elf::Endian, + symbol: &'data Elf::Sym, + ) -> read::Result<&'data [u8]> { + symbol.name(endian, self.strings) + } + + /// Construct a map from addresses to a user-defined map entry. + pub fn map<Entry: SymbolMapEntry, F: Fn(&'data Elf::Sym) -> Option<Entry>>( + &self, + endian: Elf::Endian, + f: F, + ) -> SymbolMap<Entry> { + let mut symbols = Vec::with_capacity(self.symbols.len()); + for symbol in self.symbols { + if !symbol.is_definition(endian) { + continue; + } + if let Some(entry) = f(symbol) { + symbols.push(entry); + } + } + SymbolMap::new(symbols) + } +} + +/// A symbol table of an `ElfFile32`. +pub type ElfSymbolTable32<'data, 'file, Endian = Endianness, R = &'data [u8]> = + ElfSymbolTable<'data, 'file, elf::FileHeader32<Endian>, R>; +/// A symbol table of an `ElfFile32`. +pub type ElfSymbolTable64<'data, 'file, Endian = Endianness, R = &'data [u8]> = + ElfSymbolTable<'data, 'file, elf::FileHeader64<Endian>, R>; + +/// A symbol table of an `ElfFile`. +#[derive(Debug, Clone, Copy)] +pub struct ElfSymbolTable<'data, 'file, Elf, R = &'data [u8]> +where + 'data: 'file, + Elf: FileHeader, + R: ReadRef<'data>, +{ + pub(super) endian: Elf::Endian, + pub(super) symbols: &'file SymbolTable<'data, Elf, R>, +} + +impl<'data, 'file, Elf: FileHeader, R: ReadRef<'data>> read::private::Sealed + for ElfSymbolTable<'data, 'file, Elf, R> +{ +} + +impl<'data, 'file, Elf: FileHeader, R: ReadRef<'data>> ObjectSymbolTable<'data> + for ElfSymbolTable<'data, 'file, Elf, R> +{ + type Symbol = ElfSymbol<'data, 'file, Elf, R>; + type SymbolIterator = ElfSymbolIterator<'data, 'file, Elf, R>; + + fn symbols(&self) -> Self::SymbolIterator { + ElfSymbolIterator { + endian: self.endian, + symbols: self.symbols, + index: 0, + } + } + + fn symbol_by_index(&self, index: SymbolIndex) -> read::Result<Self::Symbol> { + let symbol = self.symbols.symbol(index.0)?; + Ok(ElfSymbol { + endian: self.endian, + symbols: self.symbols, + index, + symbol, + }) + } +} + +/// An iterator over the symbols of an `ElfFile32`. +pub type ElfSymbolIterator32<'data, 'file, Endian = Endianness, R = &'data [u8]> = + ElfSymbolIterator<'data, 'file, elf::FileHeader32<Endian>, R>; +/// An iterator over the symbols of an `ElfFile64`. +pub type ElfSymbolIterator64<'data, 'file, Endian = Endianness, R = &'data [u8]> = + ElfSymbolIterator<'data, 'file, elf::FileHeader64<Endian>, R>; + +/// An iterator over the symbols of an `ElfFile`. +pub struct ElfSymbolIterator<'data, 'file, Elf, R = &'data [u8]> +where + 'data: 'file, + Elf: FileHeader, + R: ReadRef<'data>, +{ + pub(super) endian: Elf::Endian, + pub(super) symbols: &'file SymbolTable<'data, Elf, R>, + pub(super) index: usize, +} + +impl<'data, 'file, Elf: FileHeader, R: ReadRef<'data>> fmt::Debug + for ElfSymbolIterator<'data, 'file, Elf, R> +{ + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("ElfSymbolIterator").finish() + } +} + +impl<'data, 'file, Elf: FileHeader, R: ReadRef<'data>> Iterator + for ElfSymbolIterator<'data, 'file, Elf, R> +{ + type Item = ElfSymbol<'data, 'file, Elf, R>; + + fn next(&mut self) -> Option<Self::Item> { + let index = self.index; + let symbol = self.symbols.symbols.get(index)?; + self.index += 1; + Some(ElfSymbol { + endian: self.endian, + symbols: self.symbols, + index: SymbolIndex(index), + symbol, + }) + } +} + +/// A symbol of an `ElfFile32`. +pub type ElfSymbol32<'data, 'file, Endian = Endianness, R = &'data [u8]> = + ElfSymbol<'data, 'file, elf::FileHeader32<Endian>, R>; +/// A symbol of an `ElfFile64`. +pub type ElfSymbol64<'data, 'file, Endian = Endianness, R = &'data [u8]> = + ElfSymbol<'data, 'file, elf::FileHeader64<Endian>, R>; + +/// A symbol of an `ElfFile`. +#[derive(Debug, Clone, Copy)] +pub struct ElfSymbol<'data, 'file, Elf, R = &'data [u8]> +where + 'data: 'file, + Elf: FileHeader, + R: ReadRef<'data>, +{ + pub(super) endian: Elf::Endian, + pub(super) symbols: &'file SymbolTable<'data, Elf, R>, + pub(super) index: SymbolIndex, + pub(super) symbol: &'data Elf::Sym, +} + +impl<'data, 'file, Elf: FileHeader, R: ReadRef<'data>> read::private::Sealed + for ElfSymbol<'data, 'file, Elf, R> +{ +} + +impl<'data, 'file, Elf: FileHeader, R: ReadRef<'data>> ObjectSymbol<'data> + for ElfSymbol<'data, 'file, Elf, R> +{ + #[inline] + fn index(&self) -> SymbolIndex { + self.index + } + + fn name_bytes(&self) -> read::Result<&'data [u8]> { + self.symbol.name(self.endian, self.symbols.strings()) + } + + fn name(&self) -> read::Result<&'data str> { + let name = self.name_bytes()?; + str::from_utf8(name) + .ok() + .read_error("Non UTF-8 ELF symbol name") + } + + #[inline] + fn address(&self) -> u64 { + self.symbol.st_value(self.endian).into() + } + + #[inline] + fn size(&self) -> u64 { + self.symbol.st_size(self.endian).into() + } + + fn kind(&self) -> SymbolKind { + match self.symbol.st_type() { + elf::STT_NOTYPE if self.index.0 == 0 => SymbolKind::Null, + elf::STT_NOTYPE => SymbolKind::Label, + elf::STT_OBJECT | elf::STT_COMMON => SymbolKind::Data, + elf::STT_FUNC | elf::STT_GNU_IFUNC => SymbolKind::Text, + elf::STT_SECTION => SymbolKind::Section, + elf::STT_FILE => SymbolKind::File, + elf::STT_TLS => SymbolKind::Tls, + _ => SymbolKind::Unknown, + } + } + + fn section(&self) -> SymbolSection { + match self.symbol.st_shndx(self.endian) { + elf::SHN_UNDEF => SymbolSection::Undefined, + elf::SHN_ABS => { + if self.symbol.st_type() == elf::STT_FILE { + SymbolSection::None + } else { + SymbolSection::Absolute + } + } + elf::SHN_COMMON => SymbolSection::Common, + elf::SHN_XINDEX => match self.symbols.shndx(self.endian, self.index.0) { + Some(index) => SymbolSection::Section(SectionIndex(index as usize)), + None => SymbolSection::Unknown, + }, + index if index < elf::SHN_LORESERVE => { + SymbolSection::Section(SectionIndex(index as usize)) + } + _ => SymbolSection::Unknown, + } + } + + #[inline] + fn is_undefined(&self) -> bool { + self.symbol.st_shndx(self.endian) == elf::SHN_UNDEF + } + + #[inline] + fn is_definition(&self) -> bool { + self.symbol.is_definition(self.endian) + } + + #[inline] + fn is_common(&self) -> bool { + self.symbol.st_shndx(self.endian) == elf::SHN_COMMON + } + + #[inline] + fn is_weak(&self) -> bool { + self.symbol.st_bind() == elf::STB_WEAK + } + + fn scope(&self) -> SymbolScope { + if self.symbol.st_shndx(self.endian) == elf::SHN_UNDEF { + SymbolScope::Unknown + } else { + match self.symbol.st_bind() { + elf::STB_LOCAL => SymbolScope::Compilation, + elf::STB_GLOBAL | elf::STB_WEAK => { + if self.symbol.st_visibility() == elf::STV_HIDDEN { + SymbolScope::Linkage + } else { + SymbolScope::Dynamic + } + } + _ => SymbolScope::Unknown, + } + } + } + + #[inline] + fn is_global(&self) -> bool { + self.symbol.st_bind() != elf::STB_LOCAL + } + + #[inline] + fn is_local(&self) -> bool { + self.symbol.st_bind() == elf::STB_LOCAL + } + + #[inline] + fn flags(&self) -> SymbolFlags<SectionIndex> { + SymbolFlags::Elf { + st_info: self.symbol.st_info(), + st_other: self.symbol.st_other(), + } + } +} + +/// A trait for generic access to `Sym32` and `Sym64`. +#[allow(missing_docs)] +pub trait Sym: Debug + Pod { + type Word: Into<u64>; + type Endian: endian::Endian; + + fn st_name(&self, endian: Self::Endian) -> u32; + fn st_info(&self) -> u8; + fn st_bind(&self) -> u8; + fn st_type(&self) -> u8; + fn st_other(&self) -> u8; + fn st_visibility(&self) -> u8; + fn st_shndx(&self, endian: Self::Endian) -> u16; + fn st_value(&self, endian: Self::Endian) -> Self::Word; + fn st_size(&self, endian: Self::Endian) -> Self::Word; + + /// Parse the symbol 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.st_name(endian)) + .read_error("Invalid ELF symbol name offset") + } + + /// Return true if the symbol is undefined. + #[inline] + fn is_undefined(&self, endian: Self::Endian) -> bool { + self.st_shndx(endian) == elf::SHN_UNDEF + } + + /// Return true if the symbol is a definition of a function or data object. + fn is_definition(&self, endian: Self::Endian) -> bool { + let st_type = self.st_type(); + (st_type == elf::STT_NOTYPE || st_type == elf::STT_FUNC || st_type == elf::STT_OBJECT) + && self.st_shndx(endian) != elf::SHN_UNDEF + } +} + +impl<Endian: endian::Endian> Sym for elf::Sym32<Endian> { + type Word = u32; + type Endian = Endian; + + #[inline] + fn st_name(&self, endian: Self::Endian) -> u32 { + self.st_name.get(endian) + } + + #[inline] + fn st_info(&self) -> u8 { + self.st_info + } + + #[inline] + fn st_bind(&self) -> u8 { + self.st_bind() + } + + #[inline] + fn st_type(&self) -> u8 { + self.st_type() + } + + #[inline] + fn st_other(&self) -> u8 { + self.st_other + } + + #[inline] + fn st_visibility(&self) -> u8 { + self.st_visibility() + } + + #[inline] + fn st_shndx(&self, endian: Self::Endian) -> u16 { + self.st_shndx.get(endian) + } + + #[inline] + fn st_value(&self, endian: Self::Endian) -> Self::Word { + self.st_value.get(endian) + } + + #[inline] + fn st_size(&self, endian: Self::Endian) -> Self::Word { + self.st_size.get(endian) + } +} + +impl<Endian: endian::Endian> Sym for elf::Sym64<Endian> { + type Word = u64; + type Endian = Endian; + + #[inline] + fn st_name(&self, endian: Self::Endian) -> u32 { + self.st_name.get(endian) + } + + #[inline] + fn st_info(&self) -> u8 { + self.st_info + } + + #[inline] + fn st_bind(&self) -> u8 { + self.st_bind() + } + + #[inline] + fn st_type(&self) -> u8 { + self.st_type() + } + + #[inline] + fn st_other(&self) -> u8 { + self.st_other + } + + #[inline] + fn st_visibility(&self) -> u8 { + self.st_visibility() + } + + #[inline] + fn st_shndx(&self, endian: Self::Endian) -> u16 { + self.st_shndx.get(endian) + } + + #[inline] + fn st_value(&self, endian: Self::Endian) -> Self::Word { + self.st_value.get(endian) + } + + #[inline] + fn st_size(&self, endian: Self::Endian) -> Self::Word { + self.st_size.get(endian) + } +} diff --git a/third_party/rust/object/src/read/elf/version.rs b/third_party/rust/object/src/read/elf/version.rs new file mode 100644 index 0000000000..6d80ba1e35 --- /dev/null +++ b/third_party/rust/object/src/read/elf/version.rs @@ -0,0 +1,421 @@ +use alloc::vec::Vec; + +use crate::read::{Bytes, ReadError, ReadRef, Result, StringTable}; +use crate::{elf, endian}; + +use super::FileHeader; + +/// A version index. +#[derive(Debug, Default, Clone, Copy)] +pub struct VersionIndex(pub u16); + +impl VersionIndex { + /// Return the version index. + pub fn index(&self) -> u16 { + self.0 & elf::VERSYM_VERSION + } + + /// Return true if it is the local index. + pub fn is_local(&self) -> bool { + self.index() == elf::VER_NDX_LOCAL + } + + /// Return true if it is the global index. + pub fn is_global(&self) -> bool { + self.index() == elf::VER_NDX_GLOBAL + } + + /// Return the hidden flag. + pub fn is_hidden(&self) -> bool { + self.0 & elf::VERSYM_HIDDEN != 0 + } +} + +/// A version definition or requirement. +/// +/// This is derived from entries in the `SHT_GNU_verdef` and `SHT_GNU_verneed` sections. +#[derive(Debug, Default, Clone, Copy)] +pub struct Version<'data> { + name: &'data [u8], + hash: u32, + // Used to keep track of valid indices in `VersionTable`. + valid: bool, +} + +impl<'data> Version<'data> { + /// Return the version name. + pub fn name(&self) -> &'data [u8] { + self.name + } + + /// Return hash of the version name. + pub fn hash(&self) -> u32 { + self.hash + } +} + +/// A table of version definitions and requirements. +/// +/// It allows looking up the version information for a given symbol index. +/// +/// This is derived from entries in the `SHT_GNU_versym`, `SHT_GNU_verdef` and `SHT_GNU_verneed` sections. +#[derive(Debug, Clone)] +pub struct VersionTable<'data, Elf: FileHeader> { + symbols: &'data [elf::Versym<Elf::Endian>], + versions: Vec<Version<'data>>, +} + +impl<'data, Elf: FileHeader> Default for VersionTable<'data, Elf> { + fn default() -> Self { + VersionTable { + symbols: &[], + versions: Vec::new(), + } + } +} + +impl<'data, Elf: FileHeader> VersionTable<'data, Elf> { + /// Parse the version sections. + pub fn parse<R: ReadRef<'data>>( + endian: Elf::Endian, + versyms: &'data [elf::Versym<Elf::Endian>], + verdefs: Option<VerdefIterator<'data, Elf>>, + verneeds: Option<VerneedIterator<'data, Elf>>, + strings: StringTable<'data, R>, + ) -> Result<Self> { + let mut max_index = 0; + if let Some(mut verdefs) = verdefs.clone() { + while let Some((verdef, _)) = verdefs.next()? { + if verdef.vd_flags.get(endian) & elf::VER_FLG_BASE != 0 { + continue; + } + let index = verdef.vd_ndx.get(endian) & elf::VERSYM_VERSION; + if max_index < index { + max_index = index; + } + } + } + if let Some(mut verneeds) = verneeds.clone() { + while let Some((_, mut vernauxs)) = verneeds.next()? { + while let Some(vernaux) = vernauxs.next()? { + let index = vernaux.vna_other.get(endian) & elf::VERSYM_VERSION; + if max_index < index { + max_index = index; + } + } + } + } + + // Indices should be sequential, but this could be up to + // 32k * size_of::<Version>() if max_index is bad. + let mut versions = vec![Version::default(); max_index as usize + 1]; + + if let Some(mut verdefs) = verdefs { + while let Some((verdef, mut verdauxs)) = verdefs.next()? { + if verdef.vd_flags.get(endian) & elf::VER_FLG_BASE != 0 { + continue; + } + let index = verdef.vd_ndx.get(endian) & elf::VERSYM_VERSION; + if index <= elf::VER_NDX_GLOBAL { + // TODO: return error? + continue; + } + if let Some(verdaux) = verdauxs.next()? { + versions[usize::from(index)] = Version { + name: verdaux.name(endian, strings)?, + hash: verdef.vd_hash.get(endian), + valid: true, + }; + } + } + } + if let Some(mut verneeds) = verneeds { + while let Some((_, mut vernauxs)) = verneeds.next()? { + while let Some(vernaux) = vernauxs.next()? { + let index = vernaux.vna_other.get(endian) & elf::VERSYM_VERSION; + if index <= elf::VER_NDX_GLOBAL { + // TODO: return error? + continue; + } + versions[usize::from(index)] = Version { + name: vernaux.name(endian, strings)?, + hash: vernaux.vna_hash.get(endian), + valid: true, + }; + } + } + } + + Ok(VersionTable { + symbols: versyms, + versions, + }) + } + + /// Return true if the version table is empty. + pub fn is_empty(&self) -> bool { + self.symbols.is_empty() + } + + /// Return version index for a given symbol index. + pub fn version_index(&self, endian: Elf::Endian, index: usize) -> VersionIndex { + let version_index = match self.symbols.get(index) { + Some(x) => x.0.get(endian), + // Ideally this would be VER_NDX_LOCAL for undefined symbols, + // but currently there are no checks that need this distinction. + None => elf::VER_NDX_GLOBAL, + }; + VersionIndex(version_index) + } + + /// Return version information for a given symbol version index. + /// + /// Returns `Ok(None)` for local and global versions. + /// Returns `Err(_)` if index is invalid. + pub fn version(&self, index: VersionIndex) -> Result<Option<&Version<'data>>> { + if index.index() <= elf::VER_NDX_GLOBAL { + return Ok(None); + } + self.versions + .get(usize::from(index.index())) + .filter(|version| version.valid) + .read_error("Invalid ELF symbol version index") + .map(Some) + } + + /// Return true if the given symbol index satisifies the requirements of `need`. + /// + /// Returns false for any error. + /// + /// Note: this function hasn't been fully tested and is likely to be incomplete. + pub fn matches(&self, endian: Elf::Endian, index: usize, need: Option<&Version>) -> bool { + let version_index = self.version_index(endian, index); + let def = match self.version(version_index) { + Ok(def) => def, + Err(_) => return false, + }; + match (def, need) { + (Some(def), Some(need)) => need.hash == def.hash && need.name == def.name, + (None, Some(_need)) => { + // Version must be present if needed. + false + } + (Some(_def), None) => { + // For a dlsym call, use the newest version. + // TODO: if not a dlsym call, then use the oldest version. + !version_index.is_hidden() + } + (None, None) => true, + } + } +} + +/// An iterator over the entries in an ELF `SHT_GNU_verdef` section. +#[derive(Debug, Clone)] +pub struct VerdefIterator<'data, Elf: FileHeader> { + endian: Elf::Endian, + data: Bytes<'data>, +} + +impl<'data, Elf: FileHeader> VerdefIterator<'data, Elf> { + pub(super) fn new(endian: Elf::Endian, data: &'data [u8]) -> Self { + VerdefIterator { + endian, + data: Bytes(data), + } + } + + /// Return the next `Verdef` entry. + pub fn next( + &mut self, + ) -> Result<Option<(&'data elf::Verdef<Elf::Endian>, VerdauxIterator<'data, Elf>)>> { + if self.data.is_empty() { + return Ok(None); + } + + let verdef = self + .data + .read_at::<elf::Verdef<_>>(0) + .read_error("ELF verdef is too short")?; + + let mut verdaux_data = self.data; + verdaux_data + .skip(verdef.vd_aux.get(self.endian) as usize) + .read_error("Invalid ELF vd_aux")?; + let verdaux = + VerdauxIterator::new(self.endian, verdaux_data.0, verdef.vd_cnt.get(self.endian)); + + let next = verdef.vd_next.get(self.endian); + if next != 0 { + self.data + .skip(next as usize) + .read_error("Invalid ELF vd_next")?; + } else { + self.data = Bytes(&[]); + } + Ok(Some((verdef, verdaux))) + } +} + +/// An iterator over the auxiliary records for an entry in an ELF `SHT_GNU_verdef` section. +#[derive(Debug, Clone)] +pub struct VerdauxIterator<'data, Elf: FileHeader> { + endian: Elf::Endian, + data: Bytes<'data>, + count: u16, +} + +impl<'data, Elf: FileHeader> VerdauxIterator<'data, Elf> { + pub(super) fn new(endian: Elf::Endian, data: &'data [u8], count: u16) -> Self { + VerdauxIterator { + endian, + data: Bytes(data), + count, + } + } + + /// Return the next `Verdaux` entry. + pub fn next(&mut self) -> Result<Option<&'data elf::Verdaux<Elf::Endian>>> { + if self.count == 0 { + return Ok(None); + } + + let verdaux = self + .data + .read_at::<elf::Verdaux<_>>(0) + .read_error("ELF verdaux is too short")?; + + self.data + .skip(verdaux.vda_next.get(self.endian) as usize) + .read_error("Invalid ELF vda_next")?; + self.count -= 1; + Ok(Some(verdaux)) + } +} + +/// An iterator over the entries in an ELF `SHT_GNU_verneed` section. +#[derive(Debug, Clone)] +pub struct VerneedIterator<'data, Elf: FileHeader> { + endian: Elf::Endian, + data: Bytes<'data>, +} + +impl<'data, Elf: FileHeader> VerneedIterator<'data, Elf> { + pub(super) fn new(endian: Elf::Endian, data: &'data [u8]) -> Self { + VerneedIterator { + endian, + data: Bytes(data), + } + } + + /// Return the next `Verneed` entry. + pub fn next( + &mut self, + ) -> Result< + Option<( + &'data elf::Verneed<Elf::Endian>, + VernauxIterator<'data, Elf>, + )>, + > { + if self.data.is_empty() { + return Ok(None); + } + + let verneed = self + .data + .read_at::<elf::Verneed<_>>(0) + .read_error("ELF verneed is too short")?; + + let mut vernaux_data = self.data; + vernaux_data + .skip(verneed.vn_aux.get(self.endian) as usize) + .read_error("Invalid ELF vn_aux")?; + let vernaux = + VernauxIterator::new(self.endian, vernaux_data.0, verneed.vn_cnt.get(self.endian)); + + let next = verneed.vn_next.get(self.endian); + if next != 0 { + self.data + .skip(next as usize) + .read_error("Invalid ELF vn_next")?; + } else { + self.data = Bytes(&[]); + } + Ok(Some((verneed, vernaux))) + } +} + +/// An iterator over the auxiliary records for an entry in an ELF `SHT_GNU_verneed` section. +#[derive(Debug, Clone)] +pub struct VernauxIterator<'data, Elf: FileHeader> { + endian: Elf::Endian, + data: Bytes<'data>, + count: u16, +} + +impl<'data, Elf: FileHeader> VernauxIterator<'data, Elf> { + pub(super) fn new(endian: Elf::Endian, data: &'data [u8], count: u16) -> Self { + VernauxIterator { + endian, + data: Bytes(data), + count, + } + } + + /// Return the next `Vernaux` entry. + pub fn next(&mut self) -> Result<Option<&'data elf::Vernaux<Elf::Endian>>> { + if self.count == 0 { + return Ok(None); + } + + let vernaux = self + .data + .read_at::<elf::Vernaux<_>>(0) + .read_error("ELF vernaux is too short")?; + + self.data + .skip(vernaux.vna_next.get(self.endian) as usize) + .read_error("Invalid ELF vna_next")?; + self.count -= 1; + Ok(Some(vernaux)) + } +} + +impl<Endian: endian::Endian> elf::Verdaux<Endian> { + /// Parse the version name from the string table. + pub fn name<'data, R: ReadRef<'data>>( + &self, + endian: Endian, + strings: StringTable<'data, R>, + ) -> Result<&'data [u8]> { + strings + .get(self.vda_name.get(endian)) + .read_error("Invalid ELF vda_name") + } +} + +impl<Endian: endian::Endian> elf::Verneed<Endian> { + /// Parse the file from the string table. + pub fn file<'data, R: ReadRef<'data>>( + &self, + endian: Endian, + strings: StringTable<'data, R>, + ) -> Result<&'data [u8]> { + strings + .get(self.vn_file.get(endian)) + .read_error("Invalid ELF vn_file") + } +} + +impl<Endian: endian::Endian> elf::Vernaux<Endian> { + /// Parse the version name from the string table. + pub fn name<'data, R: ReadRef<'data>>( + &self, + endian: Endian, + strings: StringTable<'data, R>, + ) -> Result<&'data [u8]> { + strings + .get(self.vna_name.get(endian)) + .read_error("Invalid ELF vna_name") + } +} |