elf_loader 0.16.0

A no_std-friendly ELF loader and runtime linker for Rust.
Documentation
//! ELF symbol hash table implementations
//!
//! This module provides implementations for different ELF symbol hash table formats,
//! including the traditional SYSV hash table, the GNU hash table, and a custom hash
//! implementation. These hash tables are used to efficiently locate symbols during
//! the dynamic linking process.
//!
//! The GNU hash table (.gnu.hash) is generally preferred over the traditional
//! SYSV hash table (.hash) as it provides better performance and memory usage.

use crate::elf::{
    ElfDynamic, ElfDynamicHashTab, ElfLayout, ElfSymbol, SymbolLookup, SymbolTableView,
};
use crate::{Result, memory::RegionAccess, segment::ElfSegments};
use core::fmt::Debug;
use gnu::ElfGnuHash;
use sysv::ElfHash;

mod gnu;
mod sysv;

pub trait SymbolHash<L: ElfLayout> {
    fn lookup<'sym, H>(
        &self,
        table: SymbolTableView<'sym, L, H>,
        lookup: &mut SymbolLookup<'_>,
    ) -> Option<&'sym ElfSymbol<L>>;
}

/// Standard dynamic ELF symbol hash table.
///
/// Dynamic ELF files may carry either a GNU hash table or the traditional SYSV
/// hash table. Both represent the same role in this loader, so the distinction
/// is kept inside this implementation detail.
pub struct HashTable<L: ElfLayout = crate::elf::NativeElfLayout>(HashTableKind<L>);

enum HashTableKind<L: ElfLayout = crate::elf::NativeElfLayout> {
    /// GNU hash table (.gnu.hash section).
    Gnu(ElfGnuHash<L>),

    /// Traditional SYSV hash table (.hash section).
    Sysv(ElfHash),
}

impl<L: ElfLayout> Clone for HashTable<L> {
    #[inline]
    fn clone(&self) -> Self {
        Self(self.0.clone())
    }
}

impl<L: ElfLayout> Clone for HashTableKind<L> {
    #[inline]
    fn clone(&self) -> Self {
        match self {
            Self::Gnu(hashtab) => Self::Gnu(hashtab.clone()),
            Self::Sysv(hashtab) => Self::Sysv(hashtab.clone()),
        }
    }
}

impl<L: ElfLayout> Debug for HashTable<L> {
    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
        match &self.0 {
            HashTableKind::Gnu(_) => write!(f, "GnuHash"),
            HashTableKind::Sysv(_) => write!(f, "ElfHash"),
        }
    }
}

/// Precomputed hash values for symbol lookup optimization.
///
/// This structure holds precomputed hash values and related data that can
/// be used to speed up symbol lookups in hash tables. Precomputing these
/// values avoids repeated calculations during the lookup process.
pub(in crate::elf) struct PreCompute {
    /// GNU hash value for the symbol name
    pub(in crate::elf) gnuhash: u32,

    /// Traditional hash value (used for SYSV hash tables)
    pub(in crate::elf) hash: Option<u32>,

    #[cfg(feature = "object")]
    pub(in crate::elf) custom: Option<u64>,
}

impl PreCompute {
    #[inline]
    pub(in crate::elf) fn new(name: &str) -> Self {
        let gnuhash = ElfGnuHash::<crate::elf::NativeElfLayout>::hash(name.as_bytes()) as u32;
        Self {
            gnuhash,
            hash: None,
            #[cfg(feature = "object")]
            custom: None,
        }
    }
}

impl<L: ElfLayout> HashTable<L> {
    /// Create a hash table from dynamic section information.
    ///
    /// This method creates a hash table based on the information in the
    /// ELF dynamic section. The type of hash table created depends on
    /// what hash sections are referenced in the dynamic section.
    ///
    /// # Arguments
    /// * `dynamic` - The ELF dynamic section information.
    ///
    /// # Returns
    /// A HashTable instance containing either a GNU or SYSV hash implementation.
    pub(crate) fn from_dynamic<Arch, R>(
        dynamic: &ElfDynamic<Arch>,
        segments: &ElfSegments<R>,
    ) -> Result<Self>
    where
        Arch: crate::relocation::RelocationArch<Layout = L>,
        R: RegionAccess,
    {
        Ok(Self(match dynamic.hashtab {
            ElfDynamicHashTab::Gnu(addr) => HashTableKind::Gnu(ElfGnuHash::parse(segments, addr)?),
            ElfDynamicHashTab::Elf(addr) => HashTableKind::Sysv(ElfHash::parse(segments, addr)?),
        }))
    }

    /// Returns the number of symbols covered by the parsed hash table.
    #[inline]
    pub fn count_syms(&self) -> usize {
        match &self.0 {
            HashTableKind::Gnu(hashtab) => hashtab.count_syms(),
            HashTableKind::Sysv(hashtab) => hashtab.count_syms(),
        }
    }
}

impl<L: ElfLayout> SymbolHash<L> for HashTable<L> {
    fn lookup<'sym, H>(
        &self,
        table: SymbolTableView<'sym, L, H>,
        lookup: &mut SymbolLookup<'_>,
    ) -> Option<&'sym ElfSymbol<L>> {
        match &self.0 {
            HashTableKind::Gnu(hashtab) => hashtab.lookup(table, lookup),
            HashTableKind::Sysv(hashtab) => hashtab.lookup(table, lookup),
        }
    }
}