use crate::sync::{Arc, AtomicBool};
use crate::{
Result,
elf::{ElfDyn, ElfDynamic, ElfPhdr, ElfPhdrs, ElfRelType, SymbolTable},
image::{ElfCore, ImageBuilder, common::CoreInner},
loader::{LifecycleContext, LoadHook},
os::Mmap,
relocation::{DynamicRelocation, SymbolLookup},
segment::ELFRelro,
tls::{TlsIndex, TlsResolver},
};
use alloc::{boxed::Box, vec::Vec};
use core::{
ffi::CStr,
ptr::{NonNull, null},
};
pub(crate) struct DynamicInfo {
pub(crate) eh_frame_hdr: Option<NonNull<u8>>,
pub(crate) dynamic_ptr: NonNull<ElfDyn>,
pub(crate) pltrel: Option<NonNull<ElfRelType>>,
pub(crate) phdrs: ElfPhdrs,
pub(crate) lazy_scope: Option<Box<dyn SymbolLookup>>,
}
struct ElfExtraData {
lazy: bool,
got_plt: Option<NonNull<usize>>,
relocation: DynamicRelocation,
relro: Option<ELFRelro>,
init: Box<dyn Fn()>,
rpath: Option<&'static str>,
runpath: Option<&'static str>,
needed_libs: Box<[&'static str]>,
tls_get_addr: extern "C" fn(*const crate::tls::TlsIndex) -> *mut u8,
}
impl core::fmt::Debug for ElfExtraData {
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(crate) struct DynamicImage<D>
where
D: 'static,
{
entry: usize,
interp: Option<&'static str>,
phdrs: ElfPhdrs,
module: ElfCore<D>,
extra: ElfExtraData,
}
impl<D> core::fmt::Debug for DynamicImage<D> {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.debug_struct("DynamicImage")
.field("entry", &format_args!("0x{:x}", self.entry))
.field("module", &self.module)
.field("extra", &self.extra)
.finish()
}
}
impl<D> DynamicImage<D> {
#[inline]
pub(crate) fn entry(&self) -> usize {
self.entry
}
pub(crate) fn tls_mod_id(&self) -> Option<usize> {
self.module.tls_mod_id()
}
pub(crate) fn tls_tp_offset(&self) -> Option<isize> {
self.module.tls_tp_offset()
}
pub(crate) fn tls_get_addr(&self) -> extern "C" fn(*const TlsIndex) -> *mut u8 {
self.extra.tls_get_addr
}
#[inline]
pub(crate) fn core_ref(&self) -> &ElfCore<D> {
&self.module
}
#[inline]
pub(crate) fn core(&self) -> ElfCore<D> {
self.core_ref().clone()
}
#[inline]
pub(crate) fn into_core(self) -> ElfCore<D> {
self.core()
}
#[inline]
pub(crate) fn is_lazy(&self) -> bool {
self.extra.lazy
}
#[inline]
pub(crate) fn rpath(&self) -> Option<&str> {
self.extra.rpath
}
#[inline]
pub(crate) fn runpath(&self) -> Option<&str> {
self.extra.runpath
}
#[inline]
pub(crate) fn interp(&self) -> Option<&str> {
self.interp
}
#[inline]
pub(crate) fn name(&self) -> &str {
self.module.name()
}
pub(crate) fn phdrs(&self) -> &[ElfPhdr] {
match &self.phdrs {
ElfPhdrs::Mmap(phdrs) => &phdrs,
ElfPhdrs::Vec(phdrs) => &phdrs,
}
}
#[inline]
pub(crate) fn got(&self) -> Option<NonNull<usize>> {
self.extra.got_plt
}
#[inline]
pub(crate) fn relocation(&self) -> &DynamicRelocation {
&self.extra.relocation
}
#[inline]
pub(crate) fn call_init(&self) {
self.module.set_init();
self.extra.init.as_ref()();
}
#[inline]
pub(crate) fn relro(&self) -> Option<&ELFRelro> {
self.extra.relro.as_ref()
}
#[inline]
pub(crate) fn user_data_mut(&mut self) -> Option<&mut D> {
self.module.user_data_mut()
}
pub(crate) fn base(&self) -> usize {
self.module.base()
}
pub(crate) fn mapped_len(&self) -> usize {
self.module.segments().len()
}
pub(crate) fn needed_libs(&self) -> &[&str] {
&self.extra.needed_libs
}
pub(crate) fn dynamic_ptr(&self) -> Option<NonNull<ElfDyn>> {
self.module.dynamic_ptr()
}
pub(crate) fn user_data(&self) -> &D {
self.module.user_data()
}
#[inline]
pub(crate) fn set_lazy_scope<LazyS>(&self, lazy_scope: LazyS)
where
D: 'static,
LazyS: SymbolLookup + Send + Sync + 'static,
{
let info = unsafe {
&mut *(Arc::as_ptr(&self.module.inner.dynamic_info.as_ref().unwrap())
as *mut DynamicInfo)
};
info.lazy_scope = Some(Box::new(lazy_scope));
}
}
impl<'hook, H, M, Tls, D> ImageBuilder<'hook, H, M, Tls, D>
where
H: LoadHook,
Tls: TlsResolver,
M: Mmap,
{
pub(crate) fn build_dynamic(mut self, phdrs: &[ElfPhdr]) -> Result<DynamicImage<D>> {
let is_dylib = self.ehdr.is_dylib();
for phdr in phdrs {
self.parse_phdr(phdr)?;
}
let dynamic_ptr = self.dynamic_ptr.expect("dynamic section not found");
let phdrs_repr = self.create_phdrs(phdrs);
let dynamic = ElfDynamic::new(dynamic_ptr.as_ptr(), &self.segments).unwrap();
#[cfg(feature = "log")]
log::trace!("[{}] Dynamic info: {:?}", self.name, dynamic);
let relocation = DynamicRelocation::new(
dynamic.pltrel,
dynamic.dynrel,
dynamic.relr,
dynamic.rel_count,
);
let static_tls = self.static_tls | dynamic.static_tls;
let symtab = SymbolTable::from_dynamic(&dynamic);
let needed_libs: Vec<&'static str> = dynamic
.needed_libs
.iter()
.map(|needed_lib| symtab.strtab().get_str(needed_lib.get()))
.collect();
#[cfg(feature = "log")]
if !needed_libs.is_empty() {
log::debug!("[{}] Dependencies: {:?}", self.name, needed_libs);
}
let init_handler = self.init_fn;
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)
};
Ok(DynamicImage {
entry: self.ehdr.e_entry as usize + if is_dylib { self.segments.base() } else { 0 },
interp: self
.interp
.map(|s| unsafe { CStr::from_ptr(s.as_ptr()).to_str().unwrap() }),
phdrs: phdrs_repr.clone(),
extra: ElfExtraData {
lazy: !dynamic.bind_now,
relro: self.relro,
relocation,
init: Box::new(move || {
init_handler.call(&LifecycleContext::new(
dynamic.init_fn,
dynamic.init_array_fn,
))
}),
got_plt: dynamic.got_plt,
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())),
tls_get_addr: Tls::tls_get_addr,
},
module: ElfCore {
inner: Arc::new(CoreInner {
is_init: AtomicBool::new(false),
name: self.name,
symtab,
fini: dynamic.fini_fn,
fini_array: dynamic.fini_array_fn,
fini_handler: self.fini_fn,
user_data: self.user_data,
dynamic_info: Some(Arc::new(DynamicInfo {
eh_frame_hdr: self.eh_frame_hdr,
dynamic_ptr: NonNull::new(dynamic.dyn_ptr as _).unwrap(),
pltrel: NonNull::new(dynamic.pltrel.map_or(null(), |plt| plt.as_ptr()) as _),
phdrs: phdrs_repr,
lazy_scope: None,
})),
tls_mod_id,
tls_tp_offset,
tls_unregister: Tls::unregister,
tls_desc_args: Box::new([]),
segments: self.segments,
}),
},
})
}
}