elf_loader 0.16.0

A no_std-friendly ELF loader and runtime linker for Rust.
Documentation
use crate::{
    LinkerError, RelocReason, Result,
    elf::{
        ElfRelType, ElfSectionId, ElfSectionIndex, ElfSectionType, ElfShdr, ElfSymbol,
        ElfSymbolType,
    },
    image::{LoadedCore, LoadedObject, ModuleScope, RawObject, exports_handle},
    lazy::LazyBinder,
    logging,
    memory::{RegionAccess, VmAddr, VmOffset},
    object::{ObjectExports, ObjectSegmentView, section_entries},
    observer::{
        Finalizer, InitEvent, ObjectRelocatedEvent, RelocationObserver, SymbolBindingEvent,
    },
    relocate_context_error,
    relocation::{ObjectArch, RelocHelper, RelocateArgs, find_symdef_impl},
    tls::TlsResolver,
};

pub(crate) fn object_relocation_sections<Arch>(
    shdrs: &[ElfShdr<Arch::Layout>],
) -> impl Iterator<
    Item = (
        ElfSectionId,
        ElfSectionId,
        &ElfShdr<Arch::Layout>,
        &ElfShdr<Arch::Layout>,
    ),
> + '_
where
    Arch: ObjectArch,
{
    shdrs
        .iter()
        .enumerate()
        .filter(|shdr| {
            matches!(
                shdr.1.section_type(),
                ElfSectionType::REL | ElfSectionType::RELA
            )
        })
        .map(move |(relocation_index, relocation_shdr)| {
            let target_id = ElfSectionId::new(relocation_shdr.sh_info() as usize);
            let relocation_id = ElfSectionId::new(relocation_index);
            let target = &shdrs[target_id.index()];
            (target_id, relocation_id, target, relocation_shdr)
        })
}

