use crate::arch::NativeArch;
#[cfg(feature = "lazy-binding")]
use crate::elf::ElfRelType;
use crate::sync::{Arc, AtomicBool};
use crate::{
ParsePhdrError, Result,
elf::{ElfDyn, ElfDynamic, ElfPhdr, ElfPhdrs, Lifecycle, SymbolTable},
input::{Path, PathBuf},
loader::{DynLifecycleHandler, ImageBuilder, LoadHook},
logging,
os::Mmap,
relocation::{
DynamicRelocation, EmuContext, Emulator, RelocAddr, Relocatable, RelocateArgs,
RelocationArch, RelocationHandler, Relocator,
},
segment::ELFRelro,
tls::{CoreTlsState, TlsInfo, TlsModuleId, TlsResolver, TlsTpOffset},
};
use alloc::{boxed::Box, vec::Vec};
use core::{ffi::CStr, marker::PhantomData, ptr::NonNull};
use super::{ElfCore, LoadedCore, core::CoreFiniHandler, core::CoreInner};
#[cfg(feature = "lazy-binding")]
pub(crate) struct LazyBindingInfo<Arch: RelocationArch = NativeArch> {
pub(crate) pltrel: &'static [ElfRelType<Arch>],
pub(crate) scope: Option<crate::image::ModuleScope<Arch>>,
}
#[cfg(feature = "lazy-binding")]
impl<Arch: RelocationArch> LazyBindingInfo<Arch> {
#[inline]
pub(crate) fn new(pltrel: Option<&'static [ElfRelType<Arch>]>) -> Self {
Self {
pltrel: pltrel.unwrap_or(&[]),
scope: None,
}
}
}
pub(crate) struct DynamicInfo<Arch: RelocationArch = NativeArch> {
pub(crate) eh_frame_hdr: Option<NonNull<u8>>,
pub(crate) dynamic_ptr: NonNull<ElfDyn<Arch::Layout>>,
pub(crate) phdrs: ElfPhdrs<Arch::Layout>,
pub(crate) soname: Option<&'static str>,
#[cfg(feature = "lazy-binding")]
pub(crate) lazy: LazyBindingInfo<Arch>,
}
pub(crate) struct RawDynamicParts<D, Arch: RelocationArch = NativeArch> {
pub(crate) path: PathBuf,
pub(crate) entry: RelocAddr,
pub(crate) interp: Option<&'static str>,
pub(crate) phdrs: ElfPhdrs<Arch::Layout>,
pub(crate) dynamic_ptr: NonNull<ElfDyn<Arch::Layout>>,
pub(crate) eh_frame_hdr: Option<NonNull<u8>>,
pub(crate) tls_info: Option<TlsInfo>,
pub(crate) force_static_tls: bool,
pub(crate) relro: Option<ELFRelro>,
pub(crate) segments: crate::segment::ElfSegments,
pub(crate) init_fn: DynLifecycleHandler,
pub(crate) fini_fn: DynLifecycleHandler,
pub(crate) user_data: D,
}
struct ElfExtraData<Arch: RelocationArch = NativeArch> {
lazy: bool,
#[cfg(feature = "lazy-binding")]
got_plt: Option<NonNull<usize>>,
relocation: DynamicRelocation<Arch>,
relro: Option<ELFRelro>,
init_handler: DynLifecycleHandler,
init: Lifecycle<'static>,
rpath: Option<&'static str>,
runpath: Option<&'static str>,
needed_libs: Box<[&'static str]>,
}
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>
where
D: 'static,
Arch: RelocationArch,
{
entry: RelocAddr,
interp: Option<&'static str>,
module: ElfCore<D, Arch>,
extra: ElfExtraData<Arch>,
_arch: PhantomData<fn() -> Arch>,
}
impl<D, Arch: RelocationArch> core::fmt::Debug for RawDynamic<D, Arch> {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.debug_struct("RawDynamic")
.field("entry", &format_args!("0x{:x}", self.entry.into_inner()))
.field("module", &self.module)
.field("extra", &self.extra)
.finish()
}
}
impl<D, Arch: RelocationArch> RawDynamic<D, Arch> {
#[inline]
pub fn entry(&self) -> usize {
self.entry_addr().into_inner()
}
#[inline]
pub(crate) fn entry_addr(&self) -> RelocAddr {
self.entry
}
pub fn tls_mod_id(&self) -> Option<TlsModuleId> {
self.module.tls_mod_id()
}
pub fn tls_tp_offset(&self) -> Option<TlsTpOffset> {
self.module.tls_tp_offset()
}
pub(crate) fn tls_get_addr(&self) -> RelocAddr {
self.module.tls_get_addr()
}
#[inline]
pub fn core_ref(&self) -> &ElfCore<D, Arch> {
&self.module
}
#[inline]
pub fn core(&self) -> ElfCore<D, Arch> {
self.core_ref().clone()
}
#[inline]
pub fn into_core(self) -> ElfCore<D, Arch> {
self.core()
}
#[inline]
pub fn is_lazy(&self) -> bool {
self.extra.lazy
}
#[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]
#[cfg(feature = "lazy-binding")]
pub(crate) fn got(&self) -> Option<NonNull<usize>> {
self.extra.got_plt
}
#[inline]
pub(crate) fn relocation(&self) -> &DynamicRelocation<Arch> {
&self.extra.relocation
}
#[inline]
pub(crate) fn call_init(&self) {
self.module.set_init();
self.extra.init_handler.call(&self.extra.init);
}
pub(crate) fn call_init_with_emu(&self, emu: Arc<dyn Emulator<Arch>>) -> Result<()> {
let ctx = EmuContext::new(self.core_ref());
emu.call_init(&ctx, &self.extra.init)?;
unsafe {
self.core_ref().set_emu_fini(emu);
}
self.module.set_init();
Ok(())
}
#[inline]
pub(crate) fn relro(&self) -> Option<&ELFRelro> {
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) -> usize {
self.module.base()
}
pub fn mapped_len(&self) -> usize {
self.module.mapped_len()
}
pub(crate) fn mapped_base(&self) -> usize {
self.module.mapped_base()
}
pub fn contains_addr(&self, addr: usize) -> bool {
self.module.contains_addr(addr)
}
pub fn needed_libs(&self) -> &[&str] {
&self.extra.needed_libs
}
pub fn dynamic_ptr(&self) -> Option<NonNull<ElfDyn<Arch::Layout>>> {
self.module.dynamic_ptr()
}
pub fn user_data(&self) -> &D {
self.module.user_data()
}
}
impl<D: 'static, Arch: RelocationArch> RawDynamic<D, Arch> {
pub(crate) fn from_parts<Tls>(parts: RawDynamicParts<D, Arch>) -> Result<Self>
where
Tls: TlsResolver,
{
let RawDynamicParts {
path,
entry,
interp,
phdrs,
dynamic_ptr,
eh_frame_hdr,
tls_info,
force_static_tls,
relro,
segments,
init_fn,
fini_fn,
user_data,
} = parts;
let dynamic = ElfDynamic::<Arch>::new(dynamic_ptr.as_ptr(), &segments)?;
logging::trace!("[{}] Dynamic info: {:?}", path, dynamic);
let relocation = DynamicRelocation::new(
dynamic.pltrel,
dynamic.dynrel,
dynamic.relr,
dynamic.rel_count,
)?;
let static_tls = force_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();
if !needed_libs.is_empty() {
logging::debug!("[{}] Dependencies: {:?}", path, needed_libs);
}
let soname = dynamic
.soname_off
.map(|soname_off| symtab.strtab().get_str(soname_off.get()));
let (tls_mod_id, tls_tp_offset) = if let Some(info) = &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(RawDynamic {
entry,
interp,
extra: ElfExtraData {
lazy: cfg!(feature = "lazy-binding") && !dynamic.bind_now,
relro,
relocation,
init_handler: init_fn,
init: Lifecycle::new(dynamic.init_fn, dynamic.init_array_fn),
#[cfg(feature = "lazy-binding")]
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())),
},
module: ElfCore {
inner: Arc::new(CoreInner {
is_init: AtomicBool::new(false),
path,
symtab,
fini: Lifecycle::new(dynamic.fini_fn, dynamic.fini_array_fn),
fini_handler: CoreFiniHandler::Native(fini_fn),
user_data,
dynamic_info: Some(Arc::new(DynamicInfo {
eh_frame_hdr,
dynamic_ptr,
phdrs,
soname,
#[cfg(feature = "lazy-binding")]
lazy: LazyBindingInfo::new(dynamic.pltrel),
})),
tls: CoreTlsState::new(
tls_mod_id,
tls_tp_offset,
RelocAddr::from_ptr(Tls::tls_get_addr as *const ()),
Tls::unregister,
),
segments,
}),
},
_arch: PhantomData,
})
}
pub(crate) fn from_builder<'hook, H, M, Tls>(
mut builder: ImageBuilder<'hook, H, M, Tls, D, Arch::Layout>,
phdrs: &[ElfPhdr<Arch::Layout>],
) -> Result<Self>
where
H: LoadHook<Arch::Layout>,
Tls: TlsResolver,
M: Mmap,
{
builder.parse_phdrs(phdrs)?;
let dynamic_ptr = builder
.dynamic_ptr
.ok_or(ParsePhdrError::MissingDynamicSection)?;
let phdrs = builder.create_phdrs(phdrs);
Self::from_parts::<Tls>(RawDynamicParts {
path: builder.path,
entry: if builder.ehdr.is_dylib() {
builder.segments.base_addr().offset(builder.ehdr.e_entry())
} else {
RelocAddr::new(builder.ehdr.e_entry())
},
interp: builder
.interp
.map(|s| unsafe { CStr::from_ptr(s.as_ptr()) }.to_str())
.transpose()
.map_err(|_| ParsePhdrError::InvalidUtf8 { field: "PT_INTERP" })?,
phdrs,
dynamic_ptr,
eh_frame_hdr: builder.eh_frame_hdr,
tls_info: builder.tls_info,
force_static_tls: builder.static_tls,
relro: builder.relro,
segments: builder.segments,
init_fn: builder.init_fn,
fini_fn: builder.fini_fn,
user_data: builder.user_data,
})
}
pub fn relocator(self) -> Relocator<Self, (), (), D, Arch> {
Relocator::new().with_object(self)
}
}
impl<D: 'static, Arch: RelocationArch> Relocatable<D> for RawDynamic<D, Arch> {
type Output = LoadedCore<D, Arch>;
type Arch = Arch;
fn relocate<PreH, PostH>(
self,
args: RelocateArgs<'_, D, Arch, PreH, PostH>,
) -> Result<Self::Output>
where
PreH: RelocationHandler<Arch> + ?Sized,
PostH: RelocationHandler<Arch> + ?Sized,
{
self.relocate_impl::<_, _>(args)
}
}