#[path = "../examples/common/mod.rs"]
mod fixture_support;
use criterion::{BatchSize, Criterion, criterion_group, criterion_main};
use elf_loader::{
LinkContext, Linker, Loader, Relocator, Result,
image::{LoadedCore, ModuleCapability},
input::{ElfFile, PathBuf},
linker::scan::{LinkPass, LinkPassPlan, Materialization, ReorderPass},
};
use libloading::os::unix::{Library as UnixLibrary, RTLD_LAZY, RTLD_LOCAL, RTLD_NOW};
use std::{fs, hint::black_box};
struct FixtureBytes {
liba: Vec<u8>,
libb: Vec<u8>,
libc: Vec<u8>,
}
impl FixtureBytes {
fn new(fixtures: &fixture_support::FixturePaths) -> Self {
Self {
liba: fs::read(&fixtures.liba).expect("failed to read liba fixture"),
libb: fs::read(&fixtures.libb).expect("failed to read libb fixture"),
libc: fs::read(&fixtures.libc).expect("failed to read libc fixture"),
}
}
}
struct UseRootSectionRegions;
impl LinkPass<PathBuf, ReorderPass> for UseRootSectionRegions {
fn run(&mut self, plan: &mut LinkPassPlan<'_, PathBuf, ReorderPass>) -> Result<()> {
let root = plan.root().expect("root module should be visible");
assert_eq!(root.capability(plan), ModuleCapability::SectionReorderable);
root.set_materialization(plan, Materialization::SectionRegions);
Ok(())
}
}
fn load_manual_file(fixtures: &fixture_support::FixturePaths) -> LoadedCore<()> {
let loader = Loader::new();
let relocator = Relocator::new();
let liba = relocator
.run(
loader
.load_dylib(ElfFile::from_path(black_box(fixtures.liba_str())).unwrap())
.unwrap(),
)
.relocate()
.unwrap();
let libb = relocator
.run(
loader
.load_dylib(ElfFile::from_path(black_box(fixtures.libb_str())).unwrap())
.unwrap(),
)
.scope([&liba])
.relocate()
.unwrap();
relocator
.run(
loader
.load_dylib(ElfFile::from_path(black_box(fixtures.libc_str())).unwrap())
.unwrap(),
)
.scope([&liba, &libb])
.relocate()
.unwrap()
}
fn load_manual_memory(fixtures: &FixtureBytes) -> LoadedCore<()> {
let loader = Loader::new();
let relocator = Relocator::new();
let liba = relocator
.run(
loader
.load_dylib(black_box(fixtures.liba.as_slice()))
.unwrap(),
)
.relocate()
.unwrap();
let libb = relocator
.run(
loader
.load_dylib(black_box(fixtures.libb.as_slice()))
.unwrap(),
)
.scope([&liba])
.relocate()
.unwrap();
relocator
.run(
loader
.load_dylib(black_box(fixtures.libc.as_slice()))
.unwrap(),
)
.scope([&liba, &libb])
.relocate()
.unwrap()
}
fn load_linker(root: PathBuf) {
let mut context: LinkContext<PathBuf, ()> = LinkContext::new();
let loaded = Linker::new()
.resolver(fixture_support::search_path_resolver())
.load(&mut context, black_box(root))
.unwrap();
black_box(loaded);
}
fn load_scan_first(root: PathBuf) {
let mut context: LinkContext<PathBuf, ()> = LinkContext::new();
let loaded = Linker::new()
.resolver(fixture_support::search_path_resolver())
.run()
.map_pipeline(|mut pipeline| {
pipeline.push(UseRootSectionRegions);
pipeline
})
.load_scan_first(&mut context, black_box(root))
.unwrap();
black_box(loaded);
}
fn dlopen_libc(fixtures: &fixture_support::FixturePaths, flags: i32) {
let library = unsafe { UnixLibrary::open(Some(black_box(&fixtures.libc)), flags).unwrap() };
black_box(library);
}
fn bench_load(c: &mut Criterion) {
let fixtures = fixture_support::ensure_all();
let fixture_bytes = FixtureBytes::new(&fixtures);
let mut group = c.benchmark_group("load");
group.bench_function("elf_loader/file", |b| {
b.iter(|| black_box(load_manual_file(&fixtures)));
});
group.bench_function("elf_loader/memory", |b| {
b.iter(|| black_box(load_manual_memory(&fixture_bytes)));
});
group.bench_function("linker/runtime", |b| {
b.iter_batched(
|| PathBuf::from(fixtures.libc_str()),
load_linker,
BatchSize::SmallInput,
);
});
group.bench_function("linker/scan_first", |b| {
b.iter_batched(
|| PathBuf::from(fixtures.libc_str()),
load_scan_first,
BatchSize::SmallInput,
);
});
group.bench_function("libloading/lazy", |b| {
b.iter(|| dlopen_libc(&fixtures, RTLD_LAZY | RTLD_LOCAL));
});
group.bench_function("libloading/now", |b| {
b.iter(|| dlopen_libc(&fixtures, RTLD_NOW | RTLD_LOCAL));
});
group.finish();
}
fn bench_symbol(c: &mut Criterion) {
let fixtures = fixture_support::ensure_all();
let libc = load_manual_file(&fixtures);
let dlopen_libc =
unsafe { UnixLibrary::open(Some(&fixtures.libc), RTLD_NOW | RTLD_LOCAL).unwrap() };
let mut group = c.benchmark_group("symbol");
group.bench_function("elf_loader/hit", |b| {
b.iter(|| unsafe {
black_box(libc.get::<fn() -> i32>(black_box("c")).unwrap());
});
});
group.bench_function("elf_loader/miss", |b| {
b.iter(|| unsafe {
black_box(
libc.get::<fn() -> i32>(black_box("missing_symbol"))
.is_none(),
);
});
});
group.bench_function("libloading/hit", |b| {
b.iter(|| unsafe {
black_box(dlopen_libc.get::<fn() -> i32>(black_box(b"c")).unwrap());
});
});
group.bench_function("libloading/miss", |b| {
b.iter(|| unsafe {
black_box(
dlopen_libc
.get::<fn() -> i32>(black_box(b"missing_symbol"))
.is_err(),
);
});
});
group.finish();
}
criterion_group!(benches, bench_load, bench_symbol);
criterion_main!(benches);