use super::{
ArenaDescriptor, SectionId,
layout::{DataAccess, SectionDataAccessRef},
plan::{LinkPlan, ModuleId},
};
use crate::{
LinkerError, Result, aligned_bytes::ByteRepr, image::ModuleCapability,
relocation::RelocationArch,
};
use alloc::{boxed::Box, vec::Vec};
use core::marker::PhantomData;
mod arena;
mod module;
mod section;
pub use arena::Arena;
pub use module::Module;
pub use section::Section;
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
pub enum PassScope {
#[default]
Any,
SectionData,
SectionReorderable,
}
impl PassScope {
#[inline]
pub(crate) const fn matches(self, capability: ModuleCapability) -> bool {
match self {
Self::Any => true,
Self::SectionData => !matches!(capability, ModuleCapability::Opaque),
Self::SectionReorderable => matches!(capability, ModuleCapability::SectionReorderable),
}
}
}
mod sealed {
pub trait Sealed {}
}
pub trait PassScopeMode: sealed::Sealed {
const SCOPE: PassScope;
}
#[derive(Debug, Clone, Copy, Default)]
pub struct AnyPass;
#[derive(Debug, Clone, Copy, Default)]
pub struct DataPass;
#[derive(Debug, Clone, Copy, Default)]
pub struct ReorderPass;
impl sealed::Sealed for AnyPass {}
impl sealed::Sealed for DataPass {}
impl sealed::Sealed for ReorderPass {}
impl PassScopeMode for AnyPass {
const SCOPE: PassScope = PassScope::Any;
}
impl PassScopeMode for DataPass {
const SCOPE: PassScope = PassScope::SectionData;
}
impl PassScopeMode for ReorderPass {
const SCOPE: PassScope = PassScope::SectionReorderable;
}
pub trait SectionDataAccess: PassScopeMode {}
impl SectionDataAccess for DataPass {}
impl SectionDataAccess for ReorderPass {}
pub trait ReorderAccess: SectionDataAccess {}
impl ReorderAccess for ReorderPass {}
pub struct LinkPassPlan<'a, K, S = AnyPass, Arch: RelocationArch = crate::arch::NativeArch>
where
S: PassScopeMode,
{
plan: &'a mut LinkPlan<K, Arch>,
scope: PhantomData<fn() -> S>,
}
impl<'a, K, S, Arch> LinkPassPlan<'a, K, S, Arch>
where
K: Clone + Ord,
S: PassScopeMode,
Arch: RelocationArch,
{
#[inline]
fn new(plan: &'a mut LinkPlan<K, Arch>) -> Self {
Self {
plan,
scope: PhantomData,
}
}
#[inline]
fn accepts_module(&self, id: ModuleId) -> bool {
self.plan
.module_capability(id)
.is_some_and(|capability| S::SCOPE.matches(capability))
}
#[inline]
fn accepts_section(&self, section: SectionId) -> bool {
self.plan
.section_owner(section)
.is_some_and(|id| self.accepts_module(id))
}
#[inline]
fn checked_module(&self, id: ModuleId) -> Option<Module<'a, S>> {
self.accepts_module(id).then(|| Module::new(id))
}
#[inline]
pub fn root_key(&self) -> &K {
self.plan.root_key()
}
#[inline]
pub fn root(&self) -> Option<Module<'a, S>> {
self.checked_module(self.plan.root_module())
}
#[inline]
pub fn contains_key(&self, key: &K) -> bool {
self.plan.contains_key(key)
}
#[inline]
pub fn module(&self, key: &K) -> Option<Module<'a, S>> {
self.checked_module(self.plan.module_id(key)?)
}
pub fn modules(&self) -> impl Iterator<Item = Module<'a, S>> + '_ {
self.plan
.group_order()
.iter()
.copied()
.filter_map(move |id| self.checked_module(id))
}
}
impl<'a, K, S, Arch> LinkPassPlan<'a, K, S, Arch>
where
K: Clone + Ord,
S: SectionDataAccess,
Arch: RelocationArch,
{
#[inline]
fn checked_section(&self, id: SectionId) -> Option<Section<'a, S>> {
self.accepts_section(id).then(|| Section::new(id))
}
pub fn sections(&self) -> impl Iterator<Item = Section<'a, S>> + '_ {
self.plan
.memory_layout()
.sections()
.filter_map(move |(section, _)| self.checked_section(section))
}
#[inline]
pub fn for_each_section_data<T, P>(
&mut self,
section: Section<'a, S>,
mut prepare: impl FnMut(&T, &Self) -> Result<Option<P>>,
mut apply: impl FnMut(&mut Self, usize, P) -> Result<()>,
) -> Result<()>
where
T: ByteRepr,
{
let section = section.id();
self.plan.materialize_section_data(section)?;
let entry_count = {
let data =
self.plan.memory_layout().data(section).ok_or_else(|| {
LinkerError::section_data("section data was not materialized")
})?;
data.try_cast_slice::<T>()
.ok_or_else(|| {
LinkerError::section_data(
"section data bytes do not match requested entry type",
)
})?
.len()
};
for index in 0..entry_count {
let prepared = {
let data = self.plan.memory_layout().data(section).ok_or_else(|| {
LinkerError::section_data("section data was not materialized")
})?;
let entries = data.try_cast_slice::<T>().ok_or_else(|| {
LinkerError::section_data(
"section data bytes do not match requested entry type",
)
})?;
let entry = entries
.get(index)
.expect("section data entry index should remain valid");
prepare(entry, &*self)?
};
if let Some(prepared) = prepared {
apply(self, index, prepared)?;
}
}
Ok(())
}
#[inline]
pub fn with_disjoint_section_data<const N: usize, R>(
&mut self,
accesses: [(Section<'a, S>, DataAccess); N],
f: impl FnOnce([SectionDataAccessRef<'_>; N]) -> Result<R>,
) -> Result<R> {
let accesses = accesses.map(|(section, access)| (section.id(), access));
self.plan.with_disjoint_section_data(accesses, f)
}
}
impl<'a, K, S, Arch> LinkPassPlan<'a, K, S, Arch>
where
K: Clone + Ord,
S: ReorderAccess,
Arch: RelocationArch,
{
#[inline]
pub fn create_arena(&mut self, arena: ArenaDescriptor) -> Arena<'a> {
Arena::new(self.plan.memory_layout_mut().create_arena(arena))
}
#[inline]
pub fn arenas(&self) -> impl Iterator<Item = Arena<'a>> + '_ {
self.plan
.memory_layout()
.arena_pairs()
.map(|(arena, _)| Arena::new(arena))
}
}
pub trait LinkPass<K: Clone + Ord, S = AnyPass, Arch: RelocationArch = crate::arch::NativeArch>
where
S: PassScopeMode,
{
fn run(&mut self, plan: &mut LinkPassPlan<'_, K, S, Arch>) -> Result<()>;
}
type PipelinePass<'a, K, Arch> = Box<dyn FnMut(&mut LinkPlan<K, Arch>) -> Result<()> + 'a>;
pub struct LinkPipeline<'a, K: Clone + Ord, Arch: RelocationArch = crate::arch::NativeArch> {
passes: Vec<PipelinePass<'a, K, Arch>>,
}
impl<'a, K, Arch> Default for LinkPipeline<'a, K, Arch>
where
K: Clone + Ord,
Arch: RelocationArch,
{
#[inline]
fn default() -> Self {
Self::new()
}
}
impl<'a, K, Arch> LinkPipeline<'a, K, Arch>
where
K: Clone + Ord,
Arch: RelocationArch,
{
#[inline]
pub fn new() -> Self {
Self { passes: Vec::new() }
}
#[inline]
pub fn push<S, P>(&mut self, mut pass: P) -> &mut Self
where
S: PassScopeMode + 'a,
P: LinkPass<K, S, Arch> + 'a,
{
self.passes.push(Box::new(move |plan| {
let mut scoped = LinkPassPlan::<_, S, Arch>::new(plan);
pass.run(&mut scoped)
}));
self
}
pub(crate) fn run(&mut self, plan: &mut LinkPlan<K, Arch>) -> Result<()> {
for pass in &mut self.passes {
pass(plan)?;
}
Ok(())
}
}