elf_loader 0.16.0

A no_std-friendly ELF loader and runtime linker for Rust.
Documentation
use super::defs::TlsIndex;
use crate::{
    RelocReason, Result,
    elf::{ElfLayout, ElfRelEntry, ElfRelType, ElfSymbol, ElfWord},
    image::Module,
    memory::{ImageMemory, ImageMemoryExt, RegionAccess, VmAddr, VmOffset},
    observer::RelocationObserver,
    relocation::{RelocHelper, RelocationArch, SymDef},
    tls::TlsResolver,
};

pub(crate) enum TlsRelocOutcome {
    Applied,
    Failed(RelocReason),
}

struct TlsDefinedSymbol<'a, Arch: RelocationArch, Tls: TlsResolver<Arch> + 'static> {
    symbol: &'a ElfSymbol<Arch::Layout>,
    source: &'a dyn Module<Arch, Tls>,
}

impl<'find, D, Arch, R, Tls, Obs, H, Memory> RelocHelper<'find, D, Arch, R, Tls, Obs, H, Memory>
where
    D: 'static,
    Arch: RelocationArch,
    R: RegionAccess,
    Tls: TlsResolver<Arch>,
    Obs: RelocationObserver<Arch> + ?Sized,
    Memory: ImageMemory,
    <Arch::Layout as ElfLayout>::Word: crate::ByteRepr,
{
    #[inline]
    fn defined_tls_symbol(
        &self,
        rel: &ElfRelType<Arch>,
    ) -> core::result::Result<TlsDefinedSymbol<'_, Arch, Tls>, TlsRelocOutcome> {
        let symbol = self.symbol_entry(rel);
        match self.find_symdef(&symbol) {
            Some(SymDef::Defined { symbol, source }) => Ok(TlsDefinedSymbol { symbol, source }),
            Some(SymDef::WeakUndef) => Err(TlsRelocOutcome::Applied),
            None => Err(TlsRelocOutcome::Failed(RelocReason::UnknownSymbol)),
        }
    }

    pub(crate) fn handle_tls_reloc(&mut self, rel: &ElfRelType<Arch>) -> Result<TlsRelocOutcome> {
        let r_type = rel.r_type();
        let r_sym = rel.r_symbol();
        let memory = self.memory();
        let base = memory.base();
        let place = base + rel.r_offset();
        let r_addend = rel.read_addend(memory, place)?;

        match r_type {
            value if value == Arch::DTPOFF => {
                let defined = match self.defined_tls_symbol(rel) {
                    Ok(defined) => defined,
                    Err(outcome) => return Ok(outcome),
                };
                let tls_val = VmAddr::new(defined.symbol.st_value())
                    .wrapping_add_signed(r_addend)
                    .get()
                    .wrapping_sub(Arch::TLS_DTV_OFFSET);
                unsafe {
                    memory.write_value(
                        place,
                        <Arch::Layout as ElfLayout>::Word::from_usize(tls_val),
                    )?;
                }
                Ok(TlsRelocOutcome::Applied)
            }
            value if value == Arch::DTPMOD => {
                let tls = if r_sym == 0 {
                    self.core.tls()
                } else {
                    let defined = match self.defined_tls_symbol(rel) {
                        Ok(defined) => defined,
                        Err(outcome) => return Ok(outcome),
                    };
                    defined.source.tls()
                };
                let Some(mod_id) = tls.mod_id() else {
                    return Ok(TlsRelocOutcome::Failed(RelocReason::MissingTlsModuleId));
                };
                unsafe {
                    memory.write_value(
                        place,
                        <Arch::Layout as ElfLayout>::Word::from_usize(mod_id.get()),
                    )?;
                }
                Ok(TlsRelocOutcome::Applied)
            }
            value if value == Arch::TPOFF => {
                let defined = match self.defined_tls_symbol(rel) {
                    Ok(defined) => defined,
                    Err(outcome) => return Ok(outcome),
                };
                let tls = defined.source.tls();
                let Some(tp_offset) = tls.tp_offset() else {
                    return Ok(TlsRelocOutcome::Failed(RelocReason::MissingTlsTpOffset));
                };
                let tls_val =
                    VmAddr::new((tp_offset.get() + defined.symbol.st_value() as isize) as usize)
                        .wrapping_add_signed(r_addend);
                unsafe {
                    memory.write_value(
                        place,
                        <Arch::Layout as ElfLayout>::Word::from_usize(tls_val.get()),
                    )?;
                }
                Ok(TlsRelocOutcome::Applied)
            }
            value if Arch::is_tlsdesc(value) => {
                let symbol = self.symbol_entry(rel);
                let desc = match self.find_symdef(&symbol) {
                    Some(SymDef::WeakUndef) => Tls::bind_undefweak_tlsdesc(r_addend as usize)?,
                    None => {
                        return Ok(TlsRelocOutcome::Failed(RelocReason::UnknownSymbol));
                    }
                    Some(SymDef::Defined {
                        symbol: sym,
                        source,
                    }) => {
                        let tls = source.tls();
                        if let Some(tp_offset) = tls.tp_offset() {
                            let tpoff =
                                VmAddr::new((tp_offset.get() + sym.st_value() as isize) as usize)
                                    .wrapping_add_signed(r_addend);
                            Tls::bind_static_tlsdesc(tpoff.get())?
                        } else if let Some(module_id) = tls.mod_id() {
                            let offset = VmAddr::new(sym.st_value())
                                .wrapping_add_signed(r_addend)
                                .get()
                                .wrapping_sub(Arch::TLS_DTV_OFFSET);
                            Tls::bind_dynamic_tlsdesc(TlsIndex {
                                ti_module: module_id,
                                ti_offset: offset,
                            })?
                        } else {
                            return Ok(TlsRelocOutcome::Failed(RelocReason::MissingTlsModuleId));
                        }
                    }
                };
                unsafe {
                    memory.write_value(
                        place,
                        <Arch::Layout as ElfLayout>::Word::from_usize(desc.resolver().get()),
                    )?;
                    memory.write_value(
                        place + VmOffset::new(8),
                        <Arch::Layout as ElfLayout>::Word::from_usize(desc.arg()),
                    )?;
                }
                Ok(TlsRelocOutcome::Applied)
            }
            _ => unreachable!("handle_tls_reloc called with a non-TLS relocation"),
        }
    }
}