use super::{RelocAddr, resolve_ifunc};
use crate::relocation::{TlsDescEmuRequest, TlsDescEmuValue};
use crate::{
Error, RelocReason, Result,
elf::{ElfRelEntry, ElfRelType, ElfSymbol, ElfSymbolType, SymbolInfo, SymbolTable},
image::{ElfCore, Module, ModuleScope},
logging, relocate_context_error,
relocation::{
EmuRelocationContext, Emulator, HandleResult, RelocationArch, RelocationContext,
RelocationHandler,
},
sync::Arc,
tls::{TlsDescArgs, lookup_tls_get_addr},
};
use core::marker::PhantomData;
pub(crate) struct RelocHelper<'find, D: 'static, Arch: RelocationArch, PreH: ?Sized, PostH: ?Sized>
{
pub(crate) core: &'find ElfCore<D, Arch>,
pub(crate) scope: ModuleScope<Arch>,
pub(crate) pre_handler: &'find PreH,
pub(crate) post_handler: &'find PostH,
#[allow(dead_code)]
pub(crate) tls_get_addr: RelocAddr,
pub(crate) tls_desc_args: TlsDescArgs,
pub(crate) emu: Option<Arc<dyn Emulator<Arch>>>,
}
impl<'find, D, Arch, PreH, PostH> RelocHelper<'find, D, Arch, PreH, PostH>
where
D: 'static,
Arch: RelocationArch,
PreH: RelocationHandler<Arch> + ?Sized,
PostH: RelocationHandler<Arch> + ?Sized,
{
pub(crate) fn new(
core: &'find ElfCore<D, Arch>,
scope: ModuleScope<Arch>,
pre_handler: &'find PreH,
post_handler: &'find PostH,
tls_get_addr: RelocAddr,
emu: Option<Arc<dyn Emulator<Arch>>>,
) -> Self {
Self {
core,
scope,
pre_handler,
post_handler,
tls_get_addr,
tls_desc_args: TlsDescArgs::default(),
emu,
}
}
#[inline]
pub(crate) fn handle_pre(&mut self, rel: &ElfRelType<Arch>) -> Result<HandleResult> {
let hctx = RelocationContext::new(rel, self.core, &self.scope);
self.pre_handler.handle(&hctx)
}
#[inline]
pub(crate) fn handle_post(&mut self, rel: &ElfRelType<Arch>) -> Result<HandleResult> {
let hctx = RelocationContext::new(rel, self.core, &self.scope);
self.post_handler.handle(&hctx)
}
#[inline]
pub(crate) fn find_symbol(&mut self, r_sym: usize) -> Option<RelocAddr> {
find_symbol_addr(
self.core,
self.core.symtab(),
&self.scope,
r_sym,
self.tls_get_addr,
)
}
#[inline]
pub(crate) fn find_symdef(&mut self, r_sym: usize) -> Option<SymDef<'_, D, Arch>> {
let (dynsym, syminfo) = self.core.symtab().symbol_idx(r_sym);
find_symdef_impl(self.core, &self.scope, dynsym, &syminfo)
}
#[inline]
pub(crate) fn resolve_ifunc_with_emu(
&self,
rel: &ElfRelType<Arch>,
resolver: RelocAddr,
) -> Result<Option<RelocAddr>> {
let Some(emu) = &self.emu else {
return Ok(None);
};
let ctx = EmuRelocationContext::new(self.core, rel);
emu.resolve_ifunc(&ctx, resolver.into_inner())
.map(RelocAddr::new)
.map(Some)
}
#[inline]
#[cfg_attr(not(feature = "tls"), allow(dead_code))]
pub(crate) fn resolve_tlsdesc_with_emu(
&self,
rel: &ElfRelType<Arch>,
request: TlsDescEmuRequest,
) -> Result<Option<TlsDescEmuValue>> {
let Some(emu) = &self.emu else {
return Ok(None);
};
let ctx = EmuRelocationContext::new(self.core, rel);
emu.resolve_tlsdesc(&ctx, request).map(Some)
}
}
pub struct SymDef<'lib, D: 'static, Arch: RelocationArch> {
pub(crate) sym: Option<&'lib ElfSymbol<Arch::Layout>>,
pub(crate) source: &'lib dyn Module<Arch>,
_marker: PhantomData<fn() -> D>,
}
impl<'lib, D: 'static, Arch: RelocationArch> SymDef<'lib, D, Arch> {
#[inline]
pub(crate) fn new(
sym: Option<&'lib ElfSymbol<Arch::Layout>>,
source: &'lib dyn Module<Arch>,
) -> Self {
Self {
sym,
source,
_marker: PhantomData,
}
}
pub(crate) fn convert(self) -> RelocAddr {
if likely(self.sym.is_some()) {
let base = RelocAddr::new(self.source.base_addr());
let sym = unsafe { self.sym.unwrap_unchecked() };
let addr = base.offset(sym.st_value());
if likely(
sym.symbol_type() != ElfSymbolType::GNU_IFUNC || !Arch::SUPPORTS_NATIVE_RUNTIME,
) {
addr
} else {
unsafe { resolve_ifunc(addr) }
}
} else {
RelocAddr::null()
}
}
#[inline]
pub fn symbol(&self) -> Option<&'lib ElfSymbol<Arch::Layout>> {
self.sym
}
#[inline]
pub fn source(&self) -> &'lib dyn Module<Arch> {
self.source
}
#[inline]
#[cfg_attr(not(feature = "tls"), allow(dead_code))]
pub(crate) fn tls_mod_id(&self) -> Option<crate::tls::TlsModuleId> {
self.source.tls_mod_id()
}
#[inline]
#[cfg_attr(not(feature = "tls"), allow(dead_code))]
pub(crate) fn tls_tp_offset(&self) -> Option<crate::tls::TlsTpOffset> {
self.source.tls_tp_offset()
}
#[inline]
pub(crate) fn segment_slice(&self, offset: usize, len: usize) -> Option<&[u8]> {
self.source.segment_slice(offset, len)
}
}
#[cold]
pub(crate) fn reloc_error<A, D>(
rel: &ElfRelType<A>,
reason: RelocReason,
lib: &ElfCore<D, A>,
) -> Error
where
A: RelocationArch,
{
let r_type_str = A::rel_type_to_str(rel.r_type());
let r_sym = rel.r_symbol();
if r_sym == 0 {
relocate_context_error(lib.name(), r_type_str, None, reason)
} else {
relocate_context_error(
lib.name(),
r_type_str,
Some(lib.symtab().symbol_idx(r_sym).1.name()),
reason,
)
}
}
fn find_weak<'lib, D, Arch: RelocationArch>(
lib: &'lib ElfCore<D, Arch>,
dynsym: &'lib ElfSymbol<Arch::Layout>,
) -> Option<SymDef<'lib, D, Arch>>
where
D: 'static,
{
if dynsym.is_weak() && dynsym.is_undef() {
assert!(dynsym.st_value() == 0);
Some(SymDef::new(None, lib))
} else if dynsym.st_value() != 0 {
Some(SymDef::new(Some(dynsym), lib))
} else {
None
}
}
#[inline]
fn find_symbol_addr<D, Arch>(
core: &ElfCore<D, Arch>,
symtab: &SymbolTable<Arch::Layout>,
scope: &ModuleScope<Arch>,
r_sym: usize,
tls_get_addr: RelocAddr,
) -> Option<RelocAddr>
where
Arch: RelocationArch,
D: 'static,
{
let (dynsym, syminfo) = symtab.symbol_idx(r_sym);
if Arch::SUPPORTS_NATIVE_RUNTIME
&& let Some(addr) = lookup_tls_get_addr(syminfo.name(), tls_get_addr)
{
logging::trace!(
"binding file [{}] to [tls_get_addr]: symbol [{}]",
core.name(),
syminfo.name()
);
return Some(RelocAddr::from_ptr(addr));
}
if let Some(res) = find_symdef_impl(core, scope, dynsym, &syminfo) {
return Some(res.convert());
}
None
}
pub(crate) fn find_symdef_impl<'lib, D, Arch: RelocationArch>(
core: &'lib ElfCore<D, Arch>,
scope: &'lib ModuleScope<Arch>,
sym: &'lib ElfSymbol<Arch::Layout>,
syminfo: &SymbolInfo,
) -> Option<SymDef<'lib, D, Arch>>
where
D: 'static,
{
if unlikely(sym.is_local()) {
Some(SymDef::new(Some(sym), core))
} else {
let mut precompute = syminfo.precompute();
scope
.iter()
.find_map(|source| {
source.lookup_symbol(syminfo, &mut precompute).map(|sym| {
logging::trace!(
"binding file [{}] to [{}]: symbol [{}]",
core.name(),
source.name(),
syminfo.name()
);
SymDef::new(Some(sym), &**source)
})
})
.or_else(|| find_weak(core, sym))
}
}
#[inline]
#[cold]
fn cold() {}
#[inline]
pub(crate) fn likely(b: bool) -> bool {
if !b {
cold()
}
b
}
#[inline]
pub(crate) fn unlikely(b: bool) -> bool {
if b {
cold()
}
b
}