use super::CoreInner;
use crate::{
Result, TlsError,
elf::{ElfDyn, ElfDynamic, ElfPhdr, ElfPhdrs, SymbolTable},
image::{DynamicInfo, Module, ModuleScope, ModuleTls, SymbolExports, exports_handle},
input::{Path, PathBuf},
memory::{HostRegion, ImageMemory, MappedView, RegionAccess, VmAddr},
observer::Finalizer,
relocation::RelocationArch,
runtime::{CodeExecutor, NativeCodeExecutor},
segment::ElfSegments,
sync::{Arc, AtomicBool, Ordering, Weak},
tls::{
CoreTlsState, TlsImageProvider, TlsImageSource, TlsInfo, TlsModuleId, TlsResolver,
TlsTemplate, TlsTpOffset, tls_image_provider_handle,
},
};
use alloc::{boxed::Box, vec::Vec};
use core::{cell::OnceCell, fmt::Debug, ptr::NonNull};
pub struct ElfCoreRef<
D: 'static = (),
Arch: RelocationArch = crate::arch::NativeArch,
R: RegionAccess = HostRegion,
Tls: TlsResolver<Arch> = (),
> {
inner: Weak<CoreInner<D, Arch, R, Tls>>,
}
impl<D: 'static, Arch: RelocationArch, R: RegionAccess, Tls: TlsResolver<Arch>> Clone
for ElfCoreRef<D, Arch, R, Tls>
{
#[inline]
fn clone(&self) -> Self {
Self {
inner: self.inner.clone(),
}
}
}
impl<D: 'static, Arch: RelocationArch, R: RegionAccess, Tls: TlsResolver<Arch>>
ElfCoreRef<D, Arch, R, Tls>
{
pub fn upgrade(&self) -> Option<ElfCore<D, Arch, R, Tls>> {
self.inner.upgrade().map(|inner| ElfCore { inner })
}
}
impl<D: 'static, Arch: RelocationArch, R: RegionAccess, Tls: TlsResolver<Arch>> TlsImageProvider
for CoreInner<D, Arch, R, Tls>
{
fn with_tls_template(&self, f: &mut dyn FnMut(TlsTemplate<'_>) -> Result<()>) -> Result<()> {
if self.tls.info().is_none() {
return Err(TlsError::TemplateUnavailable.into());
}
self.tls.with_template(f)
}
}
pub struct ElfCore<
D: 'static = (),
Arch: RelocationArch = crate::arch::NativeArch,
R: RegionAccess = HostRegion,
Tls: TlsResolver<Arch> = (),
> {
pub(crate) inner: Arc<CoreInner<D, Arch, R, Tls>>,
}
impl<D: 'static, Arch: RelocationArch, R: RegionAccess, Tls: TlsResolver<Arch>> Clone
for ElfCore<D, Arch, R, Tls>
{
fn clone(&self) -> Self {
ElfCore {
inner: Arc::clone(&self.inner),
}
}
}
impl<D: 'static, Arch: RelocationArch, R: RegionAccess, Tls: TlsResolver<Arch> + 'static>
ElfCore<D, Arch, R, Tls>
{
#[inline]
pub fn is_init(&self) -> bool {
self.inner.is_init.load(Ordering::Relaxed)
}
#[inline]
pub(crate) fn set_init(&self) {
self.inner.is_init.store(true, Ordering::Relaxed);
}
#[inline]
pub fn downgrade(&self) -> ElfCoreRef<D, Arch, R, Tls> {
ElfCoreRef {
inner: Arc::downgrade(&self.inner),
}
}
#[inline]
pub fn user_data(&self) -> &D {
&self.inner.user_data
}
pub fn phdrs(&self) -> Option<&[ElfPhdr<Arch::Layout>]> {
self.inner
.dynamic_info
.as_ref()
.map(|info| info.phdrs.as_slice())
}
#[inline]
pub fn user_data_mut(&mut self) -> Option<&mut D> {
Arc::get_mut(&mut self.inner).map(|inner| &mut inner.user_data)
}
#[inline]
pub fn strong_count(&self) -> usize {
Arc::strong_count(&self.inner)
}
#[inline]
pub fn weak_count(&self) -> usize {
Arc::weak_count(&self.inner)
}
#[inline]
pub fn path(&self) -> &Path {
&self.inner.path
}
#[inline]
pub fn name(&self) -> &str {
self.inner.name()
}
#[inline]
pub fn base(&self) -> VmAddr {
self.inner.segments.base()
}
#[inline]
pub(crate) fn soname(&self) -> Option<&str> {
self.inner
.dynamic_info
.as_ref()
.and_then(|info| info.soname)
}
#[inline]
pub(crate) fn symbolic(&self) -> bool {
self.inner
.dynamic_info
.as_ref()
.is_some_and(|info| info.symbolic)
}
#[inline]
pub(crate) fn set_scope(&self, scope: &ModuleScope<Arch, Tls>) {
assert!(
self.inner.scope.set(scope.downgrade()).is_ok(),
"relocation scope must be installed only once",
);
}
#[inline]
pub fn segments(&self) -> &ElfSegments<R> {
&self.inner.segments
}
#[inline]
pub fn exports(&self) -> &dyn SymbolExports<Arch::Layout> {
&*self.inner.exports
}
#[inline]
pub fn eh_frame_hdr(&self) -> Option<NonNull<u8>> {
self.inner
.dynamic_info
.as_ref()
.and_then(|info| info.eh_frame_hdr)
}
#[inline]
pub fn tls(&self) -> ModuleTls {
ModuleTls::new(self.inner.tls.mod_id(), self.inner.tls.tp_offset())
}
pub(crate) fn init_tls(&self) -> Result<()> {
let Some(info) = self.inner.tls.info() else {
return Ok(());
};
let provider = tls_image_provider_handle(self.inner.clone());
self.inner
.tls
.init_tls(TlsImageSource::new(info, Arc::downgrade(&provider)))
}
pub(crate) fn tls_addr(&self, offset: usize) -> Option<VmAddr> {
self.inner.tls.addr(offset)
}
#[inline]
pub(crate) fn executor(&self) -> &dyn CodeExecutor<Arch> {
self.inner.executor.as_ref()
}
pub(crate) fn set_finalizer(&self, finalizer: Finalizer) {
assert!(
self.inner.finalizer.set(finalizer).is_ok(),
"finalizer must be set only once",
);
}
}
impl<D: 'static, Arch: RelocationArch, R: RegionAccess, Tls: TlsResolver<Arch>>
ElfCore<D, Arch, R, Tls>
{
#[allow(clippy::too_many_arguments)]
pub(super) unsafe fn from_raw(
path: PathBuf,
base: VmAddr,
dynamic_entries: MappedView<ElfDyn<Arch::Layout>>,
dynamic_addr: VmAddr,
phdrs: Vec<ElfPhdr<Arch::Layout>>,
eh_frame_hdr: Option<NonNull<u8>>,
mut segments: ElfSegments<R>,
tls_mod_id: Option<TlsModuleId>,
tls_tp_offset: Option<TlsTpOffset>,
tls_info: Option<TlsInfo>,
tls_image: Option<MappedView<u8>>,
user_data: D,
) -> Result<Self> {
segments.set_base(base);
let dynamic = ElfDynamic::<Arch>::new(dynamic_entries, dynamic_addr, &segments)?;
let symtab = SymbolTable::from_dynamic(&dynamic, &segments)?;
let exports = symtab.clone();
let lazy_symtab = symtab.clone();
let soname = dynamic
.soname_off
.map(|soname_off| symtab.strtab().get_str(soname_off.get()));
let lazy_plt = crate::image::PltRelocInfo::new(dynamic.pltrel, lazy_symtab);
let inner = Arc::new(CoreInner {
runtime: Box::new(crate::image::CoreRuntime::new::<D, R, Tls>(Some(lazy_plt))),
executor: Arc::from(Box::new(NativeCodeExecutor) as Box<dyn CodeExecutor<Arch>>),
path,
is_init: AtomicBool::new(true),
exports: exports_handle(exports),
dynamic_info: Some(Arc::new(DynamicInfo::<Arch> {
eh_frame_hdr,
phdrs: ElfPhdrs::Vec(phdrs),
soname,
symbolic: dynamic.symbolic,
})),
scope: OnceCell::new(),
tls: CoreTlsState::new(tls_mod_id, tls_tp_offset, tls_info, tls_image),
segments,
finalizer: OnceCell::new(),
user_data,
});
CoreInner::bind_runtime_owner(&inner);
Ok(Self { inner })
}
}
impl<D: 'static, Arch: RelocationArch, R: RegionAccess, Tls: TlsResolver<Arch>> Debug
for ElfCore<D, Arch, R, Tls>
{
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.debug_struct("ElfCore")
.field("path", &self.inner.path)
.field("base", &format_args!("{}", self.base()))
.field("tls", &self.tls())
.finish()
}
}
impl<D, Arch, R, Tls> Module<Arch, Tls> for ElfCore<D, Arch, R, Tls>
where
D: 'static,
Arch: RelocationArch,
R: RegionAccess,
Tls: TlsResolver<Arch> + 'static,
{
#[inline]
fn name(&self) -> &str {
ElfCore::name(self)
}
#[inline]
fn exports(&self) -> &dyn SymbolExports<Arch::Layout> {
ElfCore::exports(self)
}
#[inline]
fn memory(&self) -> &dyn ImageMemory {
self.segments()
}
#[inline]
fn tls(&self) -> ModuleTls {
ElfCore::tls(self)
}
}
impl<D, Arch, R, Tls> Module<Arch, Tls> for CoreInner<D, Arch, R, Tls>
where
D: 'static,
Arch: RelocationArch,
R: RegionAccess,
Tls: TlsResolver<Arch> + 'static,
{
#[inline]
fn name(&self) -> &str {
CoreInner::name(self)
}
#[inline]
fn exports(&self) -> &dyn SymbolExports<Arch::Layout> {
&*self.exports
}
#[inline]
fn memory(&self) -> &dyn ImageMemory {
&self.segments
}
#[inline]
fn tls(&self) -> ModuleTls {
ModuleTls::new(self.tls.mod_id(), self.tls.tp_offset())
}
}
#[cfg(test)]
mod tests {
use super::*;
struct NonCloneData;
#[test]
fn weak_core_ref_clone_does_not_require_user_data_clone() {
fn assert_clone<T: Clone>() {}
assert_clone::<ElfCoreRef<NonCloneData>>();
}
}