use crate::arch::NativeArch;
use crate::elf::ElfRelType;
use crate::{
ParseDynamicError, ParsePhdrError, Result,
elf::{
ElfDyn, ElfDynamic, ElfDynamicTag, ElfLayout, ElfPhdr, ElfPhdrs, ElfStringTable, ElfSymbol,
HashTable, Lifecycle, LifecycleSpec, SymbolTable,
},
input::Path,
lazy::{LazyBinder, SupportLazy},
loader::ImageBuilder,
logging,
memory::{HostRegion, ImageMemoryExt, MappedView, RegionAccess, VmAddr, VmOffset},
observer::{InitEvent, RelocationObserver},
relocation::{DynamicRelocation, Relocatable, RelocateArgs, RelocationArch},
segment::{ElfSegments, MemoryProtection},
sync::{Arc, AtomicBool},
tls::{CoreTlsState, TlsResolver},
};
use alloc::{boxed::Box, vec::Vec};
use core::{cell::OnceCell, mem::size_of, ptr::NonNull};
use crate::image::{ElfCore, LoadedCore, ModuleTls, core::CoreInner, exports_handle};
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 symbol_count = hashtab.count_syms();
let symtab_off = dynamic
.symtab
.checked_offset_from(segments.base())
.ok_or(ParseDynamicError::AddressOverflow)?;
let symtab_size = symbol_count
.checked_mul(size_of::<ElfSymbol<L>>())
.ok_or(ParseDynamicError::AddressOverflow)?;
let symbols = segments
.read_view::<ElfSymbol<L>>(symtab_off, symtab_size)
.ok_or(ParseDynamicError::MalformedSymbolTable {
detail: "DT_SYMTAB symbol table size is malformed",
})?
.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 = crate::elf::version::ELFVersion::new(
dynamic.version_idx,
dynamic.verneed,
dynamic.verdef,
&strtab,
);
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) soname: Option<&'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,
rpath: Option<&'static str>,
runpath: Option<&'static str>,
needed_libs: Box<[&'static str]>,
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())
.field("needed_libs", &self.needed_libs)
.finish()
}
}
pub struct RawDynamic<
D,
Arch = NativeArch,
R: RegionAccess = HostRegion,
Tls: TlsResolver<Arch> = (),
> where
D: 'static,
Arch: RelocationArch,
{
entry: VmAddr,
interp: Option<&'static str>,
module: ElfCore<D, Arch, R, Tls>,
extra: ElfExtraData<Arch>,
}
impl<D, 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: 'static, Arch: RelocationArch, R: RegionAccess, Tls: TlsResolver<Arch>> SupportLazy
for RawDynamic<D, Arch, R, Tls>
{
}
impl<D, 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
}
pub fn tls(&self) -> ModuleTls {
self.module.tls()
}
#[inline]
pub fn core_ref(&self) -> &ElfCore<D, Arch, R, Tls> {
&self.module
}
#[inline]
pub fn core(&self) -> ElfCore<D, Arch, R, Tls> {
self.core_ref().clone()
}
#[inline]
pub fn into_core(self) -> ElfCore<D, Arch, R, Tls> {
self.core()
}
#[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 rpath(&self) -> Option<&str> {
self.extra.rpath
}
#[inline]
pub fn runpath(&self) -> Option<&str> {
self.extra.runpath
}
#[inline]
pub fn soname(&self) -> Option<&str> {
self.module.soname()
}
#[inline]
pub fn interp(&self) -> Option<&str> {
self.interp
}
#[inline]
pub fn path(&self) -> &Path {
self.module.path()
}
#[inline]
pub fn name(&self) -> &str {
self.module.name()
}
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 call_init<Obs>(&self, observer: &mut Obs, init: &Lifecycle) -> Result<()>
where
Obs: RelocationObserver<Arch> + ?Sized,
{
self.module.set_init();
let segments = self.module.segments();
let mut event = InitEvent::new(self.core_ref(), init);
observer.on_init(&mut event)?;
event.run_with(segments, self.module.executor())?;
Ok(())
}
#[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()
}
pub fn base(&self) -> VmAddr {
self.module.base()
}
pub fn segments(&self) -> &ElfSegments<R> {
self.module.segments()
}
pub fn needed_libs(&self) -> &[&str] {
&self.extra.needed_libs
}
#[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
}
#[inline]
pub fn user_data(&self) -> &D {
self.module.user_data()
}
}
impl<Tls, D: '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>],
) -> 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 tls_image = if let Some(info) = &self.tls_info {
Some(
self.segments
.read_view::<u8>(VmOffset::new(info.vaddr), info.filesz)
.ok_or_else(|| ParsePhdrError::malformed("PT_TLS image is malformed"))?,
)
} else {
None
};
let (tls_mod_id, tls_tp_offset) = if let Some(info) = &self.tls_info {
if static_tls {
let (mod_id, offset) = Tls::register_static(info)?;
(Some(mod_id), Some(offset))
} else {
(Some(Tls::register(info)?), None)
}
} else {
(None, None)
};
let lazy_plt = PltRelocInfo::new(dynamic.pltrel.clone(), lazy_symtab);
let inner = Arc::new(CoreInner {
runtime: Box::new(crate::image::CoreRuntime::new::<D, R, Tls>(Some(lazy_plt))),
executor: self.executor,
is_init: AtomicBool::new(false),
path: self.path,
exports: exports_handle(exports),
finalizer: OnceCell::new(),
user_data: self.user_data,
dynamic_info: Some(Arc::new(DynamicInfo::<Arch> {
eh_frame_hdr: self.eh_frame_hdr,
phdrs,
soname,
symbolic: dynamic.symbolic,
})),
scope: OnceCell::new(),
tls: CoreTlsState::new(tls_mod_id, tls_tp_offset, self.tls_info, tls_image),
segments: self.segments,
});
CoreInner::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,
rpath: dynamic
.rpath_off
.map(|rpath_off| symtab.strtab().get_str(rpath_off.get())),
needed_libs: needed_libs.into_boxed_slice(),
runpath: dynamic
.runpath_off
.map(|runpath_off| symtab.strtab().get_str(runpath_off.get())),
symtab,
},
module: ElfCore { inner },
})
}
}
impl<D, Arch, R, Tls> Relocatable<D> for RawDynamic<D, Arch, R, Tls>
where
D: 'static,
Arch: RelocationArch,
R: RegionAccess,
Tls: TlsResolver<Arch>,
<Arch::Layout as crate::elf::ElfLayout>::Word: crate::ByteRepr,
{
type Output = LoadedCore<D, Arch, R, Tls>;
type Arch = Arch;
type Tls = Tls;
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)
}
}