use crate::{
Error, RelocReason, Result,
elf::{
ElfRelEntry, ElfRelType, ElfSymbol, ElfSymbolType, HashTable, SymbolEntry, SymbolInfo,
SymbolTableView,
},
hint::unlikely,
image::{ElfCore, Module, ModuleScope, SymbolLookup},
logging,
memory::{ImageMemory, RegionAccess, VmAddr, VmOffset},
observer::{RelocationObserver, SymbolBindingEvent},
relocate_context_error,
relocation::{HandleResult, RelocationArch, RelocationEvent},
runtime::{CodeContext, CodeExecutor},
segment::ElfSegments,
tls::{TLS_GET_ADDR_SYMBOL, TlsResolver},
};
pub(crate) struct RelocHelper<
'find,
D: 'static,
Arch: RelocationArch,
R: RegionAccess,
Tls: TlsResolver<Arch> + 'static,
Obs: ?Sized,
H = HashTable<<Arch as RelocationArch>::Layout>,
Memory = &'find ElfSegments<R>,
> {
pub(crate) core: &'find ElfCore<D, Arch, R, Tls>,
symbols: SymbolTableView<'find, Arch::Layout, H>,
memory: Memory,
pub(crate) scope: ModuleScope<Arch, Tls>,
pub(crate) observer: &'find mut Obs,
}
impl<'find, D, Arch, R, Tls, Obs, H, Memory> RelocHelper<'find, D, Arch, R, Tls, Obs, H, Memory>
where
D: 'static,
Arch: RelocationArch,
R: RegionAccess,
Tls: TlsResolver<Arch>,
Obs: RelocationObserver<Arch> + ?Sized,
Memory: ImageMemory,
{
pub(crate) fn new(
core: &'find ElfCore<D, Arch, R, Tls>,
symbols: SymbolTableView<'find, Arch::Layout, H>,
memory: Memory,
scope: ModuleScope<Arch, Tls>,
observer: &'find mut Obs,
) -> Self {
Self {
core,
symbols,
memory,
scope,
observer,
}
}
#[inline]
pub(crate) fn memory(&self) -> &Memory {
&self.memory
}
#[inline]
pub(crate) fn handle_pre(&mut self, rel: &ElfRelType<Arch>) -> Result<HandleResult> {
let hctx = RelocationEvent::new(rel, self.core, self.symbols, &self.scope);
self.observer.on_relocation_pre(&hctx)
}
#[inline]
pub(crate) fn handle_post(&mut self, rel: &ElfRelType<Arch>) -> Result<HandleResult> {
let hctx = RelocationEvent::new(rel, self.core, self.symbols, &self.scope);
self.observer.on_relocation_post(&hctx)
}
#[cold]
pub(crate) fn reloc_error(&self, rel: &ElfRelType<Arch>, reason: RelocReason) -> Error {
let r_type_str = Arch::rel_type_to_str(rel.r_type());
let r_sym = rel.r_symbol();
if r_sym == 0 {
relocate_context_error(self.core.name(), r_type_str, None, reason)
} else {
relocate_context_error(
self.core.name(),
r_type_str,
Some(self.symbols.symbol_idx(r_sym).name()),
reason,
)
}
}
#[inline]
#[cfg(feature = "object")]
pub(crate) fn symbol_addr(&self, r_sym: usize) -> VmAddr {
let symbol = self.symbols.symbol_idx(r_sym);
self.core.base() + VmOffset::new(symbol.symbol().st_value())
}
#[inline]
pub(crate) fn symbol_entry(&self, rel: &ElfRelType<Arch>) -> SymbolEntry<'find, Arch::Layout> {
self.symbols.symbol_idx(rel.r_symbol())
}
#[inline]
pub(crate) fn find_symdef<'a>(
&'a self,
symbol: &SymbolEntry<'a, Arch::Layout>,
) -> Option<SymDef<'a, Arch, Tls>> {
find_symdef_impl(
self.core,
&self.scope,
symbol.symbol(),
symbol.info(),
self.core.symbolic(),
)
}
#[inline]
pub(crate) fn resolve_symbol_addr(
&self,
symbol: &SymbolEntry<'_, Arch::Layout>,
symdef: Option<&SymDef<'_, Arch, Tls>>,
) -> Result<Option<VmAddr>> {
Ok(
if Tls::OVERRIDE_TLS_GET_ADDR && symbol.name() == TLS_GET_ADDR_SYMBOL {
Some(Tls::bind_tls_get_addr()?)
} else {
symdef
.map(|symdef| symdef.resolve_addr(self.core.executor()))
.transpose()?
},
)
}
#[inline]
pub(crate) fn bind_symbol_addr(
&mut self,
rel: &ElfRelType<Arch>,
symbol: &SymbolEntry<'_, Arch::Layout>,
resolved: Option<VmAddr>,
) -> Result<Option<VmAddr>> {
let mut event = SymbolBindingEvent::new(
self.core,
Some(rel),
symbol.symbol(),
symbol.name(),
resolved,
);
self.observer.on_symbol_binding(&mut event)?;
Ok(event.into_resolved_addr())
}
}
pub enum SymDef<'lib, Arch: RelocationArch, Tls: TlsResolver<Arch> = ()> {
Defined {
symbol: &'lib ElfSymbol<Arch::Layout>,
source: &'lib dyn Module<Arch, Tls>,
},
WeakUndef,
}
impl<'lib, Arch: RelocationArch, Tls: TlsResolver<Arch> + 'static> SymDef<'lib, Arch, Tls> {
#[inline]
pub(crate) fn defined(
symbol: &'lib ElfSymbol<Arch::Layout>,
source: &'lib dyn Module<Arch, Tls>,
) -> Self {
Self::Defined { symbol, source }
}
#[inline]
pub(crate) fn weak_undef() -> Self {
Self::WeakUndef
}
pub(crate) fn addr(&self) -> VmAddr {
match self {
Self::Defined { symbol, source } => {
if symbol.st_shndx().is_abs() {
VmAddr::new(symbol.st_value())
} else {
source.memory().base() + VmOffset::new(symbol.st_value())
}
}
Self::WeakUndef => VmAddr::null(),
}
}
#[inline]
pub(crate) fn resolve_addr(&self, executor: &dyn CodeExecutor<Arch>) -> Result<VmAddr> {
let addr = self.addr();
if unlikely(matches!(
self,
Self::Defined { symbol, .. } if symbol.symbol_type() == ElfSymbolType::GNU_IFUNC
)) {
self.resolve_ifunc_addr(executor, addr)
} else {
Ok(addr)
}
}
#[cold]
#[inline(never)]
fn resolve_ifunc_addr(
&self,
executor: &dyn CodeExecutor<Arch>,
resolver: VmAddr,
) -> Result<VmAddr> {
let Self::Defined { source, .. } = self else {
unreachable!("undefined weak symbols cannot be IFUNC resolvers")
};
executor.resolve_ifunc(
CodeContext::<Arch>::new(source.name(), source.memory()),
resolver,
)
}
}
#[cold]
fn weak_undef<'lib, Arch, Tls>(
sym: &'lib ElfSymbol<Arch::Layout>,
) -> Option<SymDef<'lib, Arch, Tls>>
where
Arch: RelocationArch,
Tls: TlsResolver<Arch> + 'static,
{
if sym.is_weak() && sym.is_undef() {
debug_assert_eq!(sym.st_value(), 0);
Some(SymDef::weak_undef())
} else {
None
}
}
pub(crate) fn find_symdef_impl<
'lib,
Arch: RelocationArch,
Tls: TlsResolver<Arch> + 'static,
Source,
>(
source: &'lib Source,
scope: &'lib ModuleScope<Arch, Tls>,
sym: &'lib ElfSymbol<Arch::Layout>,
syminfo: &SymbolInfo,
symbolic: bool,
) -> Option<SymDef<'lib, Arch, Tls>>
where
Source: Module<Arch, Tls>,
{
if unlikely(sym.is_local()) {
return Some(SymDef::defined(sym, source));
}
let self_def = || (!sym.is_undef()).then(|| SymDef::defined(sym, source));
let scope_def = || {
let mut lookup = SymbolLookup::from_info(syminfo.clone());
scope.iter().find_map(|scope_source| {
scope_source.exports().lookup(&mut lookup).map(|sym| {
logging::trace!(
"binding file [{}] to [{}]: symbol [{}]",
source.name(),
scope_source.name(),
syminfo.name()
);
SymDef::defined(sym, &**scope_source)
})
})
};
if symbolic {
self_def().or_else(scope_def)
} else {
scope_def().or_else(self_def)
}
.or_else(|| weak_undef(sym))
}