elf_loader 0.16.0

A no_std-friendly ELF loader and runtime linker for Rust.
Documentation
use super::lifecycle::{Finalizer, FiniEvent};
use crate::{
    Result,
    arch::NativeArch,
    elf::{ElfRelEntry, ElfRelType, ElfSymbol, HashTable, Lifecycle, SymbolEntry, SymbolTableView},
    image::{ElfCore, ModuleScope},
    input::Path,
    memory::{HostRegion, RegionAccess, VmAddr},
    relocation::{RelocationArch, SymDef, find_symdef_impl},
    tls::TlsResolver,
};

/// Result of a relocation hook.
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum HandleResult {
    /// The handler did not process this relocation.
    Unhandled,
    /// The handler processed this relocation.
    Handled,
}

impl HandleResult {
    /// Returns whether the handler left the relocation for the default path.
    #[inline]
    pub const fn is_unhandled(self) -> bool {
        matches!(self, Self::Unhandled)
    }
}

/// Context passed to relocation observer hooks.
///
/// This struct provides access to the relocation entry, the module being relocated,
/// and the current symbol resolution scope.
pub struct RelocationEvent<
    'a,
    D: 'static,
    Arch: RelocationArch = NativeArch,
    R: RegionAccess = HostRegion,
    Tls: TlsResolver<Arch> = (),
    H = HashTable<<Arch as RelocationArch>::Layout>,
> {
    rel: &'a ElfRelType<Arch>,
    lib: &'a ElfCore<D, Arch, R, Tls>,
    symbols: SymbolTableView<'a, Arch::Layout, H>,
    scope: &'a ModuleScope<Arch, Tls>,
}

impl<'a, D: 'static, Arch: RelocationArch, R: RegionAccess, Tls: TlsResolver<Arch>, H>
    RelocationEvent<'a, D, Arch, R, Tls, H>
{
    /// Construct a new `RelocationEvent`.
    #[inline]
    pub(crate) fn new(
        rel: &'a ElfRelType<Arch>,
        lib: &'a ElfCore<D, Arch, R, Tls>,
        symbols: SymbolTableView<'a, Arch::Layout, H>,
        scope: &'a ModuleScope<Arch, Tls>,
    ) -> Self {
        Self {
            rel,
            lib,
            symbols,
            scope,
        }
    }

    /// Access the relocation entry.
    #[inline]
    pub fn rel(&self) -> &ElfRelType<Arch> {
        self.rel
    }

    /// Access the core component where the relocation appears.
    #[inline]
    pub fn lib(&self) -> &ElfCore<D, Arch, R, Tls> {
        self.lib
    }

    /// Access the current resolution scope.
    #[inline]
    pub fn scope(&self) -> &ModuleScope<Arch, Tls> {
        self.scope
    }

    /// Access a symbol table entry by index for this relocation context.
    #[inline]
    pub fn symbol(&self, r_sym: usize) -> SymbolEntry<'a, Arch::Layout> {
        self.symbols.symbol_idx(r_sym)
    }

    /// Access the symbol referenced by the current relocation, if it has one.
    #[inline]
    pub fn relocation_symbol(&self) -> Option<SymbolEntry<'a, Arch::Layout>> {
        let r_sym = self.rel.r_symbol();
        (r_sym != 0).then(|| self.symbol(r_sym))
    }

    /// Find symbol definition in the current scope.
    #[inline]
    pub fn find_symdef(&self, r_sym: usize) -> Option<SymDef<'a, Arch, Tls>> {
        let symbol = self.symbol(r_sym);
        find_symdef_impl(
            self.lib,
            self.scope,
            symbol.symbol(),
            symbol.info(),
            self.lib.symbolic(),
        )
    }
}

/// Ordinary symbol relocation binding event.
///
/// Observers may inspect the requested symbol and override the resolved address.
pub struct SymbolBindingEvent<
    'a,
    D: 'static,
    Arch: RelocationArch = NativeArch,
    R: RegionAccess = HostRegion,
    Tls: TlsResolver<Arch> = (),
> {
    core: &'a ElfCore<D, Arch, R, Tls>,
    rel: Option<&'a ElfRelType<Arch>>,
    symbol: &'a ElfSymbol<Arch::Layout>,
    symbol_name: &'a str,
    resolved: Option<VmAddr>,
}

