use super::{DynLifecycleHandler, LifecycleHandler, LoadHook};
use crate::{
Result,
arch::NativeArch,
elf::Lifecycle,
image::RawDynamic,
os::{DefaultMmap, Mmap, PageSize},
relocation::RelocationArch,
sync::Arc,
tls::TlsResolver,
};
use alloc::boxed::Box;
use core::marker::PhantomData;
pub struct Loader<M = DefaultMmap, H = (), D: 'static = (), Tls = (), Arch = NativeArch>
where
M: Mmap,
H: LoadHook<Arch::Layout>,
Tls: TlsResolver,
Arch: RelocationArch,
{
pub(crate) buf: super::ElfBuf,
pub(crate) inner: LoaderInner<H, D, Arch>,
_marker: PhantomData<(M, Tls, Arch)>,
}
pub(crate) struct LoaderInner<H, D: 'static, Arch: RelocationArch> {
pub(crate) init_fn: DynLifecycleHandler,
pub(crate) fini_fn: DynLifecycleHandler,
pub(crate) hook: H,
pub(crate) page_size: Option<PageSize>,
pub(crate) force_static_tls: bool,
pub(crate) dynamic_initializer: Box<dyn FnMut(&mut RawDynamic<D, Arch>) -> Result<()>>,
}
impl Loader<DefaultMmap, (), (), (), NativeArch> {
pub fn new() -> Self {
let c_abi: DynLifecycleHandler = Arc::new(Box::new(|ctx: &Lifecycle<'_>| {
ctx.func()
.iter()
.chain(ctx.func_array().unwrap_or(&[]).iter())
.for_each(|init| {
#[cfg(not(windows))]
unsafe {
core::mem::transmute::<_, &extern "C" fn()>(init)()
};
#[cfg(windows)]
unsafe {
core::mem::transmute::<_, &extern "sysv64" fn()>(init)()
};
})
}));
Self {
buf: super::ElfBuf::new(),
inner: LoaderInner {
hook: (),
init_fn: c_abi.clone(),
fini_fn: c_abi,
page_size: None,
force_static_tls: false,
dynamic_initializer: Box::new(|_| Ok(())),
},
_marker: PhantomData,
}
}
}
impl<M, H, D, Tls, Arch> Loader<M, H, D, Tls, Arch>
where
H: LoadHook<Arch::Layout>,
M: Mmap,
D: 'static,
Tls: TlsResolver,
Arch: RelocationArch,
{
pub fn with_init<F>(mut self, init_fn: F) -> Self
where
F: LifecycleHandler + 'static,
{
self.inner.init_fn = Arc::new(Box::new(init_fn));
self
}
pub fn with_fini<F>(mut self, fini_fn: F) -> Self
where
F: LifecycleHandler + 'static,
{
self.inner.fini_fn = Arc::new(Box::new(fini_fn));
self
}
pub fn with_dynamic_initializer<NewD>(
self,
initializer: impl FnMut(&mut RawDynamic<NewD, Arch>) -> Result<()> + 'static,
) -> Loader<M, H, NewD, Tls, Arch>
where
NewD: Default + 'static,
{
Loader {
buf: self.buf,
inner: LoaderInner {
init_fn: self.inner.init_fn,
fini_fn: self.inner.fini_fn,
hook: self.inner.hook,
page_size: self.inner.page_size,
force_static_tls: self.inner.force_static_tls,
dynamic_initializer: Box::new(initializer),
},
_marker: PhantomData,
}
}
pub fn with_hook<NewHook>(self, hook: NewHook) -> Loader<M, NewHook, D, Tls, Arch>
where
NewHook: LoadHook<Arch::Layout>,
{
Loader {
buf: self.buf,
inner: LoaderInner {
init_fn: self.inner.init_fn,
fini_fn: self.inner.fini_fn,
hook,
page_size: self.inner.page_size,
force_static_tls: self.inner.force_static_tls,
dynamic_initializer: self.inner.dynamic_initializer,
},
_marker: PhantomData,
}
}
pub fn with_page_size(mut self, page_size: PageSize) -> Self {
self.inner.page_size = Some(page_size);
self
}
pub fn with_mmap<NewMmap: Mmap>(self) -> Loader<NewMmap, H, D, Tls, Arch> {
Loader {
buf: self.buf,
inner: self.inner,
_marker: PhantomData,
}
}
#[cfg(feature = "tls")]
pub fn with_tls_resolver<NewTls>(self) -> Loader<M, H, D, NewTls, Arch>
where
NewTls: TlsResolver,
{
Loader {
buf: self.buf,
inner: self.inner,
_marker: PhantomData,
}
}
#[cfg(feature = "tls")]
pub fn with_default_tls_resolver(
self,
) -> Loader<M, H, D, crate::tls::DefaultTlsResolver, Arch> {
Loader {
buf: self.buf,
inner: self.inner,
_marker: PhantomData,
}
}
#[cfg(feature = "tls")]
pub fn with_static_tls(mut self, enabled: bool) -> Self {
self.inner.force_static_tls = enabled;
self
}
}
impl<M, H, Tls, Arch> Loader<M, H, (), Tls, Arch>
where
H: LoadHook<Arch::Layout>,
M: Mmap,
Tls: TlsResolver,
Arch: RelocationArch,
{
pub fn for_arch<NewArch>(self) -> Loader<M, H, (), Tls, NewArch>
where
NewArch: RelocationArch,
H: LoadHook<NewArch::Layout>,
{
Loader {
buf: self.buf,
inner: LoaderInner {
init_fn: self.inner.init_fn,
fini_fn: self.inner.fini_fn,
hook: self.inner.hook,
page_size: self.inner.page_size,
force_static_tls: self.inner.force_static_tls,
dynamic_initializer: Box::new(|_| Ok(())),
},
_marker: PhantomData,
}
}
}