use super::{
context::LinkContext,
run::LinkerRun,
scan::LinkPipeline,
storage::{ModuleId, ModuleLease},
unload::UnloadGroup,
};
use crate::{
Loader, Relocator, Result,
arch::NativeArch,
const_builder::NoDrop,
os::Mmap,
relocation::{LookupOrder, RelocationArch},
runtime::CodeExecutor,
tls::TlsResolver,
};
use alloc::{boxed::Box, vec::Vec};
use core::{fmt, marker::PhantomData, mem::MaybeUninit, ptr};
#[must_use = "a loaded module lease must eventually be released"]
pub struct LoadResult {
lease: ModuleLease,
modules: Box<[ModuleId]>,
}
impl fmt::Debug for LoadResult {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("LoadResult")
.field("root_id", &self.lease.id())
.field("modules", &self.modules)
.finish()
}
}
impl LoadResult {
#[inline]
pub(crate) fn new(lease: ModuleLease, modules: Box<[ModuleId]>) -> Self {
Self { lease, modules }
}
#[inline]
pub const fn root(&self) -> ModuleId {
self.lease.id()
}
#[inline]
pub fn modules(&self) -> &[ModuleId] {
&self.modules
}
#[inline]
pub fn into_parts(self) -> (ModuleLease, Box<[ModuleId]>) {
(self.lease, self.modules)
}
#[inline]
pub fn release<Meta, Arch, Tls>(
self,
context: &mut LinkContext<Meta, Arch, Tls>,
) -> Result<UnloadGroup<Meta, Arch, Tls>>
where
Arch: RelocationArch,
Tls: TlsResolver<Arch>,
{
context.release(self.lease)
}
}
pub struct Linker<
Arch: RelocationArch = NativeArch,
L = Loader<(), (), Arch>,
R = (),
RelocBinder = (),
Tls: TlsResolver<Arch> = (),
> {
pub(super) loader: L,
pub(super) resolver: R,
pub(super) relocator: Relocator<RelocBinder>,
marker: PhantomData<(Arch, Tls)>,
}
impl<Arch, L, R, RelocBinder, Tls> Clone for Linker<Arch, L, R, RelocBinder, Tls>
where
Arch: RelocationArch,
L: Clone,
R: Clone,
Relocator<RelocBinder>: Clone,
Tls: TlsResolver<Arch>,
{
#[inline]
fn clone(&self) -> Self {
Self {
loader: self.loader.clone(),
resolver: self.resolver.clone(),
relocator: self.relocator.clone(),
marker: PhantomData,
}
}
}
struct LinkerFields<L, R, RelocBinder> {
loader: NoDrop<L>,
resolver: NoDrop<R>,
relocator: NoDrop<Relocator<RelocBinder>>,
}
impl Linker {
#[inline]
pub const fn new() -> Self {
Self {
loader: Loader::new(),
resolver: (),
relocator: Relocator::new().defer_init(),
marker: PhantomData,
}
}
#[inline]
#[allow(clippy::type_complexity)]
pub const fn for_arch<NewArch>(self) -> Linker<NewArch, Loader<(), (), NewArch>, (), ()>
where
NewArch: RelocationArch,
{
Linker {
loader: self.loader.for_arch::<NewArch>(),
resolver: (),
relocator: self.relocator,
marker: PhantomData,
}
}
}
impl Default for Linker {
#[inline]
fn default() -> Self {
Self::new()
}
}
impl<L, R, RelocBinder, Arch, Tls> Linker<Arch, L, R, RelocBinder, Tls>
where
Arch: RelocationArch,
Tls: TlsResolver<Arch>,
{
#[inline]
const fn into_fields(self) -> LinkerFields<L, R, RelocBinder> {
let this = MaybeUninit::new(self);
let this = this.as_ptr();
unsafe {
LinkerFields {
loader: NoDrop::read(ptr::addr_of!((*this).loader)),
resolver: NoDrop::read(ptr::addr_of!((*this).resolver)),
relocator: NoDrop::read(ptr::addr_of!((*this).relocator)),
}
}
}
pub const fn resolver<NewR>(self, resolver: NewR) -> Linker<Arch, L, NewR, RelocBinder, Tls>
where
R: Copy,
{
let LinkerFields {
loader, relocator, ..
} = self.into_fields();
Linker {
loader: loader.into_inner(),
resolver,
relocator: relocator.into_inner(),
marker: PhantomData,
}
}
pub const fn relocator<NewRelocBinder>(
self,
relocator: Relocator<NewRelocBinder>,
) -> Linker<Arch, L, R, NewRelocBinder, Tls>
where
Relocator<RelocBinder>: Copy,
{
let LinkerFields {
loader, resolver, ..
} = self.into_fields();
Linker {
loader: loader.into_inner(),
resolver: resolver.into_inner(),
relocator: relocator.defer_init(),
marker: PhantomData,
}
}
pub fn map_relocator<NewRelocBinder>(
self,
configure: impl FnOnce(Relocator<RelocBinder>) -> Relocator<NewRelocBinder>,
) -> Linker<Arch, L, R, NewRelocBinder, Tls> {
Linker {
loader: self.loader,
resolver: self.resolver,
relocator: configure(self.relocator).defer_init(),
marker: PhantomData,
}
}
#[inline]
pub fn run<'pipe>(&self) -> LinkerRun<'_, 'pipe, Arch, L, R, RelocBinder, Tls, ()> {
LinkerRun {
linker: self,
pipeline: LinkPipeline::new(),
observer: (),
caller: None,
lookup_order: LookupOrder::GlobalFirst,
scratch_order: Vec::new(),
}
}
}
impl<D: Send + Sync + 'static, Tls, Arch, M, Exec, R>
Linker<Arch, Loader<D, Tls, Arch, M, Exec>, R, (), Tls>
where
D: Send + Sync + 'static,
Tls: TlsResolver<Arch>,
Arch: RelocationArch,
M: Mmap,
Exec: CodeExecutor<Arch> + Clone,
{
#[allow(clippy::type_complexity)]
pub const fn loader<NewD, NewTls, NewM, NewExec>(
self,
loader: Loader<NewD, NewTls, Arch, NewM, NewExec>,
) -> Linker<Arch, Loader<NewD, NewTls, Arch, NewM, NewExec>, R, (), NewTls>
where
Loader<D, Tls, Arch, M, Exec>: Copy,
NewD: Send + Sync + 'static,
NewTls: TlsResolver<Arch>,
NewM: Mmap,
NewExec: CodeExecutor<Arch> + Clone,
{
let LinkerFields {
relocator,
resolver,
..
} = self.into_fields();
Linker {
loader,
resolver: resolver.into_inner(),
relocator: relocator.into_inner(),
marker: PhantomData,
}
}
#[allow(clippy::type_complexity)]
pub fn map_loader<NewD, NewTls, NewM, NewExec>(
self,
configure: impl FnOnce(
Loader<D, Tls, Arch, M, Exec>,
) -> Loader<NewD, NewTls, Arch, NewM, NewExec>,
) -> Linker<Arch, Loader<NewD, NewTls, Arch, NewM, NewExec>, R, (), NewTls>
where
NewD: Send + Sync + 'static,
NewTls: TlsResolver<Arch>,
NewM: Mmap,
NewExec: CodeExecutor<Arch> + Clone,
{
Linker {
loader: configure(self.loader),
resolver: self.resolver,
relocator: self.relocator,
marker: PhantomData,
}
}
}