#[cfg(feature = "object")]
use super::RelocHelper;
use super::{Emulator, RelocAddr, RelocValue, RelocationValueKind, SymDef, find_symdef_impl};
use crate::{
RelocReason, Result,
arch::{ArchKind, NativeArch},
elf::{ElfLayout, ElfMachine, ElfRelEntry, ElfRelType, ElfRelocationType},
image::{ElfCore, ModuleScope},
sync::Arc,
};
use alloc::boxed::Box;
use core::marker::PhantomData;
pub trait RelocationArch: 'static {
const KIND: ArchKind;
const MACHINE: ElfMachine;
type Layout: ElfLayout;
type Relocation: ElfRelEntry<Self::Layout> + 'static;
const NONE: ElfRelocationType;
const RELATIVE: ElfRelocationType;
const GOT: ElfRelocationType;
const SYMBOLIC: ElfRelocationType;
const JUMP_SLOT: ElfRelocationType;
const IRELATIVE: ElfRelocationType;
const COPY: ElfRelocationType;
const DTPMOD: ElfRelocationType;
const DTPOFF: ElfRelocationType;
const TPOFF: ElfRelocationType;
const TLSDESC: Option<ElfRelocationType> = None;
const TLS_DTV_OFFSET: usize = 0;
const SUPPORTS_NATIVE_RUNTIME: bool = false;
#[inline]
fn is_tlsdesc(r_type: ElfRelocationType) -> bool {
Self::TLSDESC.is_some_and(|tlsdesc| r_type == tlsdesc)
}
#[inline]
fn is_tls(r_type: ElfRelocationType) -> bool {
r_type == Self::DTPMOD
|| r_type == Self::DTPOFF
|| r_type == Self::TPOFF
|| Self::is_tlsdesc(r_type)
}
#[inline]
fn rel_type_to_str(_r_type: ElfRelocationType) -> &'static str {
"UNKNOWN"
}
#[cfg(feature = "object")]
#[doc(hidden)]
#[allow(private_interfaces)]
fn relocate_object<D, PreH, PostH>(
helper: &mut RelocHelper<'_, D, Self, PreH, PostH>,
rel: &ElfRelType<Self>,
_pltgot: &mut crate::object::layout::PltGotSection,
) -> Result<()>
where
Self: Sized,
D: 'static,
PreH: RelocationHandler<Self> + ?Sized,
PostH: RelocationHandler<Self> + ?Sized,
{
Err(super::reloc_error::<Self, _>(
rel,
RelocReason::Unsupported,
helper.core,
))
}
#[cfg(feature = "object")]
#[doc(hidden)]
#[inline]
fn object_needs_got(_r_type: ElfRelocationType) -> bool
where
Self: Sized,
{
false
}
#[cfg(feature = "object")]
#[doc(hidden)]
#[inline]
fn object_needs_plt(_r_type: ElfRelocationType) -> bool
where
Self: Sized,
{
false
}
}
pub(crate) trait RelocationValueProvider {
fn relocation_value_kind(
_relocation_type: usize,
) -> core::result::Result<RelocationValueKind, RelocReason> {
Err(RelocReason::Unsupported)
}
fn relocation_value<T>(
relocation_type: usize,
target: usize,
addend: isize,
place: usize,
skip: impl FnOnce(RelocValue<()>) -> T,
write_addr: impl FnOnce(RelocAddr) -> T,
write_word32: impl FnOnce(RelocValue<u32>) -> T,
write_sword32: impl FnOnce(RelocValue<i32>) -> T,
) -> core::result::Result<T, RelocReason> {
let kind = Self::relocation_value_kind(relocation_type)?;
match kind {
RelocationValueKind::None => Ok(skip(RelocValue::new(()))),
RelocationValueKind::Address(formula) => Ok(write_addr(RelocAddr::new(
formula.compute(target, addend, place) as usize,
))),
RelocationValueKind::Word32(formula) => {
u32::try_from(formula.compute(target, addend, place))
.map(RelocValue::new)
.map(write_word32)
.map_err(|_| RelocReason::IntConversionOutOfRange)
}
RelocationValueKind::SWord32(formula) => {
i32::try_from(formula.compute(target, addend, place))
.map(RelocValue::new)
.map(write_sword32)
.map_err(|_| RelocReason::IntConversionOutOfRange)
}
}
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum HandleResult {
Unhandled,
Handled,
}
impl HandleResult {
#[inline]
pub const fn is_unhandled(self) -> bool {
matches!(self, Self::Unhandled)
}
}
pub trait RelocationHandler<Arch: RelocationArch = NativeArch> {
fn handle<D: 'static>(&self, ctx: &RelocationContext<'_, D, Arch>) -> Result<HandleResult>;
}
pub struct RelocationContext<'a, D: 'static, Arch: RelocationArch = NativeArch> {
rel: &'a ElfRelType<Arch>,
lib: &'a ElfCore<D, Arch>,
scope: &'a ModuleScope<Arch>,
}
impl<'a, D: 'static, Arch: RelocationArch> RelocationContext<'a, D, Arch> {
#[inline]
pub(crate) fn new(
rel: &'a ElfRelType<Arch>,
lib: &'a ElfCore<D, Arch>,
scope: &'a ModuleScope<Arch>,
) -> Self {
Self { rel, lib, scope }
}
#[inline]
pub fn rel(&self) -> &ElfRelType<Arch> {
self.rel
}
#[inline]
pub fn lib(&self) -> &ElfCore<D, Arch> {
self.lib
}
#[inline]
pub fn scope(&self) -> &ModuleScope<Arch> {
&self.scope
}
#[inline]
pub fn find_symdef(&self, r_sym: usize) -> Option<SymDef<'a, D, Arch>> {
let symbol = self.lib.symtab();
let (sym, syminfo) = symbol.symbol_idx(r_sym);
find_symdef_impl(self.lib, self.scope, sym, &syminfo)
}
}
impl<Arch: RelocationArch> RelocationHandler<Arch> for () {
fn handle<D: 'static>(&self, _ctx: &RelocationContext<'_, D, Arch>) -> Result<HandleResult> {
Ok(HandleResult::Unhandled)
}
}
impl<Arch: RelocationArch, H: RelocationHandler<Arch> + ?Sized> RelocationHandler<Arch> for &H {
fn handle<D: 'static>(&self, ctx: &RelocationContext<'_, D, Arch>) -> Result<HandleResult> {
(**self).handle(ctx)
}
}
impl<Arch: RelocationArch, H: RelocationHandler<Arch> + ?Sized> RelocationHandler<Arch> for &mut H {
fn handle<D: 'static>(&self, ctx: &RelocationContext<'_, D, Arch>) -> Result<HandleResult> {
(**self).handle(ctx)
}
}
impl<Arch: RelocationArch, H: RelocationHandler<Arch> + ?Sized> RelocationHandler<Arch> for Box<H> {
fn handle<D: 'static>(&self, ctx: &RelocationContext<'_, D, Arch>) -> Result<HandleResult> {
(**self).handle(ctx)
}
}
impl<Arch: RelocationArch, H: RelocationHandler<Arch> + ?Sized> RelocationHandler<Arch> for Arc<H> {
fn handle<D: 'static>(&self, ctx: &RelocationContext<'_, D, Arch>) -> Result<HandleResult> {
(**self).handle(ctx)
}
}
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
pub enum BindingMode {
#[default]
Default,
Eager,
Lazy,
}
pub struct RelocateArgs<'a, D: 'static, Arch: RelocationArch, PreH: ?Sized, PostH: ?Sized> {
pub(crate) scope: ModuleScope<Arch>,
pub(crate) binding: BindingMode,
pub(crate) pre_handler: &'a PreH,
pub(crate) post_handler: &'a PostH,
pub(crate) emu: Option<Arc<dyn Emulator<Arch>>>,
_marker: PhantomData<fn() -> (D, Arch)>,
}
impl<'a, D: 'static, Arch: RelocationArch, PreH: ?Sized, PostH: ?Sized>
RelocateArgs<'a, D, Arch, PreH, PostH>
{
#[inline]
pub(crate) fn new(
scope: ModuleScope<Arch>,
binding: BindingMode,
pre_handler: &'a PreH,
post_handler: &'a PostH,
emu: Option<Arc<dyn Emulator<Arch>>>,
) -> Self {
Self {
scope,
binding,
pre_handler,
post_handler,
emu,
_marker: PhantomData,
}
}
}
pub trait Relocatable<D = ()>: Sized {
type Output;
type Arch: RelocationArch;
fn relocate<PreH, PostH>(
self,
args: RelocateArgs<'_, D, Self::Arch, PreH, PostH>,
) -> Result<Self::Output>
where
PreH: RelocationHandler<Self::Arch> + ?Sized,
PostH: RelocationHandler<Self::Arch> + ?Sized;
}
pub trait SupportLazy {}
impl SupportLazy for () {}