elf_loader 0.15.0

A no_std-friendly ELF loader, runtime linker, and JIT linker for Rust.
use crate::{
    RelocReason,
    arch::x86_64::relocation::X86_64Arch,
    elf::ElfRelType,
    object::layout::{GotEntry, PltEntry, PltGotSection},
    relocation::{RelocAddr, RelocHelper, RelocationHandler, RelocationValueProvider, reloc_error},
};
use elf::abi::*;

pub(crate) const PLT_ENTRY_SIZE: usize = 16;

pub(crate) const PLT_ENTRY: [u8; PLT_ENTRY_SIZE] = [
    0xf3, 0x0f, 0x1e, 0xfa, 0xff, 0x25, 0, 0, 0, 0, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc,
];

impl X86_64Arch {
    pub(crate) fn relocate_object_impl<D, PreH, PostH>(
        helper: &mut RelocHelper<'_, D, Self, PreH, PostH>,
        rel: &ElfRelType<Self>,
        pltgot: &mut PltGotSection,
    ) -> crate::Result<()>
    where
        D: 'static,
        PreH: RelocationHandler<Self> + ?Sized,
        PostH: RelocationHandler<Self> + ?Sized,
    {
        let r_sym = rel.r_symbol();
        let r_type = rel.r_type();
        let base = helper.core.base_addr();
        let segments = helper.core.segments();
        let append = rel.r_addend(base.into_inner());
        let offset = rel.r_offset();
        let p = base.offset(rel.r_offset());
        let unknown_symbol =
            || reloc_error::<Self, _>(rel, crate::RelocReason::UnknownSymbol, helper.core);
        let value_error = |reason| reloc_error::<Self, _>(rel, reason, helper.core);
        let write_relocation_target = |target| {
            <Self as RelocationValueProvider>::relocation_value(
                r_type.raw() as usize,
                target,
                append,
                p.into_inner(),
                |_| {},
                |value| segments.write(offset, value),
                |value| segments.write(offset, value),
                |value| segments.write(offset, value),
            )
        };

        match r_type.raw() {
            R_X86_64_NONE => {}
            R_X86_64_64 => {
                let Some(sym) = helper.find_symbol(r_sym) else {
                    return Err(unknown_symbol());
                };
                write_relocation_target(sym.into_inner()).map_err(value_error)?;
            }
            R_X86_64_PC32 => {
                let Some(sym) = helper.find_symbol(r_sym) else {
                    return Err(unknown_symbol());
                };
                write_relocation_target(sym.into_inner()).map_err(value_error)?;
            }
            R_X86_64_PLT32 => {
                let Some(sym) = helper.find_symbol(r_sym) else {
                    return Err(unknown_symbol());
                };
                match write_relocation_target(sym.into_inner()) {
                    Ok(()) => {}
                    Err(RelocReason::IntConversionOutOfRange) => {
                        let plt_entry = pltgot.add_plt_entry(r_sym);
                        let plt_entry_addr = match plt_entry {
                            PltEntry::Occupied(plt_entry_addr) => plt_entry_addr,
                            PltEntry::Vacant { plt, mut got } => {
                                let plt_entry_addr = RelocAddr::from_ptr(plt.as_ptr());
                                got.update(sym);
                                let call_offset = got
                                    .get_addr()
                                    .relative_to(plt_entry_addr.into_inner())
                                    .relative_to(10);
                                let call_offset_val =
                                    call_offset.try_into_sword32().map_err(value_error)?;
                                plt[6..10].copy_from_slice(&call_offset_val.to_ne_bytes());
                                plt_entry_addr
                            }
                        };
                        write_relocation_target(plt_entry_addr.into_inner())
                            .map_err(value_error)?;
                    }
                    Err(reason) => return Err(value_error(reason)),
                }
            }
            R_X86_64_GOTPCREL => {
                let Some(sym) = helper.find_symbol(r_sym) else {
                    return Err(unknown_symbol());
                };
                let got_entry = pltgot.add_got_entry(r_sym);
                let got_entry_addr = match got_entry {
                    GotEntry::Occupied(got_entry_addr) => got_entry_addr,
                    GotEntry::Vacant(mut got) => {
                        got.update(sym);
                        got.get_addr()
                    }
                };
                write_relocation_target(got_entry_addr.into_inner()).map_err(value_error)?;
            }
            R_X86_64_32 => {
                let Some(sym) = helper.find_symbol(r_sym) else {
                    return Err(unknown_symbol());
                };
                write_relocation_target(sym.into_inner()).map_err(value_error)?;
            }
            R_X86_64_32S => {
                let Some(sym) = helper.find_symbol(r_sym) else {
                    return Err(unknown_symbol());
                };
                write_relocation_target(sym.into_inner()).map_err(value_error)?;
            }
            _ => return Err(unknown_symbol()),
        }

        Ok(())
    }

    pub(crate) fn object_needs_got_impl(rel_type: crate::elf::ElfRelocationType) -> bool {
        matches!(rel_type.raw(), R_X86_64_GOTPCREL | R_X86_64_PLT32)
    }

    pub(crate) fn object_needs_plt_impl(rel_type: crate::elf::ElfRelocationType) -> bool {
        rel_type.raw() == R_X86_64_PLT32
    }
}