summaryrefslogtreecommitdiffstats
path: root/third_party/rust/object/src/read/elf
diff options
context:
space:
mode:
Diffstat (limited to 'third_party/rust/object/src/read/elf')
-rw-r--r--third_party/rust/object/src/read/elf/comdat.rs162
-rw-r--r--third_party/rust/object/src/read/elf/compression.rs56
-rw-r--r--third_party/rust/object/src/read/elf/dynamic.rs117
-rw-r--r--third_party/rust/object/src/read/elf/file.rs885
-rw-r--r--third_party/rust/object/src/read/elf/hash.rs220
-rw-r--r--third_party/rust/object/src/read/elf/mod.rs39
-rw-r--r--third_party/rust/object/src/read/elf/note.rs185
-rw-r--r--third_party/rust/object/src/read/elf/relocation.rs571
-rw-r--r--third_party/rust/object/src/read/elf/section.rs1090
-rw-r--r--third_party/rust/object/src/read/elf/segment.rs333
-rw-r--r--third_party/rust/object/src/read/elf/symbol.rs580
-rw-r--r--third_party/rust/object/src/read/elf/version.rs421
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(&section_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")
+ }
+}