use crate::{
elf::{
ElfHeader, ElfLayout, ElfSectionId, ElfSectionType, ElfShdr, Lifecycle, NativeElfLayout,
SymbolTableView,
},
image::{ElfCore, RawObject, SymbolExports, exports_handle},
input::{ElfReader, ElfReaderExt, Path},
memory::{HostRegion, ImageMemory, RegionAccess, VmAddr},
object::{
CustomHash, ObjectExports, ObjectSections, ObjectSegmentView,
layout::{SectionGroup, SectionPlacement},
},
relocation::{ObjectArch, RelocationArch},
sync::Arc,
tls::TlsResolver,
};
use alloc::vec::Vec;
use core::{ffi::CStr, ptr::NonNull};
use super::lifecycle::{Finalizer, FiniEvent};
type ObjectExportsHandle<L> = Option<Arc<dyn SymbolExports<L>>>;
pub struct SectionLayoutEvent<'event, L: ElfLayout = NativeElfLayout> {
sections: &'event mut ObjectSections<L>,
placements: Vec<Option<SectionPlacement>>,
}
impl<'event, L: ElfLayout> SectionLayoutEvent<'event, L> {
#[inline]
pub(crate) fn new(sections: &'event mut ObjectSections<L>) -> Self {
let mut placements = Vec::new();
placements.resize(sections.headers().len(), None);
Self {
sections,
placements,
}
}
#[inline]
pub(crate) fn into_placements(self) -> Vec<Option<SectionPlacement>> {
self.placements
}
#[inline]
pub fn section_ids(&self) -> impl Iterator<Item = ElfSectionId> + '_ {
(0..self.sections.headers().len()).map(ElfSectionId::new)
}
#[inline]
pub fn place(&mut self, id: ElfSectionId, group: SectionGroup) {
self.placements[id.index()] = Some(SectionPlacement::Place(group));
}
#[inline]
pub fn skip(&mut self, id: ElfSectionId) {
self.placements[id.index()] = Some(SectionPlacement::Skip);
}
#[inline]
pub fn group(&self, id: ElfSectionId) -> Option<SectionGroup> {
match self.placements[id.index()] {
Some(SectionPlacement::Place(group)) => Some(group),
Some(SectionPlacement::Skip) | None => None,
}
}
#[inline]
pub fn sections(&self) -> &ObjectSections<L> {
self.sections
}
}
pub struct AfterObjectLoadEvent<
'event,
D: 'static,
Arch: ObjectArch,
R: RegionAccess = HostRegion,
Tls: TlsResolver<Arch> = (),
> {
raw: &'event mut RawObject<D, Arch, R, Tls>,
}
impl<'event, D: 'static, Arch, R, Tls> AfterObjectLoadEvent<'event, D, Arch, R, Tls>
where
Arch: ObjectArch,
R: RegionAccess,
Tls: TlsResolver<Arch>,
{
#[inline]
pub(crate) const fn new(raw: &'event mut RawObject<D, Arch, R, Tls>) -> Self {
Self { raw }
}
#[inline]
pub const fn raw(&self) -> &RawObject<D, Arch, R, Tls> {
self.raw
}
#[inline]
pub fn raw_mut(&mut self) -> &mut RawObject<D, Arch, R, Tls> {
self.raw
}
}
pub struct ObjectRelocatedEvent<
'event,
D: 'static,
Arch: RelocationArch,
R: RegionAccess = HostRegion,
Tls: TlsResolver<Arch> = (),
> {
core: &'event ElfCore<D, Arch, R, Tls>,
sections: &'event ObjectSections<Arch::Layout>,
symtab: SymbolTableView<'event, Arch::Layout, CustomHash>,
memory: ObjectSegmentView<'event, R>,
exports: ObjectExportsHandle<Arch::Layout>,
finalizer: Finalizer,
}
impl<'event, D: 'static, Arch: RelocationArch, R: RegionAccess, Tls: TlsResolver<Arch>>
ObjectRelocatedEvent<'event, D, Arch, R, Tls>
{
#[inline]
pub(crate) fn new(
core: &'event ElfCore<D, Arch, R, Tls>,
sections: &'event ObjectSections<Arch::Layout>,
symtab: SymbolTableView<'event, Arch::Layout, CustomHash>,
memory: ObjectSegmentView<'event, R>,
finalizer: Finalizer,
) -> Self {
Self {
core,
sections,
symtab,
memory,
exports: None,
finalizer,
}
}
#[inline]
pub const fn core(&self) -> &ElfCore<D, Arch, R, Tls> {
self.core
}
#[inline]
pub const fn sections(&self) -> &'event ObjectSections<Arch::Layout> {
self.sections
}
#[inline]
pub fn section_is_mapped(&self, id: ElfSectionId) -> bool {
self.sections.section_is_mapped(id)
}
#[inline]
pub const fn symtab(&self) -> SymbolTableView<'event, Arch::Layout, CustomHash> {
self.symtab
}
#[inline]
pub const fn memory(&self) -> ObjectSegmentView<'event, R> {
self.memory
}
#[inline]
pub fn section_addr(&self, id: ElfSectionId) -> Option<VmAddr> {
self.section_is_mapped(id)
.then(|| VmAddr::new(self.sections.section(id).sh_addr()))
}
#[inline]
pub fn section_size(&self, id: ElfSectionId) -> usize {
self.sections.section(id).sh_size()
}
#[inline]
pub fn section_host_ptr(&self, id: ElfSectionId) -> Option<NonNull<u8>> {
self.section_host_ptr_range(id, self.section_size(id))
}
pub fn section_host_ptr_range(&self, id: ElfSectionId, len: usize) -> Option<NonNull<u8>> {
let addr = self.section_addr(id)?;
if len > self.section_size(id) {
return None;
}
self.memory.host_ptr_range(addr, len)
}
#[inline]
pub fn set_exports<E>(&mut self, exports: E)
where
E: SymbolExports<Arch::Layout> + 'static,
{
self.exports = Some(exports_handle(exports));
}
#[inline]
pub fn clear_exports(&mut self) {
self.set_exports(ObjectExports::<Arch::Layout>::empty());
}
#[inline]
pub fn fini(&self) -> &Lifecycle {
self.finalizer.lifecycle()
}
#[inline]
pub fn fini_mut(&mut self) -> &mut Lifecycle {
self.finalizer.lifecycle_mut()
}
#[inline]
pub fn set_fini_hook<F>(&mut self, hook: F)
where
F: for<'fini> Fn(&mut FiniEvent<'fini>) -> crate::Result<()> + Send + Sync + 'static,
{
self.finalizer.set_hook(hook);
}
#[inline]
pub(crate) fn into_parts(self) -> (ObjectExportsHandle<Arch::Layout>, Finalizer) {
(self.exports, self.finalizer)
}
}
pub struct BeforeObjectLoadEvent<'event, D: 'static, L: ElfLayout = NativeElfLayout> {
ehdr: &'event ElfHeader<L>,
sections: &'event mut ObjectSections<L>,
object: &'event dyn ElfReader,
user_data: &'event mut D,
}
impl<'event, D: 'static, L: ElfLayout> BeforeObjectLoadEvent<'event, D, L> {
#[inline]
pub(crate) fn new(
ehdr: &'event ElfHeader<L>,
sections: &'event mut ObjectSections<L>,
object: &'event dyn ElfReader,
user_data: &'event mut D,
) -> Self {
Self {
ehdr,
sections,
object,
user_data,
}
}
#[inline]
pub fn path(&self) -> &Path {
self.object.path()
}
#[inline]
pub const fn ehdr(&self) -> &ElfHeader<L> {
self.ehdr
}
#[inline]
pub fn sections(&self) -> &[ElfShdr<L>] {
self.sections.headers()
}
#[inline]
pub fn section(&self, id: ElfSectionId) -> &ElfShdr<L> {
self.sections.section(id)
}
#[inline]
pub fn sections_mut(&mut self) -> &mut [ElfShdr<L>] {
self.sections.headers_mut()
}
#[inline]
pub fn section_mut(&mut self, id: ElfSectionId) -> &mut ElfShdr<L> {
self.sections.section_mut(id)
}
#[inline]
pub fn section_name_table(&self) -> &[u8] {
self.sections.name_table()
}
#[inline]
pub fn section_name(&self, id: ElfSectionId) -> &CStr {
self.sections.section_name(id)
}
#[inline]
pub fn find_section(&self, name: &str) -> Option<ElfSectionId> {
self.sections.find_section(name)
}
pub fn borrow_section_bytes(&self, id: ElfSectionId) -> crate::Result<Option<&[u8]>> {
let Some((offset, len)) = self.section_content_range(id) else {
return Ok(Some(&[]));
};
self.object.borrow_bytes(offset, len)
}
pub fn with_section_bytes<T>(
&mut self,
id: ElfSectionId,
scratch: &mut Vec<u8>,
f: impl FnOnce(&[u8]) -> crate::Result<T>,
) -> crate::Result<T> {
let Some((offset, len)) = self.section_content_range(id) else {
return f(&[]);
};
self.object.with_bytes::<u8, _, _>(offset, len, scratch, f)
}
#[inline]
pub const fn user_data(&self) -> &D {
self.user_data
}
#[inline]
pub fn user_data_mut(&mut self) -> &mut D {
self.user_data
}
fn section_content_range(&self, id: ElfSectionId) -> Option<(usize, usize)> {
let shdr = self.section(id);
let len = shdr.sh_size();
if len == 0 || shdr.section_type() == ElfSectionType::NOBITS {
return None;
}
Some((shdr.sh_offset(), len))
}
}