use super::{
arena::{ArenaDescriptor, ArenaId, ArenaUsage},
section::{
DataAccess, ModuleLayout, SectionArena, SectionDataAccessRef, SectionId, SectionMetadata,
SectionPlacement,
},
};
use crate::{
AlignedBytes, LinkerError, Result,
entity::{PrimaryMap, SecondaryMap},
image::{ModuleCapability, ScannedDynamic, ScannedSectionId},
linker::scan::ModuleId,
relocation::RelocationArch,
segment::align_up,
};
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Materialization {
WholeDsoRegion,
SectionRegions,
}
impl Materialization {
pub(crate) const fn default(capability: ModuleCapability) -> Self {
if capability.supports_reorder_repair() {
Self::SectionRegions
} else {
Self::WholeDsoRegion
}
}
}
#[derive(Debug, Clone, Default)]
pub(in crate::linker) struct MemoryLayoutPlan {
arenas: PrimaryMap<ArenaId, ArenaDescriptor>,
modules: SecondaryMap<ModuleId, ModuleLayout>,
materialization: SecondaryMap<ModuleId, Materialization>,
sections: SectionArena,
}
impl MemoryLayoutPlan {
#[inline]
pub(in crate::linker) fn arena_pairs(
&self,
) -> impl Iterator<Item = (ArenaId, &ArenaDescriptor)> {
self.arenas.iter()
}
#[inline]
pub(in crate::linker) fn arena(&self, id: ArenaId) -> &ArenaDescriptor {
self.arenas
.get(id)
.expect("layout plan referenced missing arena")
}
#[inline]
pub(in crate::linker) fn module(&self, module_id: ModuleId) -> &ModuleLayout {
self.modules
.get(module_id)
.expect("layout plan referenced missing module layout")
}
#[inline]
pub(in crate::linker) fn materialization(
&self,
module_id: ModuleId,
) -> Option<Materialization> {
self.materialization.get(module_id).copied()
}
#[inline]
pub(in crate::linker) fn set_materialization(
&mut self,
module_id: ModuleId,
mode: Materialization,
) -> Option<Materialization> {
self.materialization.insert(module_id, mode)
}
#[inline]
pub(in crate::linker) fn sections(
&self,
) -> impl Iterator<Item = (SectionId, &SectionMetadata)> {
self.sections.iter()
}
#[inline]
pub(in crate::linker) fn section(&self, id: SectionId) -> &SectionMetadata {
self.sections
.get(id)
.expect("layout plan referenced missing section metadata")
}
#[inline]
pub(in crate::linker) fn data(&self, section: SectionId) -> Option<&AlignedBytes> {
self.sections.data(section)
}
#[inline]
pub(in crate::linker) fn data_mut(&mut self, section: SectionId) -> Option<&mut AlignedBytes> {
self.sections.data_mut(section)
}
#[inline]
pub(in crate::linker) fn resize_section(
&mut self,
section: SectionId,
byte_len: usize,
) -> Result<()> {
self.sections.resize_data(section, byte_len).ok_or_else(|| {
LinkerError::section_data("section data length overflow or missing materialized data")
.into()
})
}
#[inline]
pub(in crate::linker) fn install_data(&mut self, section: SectionId, bytes: AlignedBytes) {
self.sections.install_data(section, bytes);
}
#[inline]
pub(in crate::linker) fn mark_section_data_override(&mut self, section: SectionId) {
self.sections.mark_data_override(section);
}
#[inline]
pub(in crate::linker) fn with_disjoint_section_data<const N: usize, R>(
&mut self,
accesses: [(SectionId, DataAccess); N],
f: impl FnOnce([SectionDataAccessRef<'_>; N]) -> R,
) -> Option<R> {
self.sections.with_disjoint_data(accesses, f)
}
#[inline]
pub(in crate::linker) fn section_is_override(&self, section: SectionId) -> bool {
self.sections.is_override(section)
}
#[inline]
pub(in crate::linker) fn owner(&self, section: SectionId) -> Option<ModuleId> {
self.sections.owner(section)
}
#[inline]
pub(in crate::linker) fn placement(&self, section: SectionId) -> Option<SectionPlacement> {
self.sections.placement(section)
}
pub(in crate::linker) fn section_placements(
&self,
) -> impl Iterator<Item = (SectionId, SectionPlacement)> + '_ {
self.sections.placements()
}
#[inline]
pub(in crate::linker) fn section_id(
&self,
module_id: ModuleId,
id: impl Into<ScannedSectionId>,
) -> Option<SectionId> {
self.module(module_id).section_id(id)
}
fn arena_section_placements(
&self,
arena: ArenaId,
) -> impl Iterator<Item = (SectionId, SectionPlacement)> + '_ {
self.sections.placements_in(arena)
}
pub(in crate::linker) fn usage(&self, id: ArenaId) -> ArenaUsage {
let arena = self.arena(id);
let mut section_count = 0usize;
let mut used_len = 0usize;
for (_, placement) in self.arena_section_placements(id) {
section_count += 1;
let section_end = placement
.offset()
.checked_add(placement.size())
.expect("arena usage overflowed while computing section end");
used_len = used_len.max(section_end);
}
let mapped_len = align_up(used_len, arena.page_size().bytes());
ArenaUsage::new(section_count, used_len, mapped_len)
}
pub(in crate::linker) fn next_offset(&self, arena: ArenaId, alignment: usize) -> usize {
align_up(self.usage(arena).used_len(), alignment)
}
#[inline]
fn placement_for(
&self,
section: SectionId,
arena: ArenaId,
offset: usize,
) -> Option<SectionPlacement> {
let metadata = self.section(section);
if !metadata.is_allocated() {
return None;
}
let memory_class = metadata.memory_class()?;
if memory_class != self.arena(arena).memory_class() {
return None;
}
Some(SectionPlacement::new(arena, offset, metadata.size()))
}
pub(in crate::linker) fn assign_next(&mut self, section: SectionId, arena: ArenaId) -> bool {
let offset = self.next_offset(arena, self.section(section).alignment());
self.assign(section, arena, offset)
}
pub(in crate::linker) fn assign(
&mut self,
section: SectionId,
arena: ArenaId,
offset: usize,
) -> bool {
let Some(placement) = self.placement_for(section, arena, offset) else {
return false;
};
self.sections.set_placement(section, placement)
}
pub(in crate::linker) fn clear_section(
&mut self,
section: SectionId,
) -> Option<SectionPlacement> {
self.sections.clear_placement(section)
}
#[inline]
pub(in crate::linker) fn create_arena(&mut self, arena: ArenaDescriptor) -> ArenaId {
self.arenas.push(arena)
}
pub(in crate::linker) fn from_scanned<'a, I, Arch>(modules: I) -> Self
where
Arch: RelocationArch + 'a,
I: IntoIterator<Item = (ModuleId, &'a ScannedDynamic<Arch>)>,
{
let mut plan = Self::default();
for (module_id, module) in modules {
let layout = ModuleLayout::from_scanned(module_id, module, &mut plan.sections);
plan.modules.insert(module_id, layout);
}
plan
}
}