impl<D: 'static, Arch, R, Tls> RawObject<D, Arch, R, Tls>
where
    Arch: ObjectArch,
    R: RegionAccess,
    Tls: TlsResolver<Arch>,
{
    pub(crate) fn relocate_impl<Obs, Binder>(
        mut self,
        args: RelocateArgs<'_, Arch, Tls, Obs, Binder>,
    ) -> Result<LoadedObject<D, Arch, R, Tls>>
    where
        Obs: RelocationObserver<Arch> + ?Sized,
        Binder: LazyBinder<Arch> + ?Sized,
    {
        logging::debug!("Relocating object: {}", self.core.name());
        let RelocateArgs {
            scope, observer, ..
        } = args;
        self.simplify_symbols(&scope, observer)?;

        let relocation_segments =
            ObjectSegmentView::new(self.core.segments(), self.init_segments.as_ref());
        let mut helper = RelocHelper::new(
            &self.core,
            self.symtab.view(),
            relocation_segments,
            scope,
            observer,
        );
        let shdrs = self.sections.headers();
        let mut state = Arch::State::default();
        Arch::prepare_relocation(&mut state, &mut helper, shdrs)?;
        for (target_id, relocation_id, target, relocation_shdr) in
            object_relocation_sections::<Arch>(shdrs)
        {
            if !self.section_is_mapped(target_id) || !self.section_is_mapped(relocation_id) {
                continue;
            }
            let rels = section_entries::<Arch::Layout, ElfRelType<Arch>, _>(
                helper.memory(),
                relocation_shdr,
            )?;
            for rel in rels {
                if !helper.handle_pre(rel)?.is_unhandled() {
                    continue;
                }
                match Arch::relocate(&mut state, &mut helper, rel, target, &mut self.pltgot) {
                    Ok(()) => continue,
                    Err(err) => {
                        if helper.handle_post(rel)?.is_unhandled() {
                            return Err(err);
                        }
                    }
                }
            }
        }

        let RelocHelper { scope, .. } = helper;

        let finalizer = Finalizer::new(core::mem::take(&mut self.fini));
        let event_segments =
            ObjectSegmentView::new(self.core.segments(), self.init_segments.as_ref());
        let mut event = ObjectRelocatedEvent::new(
            &self.core,
            &self.sections,
            self.symtab.view(),
            event_segments,
            finalizer,
        );
        observer.on_object_relocated(&mut event)?;
        let (exports, finalizer) = event.into_parts();
        let exports = exports.unwrap_or_else(|| exports_handle(self.default_exports()));
        let inner = crate::sync::Arc::get_mut(&mut self.core.inner)
            .ok_or_else(|| LinkerError::ObjectCoreRetainedBeforeExports)?;
        inner.exports = exports;
        self.core.set_finalizer(finalizer);

        let object_segments =
            ObjectSegmentView::new(self.core.segments(), self.init_segments.as_ref());
        self.section_segments.mprotect(&object_segments)?;

        self.call_init(observer, object_segments)?;
        self.section_segments.mprotect_final(&object_segments)?;

        logging::info!("Relocation completed for {}", self.core.name());

        let core = self.core;
        Ok(LoadedObject {
            inner: unsafe { LoadedCore::from_core_scope(core, scope) },
        })
    }

    #[inline]
    fn call_init<Obs>(&self, observer: &mut Obs, segments: ObjectSegmentView<'_, R>) -> Result<()>
    where
        Obs: RelocationObserver<Arch> + ?Sized,
    {
        logging::trace!("[{}] Executing init functions", self.core.name());
        let mut event = InitEvent::new(&self.core, &self.init);
        observer.on_init(&mut event)?;
        event.run_with(&segments, self.core.executor())?;
        self.core.set_init();
        Ok(())
    }

    fn simplify_symbols<Obs>(
        &mut self,
        scope: &ModuleScope<Arch, Tls>,
        observer: &mut Obs,
    ) -> Result<()>
    where
        Obs: RelocationObserver<Arch> + ?Sized,
    {
        let base = self.core.base();
        let symbol_count = self.symtab.symbols().len();

        for idx in 0..symbol_count {
            let value = {
                let entry = self.symtab.symbol_idx(idx);
                let symbol = entry.symbol();
                if symbol.symbol_type() == ElfSymbolType::FILE {
                    continue;
                }

                let addr = if symbol.is_undef() {
                    let resolved = if let Some(symdef) = find_symdef_impl(
                        &self.core,
                        scope,
                        symbol,
                        entry.info(),
                        self.core.symbolic(),
                    ) {
                        Some(symdef.resolve_addr(self.core.executor())?)
                    } else {
                        None
                    };
                    let mut event =
                        SymbolBindingEvent::new(&self.core, None, symbol, entry.name(), resolved);
                    observer.on_symbol_binding(&mut event)?;
                    let Some(resolved) = event.into_resolved_addr() else {
                        return Err(unresolved_symbol_error(&self.core, entry.name()));
                    };
                    resolved
                } else if symbol.st_shndx().is_abs() {
                    VmAddr::new(symbol.st_value())
                } else {
                    let Some(section_id) = ElfSectionId::from_symbol_shndx(symbol.st_shndx())
                    else {
                        continue;
                    };
                    VmAddr::new(self.sections.section(section_id).sh_addr())
                        .wrapping_add(VmOffset::new(symbol.st_value()))
                };
                addr.wrapping_offset_from(base).get()
            };

            let symbols = self.symtab.symbols_mut();
            symbols[idx].set_value(value);
        }

        Ok(())
    }

    fn default_exports(&self) -> ObjectExports<Arch::Layout> {
        let mut exports = ObjectExports::empty();
        for idx in 0..self.symtab.symbols().len() {
            let entry = self.symtab.symbol_idx(idx);
            let symbol = entry.symbol();
            if symbol.is_undef()
                || !symbol.is_ok_bind()
                || !symbol.is_ok_type()
                || self.symbol_uses_init_memory(symbol)
            {
                continue;
            }
            exports.insert(entry.name(), symbol.clone());
        }
        exports
    }

    fn symbol_uses_init_memory(&self, symbol: &ElfSymbol<Arch::Layout>) -> bool {
        let Some(init_segments) = self.init_segments.as_ref() else {
            return false;
        };
        if matches!(
            symbol.st_shndx(),
            ElfSectionIndex::ABS | ElfSectionIndex::COMMON
        ) {
            return false;
        }
        let Some(section_id) = ElfSectionId::from_symbol_shndx(symbol.st_shndx()) else {
            return false;
        };
        let section_addr = VmAddr::new(self.sections.section(section_id).sh_addr());
        init_segments.contains_addr(section_addr)
    }
}

#[cold]
fn unresolved_symbol_error<D, Arch, R, Tls>(
    core: &crate::image::ElfCore<D, Arch, R, Tls>,
    name: &str,
) -> crate::Error
where
    D: 'static,
    Arch: crate::relocation::RelocationArch,
    R: RegionAccess,
    Tls: TlsResolver<Arch>,
{
    relocate_context_error(
        core.name(),
        "object symbol",
        Some(name),
        RelocReason::UnknownSymbol,
    )
}