elf_loader 0.15.1

A no_std-friendly ELF loader, runtime linker, and JIT linker for Rust.
Documentation
#![cfg(unix)]

mod support;

use gen_elf::{Arch, DylibWriter, RelocEntry, SymbolDesc};
use support::{
    generated_dylib::{generated_dylib_path, load_unix_library, return_42_stub},
    memory::read_native_word,
};

#[test]
fn plt_call_patches_got() {
    let arch = Arch::current();
    let generated_dylib = DylibWriter::new(arch)
        .write(
            &[
                RelocEntry::jump_slot("callee", arch),
                RelocEntry::irelative(arch),
            ],
            &[SymbolDesc::global_func("callee", &return_42_stub(arch))],
        )
        .expect("failed to generate ELF");

    let dylib_path = generated_dylib_path("generated_dylib", arch);
    std::fs::write(&dylib_path, &generated_dylib.data).expect("failed to write ELF to file");

    let library = unsafe { load_unix_library(&dylib_path) };
    let callee_abs_addr = unsafe {
        let func: libloading::Symbol<unsafe extern "C" fn() -> i32> = library
            .get(b"callee")
            .expect("failed to get callee function");
        let addr = *func as *const () as usize;
        let result = func();
        assert_eq!(result, 42, "direct call returned wrong value");
        addr
    };

    let resolver_abs_addr = unsafe {
        let func: libloading::Symbol<unsafe extern "C" fn() -> u64> = library
            .get(b"__ifunc_resolver")
            .expect("failed to get resolver");
        *func as *const () as u64
    };
    let load_bias = resolver_abs_addr - generated_dylib.text_vaddr;

    let jump_slot_relocation = generated_dylib
        .find_relocation(Arch::current().jump_slot_reloc(), "callee")
        .expect("failed to find PLT relocation");

    let got_entry_addr = (load_bias
        + jump_slot_relocation
            .vaddr
            .expect("dynamic relocation metadata should include a virtual address"))
        as usize;

    let got_value_before = read_native_word(got_entry_addr);
    let func: libloading::Symbol<extern "C" fn() -> i32> = unsafe {
        library
            .get(b"callee@plt_helper")
            .expect("failed to get helper function")
    };

    let result = func();
    assert_eq!(result, 42, "PLT call returned wrong value");
    let got_value_after = read_native_word(got_entry_addr);

    assert_eq!(
        got_value_after as usize, callee_abs_addr,
        "GOT entry should be updated to callee address"
    );
    assert!(
        got_value_before != got_value_after,
        "GOT entry should change after PLT call"
    );
}