use crate::{
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 LazyBindingSlots {
context: usize,
resolver: usize,
}
impl LazyBindingSlots {
#[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
}
}
pub struct LazyBindingEntries {
context: VmAddr,
resolver: VmAddr,
state: Option<Box<dyn Any + Send + Sync>>,
}
impl LazyBindingEntries {
#[inline]
pub fn new(context: VmAddr, resolver: VmAddr) -> Self {
Self {
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 {
context,
resolver,
state: Some(state),
}
}
#[inline]
pub const fn context(&self) -> VmAddr {
self.context
}
#[inline]
pub const fn resolver(&self) -> VmAddr {
self.resolver
}
#[inline]
pub(crate) fn into_parts(self) -> (VmAddr, VmAddr, Option<Box<dyn Any + Send + Sync>>) {
(self.context, self.resolver, self.state)
}
}
#[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(crate) fn lazy_plt(&self) -> Option<&crate::image::PltRelocInfo<Arch>> {
self.core().lazy_plt()
}
#[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 rel = self.lazy_plt()?.relocs.as_slice().get(rela_idx)?;
Some(LazyPltReloc {
runtime: *self,
index: rela_idx,
rel,
})
}
#[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: crate::ByteRepr,
{
let word = <Arch::Layout as ElfLayout>::Word::from_usize(value.get());
unsafe { self.memory().write_value(reloc.place(), word) }
}
pub fn resolve_default(&self, rela_idx: usize) -> Result<Option<VmAddr>>
where
<Arch::Layout as ElfLayout>::Word: crate::ByteRepr,
{
let lazy_plt = self
.lazy_plt()
.expect("lazy PLT metadata must be installed before default lazy binding");
let rel = lazy_plt
.relocs
.as_slice()
.get(rela_idx)
.ok_or(RelocationError::LazyBinding(
LazyBindingError::RelocIndexOutOfRange,
))?;
let reloc = LazyPltReloc {
runtime: *self,
index: rela_idx,
rel,
};
if reloc.r_type() != Arch::JUMP_SLOT || reloc.symbol_index() == 0 {
return Err(RelocationError::LazyBinding(LazyBindingError::InvalidPltReloc).into());
}
let symbol = reloc.symbol().ok_or(RelocationError::LazyBinding(
LazyBindingError::SymbolIndexOutOfRange,
))?;
let resolved = self.lookup_symbol(symbol)?;
if let Some(addr) = resolved {
self.write_jump_slot(&reloc, addr)?;
}
Ok(resolved)
}
}
pub struct LazyPltReloc<'a, Arch: RelocationArch> {
runtime: LazyRuntime<Arch>,
index: usize,
rel: &'a ElfRelType<Arch>,
}
impl<'a, Arch: RelocationArch> LazyPltReloc<'a, Arch> {
#[inline]
pub const fn index(&self) -> usize {
self.index
}
#[inline]
pub const fn relocation(&self) -> &'a ElfRelType<Arch> {
self.rel
}
#[inline]
pub fn r_type(&self) -> crate::elf::ElfRelocationType {
self.rel.r_type()
}
#[inline]
pub fn symbol_index(&self) -> usize {
self.rel.r_symbol()
}
#[inline]
pub fn place(&self) -> VmAddr {
self.runtime.memory().base() + self.rel.r_offset()
}
#[inline]
pub fn is_jump_slot(&self) -> bool {
self.r_type() == Arch::JUMP_SLOT
}
#[inline]
pub fn symbol(&self) -> Option<SymbolEntry<'_, Arch::Layout>> {
let symtab = self.runtime.lazy_plt()?.symbols.view();
(self.symbol_index() < symtab.count_syms()).then(|| symtab.symbol_idx(self.symbol_index()))
}
#[inline]
pub fn symbol_name(&self) -> Option<&str> {
self.symbol().map(|symbol| symbol.name())
}
}