use crate::arch::NativeArch;
use crate::elf::ElfRelType;
#[cfg(feature = "version")]
use crate::elf::version::ELFVersion;
use crate::{
ByteRepr, ParseDynamicError, ParsePhdrError, Result,
elf::{
ElfDyn, ElfDynamic, ElfDynamicTag, ElfLayout, ElfPhdr, ElfPhdrs, ElfStringTable, ElfSymbol,
HashTable, Lifecycle, LifecycleSpec, SymbolTable,
},
lazy::{LazyBinder, SupportLazy},
loader::ImageBuilder,
logging,
memory::{HostRegion, ImageMemoryExt, MappedView, RegionAccess, VmAddr, VmOffset},
observer::RelocationObserver,
relocation::{DynamicRelocation, Relocatable, RelocateArgs, RelocationArch},
runtime::DomainId,
segment::{ElfSegments, MemoryProtection},
sync::{Arc, OnceCell, arc_unsize},
tls::{CoreTlsState, TlsRequest, TlsResolver},
};
use alloc::{boxed::Box, vec::Vec};
use core::{ops::Deref, ptr::NonNull};
use crate::image::{
CoreRuntime, ElfCore, ElfModule, LoadedCore, ModuleSearch, ModuleState, SearchPathPool,
SymbolExports,
};
impl<L: ElfLayout> SymbolTable<L> {
pub(crate) fn from_dynamic<Arch, R>(
dynamic: &ElfDynamic<Arch>,
segments: &ElfSegments<R>,
) -> Result<Self>
where
Arch: RelocationArch<Layout = L>,
R: RegionAccess,
{
let hashtab = HashTable::from_dynamic(dynamic, segments)?;
let symtab_off = dynamic
.symtab
.checked_offset_from(segments.base())
.ok_or(ParseDynamicError::AddressOverflow)?;
let symbols = segments
.read_tail_view::<ElfSymbol<L>>(symtab_off)
.ok_or(ParseDynamicError::MalformedSymbolTable {
detail: "DT_SYMTAB is outside mapped ELF segments or has invalid alignment",
})?
.as_slice();
let strtab_size = dynamic
.strtab_size
.ok_or(ParseDynamicError::MissingRequiredTag {
tag: ElfDynamicTag::STRSZ,
})?;
let strtab_off = dynamic
.strtab
.checked_offset_from(segments.base())
.ok_or(ParseDynamicError::AddressOverflow)?;
let strtab = segments
.read_view::<u8>(strtab_off, strtab_size.get())
.ok_or(ParseDynamicError::MalformedStringTable {
detail: "DT_STRTAB string table size is malformed",
})?;
let strtab = ElfStringTable::new(strtab);
#[cfg(feature = "version")]
let version = ELFVersion::new(
dynamic.version_idx,
dynamic.verneed,
dynamic.verdef,
&strtab,
segments,
)?;
Ok(Self {
hashtab,
symbols,
strtab,
#[cfg(feature = "version")]
version,
})
}
}
pub(crate) struct PltRelocInfo<Arch: RelocationArch = NativeArch> {
pub(crate) relocs: MappedView<ElfRelType<Arch>>,
pub(crate) symbols: SymbolTable<Arch::Layout>,
}
impl<Arch: RelocationArch> PltRelocInfo<Arch> {
#[inline]
pub(crate) fn new(
pltrel: Option<MappedView<ElfRelType<Arch>>>,
symtab: SymbolTable<Arch::Layout>,
) -> Self {
Self {
relocs: pltrel.unwrap_or_else(MappedView::empty),
symbols: symtab,
}
}
}
pub(crate) struct DynamicInfo<Arch: RelocationArch = NativeArch> {
pub(crate) eh_frame_hdr: Option<NonNull<u8>>,
pub(crate) phdrs: ElfPhdrs<Arch::Layout>,
pub(crate) needed_libs: Box<[&'static str]>,
pub(crate) symbolic: bool,
}
struct ElfExtraData<Arch: RelocationArch = NativeArch> {
lazy: bool,
relocation: DynamicRelocation<Arch>,
relro: Option<MemoryProtection>,
dynamic_addr: VmAddr,
dt_debug_addr: Option<VmAddr>,
got_plt: Option<VmAddr>,
init: LifecycleSpec,
fini: LifecycleSpec,
symtab: SymbolTable<Arch::Layout>,
}
impl<Arch: RelocationArch> core::fmt::Debug for ElfExtraData<Arch> {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.debug_struct("ElfExtraData")
.field("lazy", &self.lazy)
.field("relro", &self.relro.is_some())
.finish()
}
}
pub struct RawDynamic<
D,
Arch = NativeArch,
R: RegionAccess = HostRegion,
Tls: TlsResolver<Arch> = (),
> where
D: Send + Sync + 'static,
Arch: RelocationArch,
{
entry: VmAddr,
interp: Option<&'static str>,
module: ElfCore<D, Arch, R, Tls>,
extra: ElfExtraData<Arch>,
}
impl<D: Send + Sync + 'static, Arch: RelocationArch, R: RegionAccess, Tls: TlsResolver<Arch>>
core::fmt::Debug for RawDynamic<D, Arch, R, Tls>
{
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.debug_struct("RawDynamic")
.field("entry", &format_args!("0x{:x}", self.entry.get()))
.field("module", &self.module)
.field("extra", &self.extra)
.finish()
}
}
impl<D: Send + Sync + 'static, Arch: RelocationArch, R: RegionAccess, Tls: TlsResolver<Arch>>
SupportLazy for RawDynamic<D, Arch, R, Tls>
{
}
impl<D: Send + Sync + 'static, Arch: RelocationArch, R: RegionAccess, Tls: TlsResolver<Arch>>
RawDynamic<D, Arch, R, Tls>
{
#[inline]
pub fn entry(&self) -> usize {
self.entry_addr().get()
}
#[inline]
pub(crate) fn entry_addr(&self) -> VmAddr {
self.entry
}
#[inline]
pub fn into_core(self) -> ElfCore<D, Arch, R, Tls> {
self.module
}
#[inline]
pub fn is_lazy(&self) -> bool {
self.extra.lazy
}
#[inline]
pub(crate) fn got_plt(&self) -> Option<VmAddr> {
self.extra.got_plt
}
#[inline]
pub fn interp(&self) -> Option<&str> {
self.interp
}
pub fn phdrs(&self) -> &[ElfPhdr<Arch::Layout>] {
self.module
.phdrs()
.expect("raw dynamic image should always carry program headers")
}
#[inline]
pub(crate) fn relocation(&self) -> &DynamicRelocation<Arch> {
&self.extra.relocation
}
pub(crate) fn resolve_lifecycle(&self) -> Result<(Lifecycle, Lifecycle)> {
let segments = self.module.segments();
let init = self
.extra
.init
.resolve::<Arch::Layout, R>(segments, "DT_INIT_ARRAY size is malformed")?;
let fini = self
.extra
.fini
.resolve::<Arch::Layout, R>(segments, "DT_FINI_ARRAY size is malformed")?;
Ok((init, fini))
}
#[inline]
pub(crate) fn relro(&self) -> Option<&MemoryProtection> {
self.extra.relro.as_ref()
}
#[inline]
pub fn user_data_mut(&mut self) -> Option<&mut D> {
self.module.user_data_mut()
}
#[inline]
pub(crate) fn dynamic_addr(&self) -> VmAddr {
self.extra.dynamic_addr
}
#[inline]
pub fn dt_debug_addr(&self) -> Option<VmAddr> {
self.extra.dt_debug_addr
}
#[inline]
pub fn write_dt_debug_addr(&self, addr: VmAddr) -> Result<bool> {
let Some(dt_debug_addr) = self.dt_debug_addr() else {
return Ok(false);
};
let entry = ElfDyn::<Arch::Layout>::new(ElfDynamicTag::DEBUG, addr.get());
unsafe { ImageMemoryExt::write_value(self.module.segments(), dt_debug_addr, entry)? };
Ok(true)
}
#[inline]
pub(crate) fn symtab(&self) -> &SymbolTable<Arch::Layout> {
&self.extra.symtab
}
}
impl<D: Send + Sync + 'static, Arch: RelocationArch, R: RegionAccess, Tls: TlsResolver<Arch>> Deref
for RawDynamic<D, Arch, R, Tls>
{
type Target = ElfCore<D, Arch, R, Tls>;
#[inline]
fn deref(&self) -> &Self::Target {
&self.module
}
}
impl<Tls, D: Send + Sync + 'static, Arch: RelocationArch, R: RegionAccess>
ImageBuilder<Tls, D, Arch, R>
where
Tls: TlsResolver<Arch>,
{
pub(crate) fn build_dynamic(
mut self,
phdrs: &[ElfPhdr<Arch::Layout>],
search_paths: Option<&mut SearchPathPool>,
) -> Result<RawDynamic<D, Arch, R, Tls>> {
self.parse_phdrs(phdrs)?;
let phdrs = self.create_phdrs(phdrs)?;
let dynamic = self.dynamic.ok_or(ParsePhdrError::MissingDynamicSection)?;
let dynamic_addr = self
.dynamic_addr
.ok_or(ParsePhdrError::MissingDynamicSection)?;
let dynamic = ElfDynamic::<Arch>::new(dynamic, dynamic_addr, &self.segments)?;
logging::trace!("[{}] Dynamic info: {:?}", self.path, dynamic);
let relocation = DynamicRelocation::new(
dynamic.pltrel.clone(),
dynamic.dynrel.clone(),
dynamic.relr.clone(),
dynamic.rel_count,
dynamic.pltrel_is_dynrel_tail,
)?;
let static_tls = self.static_tls | dynamic.static_tls;
let symtab = SymbolTable::from_dynamic(&dynamic, &self.segments)?;
let exports = symtab.clone();
let lazy_symtab = symtab.clone();
let needed_libs: Vec<&'static str> = dynamic
.needed_libs
.iter()
.map(|needed_lib| symtab.strtab().get_str(needed_lib.get()))
.collect();
if !needed_libs.is_empty() {
logging::debug!("[{}] Dependencies: {:?}", self.path, needed_libs);
}
let soname = dynamic
.soname_off
.map(|soname_off| symtab.strtab().get_str(soname_off.get()));
let rpath = dynamic
.rpath_off
.map(|rpath_off| symtab.strtab().get_str(rpath_off));
let runpath = dynamic
.runpath_off
.map(|runpath_off| symtab.strtab().get_str(runpath_off));
let search = match search_paths {
Some(paths) => paths.module_search(self.path, soname, runpath, rpath),
None => ModuleSearch::from_dynamic(self.path, soname, runpath, rpath),
};
let tls = if let Some(info) = &self.tls_info {
let image = self
.segments
.read_view::<u8>(VmOffset::new(info.vaddr), info.filesz)
.ok_or_else(|| ParsePhdrError::malformed("PT_TLS image is malformed"))?;
let request = if static_tls {
TlsRequest::Static(None)
} else {
TlsRequest::Dynamic
};
CoreTlsState::with_module(
self.tls_resolver.clone(),
self.tls_resolver.register(*info, request)?,
image,
)
} else {
CoreTlsState::without_module(self.tls_resolver.clone())
};
let lazy_plt = PltRelocInfo::new(dynamic.pltrel.clone(), lazy_symtab);
let inner = Arc::new(ElfModule {
runtime: Box::new(CoreRuntime::new::<D, R, Tls>(Some(lazy_plt))),
executor: self.executor,
state: ModuleState::new(self.source_id, self.domain),
lifecycle: OnceCell::new(),
search,
exports: arc_unsize!(Arc::new(exports) => dyn SymbolExports<Arch::Layout>),
user_data: self.user_data,
dynamic_info: Some(Arc::new(DynamicInfo::<Arch> {
eh_frame_hdr: self.eh_frame_hdr,
phdrs,
needed_libs: needed_libs.into_boxed_slice(),
symbolic: dynamic.symbolic,
})),
lazy_lookup: OnceCell::new(),
tls,
segments: self.segments,
});
ElfModule::bind_runtime_owner(&inner);
Ok(RawDynamic {
entry: self.entry,
interp: self.interp,
extra: ElfExtraData::<Arch> {
lazy: !dynamic.bind_now,
relro: self.relro,
dynamic_addr,
dt_debug_addr: dynamic.dt_debug_addr,
got_plt: dynamic.got_plt,
relocation,
init: dynamic.init,
fini: dynamic.fini,
symtab,
},
module: ElfCore { inner },
})
}
}
impl<D: Send + Sync + 'static, Arch, R, Tls> Relocatable<D> for RawDynamic<D, Arch, R, Tls>
where
D: Send + Sync + 'static,
Arch: RelocationArch,
R: RegionAccess,
Tls: TlsResolver<Arch>,
<Arch::Layout as ElfLayout>::Word: ByteRepr,
{
type Output = LoadedCore<D, Arch, R, Tls>;
type Arch = Arch;
type Tls = Tls;
#[inline]
fn domain_id(&self) -> DomainId {
self.module.domain_id()
}
fn relocate<Obs, Binder>(
self,
args: RelocateArgs<'_, Arch, Tls, Obs, Binder>,
) -> Result<Self::Output>
where
Obs: RelocationObserver<Arch> + ?Sized,
Binder: LazyBinder<Arch> + ?Sized,
{
self.relocate_impl(args)
}
}