use crate::{
Result,
elf::{ElfDyn, ElfDynamic, ElfPhdr, ElfPhdrs, SymbolInfo, SymbolTable},
image::{Symbol, common::DynamicInfo},
loader::{DynLifecycleHandler, LifecycleContext},
relocation::SymDef,
segment::ElfSegments,
sync::{Arc, AtomicBool, Ordering, Weak},
tls::{TlsDescDynamicArg, TlsInfo, TlsResolver},
};
use alloc::boxed::Box;
use alloc::string::String;
use alloc::vec::Vec;
use core::{ffi::c_void, fmt::Debug, marker::PhantomData, ptr::NonNull};
use elf::abi::{DF_STATIC_TLS, DT_FLAGS, PT_DYNAMIC, PT_GNU_EH_FRAME, PT_TLS};
pub struct LoadedCore<D = ()> {
pub(crate) core: ElfCore<D>,
pub(crate) deps: Arc<[LoadedCore<D>]>,
}
impl<D> Debug for LoadedCore<D> {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.debug_struct("LoadedCore")
.field("name", &self.core.name())
.field("base", &format_args!("0x{:x}", self.core.base()))
.field(
"deps",
&self
.deps
.iter()
.map(|d| d.name())
.collect::<alloc::vec::Vec<_>>(),
)
.finish()
}
}
impl<D> Clone for LoadedCore<D> {
fn clone(&self) -> Self {
LoadedCore {
core: self.core.clone(),
deps: Arc::clone(&self.deps),
}
}
}
impl<D> LoadedCore<D> {
#[inline]
pub unsafe fn from_core(core: ElfCore<D>) -> Self {
LoadedCore {
core,
deps: Arc::from([]),
}
}
pub fn deps(&self) -> &[LoadedCore<D>] {
&self.deps
}
#[inline]
pub fn name(&self) -> &str {
self.core.name()
}
#[inline]
pub fn short_name(&self) -> &str {
self.core.short_name()
}
#[inline]
pub fn base(&self) -> usize {
self.core.base()
}
#[inline]
pub fn mapped_len(&self) -> usize {
self.core.mapped_len()
}
#[inline]
pub fn user_data(&self) -> &D {
self.core.user_data()
}
#[inline]
pub fn user_data_mut(&mut self) -> Option<&mut D> {
self.core.user_data_mut()
}
#[inline]
pub fn is_init(&self) -> bool {
self.core.is_init()
}
#[inline]
pub fn phdrs(&self) -> Option<&[ElfPhdr]> {
self.core.phdrs()
}
#[inline]
pub fn eh_frame_hdr(&self) -> Option<NonNull<u8>> {
self.core.eh_frame_hdr()
}
#[inline]
pub fn dynamic_ptr(&self) -> Option<NonNull<ElfDyn>> {
self.core.dynamic_ptr()
}
#[inline]
pub fn strong_count(&self) -> usize {
self.core.strong_count()
}
#[inline]
pub fn weak_count(&self) -> usize {
self.core.weak_count()
}
#[inline]
pub fn downgrade(&self) -> ElfCoreRef<D> {
self.core.downgrade()
}
#[inline]
pub fn tls_mod_id(&self) -> Option<usize> {
self.core.tls_mod_id()
}
#[inline]
pub fn tls_tp_offset(&self) -> Option<isize> {
self.core.tls_tp_offset()
}
#[inline]
pub unsafe fn from_core_deps(core: ElfCore<D>, deps: Arc<[LoadedCore<D>]>) -> Self {
LoadedCore { core, deps }
}
#[inline]
pub unsafe fn core_ref(&self) -> &ElfCore<D> {
&self.core
}
#[inline]
pub unsafe fn new_unchecked<Tls: TlsResolver>(
name: String,
phdrs: impl Into<Vec<ElfPhdr>>,
memory: (*mut c_void, usize),
munmap: unsafe fn(*mut c_void, usize) -> Result<()>,
tls_tp_offset: Option<isize>,
user_data: D,
) -> Result<Self> {
let segments = ElfSegments::new(memory.0, memory.1, munmap);
let base = segments.base();
let mut tls_mod_id = None;
let mut actual_tls_tp_offset = tls_tp_offset;
let mut dynamic_ptr = core::ptr::null();
let mut eh_frame_hdr = None;
let mut tls_phdr = None;
let phdrs = phdrs.into();
for phdr in &phdrs {
match phdr.p_type {
PT_DYNAMIC => {
dynamic_ptr = base.wrapping_add(phdr.p_vaddr as usize) as *const ElfDyn;
}
PT_GNU_EH_FRAME => {
eh_frame_hdr =
NonNull::new(base.wrapping_add(phdr.p_vaddr as usize) as *mut u8);
}
PT_TLS => {
tls_phdr = Some(phdr);
}
_ => {}
}
}
if let Some(phdr) = tls_phdr {
unsafe {
let template = core::slice::from_raw_parts(
base.wrapping_add(phdr.p_vaddr as usize) as *const u8,
phdr.p_filesz as usize,
);
let info = TlsInfo::new(phdr, core::mem::transmute(template));
let mut static_tls = actual_tls_tp_offset.is_some();
if !static_tls && !dynamic_ptr.is_null() {
let mut cur = dynamic_ptr;
while (*cur).d_tag != 0 {
if (*cur).d_tag as u64 == DT_FLAGS as u64 {
if (*cur).d_un as usize & DF_STATIC_TLS as usize != 0 {
static_tls = true;
break;
}
}
cur = cur.add(1);
}
}
if static_tls {
if let Some(offset) = actual_tls_tp_offset {
tls_mod_id = Some(Tls::add_static_tls(&info, offset)?);
} else {
let (mid, offset) = Tls::register_static(&info)?;
tls_mod_id = Some(mid);
actual_tls_tp_offset = Some(offset);
}
} else {
tls_mod_id = Some(Tls::register(&info)?);
}
}
}
Ok(Self {
core: unsafe {
ElfCore::from_raw(
name,
base,
dynamic_ptr,
phdrs,
eh_frame_hdr,
segments,
tls_mod_id,
actual_tls_tp_offset,
Tls::unregister,
user_data,
)
},
deps: Arc::from([]),
})
}
pub fn symtab(&self) -> &SymbolTable {
&self.core.symtab()
}
#[inline]
pub unsafe fn get<'lib, T>(&'lib self, name: &str) -> Option<Symbol<'lib, T>> {
let syminfo = SymbolInfo::from_str(name, None);
let mut precompute = syminfo.precompute();
self.symtab()
.lookup_filter(&syminfo, &mut precompute)
.map(|sym| Symbol {
ptr: SymDef {
sym: Some(sym),
lib: unsafe { self.core_ref() },
}
.convert() as _,
pd: PhantomData,
})
}
#[cfg(feature = "version")]
#[inline]
pub unsafe fn get_version<'lib, T>(
&'lib self,
name: &str,
version: &str,
) -> Option<Symbol<'lib, T>> {
let syminfo = SymbolInfo::from_str(name, Some(version));
let mut precompute = syminfo.precompute();
self.symtab()
.lookup_filter(&syminfo, &mut precompute)
.map(|sym| Symbol {
ptr: SymDef {
sym: Some(sym),
lib: unsafe { self.core_ref() },
}
.convert() as _,
pd: PhantomData,
})
}
}
#[repr(C)]
pub(crate) struct CoreInner<D = ()> {
pub(crate) is_init: AtomicBool,
pub(crate) name: String,
pub(crate) symtab: SymbolTable,
pub(crate) fini: Option<fn()>,
pub(crate) fini_array: Option<&'static [fn()]>,
pub(crate) fini_handler: DynLifecycleHandler,
pub(crate) dynamic_info: Option<Arc<DynamicInfo>>,
pub(crate) tls_mod_id: Option<usize>,
pub(crate) tls_tp_offset: Option<isize>,
pub(crate) tls_unregister: fn(usize),
pub(crate) tls_desc_args: Box<[Box<TlsDescDynamicArg>]>,
pub(crate) segments: ElfSegments,
pub(crate) user_data: D,
}
impl<D> Drop for CoreInner<D> {
fn drop(&mut self) {
if self.is_init.load(Ordering::Relaxed) {
self.fini_handler
.call(&LifecycleContext::new(self.fini, self.fini_array));
}
if let Some(mod_id) = self.tls_mod_id {
(self.tls_unregister)(mod_id);
}
}
}
#[derive(Clone)]
pub struct ElfCoreRef<D = ()> {
inner: Weak<CoreInner<D>>,
}
impl<D> ElfCoreRef<D> {
pub fn upgrade(&self) -> Option<ElfCore<D>> {
self.inner.upgrade().map(|inner| ElfCore { inner })
}
}
pub struct ElfCore<D = ()> {
pub(crate) inner: Arc<CoreInner<D>>,
}
impl<D> Clone for ElfCore<D> {
fn clone(&self) -> Self {
ElfCore {
inner: Arc::clone(&self.inner),
}
}
}
unsafe impl<D> Sync for CoreInner<D> {}
unsafe impl<D> Send for CoreInner<D> {}
impl<D> ElfCore<D> {
#[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> {
ElfCoreRef {
inner: Arc::downgrade(&self.inner),
}
}
#[inline]
pub fn user_data(&self) -> &D {
&self.inner.user_data
}
pub fn phdrs(&self) -> Option<&[ElfPhdr]> {
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 name(&self) -> &str {
&self.inner.name
}
#[inline]
pub fn short_name(&self) -> &str {
let name = self.name();
name.rsplit(|c| c == '/' || c == '\\')
.next()
.unwrap_or(name)
}
#[inline]
pub fn base(&self) -> usize {
self.inner.segments.base()
}
#[inline]
pub fn mapped_len(&self) -> usize {
self.inner.segments.len()
}
#[inline]
pub fn symtab(&self) -> &SymbolTable {
&self.inner.symtab
}
#[inline]
pub fn dynamic_ptr(&self) -> Option<NonNull<ElfDyn>> {
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 fn tls_mod_id(&self) -> Option<usize> {
self.inner.tls_mod_id
}
#[inline]
pub fn tls_tp_offset(&self) -> Option<isize> {
self.inner.tls_tp_offset
}
pub(crate) unsafe fn set_tls_desc_args(&self, args: Vec<Box<TlsDescDynamicArg>>) {
let inner = Arc::as_ptr(&self.inner) as *mut CoreInner<D>;
unsafe {
(*inner).tls_desc_args = args.into_boxed_slice();
}
}
unsafe fn from_raw(
name: String,
base: usize,
dynamic_ptr: *const ElfDyn,
phdrs: Vec<ElfPhdr>,
eh_frame_hdr: Option<NonNull<u8>>,
mut segments: ElfSegments,
tls_mod_id: Option<usize>,
tls_tp_offset: Option<isize>,
tls_unregister: fn(usize),
user_data: D,
) -> Self {
segments.offset = (segments.memory as usize).wrapping_sub(base);
let dynamic = ElfDynamic::new(dynamic_ptr, &segments).unwrap();
Self {
inner: Arc::new(CoreInner {
name,
is_init: AtomicBool::new(true),
symtab: SymbolTable::from_dynamic(&dynamic),
dynamic_info: Some(Arc::new(DynamicInfo {
eh_frame_hdr,
dynamic_ptr: NonNull::new(dynamic.dyn_ptr as _).unwrap(),
pltrel: dynamic
.pltrel
.and_then(|plt| NonNull::new(plt.as_ptr() as *mut _)),
phdrs: ElfPhdrs::Vec(phdrs),
lazy_scope: None,
})),
tls_mod_id,
tls_tp_offset,
tls_unregister,
tls_desc_args: Box::new([]),
segments,
fini: None,
fini_array: None,
fini_handler: Arc::new(Box::new(|_: &LifecycleContext| {})),
user_data,
}),
}
}
}
impl<D> Debug for ElfCore<D> {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.debug_struct("ElfCore")
.field("name", &self.inner.name)
.field("base", &format_args!("0x{:x}", self.base()))
.field("mapped_len", &self.mapped_len())
.field("tls_mod_id", &self.tls_mod_id())
.finish()
}
}