impl<'a, D: 'static, Arch: RelocationArch, R: RegionAccess, Tls: TlsResolver<Arch>>
    SymbolBindingEvent<'a, D, Arch, R, Tls>
{
    #[inline]
    pub(crate) const fn new(
        core: &'a ElfCore<D, Arch, R, Tls>,
        rel: Option<&'a ElfRelType<Arch>>,
        symbol: &'a ElfSymbol<Arch::Layout>,
        symbol_name: &'a str,
        resolved: Option<VmAddr>,
    ) -> Self {
        Self {
            core,
            rel,
            symbol,
            symbol_name,
            resolved,
        }
    }

    /// Returns the image core associated with this binding.
    #[inline]
    pub const fn core(&self) -> &ElfCore<D, Arch, R, Tls> {
        self.core
    }

    /// Returns the relocation entry that requested this binding, when the
    /// binding is tied to one concrete relocation.
    #[inline]
    pub const fn rel(&self) -> Option<&ElfRelType<Arch>> {
        self.rel
    }

    /// Returns the symbol table entry referenced by the relocation.
    #[inline]
    pub const fn symbol(&self) -> &ElfSymbol<Arch::Layout> {
        self.symbol
    }

    /// Returns the symbol name referenced by the relocation.
    #[inline]
    pub const fn symbol_name(&self) -> &'a str {
        self.symbol_name
    }

    /// Returns the currently resolved address, if any.
    #[inline]
    pub const fn resolved_addr(&self) -> Option<VmAddr> {
        self.resolved
    }

    /// Sets the resolved address.
    #[inline]
    pub fn set_resolved_addr(&mut self, addr: VmAddr) {
        self.resolved = Some(addr);
    }

    /// Clears the resolved address.
    #[inline]
    pub fn clear_resolved_addr(&mut self) {
        self.resolved = None;
    }

    #[inline]
    pub(crate) const fn into_resolved_addr(self) -> Option<VmAddr> {
        self.resolved
    }
}

/// Event emitted after a dynamic image has been relocated.
pub struct DynamicRelocatedEvent<
    'a,
    D: 'static,
    Arch: RelocationArch = NativeArch,
    R: RegionAccess = HostRegion,
    Tls: TlsResolver<Arch> = (),
> {
    core: &'a ElfCore<D, Arch, R, Tls>,
    dynamic_addr: VmAddr,
    finalizer: Finalizer,
}

impl<'a, D: 'static, Arch: RelocationArch, R: RegionAccess, Tls: TlsResolver<Arch>>
    DynamicRelocatedEvent<'a, D, Arch, R, Tls>
{
    #[inline]
    pub(crate) const fn new(
        core: &'a ElfCore<D, Arch, R, Tls>,
        dynamic_addr: VmAddr,
        finalizer: Finalizer,
    ) -> Self {
        Self {
            core,
            dynamic_addr,
            finalizer,
        }
    }

    /// Returns the image core associated with this event.
    #[inline]
    pub const fn core(&self) -> &ElfCore<D, Arch, R, Tls> {
        self.core
    }

    /// Returns the loader source path or caller-provided source identifier.
    #[inline]
    pub fn path(&self) -> &Path {
        self.core.path()
    }

    /// Returns the module identity used for diagnostics.
    #[inline]
    pub fn name(&self) -> &str {
        self.core.name()
    }

    /// Returns the load base used by this image.
    #[inline]
    pub fn base(&self) -> VmAddr {
        self.core.base()
    }

    /// Returns the runtime address of the first dynamic entry.
    #[inline]
    pub const fn dynamic_addr(&self) -> VmAddr {
        self.dynamic_addr
    }

    /// Returns the finalization lifecycle that will be run when the initialized
    /// image is dropped.
    #[inline]
    pub fn fini(&self) -> &Lifecycle {
        self.finalizer.lifecycle()
    }

    /// Returns mutable finalization lifecycle addresses.
    #[inline]
    pub fn fini_mut(&mut self) -> &mut Lifecycle {
        self.finalizer.lifecycle_mut()
    }

    /// Installs a hook that runs immediately before finalization functions.
    #[inline]
    pub fn set_fini_hook<F>(&mut self, hook: F)
    where
        F: for<'event> Fn(&mut FiniEvent<'event>) -> Result<()> + Send + Sync + 'static,
    {
        self.finalizer.set_hook(hook);
    }

    #[inline]
    pub(crate) fn into_finalizer(self) -> Finalizer {
        self.finalizer
    }
}