use crate::{
ByteRepr, LazyBindingError, RelocationError, Result,
elf::{ElfLayout, ElfRelEntry, ElfRelType, ElfWord, SymbolEntry},
image::CoreRuntime,
memory::{ImageMemory, ImageMemoryExt, VmAddr},
relocation::RelocationArch,
};
use alloc::boxed::Box;
use core::{any::Any, ptr::NonNull};
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct LazySlots {
context: usize,
resolver: usize,
}
impl LazySlots {
#[inline]
pub const fn new(context: usize, resolver: usize) -> Self {
Self { context, resolver }
}
#[inline]
pub const fn context(self) -> usize {
self.context
}
#[inline]
pub const fn resolver(self) -> usize {
self.resolver
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum LazyPlacement {
Unsupported,
Slots(LazySlots),
Custom,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct LazyValues {
context: VmAddr,
resolver: VmAddr,
}
impl LazyValues {
#[inline]
pub const fn context(self) -> VmAddr {
self.context
}
#[inline]
pub const fn resolver(self) -> VmAddr {
self.resolver
}
}
pub struct LazySetup {
values: LazyValues,
_state: Option<Box<dyn Any + Send + Sync>>,
}
impl LazySetup {
#[inline]
pub const fn new(context: VmAddr, resolver: VmAddr) -> Self {
Self {
values: LazyValues { context, resolver },
_state: None,
}
}
pub fn with_state<T>(state: T, resolver: VmAddr) -> Self
where
T: Send + Sync + 'static,
{
let state = Box::new(state);
let context = VmAddr::from_ptr(state.as_ref());
let state = state as Box<dyn Any + Send + Sync>;
Self {
values: LazyValues { context, resolver },
_state: Some(state),
}
}
#[inline]
pub(crate) const fn values(&self) -> LazyValues {
self.values
}
}
#[derive(Debug)]
pub struct LazyRuntime<Arch: RelocationArch> {
runtime: NonNull<CoreRuntime<Arch>>,
}
impl<Arch: RelocationArch> Copy for LazyRuntime<Arch> {}
impl<Arch: RelocationArch> Clone for LazyRuntime<Arch> {
#[inline]
fn clone(&self) -> Self {
*self
}
}
impl<Arch: RelocationArch> LazyRuntime<Arch> {
#[inline]
pub(crate) fn new(runtime: &CoreRuntime<Arch>) -> Self {
Self {
runtime: NonNull::from(runtime),
}
}
#[inline]
pub unsafe fn from_runtime(runtime: VmAddr) -> Self {
Self {
runtime: NonNull::new(runtime.as_mut_ptr::<CoreRuntime<Arch>>())
.expect("lazy resolver context entry must not be null"),
}
}
#[inline]
pub(super) fn core(&self) -> &CoreRuntime<Arch> {
unsafe { self.runtime.as_ref() }
}
#[inline]
pub fn runtime(&self) -> VmAddr {
VmAddr::from_ptr(self.runtime.as_ptr())
}
#[inline]
pub fn memory(&self) -> &dyn ImageMemory {
self.core().module().memory()
}
#[inline]
pub fn plt_relocation(&self, rela_idx: usize) -> Option<LazyPltReloc<'_, Arch>> {
let plt = self.core().lazy_plt()?;
let rel = plt.relocs.as_slice().get(rela_idx)?;
let symbol = plt.symbols.view().entry(rel.r_symbol());
Some(LazyPltReloc { rel, symbol })
}
#[inline]
pub fn lookup_symbol(&self, symbol: &SymbolEntry<'_, Arch::Layout>) -> Result<Option<VmAddr>> {
self.core().module().lookup_symbol(symbol)
}
pub fn write_jump_slot(&self, reloc: &LazyPltReloc<'_, Arch>, value: VmAddr) -> Result<()>
where
<Arch::Layout as ElfLayout>::Word: ByteRepr,
{
let word = <Arch::Layout as ElfLayout>::Word::from_usize(value.get());
let place = self.memory().base() + reloc.rel.r_offset();
unsafe { self.memory().write_value(place, word) }
}
pub fn resolve_default(&self, rela_idx: usize) -> Result<VmAddr>
where
<Arch::Layout as ElfLayout>::Word: ByteRepr,
{
let reloc = self
.plt_relocation(rela_idx)
.ok_or(RelocationError::LazyBinding(
LazyBindingError::RelocIndexOutOfRange,
))?;
if reloc.rel.r_type() != Arch::JUMP_SLOT || reloc.rel.r_symbol() == 0 {
return Err(RelocationError::LazyBinding(LazyBindingError::InvalidPltReloc).into());
}
let symbol = reloc.symbol();
let resolved = self
.lookup_symbol(symbol)?
.ok_or(RelocationError::LazyBinding(
LazyBindingError::UnknownSymbol,
))?;
self.write_jump_slot(&reloc, resolved)?;
Ok(resolved)
}
}
pub struct LazyPltReloc<'a, Arch: RelocationArch> {
rel: &'a ElfRelType<Arch>,
symbol: SymbolEntry<'a, Arch::Layout>,
}
impl<'a, Arch: RelocationArch> LazyPltReloc<'a, Arch> {
#[inline]
pub const fn relocation(&self) -> &'a ElfRelType<Arch> {
self.rel
}
#[inline]
pub const fn symbol(&self) -> &SymbolEntry<'a, Arch::Layout> {
&self.symbol
}
}