use super::run::LoaderRun;
#[cfg(feature = "object")]
use crate::object::SectionGroups;
use crate::{
MmapError, Result,
arch::NativeArch,
const_builder::NoDrop,
os::{DefaultMmap, Mmap, PageSize},
relocation::RelocationArch,
runtime::{CodeExecutor, NativeCodeExecutor},
sync::Arc,
tls::TlsResolver,
};
use alloc::boxed::Box;
use core::{marker::PhantomData, mem::MaybeUninit, ptr};
pub struct Loader<
D: 'static = (),
Tls = (),
Arch = NativeArch,
M = DefaultMmap,
Exec = NativeCodeExecutor,
> where
Tls: TlsResolver<Arch>,
Arch: RelocationArch,
M: Mmap,
{
mapper: M,
executor: Exec,
page_size: Option<PageSize>,
force_static_tls: bool,
_marker: PhantomData<fn() -> (D, Tls, Arch)>,
}
impl<D, Tls, Arch, M, Exec> Clone for Loader<D, Tls, Arch, M, Exec>
where
D: 'static,
Tls: TlsResolver<Arch>,
Arch: RelocationArch,
M: Mmap + Clone,
Exec: Clone,
{
#[inline]
fn clone(&self) -> Self {
Self {
mapper: self.mapper.clone(),
executor: self.executor.clone(),
page_size: self.page_size,
force_static_tls: self.force_static_tls,
_marker: PhantomData,
}
}
}
impl<D, Tls, Arch, M, Exec> Copy for Loader<D, Tls, Arch, M, Exec>
where
D: 'static,
Tls: TlsResolver<Arch>,
Arch: RelocationArch,
M: Mmap + Copy,
Exec: Copy,
{
}
struct LoaderFields<M, Exec> {
mapper: NoDrop<M>,
executor: NoDrop<Exec>,
page_size: Option<PageSize>,
force_static_tls: bool,
}
impl<M, Exec> LoaderFields<M, Exec> {
#[inline]
const fn into_loader<D, Tls, Arch>(self) -> Loader<D, Tls, Arch, M, Exec>
where
D: 'static,
Tls: TlsResolver<Arch>,
Arch: RelocationArch,
M: Mmap,
{
let Self {
mapper,
executor,
page_size,
force_static_tls,
} = self;
Loader {
mapper: mapper.into_inner(),
executor: executor.into_inner(),
page_size,
force_static_tls,
_marker: PhantomData,
}
}
#[inline]
const fn with_executor<D, Tls, Arch, NewExec>(
self,
executor: NewExec,
) -> Loader<D, Tls, Arch, M, NewExec>
where
D: 'static,
Tls: TlsResolver<Arch>,
Arch: RelocationArch,
M: Mmap,
Exec: Copy,
{
let Self {
mapper,
page_size,
force_static_tls,
..
} = self;
Loader {
mapper: mapper.into_inner(),
executor,
page_size,
force_static_tls,
_marker: PhantomData,
}
}
#[inline]
const fn with_mapper<D, Tls, Arch, NewM>(self, mapper: NewM) -> Loader<D, Tls, Arch, NewM, Exec>
where
D: 'static,
Tls: TlsResolver<Arch>,
Arch: RelocationArch,
NewM: Mmap,
M: Copy,
{
let Self {
executor,
page_size,
force_static_tls,
..
} = self;
Loader {
mapper,
executor: executor.into_inner(),
page_size,
force_static_tls,
_marker: PhantomData,
}
}
}
impl Loader<(), (), NativeArch, DefaultMmap, NativeCodeExecutor> {
#[inline]
pub const fn new() -> Self {
Self {
mapper: DefaultMmap::new(),
executor: NativeCodeExecutor,
page_size: None,
force_static_tls: false,
_marker: PhantomData,
}
}
}
impl Default for Loader<(), (), NativeArch, DefaultMmap, NativeCodeExecutor> {
#[inline]
fn default() -> Self {
Self::new()
}
}
impl<D, Tls, Arch, M, Exec> Loader<D, Tls, Arch, M, Exec>
where
D: 'static,
Tls: TlsResolver<Arch>,
Arch: RelocationArch,
M: Mmap,
{
#[inline]
const fn into_fields(self) -> LoaderFields<M, Exec> {
let this = MaybeUninit::new(self);
let this = this.as_ptr();
unsafe {
LoaderFields {
mapper: NoDrop::read(ptr::addr_of!((*this).mapper)),
executor: NoDrop::read(ptr::addr_of!((*this).executor)),
page_size: ptr::read(ptr::addr_of!((*this).page_size)),
force_static_tls: ptr::read(ptr::addr_of!((*this).force_static_tls)),
}
}
}
}
impl<D, Tls, Arch, M, Exec> Loader<D, Tls, Arch, M, Exec>
where
D: 'static,
Tls: TlsResolver<Arch>,
Arch: RelocationArch,
M: Mmap,
Exec: CodeExecutor<Arch> + Clone,
{
#[inline]
pub(crate) const fn mapper(&self) -> &M {
&self.mapper
}
#[inline]
pub(crate) const fn force_static_tls(&self) -> bool {
self.force_static_tls
}
#[inline]
pub(crate) fn executor(&self) -> Arc<dyn CodeExecutor<Arch>> {
Arc::from(Box::new(self.executor.clone()) as Box<dyn CodeExecutor<Arch>>)
}
#[inline]
pub fn run(&self) -> LoaderRun<'_, (), D, Tls, Arch, M, Exec> {
LoaderRun {
loader: self,
observer: (),
buf: super::ElfBuf::new(),
#[cfg(feature = "object")]
object_groups: Arc::new(SectionGroups::default()),
}
}
#[inline]
pub(crate) fn page_size(&self) -> Result<PageSize> {
let required = self.mapper.page_size();
let page_size = self.page_size.unwrap_or(required);
if page_size.bytes() < required.bytes()
|| !page_size.bytes().is_multiple_of(required.bytes())
{
return Err(MmapError::InvalidPageSize {
configured: page_size.bytes(),
required: required.bytes(),
}
.into());
}
Ok(page_size)
}
pub const fn with_data<NewD>(self) -> Loader<NewD, Tls, Arch, M, Exec>
where
NewD: Default + 'static,
{
self.into_fields().into_loader()
}
pub const fn with_page_size(mut self, page_size: PageSize) -> Self {
self.page_size = Some(page_size);
self
}
pub const fn with_executor<E>(self, executor: E) -> Loader<D, Tls, Arch, M, E>
where
E: CodeExecutor<Arch> + Clone,
Exec: Copy,
{
self.into_fields().with_executor(executor)
}
pub const fn with_tls_resolver<NewTls>(self) -> Loader<D, NewTls, Arch, M, Exec>
where
NewTls: TlsResolver<Arch>,
{
self.into_fields().into_loader()
}
pub const fn with_static_tls(mut self, enabled: bool) -> Self {
self.force_static_tls = enabled;
self
}
}
impl<D, Tls, M, Exec> Loader<D, Tls, NativeArch, M, Exec>
where
D: 'static,
Tls: TlsResolver<NativeArch>,
M: Mmap,
Exec: CodeExecutor<NativeArch> + Clone,
{
#[cfg(feature = "tls")]
pub const fn with_default_tls_resolver(
self,
) -> Loader<D, crate::tls::DefaultTlsResolver, NativeArch, M, Exec> {
self.into_fields().into_loader()
}
}
impl<Tls, Arch, M, Exec> Loader<(), Tls, Arch, M, Exec>
where
Tls: TlsResolver<Arch>,
Arch: RelocationArch,
M: Mmap,
Exec: CodeExecutor<Arch> + Clone,
{
pub const fn with_mmap<NewMmap>(self, mapper: NewMmap) -> Loader<(), Tls, Arch, NewMmap, Exec>
where
NewMmap: Mmap,
M: Copy,
{
self.into_fields().with_mapper(mapper)
}
pub const fn for_arch<NewArch>(self) -> Loader<(), Tls, NewArch, M, NativeCodeExecutor>
where
NewArch: RelocationArch,
Tls: TlsResolver<NewArch>,
Exec: Copy,
{
self.into_fields().with_executor(NativeCodeExecutor)
}
}