use crate::{
Result,
elf::SymbolEntry,
image::{DynamicInfo, PltRelocInfo, SymbolExports, WeakModuleScope},
input::PathBuf,
logging,
memory::{HostRegion, ImageMemory, RegionAccess, VmAddr},
observer::Finalizer,
relocation::{RelocationArch, find_symdef_impl},
runtime::CodeExecutor,
segment::ElfSegments,
sync::{Arc, AtomicBool, Ordering},
tls::{CoreTlsState, TLS_GET_ADDR_SYMBOL, TlsResolver},
};
use alloc::boxed::Box;
use core::{any::Any, cell::OnceCell, marker::PhantomData, ops::Deref};
#[repr(C)]
pub(crate) struct CoreRuntime<Arch: RelocationArch = crate::arch::NativeArch> {
core: OnceCell<VmAddr>,
lazy_plt: Option<PltRelocInfo<Arch>>,
pub(crate) lazy_runtime: OnceCell<Box<dyn Any + Send + Sync>>,
module: for<'a> unsafe fn(&'a Self) -> &'a dyn CoreRuntimeModule<Arch>,
}
impl<Arch: RelocationArch> CoreRuntime<Arch> {
pub(crate) fn new<D, R, Tls>(lazy_plt: Option<PltRelocInfo<Arch>>) -> Self
where
D: 'static,
R: RegionAccess,
Tls: TlsResolver<Arch>,
{
Self {
core: OnceCell::new(),
lazy_plt,
lazy_runtime: OnceCell::new(),
module: core_module::<D, Arch, R, Tls>,
}
}
#[inline]
fn bind_core(&self, core: VmAddr) {
assert!(
self.core.set(core).is_ok(),
"core runtime owner must be installed only once",
);
}
#[inline]
fn core(&self) -> VmAddr {
*self
.core
.get()
.expect("core runtime owner must be installed before use")
}
#[inline]
pub(crate) fn lazy_plt(&self) -> Option<&PltRelocInfo<Arch>> {
self.lazy_plt.as_ref()
}
#[inline]
pub(crate) fn module(&self) -> &dyn CoreRuntimeModule<Arch> {
unsafe { (self.module)(self) }
}
}
pub(crate) trait CoreRuntimeModule<Arch: RelocationArch>: Send + Sync {
fn memory(&self) -> &dyn ImageMemory;
fn lookup_symbol(&self, symbol: SymbolEntry<'_, Arch::Layout>) -> Result<Option<VmAddr>>;
}
#[inline]
unsafe fn core_inner<D, Arch, R, Tls>(runtime: &CoreRuntime<Arch>) -> &CoreInner<D, Arch, R, Tls>
where
D: 'static,
Arch: RelocationArch,
R: RegionAccess,
Tls: TlsResolver<Arch>,
{
unsafe { &*runtime.core().as_ptr::<CoreInner<D, Arch, R, Tls>>() }
}
unsafe fn core_module<D, Arch, R, Tls>(runtime: &CoreRuntime<Arch>) -> &dyn CoreRuntimeModule<Arch>
where
D: 'static,
Arch: RelocationArch,
R: RegionAccess,
Tls: TlsResolver<Arch>,
{
unsafe { core_inner::<D, Arch, R, Tls>(runtime) }
}
impl<D, Arch, R, Tls> CoreRuntimeModule<Arch> for CoreInner<D, Arch, R, Tls>
where
D: 'static,
Arch: RelocationArch,
R: RegionAccess,
Tls: TlsResolver<Arch>,
{
#[inline]
fn memory(&self) -> &dyn ImageMemory {
&self.segments
}
fn lookup_symbol(&self, symbol: SymbolEntry<'_, Arch::Layout>) -> Result<Option<VmAddr>> {
if Tls::OVERRIDE_TLS_GET_ADDR && symbol.name() == TLS_GET_ADDR_SYMBOL {
return Tls::bind_tls_get_addr().map(Some);
}
let Some(scope) = self.scope.get().and_then(WeakModuleScope::upgrade) else {
return Ok(None);
};
let symbolic = self.dynamic_info.as_ref().is_some_and(|info| info.symbolic);
let executor = self.executor.as_ref();
find_symdef_impl(self, &scope, symbol.symbol(), symbol.info(), symbolic)
.map(|symdef| symdef.resolve_addr(executor))
.transpose()
}
}
pub(crate) struct CoreInner<
D: 'static = (),
Arch: RelocationArch = crate::arch::NativeArch,
R: RegionAccess = HostRegion,
Tls: TlsResolver<Arch> = (),
> {
pub(crate) runtime: Box<CoreRuntime<Arch>>,
pub(crate) executor: Arc<dyn CodeExecutor<Arch>>,
pub(crate) is_init: AtomicBool,
pub(crate) path: PathBuf,
pub(crate) exports: Arc<dyn SymbolExports<Arch::Layout>>,
pub(crate) finalizer: OnceCell<Finalizer>,
pub(crate) dynamic_info: Option<Arc<DynamicInfo<Arch>>>,
pub(crate) scope: OnceCell<WeakModuleScope<Arch, Tls>>,
pub(crate) tls: CoreTlsState<Arch, Tls>,
pub(crate) segments: ElfSegments<R>,
pub(crate) user_data: D,
}
impl<D: 'static, Arch: RelocationArch, R: RegionAccess, Tls: TlsResolver<Arch>>
CoreInner<D, Arch, R, Tls>
{
#[inline]
pub(crate) fn name(&self) -> &str {
self.dynamic_info
.as_ref()
.and_then(|info| info.soname)
.unwrap_or_else(|| self.path.file_name())
}
#[inline]
pub(crate) const fn runtime(&self) -> &CoreRuntime<Arch> {
&self.runtime
}
#[inline]
pub(crate) fn bind_runtime_owner(inner: &Arc<Self>) {
inner
.runtime
.bind_core(VmAddr::from_ptr(Arc::as_ptr(inner)));
}
}
impl<D: 'static, Arch: RelocationArch, R: RegionAccess, Tls: TlsResolver<Arch>> Drop
for CoreInner<D, Arch, R, Tls>
{
fn drop(&mut self) {
if self.is_init.load(Ordering::Relaxed)
&& let Some(finalizer) = self.finalizer.take()
{
let name = self.name();
if let Err(err) = finalizer.run(name, &self.segments, self.executor.as_ref()) {
logging::error!("finalization lifecycle failed for {}: {err}", name);
}
}
self.tls.cleanup();
}
}
unsafe impl<D: 'static, Arch: RelocationArch, R: RegionAccess, Tls: TlsResolver<Arch>> Sync
for CoreInner<D, Arch, R, Tls>
{
}
unsafe impl<D: 'static, Arch: RelocationArch, R: RegionAccess, Tls: TlsResolver<Arch>> Send
for CoreInner<D, Arch, R, Tls>
{
}
#[derive(Debug, Clone)]
pub struct Symbol<'lib, T: 'lib> {
ptr: *mut (),
pd: PhantomData<&'lib T>,
}
impl<'lib, T> Deref for Symbol<'lib, T> {
type Target = T;
fn deref(&self) -> &T {
unsafe { &*(&self.ptr as *const *mut _ as *const T) }
}
}
impl<'lib, T> Symbol<'lib, T> {
#[inline]
pub unsafe fn from_raw(ptr: *mut ()) -> Self {
Self {
ptr,
pd: PhantomData,
}
}
pub fn into_raw(self) -> *const () {
self.ptr
}
}
unsafe impl<T: Send> Send for Symbol<'_, T> {}
unsafe impl<T: Sync> Sync for Symbol<'_, T> {}