elf_loader 0.16.0

A no_std-friendly ELF loader and runtime linker for Rust.
Documentation
#[path = "common/mod.rs"]
mod fixture_support;

use elf_loader::{
    Loader, Result,
    arch::NativeArch,
    memory::RegionAccess,
    observer::RelocationObserver,
    relocation::{HandleResult, RelocationEvent, Relocator},
    tls::TlsResolver,
};

const LOADER: Loader = Loader::new();

struct MyRelocHandler;

fn my_print(s: &str) {
    println!("Caught by MyRelocHandler: {}", s);
}

impl RelocationObserver for MyRelocHandler {
    fn on_relocation_pre<D: 'static, R: RegionAccess, Tls: TlsResolver<NativeArch>, H>(
        &mut self,
        ctx: &RelocationEvent<'_, D, NativeArch, R, Tls, H>,
    ) -> Result<HandleResult> {
        let Some(symbol) = ctx.relocation_symbol() else {
            return Ok(HandleResult::Unhandled);
        };

        if symbol.name() == "print" {
            let target_addr = (ctx.lib().base() + ctx.rel().r_offset()).get() as *mut usize;
            println!(
                "Relocating 'print' for {} at {:p}",
                ctx.lib().name(),
                target_addr
            );
            unsafe { *target_addr = my_print as *const () as usize };
            return Ok(HandleResult::Handled);
        }

        Ok(HandleResult::Unhandled)
    }
}

fn main() -> Result<()> {
    unsafe { std::env::set_var("RUST_LOG", "trace") };
    env_logger::init();

    let fixtures = fixture_support::ensure_all();

    let _liba = Relocator::new()
        .run(LOADER.load_dylib(fixtures.liba_str())?)
        .observer(MyRelocHandler)
        .relocate()?;

    let libb = Relocator::new()
        .run(LOADER.load_dylib(fixtures.libb_str())?)
        .observer(MyRelocHandler)
        .scope([&_liba])
        .relocate()?;

    unsafe {
        let b = libb.get::<fn() -> i32>("b").expect("symbol 'b' not found");
        let result = b();
        println!("Result of b(): {}", result);
    }

    println!("Relocation with custom handler completed.");

    Ok(())
}