use crate::{
ParsePhdrError, Result,
elf::{
ElfDyn, ElfDynamic, ElfPhdr, ElfPhdrs, ElfSymbol, Lifecycle, PreCompute, SymbolInfo,
SymbolTable,
},
image::{DynamicInfo, Module},
input::{Path, PathBuf},
loader::DynLifecycleHandler,
relocation::{EmuContext, Emulator, RelocAddr, RelocationArch},
segment::ElfSegments,
sync::{Arc, AtomicBool, Ordering, Weak},
tls::{CoreTlsState, TlsDescArgs, TlsModuleId, TlsTpOffset},
};
use alloc::boxed::Box;
use alloc::vec::Vec;
use core::{any::Any, fmt::Debug, ptr::NonNull};
#[repr(C)]
pub(crate) struct CoreInner<D = (), Arch: RelocationArch = crate::arch::NativeArch> {
pub(crate) is_init: AtomicBool,
pub(crate) path: PathBuf,
pub(crate) symtab: SymbolTable<Arch::Layout>,
pub(crate) fini: Lifecycle<'static>,
pub(crate) fini_handler: CoreFiniHandler<Arch>,
pub(crate) dynamic_info: Option<Arc<DynamicInfo<Arch>>>,
pub(crate) tls: CoreTlsState,
pub(crate) segments: ElfSegments,
pub(crate) user_data: D,
}
impl<D, Arch: RelocationArch> Drop for CoreInner<D, Arch> {
fn drop(&mut self) {
if self.is_init.load(Ordering::Relaxed) {
match &self.fini_handler {
CoreFiniHandler::Native(handler) => {
handler.call(&self.fini);
}
CoreFiniHandler::Emu(emu) => {
let ctx = EmuContext::from_parts(
self.path.as_str(),
self.segments.base(),
&self.segments,
);
emu.call_fini(&ctx, &self.fini);
}
}
}
self.tls.cleanup();
}
}
pub(crate) enum CoreFiniHandler<Arch: RelocationArch> {
Native(DynLifecycleHandler),
Emu(Arc<dyn Emulator<Arch>>),
}
#[derive(Clone)]
pub struct ElfCoreRef<D = (), Arch: RelocationArch = crate::arch::NativeArch> {
inner: Weak<CoreInner<D, Arch>>,
}
impl<D, Arch: RelocationArch> ElfCoreRef<D, Arch> {
pub fn upgrade(&self) -> Option<ElfCore<D, Arch>> {
self.inner.upgrade().map(|inner| ElfCore { inner })
}
}
pub struct ElfCore<D = (), Arch: RelocationArch = crate::arch::NativeArch> {
pub(crate) inner: Arc<CoreInner<D, Arch>>,
}
impl<D, Arch: RelocationArch> Clone for ElfCore<D, Arch> {
fn clone(&self) -> Self {
ElfCore {
inner: Arc::clone(&self.inner),
}
}
}
unsafe impl<D, Arch: RelocationArch> Sync for CoreInner<D, Arch> {}
unsafe impl<D, Arch: RelocationArch> Send for CoreInner<D, Arch> {}
impl<D, Arch: RelocationArch> ElfCore<D, Arch> {
#[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> {
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.soname().unwrap_or_else(|| self.path().file_name())
}
#[inline]
pub(crate) fn soname(&self) -> Option<&str> {
self.inner
.dynamic_info
.as_ref()
.and_then(|info| info.soname)
}
#[inline]
pub fn base(&self) -> usize {
self.inner.segments.base()
}
#[inline]
pub(crate) fn base_addr(&self) -> RelocAddr {
self.inner.segments.base_addr()
}
#[inline]
pub fn mapped_len(&self) -> usize {
self.inner.segments.mapped_len()
}
#[inline]
pub(crate) fn mapped_base(&self) -> usize {
self.inner.segments.mapped_base()
}
#[inline]
pub fn contains_addr(&self, addr: usize) -> bool {
self.inner.segments.contains_addr(addr)
}
#[inline]
pub fn is_contiguous_mapping(&self) -> bool {
self.inner.segments.is_contiguous_mapping()
}
#[inline]
pub fn symtab(&self) -> &SymbolTable<Arch::Layout> {
&self.inner.symtab
}
#[inline]
pub fn dynamic_ptr(&self) -> Option<NonNull<ElfDyn<Arch::Layout>>> {
self.inner
.dynamic_info
.as_ref()
.map(|info| info.dynamic_ptr)
}
#[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(crate) fn segments(&self) -> &ElfSegments {
&self.inner.segments
}
#[inline]
pub(crate) fn segment_slice(&self, offset: usize, len: usize) -> &[u8] {
self.segments().get_slice(offset, len)
}
#[inline]
pub fn tls_mod_id(&self) -> Option<TlsModuleId> {
self.inner.tls.mod_id()
}
#[inline]
pub fn tls_tp_offset(&self) -> Option<TlsTpOffset> {
self.inner.tls.tp_offset()
}
#[inline]
pub(crate) fn tls_get_addr(&self) -> RelocAddr {
self.inner.tls.tls_get_addr()
}
pub(crate) unsafe fn set_tls_desc_args(&self, args: TlsDescArgs) {
let inner = Arc::as_ptr(&self.inner) as *mut CoreInner<D, Arch>;
unsafe {
(*inner).tls.set_desc_args(args);
}
}
pub(crate) unsafe fn set_emu_fini(&self, emu: Arc<dyn Emulator<Arch>>) {
let inner = Arc::as_ptr(&self.inner) as *mut CoreInner<D, Arch>;
unsafe {
(*inner).fini_handler = CoreFiniHandler::Emu(emu);
}
}
pub(super) unsafe fn from_raw(
path: PathBuf,
base: usize,
dynamic_ptr: *const ElfDyn<Arch::Layout>,
phdrs: Vec<ElfPhdr<Arch::Layout>>,
eh_frame_hdr: Option<NonNull<u8>>,
mut segments: ElfSegments,
tls_mod_id: Option<TlsModuleId>,
tls_tp_offset: Option<TlsTpOffset>,
tls_get_addr: RelocAddr,
tls_unregister: fn(TlsModuleId),
user_data: D,
) -> Result<Self> {
if dynamic_ptr.is_null() {
return Err(ParsePhdrError::MissingDynamicSection.into());
}
segments.set_base(base);
let dynamic = ElfDynamic::<Arch>::new(dynamic_ptr, &segments)?;
let symtab = SymbolTable::from_dynamic(&dynamic);
let soname = dynamic
.soname_off
.map(|soname_off| symtab.strtab().get_str(soname_off.get()));
Ok(Self {
inner: Arc::new(CoreInner {
path,
is_init: AtomicBool::new(true),
symtab,
dynamic_info: Some(Arc::new(DynamicInfo {
eh_frame_hdr,
dynamic_ptr: unsafe { NonNull::new_unchecked(dynamic_ptr.cast_mut()) },
phdrs: ElfPhdrs::Vec(phdrs),
soname,
#[cfg(feature = "lazy-binding")]
lazy: crate::image::LazyBindingInfo::new(dynamic.pltrel),
})),
tls: CoreTlsState::new(tls_mod_id, tls_tp_offset, tls_get_addr, tls_unregister),
segments,
fini: Lifecycle::empty(),
fini_handler: CoreFiniHandler::Native(Arc::new(Box::new(|_: &Lifecycle<'_>| {}))),
user_data,
}),
})
}
}
impl<D, Arch: RelocationArch> Debug for ElfCore<D, Arch> {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.debug_struct("ElfCore")
.field("path", &self.inner.path)
.field("base", &format_args!("0x{:x}", self.base()))
.field("mapped_len", &self.mapped_len())
.field("tls_mod_id", &self.tls_mod_id())
.finish()
}
}
impl<D, Arch> Module<Arch> for ElfCore<D, Arch>
where
D: 'static,
Arch: RelocationArch,
{
#[inline]
fn as_any(&self) -> &dyn Any {
self
}
#[inline]
fn name(&self) -> &str {
ElfCore::name(self)
}
#[inline]
fn soname(&self) -> Option<&str> {
ElfCore::soname(self)
}
#[inline]
fn lookup_symbol<'source>(
&'source self,
symbol: &SymbolInfo<'_>,
precompute: &mut PreCompute,
) -> Option<&'source ElfSymbol<Arch::Layout>> {
self.symtab().lookup_filter(symbol, precompute)
}
#[inline]
fn base_addr(&self) -> usize {
self.base()
}
#[inline]
fn segment_slice(&self, offset: usize, len: usize) -> Option<&[u8]> {
Some(ElfCore::segment_slice(self, offset, len))
}
#[inline]
fn tls_mod_id(&self) -> Option<TlsModuleId> {
ElfCore::tls_mod_id(self)
}
#[inline]
fn tls_tp_offset(&self) -> Option<TlsTpOffset> {
ElfCore::tls_tp_offset(self)
}
}