elf_loader 0.17.0

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

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

struct TlsDef {
    offset: usize,
    module: ModuleTls,
    effect: BindingEffect,
}

impl<'find, D: Send + Sync + 'static, Arch, R, Tls, Obs, H, Memory>
    RelocHelper<'find, D, Arch, R, Tls, Obs, H, Memory>
where
    D: Send + Sync + 'static,
    Arch: RelocationArch,
    R: RegionAccess,
    Tls: TlsResolver<Arch>,
    Obs: RelocationObserver<Arch> + ?Sized,
    Memory: ImageMemory,
    <Arch::Layout as ElfLayout>::Word: ByteRepr,
{
    #[inline]
    fn tls_def(&self, rel: &ElfRelType<Arch>) -> core::result::Result<TlsDef, TlsRelocOutcome> {
        let symbol = self.symbol_entry(rel);
        let Some(symdef) = self.find_symdef(&symbol) else {
            return Err(TlsRelocOutcome::Failed(RelocReason::UnknownSymbol));
        };
        if symdef.is_weak_undef() {
            Err(TlsRelocOutcome::Applied)
        } else {
            let effect = symdef.effect();
            let (symbol, source) = symdef
                .definition()
                .expect("defined TLS symbol must retain its provider");
            let Some(module) = source.tls() else {
                return Err(TlsRelocOutcome::Failed(RelocReason::MissingTlsModuleId));
            };
            Ok(TlsDef {
                offset: symbol.st_value(),
                module,
                effect,
            })
        }
    }

    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)?;
        let mut effect = BindingEffect::default();

        match r_type {
            value if Arch::DTPOFF == value => {
                let symbol_value = if r_sym == 0 {
                    0
                } else {
                    let symbol = match self.tls_def(rel) {
                        Ok(symbol) => symbol,
                        Err(outcome) => return Ok(outcome),
                    };
                    effect = symbol.effect;
                    symbol.offset
                };
                let tls_val = VmAddr::new(symbol_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),
                    )?;
                }
            }
            value if Arch::DTPMOD == Some(value) => {
                let tls = if r_sym == 0 {
                    self.core.tls()
                } else {
                    let symbol = match self.tls_def(rel) {
                        Ok(symbol) => symbol,
                        Err(outcome) => return Ok(outcome),
                    };
                    effect = symbol.effect;
                    Some(symbol.module)
                };
                let Some(tls) = tls else {
                    return Ok(TlsRelocOutcome::Failed(RelocReason::MissingTlsModuleId));
                };
                let mod_id = tls.mod_id();
                unsafe {
                    memory.write_value(
                        place,
                        <Arch::Layout as ElfLayout>::Word::from_usize(mod_id.get()),
                    )?;
                }
            }
            value if Arch::TPOFF == value => {
                let (tls, symbol_value) = if r_sym == 0 {
                    (self.core.tls(), 0)
                } else {
                    let symbol = match self.tls_def(rel) {
                        Ok(symbol) => symbol,
                        Err(outcome) => return Ok(outcome),
                    };
                    effect = symbol.effect;
                    (Some(symbol.module), symbol.offset)
                };
                let Some(tp_offset) = tls.and_then(ModuleTls::tp_offset) else {
                    return Ok(TlsRelocOutcome::Failed(RelocReason::MissingTlsTpOffset));
                };
                let tls_val =
                    VmAddr::new(tp_offset.get().wrapping_add(symbol_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()),
                    )?;
                }
            }
            value if Arch::TLSDESC == Some(value) => {
                let symbol = self.symbol_entry(rel);
                let request = match self.find_symdef(&symbol) {
                    None => {
                        return Ok(TlsRelocOutcome::Failed(RelocReason::UnknownSymbol));
                    }
                    Some(symdef) if symdef.is_weak_undef() => TlsDescRequest::UndefinedWeak {
                        addend: r_addend as usize,
                    },
                    Some(symdef) => {
                        effect = symdef.effect();
                        let (sym, source) = symdef
                            .definition()
                            .expect("defined TLS symbol must retain its provider");
                        let Some(tls) = source.tls() else {
                            return Ok(TlsRelocOutcome::Failed(RelocReason::MissingTlsModuleId));
                        };
                        TlsDescRequest::Defined {
                            module: tls,
                            offset: VmAddr::new(sym.st_value())
                                .wrapping_add_signed(r_addend)
                                .get(),
                        }
                    }
                };
                let desc = self.core.tls_resolver().bind_tlsdesc(request)?;
                let arg_place = place
                    + VmOffset::new(core::mem::size_of::<<Arch::Layout as ElfLayout>::Word>());
                unsafe {
                    memory.write_value(
                        place,
                        <Arch::Layout as ElfLayout>::Word::from_usize(desc.resolver().get()),
                    )?;
                    memory.write_value(
                        arg_place,
                        <Arch::Layout as ElfLayout>::Word::from_usize(desc.arg()),
                    )?;
                }
            }
            _ => unreachable!("handle_tls_reloc called with a non-TLS relocation"),
        }
        self.record_binding(effect);
        Ok(TlsRelocOutcome::Applied)
    }
}