use crate::{
Result,
elf::{ElfDyn, ElfHeader, ElfPhdr, ElfPhdrs, ElfRelType, ElfShdr, ElfSymbol, SymbolTable},
loader::{DynLifecycleHandler, LoadHook, LoadHookContext},
os::Mmap,
relocation::StaticRelocation,
segment::{ELFRelro, ElfSegments, section::PltGotSection},
tls::{TlsInfo, TlsResolver},
};
use alloc::{boxed::Box, string::String, vec::Vec};
use core::{ffi::c_char, marker::PhantomData, ptr::NonNull};
use elf::abi::{
PT_DYNAMIC, PT_GNU_EH_FRAME, PT_GNU_RELRO, PT_INTERP, PT_LOAD, PT_PHDR, PT_TLS, SHN_UNDEF,
SHT_INIT_ARRAY, SHT_REL, SHT_RELA, SHT_SYMTAB, STT_FILE,
};
pub(crate) struct ImageBuilder<'hook, H, M, Tls, D = ()>
where
H: LoadHook,
M: Mmap,
Tls: TlsResolver,
{
hook: &'hook mut H,
phdr_mmap: Option<&'static [ElfPhdr]>,
pub(crate) name: String,
pub(crate) ehdr: ElfHeader,
pub(crate) relro: Option<ELFRelro>,
pub(crate) dynamic_ptr: Option<NonNull<ElfDyn>>,
pub(crate) tls_info: Option<TlsInfo>,
pub(crate) static_tls: bool,
pub(crate) user_data: D,
pub(crate) segments: ElfSegments,
pub(crate) init_fn: DynLifecycleHandler,
pub(crate) fini_fn: DynLifecycleHandler,
pub(crate) interp: Option<NonNull<c_char>>,
pub(crate) eh_frame_hdr: Option<NonNull<u8>>,
_marker: PhantomData<(M, Tls)>,
}
impl<'hook, H, M, Tls, D> ImageBuilder<'hook, H, M, Tls, D>
where
H: LoadHook,
Tls: TlsResolver,
M: Mmap,
{
pub(crate) fn new(
hook: &'hook mut H,
segments: ElfSegments,
name: String,
ehdr: ElfHeader,
init_fn: DynLifecycleHandler,
fini_fn: DynLifecycleHandler,
static_tls: bool,
user_data: D,
) -> Self {
Self {
hook,
phdr_mmap: None,
name,
ehdr,
relro: None,
dynamic_ptr: None,
tls_info: None,
static_tls,
segments,
user_data,
init_fn,
fini_fn,
interp: None,
eh_frame_hdr: None,
_marker: PhantomData,
}
}
pub(crate) fn parse_phdr(&mut self, phdr: &ElfPhdr) -> Result<()> {
let mut ctx = LoadHookContext::new(&self.name, phdr, &self.segments);
self.hook.call(&mut ctx)?;
match phdr.p_type {
PT_DYNAMIC => {
self.dynamic_ptr =
Some(NonNull::new(self.segments.get_mut_ptr(phdr.p_paddr as usize)).unwrap())
}
PT_GNU_RELRO => self.relro = Some(ELFRelro::new::<M>(phdr, self.segments.base())),
PT_PHDR => {
self.phdr_mmap = Some(
self.segments
.get_slice::<ElfPhdr>(phdr.p_vaddr as usize, phdr.p_memsz as usize),
);
}
PT_INTERP => {
self.interp =
Some(NonNull::new(self.segments.get_mut_ptr(phdr.p_vaddr as usize)).unwrap());
}
PT_GNU_EH_FRAME => {
self.eh_frame_hdr =
Some(NonNull::new(self.segments.get_mut_ptr(phdr.p_vaddr as usize)).unwrap());
}
PT_TLS => {
let tls_image = self
.segments
.get_slice::<u8>(phdr.p_vaddr as usize, phdr.p_filesz as usize);
self.tls_info = Some(TlsInfo::new(phdr, tls_image));
}
_ => {}
};
Ok(())
}
pub(crate) fn create_phdrs(&self, phdrs: &[ElfPhdr]) -> ElfPhdrs {
let (phdr_start, phdr_end) = self.ehdr.phdr_range();
self.phdr_mmap
.or_else(|| {
phdrs
.iter()
.filter(|phdr| phdr.p_type == PT_LOAD)
.find_map(|phdr| {
let cur_range =
phdr.p_offset as usize..(phdr.p_offset + phdr.p_filesz) as usize;
if cur_range.contains(&phdr_start) && cur_range.contains(&phdr_end) {
return Some(self.segments.get_slice::<ElfPhdr>(
phdr.p_vaddr as usize + phdr_start - cur_range.start,
self.ehdr.e_phnum() * size_of::<ElfPhdr>(),
));
}
None
})
})
.map(|phdrs| ElfPhdrs::Mmap(phdrs))
.unwrap_or_else(|| ElfPhdrs::Vec(Vec::from(phdrs)))
}
}
pub(crate) struct ObjectBuilder<Tls, D = ()> {
pub(crate) name: String,
pub(crate) symtab: SymbolTable,
pub(crate) init_array: Option<&'static [fn()]>,
pub(crate) init_fn: DynLifecycleHandler,
pub(crate) fini_fn: DynLifecycleHandler,
pub(crate) segments: ElfSegments,
pub(crate) relocation: StaticRelocation,
pub(crate) mprotect: Box<dyn Fn() -> Result<()>>,
pub(crate) pltgot: PltGotSection,
pub(crate) tls_mod_id: Option<usize>,
pub(crate) tls_tp_offset: Option<isize>,
pub(crate) user_data: D,
_marker_tls: PhantomData<Tls>,
}
impl<T: TlsResolver, D> ObjectBuilder<T, D> {
pub(crate) fn new(
name: String,
shdrs: &mut [ElfShdr],
init_fn: DynLifecycleHandler,
fini_fn: DynLifecycleHandler,
segments: ElfSegments,
mprotect: Box<dyn Fn() -> Result<()>>,
mut pltgot: PltGotSection,
user_data: D,
) -> Self {
let base = segments.base();
shdrs
.iter_mut()
.for_each(|shdr| shdr.sh_addr = (shdr.sh_addr as usize + base) as _);
pltgot.rebase(base);
let mut symtab = None;
let mut relocation = Vec::with_capacity(shdrs.len());
let mut init_array = None;
for shdr in shdrs.iter() {
match shdr.sh_type {
SHT_SYMTAB => {
let symbols: &mut [ElfSymbol] = shdr.content_mut();
for symbol in symbols.iter_mut() {
if symbol.st_type() == STT_FILE || symbol.st_shndx() == SHN_UNDEF as usize {
continue;
}
let section_base = shdrs[symbol.st_shndx()].sh_addr as usize - base;
symbol.set_value(section_base + symbol.st_value());
}
symtab = Some(SymbolTable::from_shdrs(&shdr, shdrs));
}
SHT_RELA | SHT_REL => {
let rels: &mut [ElfRelType] = shdr.content_mut();
let section_base = shdrs[shdr.sh_info as usize].sh_addr as usize;
for rel in rels.iter_mut() {
rel.set_offset(section_base + rel.r_offset() - base);
}
relocation.push(shdr.content());
}
SHT_INIT_ARRAY => {
let array: &[usize] = shdr.content_mut();
init_array = Some(unsafe { core::mem::transmute(array) });
}
_ => {}
}
}
Self {
name,
symtab: symtab.unwrap(),
init_fn,
fini_fn,
segments,
mprotect,
relocation: StaticRelocation::new(relocation),
pltgot,
init_array,
tls_mod_id: None,
tls_tp_offset: None,
user_data,
_marker_tls: PhantomData,
}
}
}