use super::consts::{
SHN_ABS, SHN_COMMON, SHN_LORESERVE, SHN_UNDEF, SHN_XINDEX, STB_GLOBAL, STB_GNU_UNIQUE,
STB_LOCAL, STB_WEAK, STT_COMMON, STT_FILE, STT_FUNC, STT_GNU_IFUNC, STT_SECTION, STT_TLS,
};
use super::format::{ElfFormat, Endian, RawRecord};
use super::source::{Source, entry_offset};
use super::strtab::StringTable;
use crate::error::Result;
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
pub struct RawSymbol {
pub st_name: u32,
pub st_info: u8,
pub st_other: u8,
pub st_shndx: u16,
pub st_value: u64,
pub st_size: u64,
}
impl RawSymbol {
#[inline]
#[must_use]
pub fn binding(&self) -> u8 {
self.st_info >> 4
}
#[inline]
#[must_use]
pub fn kind(&self) -> u8 {
self.st_info & 0xf
}
#[inline]
#[must_use]
pub fn visibility(&self) -> u8 {
self.st_other & 0x3
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub enum SectionIndex {
Undefined,
Absolute,
Common,
Section(u32),
Reserved(u16),
}
impl SectionIndex {
#[inline]
#[must_use]
pub fn section(self) -> Option<u32> {
match self {
Self::Section(index) => Some(index),
_ => None,
}
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct Symbol<'a> {
pub name: &'a [u8],
pub value: u64,
pub size: u64,
pub info: u8,
pub other: u8,
pub section: SectionIndex,
}
impl Symbol<'_> {
#[inline]
#[must_use]
pub fn binding(&self) -> u8 {
self.info >> 4
}
#[inline]
#[must_use]
pub fn kind(&self) -> u8 {
self.info & 0xf
}
#[inline]
#[must_use]
pub fn visibility(&self) -> u8 {
self.other & 0x3
}
#[inline]
#[must_use]
pub fn is_local(&self) -> bool {
self.binding() == STB_LOCAL
}
#[inline]
#[must_use]
pub fn is_global(&self) -> bool {
matches!(self.binding(), STB_GLOBAL | STB_GNU_UNIQUE)
}
#[inline]
#[must_use]
pub fn is_weak(&self) -> bool {
self.binding() == STB_WEAK
}
#[inline]
#[must_use]
pub fn is_undefined(&self) -> bool {
self.section == SectionIndex::Undefined
}
#[inline]
#[must_use]
pub fn is_common(&self) -> bool {
self.section == SectionIndex::Common || self.kind() == STT_COMMON
}
#[inline]
#[must_use]
pub fn is_function(&self) -> bool {
matches!(self.kind(), STT_FUNC | STT_GNU_IFUNC)
}
#[inline]
#[must_use]
pub fn is_tls(&self) -> bool {
self.kind() == STT_TLS
}
#[inline]
#[must_use]
pub fn is_section(&self) -> bool {
self.kind() == STT_SECTION
}
#[inline]
#[must_use]
pub fn is_file(&self) -> bool {
self.kind() == STT_FILE
}
}
#[derive(Debug)]
pub struct SymbolTable<'a, F: ElfFormat> {
raw: &'a [F::Sym],
strtab: StringTable<'a>,
shndx: &'a [[u8; 4]],
first_global: usize,
section_index: u32,
file_offset: u64,
source: Source<'a>,
}
impl<F: ElfFormat> Clone for SymbolTable<'_, F> {
fn clone(&self) -> Self {
*self
}
}
impl<F: ElfFormat> Copy for SymbolTable<'_, F> {}
impl<'a, F: ElfFormat> SymbolTable<'a, F> {
#[must_use]
pub fn empty(source: Source<'a>) -> Self {
Self {
raw: &[],
strtab: StringTable::default(),
shndx: &[],
first_global: 0,
section_index: 0,
file_offset: 0,
source,
}
}
#[must_use]
pub fn new(
raw: &'a [F::Sym],
strtab: StringTable<'a>,
shndx: &'a [[u8; 4]],
first_global: u32,
section_index: u32,
file_offset: u64,
source: Source<'a>,
) -> Self {
let first_global = usize::try_from(first_global)
.unwrap_or(usize::MAX)
.min(raw.len());
Self {
raw,
strtab,
shndx,
first_global,
section_index,
file_offset,
source,
}
}
#[inline]
#[must_use]
pub fn len(&self) -> usize {
self.raw.len()
}
#[inline]
#[must_use]
pub fn is_empty(&self) -> bool {
self.raw.is_empty()
}
#[inline]
#[must_use]
pub fn first_global(&self) -> usize {
self.first_global
}
#[inline]
#[must_use]
pub fn section_index(&self) -> u32 {
self.section_index
}
#[inline]
#[must_use]
pub fn strtab(&self) -> StringTable<'a> {
self.strtab
}
#[inline]
#[must_use]
pub fn raw(&self) -> &'a [F::Sym] {
self.raw
}
#[inline]
#[must_use]
pub fn get_raw(&self, index: usize) -> Option<RawSymbol> {
self.raw.get(index).map(F::decode_sym)
}
#[inline]
pub fn get(&self, index: usize) -> Result<Symbol<'a>> {
let raw = self.get_raw(index).ok_or_else(|| {
self.source.malformed(
self.file_offset,
format!("symbol index {index} (out of range)"),
)
})?;
self.resolve(index, &raw)
}
#[inline]
pub fn name(&self, index: usize, raw: &RawSymbol) -> Result<&'a [u8]> {
self.strtab
.get(raw.st_name)
.ok_or_else(|| self.error(index, "symbol name offset"))
}
#[inline]
pub fn section(&self, index: usize, raw: &RawSymbol) -> Result<SectionIndex> {
Ok(match raw.st_shndx {
SHN_UNDEF => SectionIndex::Undefined,
SHN_ABS => SectionIndex::Absolute,
SHN_COMMON => SectionIndex::Common,
SHN_XINDEX => {
let entry = self
.shndx
.get(index)
.ok_or_else(|| self.error(index, "extended section index (missing)"))?;
SectionIndex::Section(F::Endian::u32(*entry))
}
n if n >= SHN_LORESERVE => SectionIndex::Reserved(n),
n => SectionIndex::Section(u32::from(n)),
})
}
#[inline]
#[must_use]
pub fn section_of(&self, index: usize, raw: &RawSymbol) -> Option<SectionIndex> {
Some(match raw.st_shndx {
SHN_UNDEF => SectionIndex::Undefined,
SHN_ABS => SectionIndex::Absolute,
SHN_COMMON => SectionIndex::Common,
SHN_XINDEX => SectionIndex::Section(F::Endian::u32(*self.shndx.get(index)?)),
n if n >= SHN_LORESERVE => SectionIndex::Reserved(n),
n => SectionIndex::Section(u32::from(n)),
})
}
#[inline]
pub fn resolve(&self, index: usize, raw: &RawSymbol) -> Result<Symbol<'a>> {
Ok(Symbol {
name: self.name(index, raw)?,
value: raw.st_value,
size: raw.st_size,
info: raw.st_info,
other: raw.st_other,
section: self.section(index, raw)?,
})
}
#[must_use]
pub fn iter(&self) -> SymbolIter<'a, F> {
SymbolIter {
table: *self,
index: 0,
}
}
#[must_use]
pub fn globals(&self) -> SymbolIter<'a, F> {
SymbolIter {
table: *self,
index: self.first_global,
}
}
pub fn iter_raw(&self) -> impl ExactSizeIterator<Item = RawSymbol> + use<'a, F> {
self.raw.iter().map(F::decode_sym)
}
#[cold]
fn error(&self, index: usize, what: &str) -> crate::Error {
self.source.malformed(
entry_offset(self.file_offset, index, F::Sym::SIZE),
format!("{what} (symbol {index})"),
)
}
}
#[derive(Debug)]
pub struct SymbolIter<'a, F: ElfFormat> {
table: SymbolTable<'a, F>,
index: usize,
}
impl<F: ElfFormat> Clone for SymbolIter<'_, F> {
fn clone(&self) -> Self {
Self {
table: self.table,
index: self.index,
}
}
}
impl<'a, F: ElfFormat> SymbolIter<'a, F> {
#[must_use]
pub fn index(&self) -> usize {
self.index
}
}
impl<'a, F: ElfFormat> Iterator for SymbolIter<'a, F> {
type Item = Result<Symbol<'a>>;
#[inline]
fn next(&mut self) -> Option<Self::Item> {
let index = self.index;
let raw = F::decode_sym(self.table.raw.get(index)?);
self.index = index.saturating_add(1);
Some(self.table.resolve(index, &raw))
}
#[inline]
fn size_hint(&self) -> (usize, Option<usize>) {
let n = self.table.raw.len().saturating_sub(self.index);
(n, Some(n))
}
}
impl<F: ElfFormat> ExactSizeIterator for SymbolIter<'_, F> {}