| use alloc::fmt; |
| use alloc::vec::Vec; |
| use core::fmt::Debug; |
| use core::slice; |
| use core::str; |
| |
| use crate::elf; |
| use crate::endian::{self, Endianness, U32}; |
| 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 super::{FileHeader, SectionHeader, SectionTable}; |
| |
| /// A table of symbol entries in an ELF file. |
| /// |
| /// Also includes the string table used for the symbol names. |
| /// |
| /// Returned by [`SectionTable::symbols`]. |
| #[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 in an [`ElfFile32`](super::ElfFile32). |
| pub type ElfSymbolTable32<'data, 'file, Endian = Endianness, R = &'data [u8]> = |
| ElfSymbolTable<'data, 'file, elf::FileHeader32<Endian>, R>; |
| /// A symbol table in an [`ElfFile32`](super::ElfFile32). |
| pub type ElfSymbolTable64<'data, 'file, Endian = Endianness, R = &'data [u8]> = |
| ElfSymbolTable<'data, 'file, elf::FileHeader64<Endian>, R>; |
| |
| /// A symbol table in an [`ElfFile`](super::ElfFile). |
| #[derive(Debug, Clone, Copy)] |
| pub struct ElfSymbolTable<'data, 'file, Elf, R = &'data [u8]> |
| where |
| 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 for the symbols in an [`ElfFile32`](super::ElfFile32). |
| pub type ElfSymbolIterator32<'data, 'file, Endian = Endianness, R = &'data [u8]> = |
| ElfSymbolIterator<'data, 'file, elf::FileHeader32<Endian>, R>; |
| /// An iterator for the symbols in an [`ElfFile64`](super::ElfFile64). |
| pub type ElfSymbolIterator64<'data, 'file, Endian = Endianness, R = &'data [u8]> = |
| ElfSymbolIterator<'data, 'file, elf::FileHeader64<Endian>, R>; |
| |
| /// An iterator for the symbols in an [`ElfFile`](super::ElfFile). |
| pub struct ElfSymbolIterator<'data, 'file, Elf, R = &'data [u8]> |
| where |
| 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 in an [`ElfFile32`](super::ElfFile32). |
| pub type ElfSymbol32<'data, 'file, Endian = Endianness, R = &'data [u8]> = |
| ElfSymbol<'data, 'file, elf::FileHeader32<Endian>, R>; |
| /// A symbol in an [`ElfFile64`](super::ElfFile64). |
| pub type ElfSymbol64<'data, 'file, Endian = Endianness, R = &'data [u8]> = |
| ElfSymbol<'data, 'file, elf::FileHeader64<Endian>, R>; |
| |
| /// A symbol in an [`ElfFile`](super::ElfFile). |
| /// |
| /// Most functionality is provided by the [`ObjectSymbol`] trait implementation. |
| #[derive(Debug, Clone, Copy)] |
| pub struct ElfSymbol<'data, 'file, Elf, R = &'data [u8]> |
| where |
| 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>> ElfSymbol<'data, 'file, Elf, R> { |
| /// Return a reference to the raw symbol structure. |
| #[inline] |
| pub fn raw_symbol(&self) -> &'data Elf::Sym { |
| self.symbol |
| } |
| } |
| |
| 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::Unknown, |
| 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, SymbolIndex> { |
| SymbolFlags::Elf { |
| st_info: self.symbol.st_info(), |
| st_other: self.symbol.st_other(), |
| } |
| } |
| } |
| |
| /// A trait for generic access to [`elf::Sym32`] and [`elf::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 shndx = self.st_shndx(endian); |
| if shndx == elf::SHN_UNDEF || (shndx >= elf::SHN_LORESERVE && shndx != elf::SHN_XINDEX) { |
| return false; |
| } |
| match self.st_type() { |
| elf::STT_NOTYPE => self.st_size(endian).into() != 0, |
| elf::STT_FUNC | elf::STT_OBJECT => true, |
| _ => false, |
| } |
| } |
| } |
| |
| 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) |
| } |
| } |