mod support;
use elf_loader::{
Loader,
elf::ElfProgramType,
image::ScannedElf,
input::ElfBinary,
observer::{BeforeLoadEvent, LoadObserver},
};
use gen_elf::{Arch, ElfWriterConfig, SymbolDesc};
use support::{generated_dylib::return_42_stub, test_dylib::write_test_dylib_with_config};
#[derive(Default)]
struct ScanData {
value: usize,
}
struct ScanObserver;
impl LoadObserver<ScanData> for ScanObserver {
fn on_before_load(
&mut self,
mut event: BeforeLoadEvent<'_, ScanData>,
) -> elf_loader::Result<()> {
assert!(
event
.phdrs()
.iter()
.any(|phdr| phdr.program_type() == ElfProgramType::DYNAMIC)
);
event.user_data_mut().value = 42;
Ok(())
}
}
#[test]
fn borrowed_dynamic_reuses_existing_mapping() {
let arch = Arch::current();
let output = write_test_dylib_with_config(
ElfWriterConfig::default()
.with_bind_now(true)
.with_soname("libowner.so"),
&[],
&[SymbolDesc::global_func("answer", &return_42_stub(arch))],
);
let owner_loader = Loader::new();
let owner = owner_loader
.load_dylib(ElfBinary::new("owner.so", &output.data))
.expect("failed to map owner dylib");
let borrowed_loader = Loader::new();
let borrowed = unsafe {
borrowed_loader.load_mapped_dynamic(
"borrowed-main",
owner.base(),
owner.phdrs().to_vec(),
owner.entry(),
)
}
.expect("failed to wrap borrowed mapping");
assert_eq!(borrowed.path().as_str(), "borrowed-main");
assert_eq!(borrowed.name(), "libowner.so");
assert_eq!(borrowed.base(), owner.base());
assert_eq!(borrowed.entry(), owner.entry());
assert!(borrowed.segments().contains_addr(owner.base()));
assert_eq!(borrowed.phdrs().len(), owner.phdrs().len());
assert!(
borrowed
.phdrs()
.iter()
.zip(owner.phdrs())
.all(
|(borrowed, owner)| borrowed.program_type() == owner.program_type()
&& borrowed.p_vaddr() == owner.p_vaddr()
&& borrowed.p_memsz() == owner.p_memsz()
)
);
assert_eq!(borrowed.needed_libs(), owner.needed_libs());
assert_eq!(owner.soname(), Some("libowner.so"));
assert_eq!(borrowed.soname(), owner.soname());
}
#[test]
fn scanned_dynamic_load_reuses_scanned_metadata() {
let arch = Arch::current();
let output = write_test_dylib_with_config(
ElfWriterConfig::default()
.with_bind_now(true)
.with_soname("libscanned.so"),
&[],
&[SymbolDesc::global_func("answer", &return_42_stub(arch))],
);
let bytes = output.data;
let loader = Loader::new().with_data::<ScanData>();
let mut run = loader.run().with_observer(ScanObserver);
let ScannedElf::Dynamic(scanned) = run
.scan(ElfBinary::owned("scanned.so", bytes))
.expect("failed to scan dylib")
else {
panic!("generated dylib should scan as dynamic");
};
assert_eq!(scanned.soname(), Some("libscanned.so"));
let raw = run
.load_scanned_dynamic(scanned)
.expect("failed to load scanned dynamic image");
assert_eq!(raw.path().as_str(), "scanned.so");
assert_eq!(raw.name(), "libscanned.so");
assert_eq!(raw.soname(), Some("libscanned.so"));
assert_eq!(raw.user_data().value, 42);
assert!(
raw.phdrs()
.iter()
.any(|phdr| phdr.program_type() == ElfProgramType::DYNAMIC)
);
}